From 5678624608a95ce91c3942314b8c7e4b27759084 Mon Sep 17 00:00:00 2001 From: erjemin Date: Wed, 20 May 2026 13:36:33 +0300 Subject: [PATCH] =?UTF-8?q?add:=20Production=20docker-compose.yml=20=D1=81?= =?UTF-8?q?=20nginx=20=D0=B8=20watchtower=20+=20=D0=B8=D0=BD=D1=81=D1=82?= =?UTF-8?q?=D1=80=D1=83=D0=BA=D1=86=D0=B8=D1=8F=20=D0=B4=D0=B5=D0=BF=D0=BB?= =?UTF-8?q?=D0=BE=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PRODUCTION_DEPLOY.md | 239 ++++++++++++++++++++++++++++++++++++++++ docker-compose.prod.yml | 123 +++++++++++++++++++++ 2 files changed, 362 insertions(+) create mode 100644 PRODUCTION_DEPLOY.md create mode 100644 docker-compose.prod.yml diff --git a/PRODUCTION_DEPLOY.md b/PRODUCTION_DEPLOY.md new file mode 100644 index 0000000..5450599 --- /dev/null +++ b/PRODUCTION_DEPLOY.md @@ -0,0 +1,239 @@ +# Production Deployment Guide для Oknardia + +## Структура на хосте + +Структура директорий на production сервере должна быть: + +``` +~/docker-app/oknardia-site/ +├── .env # Переменные окружения Django +├── config/ # Конфиги +│ └── nginx/ +│ └── oknardia-app--external-nginx.conf # Конфиг Nginx (шаблон) +├── database/ # БД SQLite (создается автоматически) +│ └── oknardia.sqlite3 +├── media/ # Медиа файлы, uploads, кеши +│ ├── img_avatar/ +│ ├── img_seria/ +│ ├── _serv_sitemap/ +│ └── _error/ # Error pages (генерируются контейнером) +└── docker-compose.prod.yml # Конфиг Docker +``` + +## Установка на Production сервере + +### 1. Создать директории и скопировать файлы + +```bash +# На хосте +mkdir -p ~/docker-app/oknardia-site/{config/nginx,database,media} + +# Скопировать docker-compose.prod.yml и .env из проекта +cp docker-compose.prod.yml ~/docker-app/oknardia-site/ +cp .env ~/docker-app/oknardia-site/ + +# Скопировать конфиг nginx +cp config/nginx/oknardia-app--external-nginx.conf ~/docker-app/oknardia-site/config/nginx/ +``` + +### 2. Настроить .env на сервере + +Отредактировать `~/docker-app/oknardia-site/.env`: + +```bash +# Критично: должны быть правильные credentials для реестра Gitea +REPO_USER=erjemin +REPO_PASS=<сгенерированный токен из личного кабинета> + +# Критично: переменная с путем проекта для sed в конфиге nginx +export HOST_PROJECT_PATH=/home/default_user/projects/oknardia-site +# (если путь другой, обновить эту переменную) + +# Остальное можно скопировать из локального .env +DEBUG=False +DJANGO_SECRET_KEY=... +``` + +### 3. Запустить контейнеры + +```bash +cd ~/docker-app/oknardia-site/ + +# Первый запуск (холодный старт) +docker compose -f docker-compose.prod.yml up -d + +# Проверить логи +docker compose -f docker-compose.prod.yml logs -f web + +# Проверить статус контейнеров +docker compose -f docker-compose.prod.yml ps +``` + +### 4. Проверить что nginx конфиг был сгенерирован + +После первого запуска контейнер должен: +- Создать `/config/nginx/oknardia-app--external-nginx.conf` (боевой конфиг) +- Создать `/config/nginx/oknardia-app--external-nginx.conf.example` (exemplo) + +```bash +ls -la ~/docker-app/oknardia-site/config/nginx/ +``` + +### 5. Настроить Nginx на хосте + +На хосте установить Nginx и скопировать/ссылаться на конфиг из контейнера: + +```bash +# На хосте (например, Ubuntu 20.04) +sudo systemctl install nginx + +# Включить сгенерированный конфиг в основной конфиг Nginx +# /etc/nginx/sites-available/oknardia или /etc/nginx/conf.d/oknardia.conf + +# Пример: +sudo ln -s /home/default_user/projects/oknardia-site/config/nginx/oknardia-app--external-nginx.conf \ + /etc/nginx/sites-enabled/oknardia + +# Проверить и перезагрузить Nginx +sudo nginx -t +sudo systemctl reload nginx +``` + +## Команды для управления + +```bash +cd ~/docker-app/oknardia-site/ + +# Запустить контейнеры (если они были остановлены) +docker compose -f docker-compose.prod.yml up -d + +# Остановить контейнеры +docker compose -f docker-compose.prod.yml down + +# Перезагрузить контейнеры (удалить и создать заново) +docker compose -f docker-compose.prod.yml down +docker compose -f docker-compose.prod.yml up -d + +# Посмотреть логи +docker compose -f docker-compose.prod.yml logs -f web +docker compose -f docker-compose.prod.yml logs -f watchtower + +# Выполнить команду внутри контейнера Django +docker compose -f docker-compose.prod.yml exec web python manage.py shell + +# Пересоздать пре-рендер кеши вручную +docker compose -f docker-compose.prod.yml exec web python manage.py regenerate_seria_prerender +``` + +## Что делает docker-compose.prod.yml + +### Сервис `web` (Django + Gunicorn) + +При старте контейнера: +1. ✅ Применяет миграции БД (`migrate --noinput`) +2. ✅ Собирает статику (`collectstatic --noinput`) +3. ✅ Генерирует sitemap'ы (`generate_sitemaps`) +4. ✅ Пересоздает пре-рендер кеши для серий (`regenerate_seria_prerender`) +5. ✅ Пересчитывает рейтинги (`make_rating`) +6. ✅ Копирует конфиг nginx с авто-заменой путей (через `sed`) +7. ✅ Запускает Gunicorn на `127.0.0.1:8000` + +### Сервис `watchtower` (авто-обновление) + +- 👁️ Следит за реестром Gitea (проверка каждые 30 минут) +- 🔄 Автоматически скачивает новый образ, если он есть +- ♻️ По-graceful перезагружает контейнер web +- 🗑️ Удаляет старые образы (WATCHTOWER_CLEANUP=true) + +Watchtower использует метку на контейнере `web`: +```yaml +labels: + - "com.centurylinklabs.watchtower.scope=oknardia-scope" +``` + +## Файлы, которые монтируются с хоста + +| На хосте | В контейнере | Назначение | +|----------|----------|-----------| +| `./database/` | `/home/app/database/` | БД SQLite (КРИТИЧНО!) | +| `./media/` | `/home/app/public/media/` | Медиа файлы, uploads | +| `./config/` | `/nginx_configs_host/` | Конифиги (читаются и пишущутся контейнером) | + +## Переменные окружения в docker-compose.prod.yml + +```yaml +environment: + - DEBUG=False # Production: без debug + - DJANGO_LOG_LEVEL=INFO # Минимум логов + - ALLOW_MEDIA_SERVE=False # Nginx обслуживает медиа + - HOST_PROJECT_PATH=... # Путь для sed-замены в конфиге nginx + - REPO_USER=${REPO_USER} # Из .env (для Watchtower) + - REPO_PASS=${REPO_PASS} # Из .env (для Watchtower) +``` + +## Health Check + +Контейнер имеет healthcheck, который проверяет доступность Django: +- Проверка каждые 3 минуты +- Таймаут: 12 секунд +- Unhealthy после 3 неудачных попыток +- Это критично для Watchtower (он ждет что контейнер станет healthy перед finalization) + +## Troubleshooting + +### ContainerError при запуске + +Проверить логи: +```bash +docker compose -f docker-compose.prod.yml logs web +``` + +### БД не синхронизируется между перезагрузками + +Убедиться что `./database/` правильно монтирует: +```bash +docker compose -f docker-compose.prod.yml exec web ls -la /home/app/database/ +``` + +### Watchtower не обновляет контейнер + +Проверить: +- Есть ли интернет на сервере +- Правильны ли REPO_USER и REPO_PASS в .env +- Существует ли образ `oknardia:latest` в реестре + +```bash +docker compose -f docker-compose.prod.yml logs watchtower +``` + +### Nginx конфиг не обновился + +Конфиг копируется при старте контейнера. Если нужно пересоздать: +```bash +docker compose -f docker-compose.prod.yml down +docker compose -f docker-compose.prod.yml up -d +``` + +Потом проверить: +```bash +cat ~/docker-app/oknardia-site/config/nginx/oknardia-app--external-nginx.conf +``` + +## Резервные копии + +Важно регулярно бэкапить: +- `./database/oknardia.sqlite3` - БД +- `./media/` - Загруженные файлы + +Пример cronjob для ежедневного бэкапа: +```bash +# /etc/cron.daily/backup-oknardia +#!/bin/bash +BACKUP_DIR="/backup/oknardia" +APP_DIR="/home/default_user/projects/oknardia-site" + +mkdir -p $BACKUP_DIR +tar -czf $BACKUP_DIR/oknardia-$(date +%Y%m%d).tar.gz $APP_DIR +find $BACKUP_DIR -name "oknardia-*.tar.gz" -mtime +30 -delete # Удалять старше 30 дней +``` + diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..b6d3b6d --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,123 @@ +# ============================================================================== +# Docker Compose для PRODUCTION +# Этот файл запускается на боевом сервере. +# Вариант 1 (если переименовали в docker-compose.yml): docker compose up -d +# Вариант 2 (если оставили имя): docker compose -f docker-compose.prod.yml up -d +# ============================================================================== + +services: + # --- ОСНОВНОЙ СЕРВИС: DJANGO + GUNICORN + WHITENOISE --- + web: + # Имя контейнера для удобства + container_name: oknardia-backend + + # В production используем готовый, собранный образ из реестра (Gitea) + image: git.cube2.ru/erjemin/oknardia:latest + + # ПЕРЕЗАПУСК при сбое + restart: always + + # Метки для Watchtower (авто-обновление) + labels: + - "com.centurylinklabs.watchtower.scope=oknardia-scope" + + # КОМАНДА ЗАПУСКА (Production режим) + # При старте контейнера: + # 1. Применяем миграции + # 2. Собираем статику + # 3. Генерируем sitemap'ы + # 4. Пересоздаём пре-рендер шаблоны серий + # 5. Пересчитываем рейтинги + # 6. Создаём папку конфигов nginx (если нет) + # 7. Копируем конфиг nginx с авто-заменой путей через sed + # 8. Инициализируем боевой конфиг (если нет, копируем из примера) + # 9. Запускаем Gunicorn + command: > + sh -c "python manage.py migrate --noinput && + python manage.py collectstatic --noinput && + python manage.py generate_sitemaps && + python manage.py regenerate_seria_prerender && + python manage.py make_rating && + mkdir -p /nginx_configs_host/nginx && + sed \"s|/home/user/app/oknardia-site|$${HOST_PROJECT_PATH:-/home/default_user/projects/oknardia-site}|g\" /home/app/config/nginx/oknardia-app--external-nginx.conf > /nginx_configs_host/nginx/oknardia-app--external-nginx.conf.example && + if [ ! -f /nginx_configs_host/nginx/oknardia-app--external-nginx.conf ]; then + cp /nginx_configs_host/nginx/oknardia-app--external-nginx.conf.example /nginx_configs_host/nginx/oknardia-app--external-nginx.conf; + echo 'INIT: Created new nginx config with correct paths'; + fi && + python -m gunicorn --workers 4 --bind 0.0.0.0:8000 --timeout 120 oknardia.wsgi:application" + + # Пробрасывание портов + # Слушаем только на localhost хоста для безопасности + ports: + - "127.0.0.1:8060:8000" + + # МОНТИРОВАНИЕ ТОМОВ (Volumes) + volumes: + # БД SQLite + - ./database:/home/app/database + + # Медиа файлы + - ./media:/home/app/public/media + + # Конфиги nginx + - ./config:/nginx_configs_host + + # Пользователь и права + user: "0:0" + + # ПЕРЕМЕННЫЕ ОКРУЖЕНИЯ (Production) + env_file: + - .env + environment: + - DJANGO_SETTINGS_MODULE=oknardia.settings + - PYTHONUNBUFFERED=1 + - DEBUG=False + - DJANGO_LOG_LEVEL=INFO + - ALLOW_MEDIA_SERVE=False + - HOST_PROJECT_PATH=${HOST_PROJECT_PATH:-/home/default_user/projects/oknardia-site} + + # ЗДОРОВЬЕ КОНТЕЙНЕРА (Healthcheck) + healthcheck: + test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/').read()"] + interval: 3m + timeout: 12s + start_period: 60s + retries: 3 + + # ЛОГИРОВАНИЕ (Ротация) + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "5" + + # РЕСУРСЫ + deploy: + resources: + limits: + cpus: '1.0' + memory: 1G + mem_limit: 1g + + # --- WATCHTOWER: АВТО-ОБНОВЛЕНИЕ ОБРАЗОВ --- + watchtower: + image: containrrr/watchtower + container_name: oknardia_watchtower + restart: always + volumes: + - /var/run/docker.sock:/var/run/docker.sock + environment: + - REPO_USER=${REPO_USER} + - REPO_PASS=${REPO_PASS} + - WATCHTOWER_SCOPE=oknardia-scope + - WATCHTOWER_CLEANUP=true + - DOCKER_API_VERSION=1.44 + - WATCHTOWER_WAIT_ON_TIMEOUT=60 + - WATCHTOWER_LIFECYCLE_HOOKS=true + command: --interval 1800 --cleanup + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" +