Контекст: В deploy/ есть docker-compose.prod.yml, deploy.sh, env.prod. Локально — docker-compose.yml + Makefile.
Проект разворачивается, но не production-ready — есть риски безопасности, надежности и операционности.
| Проблема | Файл | Риск |
|---|---|---|
| Secrets в plain text | env.prod, docker-compose.yml |
Утечка БД, JWT, S3 при коммите / доступе к серверу |
| Нет healthcheck у backend/frontend в prod | docker-compose.prod.yml |
Контейнеры могут быть "up" но не готовы к трафику |
| Миграции не гарантированно накатываются | docker-compose.prod.yml:52 |
migrate/migrate запускается как sidecar, нет retry логики |
| Нет reverse proxy (nginx/traefik) | docker-compose.prod.yml |
Прямой доступ к портам 8080/3000, нет SSL, нет rate limiting на входе |
| Single instance БД/Redis/MinIO | docker-compose.prod.yml |
SPOF, нет HA, нет бэкапов |
| Hardcoded IP в env.prod | env.prod:13,22,27 |
Не переносимо, не работает за доменом |
# Не хранить секреты в репозитории!
# Использовать:
# - Docker secrets (swarm mode)
# - HashiCorp Vault / AWS Secrets Manager / 1Password CLI
# - .env файл только на сервере (chmod 600), в .gitignore
# docker-compose.prod.yml — добавить сервис
proxy:
image: caddy:2-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
depends_on:
- backend
- frontend
# Caddyfile — автоматический HTTPS через Let's Encrypt
photoplaces.ru {
reverse_proxy frontend:3000
}
api.photoplaces.ru {
reverse_proxy backend:8080
}
# backend healthcheck
backend:
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/api/v1/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
# frontend healthcheck
frontend:
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000"]
interval: 30s
timeout: 10s
retries: 3
# Добавить сервис бэкапа
postgres-backup:
image: prodrigestivill/postgres-backup-local
environment:
POSTGRES_HOST: postgres
POSTGRES_DB: photoplaces
POSTGRES_USER: photoplaces
POSTGRES_PASSWORD: ${DB_PASSWORD}
SCHEDULE: "@daily"
BACKUP_KEEP_DAYS: 30
volumes:
- ./backups:/backups
# Добавить Loki + Promtail или ELK
loki:
image: grafana/loki:2.9
ports: ["3100:3100"]
promtail:
image: grafana/promtail:2.9
volumes:
- /var/log:/var/log
- ./promtail.yml:/etc/promtail.yml
# deploy/deploy.sh — улучшенная версия
#!/usr/bin/env bash
set -euo pipefail
ENV_FILE="${1:-deploy/env.prod}"
[ -f "$ENV_FILE" ] || { echo "Create $ENV_FILE from example"; exit 1; }
# 1. Load secrets (support 1Password, Vault, etc.)
source "$ENV_FILE"
# 2. Build images with tags
VERSION=$(git describe --tags --always --dirty)
docker compose -f deploy/docker-compose.prod.yml build --build-arg VERSION="$VERSION"
# 3. Run migrations (with retry)
docker compose -f deploy/docker-compose.prod.yml run --rm migrations
# 4. Deploy with zero-downtime (rolling update)
docker compose -f deploy/docker-compose.prod.yml up -d --remove-orphans
# 5. Health checks
sleep 10
curl -sf http://localhost/health || { echo "Health check failed"; exit 1; }
echo "✓ Deployed version $VERSION"
Задача: Code review PhotoPlaces — production readiness
#deploy #docker #production #security #monitoring #best-practice