357 lines
9.0 KiB
Markdown
357 lines
9.0 KiB
Markdown
# NitroKite Deployment
|
|
|
|
Simple deployment workflow using Ansible + Gitea Actions + Blue-Green deployment.
|
|
|
|
## Overview
|
|
|
|
```
|
|
┌──────────────┐
|
|
│ 1. Ansible │ Run once to set up infrastructure
|
|
│ Setup │ (Gitea, PostgreSQL, Runners, Nginx, Scripts)
|
|
└──────┬───────┘
|
|
│
|
|
┌──────▼───────┐
|
|
│ 2. Push Code │ Push to Gitea triggers automatic build
|
|
│ to Gitea │ - Backend (Go API)
|
|
│ │ - Frontend (React App)
|
|
└──────┬───────┘
|
|
│
|
|
┌──────▼───────┐
|
|
│ 3. Promote │ Manual promotion to production
|
|
│ to Prod │ (sudo promote.sh <commit-sha>)
|
|
└──────────────┘
|
|
```
|
|
|
|
## Architecture
|
|
|
|
### Deployed Applications
|
|
|
|
1. **Marketing Website** - Static Astro site
|
|
- Domain: https://nitrokite.com
|
|
- Directory: `/var/www/html/`
|
|
- Deployment: Manual via Ansible `synchronize` task
|
|
|
|
2. **Frontend App** - React TypeScript dashboard
|
|
- Domain: https://app.nitrokite.com
|
|
- Directory: `/var/www/app/`
|
|
- Deployment: Automated via Gitea Actions → staging → promotion
|
|
- Features: TanStack Query, Router, Forms, shadcn/ui
|
|
|
|
3. **Backend API** - Go Gin server
|
|
- Domain: https://api.nitrokite.com
|
|
- Directory: `/opt/api-artifacts/current/`
|
|
- Deployment: Blue-Green with systemd service
|
|
- Database: PostgreSQL with separate user/database
|
|
|
|
4. **Gitea** - Git hosting + CI/CD
|
|
- Domain: https://git.nitrokite.com
|
|
- Runners: 5 parallel runners in Docker containers
|
|
|
|
## 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/nitrokite.git
|
|
```
|
|
|
|
## Daily Deployment Workflow
|
|
|
|
### Deploy New Version
|
|
|
|
```bash
|
|
# 1. Push code (triggers both backend & frontend builds)
|
|
git push production main
|
|
|
|
# 2. Wait for build (check Gitea → Actions)
|
|
# - Builds Go backend binary
|
|
# - Builds React frontend bundle
|
|
# - Stages backend to inactive slot (blue/green)
|
|
# - Stages frontend to /opt/frontend-artifacts/staging/<commit-sha>
|
|
|
|
# 3. SSH to VPS and promote (pass the commit SHA)
|
|
ssh ubuntu@your-vps-ip
|
|
sudo /opt/api-artifacts/scripts/promote.sh <commit-sha>
|
|
# Type 'yes' to confirm
|
|
|
|
# This will:
|
|
# - Switch backend to new version (systemd reload)
|
|
# - Deploy frontend to /var/www/app/ (nginx serves it)
|
|
# - Run health checks
|
|
# - Backup previous versions
|
|
|
|
# 4. Verify
|
|
curl https://api.nitrokite.com/hello
|
|
curl https://api.nitrokite.com/version
|
|
curl https://app.nitrokite.com # Frontend should load
|
|
```
|
|
|
|
**Note:** Get the commit SHA from the Gitea Actions output or via `git rev-parse HEAD`
|
|
|
|
### Rollback
|
|
|
|
If something goes wrong after promotion:
|
|
|
|
```bash
|
|
ssh ubuntu@your-vps-ip
|
|
sudo /opt/api-artifacts/scripts/rollback.sh
|
|
```
|
|
|
|
Instantly reverts the backend to the previous deployment. For frontend, restore from backup:
|
|
```bash
|
|
# List backups
|
|
ls -l /opt/frontend-artifacts/backups/
|
|
|
|
# Restore specific backup
|
|
sudo rm -rf /var/www/app/*
|
|
sudo cp -r /opt/frontend-artifacts/backups/<timestamp>/* /var/www/app/
|
|
sudo chown -R www-data:www-data /var/www/app
|
|
```
|
|
|
|
## VPS Directory Structure
|
|
|
|
```
|
|
/opt/api-artifacts/
|
|
├── builds/
|
|
│ ├── abc123.../ # Previous backend build
|
|
│ └── def456.../ # Current backend 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 (now handles frontend)
|
|
│ ├── rollback.sh # Emergency rollback
|
|
│ ├── health-check.sh # Health validation
|
|
│ └── migrate.sh # Database migrations
|
|
└── config/
|
|
└── .env.production # Environment variables
|
|
|
|
/opt/frontend-artifacts/
|
|
├── staging/
|
|
│ ├── abc123.../ # Built frontend for commit abc123
|
|
│ └── def456.../ # Built frontend for commit def456
|
|
└── backups/
|
|
└── 20251225_120000/ # Backup of previous frontend
|
|
|
|
/var/www/
|
|
├── html/ # Marketing website (nitrokite.com)
|
|
└── app/ # Frontend app (app.nitrokite.com)
|
|
```
|
|
|
|
## Common Tasks
|
|
|
|
### View Service Logs
|
|
|
|
```bash
|
|
# Backend API logs
|
|
journalctl -u api-server -f
|
|
|
|
# Nginx logs (frontend & marketing)
|
|
tail -f /var/log/nginx/access.log
|
|
tail -f /var/log/nginx/error.log
|
|
|
|
# Last 100 lines
|
|
journalctl -u api-server -n 100
|
|
```
|
|
|
|
### Check Active Deployment
|
|
|
|
```bash
|
|
# Backend: See which slot is active
|
|
readlink /opt/api-artifacts/current
|
|
|
|
# Backend: Full build path
|
|
readlink -f /opt/api-artifacts/current
|
|
|
|
# Frontend: Check current files
|
|
ls -lh /var/www/app/
|
|
|
|
# Frontend: Check staging builds available
|
|
ls -l /opt/frontend-artifacts/staging/
|
|
```
|
|
|
|
### 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
|