Files
han-app/VM1_app/codebase/backend/deployment/DEPLOYMENT_GUIDE.ru.md
T

30 KiB
Raw Blame History

Подробная инструкция по развертыванию и запуску HAN Chat

Эта инструкция описывает первый запуск текущего legacy/stub проекта ВМ1 на одной виртуальной машине с Ubuntu 24.04. Она не разворачивает target ВМ2 Processing и не подтверждает production-готовность Message Safety v2. Все команды предполагают, что проект расположен в /opt/han-chat/backend, а команды Docker Compose выполняются из этого каталога.

PostgreSQL и Selectel S3 не запускаются в Docker Compose: их необходимо создать заранее как внешние управляемые сервисы. Из интернета должны быть доступны только порты 80 и 443 виртуальной машины.

1. Что потребуется до начала работы

Подготовьте:

  1. Виртуальную машину с Ubuntu 24.04 и минимум 4 vCPU, 8 ГБ RAM и 40 ГБ диска.
  2. SSH-доступ к VM пользователем с правом sudo.
  3. Домен, например chat.example.ru, и возможность изменить его DNS.
  4. Управляемый PostgreSQL, доступный VM по приватной сети.
  5. Три приватных бакета Selectel S3.
  6. Учетные данные приложения Bitrix24.
  7. При необходимости — удаленный OTLP-бэкенд для телеметрии.
  8. Локальную копию каталога HAN_chat_specification/codebase/backend либо URL Git-репозитория, из которого его можно получить.

Для первого тестового запуска допустимы mock OTP, заглушка Message Safety и заглушка bitrix-sync. Они не являются полноценными production-реализациями.

Целевой cutover выполняется по modules/module-10-deployment-runbook.md: самостоятельная ВМ2, root Compose/systemd unit, собственный nginx с public exact CRM webhook 80/443 и private Message Safety listener 8443, раздельные TLS-контуры, secrets/IAM, egress allow-list и local OTEL Collector. Не переносите команды этого single-VM guide на ВМ2 без VM2-specific manifests.

2. Первичный вход на VM

Подключитесь к созданной VM облачным пользователем:

ssh <cloud-user>@<VM_IP>

Проверьте версию ОС:

cat /etc/os-release

Должна использоваться Ubuntu 24.04 или более новая версия.

3. Передача и запуск скрипта настройки VM

Сначала передайте на VM только подготовительный скрипт. Например, с локального компьютера:

scp deployment/scripts/setup-vm.sh <cloud-user>@<VM_IP>:/tmp/setup-vm.sh

На VM выполните:

chmod +x /tmp/setup-vm.sh
sudo /tmp/setup-vm.sh

Скрипт:

  • обновит Ubuntu и установит базовые пакеты;
  • создаст пользователя deploy;
  • установит Docker Engine и Docker Compose;
  • настроит UFW, fail2ban и цепочку DOCKER-USER;
  • откроет только SSH, HTTP и HTTPS;
  • создаст /opt/han-chat/backend;
  • создаст swap;
  • включит автоматические обновления безопасности;
  • отключит парольный SSH-вход и X11 forwarding;
  • заблокирует локальные пароли root и deploy после проверки SSH-ключей.

Если authorized_keys пользователя deploy отсутствует, скрипт остановится до блокировки паролей. HARDEN_SSH=true дополнительно запрещает прямой вход пользователем root и SSH TCP forwarding; включайте этот режим только после проверки входа пользователем deploy по ключу в отдельной сессии.

Если SSH работает на нестандартном порту или имя внешнего интерфейса известно заранее, передайте параметры:

sudo SSH_PORT=2222 EXTERNAL_IF=ens3 /tmp/setup-vm.sh

После завершения выйдите из SSH-сессии: членство deploy в группе docker начинает действовать только после нового входа.

exit
ssh deploy@<VM_IP>
docker version
docker compose version

4. Копирование проекта на VM

Вариант A — через Git

Это предпочтительный вариант: Git применит правило LF для shell-скриптов.

git clone <URL_РЕПОЗИТОРИЯ> /tmp/han-chat-source
cp -a /tmp/han-chat-source/HAN_chat_specification/codebase/backend/. \
  /opt/han-chat/backend/
cd /opt/han-chat/backend

Если HAN_chat_specification является корнем репозитория:

cp -a /tmp/han-chat-source/codebase/backend/. /opt/han-chat/backend/

Вариант B — архивом с локального компьютера

Создайте архив именно из содержимого каталога backend, включая скрытые файлы:

tar -C HAN_chat_specification/codebase/backend -czf han-chat-backend.tar.gz .
scp han-chat-backend.tar.gz deploy@<VM_IP>:/tmp/

На VM:

cd /opt/han-chat/backend
tar -xzf /tmp/han-chat-backend.tar.gz

# Обязательно при копировании с Windows:
find . -type f \( -name '*.sh' -o -name 'validate-env' \) -exec dos2unix {} +
chmod +x scripts/validate-env deployment/scripts/*.sh redis/scripts/*.sh nginx/scripts/*.sh

Проверьте наличие точки запуска:

test -f /opt/han-chat/backend/docker-compose.yml
test -f /opt/han-chat/backend/.env.example

5. Настройка DNS и сетевого доступа

Создайте DNS-запись:

chat.example.ru A <ПУБЛИЧНЫЙ_IP_VM>

Дождитесь обновления DNS:

getent ahostsv4 chat.example.ru

В облачной группе безопасности VM разрешите входящие подключения:

  • TCP 80 из интернета;
  • TCP 443 из интернета;
  • SSH только из доверенной сети или с административного IP.

Не открывайте наружу порты 6379, 4317, 4318, 8000, 8080 и 9000.

В группе безопасности PostgreSQL разрешите входящий трафик на порт PostgreSQL только от приватного адреса или группы безопасности VM.

6. Подготовка управляемого PostgreSQL

Создайте одну базу данных:

han_chat

В ней нужны схемы:

han_app
bitrix_local
bitrix_sync
message_safety
keycloak

Для текущей MVP-реализации используются следующие пользователи:

han_app
bitrix_local_app
bitrix_sync_user
message_safety_app
keycloak_user

Создать пользователей и схемы можно через панель провайдера либо от имени администратора PostgreSQL. Пример SQL:

CREATE ROLE han_app LOGIN PASSWORD '<HAN_APP_PASSWORD>';
CREATE ROLE bitrix_local_app LOGIN PASSWORD '<BITRIX_LOCAL_PASSWORD>';
CREATE ROLE bitrix_sync_user LOGIN PASSWORD '<BITRIX_SYNC_PASSWORD>';
CREATE ROLE message_safety_app LOGIN PASSWORD '<SAFETY_PASSWORD>';
CREATE ROLE keycloak_user LOGIN PASSWORD '<KEYCLOAK_PASSWORD>';

CREATE SCHEMA IF NOT EXISTS han_app AUTHORIZATION han_app;
CREATE SCHEMA IF NOT EXISTS bitrix_local AUTHORIZATION bitrix_local_app;
CREATE SCHEMA IF NOT EXISTS bitrix_sync AUTHORIZATION bitrix_sync_user;
CREATE SCHEMA IF NOT EXISTS message_safety AUTHORIZATION message_safety_app;
CREATE SCHEMA IF NOT EXISTS keycloak AUTHORIZATION keycloak_user;

GRANT CONNECT ON DATABASE han_chat TO
  han_app, bitrix_local_app, bitrix_sync_user, message_safety_app, keycloak_user;

Текущие migration jobs используют те же DSN, что и сервисы. Поэтому владельцы схем должны иметь право создавать таблицы в своих схемах. Для более строгого production-разделения migration/runtime ролей потребуется отдельная настройка DSN и прав, которой в текущем .env.example нет.

Скачайте CA-сертификат PostgreSQL у провайдера и поместите его на VM:

mkdir -p /opt/han-chat/backend/secrets/pg
cp /путь/к/ca.pem /opt/han-chat/backend/secrets/pg/ca.pem
chmod 644 /opt/han-chat/backend/secrets/pg/ca.pem

CA-сертификат не является секретом. Права 644 нужны, чтобы его могли прочитать контейнеры, работающие не от root.

Проверьте сетевую доступность:

nc -vz <PG_HOST> 6432

Замените 6432 на фактический порт провайдера.

7. Подготовка Selectel S3

Создайте три приватных бакета:

han-chat-quarantine
han-chat-attachments
han-chat-documents

Создайте две пары ключей:

  1. Ключ API с правом чтения и записи в бакеты.
  2. Отдельный ключ Message Safety только с правом чтения карантина.

Для бакетов запретите публичный доступ. Для браузерной загрузки настройте CORS:

  • Allowed origin: https://chat.example.ru;
  • Methods: PUT, GET, HEAD;
  • Headers: Content-Type, x-amz-*;
  • Expose header: ETag.

Для карантина задайте lifecycle удаления объектов с запасом относительно MESSAGE_SAFETY_TASK_TTL_SEC.

8. Создание файла окружения

.env содержит только несекретную конфигурацию. На VM:

cd /opt/han-chat/backend
umask 077
cp .env.example .env
chmod 600 .env
nano .env

Замените несекретные адреса example.*. Не добавляйте в .env пароли, токены, ключи, credential-bearing DSN или пути *_FILE. Установите отдельно проверенный launcher deployment/secrets/han-secrets из ops-пакета. Его интерфейс:

deployment/secrets/han-secrets run --config .env -- <command>

Launcher читает SECRETS_SOURCE=file|selectel, устанавливает HAN_SECRETS_ACTIVE=1, выдаёт значения только дочернему процессу и не печатает их. Рекомендуемый HAN_RUNTIME_SECRET_MANIFEST содержит только пары SECRET_KEY=/absolute/protected/path; файлы имеют mode 0400/0600.

8.1. Основные адреса

Для домена chat.example.ru:

APP_ENV=production-like
RELEASE_VERSION=2026-07-13-1

PUBLIC_HOST=chat.example.ru
PUBLIC_WEB_URL=https://chat.example.ru
PUBLIC_API_URL=https://chat.example.ru/api
PUBLIC_AUTH_URL=https://chat.example.ru/auth

KEYCLOAK_PUBLIC_URL=https://chat.example.ru/auth
KEYCLOAK_INTERNAL_URL=http://keycloak:8080/auth
KEYCLOAK_REALM=han-chat
KEYCLOAK_AUDIENCE=han-chat-api

8.2. PostgreSQL

В .env укажите только host, port, database и путь к публичному CA:

HAN_PG_HOST=<PG_HOST>
HAN_PG_PORT=6432
HAN_PG_DATABASE=han_chat
PG_CA_HOST_PATH=/opt/han-chat/backend/secrets/pg/ca.pem

KEYCLOAK_DB_SCHEMA=keycloak

Все service DSN, включая JDBC и backup DSN, формирует secret backend. Runtime validator проверяет verify-full, sslrootcert и запрет options/currentSchema без вывода строк подключения.

Порт 5433 используется с PgBouncer в режиме session. Не добавляйте options=-csearch_path... или JDBC-параметр currentSchema: они передают startup parameter search_path, который Selectel PgBouncer отклоняет. Для Keycloak схема задаётся отдельно через KEYCLOAK_DB_SCHEMA. Для каждой сервисной роли заранее задайте database-level search_path.

Если пароль содержит @, :, /, ?, # или %, его необходимо URL-кодировать внутри PostgreSQL URL.

8.3. Подготовка runtime-секретов

Генерируйте секреты вне shell history средствами secret manager. Не выполняйте export TOKEN=... и не вставляйте значения в команды. Имена обязательных runtime-переменных определены в scripts/validate-env; парные токены связываются в secret backend.

openssl rand -hex 32

Для BITRIX_TOKEN_ENCRYPTION_KEY нужен URL-safe Base64 ключ ровно из 32 байт:

python3 -c 'import base64,secrets; print(base64.urlsafe_b64encode(secrets.token_bytes(32)).decode())'

Ни один из этих секретов не добавляется в .env. Пары проверяются runtime validator без вывода значений.

BITRIX_LOCAL_APP_INTERNAL_TOKEN=<TOKEN_A>
BITRIX_INTERNAL_API_TOKEN=<TOKEN_A>

BITRIX_API_FORWARD_TOKEN=<TOKEN_B>
BITRIX_API_INBOX_TOKEN=<TOKEN_B>

Остальные токены должны быть разными:

MESSAGE_SAFETY_SERVICE_TOKEN=<UNIQUE_TOKEN>
BITRIX_SYNC_SERVICE_TOKEN=<UNIQUE_TOKEN>
KEYCLOAK_SETTINGS_BRIDGE_TOKEN=<UNIQUE_TOKEN>
CURSOR_HMAC_SECRET=<UNIQUE_TOKEN>
KEYCLOAK_OTP_HMAC_KEY=<UNIQUE_TOKEN_НЕ_КОРОЧЕ_32_БАЙТ>
BITRIX_TOKEN_ENCRYPTION_KEY=<URLSAFE_BASE64_KEY>
KEYCLOAK_ADMIN_PASSWORD=<UNIQUE_ADMIN_PASSWORD>

8.4. Redis

Создайте три разных пароля в secret backend; там же сформируйте Redis URL. Следующий блок описывает логический контракт и не является содержимым .env:

REDIS_API_PASSWORD=<REDIS_API_PASSWORD>
REDIS_SAFETY_PASSWORD=<REDIS_SAFETY_PASSWORD>
REDIS_HEALTH_PASSWORD=<REDIS_HEALTH_PASSWORD>

REDIS_URL=redis://api_backend:<REDIS_API_PASSWORD>@redis:6379/0
REDIS_REALTIME_URL=redis://api_backend:<REDIS_API_PASSWORD>@redis:6379/1
MESSAGE_SAFETY_REDIS_URL=redis://message_safety:<REDIS_SAFETY_PASSWORD>@redis:6379/2

8.5. Mock OTP

В текущих deploy-артефактах реализован только mock OTP. Для запуска до controlled SMS rollout:

KEYCLOAK_OTP_MOCK_ENABLED=true
KEYCLOAK_OTP_MOCK_RISK_ACCEPTED=true

KEYCLOAK_OTP_MOCK_CODE хранится только в secret backend. Не используйте mock как production-механизм доставки OTP.

Целевой real mode задаёт modules/module-11-idgtl-sms.md: Keycloak генерирует и локально проверяет OTP, sms-service надёжно записывает заказ/журнал, worker вызывает i-Digital Direct, callback обновляет только delivery journal. Нельзя просто установить KEYCLOAK_OTP_MOCK_ENABLED=false.

До переключения необходимы: schema/role sms и migrations/seed, active approved auth_otp (code, ttl_min), согласованный sender, Direct TOKEN_1, парные service tokens, отдельные callback credentials, exact nginx callback route, подтверждённый source IP Direct и статический egress IP worker. Сначала deploy при mock=true, затем provider smoke/callback/redaction evidence и только после этого cutover. Rollback возвращает mock без удаления SMS schema/journal.

8.6. S3

SELECTEL_S3_ENDPOINT_URL=https://s3.storage.selcloud.ru
SELECTEL_S3_BUCKET_QUARANTINE=han-chat-quarantine
SELECTEL_S3_BUCKET_ATTACHMENTS=han-chat-attachments
SELECTEL_S3_BUCKET_DOCUMENTS=han-chat-documents

Обе пары S3 credentials хранятся только в secret backend.

API backend принудительно использует virtual-hosted addressing: https://<bucket>.s3.storage.selcloud.ru/<object-key>. Это обязательно для браузерных presigned PUT и CORS в Selectel; path-style URL для этого сценария не используйте. DNS и исходящий HTTPS с ВМ должны разрешать поддомены бакетов.

8.7. Bitrix24

До установки локального приложения загрузите credentials в secret backend. В .env остаются только несекретные connector/public URL параметры:

BITRIX_CONNECTOR_ID=han_mobile_app
BITRIX_OPEN_LINE_ID=8
BITRIX_PUBLIC_BASE_URL=https://chat.example.ru/bitrix

BITRIX_APPLICATION_TOKEN сохраняется в secret backend после создания приложения и никогда не помещается в .env.

8.8. TLS и наблюдаемость

До выпуска сертификата оставьте в .env целевые значения:

NGINX_TLS_ENABLED=true
NGINX_TLS_CERTIFICATE=/etc/letsencrypt/live/chat.example.ru/fullchain.pem
NGINX_TLS_CERTIFICATE_KEY=/etc/letsencrypt/live/chat.example.ru/privkey.pem
ACME_EMAIL=<ADMIN_EMAIL>

Если удаленный OTLP-бэкенд пока не выбран, укажите временные непубличные значения и примите ограничение: Collector будет пытаться отправлять телеметрию и сохранять ее в ограниченной очереди. Перед production-запуском задайте реальный endpoint.

9. Проверка окружения и конфигурации Compose

Выполните:

cd /opt/han-chat/backend
./scripts/validate-env .env
sudo systemctl restart han-secrets@production.service
sudo ./scripts/validate-env .env \
  --runtime-manifest /run/han-chat/secrets/manifest
sudo deployment/secrets/han-compose config --quiet
sudo deployment/secrets/han-compose config --services
python3 -m unittest discover -s tests -v

Не переходите к следующему шагу, пока все команды не завершатся успешно.

Посмотрите итоговую конфигурацию портов:

docker compose --env-file .env config | grep -n 'published:'

Публиковаться должны только 80 и 443 у nginx.

10. Сборка образов

Соберите все локальные образы:

cd /opt/han-chat/backend
docker compose --env-file .env build --pull

Проверьте список:

docker compose --env-file .env images

Сборка Keycloak включает Java OTP SPI, а сборка frontend-static экспортирует тестовый Expo Web frontend.

11. Миграции БД и начальные настройки

Перед миграциями создайте backup/PITR marker в панели провайдера PostgreSQL. Затем:

cd /opt/han-chat/backend
PITR_MARKER_CONFIRMED=true deployment/scripts/migrate.sh
deployment/scripts/seed.sh

Скрипт применит миграции han_app, bitrix_local и baseline bitrix_sync, после чего загрузит deployment/app-settings.production-like.yaml.

Повторный запуск seed должен быть безопасным:

deployment/scripts/seed.sh

12. Запуск внутренних сервисов

Сначала запустите Redis:

docker compose --env-file .env up -d redis
docker compose --env-file .env ps redis

Затем Keycloak и OpenTelemetry:

docker compose --env-file .env up -d keycloak otel-collector
docker compose --env-file .env ps keycloak otel-collector

Первый запуск Keycloak может занять несколько минут: он создаст свои таблицы и импортирует realm han-chat.

После готовности Keycloak:

docker compose --env-file .env up -d message-safety
docker compose --env-file .env up -d api-backend
docker compose --env-file .env up -d bitrix-local-app bitrix-sync
docker compose --env-file .env up -d \
  delivery-worker safety-recovery-worker cleanup-worker
docker compose --env-file .env ps

Если сервис не становится healthy:

docker compose --env-file .env logs --tail=200 <SERVICE_NAME>
docker inspect "$(docker compose --env-file .env ps -q <SERVICE_NAME>)"

12.1. Повторная раскатка upstream при уже работающем nginx

Nginx разрешает Docker DNS имена upstream при загрузке конфигурации. После up --build, pull, rollback или --force-recreate контейнер может получить новый IP, а работающий nginx продолжит использовать старый и вернёт 502 Connection refused.

После пересоздания api-backend, keycloak, sms-service или bitrix-local-app обязательно выполните:

docker compose --env-file .env up -d --wait \
  api-backend keycloak sms-service bitrix-local-app
docker compose --env-file .env exec -T nginx \
  nginx -t -c /tmp/nginx.conf
docker compose --env-file .env kill -s HUP nginx

curl -fsS "https://${PUBLIC_HOST}/api/v1/public/app-config" | jq
curl -fsS \
  "https://${PUBLIC_HOST}/auth/realms/han-chat/.well-known/openid-configuration" |
  jq

Не используйте bare-команды nginx -t и nginx -s reload: рабочая конфигурация находится в /tmp/nginx.conf, PID — в /tmp/nginx.pid, а контейнер использует read-only filesystem.

13. Первоначальный выпуск TLS-сертификата

Для ACME требуется работающий nginx по HTTP. В .env оставьте NGINX_TLS_ENABLED=true, но первый nginx запустите с временным переопределением:

NGINX_TLS_ENABLED=false \
  docker compose --env-file .env up -d frontend-static nginx

Проверьте HTTP:

curl -I http://chat.example.ru/

Сначала рекомендуется проверить Certbot через staging:

docker compose --env-file .env --profile certbot run --rm certbot certonly \
  --staging \
  --webroot -w /var/www/certbot \
  -d chat.example.ru \
  --cert-name chat.example.ru-staging \
  --email <ADMIN_EMAIL> \
  --agree-tos --no-eff-email --non-interactive

После успешного staging-теста выпустите рабочий сертификат с основным cert-name без --staging:

docker compose --env-file .env --profile certbot run --rm certbot certonly \
  --webroot -w /var/www/certbot \
  -d chat.example.ru \
  --cert-name chat.example.ru \
  --email <ADMIN_EMAIL> \
  --agree-tos --no-eff-email --non-interactive

Пересоздайте nginx уже с TLS:

docker compose --env-file .env up -d --force-recreate nginx
docker compose --env-file .env exec -T nginx nginx -t -c /tmp/nginx.conf
curl -I https://chat.example.ru/

Повторно запустите VM setup, чтобы он обнаружил проект и установил systemd-таймер продления сертификата:

sudo /opt/han-chat/backend/deployment/scripts/setup-vm.sh
systemctl status han-chat-ssl-renew.timer

14. Запуск всего контура

Теперь можно привести весь проект к состоянию, описанному Compose:

cd /opt/han-chat/backend
docker compose --env-file .env up -d
docker compose --env-file .env ps

Проверьте, что контейнеры не перезапускаются:

docker compose --env-file .env ps
docker compose --env-file .env logs --since=10m

15. Публичная проверка

Запустите smoke-тест:

cd /opt/han-chat/backend
deployment/scripts/smoke.sh

Также вручную проверьте:

curl -fsS https://chat.example.ru/api/v1/public/app-config | jq
curl -fsS https://chat.example.ru/api/v1/public/content | jq
curl -fsS \
  https://chat.example.ru/auth/realms/han-chat/.well-known/openid-configuration | jq

Внутренний API не должен быть опубликован:

curl -i https://chat.example.ru/internal/safety/v2/messages/check

Ожидаемый статус — 404.

Откройте в браузере:

https://chat.example.ru/

Для тестовой авторизации получите mock code утверждённым защищённым способом, не читая его из .env и не помещая в shell history.

16. Подключение Bitrix24

В настройках локального приложения Bitrix24 задайте HTTPS-адреса:

Установка:   https://chat.example.ru/bitrix/install
Обработчик:  https://chat.example.ru/bitrix/handler
Placement:   https://chat.example.ru/bitrix/placement

После установки:

  1. Получите и сохраните application token.
  2. Сохраните его как BITRIX_APPLICATION_TOKEN в secret backend.
  3. Пересоздайте сервис:
docker compose --env-file .env up -d --force-recreate bitrix-local-app
docker compose --env-file .env logs --tail=200 bitrix-local-app

Проверьте коннектор han_mobile_app и Открытую линию 8.

17. Включение SSH hardening

Только после успешного входа пользователем deploy по ключу в отдельной сессии:

sudo HARDEN_SSH=true \
  /opt/han-chat/backend/deployment/scripts/setup-vm.sh

Не закрывайте текущую SSH-сессию, пока не проверили новый вход.

18. Обычный перезапуск проекта

Для штатного запуска после перезагрузки VM:

cd /opt/han-chat/backend
docker compose --env-file .env up -d
docker compose --env-file .env ps

Для перезапуска одного сервиса:

docker compose --env-file .env restart api-backend

После изменения .env используйте пересоздание, а не restart:

docker compose --env-file .env up -d --force-recreate <SERVICE_NAME>

19. Обновление версии проекта

Перед обновлением:

  1. Создайте backup/PITR marker PostgreSQL.
  2. Сохраните текущие image digests.
  3. Получите новый код.
  4. Проверьте несекретный .env и runtime secret set.
  5. Пересоберите образы.
  6. Примените миграции и seed.
  7. Пересоздайте сервисы.

Команды:

cd /opt/han-chat/backend
./scripts/validate-env .env
docker compose --env-file .env build --pull
PITR_MARKER_CONFIRMED=true deployment/scripts/migrate.sh
deployment/scripts/seed.sh
docker compose --env-file .env up -d
deployment/scripts/smoke.sh

20. Диагностика

Состояние сервисов:

docker compose --env-file .env ps

Все логи:

docker compose --env-file .env logs --tail=300

Логи конкретного сервиса:

docker compose --env-file .env logs -f api-backend

Проверка firewall:

sudo ufw status verbose
sudo iptables -L HAN-CHAT-DOCKER -n -v

Проверка сертификата:

openssl s_client -connect chat.example.ru:443 -servername chat.example.ru \
  </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates

Проверка свободного места:

df -h
docker system df

Не выполняйте docker compose down -v: эта команда удалит именованные volumes. Не выполняйте Alembic downgrade. Для отката используйте deployment/scripts/rollback.sh и инструкции из RUNBOOK.ru.md.

21. Когда развертывание можно считать завершенным

Проект запущен корректно, если:

  • docker compose ps показывает healthy для критических сервисов;
  • deployment/scripts/smoke.sh завершается успешно;
  • открывается тестовый frontend;
  • проходит авторизация через mock OTP;
  • отправляются текстовые и файловые сообщения;
  • внутренние URL возвращают 404 снаружи;
  • TLS-сертификат действителен;
  • логи не содержат токены, PII и тексты сообщений;
  • настроены резервное копирование PostgreSQL и продление TLS.

Для формальной production-like приемки после этого пройдите контрольные этапы из deployment/RUNBOOK.ru.md.