688 lines
17 KiB
Bash
Executable File
688 lines
17 KiB
Bash
Executable File
#!/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 "$@"
|