النشر باستخدام Docker Compose
يُنشئ FORGE إعدادات Docker Compose جاهزة للإنتاج مع Caddy كـ reverse proxy وHTTPS تلقائي عبر Let's Encrypt وجميع خدمات التطبيق مُحوّاة. هذه هي طريقة النشر المُوصى بها لإعدادات الخادم الواحد والتطبيقات الصغيرة إلى المتوسطة.
توليد إعدادات الإنتاج
forge deploy:compose --env=production --ssl=letsencryptهذا يُنشئ الملفات التالية:
deploy/
├── docker-compose.prod.yaml # ملف Compose للإنتاج
├── Caddyfile # إعدادات reverse proxy لـ Caddy
└── .env.production # متغيرات البيئةبيئة التجربة
أنشئ إعدادات بيئة التجربة بتغيير علم --env:
forge deploy:compose --env=staging --ssl=letsencryptالخدمات المُولّدة
ملف Compose للإنتاج يُعرّف ست خدمات:
┌──────────────────────────────────────────────────────────┐
│ DOCKER COMPOSE STACK │
├──────────────────────────────────────────────────────────┤
│ │
│ الإنترنت │
│ │ │
│ ▼ │
│ ┌────────────────┐ │
│ │ Caddy │ :80, :443 │
│ │ (reverse │ HTTPS تلقائي عبر Let's Encrypt │
│ │ proxy) │ │
│ └──────┬─────────┘ │
│ │ │
│ ┌─────┼────────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────┐ ┌──────┐ ┌──────────┐ │
│ │ web │ │ api │ │ admin │ │
│ │:3000 │ │:8080 │ │ :3001 │ │
│ └──────┘ └──┬───┘ └──────────┘ │
│ │ │
│ ┌─────┴─────┐ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────┐ ┌────────┐ │
│ │ postgres │ │ redis │ │
│ │ :5432 │ │ :6379 │ │
│ └──────────┘ └────────┘ │
│ │
└──────────────────────────────────────────────────────────┘ملف Docker Compose
ملف docker-compose.prod.yaml المُولّد:
version: '3.8'
services:
api:
image: ${REGISTRY:-docker.io}/${APP_NAME}-api:${VERSION:-latest}
restart: always
environment:
- DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME}
- REDIS_URL=redis://redis:6379
- FORGE_ENCRYPTION_KEY=${FORGE_ENCRYPTION_KEY}
- RUST_LOG=info
- JWT_SECRET=${JWT_SECRET}
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
networks:
- internal
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 15s
web:
image: ${REGISTRY:-docker.io}/${APP_NAME}-web:${VERSION:-latest}
restart: always
environment:
- NEXT_PUBLIC_API_URL=https://api.${DOMAIN}
- API_URL=http://api:8080
depends_on:
- api
networks:
- internal
admin:
image: ${REGISTRY:-docker.io}/${APP_NAME}-admin:${VERSION:-latest}
restart: always
environment:
- NEXT_PUBLIC_API_URL=https://api.${DOMAIN}
- API_URL=http://api:8080
depends_on:
- api
networks:
- internal
caddy:
image: caddy:2-alpine
restart: always
ports:
- "80:80"
- "443:443"
- "443:443/udp" # HTTP/3
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
depends_on:
- web
- admin
- api
networks:
- internal
- external
postgres:
image: postgres:15-alpine
restart: always
environment:
POSTGRES_DB: ${DB_NAME}
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- internal
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
restart: always
command: redis-server --appendonly yes
volumes:
- redis_data:/data
networks:
- internal
volumes:
postgres_data:
redis_data:
caddy_data:
caddy_config:
networks:
internal:
driver: bridge
external:
driver: bridgeCaddyfile (HTTPS تلقائي)
Caddy يحصل تلقائياً على شهادات Let's Encrypt ويُجددها. لا حاجة لإعدادات SSL يدوية.
{
email admin@example.com
}
myapp.com {
reverse_proxy web:3000
encode gzip
}
admin.myapp.com {
reverse_proxy admin:3001
encode gzip
}
api.myapp.com {
reverse_proxy api:8080
encode gzip
header {
Access-Control-Allow-Origin https://myapp.com
Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
Access-Control-Allow-Headers "Authorization, Content-Type"
}
}كيف يتعامل Caddy مع HTTPS
Caddy يحصل تلقائياً على شهادات TLS من Let's Encrypt عند استخدام أسماء نطاقات حقيقية. يتعامل مع تجديد الشهادات قبل انتهاء صلاحيتها وإعادة توجيه HTTP إلى HTTPS وOCSP stapling -- كل ذلك بدون أي إعدادات. المتطلب الوحيد هو أن يُشير DNS نطاقك إلى الخادم وأن تكون المنافذ 80 و443 متاحة.
إعدادات البيئة
ملف .env.production المُولّد:
# Application
APP_NAME=myapp
DOMAIN=myapp.com
VERSION=latest
REGISTRY=docker.io
# Database
DB_NAME=myapp
DB_USER=myapp
DB_PASSWORD= # Set a strong password
DB_HOST=postgres
DB_PORT=5432
# Security
JWT_SECRET= # Generate: openssl rand -hex 32
FORGE_ENCRYPTION_KEY= # Generate: forge secrets:generate-key
# Caddy
CADDY_EMAIL=admin@myapp.comعيّن جميع الأسرار قبل النشر
لا تنشر أبداً بقيم فارغة أو افتراضية لـ DB_PASSWORD أو JWT_SECRET أو FORGE_ENCRYPTION_KEY. أنشئ قيم قوية وفريدة لكل منها.
بيئة التجربة مقابل الإنتاج
| الإعداد | التجربة | الإنتاج |
|---|---|---|
DOMAIN | staging.myapp.com | myapp.com |
RUST_LOG | debug | info |
DB_POOL_SIZE | 5 | 20 |
RATE_LIMIT_RPM | 1000 | 100 |
VERSION | staging-latest | v1.0.0 |
استمرارية البيانات
جميع البيانات الدائمة تُخزّن في Docker volumes مُسمّاة:
| Volume | الخدمة | البيانات |
|---|---|---|
postgres_data | PostgreSQL | ملفات قاعدة البيانات والجداول والفهارس |
redis_data | Redis | الذاكرة المؤقتة والجلسات والمهام المؤجلة |
caddy_data | Caddy | شهادات TLS واستجابات OCSP |
caddy_config | Caddy | الإعدادات أثناء التشغيل |
انسخ الـ volumes احتياطياً
Docker volumes تستمر عبر إعادة تشغيل الحاويات وتحديثات الصور، لكنها لا تُنسخ احتياطياً تلقائياً. أعدّ نسخ احتياطية منتظمة لـ postgres_data على الأقل.
# Backup PostgreSQL data
docker exec myapp-postgres-1 \
pg_dump -U myapp myapp > backup_$(date +%Y%m%d).sql
# Restore from backup
docker exec -i myapp-postgres-1 \
psql -U myapp myapp < backup_20260127.sqlالبدء والإدارة
النشر الأولي
# Navigate to deploy directory
cd deploy/
# Set environment variables
cp .env.production .env
# Edit .env and set all secrets
# Build and start all services
docker compose -f docker-compose.prod.yaml up -d
# Verify all services are running
docker compose -f docker-compose.prod.yaml ps
# Run database migrations
docker compose -f docker-compose.prod.yaml exec api \
./forge-cli migrate
# Seed default data
docker compose -f docker-compose.prod.yaml exec api \
./forge-cli seedالعمليات الشائعة
# All services
docker compose -f docker-compose.prod.yaml logs -f
# Specific service
docker compose -f docker-compose.prod.yaml logs -f api
docker compose -f docker-compose.prod.yaml logs -f caddy# Restart single service
docker compose -f docker-compose.prod.yaml restart api
# Restart all services
docker compose -f docker-compose.prod.yaml restart# Pull new images
docker compose -f docker-compose.prod.yaml pull
# Recreate containers with new images (zero downtime)
docker compose -f docker-compose.prod.yaml up -d --no-deps api
docker compose -f docker-compose.prod.yaml up -d --no-deps web
docker compose -f docker-compose.prod.yaml up -d --no-deps admin
# Run migrations after update
docker compose -f docker-compose.prod.yaml exec api \
./forge-cli migrate# Stop all services (preserves data)
docker compose -f docker-compose.prod.yaml down
# Stop and remove volumes (destroys data)
docker compose -f docker-compose.prod.yaml down -vفحوصات الصحة
تحقق أن نشرك سليم:
# Check service health
docker compose -f docker-compose.prod.yaml ps
# Test API health endpoint
curl https://api.myapp.com/health
# Test web application
curl -I https://myapp.com
# Test admin application
curl -I https://admin.myapp.com
# Verify SSL certificate
curl -vI https://myapp.com 2>&1 | grep "SSL certificate"بناء صور Docker
يُنشئ FORGE ملفات Dockerfile لكل خدمة. ابنِ وادفع الصور قبل النشر:
# Build all images
docker build -t myapp-api:latest ./apps/api
docker build -t myapp-web:latest ./apps/web
docker build -t myapp-admin:latest ./apps/admin# Tag and push to your registry
docker tag myapp-api:latest registry.example.com/myapp-api:v1.0.0
docker push registry.example.com/myapp-api:v1.0.0
docker tag myapp-web:latest registry.example.com/myapp-web:v1.0.0
docker push registry.example.com/myapp-web:v1.0.0
docker tag myapp-admin:latest registry.example.com/myapp-admin:v1.0.0
docker push registry.example.com/myapp-admin:v1.0.0الخطوات التالية
- النشر على Kubernetes -- التوسع لأكثر من خادم واحد
- النشر عبر SSH -- النشر بدون حاويات
- شهادات SSL -- فهم خيارات SSL بالتفصيل