Docker-развёртывание Django-проекта: Nginx, Gunicorn, PostgreSQL и Fail2Ban
Пошаговое руководство по настройке для начинающих
Введение
Развёртывание Django-приложения с полным стеком безопасности и автоматизацией. Реальные кейсы из проекта choocha.ru.
1. Архитектура решения

2. Docker Compose: финальная конфигурация
services:
postgres:
image: postgres:17-alpine
container_name: psgr
restart: always
volumes:
- postgres-data:/var/lib/postgresql/data
- ./choocha_db_backup.sql:/docker-entrypoint-initdb.d/init.sql # инициализируем базу данных с помощью файла резервной копии
environment:
- PGTZ=Europe/Moscow # Специфичная переменная для Postgres
- POSTGRES_INITDB_ARGS=--data-checksums # Для безопасности
networks:
- dbnet
app:
build: ./choocha.ru
image: choocha.ru
container_name: choocha.ru-app
restart: always
command: "gunicorn -c gunicorn.py choocha.wsgi"
env_file:
- .env
links:
- "postgres:dbps"
networks:
- dbnet
volumes:
- ./choocha.ru:/app/www/choocha.ru
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
expose:
- 8000
environment:
- PYTHONUNBUFFERED=1 # Для корректного логирования
- PYTHONDONTWRITEBYTECODE=1 # Уменьшает образ
deploy:
resources:
limits:
cpus: '1'
memory: 1G
depends_on:
- postgres
nginx:
image: nginx:alpine
container_name: nginx
restart: always
networks:
- dbnet
ports:
- "80:80"
- "443:443"
volumes:
- ./choocha.ru/static:/app/www/choocha.ru/static
- ./choocha.ru/media:/app/www/choocha.ru/media:rw
- ./nginx:/etc/nginx/conf.d
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
- ./certbot/conf/options-ssl-nginx.conf:/etc/letsencrypt/options-ssl-nginx.conf
- ./certbot/conf/ssl-dhparams.pem:/etc/letsencrypt/ssl-dhparams.pem
- ./logs/nginx:/var/log/nginx # Монтируем логи на хост
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
security_opt:
- no-new-privileges=true
depends_on:
- app
certbot:
image: certbot/certbot
volumes:
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
command: certonly --webroot --webroot-path=/var/www/certbot --email gogamaster@yandex.ru --agree-tos --no-eff-email --keep-until-expiring -d choocha.ru --noninteractive
depends_on:
- nginx
fail2ban:
image: crazymax/fail2ban:latest
container_name: fail2ban
restart: unless-stopped
networks:
- dbnet
volumes:
- ./fail2ban:/data
- ./logs/nginx:/var/log/nginx:ro # Только чтение
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /var/log:/var/log:ro # Мониторим все логи
cap_add:
- NET_ADMIN
- NET_RAW
environment:
- TZ=Europe/Moscow
- F2B_LOG_TARGET=STDOUT
- F2B_LOG_LEVEL=INFO
- F2B_DB_PURGE_AGE=30d
- F2B_IPTABLES_CHAIN=DOCKER-USER # Явное указание цепочки
networks:
dbnet:
driver: bridge
driver_opts:
com.docker.network.driver.mtu: 1450
volumes:
postgres-data:
примечание
файл с резервной копией
choocha_db_backup.sql
является инициализирующим при создании базы данныхPOSTGRES_INITDB_ARGS=--data-checksums
может снизить производительностьпример файла .env
DJANGO_SECRET_KEY="ваш ключ" DJANGO_DEBUG=False DJANGO_ALLOWED_HOSTS=127.0.0.1, localhost, choocha.ru, www.choocha.ru # Database settings DATABASE_ENGINE=django.db.backends.postgresql DATABASE_NAME=имя_базы_данных DATABASE_USERNAME=пользователь_базы_данных DATABASE_PASSWORD=пароль DATABASE_HOST=postgres DATABASE_PORT=5432 # Email settings # Для Яндекса используйте "пароль приложения", а не основной пароль аккаунта # Создать: https://id.yandex.ru/security/app-passwords EMAIL_HOST=smtp.yandex.ru EMAIL_PORT=587 EMAIL_HOST_USER=почтовый_логин EMAIL_HOST_PASSWORD=почтовый_пароль EMAIL_USE_TLS=True
3. Конфиг для nginx.
# Основной HTTP-сервер (редирект на HTTPS + certbot)
# Формат логов для fail2ban
log_format security '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time';
server {
listen 80;
server_name choocha.ru www.choocha.ru;
access_log /var/log/nginx/choocha.ru_access.log security;
error_log /var/log/nginx/choocha.ru_error.log warn;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://choocha.ru$request_uri;
}
}
# Основной HTTPS-сервер
server {
listen 443 ssl;
http2 on;
server_name choocha.ru;
# SSL-сертификаты (должны монтироваться в контейнер)
ssl_certificate /etc/letsencrypt/live/choocha.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/choocha.ru/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# Используем тот же формат логов
access_log /var/log/nginx/choocha.ru_ssl_access.log security;
error_log /var/log/nginx/choocha.ru_ssl_error.log warn;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
#исключения для яндекс-метрики и гугл-аналитики
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' www.googletagmanager.com mc.yandex.ru yastatic.net; style-src 'self' 'unsafe-inline'; img-src 'self' data: yastatic.net; font-src 'self'; connect-src 'self' mc.yandex.ru; frame-src 'self' mc.yandex.ru; object-src 'none';" always;
# Статические файлы
location /static/ {
alias /app/www/choocha.ru/static/;
expires 30d;
access_log off;
}
location /media/ {
alias /app/www/choocha.ru/media/;
expires 30d;
access_log off;
}
location /favicon.ico {
alias /app/www/choocha.ru/static/favicon.ico;
access_log off;
log_not_found off;
}
# Основной прокси-пасс
location / {
proxy_pass http://choocha.ru-app:8000; # Имя сервиса Django из docker-compose
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_redirect off;
client_max_body_size 10M;
}
# Блокировки безопасности
location ~ /\. { deny all; access_log off; log_not_found off; }
location ~* (\.env|wp-|xmlrpc|config\.) { deny all; return 403; }
}
4. Docker-файл проекта
FROM python:3.12-slim
RUN groupadd -r groupdjango && useradd -r -g groupdjango choocha
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
RUN pip install --upgrade pip
WORKDIR /app/www/choocha.ru
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
USER choocha
5. Пример настройки Fail2Ban для Docker
Создаём каталоги.
mkdir -p fail2ban/{filter.d,jail.d,action.d}
Файлы настроек.
Фильтр
root@cv4702363:/home/site# cat fail2ban/filter.d/nginx-botsearch.conf
[Definition]
failregex =
# Блокировка по User-Agent
^<HOST> -.*".*".*".*"(l9explore|Tsunami|sqlmap|nikto|wget|curl|masscan|zgrab|Go-http-client|python-requests)
# Сканирование уязвимых файлов
^<HOST> -.*(GET|POST).*(/\.env|/\.git/config|/wp-config\.php)
# Атаки на WordPress
^<HOST> -.*(GET|POST).*(wp-includes|wp-admin|wp-login\.php|xmlrpc\.php)
# Подозрительные PHP-запросы (если PHP не используется)
^<HOST> -.*GET.*\.php\?.*(cmd|exec|sh)
# DNS-атаки
^<HOST> -.*(GET|POST).*(resolve|dns-query)\?.*
# Строгое правило для бинарных атак (только NULL-байты и SMB)
^<HOST> -.*"(\\x[0-9A-Fa-f]{2})+.*"
# Блокировка известных эксплойтов (например, Log4j)
^<HOST> -.*"\$\{.*\}.*"
ignoreregex =
Описание
root@cv4702363:/home/site# cat fail2ban/jail.d/nginx-docker.conf
[nginx-botsearch]
enabled = true
port = http,https
filter = nginx-botsearch
logpath = /var/log/nginx/choocha.ru_access.log
maxretry = 3
findtime = 1h
bantime = 1d
chain = DOCKER-USER
Действия
root@cv4702363:/home/site# cat fail2ban/action.d/iptables-docker.conf
[Definition]
actionstart = iptables -N f2b-<name>
iptables -A f2b-<name> -j RETURN
iptables -I DOCKER-USER 1 -j f2b-<name>
actionstop = iptables -D DOCKER-USER -j f2b-<name>
iptables -F f2b-<name>
iptables -X f2b-<name>
actioncheck = iptables -n -L DOCKER-USER | grep -q 'f2b-<name>'
actionban = iptables -I f2b-<name> 1 -s <ip> -j DROP
actionunban = iptables -D f2b-<name> -s <ip> -j DROP
root@cv4702363:/home/site# cat fail2ban/jail.local
[nginx-botsearch]
enabled = true
port = http,https
logpath = /var/log/nginx/*access.log
maxretry = 2
findtime = 1h
bantime = 7d
chain = DOCKER-USER
filter = nginx-botsearch.conf
6. Автоматизация бэкапов
Ежедневный бэкап через cron:
скрипт бэкапа в локальное хранилище и на яндекс.диск (с помощью rclone)
root@cv4702363:/home/site# cat postgres_backup.sh
#!/bin/bash
# Параметры
BACKUP_DIR="/home/site/backup"
DB_USER="имя_пользователя"
DB_NAME="база_данных"
CONTAINER_NAME="psgr"
RCLONE_REMOTE="yandex" # Имя удаленного хранилища в rclone config
RCLONE_PATH="/backups" # Путь на Яндекс.Диске
# Создаем директорию для бэкапов
mkdir -p "${BACKUP_DIR}"
# Создаем бэкап с timestamp
BACKUP_FILE="${BACKUP_DIR}/${DB_NAME}_$(date +"%Y-%m-%d_%H-%M-%S").sql.gz"
docker exec -t "${CONTAINER_NAME}" pg_dump -U "${DB_USER}" -d "${DB_NAME}" | gzip > "${BACKUP_FILE}"
# Проверяем успешность создания бэкапа
if [ $? -ne 0 ]; then
echo "[$(date)] Ошибка при создании бэкапа!" >&2
exit 1
fi
# Копируем на Яндекс.Диск
rclone copy "${BACKUP_FILE}" "${RCLONE_REMOTE}:${RCLONE_PATH}"
if [ $? -eq 0 ]; then
echo "[$(date)] Бэкап успешно загружен на Яндекс.Диск: ${BACKUP_FILE}"
else
echo "[$(date)] Ошибка при загрузке на Яндекс.Диск!" >&2
# Не выходим с ошибкой, т.к. локальный бэкап всё равно создан
fi
# Удаляем старые бэкапы (сохраняем последние 7)
find "${BACKUP_DIR}" -name "${DB_NAME}_*.sql.gz" -type f -mtime +7 -delete
# Дополнительно: удаляем старые бэкапы на Яндекс.Диске (последние 30 дней)
rclone delete "${RCLONE_REMOTE}:${RCLONE_PATH}" --min-age 30d --include "*.sql.gz"
echo "[$(date)] Резервное копирование завершено. Локальный: ${BACKUP_FILE}, Яндекс.Диск: ${RCLONE_REMOTE}:${RCLONE_PATH}/$(basename ${BACKUP_FILE})" >> /home/site/backup.log
И задание в crontab
0 2 * * * /home/site/postgres_backup.sh >> /home/site/backup.log 2>&1
7. Практические советы
диагностика
# Проверка синтаксиса Nginx
docker exec -it nginx nginx -t
# Проверка правил Fail2Ban
docker exec -it fail2ban fail2ban-client status nginx-botsearch
# Тест бэкапа
zcat backup.sql.gz | docker exec -i psgr psql -U choocha -d choocha_d
В settings.py добавить
CSRF_TRUSTED_ORIGINS = [
'https://choocha.ru',
'http://choocha.ru',
'https://www.choocha.ru',
'http://www.choocha.ru',
]
возможные ошибки
Не загружаются картинки в ckeditor.
- Проверка текущих прав (на хосте)
ls -ld /home/site/choocha.ru/media/
- Если вывод показывает другого владельца (не 999:999):
drwxr-xr-x 2 root root 4096 Jun 1 10:00 /home/site/choocha.ru/media/
Исправление прав (выполнять на хосте)sudo chown -R 999:999 /home/site/choocha.ru/media/
sudo chmod -R 775 /home/site/choocha.ru/media/
- Проверка внутри контейнера
docker exec -it choocha.ru-app ls -ld /app/www/choocha.ru/media
drwxrwxr-x 2 choocha groupdjango 4096 Jun 15 10:00 /app/www/choocha.ru/media
- Проверка текущих прав (на хосте)
Категория: Администрирование |автор: fominyh_vv
Опубликовано: 05-05-2025 13:16
Комментарии (1)
Тестовый комментарий
Чтобы оставить комментарий, пожалуйста войдите или зарегистрируйтесь.