feat: adapt deployment for existing Docker server with nginx-proxy + Woodpecker
- docker-compose.yaml: remove port binding; add VIRTUAL_HOST/LETSENCRYPT_HOST env vars for nginx-proxy auto-routing; add internal + external proxy networks - .woodpecker.yml: consolidate build steps into single ci step; add deploy-main step that builds + deploys on every push to main; keep deploy-tag for registry-pull deploys on version tags - deploy/deploy.sh: simplify for manual/emergency use on existing server; add --from-registry flag for registry pull vs local build - Remove deploy/setup-server.sh and deploy/nginx/ (not needed on existing server) Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
f99772d8c0
commit
f29b39b40c
@ -1,18 +1,10 @@
|
|||||||
steps:
|
steps:
|
||||||
- name: build
|
- name: ci
|
||||||
image: golang:1.22-alpine
|
|
||||||
commands:
|
|
||||||
- go build ./cmd/server/
|
|
||||||
|
|
||||||
- name: vet
|
|
||||||
image: golang:1.22-alpine
|
image: golang:1.22-alpine
|
||||||
commands:
|
commands:
|
||||||
- go vet ./...
|
- go vet ./...
|
||||||
|
|
||||||
- name: test
|
|
||||||
image: golang:1.22-alpine
|
|
||||||
commands:
|
|
||||||
- go test ./...
|
- go test ./...
|
||||||
|
- go build ./cmd/server/
|
||||||
|
|
||||||
- name: docker-build
|
- name: docker-build
|
||||||
image: plugins/docker
|
image: plugins/docker
|
||||||
@ -30,7 +22,7 @@ steps:
|
|||||||
event: tag
|
event: tag
|
||||||
ref: refs/tags/v*
|
ref: refs/tags/v*
|
||||||
|
|
||||||
- name: deploy
|
- name: deploy-tag
|
||||||
image: appleboy/drone-ssh
|
image: appleboy/drone-ssh
|
||||||
settings:
|
settings:
|
||||||
host:
|
host:
|
||||||
@ -41,8 +33,30 @@ steps:
|
|||||||
from_secret: deploy_key
|
from_secret: deploy_key
|
||||||
script:
|
script:
|
||||||
- cd /opt/cast-ghl-provider
|
- cd /opt/cast-ghl-provider
|
||||||
- docker compose pull
|
- docker compose pull bridge
|
||||||
- docker compose up -d --remove-orphans
|
- docker compose up -d --remove-orphans
|
||||||
|
- sleep 5
|
||||||
|
- docker compose ps bridge
|
||||||
when:
|
when:
|
||||||
event: tag
|
event: tag
|
||||||
ref: refs/tags/v*
|
ref: refs/tags/v*
|
||||||
|
|
||||||
|
- name: deploy-main
|
||||||
|
image: appleboy/drone-ssh
|
||||||
|
settings:
|
||||||
|
host:
|
||||||
|
from_secret: deploy_host
|
||||||
|
username:
|
||||||
|
from_secret: deploy_user
|
||||||
|
key:
|
||||||
|
from_secret: deploy_key
|
||||||
|
script:
|
||||||
|
- cd /opt/cast-ghl-provider
|
||||||
|
- git pull --ff-only
|
||||||
|
- docker compose build --no-cache bridge
|
||||||
|
- docker compose up -d --remove-orphans
|
||||||
|
- sleep 5
|
||||||
|
- docker compose ps bridge
|
||||||
|
when:
|
||||||
|
branch: main
|
||||||
|
event: push
|
||||||
|
|||||||
@ -1,33 +1,35 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# deploy.sh — Pull latest code and redeploy via Docker Compose
|
# deploy.sh — Manual redeploy on an existing Docker server.
|
||||||
# Run from /opt/cast-ghl-plugin after initial server setup.
|
# Normally Woodpecker CI handles deploys automatically on push to main or tag.
|
||||||
# Usage: bash deploy/deploy.sh
|
# Use this script only for manual/emergency redeploys.
|
||||||
|
# Usage: bash deploy/deploy.sh [--from-registry]
|
||||||
|
# --from-registry Pull the pre-built image from registry instead of building locally
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
APP_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
APP_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
cd "$APP_DIR"
|
cd "$APP_DIR"
|
||||||
|
|
||||||
|
FROM_REGISTRY=false
|
||||||
|
[[ "${1:-}" == "--from-registry" ]] && FROM_REGISTRY=true
|
||||||
|
|
||||||
echo "==> Pulling latest code"
|
echo "==> Pulling latest code"
|
||||||
git pull --ff-only
|
git pull --ff-only
|
||||||
|
|
||||||
echo "==> Building and restarting services"
|
if $FROM_REGISTRY; then
|
||||||
docker compose pull mongo # pull latest Mongo image if updated
|
echo "==> Pulling pre-built image from registry"
|
||||||
|
docker compose pull bridge
|
||||||
|
else
|
||||||
|
echo "==> Building image locally"
|
||||||
docker compose build --no-cache bridge
|
docker compose build --no-cache bridge
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "==> Restarting services"
|
||||||
docker compose up -d --remove-orphans
|
docker compose up -d --remove-orphans
|
||||||
|
|
||||||
echo "==> Waiting for health check"
|
echo "==> Waiting for health check"
|
||||||
sleep 5
|
sleep 5
|
||||||
STATUS=$(docker compose ps --format json | python3 -c "
|
docker compose ps bridge
|
||||||
import sys, json
|
|
||||||
for line in sys.stdin:
|
|
||||||
s = json.loads(line)
|
|
||||||
if s.get('Service') == 'bridge':
|
|
||||||
print(s.get('Health', s.get('State', 'unknown')))
|
|
||||||
" 2>/dev/null || echo "unknown")
|
|
||||||
echo "Bridge container status: $STATUS"
|
|
||||||
|
|
||||||
echo "==> Tailing last 20 log lines"
|
|
||||||
docker compose logs --tail=20 bridge
|
docker compose logs --tail=20 bridge
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@ -1,42 +0,0 @@
|
|||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name ghl.cast.ph;
|
|
||||||
|
|
||||||
# Let's Encrypt ACME challenge
|
|
||||||
location /.well-known/acme-challenge/ {
|
|
||||||
root /var/www/certbot;
|
|
||||||
}
|
|
||||||
|
|
||||||
location / {
|
|
||||||
return 301 https://$host$request_uri;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 443 ssl;
|
|
||||||
server_name ghl.cast.ph;
|
|
||||||
|
|
||||||
ssl_certificate /etc/letsencrypt/live/ghl.cast.ph/fullchain.pem;
|
|
||||||
ssl_certificate_key /etc/letsencrypt/live/ghl.cast.ph/privkey.pem;
|
|
||||||
include /etc/letsencrypt/options-ssl-nginx.conf;
|
|
||||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
|
||||||
|
|
||||||
# Security headers
|
|
||||||
add_header X-Frame-Options DENY;
|
|
||||||
add_header X-Content-Type-Options nosniff;
|
|
||||||
add_header Referrer-Policy no-referrer;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
proxy_pass http://127.0.0.1:3002;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
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;
|
|
||||||
|
|
||||||
# Timeouts — GHL expects quick 200 for webhook; 30s is generous
|
|
||||||
proxy_connect_timeout 10s;
|
|
||||||
proxy_send_timeout 30s;
|
|
||||||
proxy_read_timeout 30s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,60 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# setup-server.sh — Bootstrap a fresh Ubuntu 22.04/24.04 LTS Vultr VPS
|
|
||||||
# Run once as root (or with sudo) after provisioning.
|
|
||||||
# Usage: bash setup-server.sh
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
DOMAIN="ghl.cast.ph"
|
|
||||||
APP_DIR="/opt/cast-ghl-plugin"
|
|
||||||
REPO_URL="https://github.com/CAST-ph/cast-ghl-plugin.git" # adjust if needed
|
|
||||||
|
|
||||||
echo "==> Updating system packages"
|
|
||||||
apt-get update -q && apt-get upgrade -y -q
|
|
||||||
|
|
||||||
echo "==> Installing dependencies"
|
|
||||||
apt-get install -y -q \
|
|
||||||
ca-certificates curl gnupg ufw \
|
|
||||||
nginx certbot python3-certbot-nginx \
|
|
||||||
git
|
|
||||||
|
|
||||||
echo "==> Installing Docker"
|
|
||||||
install -m 0755 -d /etc/apt/keyrings
|
|
||||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
|
|
||||||
| gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
|
||||||
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" \
|
|
||||||
| tee /etc/apt/sources.list.d/docker.list > /dev/null
|
|
||||||
apt-get update -q
|
|
||||||
apt-get install -y -q docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
|
||||||
systemctl enable --now docker
|
|
||||||
|
|
||||||
echo "==> Configuring firewall"
|
|
||||||
ufw default deny incoming
|
|
||||||
ufw default allow outgoing
|
|
||||||
ufw allow ssh
|
|
||||||
ufw allow 'Nginx Full'
|
|
||||||
ufw --force enable
|
|
||||||
|
|
||||||
echo "==> Cloning application"
|
|
||||||
mkdir -p "$APP_DIR"
|
|
||||||
if [ -d "$APP_DIR/.git" ]; then
|
|
||||||
git -C "$APP_DIR" pull
|
|
||||||
else
|
|
||||||
git clone "$REPO_URL" "$APP_DIR"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "==> Installing Nginx config"
|
|
||||||
cp "$APP_DIR/deploy/nginx/ghl.cast.ph.conf" /etc/nginx/sites-available/"$DOMAIN"
|
|
||||||
ln -sf /etc/nginx/sites-available/"$DOMAIN" /etc/nginx/sites-enabled/"$DOMAIN"
|
|
||||||
rm -f /etc/nginx/sites-enabled/default
|
|
||||||
nginx -t && systemctl reload nginx
|
|
||||||
|
|
||||||
echo "==> Obtaining Let's Encrypt certificate"
|
|
||||||
certbot --nginx -d "$DOMAIN" --non-interactive --agree-tos -m ops@cast.ph
|
|
||||||
systemctl reload nginx
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "=== Setup complete ==="
|
|
||||||
echo "Next: copy .env to $APP_DIR/.env then run deploy/deploy.sh"
|
|
||||||
@ -1,13 +1,20 @@
|
|||||||
services:
|
services:
|
||||||
bridge:
|
bridge:
|
||||||
build: .
|
build: .
|
||||||
# Bind to localhost only — Nginx proxies from outside
|
# No port binding — nginx-proxy routes traffic via the shared proxy network
|
||||||
ports:
|
|
||||||
- "127.0.0.1:${PORT:-3002}:${PORT:-3002}"
|
|
||||||
env_file: .env
|
env_file: .env
|
||||||
|
environment:
|
||||||
|
# nginx-proxy / acme-companion auto-routing
|
||||||
|
- VIRTUAL_HOST=${VIRTUAL_HOST:-ghl.cast.ph}
|
||||||
|
- VIRTUAL_PORT=${PORT:-3002}
|
||||||
|
- LETSENCRYPT_HOST=${VIRTUAL_HOST:-ghl.cast.ph}
|
||||||
|
- LETSENCRYPT_EMAIL=${LETSENCRYPT_EMAIL:-ops@cast.ph}
|
||||||
depends_on:
|
depends_on:
|
||||||
mongo:
|
mongo:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
|
- proxy # shared nginx-proxy network — must match the nginx-proxy container's network
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
logging:
|
logging:
|
||||||
driver: json-file
|
driver: json-file
|
||||||
@ -20,6 +27,8 @@ services:
|
|||||||
# No ports exposed — only reachable by bridge on the internal network
|
# No ports exposed — only reachable by bridge on the internal network
|
||||||
volumes:
|
volumes:
|
||||||
- mongo-data:/data/db
|
- mongo-data:/data/db
|
||||||
|
networks:
|
||||||
|
- internal
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
|
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
|
||||||
@ -28,5 +37,13 @@ services:
|
|||||||
retries: 5
|
retries: 5
|
||||||
start_period: 20s
|
start_period: 20s
|
||||||
|
|
||||||
|
networks:
|
||||||
|
internal:
|
||||||
|
# Private network for bridge ↔ mongo
|
||||||
|
proxy:
|
||||||
|
external: true
|
||||||
|
# Must match the name of the existing nginx-proxy Docker network on the server.
|
||||||
|
# Check with: docker network ls | grep proxy
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
mongo-data:
|
mongo-data:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user