gnome627andClaude Opus 5 efc506e5de DEVOPS-117: чарт on-premise 3.0 — Nanabush Player, секреты в Secret, валидация values
Актуализация чарта по продовому .helm из HRBOX.

Nanabush Player перенесён из прода и упрощён под one-release-топологию
on-premise: без карт env/ci_dc, но с сохранёнными инвариантами (audience =
https://<host>, basePath отдельно от aud, allowlist обратного канала только
на внутрикластерный web, метрики на непубликуемом порту). Выделенного
поддомена в on-premise нет, поэтому плеер монтируется на основной домен под
/nanabush-player. Три переключателя для аварийного отката сохранены.

Из прода также перенесены: пул конвертации видео, PodDisruptionBudget для
web, OpenTelemetry-сайдкар, assetlinks, набор location в nginx (/healthz,
/health-check, потоковый /api/v1/integration/commit, /ai/, sw.js, шрифты).
Версии образов подтянуты к продовым.

Приведение чарта в порядок: все образы в блоке image, imagePullSecrets в
настройках, девять одинаковых define ресурсов заменены одним хелпером,
удалено ~100 строк мёртвого кода в _helpers.tpl, пароли и ключи уехали в
Secret и читаются через secretKeyRef, добавлены 00-validate.yaml и NOTES.txt.

Исправлено в 2.x:
- SERVICES_INTERNAL_SECRET имел захардкоженное значение по умолчанию, то есть
  все установки, где его не заполнили, работали на одном секрете межсервисной
  аутентификации;
- cluster.domain игнорировался: маршруты кластера NATS были захардкожены на
  cluster.local;
- CLUSTER_ADVERTISE без сегмента svc — ноды NATS объявляли соседям
  недостижимый адрес;
- nats.replicas и nats.cluster.replicas задавались независимо, при
  расхождении часть нод не входила в кластер;
- пробы web проверяли только TCP-порт 9000, поэтому под с неработающим PHP
  считался готовым;
- внутренние URL были захардкожены строками hrbox-* и молча ломались при
  nameOverride.

Имена объектов и селекторы Deployment не менялись — обновление 2.x -> 3.0 не
пересоздаёт объекты. Переехавшие ключи values.yaml чарт распознаёт и
останавливает установку с подсказкой; порядок перехода — в CHANGELOG.md.

Проверено: helm lint чистый; отрендерены 4 конфигурации (дефолт, всё
включено, плеер и инфра выключены, nameOverride); структурная проверка
рендера без замечаний; 20 негативных сценариев срабатывают с ожидаемыми
сообщениями; реалистичный values.yaml из 2.x последовательно ловится всеми
шестью guard'ами.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 16:37:42 +06:00

HRBox Helm Chart

Helm-чарт для развёртывания HRBox в собственном Kubernetes-кластере.

Чарт лежит в каталоге .helm, настройки — в .helm/values.yaml. Каждый параметр там прокомментирован; ниже — только то, что нужно сделать руками.


Что разворачивается

Компонент Что делает Порт
web PHP-FPM + nginx: веб-интерфейс и API 80
worker фоновые задачи: файлы, почта, отчёты —
conductor планировщик задач по расписанию (строго 1 реплика) 8080
chatbox вебсокеты чата и real-time уведомлений 80 → 9797
nanabush-player проигрыватель курсов: SCORM, cmi5, xAPI, нативные материалы 8094
goworker мессенджеры, push, синхронизация оргструктуры с 1С 8686
kedoca электронная подпись: КЭДО, МЧД 8558
geonames справочник городов, стран, часовых поясов 8181
file-processor конвертация документов и изображений —
file-processor-video конвертация видео, выключена по умолчанию —
dragonfly Redis-совместимый кеш и сессии 6379
nats брокер сообщений JetStream 4222

Все три публичных маршрута живут на одном домене: / — приложение, /chatbox/ — вебсокеты, /nanabush-player — плеер курсов.

Что нужно от вас

  • Kubernetes 1.21+ и ingress-контроллер (nginx или traefik);
  • PostgreSQL 14+ (с расширением Citus, если планируется шардирование);
  • S3-совместимое хранилище: AWS S3, MinIO, Yandex Object Storage, VK Cloud;
  • SMTP-сервер;
  • доступ к Docker-реестру HRBox и токен HRBox Hub — выдают сотрудники HRBox.

PostgreSQL и S3 чарт не разворачивает: это состояние, которое переживает установку и требует собственного резервного копирования.


Установка

1. Доступ к реестру образов

kubectl create secret docker-registry regsecret \
  --docker-username=json_key \
  --docker-password="$(cat key-puller.json)" \
  --docker-server=cr.yandex \
  --docker-email=not@used.com

2. TLS-сертификат домена

kubectl create secret tls hrbox-tls --cert=cert.pem --key=key.pem

Или выпустите сертификат через cert-manager и укажите его Secret в ingress.tls.secretName.

3. Ключи

# secrets.cookieValidationKey и app.chatbox.wrappingKey
openssl rand -hex 16

# secrets.encryptionKey, secrets.servicesInternalSecret,
# app.nanabushPlayer.keys.ticketHs256, app.nanabushPlayer.keys.sessionHs256
openssl rand -base64 48

secrets.encryptionKey — единственный ключ, потеря которого необратима: им зашифрованы пароли интеграций в базе. Сохраните его отдельно от кластера.

Ключи плеера ticketHs256 и sessionHs256 должны отличаться друг от друга — это ключи разных контуров, и чарт откажется ставить одинаковые.

4. Заполните values.yaml

Обязательный минимум:

ingress:
  host: "hrbox.company.com"
  tls:
    secretName: "hrbox-tls"

app:
  defaultTenantHostname: "hrbox.company.com"
  postgres:
    host: "postgresql"
    database: "hrbox"
    user: "hrbox"
    password: "..."
  s3:
    endpoint: "https://storage.company.com"
    bucket: "hrbox"
    key: "..."
    secret: "..."
  smtp:
    host: "smtp.company.com"
    user: "noreply@company.com"
    password: "..."
    from: "noreply@company.com"
    fromHost: "company.com"
  hub:
    token: "..."
  chatbox:
    wrappingKey: "..."
  nanabushPlayer:
    keys:
      ticketHs256: "..."
      sessionHs256: "..."

secrets:
  cookieValidationKey: "..."
  encryptionKey: "..."
  servicesInternalSecret: "..."

Всё остальное имеет рабочие значения по умолчанию.

Проверить конфигурацию, ничего не устанавливая:

helm template hrbox .helm -f values.yaml > /dev/null

Если чего-то не хватает, чарт скажет об этом текстом, а не оставит неработающие поды в кластере.

5. Установка

helm upgrade --install hrbox .helm -n hrbox --create-namespace -f values.yaml

6. Дождитесь миграций

kubectl -n hrbox wait --for=condition=complete --timeout=30m job -l app=jobs

7. Первый администратор

kubectl -n hrbox exec -it deploy/hrbox-worker -- \
  php yii user/create-admin admin@company.com --tenant-id=1

Секреты

Чувствительные значения из values.yaml чарт складывает в один Secret hrbox-secrets и монтирует контейнерам через secretKeyRef — в спеке Deployment паролей нет.

Если секретами управляет внешний инструмент, создайте Secret сами и укажите его имя в secrets.existingSecret. Ключи должны называться так:

Ключ Откуда берётся при existingSecret: ""
cookie-validation-key secrets.cookieValidationKey
encryption-key secrets.encryptionKey
services-internal-secret secrets.servicesInternalSecret
postgres-password app.postgres.password
s3-key app.s3.key
s3-secret app.s3.secret
smtp-password app.smtp.password
hub-token app.hub.token
chatbox-wrapping-key app.chatbox.wrappingKey
livekit-api-secret app.livekit.apiSecret
yandex-client-password app.yandex.clientPassword
yandex-ai-api-key app.ai.yandex.apiKey
google-client-secret app.integrations.google.clientSecret
slack-client-secret app.slack.clientSecret
slack-verification-token app.slack.verificationToken

Ключи плеера лежат в отдельном Secret hrbox-nanabush-player — им управляет app.nanabushPlayer.deployment.existingSecret.


Nanabush Player

Проигрыватель учебных материалов: SCORM 1.2 и 2004, cmi5, xAPI, нативные курсы, тесты и эссе.

В облаке HRBox плеер живёт на отдельном поддомене. В on-premise выделенного поддомена нет, поэтому плеер монтируется на основной домен под путём /nanabush-player. Что из этого следует:

  • Изоляции cookie нет. Плеер и HRBox делят один origin, поэтому cookie сессии HRBox отправляются браузером и на запросы к содержимому курсов, включая сторонние SCORM-пакеты. Если нужна настоящая изоляция — запрашивайте у HRBox схему с отдельным поддоменом.
  • Часы должны быть синхронизированы. Launch-тикет живёт 5 минут, сессия проверяется с допуском 2 минуты. Настройте NTP на нодах и сервере БД; если это невозможно, выключите плеер (app.nanabushPlayer.enabled: false).

Порядок первого включения:

  1. Убедитесь, что миграции применились: плееру нужны таблицы проекций.

  2. Дождитесь, пока все поды web перейдут на новую ревизию. Смешанный выкат при rollout.percent > 0 может создать вторую активную попытку по одному назначению.

  3. Проверьте, что плеер отвечает сам, а не HRBox через общий маршрут:

    kubectl -n hrbox exec deploy/hrbox-worker -- \
      curl -s http://hrbox-nanabush-player:8094/readyz
    

    Настоящий плеер вернёт JSON. HTML означает, что запрос ушёл в HRBox.

  4. Проверьте публичный путь: https://<домен>/nanabush-player/start, статику и API плеера под этим путём.

Служебные пути (/healthz, /readyz, метрики) живут в корне и не зависят от basePath. Порт метрик наружу не публикуется намеренно.

Аварийное отключение — три независимых переключателя:

app:
  nanabushPlayer:
    enabled: false            # пользователи возвращаются на старый проигрыватель
    ingress:
      enabled: false          # маршрут снимается
    deployment:
      enabled: false          # под останавливается

Выключать нужно сверху вниз: чарт не даст оставить включённым маршрут без пода или раскатку без маршрута.

Подробности контракта — docs/nanabush-player-deployment.md в репозитории HRBox.


Эксплуатация

Обновление

helm upgrade hrbox .helm -n hrbox -f values.yaml

Миграции и обновление кластера запускаются автоматически как хуки post-upgrade и выполняются строго по порядку. Имя джобы содержит номер ревизии релиза, поэтому в kubectl get jobs видно, какой выкат её запускал.

Масштабирование

app:
  replicas:
    web: 4        # по числу одновременных пользователей
    worker: 3     # по объёму фоновых задач
    chatbox: 4    # по числу открытых вкладок

conductor всегда в одном экземпляре: второй планировщик запустил бы каждую задачу дважды.

Для web при двух и более репликах создаётся PodDisruptionBudget (minAvailable: 75%), чтобы drain ноды не уронил приложение целиком.

Конвертация видео

Выключена по умолчанию: профиль ресурсов у неё на порядок тяжелее обычной обработки файлов. Включение:

app:
  videoConverter:
    enabled: true
    nodeSelector:
      role: converter-dedicated
    tolerations:
      - key: dedicated
        operator: Equal
        value: video-converter
        effect: NoSchedule

Трассировка

app:
  tracing:
    enabled: true
    tempoUrl: "http://tempo.monitoring:4317"

Рядом с web поднимется коллектор OpenTelemetry, PHP начнёт слать трейсы.

Внешние Redis и NATS

dragonfly:
  enabled: false
nats:
  enabled: false
app:
  redis:
    host: "redis.company.local"
  nats:
    url: "nats://n1:4222,nats://n2:4222,nats://n3:4222"

Диагностика

kubectl -n hrbox get pods
kubectl -n hrbox logs -l app=web -c fpm --tail=200
kubectl -n hrbox logs -l app=worker --tail=200
kubectl -n hrbox logs -l app=nanabush-player --tail=200
kubectl -n hrbox get jobs
Симптом Куда смотреть
Поды web не готовы пробы /healthz и /health-check, логи контейнера fpm
Джоба миграций висит init-контейнер wait-postgres: доступность и права в БД
Курс не открывается логи nanabush-player, совпадение домена в ingress.host и app.defaultTenantHostname
Не приходят уведомления логи goworker, очереди NATS, app.launchpad.topics
Пользователей разлогинивает перезапуски dragonfly: сессии живут в памяти

Миграция с версии 2.x

Часть ключей values.yaml переехала. Чарт проверяет старые имена и останавливает установку с подсказкой, поэтому незамеченным ничего не останется. Полный список изменений и порядок перехода — в CHANGELOG.md.

S
Description
Helm чарт для деплоя on-premise
Readme
168 KiB
Languages
Go Template 100%