Files
hrbox-helm-chart/README.md
T
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

341 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# HRBox Helm Chart
Helm-чарт для развёртывания HRBox в собственном Kubernetes-кластере.
Чарт лежит в каталоге [`.helm`](.helm), настройки — в [`.helm/values.yaml`](.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. Доступ к реестру образов
```bash
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-сертификат домена
```bash
kubectl create secret tls hrbox-tls --cert=cert.pem --key=key.pem
```
Или выпустите сертификат через cert-manager и укажите его Secret
в `ingress.tls.secretName`.
### 3. Ключи
```bash
# 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`
Обязательный минимум:
```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: "..."
```
Всё остальное имеет рабочие значения по умолчанию.
Проверить конфигурацию, ничего не устанавливая:
```bash
helm template hrbox .helm -f values.yaml > /dev/null
```
Если чего-то не хватает, чарт скажет об этом текстом, а не оставит
неработающие поды в кластере.
### 5. Установка
```bash
helm upgrade --install hrbox .helm -n hrbox --create-namespace -f values.yaml
```
### 6. Дождитесь миграций
```bash
kubectl -n hrbox wait --for=condition=complete --timeout=30m job -l app=jobs
```
### 7. Первый администратор
```bash
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 через общий маршрут:
```bash
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`. Порт метрик наружу не публикуется намеренно.
Аварийное отключение — три независимых переключателя:
```yaml
app:
nanabushPlayer:
enabled: false # пользователи возвращаются на старый проигрыватель
ingress:
enabled: false # маршрут снимается
deployment:
enabled: false # под останавливается
```
Выключать нужно сверху вниз: чарт не даст оставить включённым маршрут
без пода или раскатку без маршрута.
Подробности контракта — `docs/nanabush-player-deployment.md` в репозитории HRBox.
---
## Эксплуатация
### Обновление
```bash
helm upgrade hrbox .helm -n hrbox -f values.yaml
```
Миграции и обновление кластера запускаются автоматически как хуки
`post-upgrade` и выполняются строго по порядку. Имя джобы содержит номер
ревизии релиза, поэтому в `kubectl get jobs` видно, какой выкат её запускал.
### Масштабирование
```yaml
app:
replicas:
web: 4 # по числу одновременных пользователей
worker: 3 # по объёму фоновых задач
chatbox: 4 # по числу открытых вкладок
```
`conductor` всегда в одном экземпляре: второй планировщик запустил бы
каждую задачу дважды.
Для `web` при двух и более репликах создаётся PodDisruptionBudget
(`minAvailable: 75%`), чтобы drain ноды не уронил приложение целиком.
### Конвертация видео
Выключена по умолчанию: профиль ресурсов у неё на порядок тяжелее обычной
обработки файлов. Включение:
```yaml
app:
videoConverter:
enabled: true
nodeSelector:
role: converter-dedicated
tolerations:
- key: dedicated
operator: Equal
value: video-converter
effect: NoSchedule
```
### Трассировка
```yaml
app:
tracing:
enabled: true
tempoUrl: "http://tempo.monitoring:4317"
```
Рядом с `web` поднимется коллектор OpenTelemetry, PHP начнёт слать трейсы.
### Внешние Redis и NATS
```yaml
dragonfly:
enabled: false
nats:
enabled: false
app:
redis:
host: "redis.company.local"
nats:
url: "nats://n1:4222,nats://n2:4222,nats://n3:4222"
```
### Диагностика
```bash
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](CHANGELOG.md).