425 lines
No EOL
9.3 KiB
Markdown
425 lines
No EOL
9.3 KiB
Markdown
# Deployment Guide — go-first / URPI
|
|
|
|
This guide covers deploying the go-first application to production at **https://www.urpi.be**.
|
|
|
|
---
|
|
|
|
## 🏗️ Architecture Overview
|
|
|
|
```
|
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
│ Users │────▶│ NGINX │────▶│ Go App │
|
|
│ (HTTPS) │ │ (SSL/TLS) │ │ (Port 8080)│
|
|
└─────────────┘ └─────────────┘ └──────┬──────┘
|
|
│
|
|
┌─────────────┐ ┌──────▼──────┐
|
|
│ PostgreSQL │◀───▶│ Redis │
|
|
│ (Port 5432)│ │ (Port 6379)│
|
|
└─────────────┘ └─────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 📋 Prerequisites
|
|
|
|
### Server Requirements
|
|
- **OS**: Ubuntu 22.04+ / Debian 12+ / Alpine Linux
|
|
- **CPU**: 2+ vCPUs
|
|
- **RAM**: 4GB+ (2GB minimum for app + DB)
|
|
- **Disk**: 20GB+ SSD
|
|
- **Network**: Public IPv4/IPv6, ports 80, 443, 22
|
|
|
|
### Software
|
|
- Docker 24+ & Docker Compose 2+
|
|
- Git
|
|
- Certbot (Let's Encrypt)
|
|
- (Optional) kubectl for Kubernetes
|
|
|
|
---
|
|
|
|
## 🔧 Server Setup
|
|
|
|
### 1. Initial Server Hardening
|
|
|
|
```bash
|
|
# Update system
|
|
sudo apt update && sudo apt upgrade -y
|
|
|
|
# Create deploy user
|
|
sudo adduser deploy --gecos "" --disabled-password
|
|
sudo usermod -aG docker deploy
|
|
sudo usermod -aG sudo deploy
|
|
|
|
# Setup SSH keys (from your local machine)
|
|
ssh-copy-id deploy@your-server-ip
|
|
|
|
# Configure firewall
|
|
sudo ufw allow 22/tcp # SSH
|
|
sudo ufw allow 80/tcp # HTTP
|
|
sudo ufw allow 443/tcp # HTTPS
|
|
sudo ufw enable
|
|
```
|
|
|
|
### 2. Install Docker
|
|
|
|
```bash
|
|
# Install Docker
|
|
curl -fsSL https://get.docker.com | sh
|
|
|
|
# Install Docker Compose
|
|
sudo apt install -y docker-compose-plugin
|
|
|
|
# Verify
|
|
docker --version
|
|
docker compose version
|
|
```
|
|
|
|
### 3. Install Certbot
|
|
|
|
```bash
|
|
sudo apt install -y certbot python3-certbot-nginx
|
|
```
|
|
|
|
---
|
|
|
|
## 📦 Application Deployment
|
|
|
|
### 1. Clone Repository
|
|
|
|
```bash
|
|
cd /opt
|
|
sudo git clone https://github.com/dnoulet/go-first.git
|
|
sudo chown -R deploy:deploy go-first
|
|
cd go-first
|
|
```
|
|
|
|
### 2. Configure Environment
|
|
|
|
```bash
|
|
# Copy production template
|
|
cp .env.production.example .env.production
|
|
|
|
# Edit with your values (use strong passwords!)
|
|
vim .env.production
|
|
|
|
# Required changes:
|
|
# - DATABASE_PASSWORD: Strong random password
|
|
# - JWT_SECRET: 32+ char random string
|
|
# - EMAIL_*: Your SMTP credentials
|
|
```
|
|
|
|
### 3. Generate SSL Certificates
|
|
|
|
```bash
|
|
# Stop any service on port 80
|
|
sudo systemctl stop nginx 2>/dev/null || true
|
|
|
|
# Get certificates
|
|
sudo certbot certonly --standalone \
|
|
-d www.urpi.be \
|
|
-d urpi.be \
|
|
--email daniel.noulet@urpi.be \
|
|
--agree-tos --non-interactive
|
|
|
|
# Certificates will be at:
|
|
# /etc/letsencrypt/live/www.urpi.be/fullchain.pem
|
|
# /etc/letsencrypt/live/www.urpi.be/privkey.pem
|
|
```
|
|
|
|
### 4. Setup NGINX SSL Directory
|
|
|
|
```bash
|
|
sudo mkdir -p /opt/go-first/deploy/ssl
|
|
sudo cp /etc/letsencrypt/live/www.urpi.be/fullchain.pem /opt/go-first/deploy/ssl/
|
|
sudo cp /etc/letsencrypt/live/www.urpi.be/privkey.pem /opt/go-first/deploy/ssl/
|
|
sudo chown -R deploy:deploy /opt/go-first/deploy/ssl
|
|
```
|
|
|
|
### 5. Deploy with Docker Compose
|
|
|
|
```bash
|
|
cd /opt/go-first
|
|
|
|
# Build and start
|
|
docker compose --env-file .env.production up -d --build
|
|
|
|
# Verify
|
|
docker compose --env-file .env.production ps
|
|
docker compose --env-file .env.production logs -f app
|
|
```
|
|
|
|
### 6. Verify Deployment
|
|
|
|
```bash
|
|
# Health check
|
|
curl -f https://www.urpi.be/api/v1/health
|
|
|
|
# Check frontend
|
|
curl -f https://www.urpi.be/ | grep -c "URPI"
|
|
```
|
|
|
|
---
|
|
|
|
## 🔐 SSL Certificate Renewal
|
|
|
|
### Automatic Renewal (systemd timer)
|
|
|
|
```bash
|
|
# Check certbot timer
|
|
sudo systemctl status certbot.timer
|
|
|
|
# Test renewal
|
|
sudo certbot renew --dry-run
|
|
```
|
|
|
|
### Manual Renewal Hook
|
|
|
|
```bash
|
|
# Create renewal hook
|
|
sudo tee /etc/letsencrypt/renewal-hooks/deploy/nginx-reload.sh > /dev/null <<'EOF'
|
|
#!/bin/bash
|
|
cp /etc/letsencrypt/live/www.urpi.be/fullchain.pem /opt/go-first/deploy/ssl/
|
|
cp /etc/letsencrypt/live/www.urpi.be/privkey.pem /opt/go-first/deploy/ssl/
|
|
docker compose -f /opt/go-first/docker-compose.yml --env-file /opt/go-first/.env.production exec nginx nginx -s reload
|
|
EOF
|
|
|
|
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/nginx-reload.sh
|
|
```
|
|
|
|
---
|
|
|
|
## 🗄️ Database Management
|
|
|
|
### Backup
|
|
|
|
```bash
|
|
# Create backup script
|
|
cat > /opt/go-first/backup.sh <<'EOF'
|
|
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
BACKUP_DIR="/opt/backups/go-first"
|
|
DATE=$(date +%Y%m%d_%H%M%S)
|
|
mkdir -p "$BACKUP_DIR"
|
|
|
|
docker compose -f /opt/go-first/docker-compose.yml --env-file /opt/go-first/.env.production \
|
|
exec -T postgres pg_dump -U postgres go_first | gzip > "$BACKUP_DIR/go_first_$DATE.sql.gz"
|
|
|
|
# Keep last 30 days
|
|
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +30 -delete
|
|
|
|
echo "Backup saved to $BACKUP_DIR/go_first_$DATE.sql.gz"
|
|
EOF
|
|
|
|
chmod +x /opt/go-first/backup.sh
|
|
|
|
# Add to crontab (daily at 2 AM)
|
|
(crontab -l 2>/dev/null; echo "0 2 * * * /opt/go-first/backup.sh") | crontab -
|
|
```
|
|
|
|
### Restore
|
|
|
|
```bash
|
|
# Stop app
|
|
docker compose -f /opt/go-first/docker-compose.yml --env-file /opt/go-first/.env.production stop app
|
|
|
|
# Restore
|
|
gunzip -c /opt/backups/go-first/go_first_20240115_020000.sql.gz | \
|
|
docker compose -f /opt/go-first/docker-compose.yml --env-file /opt/go-first/.env.production exec -T postgres psql -U postgres go_first
|
|
|
|
# Start app
|
|
docker compose -f /opt/go-first/docker-compose.yml --env-file /opt/go-first/.env.production start app
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 Monitoring & Logging
|
|
|
|
### Log Aggregation (Optional: Loki + Promtail)
|
|
|
|
```yaml
|
|
# Add to docker-compose.yml
|
|
services:
|
|
promtail:
|
|
image: grafana/promtail:2.9
|
|
volumes:
|
|
- /var/log:/var/log
|
|
- /opt/go-first/deploy/promtail.yml:/etc/promtail/config.yml
|
|
depends_on:
|
|
- loki
|
|
|
|
loki:
|
|
image: grafana/loki:2.9
|
|
volumes:
|
|
- loki_data:/loki
|
|
```
|
|
|
|
### Health Checks
|
|
|
|
```bash
|
|
# Application health
|
|
curl https://www.urpi.be/api/v1/health
|
|
|
|
# Database
|
|
docker compose exec postgres pg_isready -U postgres
|
|
|
|
# Redis
|
|
docker compose exec redis redis-cli ping
|
|
|
|
# NGINX
|
|
docker compose exec nginx nginx -t
|
|
```
|
|
|
|
### Metrics (Optional: Prometheus + Grafana)
|
|
|
|
Add to `docker-compose.yml`:
|
|
```yaml
|
|
prometheus:
|
|
image: prom/prometheus:v2.47
|
|
volumes:
|
|
- ./deploy/prometheus.yml:/etc/prometheus/prometheus.yml
|
|
- prometheus_data:/prometheus
|
|
ports:
|
|
- "9090:9090"
|
|
|
|
grafana:
|
|
image: grafana/grafana:10.2
|
|
ports:
|
|
- "3000:3000"
|
|
volumes:
|
|
- grafana_data:/var/lib/grafana
|
|
```
|
|
|
|
---
|
|
|
|
## 🔄 Updates & Rollbacks
|
|
|
|
### Update Application
|
|
|
|
```bash
|
|
cd /opt/go-first
|
|
|
|
# Pull latest code
|
|
git pull origin main
|
|
|
|
# Rebuild and deploy
|
|
docker compose --env-file .env.production up -d --build --remove-orphans
|
|
|
|
# Verify
|
|
curl -f https://www.urpi.be/api/v1/health
|
|
```
|
|
|
|
### Rollback
|
|
|
|
```bash
|
|
# List images
|
|
docker images ghcr.io/dnoulet/go-first
|
|
|
|
# Rollback to previous tag
|
|
docker tag ghcr.io/dnoulet/go-first:previous-tag ghcr.io/dnoulet/go-first:latest
|
|
docker compose --env-file .env.production up -d --no-deps app
|
|
```
|
|
|
|
### Zero-Downtime Deploy (with multiple replicas)
|
|
|
|
```yaml
|
|
# In docker-compose.yml, add:
|
|
deploy:
|
|
replicas: 2
|
|
update_config:
|
|
parallelism: 1
|
|
delay: 10s
|
|
order: start-first
|
|
restart_policy:
|
|
condition: on-failure
|
|
```
|
|
|
|
---
|
|
|
|
## 🛡️ Security Checklist
|
|
|
|
- [ ] Strong DATABASE_PASSWORD (32+ chars)
|
|
- [ ] Strong JWT_SECRET (32+ chars, random)
|
|
- [ ] SMTP credentials secured
|
|
- [ ] Firewall: only 22, 80, 443 open
|
|
- [ ] SSH key-only authentication
|
|
- [ ] Fail2ban configured for SSH
|
|
- [ ] Regular security updates (`apt update && apt upgrade`)
|
|
- [ ] Docker images scanned for vulnerabilities (`docker scout cves`)
|
|
- [ ] NGINX security headers enabled
|
|
- [ ] Rate limiting configured
|
|
- [ ] CSP headers configured
|
|
- [ ] HSTS enabled
|
|
- [ ] Database not exposed publicly
|
|
- [ ] Redis not exposed publicly
|
|
- [ ] Backup encryption at rest
|
|
- [ ] Audit logging enabled
|
|
|
|
---
|
|
|
|
## 🚨 Troubleshooting
|
|
|
|
### Application Won't Start
|
|
|
|
```bash
|
|
# Check logs
|
|
docker compose --env-file .env.production logs app
|
|
|
|
# Common issues:
|
|
# - Port 8080 already in use
|
|
# - Database connection failed
|
|
# - Config file missing
|
|
# - Permission denied on volumes
|
|
```
|
|
|
|
### 502 Bad Gateway
|
|
|
|
```bash
|
|
# Check if app is healthy
|
|
docker compose --env-file .env.production ps app
|
|
|
|
# Check NGINX config
|
|
docker compose --env-file .env.production exec nginx nginx -t
|
|
|
|
# Check upstream
|
|
docker compose --env-file .env.production exec nginx curl -f http://app:8080/api/v1/health
|
|
```
|
|
|
|
### SSL Certificate Issues
|
|
|
|
```bash
|
|
# Check certificate validity
|
|
openssl x509 -in /etc/letsencrypt/live/www.urpi.be/fullchain.pem -text -noout
|
|
|
|
# Check NGINX SSL config
|
|
docker compose --env-file .env.production exec nginx nginx -T | grep ssl
|
|
```
|
|
|
|
### Database Connection Issues
|
|
|
|
```bash
|
|
# Test connection
|
|
docker compose --env-file .env.production exec postgres psql -U postgres -d go_first -c "SELECT 1;"
|
|
|
|
# Check credentials in .env.production match docker-compose.yml
|
|
```
|
|
|
|
---
|
|
|
|
## 📞 Support
|
|
|
|
For deployment issues:
|
|
- Check logs: `docker compose logs -f`
|
|
- Review this guide
|
|
- Contact: daniel.noulet@urpi.be
|
|
|
|
---
|
|
|
|
## 📝 Maintenance Windows
|
|
|
|
Recommended: **Sunday 02:00-04:00 UTC**
|
|
|
|
- System updates
|
|
- Docker image updates
|
|
- Database maintenance
|
|
- Certificate renewal verification |