nanochat/deploy.sh
Manmohan Sharma b766dcf703
feat(deploy): add dual-mode deploy switch (EC2 monolith + EKS)
- deploy.sh: single script to switch between EC2 and EKS modes
  - ec2: docker-compose with ECR images + nginx SSL reverse proxy
  - eks: terraform apply + helm install (for demos/grading)
  - eks-down: terraform destroy (stop costs)
- docker-compose.prod.yml: ECR image overrides + nginx service
- nginx/nginx.conf: reverse proxy with SSL, SSE streaming support
- deploy-ec2.yml: auto-deploy to EC2 after images are built
- Remove old single-server deploy.yml

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 12:57:57 -07:00

265 lines
10 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
###############################################################################
# samosaChaat Deploy Switch
#
# Usage:
# ./deploy.sh ec2 Deploy monolith to EC2 via docker-compose
# ./deploy.sh ec2-down Stop services on EC2
# ./deploy.sh eks Provision EKS + deploy via Helm (demo/grading)
# ./deploy.sh eks-down Tear down EKS (save $$$)
# ./deploy.sh status Show what's currently running
###############################################################################
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
AWS_ACCOUNT="883107058766"
AWS_REGION="us-west-2"
ECR_REGISTRY="${AWS_ACCOUNT}.dkr.ecr.${AWS_REGION}.amazonaws.com"
EC2_HOST="52.10.243.118"
EC2_USER="ubuntu"
EC2_KEY="$HOME/.ssh/samosachaat.pem" # adjust if your key is elsewhere
DOMAIN="samosachaat.art"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log() { echo -e "${GREEN}[samosaChaat]${NC} $1"; }
warn() { echo -e "${YELLOW}[samosaChaat]${NC} $1"; }
err() { echo -e "${RED}[samosaChaat]${NC} $1" >&2; }
#─── EC2 MONOLITH ─────────────────────────────────────────────────────────────
ec2_deploy() {
log "Deploying to EC2 monolith at ${EC2_HOST}..."
# 1. Login to ECR locally to get credentials
log "Logging into ECR..."
aws ecr get-login-password --region ${AWS_REGION} | \
ssh -i "${EC2_KEY}" -o StrictHostKeyChecking=no ${EC2_USER}@${EC2_HOST} \
"docker login --username AWS --password-stdin ${ECR_REGISTRY}" 2>/dev/null
# 2. Sync repo to EC2
log "Syncing code to EC2..."
ssh -i "${EC2_KEY}" ${EC2_USER}@${EC2_HOST} bash -s << 'REMOTE_SCRIPT'
set -e
cd /home/ubuntu
# Clone or update repo
if [ -d samosachaat ]; then
cd samosachaat
git fetch origin master
git reset --hard origin/master
else
git clone https://github.com/manmohan659/nanochat.git samosachaat
cd samosachaat
fi
# Ensure .env exists
if [ ! -f .env ]; then
cp .env.example .env
echo "⚠️ Created .env from template — edit it with real values!"
fi
REMOTE_SCRIPT
# 3. Copy .env from local if it exists
if [ -f "${SCRIPT_DIR}/.env" ]; then
log "Syncing .env to EC2..."
scp -i "${EC2_KEY}" "${SCRIPT_DIR}/.env" ${EC2_USER}@${EC2_HOST}:/home/ubuntu/samosachaat/.env
fi
# 4. Pull images and start services
log "Pulling images and starting services..."
ssh -i "${EC2_KEY}" ${EC2_USER}@${EC2_HOST} bash -s << REMOTE_DEPLOY
set -e
cd /home/ubuntu/samosachaat
# Set ECR registry in environment
export ECR_REGISTRY=${ECR_REGISTRY}
export IMAGE_TAG=dev-latest
# Pull latest images
docker compose -f docker-compose.yml -f docker-compose.prod.yml pull
# Start everything
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# Run DB migrations
echo "Running database migrations..."
sleep 5 # wait for postgres to be ready
docker compose exec -T chat-api alembic upgrade head 2>/dev/null || \
echo "Migrations skipped (may need manual .env setup)"
echo ""
docker compose ps
REMOTE_DEPLOY
# 5. Setup SSL if not already done
log "Checking SSL..."
ssh -i "${EC2_KEY}" ${EC2_USER}@${EC2_HOST} bash -s << 'SSL_CHECK'
if [ ! -d /etc/letsencrypt/live/samosachaat.art ]; then
echo "Setting up SSL with certbot..."
sudo apt-get update -qq && sudo apt-get install -y -qq certbot > /dev/null 2>&1
sudo certbot certonly --standalone --non-interactive \
--agree-tos -m manmohan659@gmail.com \
-d samosachaat.art -d www.samosachaat.art \
--pre-hook "docker compose -f /home/ubuntu/samosachaat/docker-compose.yml -f /home/ubuntu/samosachaat/docker-compose.prod.yml stop nginx" \
--post-hook "docker compose -f /home/ubuntu/samosachaat/docker-compose.yml -f /home/ubuntu/samosachaat/docker-compose.prod.yml start nginx"
else
echo "SSL already configured."
fi
SSL_CHECK
echo ""
log "EC2 deploy complete!"
log " App: https://${DOMAIN}"
log " Grafana: https://${DOMAIN}/grafana/"
log " EC2: ${EC2_HOST}"
}
ec2_down() {
log "Stopping services on EC2..."
ssh -i "${EC2_KEY}" ${EC2_USER}@${EC2_HOST} \
"cd /home/ubuntu/samosachaat && docker compose -f docker-compose.yml -f docker-compose.prod.yml down"
log "EC2 services stopped."
}
#─── EKS CLUSTER ──────────────────────────────────────────────────────────────
eks_deploy() {
local ENV="${1:-dev}"
log "Provisioning EKS cluster (${ENV})... This takes ~15-20 minutes."
cd "${SCRIPT_DIR}/terraform/environments/${ENV}"
# Init & apply Terraform
log "Running terraform init..."
terraform init
log "Running terraform apply..."
terraform apply -auto-approve
# Get cluster info
local CLUSTER_NAME=$(terraform output -raw eks_cluster_name 2>/dev/null || echo "samosachaat-${ENV}")
log "Configuring kubectl for ${CLUSTER_NAME}..."
aws eks update-kubeconfig --name "${CLUSTER_NAME}" --region ${AWS_REGION}
# Install ALB Ingress Controller
log "Installing ALB Ingress Controller..."
helm repo add eks https://aws.github.io/eks-charts 2>/dev/null || true
helm repo update
helm upgrade --install aws-load-balancer-controller eks/aws-load-balancer-controller \
-n kube-system \
--set clusterName="${CLUSTER_NAME}" \
--set serviceAccount.create=true \
--set serviceAccount.name=aws-load-balancer-controller \
--wait --timeout 5m 2>/dev/null || warn "ALB controller may need IRSA setup"
# Deploy observability stack
log "Deploying observability stack..."
helm dependency build "${SCRIPT_DIR}/helm/observability" 2>/dev/null || true
helm upgrade --install observability "${SCRIPT_DIR}/helm/observability" \
--namespace monitoring --create-namespace \
--wait --timeout 10m 2>/dev/null || warn "Observability deploy needs review"
# Deploy samosaChaat
local VALUES_FILE="${SCRIPT_DIR}/helm/samosachaat/values-${ENV}.yaml"
log "Deploying samosaChaat to EKS..."
helm upgrade --install samosachaat "${SCRIPT_DIR}/helm/samosachaat" \
-f "${VALUES_FILE}" \
--set global.imageRegistry="${ECR_REGISTRY}" \
--set global.imageTag="dev-latest" \
--set ingress.acmCertArn="$(terraform output -raw acm_certificate_arn 2>/dev/null || echo '')" \
--namespace samosachaat --create-namespace \
--wait --timeout 10m
echo ""
log "EKS deploy complete!"
log " Cluster: ${CLUSTER_NAME}"
kubectl get pods -n samosachaat
echo ""
kubectl get ingress -n samosachaat 2>/dev/null || true
}
eks_down() {
local ENV="${1:-dev}"
warn "Tearing down EKS cluster (${ENV})... This saves ~\$0.10/hr + node costs."
cd "${SCRIPT_DIR}/terraform/environments/${ENV}"
# Remove Helm releases first (cleans up ALB, etc.)
local CLUSTER_NAME=$(terraform output -raw eks_cluster_name 2>/dev/null || echo "samosachaat-${ENV}")
aws eks update-kubeconfig --name "${CLUSTER_NAME}" --region ${AWS_REGION} 2>/dev/null || true
log "Removing Helm releases..."
helm uninstall samosachaat -n samosachaat 2>/dev/null || true
helm uninstall observability -n monitoring 2>/dev/null || true
helm uninstall aws-load-balancer-controller -n kube-system 2>/dev/null || true
# Destroy infrastructure
log "Running terraform destroy..."
terraform destroy -auto-approve
log "EKS cluster destroyed. Costs stopped."
}
#─── STATUS ───────────────────────────────────────────────────────────────────
show_status() {
echo ""
log "=== samosaChaat Deployment Status ==="
echo ""
# Check EC2
echo "EC2 Monolith (${EC2_HOST}):"
if ssh -i "${EC2_KEY}" -o ConnectTimeout=5 ${EC2_USER}@${EC2_HOST} \
"docker compose -f /home/ubuntu/samosachaat/docker-compose.yml -f /home/ubuntu/samosachaat/docker-compose.prod.yml ps --format 'table {{.Name}}\t{{.Status}}'" 2>/dev/null; then
echo ""
else
echo " Not running or unreachable."
fi
# Check EKS
echo "EKS Cluster:"
if kubectl get nodes 2>/dev/null; then
echo ""
kubectl get pods -n samosachaat 2>/dev/null || echo " No samosachaat namespace."
else
echo " No EKS cluster configured."
fi
# Check ECR images
echo ""
echo "ECR Images (latest):"
for svc in frontend auth chat-api inference; do
TAG=$(aws ecr describe-images --repository-name samosachaat/${svc} --region ${AWS_REGION} \
--query 'sort_by(imageDetails,&imagePushedAt)[-1].imageTags[0]' --output text 2>/dev/null || echo "none")
echo " samosachaat/${svc}: ${TAG}"
done
}
#─── MAIN ─────────────────────────────────────────────────────────────────────
case "${1:-help}" in
ec2) ec2_deploy ;;
ec2-down) ec2_down ;;
eks) eks_deploy "${2:-dev}" ;;
eks-down) eks_down "${2:-dev}" ;;
status) show_status ;;
*)
echo "samosaChaat Deploy Switch"
echo ""
echo "Usage: ./deploy.sh <mode>"
echo ""
echo "Modes:"
echo " ec2 Deploy monolith to EC2 (cheap, always-on)"
echo " ec2-down Stop EC2 services"
echo " eks [env] Provision EKS + deploy (demo/grading) [dev|uat|prod]"
echo " eks-down Tear down EKS (save \$\$\$)"
echo " status Show what's running"
;;
esac