talk2me/deploy.sh
Adolfo Delorenzo 92fd390866 Add production WSGI server - Flask dev server unsuitable for production load
This adds a complete production deployment setup using Gunicorn as the WSGI server, replacing Flask's development server.

Key components:
- Gunicorn configuration with optimized worker settings
- Support for sync, threaded, and async (gevent) workers
- Automatic worker recycling to prevent memory leaks
- Increased timeouts for audio processing
- Production-ready logging and monitoring

Deployment options:
1. Docker/Docker Compose for containerized deployment
2. Systemd service for traditional deployment
3. Nginx reverse proxy configuration
4. SSL/TLS support

Production features:
- wsgi.py entry point for WSGI servers
- gunicorn_config.py with production settings
- Dockerfile with multi-stage build
- docker-compose.yml with full stack (Redis, PostgreSQL)
- nginx.conf with caching and security headers
- systemd service with security hardening
- deploy.sh automated deployment script

Configuration:
- .env.production template with all settings
- Support for environment-based configuration
- Separate requirements-prod.txt
- Prometheus metrics endpoint (/metrics)

Monitoring:
- Health check endpoints for liveness/readiness
- Prometheus-compatible metrics
- Structured logging
- Memory usage tracking
- Request counting

Security:
- Non-root user in Docker
- Systemd security restrictions
- Nginx security headers
- File permission hardening
- Resource limits

Documentation:
- Comprehensive PRODUCTION_DEPLOYMENT.md
- Scaling strategies
- Performance tuning guide
- Troubleshooting section

Also fixed memory_manager.py GC stats collection error.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-03 08:49:32 -06:00

208 lines
5.2 KiB
Bash
Executable File

#!/bin/bash
# Production deployment script for Talk2Me
set -e # Exit on error
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Configuration
APP_NAME="talk2me"
APP_USER="talk2me"
APP_DIR="/opt/talk2me"
VENV_DIR="$APP_DIR/venv"
LOG_DIR="/var/log/talk2me"
PID_FILE="/var/run/talk2me.pid"
WORKERS=${WORKERS:-4}
# Functions
print_status() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
# Check if running as root
if [[ $EUID -ne 0 ]]; then
print_error "This script must be run as root"
exit 1
fi
# Create application user if doesn't exist
if ! id "$APP_USER" &>/dev/null; then
print_status "Creating application user: $APP_USER"
useradd -m -s /bin/bash $APP_USER
fi
# Create directories
print_status "Creating application directories"
mkdir -p $APP_DIR $LOG_DIR
chown -R $APP_USER:$APP_USER $APP_DIR $LOG_DIR
# Copy application files
print_status "Copying application files"
rsync -av --exclude='venv' --exclude='__pycache__' --exclude='*.pyc' \
--exclude='logs' --exclude='.git' --exclude='node_modules' \
./ $APP_DIR/
# Create virtual environment
print_status "Setting up Python virtual environment"
su - $APP_USER -c "cd $APP_DIR && python3 -m venv $VENV_DIR"
# Install dependencies
print_status "Installing Python dependencies"
su - $APP_USER -c "cd $APP_DIR && $VENV_DIR/bin/pip install --upgrade pip"
su - $APP_USER -c "cd $APP_DIR && $VENV_DIR/bin/pip install -r requirements-prod.txt"
# Install Whisper model
print_status "Downloading Whisper model (this may take a while)"
su - $APP_USER -c "cd $APP_DIR && $VENV_DIR/bin/python -c 'import whisper; whisper.load_model(\"base\")'"
# Build frontend assets
if [ -f "package.json" ]; then
print_status "Building frontend assets"
cd $APP_DIR
npm install
npm run build
fi
# Create systemd service
print_status "Creating systemd service"
cat > /etc/systemd/system/talk2me.service <<EOF
[Unit]
Description=Talk2Me Translation Service
After=network.target
[Service]
Type=notify
User=$APP_USER
Group=$APP_USER
WorkingDirectory=$APP_DIR
Environment="PATH=$VENV_DIR/bin"
Environment="FLASK_ENV=production"
Environment="UPLOAD_FOLDER=/tmp/talk2me_uploads"
Environment="LOGS_DIR=$LOG_DIR"
ExecStart=$VENV_DIR/bin/gunicorn --config gunicorn_config.py wsgi:application
ExecReload=/bin/kill -s HUP \$MAINPID
KillMode=mixed
TimeoutStopSec=5
Restart=always
RestartSec=10
# Security settings
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=$LOG_DIR /tmp
[Install]
WantedBy=multi-user.target
EOF
# Create nginx configuration
print_status "Creating nginx configuration"
cat > /etc/nginx/sites-available/talk2me <<EOF
server {
listen 80;
server_name _; # Replace with your domain
# Security headers
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "strict-origin-when-cross-origin";
# File upload size limit
client_max_body_size 50M;
client_body_buffer_size 1M;
# Timeouts for long audio processing
proxy_connect_timeout 120s;
proxy_send_timeout 120s;
proxy_read_timeout 120s;
location / {
proxy_pass http://127.0.0.1:5005;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_cache_bypass \$http_upgrade;
# Don't buffer responses
proxy_buffering off;
# WebSocket support
proxy_set_header Connection "upgrade";
}
location /static {
alias $APP_DIR/static;
expires 1y;
add_header Cache-Control "public, immutable";
}
# Health check endpoint
location /health {
proxy_pass http://127.0.0.1:5005/health;
access_log off;
}
}
EOF
# Enable nginx site
if [ -f /etc/nginx/sites-enabled/default ]; then
rm /etc/nginx/sites-enabled/default
fi
ln -sf /etc/nginx/sites-available/talk2me /etc/nginx/sites-enabled/
# Set permissions
chown -R $APP_USER:$APP_USER $APP_DIR
# Reload systemd
print_status "Reloading systemd"
systemctl daemon-reload
# Start services
print_status "Starting services"
systemctl enable talk2me
systemctl restart talk2me
systemctl restart nginx
# Wait for service to start
sleep 5
# Check service status
if systemctl is-active --quiet talk2me; then
print_status "Talk2Me service is running"
else
print_error "Talk2Me service failed to start"
journalctl -u talk2me -n 50
exit 1
fi
# Test health endpoint
if curl -s http://localhost:5005/health | grep -q "healthy"; then
print_status "Health check passed"
else
print_error "Health check failed"
exit 1
fi
print_status "Deployment complete!"
print_status "Talk2Me is now running at http://$(hostname -I | awk '{print $1}')"
print_status "Check logs at: $LOG_DIR"
print_status "Service status: systemctl status talk2me"