# Rolling Update Deployment Guide

This guide explains how to perform zero-downtime deployments using the rolling update strategy.

## Overview

The rolling update approach allows you to deploy new backend code without any downtime for users. Here's how it works:

1. **Build** new backend image while old container is still running
2. **Start** new container on port 8082 (old one stays on 8080)
3. **Health check** new container to ensure it's ready
4. **Switch** Nginx to point to new container (zero downtime)
5. **Stop** old container after grace period

## Architecture

```
┌─────────────┐
│   Nginx     │  (Port 80/443)
│  (Host)     │
└──────┬──────┘
       │
       ├───> Backend (Port 8080) - Primary
       └───> Backend-New (Port 8082) - Standby (during deployment)
```

## Prerequisites

1. **Nginx running on host** (not in Docker)
2. **Backend containers** managed by Docker Compose
3. **Health check endpoint** available at `/actuator/health/readiness`
4. **Sufficient memory** for two backend containers during deployment (~24GB)

## Quick Start

### 1. Make Script Executable

```bash
cd /opt/app/backend/lottery-be
chmod +x scripts/rolling-update.sh
```

### 2. Run Deployment

```bash
# Load database password (if not already set)
source scripts/load-db-password.sh

# Run rolling update
sudo ./scripts/rolling-update.sh
```

That's it! The script handles everything automatically.

## What the Script Does

1. **Checks prerequisites**:
   - Verifies Docker and Nginx are available
   - Ensures primary backend is running
   - Loads database password

2. **Builds new image**:
   - Builds backend-new service
   - Uses Docker Compose build cache for speed

3. **Starts new container**:
   - Starts `lottery-backend-new` on port 8082
   - Waits for container initialization

4. **Health checks**:
   - Checks `/actuator/health/readiness` endpoint
   - Retries up to 30 times (60 seconds total)
   - Fails deployment if health check doesn't pass

5. **Updates Nginx**:
   - Backs up current Nginx config
   - Updates upstream to point to port 8082
   - Sets old backend (8080) as backup
   - Tests Nginx configuration

6. **Reloads Nginx**:
   - Uses `systemctl reload nginx` (zero downtime)
   - Traffic immediately switches to new backend

7. **Stops old container**:
   - Waits 10 seconds grace period
   - Stops old backend container
   - Old container can be removed or kept for rollback

## Manual Steps (If Needed)

If you prefer to do it manually or need to troubleshoot:

### Step 1: Build New Image

```bash
cd /opt/app/backend/lottery-be
source scripts/load-db-password.sh
docker-compose -f docker-compose.prod.yml --profile rolling-update build backend-new
```

### Step 2: Start New Container

```bash
docker-compose -f docker-compose.prod.yml --profile rolling-update up -d backend-new
```

### Step 3: Health Check

```bash
# Wait for container to be ready
sleep 10

# Check health
curl http://127.0.0.1:8082/actuator/health/readiness

# Check logs
docker logs lottery-backend-new
```

### Step 4: Update Nginx

```bash
# Backup config
sudo cp /etc/nginx/conf.d/lottery.conf /etc/nginx/conf.d/lottery.conf.backup

# Edit config
sudo nano /etc/nginx/conf.d/lottery.conf
```

Change upstream from:
```nginx
upstream lottery_backend {
    server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
}
```

To:
```nginx
upstream lottery_backend {
    server 127.0.0.1:8082 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:8080 backup;
}
```

### Step 5: Reload Nginx

```bash
# Test config
sudo nginx -t

# Reload (zero downtime)
sudo systemctl reload nginx
```

### Step 6: Stop Old Container

```bash
# Wait for active connections to finish
sleep 10

# Stop old container
docker-compose -f docker-compose.prod.yml stop backend
```

## Rollback Procedure

If something goes wrong, you can quickly rollback:

### Automatic Rollback

The script automatically rolls back if:
- Health check fails
- Nginx config test fails
- Nginx reload fails

### Manual Rollback

```bash
# 1. Restore Nginx config
sudo cp /etc/nginx/conf.d/lottery.conf.backup /etc/nginx/conf.d/lottery.conf
sudo systemctl reload nginx

# 2. Start old backend (if stopped)
cd /opt/app/backend/lottery-be
docker-compose -f docker-compose.prod.yml start backend

# 3. Stop new backend
docker-compose -f docker-compose.prod.yml --profile rolling-update stop backend-new
docker-compose -f docker-compose.prod.yml --profile rolling-update rm -f backend-new
```

## Configuration

### Health Check Settings

Edit `scripts/rolling-update.sh` to adjust:

```bash
HEALTH_CHECK_RETRIES=30      # Number of retries
HEALTH_CHECK_INTERVAL=2       # Seconds between retries
GRACE_PERIOD=10              # Seconds to wait before stopping old container
```

### Nginx Upstream Settings

Edit `/etc/nginx/conf.d/lottery.conf`:

```nginx
upstream lottery_backend {
    server 127.0.0.1:8082 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:8080 backup;  # Old backend as backup
    keepalive 32;
}
```

## Monitoring

### During Deployment

```bash
# Watch container status
watch -n 1 'docker ps | grep lottery-backend'

# Monitor new backend logs
docker logs -f lottery-backend-new

# Check Nginx access logs
sudo tail -f /var/log/nginx/access.log

# Monitor memory usage
free -h
docker stats --no-stream
```

### After Deployment

```bash
# Verify new backend is serving traffic
curl http://localhost/api/health

# Check container status
docker ps | grep lottery-backend

# Verify Nginx upstream
curl http://localhost/actuator/health
```

## Troubleshooting

### Health Check Fails

```bash
# Check new container logs
docker logs lottery-backend-new

# Check if container is running
docker ps | grep lottery-backend-new

# Test health endpoint directly
curl -v http://127.0.0.1:8082/actuator/health/readiness

# Check database connection
docker exec lottery-backend-new wget -q -O- http://localhost:8080/actuator/health
```

### Nginx Reload Fails

```bash
# Test Nginx config
sudo nginx -t

# Check Nginx error logs
sudo tail -f /var/log/nginx/error.log

# Verify upstream syntax
sudo nginx -T | grep -A 5 upstream
```

### Memory Issues

If you run out of memory during deployment:

```bash
# Check memory usage
free -h
docker stats --no-stream

# Option 1: Reduce heap size temporarily
# Edit docker-compose.prod.yml, change JAVA_OPTS to use 8GB heap

# Option 2: Stop other services temporarily
docker stop lottery-phpmyadmin  # If not needed
```

### Old Container Won't Stop

```bash
# Force stop
docker stop lottery-backend

# If still running, kill it
docker kill lottery-backend

# Remove container
docker rm lottery-backend
```

## Best Practices

1. **Test in staging first** - Always test the deployment process in a staging environment

2. **Monitor during deployment** - Watch logs and metrics during the first few deployments

3. **Keep backups** - The script automatically backs up Nginx config, but keep your own backups too

4. **Database migrations** - Ensure migrations are backward compatible or run them separately

5. **Gradual rollout** - For major changes, consider deploying during low-traffic periods

6. **Health checks** - Ensure your health check endpoint properly validates all dependencies

7. **Graceful shutdown** - Spring Boot graceful shutdown (30s) allows active requests to finish

## Performance Considerations

- **Build time**: First build takes longer, subsequent builds use cache
- **Memory**: Two containers use ~24GB during deployment (brief period)
- **Network**: No network interruption, Nginx handles the switch seamlessly
- **Database**: No impact, both containers share the same database

## Security Notes

- New container uses same secrets and configuration as old one
- No exposure of new port to internet (only localhost)
- Nginx handles all external traffic
- Health checks are internal only

## Next Steps

After successful deployment:

1. ✅ Monitor new backend for errors
2. ✅ Verify all endpoints are working
3. ✅ Check application logs
4. ✅ Remove old container image (optional): `docker image prune`

## Support

If you encounter issues:

1. Check logs: `docker logs lottery-backend-new`
2. Check Nginx: `sudo nginx -t && sudo tail -f /var/log/nginx/error.log`
3. Rollback if needed (see Rollback Procedure above)
4. Review this guide's Troubleshooting section

