-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdeploy.sh
More file actions
executable file
·232 lines (197 loc) · 9.11 KB
/
deploy.sh
File metadata and controls
executable file
·232 lines (197 loc) · 9.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
#!/usr/bin/env bash
set -euo pipefail
# ── Arguments ─────────────────────────────────────────────────────────────────
if [ $# -lt 2 ]; then
echo "Usage: $0 <domain> <email>"
echo " domain — FQDN pointed at this server (e.g. proxy.example.com)"
echo " email — contact email for Let's Encrypt certificate notifications"
exit 1
fi
DOMAIN="$1"
EMAIL="$2"
PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)"
log() { echo -e "\n\033[1;32m[✓]\033[0m $1"; }
info() { echo -e "\033[1;34m[→]\033[0m $1"; }
err() { echo -e "\033[1;31m[✗]\033[0m $1"; exit 1; }
check() {
if [ $? -eq 0 ]; then
log "$1"
else
err "$2"
fi
}
echo ""
echo "============================================="
echo " Deploying LiteLLM proxy with SSL"
echo " Domain: $DOMAIN"
echo "============================================="
# ── Pre-flight checks ──────────────────────────────────────────────────────
info "Running pre-flight checks..."
[ -f "$PROJECT_DIR/docker-compose.yml" ] || err "docker-compose.yml not found in $PROJECT_DIR"
log "docker-compose.yml found"
[ -f "$PROJECT_DIR/.env" ] || err ".env file not found in $PROJECT_DIR"
log ".env file found"
[ -f "$PROJECT_DIR/litellm-config.yaml" ] || err "litellm-config.yaml not found in $PROJECT_DIR"
log "litellm-config.yaml found"
[ -f "$PROJECT_DIR/nginx.conf" ] || err "nginx.conf not found in $PROJECT_DIR"
log "nginx.conf found"
info "Checking DNS resolution for $DOMAIN..."
RESOLVED_IP=$(dig +short "$DOMAIN" A 2>/dev/null || true)
SERVER_IP=$(curl -s ifconfig.me 2>/dev/null || true)
if [ -n "$RESOLVED_IP" ]; then
log "DNS resolves $DOMAIN -> $RESOLVED_IP"
if [ "$RESOLVED_IP" = "$SERVER_IP" ]; then
log "DNS matches this server's IP ($SERVER_IP)"
else
info "WARNING: DNS ($RESOLVED_IP) does not match this server's IP ($SERVER_IP). SSL may fail."
fi
else
err "DNS does not resolve for $DOMAIN. Set an A record before running this script."
fi
# ── 1. Install Docker ──────────────────────────────────────────────────────
if ! command -v docker &>/dev/null; then
info "Installing Docker..."
sudo apt-get update -qq
sudo apt-get install -y -qq ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update -qq
sudo apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo usermod -aG docker "$USER"
log "Docker installed (version: $(docker --version))"
else
log "Docker already installed (version: $(docker --version))"
fi
# Verify docker daemon is running
if sudo systemctl is-active --quiet docker; then
log "Docker daemon is running"
else
info "Starting Docker daemon..."
sudo systemctl start docker
sudo systemctl is-active --quiet docker || err "Failed to start Docker daemon"
log "Docker daemon started"
fi
# ── 2. Install Nginx ───────────────────────────────────────────────────────
if ! command -v nginx &>/dev/null; then
info "Installing Nginx..."
sudo apt-get install -y -qq nginx
log "Nginx installed (version: $(nginx -v 2>&1))"
else
log "Nginx already installed (version: $(nginx -v 2>&1))"
fi
if sudo systemctl is-active --quiet nginx; then
log "Nginx is running"
else
info "Starting Nginx..."
sudo systemctl start nginx
sudo systemctl is-active --quiet nginx || err "Failed to start Nginx"
log "Nginx started"
fi
# ── 3. Install Certbot ─────────────────────────────────────────────────────
if ! command -v certbot &>/dev/null; then
info "Installing Certbot..."
sudo apt-get install -y -qq certbot python3-certbot-nginx
log "Certbot installed (version: $(certbot --version 2>&1))"
else
log "Certbot already installed (version: $(certbot --version 2>&1))"
fi
# ── 4. Configure Nginx (HTTP only first, for cert issuance) ────────────────
info "Configuring Nginx for HTTP challenge..."
sudo mkdir -p /var/www/certbot
cat <<HTTPCONF | sudo tee "/etc/nginx/sites-available/$DOMAIN" > /dev/null
server {
listen 80;
server_name $DOMAIN;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://\$host\$request_uri;
}
}
HTTPCONF
sudo ln -sf "/etc/nginx/sites-available/$DOMAIN" /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
info "Testing Nginx config..."
sudo nginx -t || err "Nginx config test failed"
sudo systemctl reload nginx
log "Nginx HTTP config active"
# ── 5. Obtain SSL certificate ──────────────────────────────────────────────
if [ ! -d "/etc/letsencrypt/live/$DOMAIN" ]; then
info "Obtaining SSL certificate for $DOMAIN..."
sudo certbot certonly --webroot -w /var/www/certbot \
-d "$DOMAIN" \
--non-interactive --agree-tos \
--email "$EMAIL"
sudo test -f "/etc/letsencrypt/live/$DOMAIN/fullchain.pem" || err "SSL certificate files not found after certbot"
log "SSL certificate obtained successfully"
else
log "SSL certificate already exists for $DOMAIN"
info "Certificate details:"
sudo certbot certificates 2>/dev/null | grep -A3 "$DOMAIN" || true
fi
# ── 6. Deploy full Nginx config with SSL ───────────────────────────────────
info "Installing full Nginx config with SSL..."
sed "s/__DOMAIN__/$DOMAIN/g" "$PROJECT_DIR/nginx.conf" | sudo tee "/etc/nginx/sites-available/$DOMAIN" > /dev/null
info "Testing Nginx SSL config..."
sudo nginx -t || err "Nginx SSL config test failed"
sudo systemctl reload nginx
log "Nginx SSL config active"
# ── 7. Enable certbot auto-renewal timer ───────────────────────────────────
sudo systemctl enable --now certbot.timer 2>/dev/null || true
log "Certbot auto-renewal enabled"
# ── 8. Start the application ───────────────────────────────────────────────
info "Starting LiteLLM + Postgres with Docker Compose..."
cd "$PROJECT_DIR"
# Use 'docker compose' (v2 plugin) or fall back to 'docker-compose'
if docker compose version &>/dev/null; then
COMPOSE="docker compose"
else
COMPOSE="docker-compose"
fi
info "Pulling latest images..."
sudo $COMPOSE pull
log "Images pulled"
info "Starting containers..."
sudo $COMPOSE up -d
log "Containers started"
# ── 9. Post-deploy verification ────────────────────────────────────────────
info "Waiting 10s for services to initialize..."
sleep 10
info "Checking container status..."
sudo $COMPOSE ps
DB_STATUS=$(sudo $COMPOSE ps --format json 2>/dev/null | grep -o '"db"' || echo "")
LITELLM_STATUS=$(sudo $COMPOSE ps --format json 2>/dev/null | grep -o '"litellm"' || echo "")
if sudo $COMPOSE ps | grep -q "Up"; then
log "Containers are running"
else
err "Some containers failed to start. Check: sudo $COMPOSE logs"
fi
info "Testing HTTPS endpoint..."
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "https://$DOMAIN/health" 2>/dev/null || echo "000")
if [ "$HTTP_CODE" = "200" ]; then
log "HTTPS health check passed (HTTP $HTTP_CODE)"
elif [ "$HTTP_CODE" = "000" ]; then
info "WARNING: Could not reach https://$DOMAIN/health — LiteLLM may still be starting up"
info "Check logs with: sudo $COMPOSE logs -f litellm"
else
info "HTTPS returned HTTP $HTTP_CODE — LiteLLM may still be initializing"
info "Check logs with: sudo $COMPOSE logs -f litellm"
fi
echo ""
echo "============================================="
log "Deployment complete!"
echo "============================================="
echo ""
echo " HTTP: http://$DOMAIN (redirects to HTTPS)"
echo " HTTPS: https://$DOMAIN"
echo ""
echo "Useful commands:"
echo " sudo $COMPOSE logs -f # follow logs"
echo " sudo $COMPOSE ps # container status"
echo " sudo $COMPOSE restart # restart services"
echo " sudo certbot renew --dry-run # test cert renewal"
echo " sudo nginx -t && sudo systemctl reload nginx # reload nginx"