ai-hackaton-backend/scripts/deploy.sh
2025-09-10 21:41:42 +03:00

301 lines
9.9 KiB
Bash
Executable File

#!/bin/bash
# SSH Deploy script for HR AI Backend
# Usage: ./scripts/deploy.sh [environment] [image_tag]
set -e
# Configuration
ENVIRONMENT="${1:-production}"
IMAGE_TAG="${2:-latest}"
REGISTRY_ID="${YANDEX_REGISTRY_ID:-your-registry-id}"
IMAGE_NAME="hr-ai-backend"
FULL_IMAGE_NAME="cr.yandex/${REGISTRY_ID}/${IMAGE_NAME}:${IMAGE_TAG}"
# Server configuration (set these as environment variables)
SERVER_HOST="${DEPLOY_HOST:-your-server.com}"
SERVER_USER="${DEPLOY_USER:-deploy}"
SERVER_PORT="${DEPLOY_PORT:-22}"
DEPLOY_PATH="${DEPLOY_PATH:-/opt/hr-ai-backend}"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
echo -e "${YELLOW}Deploying HR AI Backend to ${ENVIRONMENT} environment${NC}"
# Check if required environment variables are set
missing_vars=""
if [ -z "$SERVER_HOST" ] || [ "$SERVER_HOST" = "your-server.com" ]; then
missing_vars="$missing_vars DEPLOY_HOST"
fi
if [ -z "$SERVER_USER" ] || [ "$SERVER_USER" = "deploy" ]; then
missing_vars="$missing_vars DEPLOY_USER"
fi
if [ -z "$REGISTRY_ID" ] || [ "$REGISTRY_ID" = "your-registry-id" ]; then
missing_vars="$missing_vars YANDEX_REGISTRY_ID"
fi
if [ -n "$missing_vars" ]; then
echo -e "${RED}Error: Required environment variables are not set:${NC}"
for var in $missing_vars; do
echo " - $var"
done
echo ""
echo "Example configuration:"
echo "export DEPLOY_HOST=your-server.example.com"
echo "export DEPLOY_USER=deploy"
echo "export YANDEX_REGISTRY_ID=crp1234567890abcdef"
echo "export DEPLOY_PATH=/opt/hr-ai-backend # optional"
echo "export DEPLOY_PORT=22 # optional"
exit 1
fi
# Test SSH connection
echo -e "${BLUE}Testing SSH connection to ${SERVER_USER}@${SERVER_HOST}:${SERVER_PORT}...${NC}"
if ! ssh -p "${SERVER_PORT}" -o ConnectTimeout=10 -o StrictHostKeyChecking=no "${SERVER_USER}@${SERVER_HOST}" "echo 'SSH connection successful'"; then
echo -e "${RED}Error: Cannot connect to server via SSH${NC}"
echo "Please check your SSH key configuration and server details"
exit 1
fi
# Create deployment directory structure on server
echo -e "${BLUE}Creating deployment directories on server...${NC}"
ssh -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" "
sudo mkdir -p ${DEPLOY_PATH}/{config,logs,data,agent_commands}
sudo mkdir -p ${DEPLOY_PATH}/data/{postgres,redis,uploads,caddy_data,caddy_config}
sudo mkdir -p ${DEPLOY_PATH}/logs/{caddy}
sudo chown -R ${SERVER_USER}:${SERVER_USER} ${DEPLOY_PATH}
"
# Copy configuration files to server
echo -e "${BLUE}Copying configuration files to server...${NC}"
scp -P "${SERVER_PORT}" docker-compose.yml "${SERVER_USER}@${SERVER_HOST}:${DEPLOY_PATH}/"
scp -P "${SERVER_PORT}" Caddyfile "${SERVER_USER}@${SERVER_HOST}:${DEPLOY_PATH}/"
# Create frontend environment file
echo -e "${BLUE}Creating frontend environment configuration...${NC}"
ssh -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" "
cat > ${DEPLOY_PATH}/.env.local << 'EOF'
# Frontend Environment Configuration
NODE_ENV=production
# API URL (adjust based on your setup)
NEXT_PUBLIC_API_URL=https://\${SERVER_HOST}/api
REACT_APP_API_URL=https://\${SERVER_HOST}/api
VUE_APP_API_URL=https://\${SERVER_HOST}/api
# LiveKit Configuration for frontend
NEXT_PUBLIC_LIVEKIT_URL=ws://\${SERVER_HOST}/rtc
REACT_APP_LIVEKIT_URL=ws://\${SERVER_HOST}/rtc
VUE_APP_LIVEKIT_URL=ws://\${SERVER_HOST}/rtc
# For localhost development (no HTTPS)
# NEXT_PUBLIC_API_URL=http://\${SERVER_HOST}/api
# REACT_APP_API_URL=http://\${SERVER_HOST}/api
# VUE_APP_API_URL=http://\${SERVER_HOST}/api
# NEXT_PUBLIC_LIVEKIT_URL=ws://\${SERVER_HOST}/rtc
# REACT_APP_LIVEKIT_URL=ws://\${SERVER_HOST}/rtc
# VUE_APP_LIVEKIT_URL=ws://\${SERVER_HOST}/rtc
# Add your frontend-specific environment variables here
EOF
"
# Create production environment file on server
echo -e "${BLUE}Creating production environment configuration...${NC}"
ssh -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" "
cat > ${DEPLOY_PATH}/.env << 'EOF'
# Production Environment Configuration
DATABASE_URL=postgresql+asyncpg://hr_user:hr_password@postgres:5432/hr_ai
REDIS_CACHE_URL=redis
REDIS_CACHE_PORT=6379
REDIS_CACHE_DB=0
# LiveKit Configuration
LIVEKIT_URL=ws://livekit:7880
LIVEKIT_API_KEY=devkey
LIVEKIT_API_SECRET=devkey_secret_32chars_minimum_length
# Caddy Domain Configuration (set your domain for automatic HTTPS)
DOMAIN=${SERVER_HOST:-localhost}
# App Configuration
APP_ENV=production
DEBUG=false
# Add your production API keys here:
# OPENAI_API_KEY=your-openai-api-key
# DEEPGRAM_API_KEY=your-deepgram-api-key
# CARTESIA_API_KEY=your-cartesia-api-key
# ELEVENLABS_API_KEY=your-elevenlabs-api-key
# S3 Storage Configuration (optional)
# S3_ENDPOINT_URL=https://s3.storage.selcloud.ru
# S3_ACCESS_KEY_ID=your_s3_access_key
# S3_SECRET_ACCESS_KEY=your_s3_secret_key
# S3_BUCKET_NAME=your-bucket-name
# S3_REGION=ru-1
# Milvus Vector Database Configuration (optional)
# MILVUS_URI=http://milvus:19530
# MILVUS_COLLECTION=hr_candidate_profiles
EOF
"
# Create production docker compose override
echo -e "${BLUE}Creating production docker compose configuration...${NC}"
ssh -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" "
cat > ${DEPLOY_PATH}/docker-compose.prod.yml << 'EOF'
services:
backend:
image: ${FULL_IMAGE_NAME}
env_file:
- .env
restart: unless-stopped
volumes:
- ./agent_commands:/tmp/agent_commands
- ./data/uploads:/app/uploads
- ./logs:/app/logs
postgres:
restart: unless-stopped
volumes:
- ./data/postgres:/var/lib/postgresql/data
redis:
restart: unless-stopped
volumes:
- ./data/redis:/data
livekit:
restart: unless-stopped
ports:
- \"3478:3478/udp\"
caddy:
env_file:
- .env
restart: unless-stopped
volumes:
- ./data/caddy_data:/data
- ./data/caddy_config:/config
- ./logs/caddy:/var/log/caddy
frontend:
restart: unless-stopped
EOF
"
# Pull latest image and deploy
echo -e "${BLUE}Pulling latest image and starting services...${NC}"
ssh -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" "
cd ${DEPLOY_PATH}
# Configure Docker for Yandex Cloud Registry
echo 'Configuring Docker for Yandex Cloud Registry...'
# Completely reset Docker config to fix credential helper issues
echo 'Resetting Docker configuration...'
mkdir -p ~/.docker
cat > ~/.docker/config.json << 'DOCKER_CONFIG'
{
\"auths\": {},
\"HttpHeaders\": {
\"User-Agent\": \"Docker-Client/20.10.0 (linux)\"
}
}
DOCKER_CONFIG
# Install yc CLI if not found
if ! command -v yc &> /dev/null; then
echo 'Installing Yandex Cloud CLI...'
curl -sSL https://storage.yandexcloud.net/yandexcloud-yc/install.sh | bash
source ~/.bashrc || source ~/.bash_profile || export PATH=\"\$HOME/yandex-cloud/bin:\$PATH\"
fi
# Use manual login instead of yc configure-docker
if command -v yc &> /dev/null; then
echo 'Getting Yandex Cloud token and logging in manually...'
YC_TOKEN=\$(yc iam create-token 2>/dev/null)
if [ ! -z \"\$YC_TOKEN\" ]; then
echo \"\$YC_TOKEN\" | docker login --username oauth --password-stdin cr.yandex
echo 'Docker login successful'
else
echo 'Error: Could not get YC token. Please run: yc init'
echo 'You need to authenticate yc CLI first on the server'
exit 1
fi
else
echo 'Error: yc CLI installation failed'
exit 1
fi
echo 'Current Docker config:'
cat ~/.docker/config.json
# Pull only our custom images from Yandex Registry first
echo 'Pulling custom images from Yandex Registry...'
docker pull ${FULL_IMAGE_NAME} || echo 'Failed to pull backend image'
docker pull cr.yandex/crp9p5rtbnbop36duusi/hr-ai-frontend:latest || echo 'Failed to pull frontend image'
# Reset Docker config to default for pulling public images
echo 'Resetting Docker config for public images...'
cat > ~/.docker/config.json << 'DOCKER_CONFIG'
{
\"auths\": {}
}
DOCKER_CONFIG
# Stop old containers
echo 'Stopping existing services...'
docker compose -f docker-compose.yml -f docker-compose.prod.yml down --remove-orphans
# Start new containers
echo 'Starting services with new image...'
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# Wait for services to start
echo 'Waiting for services to start...'
sleep 10
# Run database migrations
echo 'Running database migrations...'
docker compose -f docker-compose.yml -f docker-compose.prod.yml exec -T backend uv run alembic upgrade head || echo 'Migration failed or already up to date'
# Show service status
echo 'Service status:'
docker compose -f docker-compose.yml -f docker-compose.prod.yml ps
"
# Health check
echo -e "${BLUE}Performing health check...${NC}"
sleep 20
if ssh -p "${SERVER_PORT}" "${SERVER_USER}@${SERVER_HOST}" "curl -f http://localhost/health" >/dev/null 2>&1; then
echo -e "${GREEN}✓ Deployment successful! Service is healthy.${NC}"
else
echo -e "${YELLOW}⚠ Service deployed but health check failed. Check logs:${NC}"
echo "ssh -p ${SERVER_PORT} ${SERVER_USER}@${SERVER_HOST} 'cd ${DEPLOY_PATH} && docker compose logs backend caddy'"
fi
echo -e "${GREEN}Deployment completed!${NC}"
echo ""
echo "Service URLs:"
if [ "\$DOMAIN" != "localhost" ]; then
echo " Main site: https://${SERVER_HOST}"
echo " API: https://${SERVER_HOST}/api"
echo " LiveKit: https://${SERVER_HOST}/livekit"
else
echo " Main site: http://${SERVER_HOST}"
echo " API: http://${SERVER_HOST}/api"
echo " LiveKit: http://${SERVER_HOST}/livekit"
fi
echo ""
echo "Useful commands:"
echo " Check logs: ssh ${SERVER_USER}@${SERVER_HOST} 'cd ${DEPLOY_PATH} && docker compose logs -f'"
echo " Service status: ssh ${SERVER_USER}@${SERVER_HOST} 'cd ${DEPLOY_PATH} && docker compose ps'"
echo " Restart: ssh ${SERVER_USER}@${SERVER_HOST} 'cd ${DEPLOY_PATH} && docker compose restart'"