Дата: 2025-11-10
Версия: 1.0
Статус: Production Ready
Этот документ описывает полный процесс развёртывания CIFRA Platform от development до production.
Назначение: Разработка и отладка
Инфраструктура:
Hardware:
CPU: 2-4 cores
RAM: 8 GB
Disk: 20 GB SSD
Software:
OS: Linux/macOS/Windows
Python: 3.10+
Docker: 24+
Docker Compose: 2.0+
Git: 2.30+
Установка:
# 1. Клонировать репозиторий
git clone https://github.com/cifra/cifra-platform.git
cd cifra-platform
# 2. Создать виртуальное окружение
python3 -m venv venv
source venv/bin/activate # Linux/macOS
# venv\Scripts\activate # Windows
# 3. Установить зависимости
pip install -r requirements-dev.txt
# 4. Скопировать конфигурацию
cp .env.example .env
# 5. Редактировать .env
nano .env
# 6. Запустить зависимости (PostgreSQL, Redis)
docker-compose -f docker-compose.dev.yml up -d
# 7. Применить миграции
alembic upgrade head
# 8. Создать суперпользователя
python scripts/create_superuser.py
# 9. Запустить сервер
uvicorn cifra.main:app --reload --port 8000
# 10. В отдельном терминале - Celery worker
celery -A cifra.worker worker -l info
# 11. В третьем терминале - Celery beat
celery -A cifra.worker beat -l info
Проверка:
# API
curl http://localhost:8000/health
# {"status": "ok", "version": "1.0.0"}
# Admin UI
open http://localhost:8000/admin
# API Docs
open http://localhost:8000/docs # Swagger UI
Назначение: Тестирование перед production
Инфраструктура:
Hardware:
CPU: 2 cores
RAM: 4 GB
Disk: 40 GB SSD
Cloud:
Provider: DigitalOcean / AWS / GCP / Azure
Instance: $20-40/month
Software:
OS: Ubuntu 22.04 LTS
Docker: 24+
Docker Compose: 2.0+
Развёртывание:
# 1. Подключиться к серверу
ssh deploy@staging.cifra.io
# 2. Клонировать репозиторий
git clone https://github.com/cifra/cifra-platform.git /opt/cifra
cd /opt/cifra
# 3. Checkout нужной ветки
git checkout develop
# 4. Скопировать production конфигурацию
cp .env.staging .env
# 5. Редактировать .env
nano .env
# Обязательные переменные:
DATABASE_URL=postgresql://user:pass@db:5432/cifra_staging
REDIS_URL=redis://redis:6379/0
SECRET_KEY=<random-secret-key>
ALLOWED_HOSTS=staging.cifra.io
DEBUG=false
ENVIRONMENT=staging
# 6. Запустить через Docker Compose
docker-compose -f docker-compose.staging.yml up -d
# 7. Применить миграции
docker-compose exec app alembic upgrade head
# 8. Создать суперпользователя
docker-compose exec app python scripts/create_superuser.py
# 9. Собрать статику (если есть)
docker-compose exec app python scripts/collectstatic.py
docker-compose.staging.yml:
version: '3.8'
services:
app:
build: .
image: cifra/platform:staging
ports:
- "8000:8000"
environment:
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
- SECRET_KEY=${SECRET_KEY}
depends_on:
- db
- redis
restart: unless-stopped
volumes:
- ./media:/app/media
- ./logs:/app/logs
db:
image: postgres:15
environment:
POSTGRES_USER: cifra
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: cifra_staging
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
redis:
image: redis:7
volumes:
- redis_data:/data
restart: unless-stopped
celery:
image: cifra/platform:staging
command: celery -A cifra.worker worker -l info
environment:
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
depends_on:
- redis
- db
restart: unless-stopped
celery-beat:
image: cifra/platform:staging
command: celery -A cifra.worker beat -l info
environment:
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
depends_on:
- redis
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/staging.conf:/etc/nginx/conf.d/default.conf
- ./ssl:/etc/nginx/ssl
- ./media:/usr/share/nginx/media
depends_on:
- app
restart: unless-stopped
volumes:
postgres_data:
redis_data:
nginx/staging.conf:
upstream app {
server app:8000;
}
server {
listen 80;
server_name staging.cifra.io;
# Redirect to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name staging.cifra.io;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
client_max_body_size 100M;
location / {
proxy_pass http://app;
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;
}
location /media {
alias /usr/share/nginx/media;
}
location /static {
alias /usr/share/nginx/static;
}
}
SSL сертификат (Let's Encrypt):
# Установить certbot
sudo apt install certbot python3-certbot-nginx
# Получить сертификат
sudo certbot --nginx -d staging.cifra.io
# Автоматическое обновление
sudo certbot renew --dry-run
Назначение: Production для пользователей
Инфраструктура:
Hardware:
CPU: 4 cores
RAM: 8 GB
Disk: 100 GB SSD
Cloud:
Provider: DigitalOcean / AWS / GCP / Azure
Instance: $80-120/month
Load Balancer: 1x (2 cores, 2GB RAM)
App Servers: 3x (4 cores, 8GB RAM)
Database: 1x Primary + 1x Replica (4 cores, 16GB RAM)
Redis Cluster: 3x (2 cores, 4GB RAM)
Celery Workers: 2x (2 cores, 4GB RAM)
Total: ~$500-800/month
Cluster:
Nodes: 5-10 nodes (4 cores, 16GB RAM each)
Autoscaling: enabled
Multi-zone: yes
Services:
- App: 3-10 pods (autoscale)
- Celery: 2-5 pods (autoscale)
- PostgreSQL: Managed service (AWS RDS, GCP CloudSQL)
- Redis: Managed service (AWS ElastiCache, GCP Memorystore)
- S3: Object storage (AWS S3, GCP GCS)
Total: $1000-5000/month
# Dockerfile
FROM python:3.11-slim
# Установка системных зависимостей
RUN apt-get update && apt-get install -y \
postgresql-client \
build-essential \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
# Рабочая директория
WORKDIR /app
# Копирование зависимостей
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Копирование кода
COPY . .
# Создание пользователя (не root)
RUN useradd -m -u 1000 cifra && chown -R cifra:cifra /app
USER cifra
# Healthcheck
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# Expose порт
EXPOSE 8000
# Запуск
CMD ["uvicorn", "cifra.main:app", "--host", "0.0.0.0", "--port", "8000"]
version: '3.8'
services:
app:
build: .
image: cifra/platform:${VERSION:-latest}
deploy:
replicas: 3
restart_policy:
condition: on-failure
resources:
limits:
cpus: '2'
memory: 2G
reservations:
cpus: '1'
memory: 1G
environment:
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
- SECRET_KEY=${SECRET_KEY}
- ENVIRONMENT=production
depends_on:
- db
- redis
volumes:
- ./media:/app/media
- ./logs:/app/logs
networks:
- cifra-network
db:
image: postgres:15
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME}
volumes:
- postgres_data:/var/lib/postgresql/data
- ./backups:/backups
networks:
- cifra-network
restart: unless-stopped
# Backup script
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7
command: redis-server --appendonly yes --maxmemory 2gb --maxmemory-policy allkeys-lru
volumes:
- redis_data:/data
networks:
- cifra-network
restart: unless-stopped
celery:
image: cifra/platform:${VERSION:-latest}
command: celery -A cifra.worker worker -l info --concurrency=4
environment:
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
depends_on:
- redis
- db
networks:
- cifra-network
restart: unless-stopped
deploy:
replicas: 2
celery-beat:
image: cifra/platform:${VERSION:-latest}
command: celery -A cifra.worker beat -l info
environment:
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
depends_on:
- redis
networks:
- cifra-network
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/production.conf:/etc/nginx/conf.d/default.conf
- ./ssl:/etc/nginx/ssl
- ./media:/usr/share/nginx/media
depends_on:
- app
networks:
- cifra-network
restart: unless-stopped
# Monitoring
prometheus:
image: prom/prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
ports:
- "9090:9090"
networks:
- cifra-network
restart: unless-stopped
grafana:
image: grafana/grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
volumes:
- grafana_data:/var/lib/grafana
ports:
- "3000:3000"
networks:
- cifra-network
restart: unless-stopped
networks:
cifra-network:
driver: bridge
volumes:
postgres_data:
redis_data:
prometheus_data:
grafana_data:
# 1. Подключиться к серверу
ssh deploy@production.cifra.io
# 2. Клонировать репозиторий
git clone https://github.com/cifra/cifra-platform.git /opt/cifra
cd /opt/cifra
# 3. Checkout production ветки
git checkout main
# 4. Создать .env
cp .env.production.example .env
nano .env
# 5. Сгенерировать SECRET_KEY
python -c "import secrets; print(secrets.token_urlsafe(50))"
# 6. Запустить
docker-compose -f docker-compose.production.yml up -d
# 7. Проверить статус
docker-compose ps
# 8. Применить миграции
docker-compose exec app alembic upgrade head
# 9. Создать суперпользователя
docker-compose exec app python scripts/create_superuser.py
# 10. Проверить логи
docker-compose logs -f app
# 11. Проверить доступность
curl https://production.cifra.io/health
# k8s/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: cifra
# k8s/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: cifra-config
namespace: cifra
data:
ENVIRONMENT: "production"
ALLOWED_HOSTS: "app.cifra.io,*.cifra.io"
LOG_LEVEL: "INFO"
# k8s/secrets.yaml
apiVersion: v1
kind: Secret
metadata:
name: cifra-secrets
namespace: cifra
type: Opaque
data:
# Base64 encoded
DATABASE_URL: cG9zdGdyZXNxbDovL3VzZXI6cGFzc0BkYjo1NDMyL2NpZnJh
REDIS_URL: cmVkaXM6Ly9yZWRpczozNjM3OS8w
SECRET_KEY: c3VwZXItc2VjcmV0LWtleQ==
Создание секретов:
kubectl create secret generic cifra-secrets \
--from-literal=DATABASE_URL="postgresql://user:pass@db:5432/cifra" \
--from-literal=REDIS_URL="redis://redis:6379/0" \
--from-literal=SECRET_KEY="super-secret-key" \
-n cifra
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: cifra-app
namespace: cifra
spec:
replicas: 3
selector:
matchLabels:
app: cifra
template:
metadata:
labels:
app: cifra
spec:
containers:
- name: app
image: cifra/platform:1.0.0
ports:
- containerPort: 8000
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: cifra-secrets
key: DATABASE_URL
- name: REDIS_URL
valueFrom:
secretKeyRef:
name: cifra-secrets
key: REDIS_URL
- name: SECRET_KEY
valueFrom:
secretKeyRef:
name: cifra-secrets
key: SECRET_KEY
envFrom:
- configMapRef:
name: cifra-config
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8000
initialDelaySeconds: 5
periodSeconds: 5
# k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
name: cifra-service
namespace: cifra
spec:
selector:
app: cifra
ports:
- protocol: TCP
port: 80
targetPort: 8000
type: LoadBalancer
# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: cifra-ingress
namespace: cifra
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- app.cifra.io
secretName: cifra-tls
rules:
- host: app.cifra.io
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: cifra-service
port:
number: 80
# k8s/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: cifra-hpa
namespace: cifra
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: cifra-app
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
# 1. Создать namespace
kubectl apply -f k8s/namespace.yaml
# 2. Создать секреты
kubectl apply -f k8s/secrets.yaml
# 3. Создать ConfigMap
kubectl apply -f k8s/configmap.yaml
# 4. Развернуть приложение
kubectl apply -f k8s/deployment.yaml
# 5. Создать Service
kubectl apply -f k8s/service.yaml
# 6. Создать Ingress
kubectl apply -f k8s/ingress.yaml
# 7. Создать HPA
kubectl apply -f k8s/hpa.yaml
# 8. Проверить статус
kubectl get pods -n cifra
kubectl get svc -n cifra
kubectl get ingress -n cifra
# 9. Проверить логи
kubectl logs -f deployment/cifra-app -n cifra
# 10. Применить миграции
kubectl exec -it deployment/cifra-app -n cifra -- alembic upgrade head
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [main]
tags: ['v*']
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install pytest pytest-cov
- name: Run tests
run: pytest --cov=cifra tests/
- name: Upload coverage
uses: codecov/codecov-action@v3
build:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: |
cifra/platform:latest
cifra/platform:${{ github.sha }}
cifra/platform:${{ github.ref_name }}
deploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Deploy to production
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.PRODUCTION_HOST }}
username: deploy
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /opt/cifra
git pull origin main
docker-compose pull
docker-compose up -d
docker-compose exec -T app alembic upgrade head
# .gitlab-ci.yml
stages:
- test
- build
- deploy
variables:
DOCKER_IMAGE: cifra/platform
test:
stage: test
image: python:3.11
script:
- pip install -r requirements.txt
- pip install pytest pytest-cov
- pytest --cov=cifra tests/
coverage: '/TOTAL.*\s+(\d+%)$/'
build:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD
- docker build -t $DOCKER_IMAGE:$CI_COMMIT_SHA .
- docker push $DOCKER_IMAGE:$CI_COMMIT_SHA
- docker tag $DOCKER_IMAGE:$CI_COMMIT_SHA $DOCKER_IMAGE:latest
- docker push $DOCKER_IMAGE:latest
only:
- main
deploy_production:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache openssh-client
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | ssh-add -
script:
- ssh deploy@$PRODUCTION_HOST "cd /opt/cifra && ./deploy.sh"
only:
- main
when: manual
#!/bin/bash
# deploy-blue-green.sh
# Current version (blue)
CURRENT_VERSION=$(docker ps --filter "name=cifra-app-blue" --format "{{.Image}}")
NEW_VERSION="cifra/platform:${1:-latest}"
echo "Current version: $CURRENT_VERSION"
echo "New version: $NEW_VERSION"
# Deploy green
echo "Deploying green..."
docker-compose -f docker-compose.green.yml up -d
# Wait for green to be healthy
echo "Waiting for green to be healthy..."
for i in {1..30}; do
if curl -f http://localhost:8001/health; then
echo "Green is healthy!"
break
fi
sleep 2
done
# Switch traffic to green
echo "Switching traffic to green..."
# Update nginx config to point to green
sed -i 's/app-blue/app-green/g' /etc/nginx/sites-available/cifra
nginx -s reload
# Wait 30 seconds
sleep 30
# Check if everything is OK
if ! curl -f https://app.cifra.io/health; then
echo "ERROR: Green is not responding! Rolling back..."
sed -i 's/app-green/app-blue/g' /etc/nginx/sites-available/cifra
nginx -s reload
exit 1
fi
# Stop blue
echo "Stopping blue..."
docker-compose -f docker-compose.blue.yml down
echo "Deployment complete!"
# Rolling update with kubectl
kubectl set image deployment/cifra-app \
app=cifra/platform:1.0.1 \
-n cifra
# Проверить статус
kubectl rollout status deployment/cifra-app -n cifra
# Rollback если нужно
kubectl rollout undo deployment/cifra-app -n cifra
# Development
alembic revision --autogenerate -m "Add user preferences table"
# Проверить что сгенерировалось
cat alembic/versions/xxxx_add_user_preferences_table.py
# Применить локально
alembic upgrade head
# ВАЖНО: Сделать backup БД ПЕРЕД миграцией!
# 1. Backup
docker-compose exec db pg_dump -U cifra cifra > backup_$(date +%Y%m%d_%H%M%S).sql
# 2. Применить миграцию
docker-compose exec app alembic upgrade head
# 3. Проверить
docker-compose exec db psql -U cifra -c "\dt"
# 4. Если что-то пошло не так - откат
docker-compose exec app alembic downgrade -1
# 5. Или восстановление из backup
docker-compose exec -T db psql -U cifra cifra < backup_20251110_123456.sql
#!/bin/bash
# scripts/backup-db.sh
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backups/postgres"
FILENAME="cifra_${DATE}.sql"
# Backup
docker-compose exec -T db pg_dump -U cifra cifra | gzip > ${BACKUP_DIR}/${FILENAME}.gz
# Upload to S3
aws s3 cp ${BACKUP_DIR}/${FILENAME}.gz s3://cifra-backups/postgres/
# Keep only last 30 days locally
find ${BACKUP_DIR} -name "*.sql.gz" -mtime +30 -delete
echo "Backup complete: ${FILENAME}.gz"
Cron job:
# crontab -e
0 3 * * * /opt/cifra/scripts/backup-db.sh
#!/bin/bash
# scripts/restore-db.sh
BACKUP_FILE=$1
if [ -z "$BACKUP_FILE" ]; then
echo "Usage: ./restore-db.sh <backup-file.sql.gz>"
exit 1
fi
# Download from S3 if needed
if [[ $BACKUP_FILE == s3://* ]]; then
aws s3 cp $BACKUP_FILE /tmp/restore.sql.gz
BACKUP_FILE="/tmp/restore.sql.gz"
fi
# Extract
gunzip -c $BACKUP_FILE > /tmp/restore.sql
# Restore
docker-compose exec -T db psql -U cifra cifra < /tmp/restore.sql
# Cleanup
rm /tmp/restore.sql
echo "Restore complete!"
# cifra/api/health.py
from fastapi import APIRouter, status
from sqlalchemy import text
router = APIRouter()
@router.get("/health")
async def health_check():
"""Basic health check"""
return {"status": "ok", "version": "1.0.0"}
@router.get("/ready")
async def readiness_check(db: AsyncSession = Depends(get_db)):
"""Readiness check (checks DB connection)"""
try:
# Check DB
await db.execute(text("SELECT 1"))
# Check Redis
await redis.ping()
return {"status": "ready"}
except Exception as e:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail=f"Service not ready: {str(e)}"
)
# cifra/metrics.py
from prometheus_client import Counter, Histogram, Gauge
from fastapi import Request
import time
# Metrics
http_requests_total = Counter(
'http_requests_total',
'Total HTTP requests',
['method', 'endpoint', 'status']
)
http_request_duration_seconds = Histogram(
'http_request_duration_seconds',
'HTTP request duration',
['method', 'endpoint']
)
active_users = Gauge(
'active_users',
'Number of active users'
)
# Middleware
@app.middleware("http")
async def metrics_middleware(request: Request, call_next):
start_time = time.time()
response = await call_next(request)
duration = time.time() - start_time
http_requests_total.labels(
method=request.method,
endpoint=request.url.path,
status=response.status_code
).inc()
http_request_duration_seconds.labels(
method=request.method,
endpoint=request.url.path
).observe(duration)
return response
# Metrics endpoint
from prometheus_client import generate_latest
@app.get("/metrics")
async def metrics():
return Response(
generate_latest(),
media_type="text/plain"
)
# cifra/logging_config.py
import logging
import sys
from loguru import logger
# Remove default handler
logger.remove()
# Add JSON formatted handler for production
if settings.ENVIRONMENT == "production":
logger.add(
sys.stdout,
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {name}:{function}:{line} | {message}",
serialize=True, # JSON output
level="INFO"
)
else:
# Human-readable for development
logger.add(
sys.stdout,
format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>",
level="DEBUG"
)
# File handler (rotated)
logger.add(
"logs/cifra_{time:YYYY-MM-DD}.log",
rotation="00:00", # Rotate daily
retention="30 days",
compression="gz",
level="INFO"
)
# Error handler (separate file)
logger.add(
"logs/errors_{time:YYYY-MM-DD}.log",
rotation="00:00",
retention="90 days",
level="ERROR"
)
Environment:
- [ ] DEBUG=false
- [ ] ENVIRONMENT=production
- [ ] SECRET_KEY - случайный, 50+ символов
- [ ] ALLOWED_HOSTS - только production домены
Database:
- [ ] Сильный пароль (20+ символов)
- [ ] Не использовать default пользователя
- [ ] Включить SSL соединение
- [ ] Регулярные backups (ежедневно)
Redis:
- [ ] Пароль установлен
- [ ] Не слушает на публичном интерфейсе
- [ ] maxmemory policy настроен
HTTPS:
- [ ] SSL сертификат установлен (Let's Encrypt / commercial)
- [ ] HTTP → HTTPS redirect
- [ ] HSTS header включён
- [ ] SSL Labs оценка A+
Headers:
- [ ] X-Frame-Options: DENY
- [ ] X-Content-Type-Options: nosniff
- [ ] X-XSS-Protection: 1; mode=block
- [ ] Content-Security-Policy настроен
- [ ] Strict-Transport-Security
CORS:
- [ ] Настроены allowed origins
- [ ] Не используется "*" wildcard
Rate Limiting:
- [ ] Включён на уровне nginx/application
- [ ] DDoS защита (Cloudflare)
Firewall:
- [ ] Только нужные порты открыты (80, 443)
- [ ] SSH только с определённых IP
- [ ] Database не доступна извне
Monitoring:
- [ ] Prometheus + Grafana настроены
- [ ] Alerting настроен (PagerDuty, Slack)
- [ ] Log aggregation (ELK, CloudWatch)
Backups:
- [ ] Автоматические backups БД (ежедневно)
- [ ] Backups files (еженедельно)
- [ ] Backups хранятся offsite (S3)
- [ ] Restore процедура протестирована
# 1. Проверить логи
docker-compose logs app
# 2. Проверить переменные окружения
docker-compose exec app env | grep DATABASE
# 3. Проверить соединение с БД
docker-compose exec app python -c "from cifra.db import engine; print(engine.url)"
# 4. Проверить миграции
docker-compose exec app alembic current
# 1. Проверить топ процессов
docker stats
# 2. Проверить медленные запросы
docker-compose exec db psql -U cifra -c "SELECT query, calls, mean_exec_time FROM pg_stat_statements ORDER BY mean_exec_time DESC LIMIT 10;"
# 3. Проверить Redis memory
docker-compose exec redis redis-cli info memory
# 4. Включить query logging (временно!)
# postgresql.conf: log_min_duration_statement = 1000 (>1 second)
#!/bin/bash
# rollback.sh
PREVIOUS_VERSION=$1
if [ -z "$PREVIOUS_VERSION" ]; then
echo "Usage: ./rollback.sh <previous-version>"
exit 1
fi
echo "Rolling back to version: $PREVIOUS_VERSION"
# 1. Deploy previous version
docker-compose pull cifra/platform:$PREVIOUS_VERSION
docker-compose up -d
# 2. Rollback database migration (if needed)
read -p "Rollback database? (y/n) " -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]; then
docker-compose exec app alembic downgrade -1
fi
# 3. Verify
curl -f https://app.cifra.io/health
echo "Rollback complete!"
Версия: 1.0
Дата: 2025-11-10
Статус: Production Ready ✅