first commit
This commit is contained in:
Executable
+687
@@ -0,0 +1,687 @@
|
||||
#!/bin/bash
|
||||
|
||||
# TankStopp Docker Deployment Script
|
||||
# This script deploys the TankStopp application using Docker Compose
|
||||
|
||||
set -e # Exit on any error
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
PURPLE='\033[0;35m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Default values
|
||||
ENVIRONMENT="production"
|
||||
ACTION="deploy"
|
||||
SERVICE_NAME="tankstopp"
|
||||
COMPOSE_FILE="docker-compose.yml"
|
||||
COMPOSE_OVERRIDE=""
|
||||
PROJECT_NAME="tankstopp"
|
||||
BACKUP_BEFORE_DEPLOY=true
|
||||
WAIT_TIMEOUT=300
|
||||
HEALTH_CHECK_RETRIES=10
|
||||
ROLLBACK_ON_FAILURE=true
|
||||
SCALE_REPLICAS=1
|
||||
|
||||
# Configuration
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
DATA_DIR="/var/lib/tankstopp"
|
||||
BACKUP_DIR="/var/lib/tankstopp/backups"
|
||||
LOG_DIR="/var/log/tankstopp"
|
||||
|
||||
# Functions
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
|
||||
}
|
||||
|
||||
log_debug() {
|
||||
if [ "$DEBUG" = true ]; then
|
||||
echo -e "${PURPLE}[DEBUG]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
|
||||
fi
|
||||
}
|
||||
|
||||
show_help() {
|
||||
cat << EOF
|
||||
TankStopp Docker Deployment Script
|
||||
|
||||
Usage: $0 [ACTION] [OPTIONS]
|
||||
|
||||
ACTIONS:
|
||||
deploy Deploy the application (default)
|
||||
start Start existing containers
|
||||
stop Stop running containers
|
||||
restart Restart containers
|
||||
update Update to latest images
|
||||
rollback Rollback to previous version
|
||||
status Show deployment status
|
||||
logs Show application logs
|
||||
backup Create database backup
|
||||
restore Restore from backup
|
||||
scale Scale service replicas
|
||||
cleanup Clean up unused resources
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Show this help message
|
||||
-e, --env ENV Environment: development|production|staging (default: production)
|
||||
-f, --file FILE Docker compose file (default: docker-compose.yml)
|
||||
-o, --override FILE Docker compose override file
|
||||
-p, --project NAME Project name (default: tankstopp)
|
||||
-s, --service NAME Service name (default: tankstopp)
|
||||
-r, --replicas COUNT Number of replicas for scaling (default: 1)
|
||||
-t, --timeout SECONDS Wait timeout in seconds (default: 300)
|
||||
--no-backup Skip backup before deployment
|
||||
--no-rollback Don't rollback on failure
|
||||
--force Force action without confirmation
|
||||
--debug Enable debug logging
|
||||
|
||||
EXAMPLES:
|
||||
# Deploy to production
|
||||
$0 deploy --env production
|
||||
|
||||
# Deploy with custom compose file
|
||||
$0 deploy --file docker-compose.prod.yml
|
||||
|
||||
# Start development environment
|
||||
$0 start --env development
|
||||
|
||||
# Scale production service
|
||||
$0 scale --replicas 3
|
||||
|
||||
# View logs
|
||||
$0 logs --service tankstopp
|
||||
|
||||
# Backup database
|
||||
$0 backup
|
||||
|
||||
# Rollback deployment
|
||||
$0 rollback
|
||||
|
||||
ENVIRONMENT CONFIGURATIONS:
|
||||
development - Local development with debug enabled
|
||||
staging - Staging environment for testing
|
||||
production - Production deployment with optimizations
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
check_requirements() {
|
||||
log_info "Checking deployment requirements..."
|
||||
|
||||
# Check Docker
|
||||
if ! command -v docker &> /dev/null; then
|
||||
log_error "Docker is not installed or not in PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check Docker Compose
|
||||
if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then
|
||||
log_error "Docker Compose is not installed or not in PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Set Docker Compose command
|
||||
if docker compose version &> /dev/null; then
|
||||
DOCKER_COMPOSE_CMD="docker compose"
|
||||
else
|
||||
DOCKER_COMPOSE_CMD="docker-compose"
|
||||
fi
|
||||
|
||||
# Check Docker daemon
|
||||
if ! docker info &> /dev/null; then
|
||||
log_error "Docker daemon is not running"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check compose file
|
||||
if [ ! -f "$PROJECT_ROOT/$COMPOSE_FILE" ]; then
|
||||
log_error "Docker Compose file not found: $PROJECT_ROOT/$COMPOSE_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_success "Requirements check passed"
|
||||
}
|
||||
|
||||
setup_environment() {
|
||||
log_info "Setting up environment: $ENVIRONMENT"
|
||||
|
||||
# Create necessary directories
|
||||
create_directories
|
||||
|
||||
# Set compose files based on environment
|
||||
case "$ENVIRONMENT" in
|
||||
"development")
|
||||
COMPOSE_OVERRIDE="docker-compose.override.yml"
|
||||
;;
|
||||
"staging")
|
||||
COMPOSE_OVERRIDE="docker-compose.staging.yml"
|
||||
;;
|
||||
"production")
|
||||
COMPOSE_OVERRIDE="docker-compose.prod.yml"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Build compose command
|
||||
COMPOSE_CMD="$DOCKER_COMPOSE_CMD -p $PROJECT_NAME -f $COMPOSE_FILE"
|
||||
if [ -n "$COMPOSE_OVERRIDE" ] && [ -f "$PROJECT_ROOT/$COMPOSE_OVERRIDE" ]; then
|
||||
COMPOSE_CMD="$COMPOSE_CMD -f $COMPOSE_OVERRIDE"
|
||||
log_info "Using override file: $COMPOSE_OVERRIDE"
|
||||
fi
|
||||
|
||||
log_success "Environment setup completed"
|
||||
}
|
||||
|
||||
create_directories() {
|
||||
log_info "Creating necessary directories..."
|
||||
|
||||
# Create directories with proper permissions
|
||||
sudo mkdir -p "$DATA_DIR/data" "$BACKUP_DIR" "$LOG_DIR"
|
||||
sudo chown -R 1001:1001 "$DATA_DIR" 2>/dev/null || true
|
||||
sudo chmod -R 755 "$DATA_DIR" 2>/dev/null || true
|
||||
|
||||
log_success "Directories created"
|
||||
}
|
||||
|
||||
backup_database() {
|
||||
if [ "$BACKUP_BEFORE_DEPLOY" = false ]; then
|
||||
log_info "Skipping backup (disabled)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "Creating database backup..."
|
||||
|
||||
local backup_file="$BACKUP_DIR/fuel_stops_$(date +%Y%m%d_%H%M%S).db"
|
||||
|
||||
# Check if database exists
|
||||
if [ ! -f "$DATA_DIR/data/fuel_stops.db" ]; then
|
||||
log_warning "Database file not found, skipping backup"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Create backup
|
||||
if cp "$DATA_DIR/data/fuel_stops.db" "$backup_file"; then
|
||||
log_success "Database backup created: $backup_file"
|
||||
|
||||
# Keep only last 10 backups
|
||||
find "$BACKUP_DIR" -name "fuel_stops_*.db" -type f | sort -r | tail -n +11 | xargs rm -f 2>/dev/null || true
|
||||
else
|
||||
log_error "Failed to create database backup"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
pull_images() {
|
||||
log_info "Pulling latest images..."
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
if $COMPOSE_CMD pull; then
|
||||
log_success "Images pulled successfully"
|
||||
else
|
||||
log_error "Failed to pull images"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
deploy_application() {
|
||||
log_info "Deploying TankStopp application..."
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# Backup database
|
||||
backup_database
|
||||
|
||||
# Pull latest images
|
||||
pull_images
|
||||
|
||||
# Deploy with docker-compose
|
||||
log_info "Starting containers..."
|
||||
if $COMPOSE_CMD up -d --remove-orphans; then
|
||||
log_success "Containers started successfully"
|
||||
else
|
||||
log_error "Failed to start containers"
|
||||
if [ "$ROLLBACK_ON_FAILURE" = true ]; then
|
||||
log_info "Attempting rollback..."
|
||||
rollback_deployment
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Wait for services to be healthy
|
||||
wait_for_health
|
||||
|
||||
# Run post-deployment checks
|
||||
post_deployment_checks
|
||||
|
||||
log_success "Application deployed successfully"
|
||||
}
|
||||
|
||||
wait_for_health() {
|
||||
log_info "Waiting for services to become healthy..."
|
||||
|
||||
local retries=0
|
||||
local max_retries=$HEALTH_CHECK_RETRIES
|
||||
|
||||
while [ $retries -lt $max_retries ]; do
|
||||
if check_service_health; then
|
||||
log_success "All services are healthy"
|
||||
return 0
|
||||
fi
|
||||
|
||||
retries=$((retries + 1))
|
||||
log_info "Health check attempt $retries/$max_retries..."
|
||||
sleep 10
|
||||
done
|
||||
|
||||
log_error "Services failed to become healthy within timeout"
|
||||
return 1
|
||||
}
|
||||
|
||||
check_service_health() {
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# Check container status
|
||||
local unhealthy_containers=$($COMPOSE_CMD ps --filter "status=unhealthy" --format "{{.Service}}" 2>/dev/null | wc -l)
|
||||
local running_containers=$($COMPOSE_CMD ps --filter "status=running" --format "{{.Service}}" 2>/dev/null | wc -l)
|
||||
|
||||
log_debug "Running containers: $running_containers, Unhealthy containers: $unhealthy_containers"
|
||||
|
||||
if [ "$unhealthy_containers" -gt 0 ]; then
|
||||
log_warning "Found $unhealthy_containers unhealthy containers"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "$running_containers" -eq 0 ]; then
|
||||
log_warning "No running containers found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Test HTTP endpoint
|
||||
if command -v curl &> /dev/null; then
|
||||
local port=$(get_service_port)
|
||||
if curl -sf "http://localhost:$port/" > /dev/null 2>&1; then
|
||||
return 0
|
||||
else
|
||||
log_debug "HTTP health check failed"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
get_service_port() {
|
||||
case "$ENVIRONMENT" in
|
||||
"development")
|
||||
echo "8081"
|
||||
;;
|
||||
*)
|
||||
echo "8080"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
post_deployment_checks() {
|
||||
log_info "Running post-deployment checks..."
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# Check container logs for errors
|
||||
local error_count=$($COMPOSE_CMD logs --tail=50 "$SERVICE_NAME" 2>/dev/null | grep -i "error\|panic\|fatal" | wc -l)
|
||||
if [ "$error_count" -gt 0 ]; then
|
||||
log_warning "Found $error_count error messages in logs"
|
||||
log_info "Recent errors:"
|
||||
$COMPOSE_CMD logs --tail=10 "$SERVICE_NAME" | grep -i "error\|panic\|fatal" || true
|
||||
fi
|
||||
|
||||
# Check resource usage
|
||||
check_resource_usage
|
||||
|
||||
log_success "Post-deployment checks completed"
|
||||
}
|
||||
|
||||
check_resource_usage() {
|
||||
log_info "Checking resource usage..."
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# Get container stats
|
||||
local stats=$($COMPOSE_CMD exec -T "$SERVICE_NAME" sh -c "
|
||||
echo 'Memory usage:'
|
||||
free -h | grep Mem
|
||||
echo 'Disk usage:'
|
||||
df -h /app/data
|
||||
echo 'CPU info:'
|
||||
uptime
|
||||
" 2>/dev/null || echo "Could not retrieve stats")
|
||||
|
||||
log_debug "Resource stats: $stats"
|
||||
}
|
||||
|
||||
start_services() {
|
||||
log_info "Starting services..."
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
if $COMPOSE_CMD start; then
|
||||
log_success "Services started successfully"
|
||||
wait_for_health
|
||||
else
|
||||
log_error "Failed to start services"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
stop_services() {
|
||||
log_info "Stopping services..."
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
if $COMPOSE_CMD stop; then
|
||||
log_success "Services stopped successfully"
|
||||
else
|
||||
log_error "Failed to stop services"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
restart_services() {
|
||||
log_info "Restarting services..."
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
if $COMPOSE_CMD restart; then
|
||||
log_success "Services restarted successfully"
|
||||
wait_for_health
|
||||
else
|
||||
log_error "Failed to restart services"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
update_application() {
|
||||
log_info "Updating application..."
|
||||
|
||||
backup_database
|
||||
pull_images
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
if $COMPOSE_CMD up -d --remove-orphans; then
|
||||
log_success "Application updated successfully"
|
||||
wait_for_health
|
||||
post_deployment_checks
|
||||
else
|
||||
log_error "Failed to update application"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
rollback_deployment() {
|
||||
log_info "Rolling back deployment..."
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# Stop current containers
|
||||
$COMPOSE_CMD down
|
||||
|
||||
# Restore from latest backup
|
||||
local latest_backup=$(find "$BACKUP_DIR" -name "fuel_stops_*.db" -type f | sort -r | head -n 1)
|
||||
if [ -n "$latest_backup" ] && [ -f "$latest_backup" ]; then
|
||||
log_info "Restoring database from: $latest_backup"
|
||||
cp "$latest_backup" "$DATA_DIR/data/fuel_stops.db"
|
||||
log_success "Database restored"
|
||||
else
|
||||
log_warning "No backup found for rollback"
|
||||
fi
|
||||
|
||||
# Start with previous configuration
|
||||
if $COMPOSE_CMD up -d; then
|
||||
log_success "Rollback completed successfully"
|
||||
else
|
||||
log_error "Rollback failed"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
show_status() {
|
||||
log_info "Deployment status:"
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
echo ""
|
||||
echo "=== Container Status ==="
|
||||
$COMPOSE_CMD ps
|
||||
|
||||
echo ""
|
||||
echo "=== Service Health ==="
|
||||
if check_service_health; then
|
||||
echo -e "${GREEN}✓ Services are healthy${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Services are unhealthy${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== Resource Usage ==="
|
||||
check_resource_usage
|
||||
|
||||
echo ""
|
||||
echo "=== Recent Logs ==="
|
||||
$COMPOSE_CMD logs --tail=5 "$SERVICE_NAME"
|
||||
}
|
||||
|
||||
show_logs() {
|
||||
log_info "Showing application logs..."
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
$COMPOSE_CMD logs -f "$SERVICE_NAME"
|
||||
}
|
||||
|
||||
scale_service() {
|
||||
log_info "Scaling service to $SCALE_REPLICAS replicas..."
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
if $COMPOSE_CMD up -d --scale "$SERVICE_NAME=$SCALE_REPLICAS"; then
|
||||
log_success "Service scaled to $SCALE_REPLICAS replicas"
|
||||
wait_for_health
|
||||
else
|
||||
log_error "Failed to scale service"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup_resources() {
|
||||
log_info "Cleaning up unused resources..."
|
||||
|
||||
# Remove unused containers
|
||||
docker container prune -f
|
||||
|
||||
# Remove unused images
|
||||
docker image prune -f
|
||||
|
||||
# Remove unused volumes (be careful!)
|
||||
if [ "$FORCE" = true ]; then
|
||||
docker volume prune -f
|
||||
fi
|
||||
|
||||
# Remove unused networks
|
||||
docker network prune -f
|
||||
|
||||
log_success "Cleanup completed"
|
||||
}
|
||||
|
||||
restore_backup() {
|
||||
log_info "Available backups:"
|
||||
ls -la "$BACKUP_DIR"/fuel_stops_*.db 2>/dev/null || {
|
||||
log_error "No backups found in $BACKUP_DIR"
|
||||
return 1
|
||||
}
|
||||
|
||||
echo ""
|
||||
read -p "Enter backup filename to restore (or 'latest' for most recent): " backup_choice
|
||||
|
||||
local backup_file
|
||||
if [ "$backup_choice" = "latest" ]; then
|
||||
backup_file=$(find "$BACKUP_DIR" -name "fuel_stops_*.db" -type f | sort -r | head -n 1)
|
||||
else
|
||||
backup_file="$BACKUP_DIR/$backup_choice"
|
||||
fi
|
||||
|
||||
if [ ! -f "$backup_file" ]; then
|
||||
log_error "Backup file not found: $backup_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Restoring from: $backup_file"
|
||||
|
||||
# Stop services
|
||||
stop_services
|
||||
|
||||
# Restore database
|
||||
cp "$backup_file" "$DATA_DIR/data/fuel_stops.db"
|
||||
|
||||
# Start services
|
||||
start_services
|
||||
|
||||
log_success "Database restored successfully"
|
||||
}
|
||||
|
||||
main() {
|
||||
# Parse command line arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
deploy|start|stop|restart|update|rollback|status|logs|backup|restore|scale|cleanup)
|
||||
ACTION="$1"
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
-e|--env)
|
||||
ENVIRONMENT="$2"
|
||||
shift 2
|
||||
;;
|
||||
-f|--file)
|
||||
COMPOSE_FILE="$2"
|
||||
shift 2
|
||||
;;
|
||||
-o|--override)
|
||||
COMPOSE_OVERRIDE="$2"
|
||||
shift 2
|
||||
;;
|
||||
-p|--project)
|
||||
PROJECT_NAME="$2"
|
||||
shift 2
|
||||
;;
|
||||
-s|--service)
|
||||
SERVICE_NAME="$2"
|
||||
shift 2
|
||||
;;
|
||||
-r|--replicas)
|
||||
SCALE_REPLICAS="$2"
|
||||
shift 2
|
||||
;;
|
||||
-t|--timeout)
|
||||
WAIT_TIMEOUT="$2"
|
||||
shift 2
|
||||
;;
|
||||
--no-backup)
|
||||
BACKUP_BEFORE_DEPLOY=false
|
||||
shift
|
||||
;;
|
||||
--no-rollback)
|
||||
ROLLBACK_ON_FAILURE=false
|
||||
shift
|
||||
;;
|
||||
--force)
|
||||
FORCE=true
|
||||
shift
|
||||
;;
|
||||
--debug)
|
||||
DEBUG=true
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown option: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validate environment
|
||||
if [[ "$ENVIRONMENT" != "development" && "$ENVIRONMENT" != "staging" && "$ENVIRONMENT" != "production" ]]; then
|
||||
log_error "Invalid environment: $ENVIRONMENT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Start deployment process
|
||||
log_info "Starting TankStopp deployment process..."
|
||||
log_info "Action: $ACTION"
|
||||
log_info "Environment: $ENVIRONMENT"
|
||||
log_info "Project: $PROJECT_NAME"
|
||||
|
||||
check_requirements
|
||||
setup_environment
|
||||
|
||||
# Execute action
|
||||
case "$ACTION" in
|
||||
"deploy")
|
||||
deploy_application
|
||||
;;
|
||||
"start")
|
||||
start_services
|
||||
;;
|
||||
"stop")
|
||||
stop_services
|
||||
;;
|
||||
"restart")
|
||||
restart_services
|
||||
;;
|
||||
"update")
|
||||
update_application
|
||||
;;
|
||||
"rollback")
|
||||
rollback_deployment
|
||||
;;
|
||||
"status")
|
||||
show_status
|
||||
;;
|
||||
"logs")
|
||||
show_logs
|
||||
;;
|
||||
"backup")
|
||||
backup_database
|
||||
;;
|
||||
"restore")
|
||||
restore_backup
|
||||
;;
|
||||
"scale")
|
||||
scale_service
|
||||
;;
|
||||
"cleanup")
|
||||
cleanup_resources
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown action: $ACTION"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
log_success "Deployment process completed successfully!"
|
||||
}
|
||||
|
||||
# Execute main function
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user