Bienvenue dans la documentation technique de déploiement. Ce wiki centralise toutes les informations pour déployer et maintenir l'application en production.
Sections principales :
Requis minimales :
Optionnel :
git clone https://github.com/your-org/your-app.git
cd your-app
cp .env.example .env
# Éditer .env avec vos paramètres
nano .env
# Avec Docker Compose
docker-compose up -d
# Vérifier le status
docker-compose ps
# Logs
docker-compose logs -f app
http://localhost:3000http://localhost:3000/admin.envÉtapes de configuration :
Base de données
docker-compose exec app npm run migrate
Admin user
docker-compose exec app npm run create-admin
Vérifier les logs
docker-compose logs app
Déployer l'app dans un seul container Docker :
docker build -t myapp:1.0 .
docker run -d \
--name=myapp \
-p 3000:3000 \
-e NODE_ENV=production \
-e DATABASE_URL=postgresql://user:pass@db:5432/appdb \
-v /data:/app/data \
myapp:1.0
Vérifier :
docker ps
docker logs myapp
curl http://localhost:3000/health
Configuration complète avec tous les services :
version: '3.8'
services:
# Application Node.js
app:
build: .
container_name: myapp
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://myapp:password@db:5432/myappdb
- REDIS_URL=redis://cache:6379
- LOG_LEVEL=info
depends_on:
- db
- cache
volumes:
- ./uploads:/app/uploads
- ./logs:/app/logs
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
# PostgreSQL Database
db:
image: postgres:15-alpine
container_name: myapp-db
environment:
- POSTGRES_USER=myapp
- POSTGRES_PASSWORD=secure_password
- POSTGRES_DB=myappdb
volumes:
- db_data:/var/lib/postgresql/data
- ./backups:/backups
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U myapp"]
interval: 10s
timeout: 5s
retries: 5
# Redis Cache
cache:
image: redis:7-alpine
container_name: myapp-cache
volumes:
- cache_data:/data
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
# Nginx Reverse Proxy
nginx:
image: nginx:alpine
container_name: myapp-nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
- ./logs/nginx:/var/log/nginx
depends_on:
- app
restart: unless-stopped
volumes:
db_data:
cache_data:
Démarrer :
# Production
docker-compose -f docker-compose.yml up -d
# Développement
docker-compose -f docker-compose.dev.yml up
# Vérifier
docker-compose ps
docker-compose logs -f app
Déployer avec Helm :
# Ajouter le repo
helm repo add myorg https://charts.myorg.com
helm repo update
# Installer
helm install myapp myorg/myapp \
--namespace production \
--create-namespace \
-f values.yaml
# Vérifier
helm list
kubectl get pods -n production
Deployment :
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: app
image: myapp:1.0
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: "production"
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: app-secrets
key: db-url
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
Service :
apiVersion: v1
kind: Service
metadata:
name: myapp
namespace: production
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 3000
selector:
app: myapp
Déployer :
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
# Vérifier
kubectl get pods -n production
kubectl logs -n production deployment/myapp
name: Deploy Production
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build Docker image
run: docker build -t myapp:${{ github.sha }} .
- name: Push to registry
run: docker push myapp:${{ github.sha }}
- name: Deploy to production
run: |
ssh deploy@prod-server "cd /apps/myapp && \
docker-compose pull && \
docker-compose up -d"
env:
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
stages:
- build
- test
- deploy
build:
stage: build
script:
- docker build -t myapp:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
deploy_prod:
stage: deploy
script:
- ssh deploy@prod "cd /apps/myapp && ./deploy.sh $CI_COMMIT_SHA"
only:
- main
Configuration Nginx reverse proxy + SSL :
upstream app {
server app:3000;
keepalive 64;
}
# Redirection HTTP -> HTTPS
server {
listen 80;
server_name your-domain.com www.your-domain.com;
return 301 https://$server_name$request_uri;
}
# HTTPS
server {
listen 443 ssl http2;
server_name your-domain.com www.your-domain.com;
# SSL
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Gzip
gzip on;
gzip_types text/plain text/css application/json application/javascript;
# Proxy
location / {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
# Statics
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
proxy_pass http://app;
expires 30d;
add_header Cache-Control "public, immutable";
}
}
Backup quotidien :
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
docker exec myapp-db pg_dump -U myapp myappdb | \
gzip > /backups/backup_$DATE.sql.gz
# Garder 30 jours
find /backups -name "backup_*.sql.gz" -mtime +30 -delete
Restaurer :
gunzip < /backups/backup_20240101_120000.sql.gz | \
docker exec -i myapp-db psql -U myapp myappdb
Configuration avec pgBouncer :
[databases]
myappdb = host=db port=5432 dbname=myappdb user=myapp password=secure
[pgbouncer]
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 25
reserve_pool_size = 5
reserve_pool_timeout = 3
# Vérifier l'app
curl -f http://localhost:3000/health || exit 1
# Vérifier la DB
docker exec myapp-db pg_isready -U myapp || exit 1
# Vérifier le cache
docker exec myapp-cache redis-cli ping || exit 1
Centraliser les logs :
# ELK Stack (Elasticsearch, Logstash, Kibana)
docker run -d --name elasticsearch \
-e discovery.type=single-node \
-p 9200:9200 \
docker.elastic.co/elasticsearch/elasticsearch:8.0.0
GET /health
Response :
{
"status": "ok",
"uptime": 3600,
"version": "1.0.0"
}
POST /api/auth/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "secure_password"
}
Response :
{
"token": "eyJhbGc...",
"user": {
"id": 1,
"email": "user@example.com",
"role": "admin"
}
}
Vérifier les logs :
docker-compose logs app
Solutions courantes :
| Erreur | Solution |
|---|---|
| Port déjà utilisé | docker ps puis docker stop container_id |
| DB connection refused | Vérifier DATABASE_URL dans .env |
| Out of memory | Augmenter ressources Docker |
| Module not found | npm install ou rebuild image |
Diagnostiquer :
# CPU/Memory
docker stats
# Lenteur DB
docker exec myapp-db psql -U myapp -c "SELECT * FROM pg_stat_statements"
# Lenteur Redis
docker exec myapp-cache redis-cli info stats
Vérifier connexion :
docker exec myapp-db psql -U myapp -d myappdb -c "SELECT version();"
Migrations échouées :
docker-compose exec app npm run migrate:status
docker-compose exec app npm run migrate:rollback
docker-compose exec app npm run migrate
Nettoyer :
# Vieux logs
find /var/log -name "*.log" -mtime +30 -delete
# Docker images inutilisées
docker image prune -a
# Volumes inutilisés
docker volume prune
# Container stoppés
docker container prune
Renouveler avec Let's Encrypt :
certbot renew --force-renewal
docker-compose restart nginx
Dernière mise à jour : 2024-01-15
Mainteneur : DevOps Team