Complete guide to CI/CD pipelines, blue/green deployment, and procedures.
| Server | Role | IP | SSH Alias |
|---|---|---|---|
| rpm-prod-new | Production App | 204.168.180.36 | ssh rpm-prod-new |
| rpm-prod-new | Test/Staging | 204.168.180.36 | ssh rpm-prod-new |
| onlyoffice | Document Server | 204.168.170.0 | ssh rpm-onlyoffice |
Developer Push
│
▼
┌─────────────────┐
│ CI Pipeline │ Lint, Test, Build, Security Scan
│ (ci.yml) │
└────────┬────────┘
│ Pass
▼
┌─────────────────┐
│ Build & Push │ Docker image → GHCR
│ to GHCR │ ghcr.io/velastra-uk/rapid-pm:sha
└────────┬────────┘
│ Auto trigger
▼
┌─────────────────┐
│ Deploy to Test │ Blue/green on rapidpm.uk
│ (automatic) │
└────────┬────────┘
│ Manual trigger
▼
┌─────────────────┐
│ Deploy to Prod │ Backup → Blue/green → Health check
│ (manual) │ Production: rapidpm.uk
└─────────────────┘
| Workflow | Trigger | Target |
|---|---|---|
ci.yml |
Push/PR to master | Quality checks + build image |
deploy-test.yml |
After CI passes | Test server |
deploy-prod.yml |
Manual | Production |
e2e.yml |
Manual | E2E test suite |
┌─────────────────────────────────────────────────────────────┐
│ CI PIPELINE STAGES │
├─────────────────────────────────────────────────────────────┤
│ │
│ STAGE 1: BACKEND CHECKS (parallel) │
│ ┌─────────┬─────────┬─────────┬─────────┐ │
│ │ Ruff │ MyPy │ Bandit │pip-audit│ │
│ │ (lint) │ (types) │(security)│ (vulns) │ │
│ └─────────┴─────────┴─────────┴─────────┘ │
│ │
│ STAGE 2: FRONTEND CHECKS │
│ ┌─────────┬─────────────┬─────────────┐ │
│ │ ESLint │ ng build │ ng test │ │
│ │ │ (compile) │ (coverage) │ │
│ └─────────┴─────────────┴─────────────┘ │
│ │
│ STAGE 3: SECURITY SCANS (parallel) │
│ ┌─────────┬─────────┬──────────┬─────────┐ │
│ │Hadolint │ Trivy │TruffleHog│Gitleaks │ │
│ │(Docker) │(images) │(secrets) │(secrets)│ │
│ └─────────┴─────────┴──────────┴─────────┘ │
│ │
│ STAGE 4: BUILD & PUSH │
│ ┌─────────────────────────────────────────────────┐ │
│ │ docker build → ghcr.io/velastra-uk/rapid-pm:$SHA│ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Blue/green deployment enables zero-downtime releases with instant rollback.
BEFORE DEPLOYMENT:
┌─────────────────────────────────────────────────────────────┐
│ │
│ nginx ──────────────▶ app-blue (v1.0) ✓ ACTIVE │
│ app-green (v0.9) ○ STANDBY │
│ │
└─────────────────────────────────────────────────────────────┘
DURING DEPLOYMENT:
┌─────────────────────────────────────────────────────────────┐
│ │
│ nginx ──────────────▶ app-blue (v1.0) ✓ ACTIVE │
│ app-green (v1.1) ⟳ STARTING │
│ │
│ 1. Pull new image │
│ 2. Start app-green with v1.1 │
│ 3. Wait for health check │
│ │
└─────────────────────────────────────────────────────────────┘
AFTER SWITCH:
┌─────────────────────────────────────────────────────────────┐
│ │
│ nginx ──────────────▶ app-green (v1.1) ✓ ACTIVE │
│ app-blue (v1.0) ○ STANDBY │
│ │
│ 4. Update active-backend.conf │
│ 5. nginx -s reload (instant switch) │
│ 6. Stop app-blue (keep for rollback) │
│ │
└─────────────────────────────────────────────────────────────┘
active-backend.conf contains current backend:
set $backend "green";
nginx.conf uses map for routing:
map $backend $upstream_name {
"blue" "app_blue";
"green" "app_green";
}
location /api {
proxy_pass http://$upstream_name;
}
Health check via Unix socket:
docker exec app-green curl --unix-socket /sock/rapidpm-green.sock \
http://localhost/api/health
# 1. Start Docker containers
./scripts/start-dev.sh
# OR: docker-compose -f docker-compose.prod.yml up -d
# 2. Start Angular dev server (hot reload)
cd rpm-ui
ng serve --proxy-config proxy.conf.json --port 4200
# Access:
# - Frontend: http://localhost:4200 (hot reload)
# - Backend: http://localhost:8080
┌─────────────────────────────────────────────────────────────┐
│ LOCAL DEVELOPMENT │
├─────────────────────────────────────────────────────────────┤
│ │
│ Browser ────▶ localhost:4200 ────▶ Angular Dev Server │
│ │ │ │
│ │ /api/* │ proxy │
│ ▼ ▼ │
│ localhost:8080 ◀──────────── │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Docker: nginx → app → MySQL (204.168.180.36) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
| Service | Image | Port |
|---|---|---|
| nginx | nginx:latest | 8080 (external) |
| app | rapidpm | unix socket |
| worker | rapidpm | - |
| redis | redis:5.0-alpine | 6379 |
| flyway | flyway/flyway:10 | - (runs once) |
Test deployment triggers automatically after CI passes on master:
Push to master → CI passes → deploy-test.yml triggers → Blue/green deploy
# Via GitHub CLI
gh workflow run deploy-test.yml
# Or via GitHub UI
# Actions → Deploy to Test → Run workflow
./scripts/deploy-blue-green.sh deploymaster# Via GitHub CLI
gh workflow run deploy-prod.yml \
--field confirm_deploy=deploy \
--field skip_backup=false
# Or via GitHub UI:
# Actions → Deploy to Production → Run workflow → Type "deploy"
┌─────────────────────────────────────────────────────────────┐
│ PRODUCTION DEPLOYMENT │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. VALIDATION │
│ └── Check "deploy" typed, verify test deploy OK │
│ │
│ 2. DATABASE BACKUP │
│ └── mysqldump → artifacts/backups/ │
│ │
│ 3. PULL NEW IMAGE │
│ └── docker pull ghcr.io/velastra-uk/rapid-pm:$SHA │
│ │
│ 4. BLUE/GREEN DEPLOYMENT │
│ └── ./scripts/deploy-blue-green.sh deploy │
│ │
│ 5. HEALTH CHECKS │
│ ├── HTTP 200 from /api/health │
│ ├── Container count ≥ 2 │
│ └── Redis PING → PONG │
│ │
│ 6. ON FAILURE: AUTO-ROLLBACK │
│ └── ./scripts/deploy-blue-green.sh rollback │
│ │
└─────────────────────────────────────────────────────────────┘
# On server
cd /home/deploy/rapid-pm # (test) or /root/rapid-pm (prod)
./scripts/deploy-blue-green.sh rollback
BEFORE ROLLBACK:
┌─────────────────────────────────────────────────────────────┐
│ nginx → app-green (v1.1 - BROKEN) ✓ ACTIVE │
│ app-blue (v1.0 - WORKING) ○ STANDBY │
└─────────────────────────────────────────────────────────────┘
AFTER ROLLBACK:
┌─────────────────────────────────────────────────────────────┐
│ nginx → app-blue (v1.0 - WORKING) ✓ ACTIVE │
│ app-green (v1.1) ○ STOPPED │
└─────────────────────────────────────────────────────────────┘
If script fails:
# 1. Check current backend
cat active-backend.conf
# Shows: set $backend "green";
# 2. Switch to other backend
echo 'set $backend "blue";' > active-backend.conf
# 3. Reload nginx
docker exec rapid-pm-nginx-1 nginx -s reload
# 4. Verify
curl https://rapidpm.uk/api/health
Symptom: "Health check failed after 30 retries"
Diagnosis:
1. Check app logs: docker logs rapid-pm-app-green-1
2. Check if app started: docker ps
3. Check socket exists: ls -la sock/
Fix: Usually startup error - check logs
Symptom: "unauthorized: authentication required"
Fix:
echo $GHCR_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
Symptom: "nginx: [error] open() ... failed"
Diagnosis: docker exec rapid-pm-nginx-1 nginx -t
Fix: Check active-backend.conf syntax
# All containers
docker-compose logs -f
# Specific service
docker-compose logs -f app-green
# Last 100 lines
docker-compose logs --tail=100 app-green
# Check container status
docker ps
# Active backend
cat active-backend.conf
# API health
curl http://localhost:8080/api/health
# Local Development
./scripts/start-dev.sh
cd rpm-ui && ng serve --proxy-config proxy.conf.json --port 4200
# Build Frontend
cd rpm-ui && ng build --configuration production
cp -r dist/* ../static/
# Deploy to Test (manual)
gh workflow run deploy-test.yml
# Deploy to Production
gh workflow run deploy-prod.yml --field confirm_deploy=deploy
# Rollback
./scripts/deploy-blue-green.sh rollback
| File | Purpose |
|---|---|
.github/workflows/ci.yml |
CI quality gate |
.github/workflows/deploy-test.yml |
Test deployment |
.github/workflows/deploy-prod.yml |
Production deployment |
docker-compose.deploy.yml |
Server deployment |
scripts/deploy-blue-green.sh |
Blue/green orchestration |
active-backend.conf |
Current active backend |
See also: Infrastructure for server details, Tools for development tools