16 KiB
Инструкция по развертыванию HAN Chat в production-like окружении
Это исполняемый чек-лист для контура на одной виртуальной машине. PostgreSQL и S3
используются как внешние управляемые сервисы. Во время развертывания запрещено
использовать docker compose down -v, откат миграций Alembic и изменяемые теги образов.
Подробная пошаговая инструкция для первого запуска находится в
deployment/DEPLOYMENT_GUIDE.ru.md.
Этап 0 — решения и зоны ответственности
- Зафиксированы SHA/дайджесты релиза, окно обслуживания, дежурный и ответственный за откат.
- Согласованы RPO/RTO; начальные цели: RPO PostgreSQL не более 15 минут и RTO не более 4 часов.
- Выбран удаленный OTLP-бэкенд либо принято ограничение на использование только отладочного контура.
- Явно приняты риски mock OTP, заглушки Safety и заглушки bitrix-sync.
Этап 1 — VPC, DNS и группы безопасности
- Управляемый PostgreSQL имеет только приватную точку доступа и принимает трафик от группы безопасности VM.
- Из интернета доступны только TCP-порты VM 80/443; SSH ограничен VPN или CIDR администраторов.
- Порты 6379, 4317/4318, 8000, 8080 и 9000 закрыты для внешнего доступа.
- DNS-запись
AдляPUBLIC_HOSTуказывает на VM; исходящий HTTPS доступен.
Этап 2 — защита виртуальной машины
На новой Ubuntu 24.04 можно выполнить подготовительный скрипт:
sudo deployment/scripts/setup-vm.sh
Перед включением HARDEN_SSH=true обязательно проверьте вход по ключу в отдельной
SSH-сессии. Параметры запуска и безопасные значения по умолчанию описаны в начале скрипта.
- Установлена Ubuntu 24.04; работают NTP, автоматические обновления безопасности и оповещения о заполнении диска.
- Вход учетной записью развертывания по ключу проверен во второй сессии; вход root и SSH по паролю отключены.
- Правила UFW/облачной группы безопасности и политика
DOCKER-USERсохраняются после перезагрузки. - Docker Engine и Compose поддерживают
includeи полную формуenv_file.
Этап 3 — управляемый PostgreSQL
- Включены ежедневные резервные копии, PITR, защита от удаления, шифрование и оповещения.
- CA-сертификат провайдера установлен по пути
PG_CA_HOST_PATH; все DSN используютverify-full. - Для схем
han_app,bitrix_local,bitrix_sync,message_safety,keycloakсозданы отдельные роли миграций и выполнения; запрет лишних прав проверен тестами. - Миграции проверены на пустой БД и на клоне БД предыдущего релиза.
Этап 4 — Selectel S3
- Бакеты карантина, вложений и документов закрыты от публичного доступа и зашифрованы.
- Права API ограничены префиксами; учетные данные Safety имеют доступ к карантину только на чтение.
- CORS бакетов разрешает только точный HTTPS-origin браузера и необходимые заголовки PUT.
- Срок хранения карантина превышает время Safety polling/recovery; политика хранения данных согласована.
Этап 5 — неизменяемый релиз
- Репозиторий переключен на утвержденный SHA в detached-режиме; рабочее дерево чистое.
- Образы сервисов неизменяемы и просканированы; нерешенных критических и высоких уязвимостей нет.
- Корневой
docker-compose.ymlявляется единственной точкой запуска.
Этап 6 — окружение и секреты
umask 077
cp .env.example .env
chmod 600 .env
# Замените заглушки через защищенный редактор или менеджер секретов.
./scripts/validate-env .env
docker compose --env-file .env config --quiet
- Парные токены совпадают, PostgreSQL проверяет TLS, публичные URL используют HTTPS.
- Риск mock OTP принят; все секреты уникальны и содержат не менее 128 бит энтропии.
- Установлено
FRONTEND_DEV_PROXY_ENABLED=false; таймауты Safety и nginx согласованы.
Этап 7 — образы и статический frontend
docker compose --env-file .env pull
docker compose --env-file .env build --pull frontend-static nginx redis
docker compose --env-file .env run --rm frontend-static
- Экспорт frontend проверен и просканирован, затем скопирован сервисом
frontend-staticв именованный volume. - Артефакты сборки не содержат секретов и непредусмотренных source map.
- На диске VM остается не менее 30% свободного места.
Этап 8 — топология
docker compose --env-file .env config --services
python3 -m unittest discover -s tests -v
- Только nginx публикует
80:80и443:443; сервиса PostgreSQL в Compose нет. - Присутствуют volumes Redis AOF/RDB/ACL и постоянной очереди OTEL.
- Сети
backendиobservabilityявляются внутренними.
Этап 9 — первоначальная настройка ACME/TLS
Установите NGINX_TLS_ENABLED=false только для команды первоначального запуска:
NGINX_TLS_ENABLED=false docker compose --env-file .env up -d nginx
docker compose --profile certbot run --rm certbot certonly \
--webroot -w /var/www/certbot -d "$PUBLIC_HOST" \
--cert-name "$PUBLIC_HOST" --email "$ACME_EMAIL" \
--agree-tos --no-eff-email --non-interactive
docker compose --env-file .env up -d --force-recreate nginx
docker compose exec -T nginx nginx -t -c /tmp/nginx.conf
Сначала выполните проверку с параметром Certbot --staging. Установите systemd-таймер,
запускающий deployment/scripts/ssl-renew.sh дважды в сутки, и проверьте
certbot renew --dry-run. Включайте HSTS только после проверки цепочки сертификатов,
имени хоста, перенаправления и поддержки TLS 1.2/1.3.
Этап 10 — миграции и начальные данные
Создайте у провайдера точку восстановления PITR, затем выполните:
PITR_MARKER_CONFIRMED=true deployment/scripts/migrate.sh
deployment/scripts/seed.sh
- Активны ожидаемые ревизии Alembic; runtime-пользователи не выполняли DDL.
- Повторный seed завершается успешно; обязательные настройки не содержат секретов.
- Схема остается обратно совместимой с образами предыдущего релиза.
Этап 11 — Keycloak
docker compose up -d keycloak
docker compose ps keycloak
- Issuer discovery/JWKS точно совпадает с публичным HTTPS URL
/auth. - Frontend-клиент является публичным PKCE S256; implicit, password и social flows отключены.
- Неверный или повторно использованный OTP и превышение лимитов безопасно отклоняются; settings bridge работает fail-closed.
- Временный администратор удален либо его пароль изменен; для именного администратора включена MFA.
Этап 12 — последовательный запуск и готовность
docker compose up -d redis
docker compose up -d keycloak otel-collector
docker compose up -d message-safety
docker compose up -d api-backend
docker compose up -d bitrix-local-app bitrix-sync
docker compose up -d nginx
docker compose ps
- Нет циклических перезапусков и OOM; критические readiness-проверки успешны.
- Сохраняется только документированная деградация: Bitrix не установлен и bitrix-sync работает как заглушка.
- Внешний запрос
/internal/*возвращает 404; OTEL принимает телеметрию.
Этап 13 — Bitrix24
- URL установки, обработчика и placement используют точные публичные HTTPS-пути.
- Коннектор
han_mobile_appактивен в Открытой линии 8; события привязаны однократно. - OAuth зашифрован; callback-, application- и service-токены не попадают в логи.
- Исходящие сообщения и ответы оператора идемпотентны; внутренний статус не опубликован наружу.
Этап 14 — smoke- и E2E-тесты
deployment/scripts/smoke.sh
- Успешны сценарии гостя, OTP/PKCE/bootstrap/session, обновления токена и выхода.
- Проверены Safety allow/deny/pending/timeout и один параллельный медленный poll.
- Проверены карантин, перенос и удаление файлов, скачивание только владельцем и аудит.
- Проверены переподключение WS с REST-сверкой, 404 при обращении к чужому ресурсу, идемпотентность и 429.
- Логи не содержат PII, текстов сообщений, токенов и query-параметров presigned URL.
Этап 15 — наблюдаемость
- Известный request ID связывает трассировку nginx, API и downstream-сервисов; UX ID не используется как label.
- Все три сигнала поступают в выбранный бэкенд; SLO-запросы и оповещения проверены.
- При недоступности удаленного сервиса ограниченная постоянная очередь заполняется и опустошается без остановки бизнес-функций.
- Тестовые секреты и PII отсутствуют; проверены метрики перезапуска, потерь, отказов и очереди Collector.
Только для локальной приемки запустите отладочный Collector с удалением чувствительных данных:
docker compose --profile observability-local up -d otel-collector-local.
Этап 16 — открытие трафика
- Этапы 0–15 подписаны; имеются свежие подтверждения backup/PITR и предыдущие образы.
- HSTS включен; дайджесты релиза, версии схем и realm зафиксированы.
- Активных инцидентов нет; дежурный и владелец продукта приняли ограничения заглушек.
- В течение 60 минут контролируются 5xx, auth, доставка, БД, Redis, OOM, очередь OTEL и Bitrix.
Резервное копирование и восстановление
Основным механизмом являются backup/PITR провайдера. Дополнительный проверенный логический дамп:
PG_BACKUP_DSN='postgresql://...?...sslmode=verify-full&sslrootcert=...' \
deployment/scripts/backup.sh /opt/han-chat/backups
Ежеквартально восстанавливайте PostgreSQL и S3 в изолированной VPC, развертывайте те же дайджесты образов, не направляйте туда production DNS и callbacks Bitrix, выполняйте smoke-тесты и фиксируйте фактические RPO/RTO. Redis можно восстановить пустым: его AOF/RDB не является резервной копией бизнес-данных.
Откат
Откатывайтесь только на образы, совместимые с текущей схемой:
SCHEMA_BACKWARD_COMPATIBLE_CONFIRMED=true \
deployment/scripts/rollback.sh /secure/path/previous-release.env
ENV_FILE=/secure/path/previous-release.env deployment/scripts/smoke.sh
Дополнение: rollout реальной SMS-авторизации
Текущий runbook остаётся mock-only, пока артефакты module-11 не реализованы. Для SMS release обязательны: schema/role sms, migrations/seed active approved auth_otp, sms-service/worker, exact callback route, парные service tokens, Direct TOKEN_1, согласованные sender/template, отдельные callback credentials, подтверждённый callback source IP и статический egress IP worker.
Порядок: App DB OTP seed → SMS schema/migrations/seed → test с mock Direct → production SMS deploy при KEYCLOAK_OTP_MOCK_ENABLED=true → Keycloak expand migration/SPI → provider smoke и callback/redaction evidence → real mode. Rollback: вернуть mock, не удалять journal/schema, остановить новые real orders и зафиксировать in-flight/uncertain; downgrade только при доказанной совместимости.
Никогда не выполняйте downgrade Alembic. После обратно несовместимой миграции используйте исправление вперед либо согласованный PITR с восстановлением S3 и сверкой Bitrix во время технического обслуживания. Всегда проверяйте outbox, inbox и recovery, чтобы сообщение с неопределенным статусом не было отправлено повторно.