# MemberSyncPro Deployment Simple deployment workflow using Ansible + Gitea Actions + Blue-Green deployment. ## Overview ``` ┌──────────────┐ │ 1. Ansible │ Run once to set up infrastructure │ Setup │ (Gitea, PostgreSQL, Runners, Scripts) └──────┬───────┘ │ ┌──────▼───────┐ │ 2. Push Code │ Push to Gitea triggers automatic build │ to Gitea │ and staging └──────┬───────┘ │ ┌──────▼───────┐ │ 3. Promote │ Manual promotion to production │ to Prod │ (sudo promote.sh) └──────────────┘ ``` ## One-Time Setup ### 1. Prepare Ansible Inventory ```bash cd deployment/ansible cp hosts.ini.example hosts.ini ``` Edit `hosts.ini`: ```ini [git] your-vps-ip ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/your-key.pem ansible_python_interpreter=/usr/bin/python3 ``` ### 2. Set Database Passwords ```bash export POSTGRES_ADMIN_PASSWORD="your-strong-password" export API_SERVER_DB_PASSWORD="your-api-db-password" ``` ### 3. Run Ansible Playbook ```bash cd deployment/ansible # Test connection ansible -i hosts.ini git -m ping # Deploy everything ansible-playbook -i hosts.ini site.yml ``` This sets up: - Gitea server (accessible at `http://your-vps-ip`) - PostgreSQL databases (gitea + apiserver) - 5 Gitea Actions runners - Nginx reverse proxy - API server deployment infrastructure at `/opt/api-artifacts/` - Systemd service for the API server ### 4. Create Gitea Repository 1. Visit `http://your-vps-ip` 2. Create admin account (first-time setup) 3. Create repository: `membersyncpro` ### 5. Add Git Remote ```bash git remote add production http://your-vps-ip/your-username/membersyncpro.git ``` ## Daily Deployment Workflow ### Deploy New Version ```bash # 1. Push code git push production main # 2. Wait for build (check Gitea → Actions) # - Builds binary # - Runs tests # - Stages to inactive slot (blue/green) # 3. SSH to VPS and promote ssh ubuntu@your-vps-ip sudo /opt/api-artifacts/scripts/promote.sh # Type 'yes' to confirm # 4. Verify curl http://localhost:8080/hello curl http://localhost:8080/version ``` ### Rollback If something goes wrong after promotion: ```bash ssh ubuntu@your-vps-ip sudo /opt/api-artifacts/scripts/rollback.sh ``` Instantly reverts to the previous deployment. ## VPS Directory Structure ``` /opt/api-artifacts/ ├── builds/ │ ├── abc123.../ # Previous build │ └── def456.../ # Current build ├── blue -> builds/abc123/ # Previous deployment ├── green -> builds/def456/ # Current deployment ├── current -> green # Active (systemd uses this) ├── scripts/ │ ├── deploy.sh # Auto-called by CI │ ├── promote.sh # Manual promotion │ ├── rollback.sh # Emergency rollback │ ├── health-check.sh # Health validation │ └── migrate.sh # Database migrations └── config/ └── .env.production # Environment variables ``` ## Common Tasks ### View Service Logs ```bash # Follow logs journalctl -u api-server -f # Last 100 lines journalctl -u api-server -n 100 ``` ### Check Active Deployment ```bash # See which slot is active readlink /opt/api-artifacts/current # Full build path readlink -f /opt/api-artifacts/current ``` ### Check Runners ```bash # All runners systemctl status gitea-runner-{1..5} # Individual runner logs journalctl -u gitea-runner-1 -f ``` ### List All Builds ```bash ls -lh /opt/api-artifacts/builds/ ``` ### Clean Old Builds ```bash # Keep only last 10 builds cd /opt/api-artifacts/builds ls -t | tail -n +11 | xargs -I {} sudo rm -rf {} ``` ## Configuration ### Environment Variables (on VPS) Edit `/opt/api-artifacts/config/.env.production`: ```bash DATABASE_URL=postgresql://apiserver:PASSWORD@localhost:5432/apiserver?sslmode=disable AUTO_MIGRATE=false PORT=8080 GIN_MODE=release ``` ### Ansible Variables Edit `deployment/ansible/site.yml`: ```yaml vars: create_separate_apiserver_db: true # Separate vs shared DB runner_count: 5 # Number of runners gitea_version: "1.23.3" # Gitea version ``` ## Blue-Green Deployment Flow 1. **Current state:** `current -> blue` (active) 2. **New push:** Build creates `builds/new456/` 3. **Staging:** `green -> builds/new456/` (inactive) 4. **Migrations:** Run on green slot 5. **Promotion:** `current -> green` (switch!) 6. **Restart:** Systemd loads from current 7. **Rollback available:** Blue slot still has previous build ## Troubleshooting ### Build Not Triggering ```bash # Check runners systemctl status gitea-runner-1 # Runner logs journalctl -u gitea-runner-1 -f ``` ### Deployment Fails ```bash # Check deploy script logs journalctl -u api-server -n 50 # Verify permissions ls -la /opt/api-artifacts/ # Check database connection cat /opt/api-artifacts/config/.env.production ``` ### Service Won't Start ```bash # Service logs journalctl -u api-server -n 50 # Verify binary exists ls -la /opt/api-artifacts/current/api-server # Check symlinks readlink /opt/api-artifacts/current readlink /opt/api-artifacts/blue readlink /opt/api-artifacts/green ``` ## Files Reference - `ansible/site.yml` - Complete infrastructure setup playbook - `ansible/hosts.ini` - Your VPS inventory - `scripts/` - Deployment scripts (copied to VPS by Ansible) - `systemd/api-server.service` - Service configuration - `.gitea/workflows/build-deploy.yml` - CI/CD workflow ## Ansible Playbook Details The `site.yml` playbook handles: 1. **Package installation:** Git, Nginx, PostgreSQL, Docker 2. **Gitea setup:** Binary download, database, systemd service 3. **Database provisioning:** Admin user, API server user, databases 4. **Runner deployment:** 5 self-hosted runners with auto-registration 5. **API infrastructure:** User, directories, scripts, systemd service 6. **Nginx configuration:** Reverse proxy for Gitea 7. **Firewall:** UFW with SSH, HTTP, PostgreSQL ports Run it multiple times safely (idempotent). ## Security Notes - Change default passwords (use environment variables) - Use SSH keys for VPS access - Keep `.env.production` file permissions at 600 - Review UFW firewall rules - Consider adding SSL/TLS with Let's Encrypt - PostgreSQL is exposed on 0.0.0.0 - restrict if not needed externally ## Next Steps After basic deployment: 1. Add SSL/TLS certificates (Let's Encrypt) 2. Set up monitoring (Prometheus/Grafana) 3. Configure alerting for deployment failures 4. Automate PostgreSQL backups 5. Add cron job to clean old builds