Шаблоны/ручное создание, access_projects, сид «Открытие аптеки». Плюс SILO/S3 для Django media. Co-authored-by: Cursor <cursoragent@cursor.com>
15 KiB
Развёртывание 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_SECRETDocument 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_URLhttps,*_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) |