ops: add CI/CD pipeline, a/b rolling deploy, Gitea Actions workflow
Deploy to Production / deploy (push) Failing after 10s
Deploy to Production / deploy (push) Failing after 10s
- .gitea/workflows/deploy.yml — push-to-main triggers rolling deploy - scripts/deploy-bluegreen.sh — a-stack then b-stack restart; Maven runs in Docker (no JDK needed on runner host); Caddy reload at end - scripts/deploy-all.ps1 — emergency manual deploy from dev machine - infra/docker-compose.yml — a/b pairs per service; wget health checks; Gitea service; Prometheus/Grafana/DB ports restricted to localhost - infra/Caddyfile — dual upstreams with health-based routing - infra/Dockerfile.* — one per service - infra/prometheus.yml + grafana provisioning Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
$ErrorActionPreference = "Stop"
|
||||
$VPS = "deploy@204.168.156.179"
|
||||
$MVN = "C:\Users\Anwender\IdeaProjects\Claude\bot-service-index\apix-mvp"
|
||||
|
||||
Set-Location $MVN
|
||||
|
||||
Write-Host "Copying web content into portal..."
|
||||
Copy-Item "$MVN\..\web\api-index.org\index.html" `
|
||||
"$MVN\apix-portal\src\main\resources\META-INF\resources\index.html" -Force
|
||||
|
||||
Write-Host "Building all modules..."
|
||||
mvn clean package -DskipTests -q
|
||||
|
||||
Write-Host "Transferring infra config..."
|
||||
scp "$MVN\infra\Caddyfile" "${VPS}:/opt/apix/infra/Caddyfile"
|
||||
scp "$MVN\infra\docker-compose.yml" "${VPS}:/opt/apix/infra/docker-compose.yml"
|
||||
scp "$MVN\infra\Dockerfile.registry" "${VPS}:/opt/apix/infra/Dockerfile.registry"
|
||||
scp "$MVN\infra\Dockerfile.portal" "${VPS}:/opt/apix/infra/Dockerfile.portal"
|
||||
scp "$MVN\infra\Dockerfile.spider" "${VPS}:/opt/apix/infra/Dockerfile.spider"
|
||||
scp "$MVN\infra\Dockerfile.demo" "${VPS}:/opt/apix/infra/Dockerfile.demo"
|
||||
|
||||
Write-Host "Transferring registry..."
|
||||
scp "$MVN\apix-registry\target\quarkus-app\app\apix-registry-1.0-SNAPSHOT.jar" `
|
||||
"${VPS}:/opt/apix/apix-registry/target/quarkus-app/app/apix-registry-1.0-SNAPSHOT.jar"
|
||||
scp "$MVN\apix-registry\target\quarkus-app\quarkus\quarkus-application.dat" `
|
||||
"${VPS}:/opt/apix/apix-registry/target/quarkus-app/quarkus/quarkus-application.dat"
|
||||
|
||||
Write-Host "Transferring portal..."
|
||||
scp "$MVN\apix-portal\target\quarkus-app\app\apix-portal-1.0-SNAPSHOT.jar" `
|
||||
"${VPS}:/opt/apix/apix-portal/target/quarkus-app/app/apix-portal-1.0-SNAPSHOT.jar"
|
||||
scp "$MVN\apix-portal\target\quarkus-app\quarkus\quarkus-application.dat" `
|
||||
"${VPS}:/opt/apix/apix-portal/target/quarkus-app/quarkus/quarkus-application.dat"
|
||||
|
||||
# Spider + Demo are small — full copy every time so lib/ is always current
|
||||
Write-Host "Transferring spider (full copy)..."
|
||||
ssh $VPS "mkdir -p /opt/apix/apix-spider/target"
|
||||
scp -r "$MVN\apix-spider\target\quarkus-app" `
|
||||
"${VPS}:/opt/apix/apix-spider/target/quarkus-app"
|
||||
|
||||
Write-Host "Transferring demo (full copy)..."
|
||||
ssh $VPS "mkdir -p /opt/apix/apix-demo/target"
|
||||
scp -r "$MVN\apix-demo\target\quarkus-app" `
|
||||
"${VPS}:/opt/apix/apix-demo/target/quarkus-app"
|
||||
|
||||
Write-Host "Rebuilding images and restarting stack..."
|
||||
ssh $VPS @"
|
||||
cd /opt/apix && \
|
||||
docker build -f infra/Dockerfile.registry -t apix-registry:latest . -q && \
|
||||
docker build -f infra/Dockerfile.portal -t apix-portal:latest . -q && \
|
||||
docker build -f infra/Dockerfile.spider -t apix-spider:latest . -q && \
|
||||
docker build -f infra/Dockerfile.demo -t apix-demo:latest . -q && \
|
||||
docker compose -f infra/docker-compose.yml --env-file .env up -d registry portal spider demo && \
|
||||
docker exec infra-caddy-1 caddy reload --config /etc/caddy/Caddyfile
|
||||
"@
|
||||
|
||||
Write-Host "Done."
|
||||
Write-Host " Registry: https://api-index.org/"
|
||||
Write-Host " Portal: https://www.api-index.org/"
|
||||
Write-Host " Demo: https://demo.api-index.org/"
|
||||
Write-Host " Spider: internal (no public port)"
|
||||
@@ -0,0 +1,89 @@
|
||||
#!/bin/bash
|
||||
# Rolling zero-downtime deploy for APIX.
|
||||
# Triggered by Gitea Actions (act_runner, host process) on push to main.
|
||||
# Requires: Docker on the runner host. Maven runs inside a container.
|
||||
#
|
||||
# Strategy: a/b container pairs per service. Caddy health-routes between them.
|
||||
# Deploy restarts a-stack then b-stack sequentially — one instance always healthy.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
INFRA_DIR="/opt/apix/infra"
|
||||
ENV_FILE="/opt/apix/.env"
|
||||
COMPOSE="docker compose -f $INFRA_DIR/docker-compose.yml --env-file $ENV_FILE"
|
||||
|
||||
log() { echo "[deploy $(date -u +%H:%M:%S)] $*"; }
|
||||
|
||||
wait_healthy() {
|
||||
local service=$1
|
||||
local container="infra-${service}-1"
|
||||
local max=90
|
||||
local elapsed=0
|
||||
log "Waiting for $service to become healthy..."
|
||||
while [ $elapsed -lt $max ]; do
|
||||
status=$(docker inspect "$container" \
|
||||
--format '{{.State.Health.Status}}' 2>/dev/null || echo "missing")
|
||||
if [ "$status" = "healthy" ]; then
|
||||
log "$service is healthy."
|
||||
return 0
|
||||
fi
|
||||
sleep 5
|
||||
elapsed=$((elapsed + 5))
|
||||
done
|
||||
log "ERROR: $service did not become healthy after ${max}s (last status: $status)"
|
||||
docker logs "$container" --tail 20 2>&1 || true
|
||||
return 1
|
||||
}
|
||||
|
||||
# ── 1. Sync infra config to /opt/apix/infra ──────────────────────────────────
|
||||
log "Syncing infra config..."
|
||||
cp infra/Caddyfile "$INFRA_DIR/Caddyfile"
|
||||
cp infra/docker-compose.yml "$INFRA_DIR/docker-compose.yml"
|
||||
cp infra/Dockerfile.* "$INFRA_DIR/"
|
||||
[ -f infra/prometheus.yml ] && cp infra/prometheus.yml "$INFRA_DIR/"
|
||||
|
||||
# ── 2. Build JARs (Maven runs in Docker — no JDK/Maven required on host) ──────
|
||||
log "Building JARs..."
|
||||
# GITHUB_WORKSPACE is set by act_runner; work dir is mounted at the same host path.
|
||||
BUILD_ROOT="${GITHUB_WORKSPACE:-$(pwd)}"
|
||||
docker run --rm \
|
||||
-v "${BUILD_ROOT}:/workspace" \
|
||||
-v "/home/deploy/gitea-runner/.m2:/root/.m2" \
|
||||
-w /workspace \
|
||||
maven:3.9-eclipse-temurin-21 \
|
||||
mvn clean package -DskipTests -q
|
||||
|
||||
# ── 3. Build Docker images ────────────────────────────────────────────────────
|
||||
log "Building Docker images..."
|
||||
docker build -f infra/Dockerfile.registry -t apix-registry:latest . -q
|
||||
docker build -f infra/Dockerfile.portal -t apix-portal:latest . -q
|
||||
docker build -f infra/Dockerfile.demo -t apix-demo:latest . -q
|
||||
docker build -f infra/Dockerfile.spider -t apix-spider:latest . -q
|
||||
|
||||
# ── 4. Rolling restart: a-stack (registry-a → portal-a + demo-a) ─────────────
|
||||
log "Rolling restart: a-stack..."
|
||||
$COMPOSE up -d --no-deps --force-recreate registry-a
|
||||
wait_healthy "registry-a"
|
||||
|
||||
$COMPOSE up -d --no-deps --force-recreate portal-a demo-a
|
||||
wait_healthy "portal-a"
|
||||
wait_healthy "demo-a"
|
||||
|
||||
# ── 5. Rolling restart: b-stack ───────────────────────────────────────────────
|
||||
log "Rolling restart: b-stack..."
|
||||
$COMPOSE up -d --no-deps --force-recreate registry-b
|
||||
wait_healthy "registry-b"
|
||||
|
||||
$COMPOSE up -d --no-deps --force-recreate portal-b demo-b
|
||||
wait_healthy "portal-b"
|
||||
wait_healthy "demo-b"
|
||||
|
||||
# ── 6. Spider (cron job — restart acceptable) ─────────────────────────────────
|
||||
log "Restarting spider..."
|
||||
$COMPOSE up -d --no-deps --force-recreate spider
|
||||
|
||||
# ── 7. Reload Caddy ───────────────────────────────────────────────────────────
|
||||
log "Reloading Caddy..."
|
||||
docker exec infra-caddy-1 caddy reload --config /etc/caddy/Caddyfile
|
||||
|
||||
log "Deploy complete. All services updated with zero downtime."
|
||||
Reference in New Issue
Block a user