chart 3.0

This commit is contained in:
2026-09-05 19:07:31 +06:00
parent efc506e5de
commit 67786a0160
31 changed files with 348 additions and 773 deletions
-4
View File
@@ -3,10 +3,7 @@ name: hrbox
description: HRBox — HR-платформа для on-premise установки
type: application
# Версия чарта. Увеличивается при каждом изменении чарта.
version: 3.0.0
# Версия приложения. Совпадает с линейкой образов HRBox.
appVersion: "3.1.0"
keywords:
@@ -19,7 +16,6 @@ keywords:
home: https://hrbox.io
annotations:
# Компоненты, которые разворачивает чарт
hrbox.io/components: >-
web, worker, conductor, chatbox, goworker, kedoca, geonames,
file-processor, nanabush-player, dragonfly, nats
-12
View File
@@ -1,16 +1,4 @@
{{- if not .Values.secrets.existingSecret }}
{{/*
Единый Secret с чувствительными настройками HRBox.
В чарте 2.x пароли и ключи подставлялись прямо в env-блоки Deployment,
поэтому любой, кто мог сделать `kubectl get deploy -o yaml`, видел пароль
базы и ключ шифрования. Здесь значения лежат в одном Secret, а контейнеры
читают их через secretKeyRef (см. hrbox.secretEnv в _helpers.tpl).
Если секретами управляет внешний инструмент, задайте secrets.existingSecret —
тогда чарт этот объект не создаёт и только ссылается на указанный. Набор
ключей должен совпадать с перечисленным ниже.
*/}}
---
apiVersion: v1
kind: Secret
@@ -1,21 +1,5 @@
{{- $cfg := .Values.app.nanabushPlayer.deployment }}
{{- if and $cfg.enabled $cfg.existingSecret.create }}
{{/*
Ключи Nanabush Player.
ticketHs256 читают обе стороны: HRBox подписывает им launch-тикет
(NANABUSH_PLAYER_HS256_SECRET в поде web), плеер этим же ключом тикет
проверяет (PLAYER_HS256_SECRET). Поэтому ротация — одно значение на обе
стороны, а не два независимых.
sessionHs256 принадлежит только плееру: им подписывается сессия учащегося
внутри плеера, HRBox её не читает.
При existingSecret.create: false этот объект не создаётся — оба потребителя
всё так же ссылаются на existingSecret.name, но Secret должен быть заведён
вне чарта. При смене владельца уже существующего Secret берите новое имя,
чтобы Helm не удалил внешний объект во время перехода.
*/}}
---
apiVersion: v1
kind: Secret
-19
View File
@@ -1,19 +1,3 @@
{{/*
============================================================================
Проверка values.yaml
============================================================================
Этот файл не создаёт ни одного объекта. Он выполняется первым и падает с
понятным сообщением, если конфигурация неполная или противоречивая, — до
того, как в кластере появятся поды, которые всё равно не заработают.
Проверяется три вещи:
1) ключи, переехавшие в версии 3.0 (подсказка вместо тихого игнора);
2) обязательные значения, без которых приложение не поднимется;
3) инварианты Nanabush Player.
*/}}
{{/* --- 1. Ключи из чарта 2.x ---------------------------------------------- */}}
{{- if .Values.app.image }}
{{- fail "app.image переехал в image.hrbox (см. CHANGELOG, миграция 2.x -> 3.0)" }}
{{- end }}
@@ -42,7 +26,6 @@
{{- fail "nats.image переехал в image.nats" }}
{{- end }}
{{/* --- 2. Обязательные значения ------------------------------------------- */}}
{{- if not .Values.ingress.host }}
{{- fail "Заполните ingress.host — домен, по которому открывается HRBox" }}
{{- end }}
@@ -81,7 +64,6 @@
{{- end }}
{{- end }}
{{/* --- 3. Внешние зависимости ---------------------------------------------- */}}
{{- if and (not .Values.dragonfly.enabled) (not .Values.app.redis.host) }}
{{- fail "dragonfly.enabled: false требует app.redis.host — иначе приложение обратится к сервису, которого нет в релизе" }}
{{- end }}
@@ -95,7 +77,6 @@
{{- end }}
{{- end }}
{{/* --- 4. Nanabush Player -------------------------------------------------- */}}
{{- $player := .Values.app.nanabushPlayer }}
{{- if and $player.enabled (not $player.deployment.enabled) }}
{{- fail "app.nanabushPlayer.enabled требует deployment.enabled: HRBox нельзя переключить на плеер, которого нет в кластере" }}
-9
View File
@@ -1,12 +1,4 @@
{{- if .Values.nats.enabled }}
{{/*
Конфигурация NATS JetStream.
Маршруты кластера строятся из служебных DNS-имён StatefulSet, поэтому домен
кластера берётся из cluster.domain — в чарте 2.x он был захардкожен здесь и
одновременно настраивался в values, из-за чего нестандартный домен ломал
сборку кластера.
*/}}
{{- $name := include "hrbox.component" (dict "ctx" . "name" "nats") -}}
---
apiVersion: v1
@@ -17,7 +9,6 @@ metadata:
{{- include "hrbox.labels" (dict "ctx" . "name" "nats") | nindent 4 }}
data:
nats.conf: |
# PID-файл, общий с перезагрузчиком конфигурации.
pid_file: "/var/run/nats/nats.pid"
###############
-8
View File
@@ -1,11 +1,3 @@
{{/*
Привязка мобильных приложений к домену инсталляции.
Android читает /.well-known/assetlinks.json, iOS — /.well-known/apple-app-site-association.
Без них ссылки из писем и уведомлений открываются в браузере, а не в приложении.
По умолчанию здесь идентификаторы штатных приложений HRBox; если у вас
собственная сборка, переопределите app.mobileApps в values.yaml.
*/}}
---
apiVersion: v1
kind: ConfigMap
-14
View File
@@ -1,9 +1,3 @@
{{/*
Конфигурация nginx в поде web. Отличия от чарта 2.x — набор location,
перенесённый из продового окружения: health-check для проб, потоковая
загрузка оргструктуры из 1С, длинные таймауты для AI-запросов, отдача
assetlinks мобильных приложений и корректные заголовки для service worker.
*/}}
---
apiVersion: v1
kind: ConfigMap
@@ -103,13 +97,11 @@ data:
deny all;
}
# Liveness: отвечает сам nginx, не дожидаясь PHP-FPM.
location = /healthz {
access_log off;
return 200 "ok\n";
}
# Readiness: короткая проверка, что PHP-FPM отвечает.
location = /health-check {
fastcgi_pass 127.0.0.1:9000;
fastcgi_connect_timeout 1s;
@@ -121,9 +113,6 @@ data:
fastcgi_param PATH_INFO /health-check;
}
# Загрузка оргструктуры из 1С. PHP читает тело запроса потоком и
# проксирует в GoWorker без полной буферизации, поэтому nginx
# тоже не должен буферизовать тело.
location = /api/v1/integration/commit {
client_max_body_size 500m;
fastcgi_request_buffering off;
@@ -138,7 +127,6 @@ data:
fastcgi_connect_timeout 60s;
}
# Привязка мобильных приложений к домену
location /.well-known/assetlinks.json {
default_type "application/json";
alias /var/www/assetlinks/assetlinks.json;
@@ -157,7 +145,6 @@ data:
access_log off;
}
# AI-запросы отвечают дольше обычных, им нужен свой таймаут.
location ^~ /ai/ {
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
@@ -184,7 +171,6 @@ data:
add_header X-Content-Type-Options "nosniff";
add_header Permissions-Policy "browsing-topics=()" always;
# Всё, что не является файлом, уходит в index.php
try_files $uri $uri/ /index.php?$args;
location ~* \.(js|css|png|jpg|gif|ico)$ {
-4
View File
@@ -1,8 +1,4 @@
{{- if .Values.app.tracing.enabled }}
{{/*
Конфигурация sidecar-коллектора OpenTelemetry в поде web.
Создаётся только при app.tracing.enabled.
*/}}
---
apiVersion: v1
kind: ConfigMap
-4
View File
@@ -1,7 +1,3 @@
{{/*
Скрипт init-контейнера джобы миграций: ждёт, пока PostgreSQL начнёт принимать
подключения. Без этого миграции падают, если база стартует медленнее подов.
*/}}
---
apiVersion: v1
kind: ConfigMap
-7
View File
@@ -1,12 +1,5 @@
{{- if .Values.dragonfly.enabled }}
{{- $name := include "hrbox.component" (dict "ctx" . "name" "dragonfly") -}}
{{/*
Redis-совместимый кеш: сессии, кеш приложения и очереди Nanabush Player.
Одна реплика и стратегия Recreate — намеренно: данные хранятся в памяти,
второй экземпляр не даёт ни отказоустойчивости, ни консистентности.
Селектор оставлен как в чарте 2.x.
*/}}
---
apiVersion: apps/v1
kind: Deployment
-12
View File
@@ -1,9 +1,5 @@
{{- if .Values.nats.enabled }}
{{- $name := include "hrbox.component" (dict "ctx" . "name" "nats") -}}
{{/*
Брокер сообщений. Селектор StatefulSet намеренно оставлен таким же, как в
чарте 2.x: он неизменяем, и его правка потребовала бы пересоздания объекта.
*/}}
---
apiVersion: apps/v1
kind: StatefulSet
@@ -30,8 +26,6 @@ spec:
name: {{ printf "%s-config" $name }}
- name: pid
emptyDir: {}
# Общее пространство процессов нужно перезагрузчику конфигурации:
# он посылает сигнал процессу nats-server из соседнего контейнера.
shareProcessNamespace: true
terminationGracePeriodSeconds: {{ .Values.nats.terminationGracePeriodSeconds }}
{{- include "hrbox.imagePullSecrets" . | nindent 6 }}
@@ -63,9 +57,6 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.namespace
# Адрес, который нода объявляет остальным участникам кластера.
# Сегмент svc обязателен: без него имя не резолвится и ноды
# не находят друг друга.
- name: CLUSTER_ADVERTISE
value: $(POD_NAME).{{ $name }}.$(POD_NAMESPACE).svc.{{ .Values.cluster.domain }}
{{- include "hrbox.resources" (dict "ctx" . "component" "nats") | indent 10 }}
@@ -91,8 +82,6 @@ spec:
initialDelaySeconds: 10
timeoutSeconds: 5
lifecycle:
# Перевод ноды в lame duck: она перестаёт принимать новые
# подключения и даёт клиентам время переподключиться к соседям.
preStop:
exec:
command:
@@ -140,7 +129,6 @@ metadata:
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "nats") | nindent 4 }}
spec:
# Headless: клиентам нужны адреса конкретных нод, а не общий VIP.
clusterIP: None
selector:
app.kubernetes.io/name: nats
-6
View File
@@ -1,10 +1,4 @@
{{- $replicas := .Values.app.replicas.web | int }}
{{/*
Бюджет прерываний для web.
Имеет смысл только начиная с двух реплик: при одной minAvailable округляется
до 1 и намертво блокирует drain ноды, ничего при этом не защищая.
*/}}
{{- if gt $replicas 1 }}
---
apiVersion: policy/v1
-12
View File
@@ -1,12 +1,4 @@
{{- $name := include "hrbox.component" (dict "ctx" . "name" "web") -}}
{{/*
Веб-под: PHP-FPM, nginx перед ним и экспортёр метрик FPM.
При app.tracing.enabled рядом поднимается коллектор OpenTelemetry.
Аннотации checksum перезапускают поды, когда меняется конфигурация nginx или
секрет приложения: без них правка ConfigMap остаётся незамеченной до
следующего рестарта.
*/}}
---
apiVersion: apps/v1
kind: Deployment
@@ -56,8 +48,6 @@ spec:
{{- end }}
{{- include "hrbox.resources" (dict "ctx" . "component" "app") | indent 10 }}
lifecycle:
# Пауза даёт ingress-контроллеру убрать под из балансировки,
# прежде чем FPM начнёт корректное завершение.
preStop:
exec:
command:
@@ -92,8 +82,6 @@ spec:
- sh
- '-c'
- sleep 15 && /usr/sbin/nginx -s quit
# /healthz отвечает сам nginx, /health-check проверяет,
# что за ним жив PHP-FPM.
livenessProbe:
httpGet:
path: /healthz
-4
View File
@@ -1,8 +1,4 @@
{{- $name := include "hrbox.component" (dict "ctx" . "name" "chatbox") -}}
{{/*
Chatbox — вебсокет-сервер чатов и real-time уведомлений.
До версии 2.0 назывался websocket; имена объектов не менялись с тех пор.
*/}}
---
apiVersion: apps/v1
kind: Deployment
-8
View File
@@ -1,12 +1,4 @@
{{- $name := include "hrbox.component" (dict "ctx" . "name" "worker") -}}
{{/*
Обработчик фоновых задач: конвертация файлов, рассылка почты и уведомлений,
генерация отчётов, обслуживание Nanabush Player.
Стратегия Recreate: воркеры разбирают задачи из общей очереди, и пересечение
старых и новых подов во время выката привело бы к двойной обработке.
Длинный terminationGracePeriodSeconds даёт закончить уже взятую задачу.
*/}}
---
apiVersion: apps/v1
kind: Deployment
-6
View File
@@ -1,10 +1,4 @@
{{- $name := include "hrbox.component" (dict "ctx" . "name" "conductor") -}}
{{/*
Планировщик задач по расписанию.
Строго одна реплика: два планировщика запустили бы каждую задачу дважды.
По той же причине стратегия Recreate, а не RollingUpdate.
*/}}
---
apiVersion: apps/v1
kind: Deployment
@@ -1,14 +1,5 @@
{{- if .Values.app.videoConverter.enabled }}
{{- $name := include "hrbox.component" (dict "ctx" . "name" "file-processor-video") -}}
{{/*
Конвертация видео.
Отдельный деплоймент, потому что профиль ресурсов у него другой на порядок:
несколько ядер, гигабайты памяти и десятки гигабайт временного диска на под.
Смешивать это с обычной обработкой файлов нельзя — либо ресурсы простаивают,
либо конвертация видео вытесняет всё остальное. Через nodeSelector и
tolerations пул выносится на выделенные ноды.
*/}}
---
apiVersion: apps/v1
kind: Deployment
-6
View File
@@ -1,10 +1,4 @@
{{- $name := include "hrbox.component" (dict "ctx" . "name" "file-processor") -}}
{{/*
Конвертация документов и обработка изображений.
Конвертация видео здесь всегда выключена: она требует принципиально другого
профиля ресурсов и живёт в отдельном деплойменте 09-file-processor-video.
*/}}
---
apiVersion: apps/v1
kind: Deployment
-3
View File
@@ -1,7 +1,4 @@
{{- $name := include "hrbox.component" (dict "ctx" . "name" "geonames") -}}
{{/*
Справочник городов, стран и часовых поясов.
*/}}
---
apiVersion: apps/v1
kind: Deployment
-7
View File
@@ -1,11 +1,4 @@
{{- $name := include "hrbox.component" (dict "ctx" . "name" "goworker") -}}
{{/*
Интеграции с мессенджерами, push-уведомления, синхронизация оргструктуры
с 1С и восстановление проекций Nanabush Player.
Метрики Prometheus отдаются на том же порту 8686 по пути /metrics, поэтому
аннотации сбора висят на Service, а не на поде.
*/}}
---
apiVersion: apps/v1
kind: Deployment
-8
View File
@@ -1,12 +1,4 @@
{{- $name := include "hrbox.component" (dict "ctx" . "name" "kedoca") -}}
{{/*
Сервис электронной подписи: КЭДО, МЧД, метки времени.
Умеет ГОСТ (через КриптоПро) и обычный OpenSSL — выбор в app.kedoca.engine.
app.kedoca.secretName подключает Secret с сертификатами удостоверяющего
центра. Без него сервис создаёт собственный корневой сертификат при первом
запуске — это годится для теста, но не для юридически значимого документооборота.
*/}}
---
apiVersion: apps/v1
kind: Deployment
-29
View File
@@ -2,22 +2,6 @@
{{- if $cfg.enabled }}
{{- $name := include "nanabushPlayer.serviceName" . -}}
{{- $secretName := include "nanabushPlayer.secretName" . -}}
{{/*
Nanabush Player — проигрыватель учебных материалов: SCORM 1.2 и 2004, cmi5,
xAPI, нативные курсы, тесты и эссе.
Как это работает:
* HRBox выпускает launch-тикет, подписанный общим ключом ticketHs256,
и отправляет браузер на https://<домен>{{ include "nanabushPlayer.basePath" . }}/start;
* плеер проверяет подпись, audience и issuer, заводит свою сессию;
* за пакетом курса и для отправки отчётов плеер ходит обратно в HRBox по
внутрикластерному адресу — только он и разрешён в allowlist;
* учебные события копятся в Redis Streams и досылаются форвардером,
поэтому короткая недоступность HRBox не теряет прогресс.
Ограничения on-premise описаны в values.yaml рядом с app.nanabushPlayer.
Полный контракт — docs/nanabush-player-deployment.md в репозитории HRBox.
*/}}
---
apiVersion: apps/v1
kind: Deployment
@@ -49,9 +33,6 @@ spec:
prometheus.io/path: "/metrics"
{{- end }}
spec:
# Плееру не нужен доступ к API Kubernetes, а исполняет он содержимое,
# загруженное пользователями, — поэтому токен не монтируется, процесс
# не root, корневая файловая система только на чтение.
automountServiceAccountToken: false
terminationGracePeriodSeconds: {{ $cfg.terminationGracePeriodSeconds }}
{{- include "hrbox.imagePullSecrets" . | nindent 6 }}
@@ -89,8 +70,6 @@ spec:
{{- else }}
value: "off"
{{- end }}
# Путь монтирования не входит в audience: audience — это origin
# браузера, иначе сломались бы проверки на стороне клиента.
- name: PLAYER_BASE_PATH
value: {{ include "nanabushPlayer.basePath" . | quote }}
- name: PLAYER_AUDIENCE
@@ -103,16 +82,12 @@ spec:
name: {{ $secretName | quote }}
key: {{ required "Нужно имя ключа ticketHs256 в секрете Nanabush Player" $cfg.existingSecret.keys.ticketHs256 | quote }}
{{- with $cfg.existingSecret.keys.ticketKeys }}
# Кольцо ключей: подпись проверяется ключом, выбранным по kid из
# тикета, поэтому во время ротации работают обе версии HRBox.
- name: PLAYER_TICKET_KEYS
valueFrom:
secretKeyRef:
name: {{ $secretName | quote }}
key: {{ . | quote }}
{{- with $cfg.ticket.keylessAcceptedUntil }}
# Только на первую раскатку: принимать тикеты без kid от версии
# HRBox, которая ещё не знает про кольцо ключей.
- name: PLAYER_TICKET_KEYLESS_UNTIL
value: {{ . | quote }}
{{- end }}
@@ -139,8 +114,6 @@ spec:
{{- end }}
- name: PLAYER_REDIS_KEY_PREFIX
value: {{ include "nanabushPlayer.redisKeyPrefix" . | quote }}
# Обратный канал: единственный разрешённый адрес — внутрикластерный
# HRBox. Так пакет курса не сможет заставить плеер сходить наружу.
- name: PLAYER_PACKAGE_FETCH_SCHEMES
value: {{ $cfg.backchannel.schemes | quote }}
- name: PLAYER_PACKAGE_FETCH_ALLOWLIST
@@ -222,8 +195,6 @@ spec:
type: ClusterIP
selector:
service: {{ $name }}
# Порт метрик наружу намеренно не публикуется: содержимое курсов приходит
# с того же origin и не должно читать эксплуатационные метрики.
ports:
- name: http
port: {{ .Values.app.nanabushPlayer.service.port }}
-18
View File
@@ -1,18 +1,5 @@
{{- if .Values.ingress.enabled }}
{{- $player := .Values.app.nanabushPlayer -}}
{{/*
Единая точка входа.
Все три маршрута живут на одном домене, потому что в on-premise выделенных
поддоменов под сервисы нет:
{{ include "nanabushPlayer.basePath" . }}/ -> плеер учебных материалов
/chatbox/ -> вебсокеты чата
/ -> само приложение
Длинные таймауты нужны вебсокетам и плееру: и то и другое держит соединение
открытым, пока пользователь работает.
*/}}
---
apiVersion: networking.k8s.io/v1
kind: Ingress
@@ -21,12 +8,7 @@ metadata:
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "web") | nindent 4 }}
annotations:
# Аннотация класса устарела в пользу spec.ingressClassName ниже, но
# оставлена: контроллеры старше ingress-nginx 1.0 понимают только её.
# Когда заданы обе, современный контроллер использует ingressClassName.
kubernetes.io/ingress.class: {{ .Values.ingress.className | quote }}
# Аннотация NGINX Inc.; ingress-nginx её игнорирует, вебсокеты у него
# работают за счёт proxy-read-timeout выше.
nginx.org/websocket-services: {{ include "hrbox.component" (dict "ctx" . "name" "chatbox") | quote }}
nginx.ingress.kubernetes.io/proxy-body-size: {{ .Values.ingress.maxBodySize | quote }}
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
-9
View File
@@ -1,12 +1,3 @@
{{/*
Миграции базы данных.
Хук post-install/post-upgrade с весом 10: выполняется после того, как Helm
создал ConfigMap и Secret, но до джобы обновления кластера (вес 20).
Имя джобы содержит номер ревизии релиза, поэтому в истории видно, какой
выкат её запускал, а повторный upgrade не спотыкается об уже существующий
объект.
*/}}
---
apiVersion: batch/v1
kind: Job
@@ -1,9 +1,3 @@
{{/*
Обновление данных кластера после миграций: пересчёт справочников,
перерегистрация фоновых задач, обновление настроек арендаторов.
Вес 20 — строго после джобы миграций (вес 10).
*/}}
---
apiVersion: batch/v1
kind: Job
+16 -47
View File
@@ -1,18 +1,5 @@
{{/*
============================================================================
Переменные окружения HRBox
============================================================================
envs_all включается во все контейнеры, которым нужен конфиг приложения:
web, worker, conductor, chatbox, goworker, kedoca, file-processor, geonames
и джобы миграций. Один общий блок проще расходящихся наборов, и именно так
устроено продовое окружение.
Чувствительные значения приходят через secretKeyRef из секрета приложения
(00-app-secret.yaml), а не открытым текстом в спеке пода.
*/}}
{{- define "envs_all" }}
# --- Приложение -----------------------------------------------------------
# Приложение
- name: INSTANCE_NAME
value: {{ .Values.app.instanceName | quote }}
- name: DEFAULT_TENANT_HOSTNAME
@@ -45,7 +32,7 @@ web, worker, conductor, chatbox, goworker, kedoca, file-processor, geonames
{{- include "hrbox.secretEnv" (dict "ctx" . "env" "APP_ENCRYPTION_KEY" "key" "encryption-key") | nindent 0 }}
{{- include "hrbox.secretEnv" (dict "ctx" . "env" "SERVICES_INTERNAL_SECRET" "key" "services-internal-secret") | nindent 0 }}
# --- Своя политика конфиденциальности -------------------------------------
# Своя политика конфиденциальности
- name: ALLOW_CUSTOM_POLICY_URLS
value: {{ .Values.app.customPolicy.enabled | quote }}
{{- if .Values.app.customPolicy.enabled }}
@@ -60,7 +47,7 @@ web, worker, conductor, chatbox, goworker, kedoca, file-processor, geonames
value: ""
{{- end }}
# --- PostgreSQL -----------------------------------------------------------
# PostgreSQL
- name: POSTGRES_VHOST
value: {{ .Values.app.postgres.host | quote }}
- name: POSTGRES_PORT
@@ -73,7 +60,7 @@ web, worker, conductor, chatbox, goworker, kedoca, file-processor, geonames
value: {{ .Values.app.postgres.citus | quote }}
{{- include "hrbox.secretEnv" (dict "ctx" . "env" "POSTGRES_PASSWORD" "key" "postgres-password") | nindent 0 }}
# --- Redis / Dragonfly ----------------------------------------------------
# Redis / Dragonfly
- name: REDIS_HOST
value: {{ include "hrbox.redisHost" . | quote }}
- name: REDIS_PORT
@@ -81,13 +68,11 @@ web, worker, conductor, chatbox, goworker, kedoca, file-processor, geonames
- name: REDIS_CONNECTION
value: {{ printf "tcp://%s:%s" (include "hrbox.redisHost" .) (.Values.app.redis.port | toString) | quote }}
# --- NATS -----------------------------------------------------------------
# NATS
- name: BROKER
value: "nats"
- name: NATS_URL
value: {{ include "hrbox.natsUrl" . | quote }}
# Число реплик JetStream-стримов должно совпадать с размером кластера NATS,
# иначе стрим создаётся в одном экземпляре и теряется вместе со своей нодой.
- name: NATS_STREAM_REPLICAS
{{- if and .Values.nats.enabled .Values.nats.cluster.enabled }}
value: {{ .Values.nats.cluster.replicas | quote }}
@@ -95,7 +80,7 @@ web, worker, conductor, chatbox, goworker, kedoca, file-processor, geonames
value: "1"
{{- end }}
# --- S3 -------------------------------------------------------------------
# S3
- name: S3_ENDPOINT
value: {{ .Values.app.s3.endpoint | quote }}
- name: S3_BUCKET
@@ -113,7 +98,7 @@ web, worker, conductor, chatbox, goworker, kedoca, file-processor, geonames
{{- include "hrbox.secretEnv" (dict "ctx" . "env" "S3_KEY" "key" "s3-key") | nindent 0 }}
{{- include "hrbox.secretEnv" (dict "ctx" . "env" "S3_SECRET" "key" "s3-secret") | nindent 0 }}
# --- Почта ----------------------------------------------------------------
# Почта
- name: SENDER_ENV
value: "PROD"
- name: SENDER_DEV_EMAIL
@@ -136,7 +121,7 @@ web, worker, conductor, chatbox, goworker, kedoca, file-processor, geonames
value: {{ .Values.app.smtp.sslVerifyPeer | quote }}
{{- include "hrbox.secretEnv" (dict "ctx" . "env" "SMTP_PASSWORD" "key" "smtp-password") | nindent 0 }}
# --- Внутренние сервисы ---------------------------------------------------
# Внутренние сервисы
- name: GEONAMES_URL
value: {{ include "hrbox.geonamesUrl" . | quote }}
- name: GOWORKER_URL
@@ -160,27 +145,27 @@ web, worker, conductor, chatbox, goworker, kedoca, file-processor, geonames
- name: DOCCONV_MAX_PARALLEL_TASKS
value: {{ .Values.app.fileProcessor.docconvMaxParallelTasks | quote }}
# --- HRBox Hub ------------------------------------------------------------
# HRBox Hub
- name: HUB_URL
value: {{ .Values.app.hub.url | quote }}
{{- include "hrbox.secretEnv" (dict "ctx" . "env" "HUB_TOKEN" "key" "hub-token") | nindent 0 }}
# --- Nanabush Player ------------------------------------------------------
# Nanabush Player
{{- include "envs_nanabush_player" . }}
# --- Чат ------------------------------------------------------------------
# Чат
- name: CHATBOX_SENTRY_DSN
value: {{ .Values.app.sentry.chatboxDsn | quote }}
{{- include "hrbox.secretEnv" (dict "ctx" . "env" "CHATBOX_WRAPPING_KEY" "key" "chatbox-wrapping-key") | nindent 0 }}
# --- Видеозвонки (LiveKit) ------------------------------------------------
# Видеозвонки (LiveKit)
- name: LIVEKIT_URL
value: {{ .Values.app.livekit.url | quote }}
- name: LIVEKIT_API_KEY
value: {{ .Values.app.livekit.apiKey | quote }}
{{- include "hrbox.secretEnv" (dict "ctx" . "env" "LIVEKIT_API_SECRET" "key" "livekit-api-secret") | nindent 0 }}
# --- AI -------------------------------------------------------------------
# AI
- name: AI_DOCS_INDEX_ENABLED
value: {{ .Values.app.ai.docsIndexEnabled | quote }}
- name: AI_SEMANTIC_INDEX_ENABLED
@@ -191,7 +176,7 @@ web, worker, conductor, chatbox, goworker, kedoca, file-processor, geonames
value: {{ .Values.app.ai.yandex.folderId | quote }}
{{- include "hrbox.secretEnv" (dict "ctx" . "env" "YANDEX_CLOUD_AI_API_KEY" "key" "yandex-ai-api-key") | nindent 0 }}
# --- Внешние интеграции ---------------------------------------------------
# Внешние интеграции
- name: TELEGRAM_API_URL
value: {{ .Values.app.integrations.telegram.apiUrl | quote }}
- name: AUTH_GOOGLE_ENABLED
@@ -211,7 +196,7 @@ web, worker, conductor, chatbox, goworker, kedoca, file-processor, geonames
- name: GLAGOL_API_URL
value: {{ .Values.app.integrations.glagol.apiUrl | quote }}
# --- Sentry ---------------------------------------------------------------
# Sentry
- name: SENTRY_DSN
value: {{ .Values.app.sentry.dsn | quote }}
- name: SENTRY_DSN_LAUNCHPAD
@@ -223,7 +208,7 @@ web, worker, conductor, chatbox, goworker, kedoca, file-processor, geonames
- name: SENTRY_RELEASE
value: {{ .Values.image.hrbox | quote }}
# --- Метаданные пода ------------------------------------------------------
# Метаданные пода
- name: POD_NAME
valueFrom:
fieldRef:
@@ -242,13 +227,6 @@ web, worker, conductor, chatbox, goworker, kedoca, file-processor, geonames
fieldPath: spec.nodeName
{{- end }}
{{/*
Настройки Nanabush Player на стороне HRBox.
NANABUSH_PLAYER_AUDIENCE и NANABUSH_PLAYER_PUBLIC_ORIGIN всегда равны:
audience тикета — это origin браузера без пути, а путь монтирования едет
отдельной переменной NANABUSH_PLAYER_BASE_PATH.
*/}}
{{- define "envs_nanabush_player" -}}
{{- $player := .Values.app.nanabushPlayer }}
- name: NANABUSH_PLAYER_ENABLED
@@ -291,10 +269,6 @@ audience тикета — это origin браузера без пути, а п
{{- end }}
{{- end -}}
{{/*
Ключ подписи launch-тикета. Живёт только в PHP-контейнерах, которые выпускают
тикеты: nginx и вспомогательным контейнерам он не нужен.
*/}}
{{- define "envs_nanabush_player_signing" -}}
{{- $cfg := .Values.app.nanabushPlayer.deployment }}
{{- if $cfg.enabled }}
@@ -304,9 +278,6 @@ audience тикета — это origin браузера без пути, а п
name: {{ include "nanabushPlayer.secretName" . | quote }}
key: {{ required "Нужно имя ключа ticketHs256 в секрете Nanabush Player" $cfg.existingSecret.keys.ticketHs256 | quote }}
{{- with $cfg.existingSecret.keys.ticketKeys }}
# Кольцо ключей launch-тикета: HRBox подписывает первым ключом, Player
# принимает все. Тот же ключ секрета читает и сам Player, поэтому ротация —
# это одно значение на обе стороны.
- name: NANABUSH_PLAYER_TICKET_KEYS
valueFrom:
secretKeyRef:
@@ -316,7 +287,6 @@ audience тикета — это origin браузера без пути, а п
{{- end }}
{{- end -}}
{{/* Криптографические настройки сервиса электронной подписи. */}}
{{- define "envs_kedoca" }}
- name: ROOT_CA_PASS
value: {{ .Values.app.kedoca.rootCaPass | quote }}
@@ -328,7 +298,6 @@ audience тикета — это origin браузера без пути, а п
value: {{ .Values.app.kedoca.pkeyopt | quote }}
{{- end }}
{{/* OpenTelemetry — только когда app.tracing.enabled. */}}
{{- define "envs_tracing" }}
- name: OTEL_PHP_AUTOLOAD_ENABLED
value: "true"
-101
View File
@@ -1,49 +1,19 @@
{{/*
============================================================================
Общие хелперы чарта HRBox
============================================================================
Правило именования: все объекты называются "<имя чарта>-<компонент>"
(hrbox-web, hrbox-worker, ...). Имя чарта можно переопределить через
nameOverride, но по умолчанию оно равно "hrbox" — так же, как в чарте 2.x,
поэтому обновление 2.x -> 3.x не пересоздаёт объекты.
*/}}
{{/* Базовое имя релиза: "hrbox" или nameOverride. */}}
{{- define "hrbox.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/* Имя чарта с версией — для лейбла helm.sh/chart. */}}
{{- define "hrbox.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Полное имя объекта компонента.
Usage: {{ include "hrbox.component" (dict "ctx" . "name" "web") }} -> hrbox-web
*/}}
{{- define "hrbox.component" -}}
{{- printf "%s-%s" (include "hrbox.name" .ctx) .name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Селектор пода. ВАЖНО: spec.selector в Deployment неизменяем, поэтому набор
лейблов здесь совпадает с чартом 2.x (один лейбл "service") и меняться не должен.
Usage: {{- include "hrbox.selectorLabels" (dict "ctx" . "name" "web") | nindent 6 }}
*/}}
{{- define "hrbox.selectorLabels" -}}
service: {{ include "hrbox.component" (dict "ctx" .ctx "name" .name) }}
{{- end -}}
{{/*
Лейблы объекта. Лейбл "app" сохранён из чарта 2.x — на него опираются
привычные команды вида "kubectl get pods -l app=worker".
Usage: {{- include "hrbox.labels" (dict "ctx" . "name" "web") | nindent 4 }}
*/}}
{{- define "hrbox.labels" -}}
app: {{ .name }}
app.kubernetes.io/name: {{ include "hrbox.name" .ctx }}
@@ -54,11 +24,6 @@ app.kubernetes.io/version: {{ .ctx.Chart.AppVersion | quote }}
helm.sh/chart: {{ include "hrbox.chart" .ctx }}
{{- end -}}
{{/*
Секреты для доступа к приватному Docker-реестру.
Usage: {{- include "hrbox.imagePullSecrets" . | nindent 6 }}
*/}}
{{- define "hrbox.imagePullSecrets" -}}
{{- if .Values.imagePullSecrets }}
imagePullSecrets:
@@ -68,11 +33,6 @@ imagePullSecrets:
{{- end }}
{{- end -}}
{{/*
Тег из ссылки на образ: "registry/repo:1.2.3" -> "1.2.3".
Двоеточие ищется только в последнем сегменте пути, чтобы не спутать тег
с портом реестра ("registry:5000/repo").
*/}}
{{- define "hrbox.imageTag" -}}
{{- $lastSegment := splitList "/" . | last -}}
{{- if contains ":" $lastSegment -}}
@@ -80,27 +40,6 @@ imagePullSecrets:
{{- end -}}
{{- end -}}
{{/*
============================================================================
Секрет приложения
============================================================================
Чувствительные значения (пароли БД и SMTP, ключи шифрования, токены) не
попадают в env-блоки Deployment открытым текстом: чарт складывает их в один
Secret, а контейнеры читают его через secretKeyRef. Полный список ключей —
в README, раздел "Секреты".
secrets.existingSecret позволяет подставить Secret, созданный вне чарта
(например, из внешнего vault-оператора) — тогда чарт свой Secret не создаёт.
*/}}
{{/*
Адреса внутренних сервисов.
Сервисы релиза адресуются по именам, которые создаёт этот же чарт, поэтому
пустое значение в values резолвится в имя из hrbox.component: адреса не
разъезжаются при nameOverride и их не нужно править вручную. Непустое
значение используется как есть — для сервиса за пределами релиза.
*/}}
{{- define "hrbox.geonamesUrl" -}}
{{- .Values.app.geonames.url | default (printf "http://%s:8181" (include "hrbox.component" (dict "ctx" . "name" "geonames"))) -}}
{{- end -}}
@@ -129,11 +68,6 @@ secrets.existingSecret позволяет подставить Secret, созд
{{- end -}}
{{- end -}}
{{/*
Одна переменная окружения из секрета приложения.
Usage: {{ include "hrbox.secretEnv" (dict "ctx" . "env" "POSTGRES_PASSWORD" "key" "postgres-password") }}
*/}}
{{- define "hrbox.secretEnv" -}}
- name: {{ .env }}
valueFrom:
@@ -142,23 +76,6 @@ Usage: {{ include "hrbox.secretEnv" (dict "ctx" . "env" "POSTGRES_PASSWORD" "key
key: {{ .key | quote }}
{{- end -}}
{{/*
============================================================================
Nanabush Player
============================================================================
On-premise-топология Player'а отличается от облачной: выделенного поддомена
нет, поэтому Player живёт на основном домене HRBox под путём basePath
(по умолчанию /nanabush-player). Отсюда следуют три инварианта, которые
проверяет 00-validate.yaml:
* audience тикета = https://<ingress.host> (origin без пути)
* basePath = отдельная переменная, в aud не входит
* issuer тикета = https://<ingress.host>/lms/player
Подробности контракта — docs/nanabush-player-deployment.md в репозитории HRBox.
*/}}
{{- define "nanabushPlayer.host" -}}
{{- required "Для Nanabush Player нужен непустой ingress.host" .Values.ingress.host -}}
{{- end -}}
@@ -167,10 +84,6 @@ On-premise-топология Player'а отличается от облачно
{{- printf "https://%s" (include "nanabushPlayer.host" .) -}}
{{- end -}}
{{/*
Путь монтирования Player'а на основном домене. Пустое значение и "/" запрещены:
это отдало бы Player'у корень, занятый самим HRBox.
*/}}
{{- define "nanabushPlayer.basePath" -}}
{{- $path := .Values.app.nanabushPlayer.basePath | default "/nanabush-player" -}}
{{- $path | trimSuffix "/" -}}
@@ -188,11 +101,6 @@ On-premise-топология Player'а отличается от облачно
{{- .Values.app.nanabushPlayer.service.name | default (include "hrbox.component" (dict "ctx" . "name" "nanabush-player")) -}}
{{- end -}}
{{/*
Хост обратного канала Player -> HRBox. Только внутрикластерное имя: по нему
Player забирает пакеты курсов и отправляет отчёты, и оно же попадает в
allowlist самого Player'а.
*/}}
{{- define "nanabushPlayer.backchannelHost" -}}
{{- .Values.app.nanabushPlayer.deployment.backchannel.host | default (include "hrbox.component" (dict "ctx" . "name" "web")) -}}
{{- end -}}
@@ -201,10 +109,6 @@ allowlist самого Player'а.
{{- printf "http://%s" (include "nanabushPlayer.backchannelHost" .) -}}
{{- end -}}
{{/*
Версия Player'а берётся из тега образа, а не из отдельной настройки: тикет
не должен объявлять версию, отличную от той, что реально запущена в кластере.
*/}}
{{- define "nanabushPlayer.version" -}}
{{- $tag := include "hrbox.imageTag" .Values.image.nanabushPlayer -}}
{{- if not $tag -}}
@@ -217,15 +121,10 @@ allowlist самого Player'а.
{{- required "app.nanabushPlayer.deployment.existingSecret.name обязателен, когда Player включён" .Values.app.nanabushPlayer.deployment.existingSecret.name -}}
{{- end -}}
{{/* URL Redis, который чарт собирает из штатных настроек app.redis. */}}
{{- define "nanabushPlayer.redisUrl" -}}
{{- printf "redis://%s:%s/%d" (include "hrbox.redisHost" .) (.Values.app.redis.port | toString) (.Values.app.nanabushPlayer.deployment.redis.database | int) -}}
{{- end -}}
{{/*
Префикс ключей Redis. Namespace в префиксе разводит несколько релизов HRBox,
живущих на одном Dragonfly.
*/}}
{{- define "nanabushPlayer.redisKeyPrefix" -}}
{{- printf "hrbox:%s:%s" .Release.Namespace .Values.app.nanabushPlayer.deployment.redis.keyPrefixSuffix -}}
{{- end -}}
-13
View File
@@ -1,16 +1,3 @@
{{/*
============================================================================
resources: для контейнера
============================================================================
В чарте 2.x на каждый компонент был отдельный define из восьми одинаковых
строк. Здесь один хелпер, который читает resources.<компонент> из values.yaml.
Незаданные поля просто не рендерятся, поэтому компоненту можно указать,
например, только requests.
Usage:
{{- include "hrbox.resources" (dict "ctx" . "component" "app") | indent 10 }}
*/}}
{{- define "hrbox.resources" -}}
{{- $r := index .ctx.Values.resources .component -}}
{{- if $r }}
+166 -203
View File
@@ -1,57 +1,44 @@
---
# ============================================================================
# HRBox — конфигурация on-premise установки
# ============================================================================
# Конфигурация HRBox для установки в своём кластере.
#
# Минимум, который нужно заполнить перед первым деплоем:
# Заполните перед первой установкой:
# ingress.host домен, по которому будет открываться HRBox
# app.defaultTenantHostname тот же домен
# app.postgres.* подключение к PostgreSQL
# app.s3.* хранилище файлов
# app.smtp.* почта
# app.hub.token токен HRBox Hub, выдают сотрудники HRBox
# secrets.* три ключа, генерируются openssl
# app.chatbox.wrappingKey ключ шифрования чатов
# app.nanabushPlayer.keys.* два ключа плеера курсов
#
# ingress.host домен инсталляции
# app.defaultTenantHostname тот же домен
# app.postgres.* подключение к PostgreSQL
# app.s3.* S3-совместимое хранилище
# app.smtp.* почта
# app.hub.token токен HRBox Hub (выдаёт HRBox)
# secrets.* три сгенерированных ключа
# app.chatbox.wrappingKey ключ шифрования чатов
# app.nanabushPlayer.keys.* два ключа плеера курсов
#
# Чарт проверяет их на этапе рендера и падает с понятной ошибкой, если
# что-то не заполнено, — до того, как в кластере появятся поды.
#
# Команды генерации ключей перечислены рядом с каждым полем и целиком —
# в README, раздел «Секреты».
# ============================================================================
# Если что-то из этого не заполнено, helm остановится и напишет, чего не
# хватает. Команды генерации ключей — в README, раздел «Секреты».
# Переопределение базового имени объектов. По умолчанию — имя чарта (hrbox),
# то есть hrbox-web, hrbox-worker и так далее. Менять на работающей установке
# нельзя: selector у Deployment неизменяем.
# Базовое имя объектов. Пусто — имя чарта: hrbox-web, hrbox-worker и т.д.
# На уже работающей установке менять нельзя.
nameOverride: ""
# ============================================================================
# Образы
# ============================================================================
# Все образы собраны в одном месте: обновление версии сервиса — это правка
# одной строки здесь, а не поиск по шаблонам.
# --- Образы ---------------------------------------------------------------
image:
# Основное приложение (PHP-FPM, nginx, worker, conductor-задачи, миграции).
# Приложение: PHP-FPM, nginx, worker, миграции.
# Доступ к реестру выдают сотрудники HRBox.
hrbox: "cr.yandex/crphnph9c2s776j32sjd/hrbox:latest"
# Чат и вебсокеты
chatbox: "cr.yandex/crphnph9c2s776j32sjd/hrbox-chatbox:5.18"
# Интеграции с мессенджерами, пуши, синхронизация оргструктуры
# Мессенджеры, пуши, синхронизация оргструктуры
goworker: "cr.yandex/crphnph9c2s776j32sjd/hrbox-goworker:3.21.2"
# Электронная подпись (КЭДО, МЧД)
# Электронная подпись: КЭДО, МЧД
kedoca: "cr.yandex/crphnph9c2s776j32sjd/hrbox-kedoca:1.0.6"
# Справочник городов, стран и часовых поясов
# Справочник городов, стран, часовых поясов
geonames: "cr.yandex/crphnph9c2s776j32sjd/hrbox-geonames:3.3"
# Конвертация документов и обработка изображений
# Конвертация документов и изображений
fileProcessor: "cr.yandex/crphnph9c2s776j32sjd/hrbox-file-processor:1.4.6"
# Планировщик задач по расписанию
conductor: "cr.yandex/crphnph9c2s776j32sjd/hrbox-conductor:2.2.0"
# Плеер учебных материалов (SCORM, cmi5, xAPI, нативные курсы).
# Тег обязателен и не может быть latest: из него выводится версия плеера,
# которую HRBox кладёт в launch-тикет.
# Плеер курсов. Нужен конкретный тег, latest не подходит:
# из тега берётся версия плеера.
nanabushPlayer: "cr.yandex/crphnph9c2s776j32sjd/nanabush-player:1.0.11"
# Инфраструктура
@@ -59,48 +46,43 @@ image:
nats: "nats:2.10"
natsConfigReloader: "natsio/nats-server-config-reloader:0.6.2"
phpFpmExporter: "cr.yandex/crpnn6fi85p3dauha6nc/php-fpm_exporter:latest"
# Используется только init-контейнером джобы миграций (ждёт готовности БД)
# Только для джобы миграций: ждёт готовности базы
postgresClient: "postgres:14-alpine"
# Используется только при app.tracing.enabled
# Только при app.tracing.enabled
otelCollector: "otel/opentelemetry-collector:0.109.0"
pullPolicy: IfNotPresent
# Секреты доступа к приватному Docker-реестру.
# Создаются отдельно, см. README, шаг 1.
# Secret с доступом к реестру образов. Создаётся отдельно, см. README, шаг 1.
imagePullSecrets:
- name: regsecret
# ============================================================================
# Приложение
# ============================================================================
# --- Приложение -----------------------------------------------------------
app:
# Основной домен инсталляции. Должен совпадать с ingress.host.
# Домен установки. Должен совпадать с ingress.host.
defaultTenantHostname: "hrbox.example.com"
# Имя инстанса — попадает в логи и метрики, помогает различать установки.
# Имя установки, видно в логах и метриках
instanceName: "onprem"
# Режим одного арендатора. "1" — вся установка обслуживает одну компанию.
# "1" — установка обслуживает одну компанию
singleTenant: "0"
# Frontend 2: "true" — включён (по умолчанию), "false" — старый интерфейс.
# "true" — новый интерфейс, "false" — старый
front2: "true"
# Отдельный домен авторизации. Для on-premise почти всегда пусто.
# Отдельный домен авторизации. Для on-premise обычно пусто.
authHost: ""
# "0" — production, "1" — отладка. В production всегда "0".
# "0" — production, "1" — отладка
debug: "0"
# Принудительный https во всех сгенерированных ссылках.
# Подставлять https во всех ссылках, которые генерирует приложение
forceHttps: "true"
# Количество реплик.
# web 2-10, по числу пользователей
# worker 2-5, фоновые задачи
# chatbox 2-6, real-time
# остальные обычно хватает 1-2
# Число подов каждого сервиса.
# web — по числу пользователей, worker — по объёму фоновых задач,
# chatbox — по числу открытых вкладок, остальным обычно хватает 1-2.
replicas:
web: 2
worker: 2
@@ -111,43 +93,41 @@ app:
fileProcessor: 1
fileProcessorVideo: 1
# Своя политика конфиденциальности и правила использования.
# При enabled: false приложение показывает ссылки HRBox.
# Свои ссылки на политику конфиденциальности и правила использования.
# false — показываются ссылки HRBox.
customPolicy:
enabled: false
# privacyUrl: "https://example.com/privacy"
# termsUrl: "https://example.com/terms"
# --- PostgreSQL -----------------------------------------------------------
# Требуется PostgreSQL 14+. Для отказоустойчивости настройте репликацию.
# --- PostgreSQL ---------------------------------------------------------
# Нужен PostgreSQL 14 или новее. Чарт базу не разворачивает.
postgres:
host: "postgresql"
port: "5432"
database: "hrbox"
user: "hrbox"
# Пароль попадает в Secret чарта, а не в env-блок Deployment.
# Попадает в Secret, в спеке подов пароля нет
password: ""
# "true", если база работает на расширении Citus (шардирование).
# "true", если база на Citus (шардирование)
citus: "false"
# --- Redis / Dragonfly ----------------------------------------------------
# Хранит сессии, кеш и очереди плеера курсов. Потеря данных не критична,
# но разлогинивает пользователей и сбрасывает незавершённые события плеера.
#
# Пусто — используется Dragonfly из этого же релиза. Заполняйте, только
# если Redis внешний (тогда обычно dragonfly.enabled: false).
# --- Redis / Dragonfly --------------------------------------------------
# Сессии, кеш и очереди плеера курсов.
# Пусто — используется Dragonfly из этого же релиза.
# Адрес указывают, если Redis внешний (тогда обычно dragonfly.enabled: false).
redis:
host: ""
port: "6379"
# --- NATS JetStream -------------------------------------------------------
# Пусто — NATS из этого же релиза.
# --- NATS ---------------------------------------------------------------
# Пусто — используется NATS из этого же релиза.
# Внешний кластер: nats://node1:4222,nats://node2:4222,nats://node3:4222
nats:
url: ""
# --- S3 -------------------------------------------------------------------
# Подходят AWS S3, MinIO, Yandex Object Storage, VK Cloud Storage.
# --- S3 -----------------------------------------------------------------
# Подойдёт AWS S3, MinIO, Yandex Object Storage, VK Cloud Storage.
s3:
# MinIO: http://minio:9000
# Yandex: https://storage.yandexcloud.net
@@ -158,12 +138,12 @@ app:
secret: ""
region: "us-east-1"
version: "latest"
# "true" для MinIO и self-hosted S3, "false" для AWS S3
# "true" для MinIO и своего S3, "false" для AWS S3
pathStyle: "true"
multipart: "true"
prefix: ""
# --- Почта ----------------------------------------------------------------
# --- Почта --------------------------------------------------------------
smtp:
host: "smtp.example.com"
# 25 — без шифрования, 587 — STARTTLS, 465 — SSL/TLS
@@ -177,33 +157,33 @@ app:
sslAllowSelfSigned: "true"
sslVerifyPeer: "false"
# --- Мониторинг ошибок ----------------------------------------------------
# --- Sentry -------------------------------------------------------------
sentry:
dsn: ""
environment: "production"
# Отдельные DSN для подсистем. Пусто — используется общий dsn.
# Пусто — используется общий dsn
launchpadDsn: ""
conductorDsn: ""
# У этих двух общего fallback нет: пусто означает «не отправлять».
# Здесь пусто означает «не отправлять»
goworkerDsn: ""
chatboxDsn: ""
# --- Внутренние сервисы ---------------------------------------------------
# Пустой url означает сервис из этого же релиза: адрес собирается из имени,
# которое создаёт чарт. Заполняйте, только если вынесли сервис наружу.
# --- Внутренние сервисы -------------------------------------------------
# Пустой url — сервис берётся из этого же релиза.
# Заполняют, если сервис вынесен наружу.
geonames:
url: ""
goworker:
url: ""
# Минимальный интервал между push-уведомлениями одному пользователю
# Не чаще одного push одному пользователю за этот интервал
pushMinInterval: "5m"
kedoca:
url: ""
# Пароль корневого сертификата, который сервис создаёт при первом запуске
rootCaPass: "hrbox"
# "gost" (ГОСТ, КриптоПро) или "openssl"
# "gost" (КриптоПро) или "openssl"
engine: "gost"
# "gost2012_256" для ГОСТ, "rsa" для OpenSSL
algorithm: "gost2012_256"
@@ -211,81 +191,79 @@ app:
pkeyopt: "paramset:A"
# Служба меток времени
tspUrl: "http://qs.cryptopro.ru/tsp/tsp.srf"
# Kubernetes Secret с сертификатами УЦ. Если задан — монтируется в /ca-cert.
# Secret с сертификатами УЦ. Если задан, монтируется в /ca-cert.
secretName: ""
fileProcessor:
# Сколько документов конвертировать одновременно в одном поде
docconvMaxParallelTasks: "1"
# Сколько изображений ресайзить одновременно (пул конвертации видео)
# Сколько изображений обрабатывать одновременно в поде конвертации видео
imageResizeMaxParallel: "20"
# --- Конвертация видео ----------------------------------------------------
# Отдельный пул подов: конвертация видео требует много CPU и диска и
# выносится на выделенные ноды, чтобы не мешать остальным задачам.
# --- Конвертация видео --------------------------------------------------
# Отдельные поды: видео требует много CPU и диска, поэтому его выносят
# на выделенные ноды.
videoConverter:
enabled: false
# Внутренний адрес HRBox, по которому конвертер забирает и отдаёт файлы.
# Пусто — берётся http://<релиз>-web.
# Внутренний адрес HRBox, откуда конвертер берёт и куда отдаёт файлы.
# Пусто — http://<релиз>-web.
internalUrl: ""
# Куда планировать поды конвертера. Пусто — без ограничений.
# На каких нодах запускать. Пусто — на любых.
# Пример: role: converter-dedicated
nodeSelector: {}
# role: converter-dedicated
tolerations: []
# Пример:
# - key: dedicated
# operator: Equal
# value: video-converter
# effect: NoSchedule
tolerations: []
# --- Чат ------------------------------------------------------------------
# --- Чат ----------------------------------------------------------------
chatbox:
# Часть ключа асимметричного шифрования чатов, 32 символа.
# Часть ключа шифрования чатов, 32 символа.
# Генерация: openssl rand -hex 16
wrappingKey: ""
# --- Видеозвонки ----------------------------------------------------------
# --- Видеозвонки --------------------------------------------------------
livekit:
url: ""
apiKey: ""
apiSecret: ""
# --- Nanabush Player ------------------------------------------------------
# Плеер учебных материалов: SCORM 1.2/2004, cmi5, xAPI, нативные курсы,
# тесты и эссе. Полное описание контракта — docs/nanabush-player-deployment.md
# в репозитории HRBox.
# --- Nanabush Player ----------------------------------------------------
# Плеер учебных материалов: SCORM 1.2 и 2004, cmi5, xAPI, нативные курсы,
# тесты и эссе.
#
# В облаке плеер живёт на отдельном поддомене. В on-premise выделенного
# поддомена нет, поэтому плеер монтируется на основной домен под путём
# basePath. Практическое следствие: cookie HRBox видны содержимому курсов
# (один origin). Если для сторонних SCORM-пакетов нужна изоляция — заводите
# отдельный поддомен и обращайтесь в HRBox за облачной схемой.
# В облаке плеер стоит на отдельном поддомене, в on-premise поддомена нет,
# поэтому он живёт на основном домене по пути basePath. Домен общий, значит
# cookie HRBox доступны содержимому курсов. Если нужна изоляция сторонних
# SCORM-пакетов, обратитесь в HRBox за схемой с отдельным поддоменом.
nanabushPlayer:
# Отдавать ли учебные материалы через плеер. false — старый проигрыватель.
# false — материалы открываются старым проигрывателем
enabled: true
# Путь монтирования на основном домене. Пустой и "/" запрещены: корень
# занят самим HRBox.
# Путь на основном домене. Корень "/" занят самим HRBox.
basePath: "/nanabush-player"
# Issuer launch-тикета. Пусто — https://<ingress.host>/lms/player.
# Issuer launch-тикета. Пусто — https://<ingress.host>/lms/player
issuer: ""
contractVersion: "v0-current"
# Ключи плеера. Оба обязательны и должны отличаться друг от друга.
# Оба ключа обязательны и должны быть разными.
# Генерация каждого: openssl rand -base64 48
keys:
ticketHs256: ""
sessionHs256: ""
# Необязательное кольцо ключей для ротации без простоя:
# "kid1:секрет1,kid2:секрет2", текущий ключ подписи — первым.
# Секрет не должен содержать запятую (используйте base64url).
# Набор ключей для ротации без простоя, необязательный:
# "kid1:секрет1,kid2:секрет2". Первым идёт ключ, которым подписывают
# сейчас. Запятая внутри секрета недопустима, используйте base64url.
ticketKeys: ""
# Доля запусков, уходящих в новый плеер. 100 — все.
rollout:
# Какая доля запусков идёт в новый плеер. 100 — все.
percent: 100
# Разрешить откат на старый проигрыватель, если плеер недоступен
# Открывать материал старым проигрывателем, если плеер не ответил
legacyFallback: true
allowedContentTypes:
- document
@@ -299,7 +277,7 @@ app:
- native
- native_quiz
- native_essay
# "*" — все арендаторы, включая будущих
# "*" — все компании, включая новые
allowedTenants:
- "*"
@@ -308,26 +286,25 @@ app:
name: ""
port: 8094
# Публиковать ли путь плеера на основном Ingress.
# Выключать имеет смысл только при аварийном откате.
# Публиковать ли путь плеера на общем Ingress.
# Выключают только при откате.
ingress:
enabled: true
deployment:
# Создавать ли Deployment и Service плеера.
# Создавать ли Deployment и Service плеера
enabled: true
replicas: 1
terminationGracePeriodSeconds: 120
# Образ плеера должен уметь работать под путём (а не только в корне
# домена). Если ставите старый образ без этой поддержки — переключите
# в false, и чарт остановит рендер с объяснением вместо неработающего
# плеера в кластере.
# Умеет ли образ плеера работать не в корне домена, а по пути.
# Со старым образом без такой поддержки поставьте false — чарт
# остановит установку вместо того, чтобы развернуть нерабочий плеер.
binaryCapabilities:
basePath: true
# Secret с ключами плеера. create: false — Secret создаётся вне чарта
# (например, внешним vault-оператором), чарт только ссылается на него.
# Secret с ключами плеера.
# create: false — Secret заводят вне чарта, чарт только ссылается.
existingSecret:
create: true
name: "hrbox-nanabush-player"
@@ -338,48 +315,46 @@ app:
ticketKeys: ""
ticket:
# Время жизни launch-тикета, секунды
# Сколько живёт launch-тикет, секунды
ttlSeconds: 300
# Время жизни учётных данных обратного канала: сессия плеера + 2 минуты
# на расхождение часов
# Сколько живут учётные данные обратного канала: сессия плеера плюс
# 2 минуты на расхождение часов
credentialTtlSeconds: 28920
maxLifetime: "5m"
sessionTtl: "8h"
# Только на время первой раскатки кольца ключей: до какого момента
# Нужно только при первом переходе на набор ключей: до какого момента
# (RFC3339) принимать тикеты без kid. Пусто — не принимать.
keylessAcceptedUntil: ""
redis:
# false — URL собирается из app.redis.
# true — полный redis:// или rediss:// URL читается из внешнего
# Secret (нужно для ACL, пароля или TLS) и требует
# existingSecret.create: false.
# false — адрес собирается из app.redis.
# true — полный redis:// или rediss:// URL берётся из внешнего Secret
# (нужно для пароля, ACL или TLS), требует existingSecret.create: false
urlFromExistingSecret: false
database: 0
# Итоговый префикс: hrbox:<namespace>:<suffix> — разводит несколько
# релизов HRBox на одном Dragonfly.
# Полный префикс: hrbox:<namespace>:<суффикс>.
# Разделяет несколько установок HRBox на одном Redis.
keyPrefixSuffix: "nanabush-player"
# Обращения плеера обратно в HRBox: за пакетом курса и с отчётами
backchannel:
# Внутрикластерный адрес HRBox. Пусто — <релиз>-web.
# Внутренний адрес HRBox. Пусто — <релиз>-web.
host: ""
schemes: "http"
allowPrivateIps: true
packageFetchMaxBytes: "5242880"
# Транспорт должен вмещать самый большой поддерживаемый медиафайл
# Должно вмещать самый большой медиафайл курса
packageContentMaxBytes: "2147483648"
scormPackageMaxBytes: "1073741824"
# Пусто — ограничение по числу файлов в SCORM-пакете берётся из
# умолчаний приложения (4000)
# Пусто — берётся значение приложения (4000 файлов)
scormPackageMaxFiles: ""
packageFetchTimeout: "15s"
# Таймаут покрывает всю передачу, включая отдачу байтов браузеру
# учащегося, поэтому упирается в скорость его канала. Двух минут
# не хватало на крупное видео.
# Считается до конца передачи файла в браузер учащегося, поэтому
# зависит от скорости его интернета
packageStreamTimeout: "20m"
reportTimeout: "30s"
# Очередь отправки учебных событий в HRBox.
# Очередь отправки учебных событий в HRBox
forwarder:
queueSize: "1000"
workers: "4"
@@ -398,8 +373,7 @@ app:
level: info
format: json
# Метрики отдаются на отдельном порту, который не публикуется наружу:
# содержимое курсов не должно читать эксплуатационные метрики.
# Метрики отдаются на отдельном порту и наружу не публикуются
metrics:
enabled: true
port: 9090
@@ -418,22 +392,22 @@ app:
timeoutSeconds: 2
failureThreshold: 3
# --- Фоновые задачи -------------------------------------------------------
# Формат: "топик:количество-воркеров"
# --- Фоновые задачи -----------------------------------------------------
# Формат: "очередь:сколько воркеров"
launchpad:
topics: "launcher:1,file-processor:1,image-processor:1,send-mail:1,send-im:1,send-bell:1,send-push:1,backup:1,cleaning:1"
sms:
senderName: "HRBOX"
# --- Внешние интеграции ---------------------------------------------------
# --- Внешние интеграции -------------------------------------------------
integrations:
telegram:
# Прямой API Telegram; для установок без доступа к нему укажите прокси
# Если прямого доступа к Telegram нет, укажите адрес прокси
apiUrl: "https://api.telegram.org/bot"
google:
enabled: "false"
# Client ID публичный (уходит в браузер), secret — в Secret чарта
# clientId уходит в браузер, clientSecret попадает в Secret
clientId: "692956564858-ccapt35khk7tmadl17b3879ht2cropp6.apps.googleusercontent.com"
clientSecret: ""
glagol:
@@ -441,8 +415,8 @@ app:
apiUrl: "https://api.glagol.online/glagol-api"
# Привязка мобильных приложений к домену: без неё ссылки из писем и пушей
# открываются в браузере вместо приложения. По умолчанию — штатные
# приложения HRBox; меняйте, только если у вас собственная сборка.
# открываются в браузере, а не в приложении. По умолчанию указаны
# приложения HRBox, меняйте только для своей сборки.
mobileApps:
android:
- packageName: "io.hrbox.hrboxapp"
@@ -461,13 +435,13 @@ app:
clientSecret: ""
verificationToken: ""
# --- HRBox Hub ------------------------------------------------------------
# --- HRBox Hub ----------------------------------------------------------
# Лицензирование и общие сервисы. Токен выдают сотрудники HRBox.
hub:
url: "https://hub.hrbox.io"
token: ""
# --- AI -------------------------------------------------------------------
# --- AI -----------------------------------------------------------------
ai:
docsIndexEnabled: "true"
semanticIndexEnabled: "true"
@@ -476,82 +450,74 @@ app:
folderId: ""
apiKey: ""
# --- Трассировка ----------------------------------------------------------
# Включает OpenTelemetry в PHP и sidecar-коллектор в поде web.
# --- Трассировка --------------------------------------------------------
# Включает OpenTelemetry в PHP и коллектор рядом с подом web
tracing:
enabled: false
exporterUrl: "http://localhost:4318"
# Куда коллектор пересылает трейсы (Tempo, Jaeger)
# Куда коллектор отправляет трейсы: Tempo, Jaeger
tempoUrl: ""
# --- Безопасность ---------------------------------------------------------
# --- Безопасность -------------------------------------------------------
security:
# "true" разрешает встроенному iframe полный доступ к родительскому окну.
# Для production рекомендуется "false".
# "true" даёт встроенному iframe полный доступ к родительскому окну.
# Для production лучше "false".
iframeFullAccess: "false"
# ============================================================================
# Секреты приложения
# ============================================================================
# --- Секреты --------------------------------------------------------------
# Эти значения вместе с паролями из app.* складываются в один Kubernetes
# Secret, а контейнеры читают его через secretKeyRef.
# Secret, поды читают их оттуда.
secrets:
# Имя Secret, созданного вне чарта. Если задано — чарт свой Secret не
# создаёт и берёт все ключи из указанного. Список ключей — в README.
# Имя Secret, созданного вне чарта. Если задано, чарт свой Secret не
# создаёт и берёт ключи из указанного. Список ключей — в README.
existingSecret: ""
# Защита cookie от подделки, 32 символа.
# Генерация: openssl rand -hex 16
cookieValidationKey: ""
# Шифрование данных: пароли интеграций (S3, ADFS и другие).
# ВАЖНО: без этого ключа данные не расшифровать. Не теряйте его.
# Шифрование паролей интеграций в базе.
# Без этого ключа данные не расшифровать, храните копию отдельно.
# Генерация: openssl rand -base64 48
encryptionKey: ""
# Аутентификация вызовов между микросервисами релиза.
# Проверка вызовов между сервисами внутри релиза.
# Генерация: openssl rand -base64 48
servicesInternalSecret: ""
# ============================================================================
# Ingress
# ============================================================================
# --- Ingress --------------------------------------------------------------
ingress:
enabled: true
# Обычно "nginx" или "traefik"
className: "nginx"
# Домен инсталляции. Wildcard (*.hrbox.company.com) — для мультиарендности.
# Домен установки. Для мультиарендности можно wildcard: *.hrbox.company.com
host: "hrbox.example.com"
# Дополнительные аннотации ingress-контроллера
annotations: {}
# Максимальный размер загружаемого файла на общих путях.
# Загрузка оргструктуры из 1С идёт потоком и имеет собственный лимит.
# Максимальный размер загружаемого файла.
# У загрузки оргструктуры из 1С отдельный лимит, он выше.
maxBodySize: "10m"
tls:
enabled: true
# kubectl create secret tls hrbox-tls --cert=cert.pem --key=key.pem
# либо выпуск через cert-manager
# или выпустить сертификат через cert-manager
secretName: "hrbox-tls"
# ============================================================================
# Кластер
# ============================================================================
# --- Кластер --------------------------------------------------------------
cluster:
# Домен служебных имён Kubernetes. Меняйте, только если он нестандартный.
# Домен служебных имён Kubernetes. Менять только если он нестандартный.
domain: "cluster.local"
# ============================================================================
# Ресурсы
# ============================================================================
# Значения близки к продовым. Незаданные поля не рендерятся, поэтому
# компоненту можно оставить только requests.
# --- Ресурсы --------------------------------------------------------------
# Значения близки к тем, что используются в облаке HRBox.
# Можно оставить только requests: незаполненные поля в манифест не попадают.
resources:
# PHP-FPM: HTTP-запросы, API, веб-интерфейс
# PHP-FPM: веб-интерфейс и API
app:
requests:
cpu: "1"
@@ -562,7 +528,7 @@ resources:
memory: "2Gi"
ephemeralStorage: "2Gi"
# nginx в поде web: проксирует запросы в PHP-FPM
# nginx в поде web
nginx:
requests:
cpu: "0.1"
@@ -573,7 +539,7 @@ resources:
memory: "256Mi"
ephemeralStorage: "512Mi"
# Фоновые задачи: конвертация файлов, почта, отчёты
# Фоновые задачи: файлы, почта, отчёты
worker:
requests:
cpu: "0.6"
@@ -645,7 +611,7 @@ resources:
memory: "1Gi"
ephemeralStorage: "2Gi"
# Конвертация видео: тяжёлый профиль, отдельные ноды
# Конвертация видео: тяжёлые поды на отдельных нодах
fileProcessorVideo:
requests:
cpu: "4"
@@ -679,7 +645,7 @@ resources:
cpu: "1"
memory: "2Gi"
# Разовые задачи: миграции, обновление кластера
# Разовые задачи: миграции и обновление кластера
jobs:
requests:
cpu: "0.5"
@@ -690,31 +656,29 @@ resources:
memory: "4Gi"
ephemeralStorage: "256Mi"
# ============================================================================
# NATS JetStream
# ============================================================================
# --- NATS -----------------------------------------------------------------
nats:
# Обязателен для работы HRBox. Выключайте только если ставите NATS отдельно
# и указали его адрес в app.nats.url.
# HRBox без брокера не работает. Выключают, только если NATS ставят
# отдельно и указали его адрес в app.nats.url.
enabled: true
# Для отказоустойчивости — 3 или 5. Должно совпадать с cluster.replicas.
# Для отказоустойчивости 3 или 5. Должно совпадать с cluster.replicas.
replicas: 3
jetstream:
enabled: true
# Хранение в памяти — для небольших установок
# Хранение в памяти: подходит небольшим установкам
memStorage:
enabled: true
size: "2Gi"
# Хранение на диске — для production
# Хранение на диске: для production
fileStorage:
enabled: false
size: "4Gi"
storageDirectory: "/data"
# Для JetStream нужен быстрый диск
# JetStream нужен быстрый диск
storageClassName: "fast-ssd"
cluster:
@@ -722,17 +686,16 @@ nats:
# Должно совпадать с nats.replicas
replicas: 3
# Автоматическая перезагрузка конфигурации без рестарта сервера
# Подхватывает изменения конфигурации без перезапуска NATS
reloader:
enabled: true
terminationGracePeriodSeconds: 120
# ============================================================================
# Dragonfly (Redis-совместимый кеш)
# ============================================================================
# --- Dragonfly ------------------------------------------------------------
# Redis-совместимый кеш.
dragonfly:
# false — если Redis разворачивается отдельно; тогда укажите его в app.redis.
# false — если Redis ставят отдельно; тогда заполните app.redis.host
enabled: true
# Ограничение памяти. Раскомментируйте при необходимости.
# Ограничение памяти
# maxmemory: "256M"