Files
ACORE/docs/развёртывание-прод.md
T
lokses 030bbc98fd projects: модуль проектов с шагами по отделам и Task.
Шаблоны/ручное создание, access_projects, сид «Открытие аптеки». Плюс SILO/S3 для Django media.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-23 16:26:45 +03:00

15 KiB
Raw Blame History

Развёртывание ACORE в production

Полный мануал первичной установки портала на боевой сервер «с нуля». Перенос данных из тестовой БД описан отдельно — см. перенос-на-прод.md.

Целевая топология (как в .env.example):

        HTTPS                     HTTP :8000
клиент ───────▶ Traefik ───────▶ LXC с ACORE ──▶ PostgreSQL (10.10.31.2:5432)
        (TLS)   (Host rule)      ├─ nginx  ─▶ gunicorn (WSGI)      └─ Redis (:6379, requirepass)
                                 ├─ celery worker
                                 └─ celery beat
  • Traefik терминирует TLS и проксирует на LXC по HTTP с заголовком X-Forwarded-Proto: https (production.py уже настроен на SECURE_PROXY_SSL_HEADER).
  • nginx внутри LXC отдаёт /static/ (и /media/ только пока media на локальном диске).
  • PostgreSQL и Redis — отдельные сервисы (могут быть на других хостах).
  • SILO (S3) — опционально: Django media + primary store Nextcloud; см. silo.md.

0. Требования

Компонент Версия Назначение
Python 3.14 как в .venv проекта
PostgreSQL 14+ основная БД
Redis 6+ кэш (троттлинг логина) + брокер Celery, обязателен requirepass
nginx любой статика/медиа + reverse-proxy
gettext — для compilemessages (uk/en)
gcc, libpq-dev, libldap2-dev, libsasl2-dev — сборка psycopg2 / ldap3

Внешние интеграции (опциональны, настраиваются позже в админке): Mattermost, Nextcloud (WebDAV) + OnlyOffice Document Server, почта (IMAP/SMTP), Active Directory, порталы PNMS/PVMS/MSorter.


1. Системный пользователь и код

# от root
adduser --system --group --home /opt/acore acore
apt-get update && apt-get install -y \
    python3.14 python3.14-venv python3-pip \
    build-essential libpq-dev libldap2-dev libsasl2-dev gettext \
    nginx redis-server postgresql-client git

sudo -u acore -H bash
cd /opt/acore
git clone <REPO_URL> app        # или скопировать релиз
cd app
python3.14 -m venv .venv
.venv/bin/pip install --upgrade pip
.venv/bin/pip install -r requirements.txt

main.css (Tailwind) уже собран и лежит в репозитории — npm-сборка в проде не нужна. Пересобирать только при изменении стилей (см. ACORE-template/).


2. PostgreSQL

На сервере БД (10.10.31.2):

CREATE USER acore WITH PASSWORD 'СИЛЬНЫЙ_ПАРОЛЬ';
CREATE DATABASE acore OWNER acore ENCODING 'UTF8' LC_COLLATE 'C' TEMPLATE template0;
GRANT ALL PRIVILEGES ON DATABASE acore TO acore;

pg_hba.conf — разрешить доступ с IP LXC по scram-sha-256. Проверка с LXC:

psql "postgres://acore:ПАРОЛЬ@10.10.31.2:5432/acore" -c '\conninfo'

3. Redis (с паролем — production это требует)

/etc/redis/redis.conf:

requirepass СИЛЬНЫЙ_REDIS_ПАРОЛЬ
bind 127.0.0.1        # или адрес, доступный только LXC
maxmemory-policy allkeys-lru
systemctl enable --now redis-server
redis-cli -a СИЛЬНЫЙ_REDIS_ПАРОЛЬ ping   # → PONG

Продовые настройки откажутся стартовать, если в CACHE_URL / CELERY_BROKER_URL нет пароля (см. _assert_redis_auth). Временный обход на время cutover: REDIS_ALLOW_UNAUTHENTICATED=1 (убрать сразу после установки requirepass).

Используются две БД Redis: /0 — брокер Celery, /1 — кэш Django.


4. Секреты и .env

Сгенерировать два независимых секрета:

python3 -c "import secrets; print(secrets.token_urlsafe(64))"   # для SECRET_KEY
python3 -c "import secrets; print(secrets.token_urlsafe(64))"   # для FIELD_ENCRYPTION_KEY

⚠️ FIELD_ENCRYPTION_KEY — это не Fernet-ключ, а любая длинная строка (внутри core/utils/crypto.py она хэшируется в ключ). Задать один раз и не менять — смена ключа делает все зашифрованные секреты коннекторов (пароли/токены) нечитаемыми. Хранить в менеджере секретов.

/opt/acore/app/.env:

DJANGO_SETTINGS_MODULE=acore.settings.production
DEBUG=False

SECRET_KEY=<секрет-1>
FIELD_ENCRYPTION_KEY=<секрет-2>

ALLOWED_HOSTS=portal.example.local,10.10.31.3
PORTAL_BASE_URL=https://portal.example.local
CSRF_TRUSTED_ORIGINS=https://portal.example.local

DATABASE_URL=postgres://acore:ПАРОЛЬ@10.10.31.2:5432/acore

# Redis с паролем (две отдельные БД)
CACHE_URL=redis://:REDIS_ПАРОЛЬ@127.0.0.1:6379/1
CELERY_BROKER_URL=redis://:REDIS_ПАРОЛЬ@127.0.0.1:6379/0
CELERY_RESULT_BACKEND=redis://:REDIS_ПАРОЛЬ@127.0.0.1:6379/0

# Интеграции (заполняются позже; пустые = fail-closed)
# MATTERMOST_SLASH_COMMAND_TOKEN=
# MATTERMOST_INTERACTIVE_TOKEN=
# PNMS_SYNC_ENABLED=1 / PNMS_BASE_URL=https://... / PNMS_ACORE_TOKEN=...
# MSORTER_* / PVMS_*  — см. .env.example
chmod 600 /opt/acore/app/.env && chown acore:acore /opt/acore/app/.env

5. Миграции, статика, локали, суперпользователь

cd /opt/acore/app
export DJANGO_SETTINGS_MODULE=acore.settings.production

.venv/bin/python manage.py check --deploy      # должно быть 0 issues
.venv/bin/python manage.py migrate
.venv/bin/python manage.py collectstatic --noinput   # → staticfiles/
.venv/bin/python manage.py compilemessages           # uk/en (нужен gettext)
.venv/bin/python manage.py rebuild_directory_tree    # пересборка дерева справочников
.venv/bin/python manage.py createsuperuser

При первом входе суперпользователь обязан включить 2FA (middleware принуждает всех staff). Держите под рукой доступ к TOTP-приложению.


6. gunicorn (systemd)

/etc/systemd/system/acore-web.service:

[Unit]
Description=ACORE gunicorn
After=network.target

[Service]
User=acore
Group=acore
WorkingDirectory=/opt/acore/app
EnvironmentFile=/opt/acore/app/.env
ExecStart=/opt/acore/app/.venv/bin/gunicorn acore.wsgi:application \
    --bind 127.0.0.1:8000 \
    --workers 5 \
    --timeout 120 \
    --access-logfile - --error-logfile -
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target

Воркеры: (2 × vCPU) + 1. Загрузки/скачивания документов и OnlyOffice-callback могут быть крупными — --timeout 120.


7. Celery worker + beat (systemd)

/etc/systemd/system/acore-celery.service:

[Unit]
Description=ACORE Celery worker
After=network.target redis-server.service

[Service]
User=acore
Group=acore
WorkingDirectory=/opt/acore/app
EnvironmentFile=/opt/acore/app/.env
ExecStart=/opt/acore/app/.venv/bin/celery -A acore worker -l info --concurrency=4
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

/etc/systemd/system/acore-celery-beat.service:

[Unit]
Description=ACORE Celery beat
After=network.target redis-server.service

[Service]
User=acore
Group=acore
WorkingDirectory=/opt/acore/app
EnvironmentFile=/opt/acore/app/.env
ExecStart=/opt/acore/app/.venv/bin/celery -A acore beat -l info \
    --scheduler django_celery_beat.schedulers:DatabaseScheduler
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now acore-web acore-celery acore-celery-beat
systemctl status acore-web

Celery нужен для фоновой синхронизации (Mattermost, AD, push в PNMS/PVMS/MSorter). Периодические задачи задаются в админке (django-celery-beat, DatabaseScheduler).


8. nginx (статика/медиа + proxy)

/etc/nginx/sites-available/acore:

server {
    listen 80;
    server_name portal.example.local 10.10.31.3;

    client_max_body_size 110m;          # аплоады до 100 МБ (документы)

    location /static/ { alias /opt/acore/app/staticfiles/; access_log off; expires 30d; }
    # При SILO (S3_ENDPOINT в .env) /media/ с диска не нужен — URL подписанные.
    location /media/  { alias /opt/acore/app/media/;       access_log off; }

    location / {
        proxy_pass http://127.0.0.1:8000;
        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 $http_x_forwarded_proto;   # приходит от Traefik
        proxy_read_timeout 120s;
    }
}
ln -s /etc/nginx/sites-available/acore /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

Traefik (внешний): Host-rule на portal.example.local → сервис http://10.10.31.3:80, TLS-termination + редирект на HTTPS. Убедитесь, что Traefik проставляет X-Forwarded-Proto: https (по умолчанию так и есть) — иначе SECURE_SSL_REDIRECT уйдёт в цикл редиректов.


9. Настройка интеграций (после запуска, в админке)

Все секреты коннекторов шифруются в БД (EncryptedCharField) — вводятся в Админка → Настройки → Конфиги коннекторов.

  • Mattermost — bot-токен + MATTERMOST_SLASH_COMMAND_TOKEN/INTERACTIVE_TOKEN в .env. Команды: create_mattermost_bot, import_mattermost_teams/channels, sync_users_to_teams.
  • Nextcloud — ConnectorConfig(nextcloud): host/логин/пароль сервисного аккаунта (WebDAV). Файлы NC лучше держать в SILO (primary object store) — silo.md.
  • SILO / media — при S3_ENDPOINT + ключах в .env фото и прочий MEDIA пишутся в бакет acore-media; nginx /media/ с диска не нужен.
  • OnlyOffice (ConnectorConfig(onlyoffice)):
    • host/port/use_ssl → публичный URL Document Server (браузер грузит api.js);
    • «Токен» = JWT_SECRET Document Server (JWT_ENABLED=true, JWT_HEADER=Authorization);
    • extra_config: {"public_url": "...", "callback_base": "https://portal.example.local", "verify_ssl": true}.
    • Сеть в обе стороны: браузер → DS, DS → портал (/documents/onlyoffice/content|callback). Если DS ходит к порталу по внутреннему адресу — добавьте его в extra_config.download_hosts (иначе SSRF-guard отклонит сохранение).
  • Почта — ConnectorConfig(mail): IMAP/SMTP хост, сервисный аккаунт.
  • Active Directory — ConnectorConfig(ad); синк: sync_users_to_ad.
  • PNMS/PVMS/MSorter — включаются флагами в .env (*_SYNC_ENABLED=1, *_BASE_URL https, *_ACORE_TOKEN). Первичная выгрузка: push_to_pnms / push_to_pvms / push_to_msorter.

10. Проверочный чек-лист

export DJANGO_SETTINGS_MODULE=acore.settings.production
cd /opt/acore/app
.venv/bin/python manage.py check --deploy        # 0 issues
.venv/bin/python manage.py makemigrations --check --dry-run   # No changes detected
  • https://portal.example.local/ открывается, редирект c http на https работает;
  • логин, принудительная 2FA у staff;
  • /static/ отдаёт nginx; /media/ — nginx или подписанные URL SILO (см. silo.md);
  • systemctl status acore-web acore-celery acore-celery-beat — все active (running);
  • redis-cli -a … -n 1 dbsize растёт при попытках логина (кэш-троттлинг жив);
  • загрузка документа в аптеке и открытие в OnlyOffice → правка сохраняется в Nextcloud.

11. Резервное копирование

# БД (ежедневно, cron от пользователя postgres или acore)
pg_dump "postgres://acore:ПАРОЛЬ@10.10.31.2:5432/acore" | gzip > /backup/acore-$(date +%F).sql.gz

# Медиа
rsync -a /opt/acore/app/media/ /backup/media/

# КРИТИЧНО: .env (SECRET_KEY + FIELD_ENCRYPTION_KEY) — в менеджер секретов.
# Потеря FIELD_ENCRYPTION_KEY = потеря всех секретов коннекторов.

12. Обновление (деплой новой версии)

sudo -u acore -H bash
cd /opt/acore/app
git pull                                   # или распаковать релиз
.venv/bin/pip install -r requirements.txt
export DJANGO_SETTINGS_MODULE=acore.settings.production
.venv/bin/python manage.py migrate
.venv/bin/python manage.py collectstatic --noinput
.venv/bin/python manage.py compilemessages
exit
systemctl restart acore-web acore-celery acore-celery-beat

13. Частые грабли

Симптом Причина / решение
ImproperlyConfigured: FIELD_ENCRYPTION_KEY must be set не задан ключ в .env
ImproperlyConfigured: CACHE_URL must include a Redis password нет пароля в Redis-URL; задать requirepass или временно REDIS_ALLOW_UNAUTHENTICATED=1
Бесконечный редирект на https Traefik не шлёт X-Forwarded-Proto: https / nginx не пробрасывает заголовок
Секреты коннекторов «поехали» после смены ключа FIELD_ENCRYPTION_KEY менять нельзя после ввода секретов
Стили/иконки админки не грузятся не выполнен collectstatic или nginx location /static/ указывает не туда
OnlyOffice не сохраняет файл не совпадает JWT_SECRET↔«Токен», или DS не достаёт callback_base; проверьте download_hosts
uk/en не переведены не выполнен compilemessages (нет пакета gettext)