mirror of
https://github.com/uprightbass360/AzerothCore-RealmMaster.git
synced 2026-01-13 00:58:34 +00:00
456 lines
12 KiB
Bash
Executable File
456 lines
12 KiB
Bash
Executable File
#!/bin/bash
|
|
# ==============================================
|
|
# AzerothCore Complete Stack Deployment Script
|
|
# ==============================================
|
|
# Deploys AzerothCore services in proper order with monitoring
|
|
# Designed for Debian systems with Docker/Docker Compose
|
|
|
|
set -euo pipefail
|
|
|
|
# Configuration
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &> /dev/null && pwd)"
|
|
LOG_DIR="$SCRIPT_DIR/deployment-logs"
|
|
LOG_FILE="$LOG_DIR/deployment-$(date +%Y%m%d_%H%M%S).log"
|
|
PID_FILE="/var/run/azerothcore-deploy.pid"
|
|
|
|
# Ensure log directory exists
|
|
mkdir -p "$LOG_DIR"
|
|
|
|
# Deployment layers in order
|
|
COMPOSE_LAYERS=(
|
|
"database"
|
|
"services"
|
|
"optional"
|
|
"tools"
|
|
)
|
|
|
|
# Service monitoring timeouts (seconds)
|
|
declare -A SERVICE_TIMEOUTS=(
|
|
["ac-mysql"]=120
|
|
["ac-db-init"]=60
|
|
["ac-db-import"]=1800
|
|
["ac-client-data"]=2400
|
|
["ac-authserver"]=180
|
|
["ac-worldserver"]=300
|
|
["ac-backup"]=60
|
|
["ac-mysql-persist"]=60
|
|
)
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
PURPLE='\033[0;35m'
|
|
CYAN='\033[0;36m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Logging functions
|
|
log() {
|
|
local level="$1"
|
|
shift
|
|
local message="$*"
|
|
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
|
|
case "$level" in
|
|
"INFO") echo -e "${CYAN}[INFO]${NC} $message" ;;
|
|
"WARN") echo -e "${YELLOW}[WARN]${NC} $message" ;;
|
|
"ERROR") echo -e "${RED}[ERROR]${NC} $message" ;;
|
|
"SUCCESS") echo -e "${GREEN}[SUCCESS]${NC} $message" ;;
|
|
"DEBUG") echo -e "${PURPLE}[DEBUG]${NC} $message" ;;
|
|
esac
|
|
|
|
echo "[$timestamp] [$level] $message" >> "$LOG_FILE"
|
|
}
|
|
|
|
# Error handling
|
|
cleanup() {
|
|
log "INFO" "Cleaning up..."
|
|
rm -f "$PID_FILE"
|
|
if [[ ${#BACKGROUND_PIDS[@]} -gt 0 ]]; then
|
|
log "INFO" "Stopping background monitoring processes..."
|
|
for pid in "${BACKGROUND_PIDS[@]}"; do
|
|
kill "$pid" 2>/dev/null || true
|
|
done
|
|
fi
|
|
}
|
|
|
|
trap cleanup EXIT
|
|
|
|
error_exit() {
|
|
log "ERROR" "$1"
|
|
cleanup
|
|
exit 1
|
|
}
|
|
|
|
# Array to track background processes
|
|
BACKGROUND_PIDS=()
|
|
|
|
# Check prerequisites
|
|
check_prerequisites() {
|
|
log "INFO" "Checking prerequisites..."
|
|
|
|
# Check if running as root for system service setup
|
|
if [[ "$DEPLOY_MODE" == "system" ]] && [[ $EUID -ne 0 ]]; then
|
|
error_exit "System deployment mode requires root privileges"
|
|
fi
|
|
|
|
# Check Docker
|
|
if ! command -v docker &> /dev/null; then
|
|
error_exit "Docker is not installed"
|
|
fi
|
|
|
|
if ! docker info &> /dev/null; then
|
|
error_exit "Docker daemon is not running"
|
|
fi
|
|
|
|
# Check Docker Compose
|
|
if ! command -v docker-compose &> /dev/null; then
|
|
error_exit "Docker Compose is not installed"
|
|
fi
|
|
|
|
# Check compose files exist
|
|
for layer in "${COMPOSE_LAYERS[@]}"; do
|
|
local compose_file="docker-compose-azerothcore-${layer}.yml"
|
|
if [[ ! -f "$SCRIPT_DIR/$compose_file" ]]; then
|
|
error_exit "Missing compose file: $compose_file"
|
|
fi
|
|
done
|
|
|
|
log "SUCCESS" "Prerequisites check passed"
|
|
}
|
|
|
|
# Setup environment
|
|
setup_environment() {
|
|
log "INFO" "Setting up deployment environment..."
|
|
|
|
# Create log directory
|
|
mkdir -p "$LOG_DIR"
|
|
|
|
# Create data directories
|
|
mkdir -p "$SCRIPT_DIR/local-data/mysql-data"
|
|
mkdir -p "$SCRIPT_DIR/local-data/config"
|
|
mkdir -p "$SCRIPT_DIR/backups"
|
|
|
|
# Set up environment file
|
|
if [[ ! -f "$SCRIPT_DIR/.env" ]]; then
|
|
if [[ -f "$SCRIPT_DIR/.env-database-local" ]]; then
|
|
log "INFO" "Using local database environment configuration"
|
|
cp "$SCRIPT_DIR/.env-database-local" "$SCRIPT_DIR/.env"
|
|
else
|
|
error_exit "No environment configuration found"
|
|
fi
|
|
fi
|
|
|
|
# Store PID for system service management
|
|
echo $$ > "$PID_FILE"
|
|
|
|
log "SUCCESS" "Environment setup complete"
|
|
}
|
|
|
|
# Monitor container health
|
|
monitor_container() {
|
|
local container_name="$1"
|
|
local timeout="${2:-300}"
|
|
local start_time=$(date +%s)
|
|
|
|
log "INFO" "Monitoring $container_name (timeout: ${timeout}s)..."
|
|
|
|
while [[ $(($(date +%s) - start_time)) -lt $timeout ]]; do
|
|
if docker ps --filter "name=$container_name" --format "{{.Names}}" | grep -q "^${container_name}$"; then
|
|
local status=$(docker ps --filter "name=$container_name" --format "{{.Status}}")
|
|
|
|
# Check if container is healthy
|
|
if echo "$status" | grep -q "healthy"; then
|
|
log "SUCCESS" "$container_name is healthy"
|
|
return 0
|
|
fi
|
|
|
|
# Check if container exited
|
|
if echo "$status" | grep -q "Exited"; then
|
|
local exit_code=$(docker ps -a --filter "name=$container_name" --format "{{.Status}}" | grep -o "Exited ([0-9]*)" | grep -o "[0-9]*")
|
|
if [[ "$exit_code" == "0" ]]; then
|
|
log "SUCCESS" "$container_name completed successfully"
|
|
return 0
|
|
else
|
|
log "ERROR" "$container_name failed (exit code: $exit_code)"
|
|
log "DEBUG" "Container logs:"
|
|
docker logs "$container_name" 2>&1 | tail -20 | while read line; do
|
|
log "DEBUG" " $line"
|
|
done
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# Show periodic status updates
|
|
if [[ $(( ($(date +%s) - start_time) % 30 )) -eq 0 ]]; then
|
|
log "INFO" "$container_name status: $status"
|
|
# Show last few log lines
|
|
docker logs "$container_name" --tail 3 2>&1 | while read line; do
|
|
log "DEBUG" " [$container_name] $line"
|
|
done
|
|
fi
|
|
else
|
|
log "WARN" "Waiting for $container_name to start..."
|
|
fi
|
|
|
|
sleep 5
|
|
done
|
|
|
|
log "ERROR" "Timeout waiting for $container_name after ${timeout}s"
|
|
return 1
|
|
}
|
|
|
|
# Deploy a specific layer
|
|
deploy_layer() {
|
|
local layer="$1"
|
|
local compose_file="docker-compose-azerothcore-${layer}.yml"
|
|
|
|
log "INFO" "Deploying layer: $layer"
|
|
|
|
# Start the layer
|
|
if ! docker-compose -f "$compose_file" up -d; then
|
|
error_exit "Failed to start $layer layer"
|
|
fi
|
|
|
|
# Get services in this layer
|
|
local services=$(docker-compose -f "$compose_file" config --services)
|
|
|
|
# Monitor each service
|
|
for service in $services; do
|
|
local container_name="$service"
|
|
local timeout="${SERVICE_TIMEOUTS[$container_name]:-300}"
|
|
|
|
if ! monitor_container "$container_name" "$timeout"; then
|
|
error_exit "Service $container_name failed to start properly"
|
|
fi
|
|
done
|
|
|
|
log "SUCCESS" "Layer $layer deployed successfully"
|
|
}
|
|
|
|
# Monitor running services
|
|
monitor_services() {
|
|
log "INFO" "Starting continuous service monitoring..."
|
|
|
|
while true; do
|
|
local unhealthy_services=()
|
|
|
|
# Check all running containers
|
|
for container in $(docker ps --format "{{.Names}}" | grep "^ac-"); do
|
|
local status=$(docker ps --filter "name=$container" --format "{{.Status}}")
|
|
|
|
if echo "$status" | grep -q "unhealthy\|Restarting\|Exited"; then
|
|
unhealthy_services+=("$container")
|
|
log "WARN" "Unhealthy service detected: $container ($status)"
|
|
fi
|
|
done
|
|
|
|
# Report status
|
|
if [[ ${#unhealthy_services[@]} -eq 0 ]]; then
|
|
log "INFO" "All services healthy ($(docker ps --filter "name=ac-" --format "{{.Names}}" | wc -l) running)"
|
|
else
|
|
log "WARN" "Unhealthy services: ${unhealthy_services[*]}"
|
|
fi
|
|
|
|
sleep 60
|
|
done
|
|
}
|
|
|
|
# Get deployment status
|
|
get_status() {
|
|
log "INFO" "Current deployment status:"
|
|
|
|
for layer in "${COMPOSE_LAYERS[@]}"; do
|
|
local compose_file="docker-compose-azerothcore-${layer}.yml"
|
|
if [[ -f "$compose_file" ]]; then
|
|
echo -e "\n${BLUE}=== $layer Layer ===${NC}"
|
|
docker-compose -f "$compose_file" ps 2>/dev/null || echo "Layer not deployed"
|
|
fi
|
|
done
|
|
|
|
echo -e "\n${BLUE}=== Resource Usage ===${NC}"
|
|
docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}" $(docker ps --filter "name=ac-" --format "{{.Names}}" 2>/dev/null || echo "none") 2>/dev/null || echo "No containers running"
|
|
}
|
|
|
|
# Stop all services
|
|
stop_all() {
|
|
log "INFO" "Stopping all AzerothCore services..."
|
|
|
|
# Stop in reverse order
|
|
for ((i=${#COMPOSE_LAYERS[@]}-1; i>=0; i--)); do
|
|
local layer="${COMPOSE_LAYERS[i]}"
|
|
local compose_file="docker-compose-azerothcore-${layer}.yml"
|
|
|
|
if [[ -f "$compose_file" ]]; then
|
|
log "INFO" "Stopping $layer layer..."
|
|
docker-compose -f "$compose_file" down 2>/dev/null || true
|
|
fi
|
|
done
|
|
|
|
log "SUCCESS" "All services stopped"
|
|
}
|
|
|
|
# Install system service
|
|
install_system_service() {
|
|
log "INFO" "Installing AzerothCore as system service..."
|
|
|
|
cat > /etc/systemd/system/azerothcore.service << EOF
|
|
[Unit]
|
|
Description=AzerothCore WoW Server
|
|
After=docker.service
|
|
Requires=docker.service
|
|
StartLimitIntervalSec=0
|
|
|
|
[Service]
|
|
Type=forking
|
|
User=root
|
|
WorkingDirectory=$SCRIPT_DIR
|
|
ExecStart=$SCRIPT_DIR/azerothcore-deploy.sh start
|
|
ExecStop=$SCRIPT_DIR/azerothcore-deploy.sh stop
|
|
ExecReload=$SCRIPT_DIR/azerothcore-deploy.sh restart
|
|
PIDFile=$PID_FILE
|
|
Restart=always
|
|
RestartSec=30
|
|
TimeoutStartSec=1800
|
|
TimeoutStopSec=300
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
systemctl daemon-reload
|
|
systemctl enable azerothcore
|
|
|
|
log "SUCCESS" "AzerothCore system service installed"
|
|
log "INFO" "Use 'systemctl start azerothcore' to start the service"
|
|
log "INFO" "Use 'systemctl status azerothcore' to check status"
|
|
}
|
|
|
|
# Main deployment function
|
|
main_deploy() {
|
|
log "INFO" "Starting AzerothCore deployment..."
|
|
|
|
check_prerequisites
|
|
setup_environment
|
|
|
|
# Deploy each layer in sequence
|
|
for layer in "${COMPOSE_LAYERS[@]}"; do
|
|
deploy_layer "$layer"
|
|
|
|
# Brief pause between layers
|
|
sleep 10
|
|
done
|
|
|
|
log "SUCCESS" "AzerothCore deployment completed successfully!"
|
|
|
|
# Show final status
|
|
get_status
|
|
|
|
# Start background monitoring if not in daemon mode
|
|
if [[ "$MONITOR_MODE" == "continuous" ]]; then
|
|
monitor_services &
|
|
BACKGROUND_PIDS+=($!)
|
|
log "INFO" "Background monitoring started (PID: $!)"
|
|
|
|
# Keep script running
|
|
wait
|
|
fi
|
|
}
|
|
|
|
# Usage information
|
|
usage() {
|
|
cat << EOF
|
|
Usage: $0 [COMMAND] [OPTIONS]
|
|
|
|
COMMANDS:
|
|
start Deploy AzerothCore stack
|
|
stop Stop all AzerothCore services
|
|
restart Restart AzerothCore stack
|
|
status Show current deployment status
|
|
logs [service] Show logs for service (or all services)
|
|
install-service Install as system service (requires root)
|
|
|
|
OPTIONS:
|
|
--monitor Enable continuous monitoring
|
|
--system System deployment mode (requires root)
|
|
--help Show this help message
|
|
|
|
Examples:
|
|
$0 start --monitor # Deploy with continuous monitoring
|
|
$0 status # Show current status
|
|
$0 logs ac-worldserver # Show worldserver logs
|
|
$0 install-service # Install as system service
|
|
|
|
Environment Variables:
|
|
DEPLOY_MODE=system # Enable system mode
|
|
MONITOR_MODE=continuous # Enable monitoring
|
|
|
|
EOF
|
|
}
|
|
|
|
# Parse command line arguments
|
|
COMMAND="${1:-start}"
|
|
DEPLOY_MODE="${DEPLOY_MODE:-local}"
|
|
MONITOR_MODE="${MONITOR_MODE:-single}"
|
|
|
|
shift || true
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--monitor)
|
|
MONITOR_MODE="continuous"
|
|
shift
|
|
;;
|
|
--system)
|
|
DEPLOY_MODE="system"
|
|
shift
|
|
;;
|
|
--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
break
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Execute command
|
|
case "$COMMAND" in
|
|
"start")
|
|
main_deploy
|
|
;;
|
|
"stop")
|
|
stop_all
|
|
;;
|
|
"restart")
|
|
stop_all
|
|
sleep 5
|
|
main_deploy
|
|
;;
|
|
"status")
|
|
get_status
|
|
;;
|
|
"logs")
|
|
service_name="${1:-}"
|
|
if [[ -n "$service_name" ]]; then
|
|
docker logs "$service_name" --follow
|
|
else
|
|
log "INFO" "Available services:"
|
|
docker ps --filter "name=ac-" --format "{{.Names}}" || echo "No services running"
|
|
fi
|
|
;;
|
|
"install-service")
|
|
install_system_service
|
|
;;
|
|
"help"|"--help")
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Unknown command: $COMMAND"
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac |