598 lines
14 KiB
Markdown
598 lines
14 KiB
Markdown
# Production Runbook
|
|
|
|
**Piano Training Platform Operations Manual**
|
|
|
|
Quick reference guide for common operational tasks and incident response procedures.
|
|
|
|
---
|
|
|
|
## Quick Links
|
|
|
|
- [Emergency Procedures](#emergency-procedures)
|
|
- [Daily Operations](#daily-operations)
|
|
- [Deployment Procedures](#deployment-procedures)
|
|
- [Incident Response](#incident-response)
|
|
- [Maintenance Tasks](#maintenance-tasks)
|
|
|
|
---
|
|
|
|
## Emergency Procedures
|
|
|
|
### 🚨 CRITICAL: Site Down
|
|
|
|
**Symptoms:** Health checks failing, users cannot access site
|
|
|
|
**Immediate Actions:**
|
|
|
|
```bash
|
|
# 1. Check service status
|
|
ssh user@piano-tutor.com
|
|
docker ps --filter "label=com.piano-tutor.environment=production"
|
|
|
|
# 2. Check logs for errors
|
|
docker-compose -f docker-compose.production.yml logs --tail=100 | grep -i error
|
|
|
|
# 3. Quick restart
|
|
docker-compose -f docker-compose.production.yml restart
|
|
|
|
# 4. If restart doesn't work, rollback
|
|
export DEPLOYMENT_COLOR=blue # switch to previous version
|
|
docker-compose -f docker-compose.production.yml -p piano-tutor-blue up -d
|
|
sudo nginx -s reload
|
|
|
|
# 5. Verify
|
|
curl https://piano-tutor.com/health
|
|
```
|
|
|
|
**Post-Incident:**
|
|
1. Document what happened in incident log
|
|
2. Create GitHub issue with root cause analysis
|
|
3. Update monitoring alerts if needed
|
|
|
|
---
|
|
|
|
### 🔴 Database Connection Failure
|
|
|
|
**Symptoms:** Backend returns 500 errors, "database connection" in logs
|
|
|
|
**Actions:**
|
|
|
|
```bash
|
|
# 1. Check database health
|
|
docker exec piano-tutor-db-production pg_isready -U piano_tutor
|
|
|
|
# 2. Check database logs
|
|
docker logs piano-tutor-db-production --tail=100
|
|
|
|
# 3. Check connection pool
|
|
curl http://localhost:8000/health/detailed | jq '.database'
|
|
|
|
# 4. Restart database if needed
|
|
docker-compose -f docker-compose.production.yml restart postgres
|
|
|
|
# 5. Wait for backend to reconnect (30 seconds)
|
|
sleep 30
|
|
|
|
# 6. Verify
|
|
curl http://localhost:8000/health/ready
|
|
```
|
|
|
|
**If database won't start:**
|
|
```bash
|
|
# Check disk space
|
|
df -h
|
|
|
|
# Check database data directory
|
|
ls -lh /var/lib/piano-tutor/postgres/
|
|
|
|
# Check for corrupted data files
|
|
docker exec piano-tutor-db-production pg_checksums -D /var/lib/postgresql/data
|
|
|
|
# If corrupted, restore from backup
|
|
./scripts/restore_database.sh /opt/backups/latest/dump.sql.gz
|
|
```
|
|
|
|
---
|
|
|
|
### 🟡 High CPU/Memory Usage
|
|
|
|
**Symptoms:** Slow response times, high resource usage alerts
|
|
|
|
**Actions:**
|
|
|
|
```bash
|
|
# 1. Identify resource hog
|
|
docker stats --no-stream | sort -k 3 -h
|
|
|
|
# 2. Check for runaway processes
|
|
docker exec piano-tutor-backend-blue ps aux | sort -k 3 -h
|
|
|
|
# 3. Check for stuck Celery tasks
|
|
docker exec piano-tutor-celery-blue celery -A app.celery.tasks inspect active
|
|
|
|
# 4. Scale down if needed
|
|
docker-compose -f docker-compose.production.yml restart celery-worker
|
|
|
|
# 5. Clear cache if memory issue
|
|
docker exec piano-tutor-redis-production redis-cli FLUSHDB
|
|
|
|
# 6. Restart services one at a time
|
|
docker-compose -f docker-compose.production.yml restart backend
|
|
docker-compose -f docker-compose.production.yml restart frontend
|
|
```
|
|
|
|
---
|
|
|
|
## Daily Operations
|
|
|
|
### Morning Health Check
|
|
|
|
Run daily at start of business day:
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# Daily health check script
|
|
|
|
echo "=== Piano Tutor Daily Health Check $(date) ==="
|
|
|
|
# 1. Service Status
|
|
echo -e "\n[Services]"
|
|
ssh user@piano-tutor.com "docker ps --filter 'label=com.piano-tutor.environment=production' --format 'table {{.Names}}\t{{.Status}}'"
|
|
|
|
# 2. Health Endpoints
|
|
echo -e "\n[Health Checks]"
|
|
curl -s https://piano-tutor.com/health | jq '.status'
|
|
curl -s https://api.piano-tutor.com/health/ready | jq '.status'
|
|
|
|
# 3. Resource Usage
|
|
echo -e "\n[Resources]"
|
|
ssh user@piano-tutor.com "docker stats --no-stream --format 'table {{.Name}}\t{{.CPUPerc}}\t{{.MemPerc}}'"
|
|
|
|
# 4. Disk Space
|
|
echo -e "\n[Disk Space]"
|
|
ssh user@piano-tutor.com "df -h /var/lib/piano-tutor"
|
|
|
|
# 5. Error Count (last 24h)
|
|
echo -e "\n[Errors - Last 24h]"
|
|
ssh user@piano-tutor.com "docker logs piano-tutor-backend-blue --since 24h 2>&1 | grep -i error | wc -l"
|
|
|
|
# 6. Active Users (from cache)
|
|
echo -e "\n[Active Users]"
|
|
ssh user@piano-tutor.com "docker exec piano-tutor-redis-production redis-cli DBSIZE"
|
|
|
|
echo -e "\n=== Health Check Complete ==="
|
|
```
|
|
|
|
**Expected Output:**
|
|
- All services: `Up` status
|
|
- Health checks: `healthy` / `ready`
|
|
- CPU: < 60%
|
|
- Memory: < 70%
|
|
- Disk: < 80%
|
|
- Errors: < 10 in 24h
|
|
|
|
**Alert if:**
|
|
- Any service is `Restarting` or `Exited`
|
|
- Health checks return `not_ready`
|
|
- CPU > 80% sustained
|
|
- Memory > 85%
|
|
- Disk > 90%
|
|
- Errors > 50 in 24h
|
|
|
|
---
|
|
|
|
### Monitor Real-time Metrics
|
|
|
|
```bash
|
|
# Watch logs in real-time
|
|
docker-compose -f docker-compose.production.yml logs -f --tail=50
|
|
|
|
# Monitor resource usage
|
|
watch -n 5 'docker stats --no-stream'
|
|
|
|
# Monitor active requests
|
|
watch -n 2 'curl -s http://localhost:8000/health/detailed | jq ".database, .cache"'
|
|
|
|
# Monitor error rate
|
|
watch -n 10 'docker logs piano-tutor-backend-blue --since 1m 2>&1 | grep -c ERROR'
|
|
```
|
|
|
|
---
|
|
|
|
## Deployment Procedures
|
|
|
|
### Standard Production Deployment
|
|
|
|
**Pre-Deployment Checklist:**
|
|
- [ ] Code reviewed and approved
|
|
- [ ] All tests passing (CI/CD green)
|
|
- [ ] Staging deployment successful
|
|
- [ ] Database migrations reviewed
|
|
- [ ] Backup completed
|
|
- [ ] Rollback plan ready
|
|
- [ ] Change window scheduled
|
|
- [ ] Team notified
|
|
|
|
**Deployment Steps:**
|
|
|
|
```bash
|
|
# 1. Create release tag
|
|
git tag -a v1.0.1 -m "Release v1.0.1 - Bug fixes and performance improvements"
|
|
git push origin v1.0.1
|
|
|
|
# GitHub Actions will automatically:
|
|
# - Build Docker images
|
|
# - Run security scans
|
|
# - Deploy to production (Blue-Green)
|
|
# - Run smoke tests
|
|
# - Monitor for 5 minutes
|
|
|
|
# 2. Monitor deployment
|
|
gh run watch
|
|
|
|
# 3. Verify deployment
|
|
curl https://piano-tutor.com/health
|
|
curl https://api.piano-tutor.com/health/ready
|
|
|
|
# 4. Check version
|
|
curl https://api.piano-tutor.com/health | jq '.version'
|
|
|
|
# 5. Monitor for 15 minutes
|
|
watch -n 30 'curl -s https://api.piano-tutor.com/health/detailed | jq ".status, .system.cpu_percent, .system.memory.percent"'
|
|
|
|
# 6. Check error logs
|
|
ssh user@piano-tutor.com "docker logs piano-tutor-backend-green --since 15m 2>&1 | grep -i error"
|
|
```
|
|
|
|
**Post-Deployment:**
|
|
- [ ] Verify version deployed
|
|
- [ ] Check error logs (should be minimal)
|
|
- [ ] Verify metrics dashboard
|
|
- [ ] Test critical user flows
|
|
- [ ] Update deployment log
|
|
- [ ] Notify team of completion
|
|
|
|
---
|
|
|
|
### Emergency Hotfix Deployment
|
|
|
|
**When:** Critical bug affecting users, must deploy ASAP
|
|
|
|
```bash
|
|
# 1. Create hotfix branch from current production tag
|
|
git checkout v1.0.0
|
|
git checkout -b hotfix/critical-bug-fix
|
|
|
|
# 2. Make minimal fix
|
|
# ... edit files ...
|
|
|
|
# 3. Test locally
|
|
npm run test
|
|
npm run build
|
|
|
|
# 4. Commit and tag
|
|
git commit -m "hotfix: Fix critical authentication bug"
|
|
git tag -a v1.0.1 -m "Hotfix: Critical authentication bug"
|
|
git push origin v1.0.1
|
|
|
|
# 5. Monitor automated deployment
|
|
gh run watch
|
|
|
|
# 6. Manual deployment if automation fails
|
|
ssh user@piano-tutor.com
|
|
cd /opt/piano-tutor
|
|
export VERSION=v1.0.1
|
|
export DEPLOYMENT_COLOR=green
|
|
docker-compose -f docker-compose.production.yml pull
|
|
docker-compose -f docker-compose.production.yml -p piano-tutor-green up -d
|
|
|
|
# 7. Verify immediately
|
|
curl https://piano-tutor.com/health
|
|
# Test the specific bug fix
|
|
|
|
# 8. Switch traffic
|
|
sudo nginx -s reload
|
|
docker-compose -f docker-compose.production.yml -p piano-tutor-blue down
|
|
|
|
# 9. Merge hotfix back to main
|
|
git checkout main
|
|
git merge hotfix/critical-bug-fix
|
|
git push origin main
|
|
```
|
|
|
|
---
|
|
|
|
## Incident Response
|
|
|
|
### Incident Severity Levels
|
|
|
|
**P0 - Critical:**
|
|
- Complete site outage
|
|
- Data loss or corruption
|
|
- Security breach
|
|
- Response time: Immediate
|
|
|
|
**P1 - High:**
|
|
- Major feature broken
|
|
- Performance degradation >50%
|
|
- Affects >50% of users
|
|
- Response time: <15 minutes
|
|
|
|
**P2 - Medium:**
|
|
- Minor feature broken
|
|
- Affects <50% of users
|
|
- Workaround available
|
|
- Response time: <1 hour
|
|
|
|
**P3 - Low:**
|
|
- Cosmetic issues
|
|
- Documentation errors
|
|
- Affects <10% of users
|
|
- Response time: Next business day
|
|
|
|
---
|
|
|
|
### P0 Incident Response Procedure
|
|
|
|
**1. Acknowledge & Assess (0-5 min)**
|
|
```bash
|
|
# Alert received - acknowledge immediately
|
|
# Post in #incidents channel: "P0 incident acknowledged, investigating"
|
|
|
|
# Quick assessment
|
|
curl https://piano-tutor.com/health
|
|
ssh user@piano-tutor.com
|
|
docker ps
|
|
docker-compose -f docker-compose.production.yml logs --tail=100
|
|
```
|
|
|
|
**2. Communicate (5-10 min)**
|
|
```
|
|
# Post status update:
|
|
"P0 Incident Update:
|
|
- Issue: [brief description]
|
|
- Impact: [user-facing impact]
|
|
- Current Status: Investigating
|
|
- ETA: [estimate if possible]
|
|
- Updates every 15 min"
|
|
```
|
|
|
|
**3. Mitigate (10-30 min)**
|
|
```bash
|
|
# Option A: Quick restart
|
|
docker-compose -f docker-compose.production.yml restart
|
|
|
|
# Option B: Rollback to previous version
|
|
export DEPLOYMENT_COLOR=blue
|
|
docker-compose -f docker-compose.production.yml -p piano-tutor-blue up -d
|
|
sudo nginx -s reload
|
|
|
|
# Option C: Enable maintenance mode
|
|
# (if need time to fix without user impact)
|
|
```
|
|
|
|
**4. Verify & Monitor (30-45 min)**
|
|
```bash
|
|
# Verify fix
|
|
curl https://piano-tutor.com/health
|
|
curl https://api.piano-tutor.com/health/ready
|
|
|
|
# Monitor for 15 minutes
|
|
watch -n 30 'curl -s https://api.piano-tutor.com/health/detailed | jq'
|
|
|
|
# Check error rate
|
|
docker logs piano-tutor-backend-blue --since 15m 2>&1 | grep -c ERROR
|
|
```
|
|
|
|
**5. Post-Incident (45-60 min)**
|
|
```
|
|
# Final status update:
|
|
"P0 Incident Resolved:
|
|
- Root Cause: [brief description]
|
|
- Fix Applied: [what was done]
|
|
- Verification: All health checks passing
|
|
- Post-Mortem: [link to document]
|
|
- Prevention: [steps to prevent recurrence]"
|
|
|
|
# Create post-mortem document
|
|
# Schedule blameless post-mortem meeting within 48h
|
|
```
|
|
|
|
---
|
|
|
|
## Maintenance Tasks
|
|
|
|
### Weekly Tasks
|
|
|
|
**1. Review Logs (Monday)**
|
|
```bash
|
|
# Check for recurring errors
|
|
ssh user@piano-tutor.com "docker logs piano-tutor-backend-blue --since 7d 2>&1" | grep -i error | sort | uniq -c | sort -rn | head -20
|
|
|
|
# Check slow queries
|
|
docker exec -it piano-tutor-db-production psql -U piano_tutor -c "
|
|
SELECT query, calls, total_time/calls as avg_time
|
|
FROM pg_stat_statements
|
|
WHERE calls > 100
|
|
ORDER BY avg_time DESC
|
|
LIMIT 10;"
|
|
```
|
|
|
|
**2. Database Maintenance (Wednesday)**
|
|
```bash
|
|
# Vacuum and analyze
|
|
docker exec piano-tutor-db-production psql -U piano_tutor -c "VACUUM ANALYZE;"
|
|
|
|
# Reindex if needed
|
|
docker exec piano-tutor-db-production psql -U piano_tutor -c "REINDEX DATABASE piano_tutor;"
|
|
|
|
# Check bloat
|
|
docker exec piano-tutor-db-production psql -U piano_tutor -c "
|
|
SELECT schemaname, tablename,
|
|
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size
|
|
FROM pg_tables
|
|
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
|
|
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC
|
|
LIMIT 10;"
|
|
```
|
|
|
|
**3. Cache Optimization (Friday)**
|
|
```bash
|
|
# Check cache hit rate
|
|
docker exec piano-tutor-redis-production redis-cli INFO stats | grep keyspace
|
|
|
|
# Clear expired keys
|
|
docker exec piano-tutor-redis-production redis-cli --scan --pattern "temp:*" | xargs docker exec piano-tutor-redis-production redis-cli DEL
|
|
|
|
# Check memory usage
|
|
docker exec piano-tutor-redis-production redis-cli INFO memory
|
|
```
|
|
|
|
---
|
|
|
|
### Monthly Tasks
|
|
|
|
**1. Security Updates (1st of month)**
|
|
```bash
|
|
# Update base images
|
|
docker pull python:3.11-slim
|
|
docker pull node:20-alpine
|
|
docker pull postgres:15-alpine
|
|
docker pull redis:7-alpine
|
|
docker pull nginx:1.25-alpine
|
|
|
|
# Rebuild application images
|
|
git pull origin main
|
|
docker-compose -f docker-compose.production.yml build --no-cache
|
|
|
|
# Deploy during maintenance window
|
|
```
|
|
|
|
**2. Certificate Renewal (15th of month)**
|
|
```bash
|
|
# Check certificate expiry
|
|
sudo certbot certificates
|
|
|
|
# Renew if needed (auto-renew should handle this)
|
|
sudo certbot renew
|
|
|
|
# Copy to application
|
|
sudo cp /etc/letsencrypt/live/piano-tutor.com/fullchain.pem /opt/piano-tutor/ssl/cert.pem
|
|
sudo cp /etc/letsencrypt/live/piano-tutor.com/privkey.pem /opt/piano-tutor/ssl/key.pem
|
|
|
|
# Reload nginx
|
|
docker-compose -f docker-compose.production.yml restart nginx
|
|
```
|
|
|
|
**3. Backup Verification (Last Sunday)**
|
|
```bash
|
|
# Test latest backup
|
|
LATEST_BACKUP=$(ls -t /opt/backups/*.tar.gz | head -1)
|
|
echo "Testing backup: $LATEST_BACKUP"
|
|
|
|
# Extract to temp location
|
|
mkdir -p /tmp/backup-test
|
|
tar -xzf $LATEST_BACKUP -C /tmp/backup-test
|
|
|
|
# Verify database backup
|
|
gunzip -c /tmp/backup-test/*/dump.sql.gz | head -100
|
|
|
|
# Cleanup
|
|
rm -rf /tmp/backup-test
|
|
|
|
echo "Backup verification complete"
|
|
```
|
|
|
|
---
|
|
|
|
## Performance Optimization
|
|
|
|
### Database Optimization
|
|
|
|
```bash
|
|
# Analyze query performance
|
|
docker exec -it piano-tutor-db-production psql -U piano_tutor -c "
|
|
SELECT query, calls, total_time, mean_time,
|
|
100.0 * total_time / SUM(total_time) OVER() AS percentage
|
|
FROM pg_stat_statements
|
|
ORDER BY total_time DESC
|
|
LIMIT 20;"
|
|
|
|
# Add indexes for slow queries
|
|
# Review and add as needed based on above output
|
|
|
|
# Update statistics
|
|
docker exec piano-tutor-db-production psql -U piano_tutor -c "ANALYZE;"
|
|
```
|
|
|
|
### Cache Optimization
|
|
|
|
```bash
|
|
# Identify cache hit rate
|
|
docker exec piano-tutor-redis-production redis-cli INFO stats | grep keyspace_hits
|
|
|
|
# Adjust TTL for frequently accessed data
|
|
# Review application cache configuration
|
|
|
|
# Warm cache after deployment
|
|
curl -X POST https://api.piano-tutor.com/admin/cache/warm
|
|
```
|
|
|
|
---
|
|
|
|
## Monitoring Setup
|
|
|
|
### Prometheus + Grafana (Optional)
|
|
|
|
```bash
|
|
# Add to docker-compose.production.yml
|
|
services:
|
|
prometheus:
|
|
image: prom/prometheus
|
|
volumes:
|
|
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml
|
|
- prometheus-data:/prometheus
|
|
ports:
|
|
- "9090:9090"
|
|
|
|
grafana:
|
|
image: grafana/grafana
|
|
volumes:
|
|
- grafana-data:/var/lib/grafana
|
|
ports:
|
|
- "3001:3000"
|
|
environment:
|
|
- GF_SECURITY_ADMIN_PASSWORD=<strong-password>
|
|
```
|
|
|
|
**Configure Prometheus to scrape:**
|
|
- `http://backend:8000/metrics`
|
|
- `http://frontend:80/metrics`
|
|
- Docker metrics
|
|
- Node exporter
|
|
|
|
---
|
|
|
|
## Contacts & Escalation
|
|
|
|
**On-Call Rotation:**
|
|
- Week 1: DevOps Team A
|
|
- Week 2: DevOps Team B
|
|
- Week 3: DevOps Team C
|
|
|
|
**Escalation Path:**
|
|
1. On-call engineer (respond within 15 min)
|
|
2. Lead DevOps (if no response in 30 min)
|
|
3. CTO (for P0 incidents lasting >1 hour)
|
|
|
|
**Communication Channels:**
|
|
- #incidents (Slack) - All incidents
|
|
- #deployments (Slack) - Deployment notifications
|
|
- ops@piano-tutor.com - Email escalations
|
|
|
|
---
|
|
|
|
**Last Updated**: 2025-10-25
|
|
**Version**: 1.0.0
|
|
**Maintained By**: DevOps Team
|