301 lines
9.9 KiB
Bash
Executable File
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'" |