创建新工程
This commit is contained in:
330
deploy.sh
Normal file
330
deploy.sh
Normal file
@@ -0,0 +1,330 @@
|
||||
#!/bin/bash
|
||||
|
||||
# AI Town Game Production Deployment Script
|
||||
# Usage: ./deploy.sh [environment]
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
ENVIRONMENT=${1:-production}
|
||||
PROJECT_NAME="ai-town-game"
|
||||
BACKUP_DIR="/opt/backups/$PROJECT_NAME"
|
||||
DEPLOY_DIR="/opt/$PROJECT_NAME"
|
||||
WEB_DIR="$DEPLOY_DIR/web"
|
||||
SERVER_DIR="$DEPLOY_DIR/server"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Logging function
|
||||
log() {
|
||||
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1"
|
||||
}
|
||||
|
||||
error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1" >&2
|
||||
}
|
||||
|
||||
success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
# Check if running as root
|
||||
check_root() {
|
||||
if [[ $EUID -eq 0 ]]; then
|
||||
error "This script should not be run as root"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check system requirements
|
||||
check_requirements() {
|
||||
log "Checking system requirements..."
|
||||
|
||||
# Check Docker
|
||||
if ! command -v docker &> /dev/null; then
|
||||
error "Docker is not installed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check Docker Compose
|
||||
if ! command -v docker-compose &> /dev/null; then
|
||||
error "Docker Compose is not installed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check Node.js (for local builds)
|
||||
if ! command -v node &> /dev/null; then
|
||||
warning "Node.js is not installed (required for local builds)"
|
||||
fi
|
||||
|
||||
# Check available disk space (at least 2GB)
|
||||
AVAILABLE_SPACE=$(df / | tail -1 | awk '{print $4}')
|
||||
if [[ $AVAILABLE_SPACE -lt 2097152 ]]; then
|
||||
error "Insufficient disk space (need at least 2GB)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
success "System requirements check passed"
|
||||
}
|
||||
|
||||
# Create backup of current deployment
|
||||
create_backup() {
|
||||
log "Creating backup of current deployment..."
|
||||
|
||||
if [[ -d "$DEPLOY_DIR" ]]; then
|
||||
BACKUP_NAME="backup_$(date +%Y%m%d_%H%M%S)"
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
# Backup data directory
|
||||
if [[ -d "$SERVER_DIR/data" ]]; then
|
||||
tar -czf "$BACKUP_DIR/${BACKUP_NAME}_data.tar.gz" -C "$SERVER_DIR" data/
|
||||
success "Data backup created: ${BACKUP_NAME}_data.tar.gz"
|
||||
fi
|
||||
|
||||
# Backup configuration
|
||||
if [[ -f "$DEPLOY_DIR/.env" ]]; then
|
||||
cp "$DEPLOY_DIR/.env" "$BACKUP_DIR/${BACKUP_NAME}_env"
|
||||
success "Configuration backup created: ${BACKUP_NAME}_env"
|
||||
fi
|
||||
|
||||
# Clean old backups (keep last 5)
|
||||
cd "$BACKUP_DIR"
|
||||
ls -t backup_*_data.tar.gz 2>/dev/null | tail -n +6 | xargs rm -f
|
||||
ls -t backup_*_env 2>/dev/null | tail -n +6 | xargs rm -f
|
||||
else
|
||||
log "No existing deployment found, skipping backup"
|
||||
fi
|
||||
}
|
||||
|
||||
# Setup deployment directory
|
||||
setup_directories() {
|
||||
log "Setting up deployment directories..."
|
||||
|
||||
sudo mkdir -p "$DEPLOY_DIR"
|
||||
sudo mkdir -p "$WEB_DIR"
|
||||
sudo mkdir -p "$SERVER_DIR"
|
||||
sudo mkdir -p "$BACKUP_DIR"
|
||||
sudo mkdir -p "/var/log/$PROJECT_NAME"
|
||||
|
||||
# Set permissions
|
||||
sudo chown -R $USER:$USER "$DEPLOY_DIR"
|
||||
sudo chown -R $USER:$USER "$BACKUP_DIR"
|
||||
|
||||
success "Directories created and configured"
|
||||
}
|
||||
|
||||
# Deploy server application
|
||||
deploy_server() {
|
||||
log "Deploying server application..."
|
||||
|
||||
# Copy server files
|
||||
cp -r server/* "$SERVER_DIR/"
|
||||
|
||||
# Copy Docker configuration
|
||||
cp docker-compose.prod.yml "$DEPLOY_DIR/"
|
||||
cp nginx/nginx.conf "$DEPLOY_DIR/nginx/"
|
||||
|
||||
# Setup environment configuration
|
||||
if [[ ! -f "$DEPLOY_DIR/.env" ]]; then
|
||||
cp .env.production "$DEPLOY_DIR/.env"
|
||||
warning "Please edit $DEPLOY_DIR/.env with your production settings"
|
||||
fi
|
||||
|
||||
# Build and start services
|
||||
cd "$DEPLOY_DIR"
|
||||
docker-compose -f docker-compose.prod.yml build
|
||||
docker-compose -f docker-compose.prod.yml up -d
|
||||
|
||||
success "Server application deployed"
|
||||
}
|
||||
|
||||
# Deploy web client
|
||||
deploy_web() {
|
||||
log "Deploying web client..."
|
||||
|
||||
# Check if web build exists
|
||||
if [[ ! -d "web" ]]; then
|
||||
error "Web build not found. Please export from Godot first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Copy web files
|
||||
cp -r web/* "$WEB_DIR/"
|
||||
|
||||
# Set proper permissions
|
||||
sudo chown -R www-data:www-data "$WEB_DIR"
|
||||
sudo chmod -R 755 "$WEB_DIR"
|
||||
|
||||
success "Web client deployed"
|
||||
}
|
||||
|
||||
# Configure system services
|
||||
configure_services() {
|
||||
log "Configuring system services..."
|
||||
|
||||
# Create systemd service for monitoring
|
||||
sudo tee /etc/systemd/system/ai-town-monitor.service > /dev/null <<EOF
|
||||
[Unit]
|
||||
Description=AI Town Game Monitor
|
||||
After=docker.service
|
||||
Requires=docker.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/opt/ai-town-game/monitor.sh
|
||||
User=$USER
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
# Create monitoring script
|
||||
tee "$DEPLOY_DIR/monitor.sh" > /dev/null <<'EOF'
|
||||
#!/bin/bash
|
||||
cd /opt/ai-town-game
|
||||
if ! docker-compose -f docker-compose.prod.yml ps | grep -q "Up"; then
|
||||
echo "AI Town services are down, restarting..."
|
||||
docker-compose -f docker-compose.prod.yml up -d
|
||||
fi
|
||||
EOF
|
||||
chmod +x "$DEPLOY_DIR/monitor.sh"
|
||||
|
||||
# Setup cron job for monitoring
|
||||
(crontab -l 2>/dev/null; echo "*/5 * * * * /opt/ai-town-game/monitor.sh") | crontab -
|
||||
|
||||
success "System services configured"
|
||||
}
|
||||
|
||||
# Setup SSL certificates (Let's Encrypt)
|
||||
setup_ssl() {
|
||||
if [[ -z "$DOMAIN" ]]; then
|
||||
warning "DOMAIN not set, skipping SSL setup"
|
||||
return
|
||||
fi
|
||||
|
||||
log "Setting up SSL certificates for $DOMAIN..."
|
||||
|
||||
# Install certbot
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y certbot python3-certbot-nginx
|
||||
|
||||
# Get certificate
|
||||
sudo certbot --nginx -d "$DOMAIN" --non-interactive --agree-tos --email "$EMAIL"
|
||||
|
||||
# Setup auto-renewal
|
||||
(crontab -l 2>/dev/null; echo "0 12 * * * /usr/bin/certbot renew --quiet") | crontab -
|
||||
|
||||
success "SSL certificates configured"
|
||||
}
|
||||
|
||||
# Health check
|
||||
health_check() {
|
||||
log "Performing health check..."
|
||||
|
||||
# Wait for services to start
|
||||
sleep 30
|
||||
|
||||
# Check server health
|
||||
if curl -f http://localhost:8080/health > /dev/null 2>&1; then
|
||||
success "Server health check passed"
|
||||
else
|
||||
error "Server health check failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check web client
|
||||
if curl -f http://localhost/ > /dev/null 2>&1; then
|
||||
success "Web client health check passed"
|
||||
else
|
||||
error "Web client health check failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check admin API
|
||||
if curl -f http://localhost:8081/api/status > /dev/null 2>&1; then
|
||||
success "Admin API health check passed"
|
||||
else
|
||||
warning "Admin API health check failed (may require authentication)"
|
||||
fi
|
||||
|
||||
success "Health check completed"
|
||||
}
|
||||
|
||||
# Rollback function
|
||||
rollback() {
|
||||
error "Deployment failed, initiating rollback..."
|
||||
|
||||
cd "$DEPLOY_DIR"
|
||||
docker-compose -f docker-compose.prod.yml down
|
||||
|
||||
# Restore from latest backup
|
||||
LATEST_BACKUP=$(ls -t "$BACKUP_DIR"/backup_*_data.tar.gz 2>/dev/null | head -1)
|
||||
if [[ -n "$LATEST_BACKUP" ]]; then
|
||||
log "Restoring from backup: $LATEST_BACKUP"
|
||||
tar -xzf "$LATEST_BACKUP" -C "$SERVER_DIR"
|
||||
fi
|
||||
|
||||
# Restart services
|
||||
docker-compose -f docker-compose.prod.yml up -d
|
||||
|
||||
error "Rollback completed"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Main deployment function
|
||||
main() {
|
||||
log "Starting AI Town Game deployment (Environment: $ENVIRONMENT)"
|
||||
|
||||
# Trap errors for rollback
|
||||
trap rollback ERR
|
||||
|
||||
check_root
|
||||
check_requirements
|
||||
create_backup
|
||||
setup_directories
|
||||
deploy_server
|
||||
deploy_web
|
||||
configure_services
|
||||
|
||||
# Setup SSL if domain is provided
|
||||
if [[ -n "$DOMAIN" ]]; then
|
||||
setup_ssl
|
||||
fi
|
||||
|
||||
# Health check
|
||||
if ! health_check; then
|
||||
rollback
|
||||
fi
|
||||
|
||||
success "Deployment completed successfully!"
|
||||
|
||||
log "Access your application at:"
|
||||
log " Web Client: http://localhost/ (or https://$DOMAIN)"
|
||||
log " Admin Panel: http://localhost:8081/admin/"
|
||||
log " Server API: http://localhost:8080/"
|
||||
|
||||
log "Important files:"
|
||||
log " Configuration: $DEPLOY_DIR/.env"
|
||||
log " Logs: /var/log/$PROJECT_NAME/"
|
||||
log " Backups: $BACKUP_DIR/"
|
||||
|
||||
log "Next steps:"
|
||||
log " 1. Edit $DEPLOY_DIR/.env with your production settings"
|
||||
log " 2. Configure your domain DNS to point to this server"
|
||||
log " 3. Set up monitoring and alerting"
|
||||
log " 4. Test all functionality"
|
||||
}
|
||||
|
||||
# Script entry point
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
main "$@"
|
||||
fi
|
||||
Reference in New Issue
Block a user