Merge branch 'feature/DEVOPS-117' into 'main'

DEVOPS-117: чарт on-premise 3.0 — Nanabush Player, секреты в Secret, валидация values

See merge request hrbox-public/helm-chart!7
This commit is contained in:
2026-09-05 13:11:13 +00:00
35 changed files with 2722 additions and 1118 deletions
+11 -9
View File
@@ -1,19 +1,21 @@
apiVersion: v2
name: hrbox
description: Simplified HRBox application Helm chart
description: HRBox — HR-платформа для on-premise установки
type: application
# Версия самого чарта. Увеличивается при каждом изменении чарта
version: 2.0.0
version: 3.0.0
appVersion: "3.1.0"
# Версия приложения, которое деплоится. Не обязательно увеличивать при изменении чарта
appVersion: "3.0.0"
# Ключевые слова для поиска
keywords:
- hrbox
- hr
- application
- lms
- kedo
- on-premise
# Домашняя страница проекта
home: https://hrbox.io
annotations:
hrbox.io/components: >-
web, worker, conductor, chatbox, goworker, kedoca, geonames,
file-processor, nanabush-player, dragonfly, nats
+26
View File
@@ -0,0 +1,26 @@
{{- if not .Values.secrets.existingSecret }}
---
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: {{ include "hrbox.secretName" . | quote }}
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "app") | nindent 4 }}
stringData:
cookie-validation-key: {{ .Values.secrets.cookieValidationKey | quote }}
encryption-key: {{ .Values.secrets.encryptionKey | quote }}
services-internal-secret: {{ .Values.secrets.servicesInternalSecret | quote }}
postgres-password: {{ .Values.app.postgres.password | quote }}
s3-key: {{ .Values.app.s3.key | quote }}
s3-secret: {{ .Values.app.s3.secret | quote }}
smtp-password: {{ .Values.app.smtp.password | quote }}
hub-token: {{ .Values.app.hub.token | quote }}
chatbox-wrapping-key: {{ .Values.app.chatbox.wrappingKey | quote }}
livekit-api-secret: {{ .Values.app.livekit.apiSecret | quote }}
yandex-client-password: {{ .Values.app.yandex.clientPassword | quote }}
yandex-ai-api-key: {{ .Values.app.ai.yandex.apiKey | quote }}
google-client-secret: {{ .Values.app.integrations.google.clientSecret | quote }}
slack-client-secret: {{ .Values.app.slack.clientSecret | quote }}
slack-verification-token: {{ .Values.app.slack.verificationToken | quote }}
{{- end }}
@@ -0,0 +1,17 @@
{{- $cfg := .Values.app.nanabushPlayer.deployment }}
{{- if and $cfg.enabled $cfg.existingSecret.create }}
---
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: {{ include "nanabushPlayer.secretName" . | quote }}
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "nanabush-player") | nindent 4 }}
stringData:
{{ required "Нужно имя ключа ticketHs256 в секрете Nanabush Player" $cfg.existingSecret.keys.ticketHs256 }}: {{ .Values.app.nanabushPlayer.keys.ticketHs256 | quote }}
{{ required "Нужно имя ключа sessionHs256 в секрете Nanabush Player" $cfg.existingSecret.keys.sessionHs256 }}: {{ .Values.app.nanabushPlayer.keys.sessionHs256 | quote }}
{{- with $cfg.existingSecret.keys.ticketKeys }}
{{ . }}: {{ $.Values.app.nanabushPlayer.keys.ticketKeys | quote }}
{{- end }}
{{- end }}
+132
View File
@@ -0,0 +1,132 @@
{{- if .Values.app.image }}
{{- fail "app.image переехал в image.hrbox (см. CHANGELOG, миграция 2.x -> 3.0)" }}
{{- end }}
{{- if .Values.app.chatbox.image }}
{{- fail "app.chatbox.image переехал в image.chatbox" }}
{{- end }}
{{- if .Values.app.goworker.image }}
{{- fail "app.goworker.image переехал в image.goworker" }}
{{- end }}
{{- if .Values.app.kedoca.image }}
{{- fail "app.kedoca.image переехал в image.kedoca" }}
{{- end }}
{{- if .Values.app.file_processor }}
{{- fail "app.file_processor переименован в app.fileProcessor, а образ переехал в image.fileProcessor" }}
{{- end }}
{{- if .Values.app.custom_policy }}
{{- fail "app.custom_policy переименован в app.customPolicy" }}
{{- end }}
{{- if .Values.app.replicas.websocket }}
{{- fail "app.replicas.websocket переименован в app.replicas.chatbox ещё в версии 2.0" }}
{{- end }}
{{- if .Values.resources.websocket }}
{{- fail "resources.websocket переименован в resources.chatbox ещё в версии 2.0" }}
{{- end }}
{{- if .Values.nats.image }}
{{- fail "nats.image переехал в image.nats" }}
{{- end }}
{{- if not .Values.ingress.host }}
{{- fail "Заполните ingress.host — домен, по которому открывается HRBox" }}
{{- end }}
{{- if not .Values.app.defaultTenantHostname }}
{{- fail "Заполните app.defaultTenantHostname (обычно совпадает с ingress.host)" }}
{{- end }}
{{- if not .Values.app.postgres.password }}
{{- fail "Заполните app.postgres.password" }}
{{- end }}
{{- if not .Values.app.s3.key }}
{{- fail "Заполните app.s3.key — без хранилища файлов HRBox не работает" }}
{{- end }}
{{- if not .Values.app.s3.secret }}
{{- fail "Заполните app.s3.secret" }}
{{- end }}
{{- if not .Values.app.hub.token }}
{{- fail "Заполните app.hub.token — токен выдают сотрудники HRBox" }}
{{- end }}
{{- if not .Values.secrets.existingSecret }}
{{- if not .Values.secrets.cookieValidationKey }}
{{- fail "Заполните secrets.cookieValidationKey (openssl rand -hex 16)" }}
{{- end }}
{{- if not .Values.secrets.encryptionKey }}
{{- fail "Заполните secrets.encryptionKey (openssl rand -base64 48). Потеря ключа делает зашифрованные данные нечитаемыми" }}
{{- end }}
{{- if not .Values.secrets.servicesInternalSecret }}
{{- fail "Заполните secrets.servicesInternalSecret (openssl rand -base64 48)" }}
{{- end }}
{{- if not .Values.app.chatbox.wrappingKey }}
{{- fail "Заполните app.chatbox.wrappingKey (openssl rand -hex 16)" }}
{{- end }}
{{- end }}
{{- if .Values.ingress.tls.enabled }}
{{- if not .Values.ingress.tls.secretName }}
{{- fail "При ingress.tls.enabled нужен ingress.tls.secretName с TLS-сертификатом" }}
{{- end }}
{{- end }}
{{- if and (not .Values.dragonfly.enabled) (not .Values.app.redis.host) }}
{{- fail "dragonfly.enabled: false требует app.redis.host — иначе приложение обратится к сервису, которого нет в релизе" }}
{{- end }}
{{- if and (not .Values.nats.enabled) (not .Values.app.nats.url) }}
{{- fail "nats.enabled: false требует app.nats.url — иначе приложение обратится к брокеру, которого нет в релизе" }}
{{- end }}
{{- if and .Values.nats.enabled .Values.nats.cluster.enabled }}
{{- if ne (.Values.nats.replicas | int) (.Values.nats.cluster.replicas | int) }}
{{- fail (printf "nats.replicas (%d) и nats.cluster.replicas (%d) должны совпадать: иначе часть нод не войдёт в кластер, а реплики JetStream-стримов будут посчитаны неверно" (.Values.nats.replicas | int) (.Values.nats.cluster.replicas | int)) }}
{{- end }}
{{- end }}
{{- $player := .Values.app.nanabushPlayer }}
{{- if and $player.enabled (not $player.deployment.enabled) }}
{{- fail "app.nanabushPlayer.enabled требует deployment.enabled: HRBox нельзя переключить на плеер, которого нет в кластере" }}
{{- end }}
{{- if and $player.enabled (not $player.ingress.enabled) }}
{{- fail "app.nanabushPlayer.enabled требует ingress.enabled: иначе HRBox выдаст ссылку на недоступный адрес" }}
{{- end }}
{{- if and $player.ingress.enabled (not $player.deployment.enabled) }}
{{- fail "app.nanabushPlayer.ingress.enabled требует deployment.enabled: маршрут вёл бы в несуществующий Service" }}
{{- end }}
{{- if and $player.ingress.enabled (not .Values.ingress.enabled) }}
{{- fail "Путь плеера публикуется на основном Ingress, поэтому нужен ingress.enabled: true" }}
{{- end }}
{{- if $player.deployment.enabled }}
{{- $basePath := $player.basePath | default "/nanabush-player" }}
{{- if eq (trimSuffix "/" $basePath) "" }}
{{- fail "app.nanabushPlayer.basePath не может быть корнем: корень домена занят самим HRBox" }}
{{- end }}
{{- if not (hasPrefix "/" $basePath) }}
{{- fail "app.nanabushPlayer.basePath должен начинаться со слэша, например /nanabush-player" }}
{{- end }}
{{- if not (include "hrbox.imageTag" .Values.image.nanabushPlayer) }}
{{- fail "image.nanabushPlayer должен содержать явный тег версии, например nanabush-player:1.0.11 — из него выводится версия плеера в launch-тикете" }}
{{- end }}
{{- if not $player.deployment.binaryCapabilities.basePath }}
{{- fail "On-premise монтирует плеер под путём, поэтому нужен образ с поддержкой base path. Проверьте сборку и верните deployment.binaryCapabilities.basePath: true" }}
{{- end }}
{{- if not $player.deployment.existingSecret.name }}
{{- fail "Заполните app.nanabushPlayer.deployment.existingSecret.name" }}
{{- end }}
{{- if $player.deployment.existingSecret.create }}
{{- if not $player.keys.ticketHs256 }}
{{- fail "Заполните app.nanabushPlayer.keys.ticketHs256 (openssl rand -base64 48)" }}
{{- end }}
{{- if not $player.keys.sessionHs256 }}
{{- fail "Заполните app.nanabushPlayer.keys.sessionHs256 (openssl rand -base64 48)" }}
{{- end }}
{{- if eq $player.keys.ticketHs256 $player.keys.sessionHs256 }}
{{- fail "app.nanabushPlayer.keys.ticketHs256 и sessionHs256 должны отличаться: это ключи разных контуров" }}
{{- end }}
{{- if and $player.deployment.existingSecret.keys.ticketKeys (not $player.keys.ticketKeys) }}
{{- fail "Указано имя ключа ticketKeys в секрете, но само кольцо app.nanabushPlayer.keys.ticketKeys пустое" }}
{{- end }}
{{- end }}
{{- if and $player.deployment.redis.urlFromExistingSecret $player.deployment.existingSecret.create }}
{{- fail "redis.urlFromExistingSecret: true требует existingSecret.create: false — Secret, создаваемый чартом, не содержит URL Redis" }}
{{- end }}
{{- if $player.deployment.metrics.enabled }}
{{- if eq ($player.deployment.metrics.port | int) ($player.service.port | int) }}
{{- fail "Порт метрик плеера должен отличаться от публичного порта сервиса, иначе метрики окажутся доступны содержимому курсов" }}
{{- end }}
{{- end }}
{{- end }}
+9 -5
View File
@@ -1,11 +1,14 @@
{{- if .Values.nats.enabled }}
{{- $name := include "hrbox.component" (dict "ctx" . "name" "nats") -}}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Chart.Name }}-nats-config
name: {{ printf "%s-config" $name }}
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "nats") | nindent 4 }}
data:
nats.conf: |
# PID file shared with configuration reloader.
pid_file: "/var/run/nats/nats.pid"
###############
@@ -42,11 +45,11 @@ data:
###################################
cluster {
port: 6222
name: {{ .Chart.Name }}-nats-cluster
name: {{ printf "%s-cluster" $name }}
routes = [
{{- range $i := until (int .Values.nats.cluster.replicas) }}
nats://{{ $.Chart.Name }}-nats-{{ $i }}.{{ $.Chart.Name }}-nats.{{ $.Release.Namespace }}.svc.cluster.local:6222,
nats://{{ $name }}-{{ $i }}.{{ $name }}.{{ $.Release.Namespace }}.svc.{{ $.Values.cluster.domain }}:6222,
{{- end }}
]
@@ -55,4 +58,5 @@ data:
}
{{- end }}
lame_duck_duration: 120s
lame_duck_duration: {{ .Values.nats.terminationGracePeriodSeconds }}s
{{- end }}
+38
View File
@@ -0,0 +1,38 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: assetlinks
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "web") | nindent 4 }}
data:
assetlinks.json: |
[
{{- range $i, $app := .Values.app.mobileApps.android }}
{{- if $i }},{{ end }}
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": {{ $app.packageName | quote }},
"sha256_cert_fingerprints": {{ toJson $app.sha256CertFingerprints }}
}
}
{{- end }}
]
apple-app-site-association: |
{
"applinks": {
"details": [
{
"appIDs": {{ toJson .Values.app.mobileApps.apple.appIds }},
"components": [
{
"/": "/*"
}
]
}
]
}
}
+78 -7
View File
@@ -3,6 +3,8 @@ apiVersion: v1
kind: ConfigMap
metadata:
name: web-configuration
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "web") | nindent 4 }}
data:
nginx.conf: |
user www-data;
@@ -43,7 +45,6 @@ data:
client_header_buffer_size 1024k;
large_client_header_buffers 16 1024k;
default_type application/octet-stream;
include /etc/nginx/mime.types;
@@ -51,7 +52,7 @@ data:
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
@@ -62,7 +63,6 @@ data:
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
rewrite_log on;
##
# Gzip Settings
@@ -97,6 +97,72 @@ data:
deny all;
}
location = /healthz {
access_log off;
return 200 "ok\n";
}
location = /health-check {
fastcgi_pass 127.0.0.1:9000;
fastcgi_connect_timeout 1s;
fastcgi_read_timeout 3s;
fastcgi_send_timeout 1s;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
fastcgi_param SCRIPT_NAME /index.php;
fastcgi_param PATH_INFO /health-check;
}
location = /api/v1/integration/commit {
client_max_body_size 500m;
fastcgi_request_buffering off;
fastcgi_pass localhost:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
fastcgi_param SCRIPT_NAME /index.php;
include fastcgi_params;
fastcgi_read_timeout 300s;
fastcgi_send_timeout 300s;
fastcgi_connect_timeout 60s;
}
location /.well-known/assetlinks.json {
default_type "application/json";
alias /var/www/assetlinks/assetlinks.json;
}
location /.well-known/apple-app-site-association {
default_type "application/json";
alias /var/www/assetlinks/apple-app-site-association;
}
location = /frontend/sw.js {
add_header Service-Worker-Allowed /;
add_header Cache-Control "no-cache, must-revalidate, public";
expires off;
try_files $uri $uri/ /index.php?$args;
access_log off;
}
location ^~ /ai/ {
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
add_header X-XSS-Protection "1; mode=block";
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header Permissions-Policy "browsing-topics=()" always;
fastcgi_pass localhost:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
fastcgi_param SCRIPT_NAME /index.php;
include fastcgi_params;
fastcgi_read_timeout 300s;
fastcgi_connect_timeout 60s;
fastcgi_send_timeout 60s;
}
location / {
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
@@ -105,11 +171,8 @@ data:
add_header X-Content-Type-Options "nosniff";
add_header Permissions-Policy "browsing-topics=()" always;
# Redirect everything that isn't a real file to index.php
try_files $uri $uri/ /index.php?$args;
# uncomment to avoid processing of calls to non-existing static files by Yii
#location ~* \.(js|css|png|jpg|gif|ico|pdf|mov|fla|zip|rar)$ {
location ~* \.(js|css|png|jpg|gif|ico)$ {
expires 30d;
@@ -127,7 +190,15 @@ data:
open_file_cache_errors off;
}
#error_page 404 /404.html;
location ~* \.(woff|woff2|ttf|otf|eot)$ {
expires 30d;
add_header Vary Accept-Encoding;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
access_log off;
try_files $uri $uri/ /index.php?$args;
}
}
location ~ \.php$ {
+34
View File
@@ -0,0 +1,34 @@
{{- if .Values.app.tracing.enabled }}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: otel-config
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "web") | nindent 4 }}
data:
config.yml: |
receivers:
otlp:
protocols:
grpc:
endpoint: "0.0.0.0:4317"
http:
endpoint: "0.0.0.0:4318"
processors:
batch:
exporters:
otlp:
endpoint: {{ required "При app.tracing.enabled нужен app.tracing.tempoUrl — адрес приёмника трейсов" .Values.app.tracing.tempoUrl | quote }}
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlp]
{{- end }}
+4 -2
View File
@@ -3,10 +3,12 @@ apiVersion: v1
kind: ConfigMap
metadata:
name: job-scripts
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "jobs") | nindent 4 }}
data:
wait.sh: |
until pg_isready -h $POSTGRES_VHOST -p $POSTGRES_PORT -U $POSTGRES_USER
until pg_isready -h "$POSTGRES_VHOST" -p "$POSTGRES_PORT" -U "$POSTGRES_USER"
do
echo "Waiting for postgres"
sleep 2;
sleep 2
done
+26 -11
View File
@@ -1,46 +1,61 @@
{{- if .Values.dragonfly.enabled }}
{{- $name := include "hrbox.component" (dict "ctx" . "name" "dragonfly") -}}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name }}-dragonfly
name: {{ $name }}
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "dragonfly") | nindent 4 }}
spec:
revisionHistoryLimit: 3
strategy:
type: Recreate
selector:
matchLabels:
component: {{ .Chart.Name }}-dragonfly
component: {{ $name }}
template:
metadata:
labels:
component: {{ .Chart.Name }}-dragonfly
component: {{ $name }}
app: dragonfly
spec:
imagePullSecrets:
- name: regsecret
{{- include "hrbox.imagePullSecrets" . | nindent 6 }}
containers:
- name: dragonfly
image: cr.yandex/crpnn6fi85p3dauha6nc/dragonflydb:1.15.1
{{- if .Values.dragonfly.maxmemory }}
image: {{ .Values.image.dragonfly | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
{{- with .Values.dragonfly.maxmemory }}
args:
- --maxmemory={{ .Values.dragonfly.maxmemory }}
- --maxmemory={{ . }}
{{- end }}
ports:
- name: dragonfly
containerPort: 6379
{{- include "hrbox.resources" (dict "ctx" . "component" "dragonfly") | indent 10 }}
readinessProbe:
tcpSocket:
port: 6379
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 6379
initialDelaySeconds: 30
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}-dragonfly
name: {{ $name }}
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "dragonfly") | nindent 4 }}
spec:
clusterIP: None
selector:
component: {{ .Chart.Name }}-dragonfly
component: {{ $name }}
ports:
- port: 6379
- name: dragonfly
port: 6379
{{- end }}
+22 -21
View File
@@ -1,18 +1,19 @@
{{- if .Values.nats.enabled }}
{{- $name := include "hrbox.component" (dict "ctx" . "name" "nats") -}}
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ .Chart.Name }}-nats
name: {{ $name }}
labels:
app.kubernetes.io/name: nats
app.kubernetes.io/instance: {{ .Release.Name }}
{{- include "hrbox.labels" (dict "ctx" . "name" "nats") | nindent 4 }}
spec:
selector:
matchLabels:
app.kubernetes.io/name: nats
app.kubernetes.io/instance: {{ .Release.Name }}
replicas: {{ .Values.nats.replicas }}
serviceName: {{ .Chart.Name }}-nats
serviceName: {{ $name }}
template:
metadata:
labels:
@@ -22,17 +23,16 @@ spec:
volumes:
- name: config-volume
configMap:
name: {{ .Chart.Name }}-nats-config
name: {{ printf "%s-config" $name }}
- name: pid
emptyDir: {}
shareProcessNamespace: true
terminationGracePeriodSeconds: {{ .Values.nats.terminationGracePeriodSeconds }}
imagePullSecrets:
- name: regsecret
{{- include "hrbox.imagePullSecrets" . | nindent 6 }}
containers:
- name: nats
image: {{ .Values.nats.image }}
imagePullPolicy: IfNotPresent
image: {{ .Values.image.nats | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: 4222
name: client
@@ -58,14 +58,15 @@ spec:
fieldRef:
fieldPath: metadata.namespace
- name: CLUSTER_ADVERTISE
value: $(POD_NAME).{{ .Chart.Name }}-nats.$(POD_NAMESPACE).{{ .Values.cluster.domain | default "cluster.local" }}
value: $(POD_NAME).{{ $name }}.$(POD_NAMESPACE).svc.{{ .Values.cluster.domain }}
{{- include "hrbox.resources" (dict "ctx" . "component" "nats") | indent 10 }}
volumeMounts:
- name: config-volume
mountPath: /etc/nats-config
- name: pid
mountPath: /var/run/nats
{{- if .Values.nats.jetstream.fileStorage.enabled }}
- name: {{ .Chart.Name }}-nats-js-pvc
- name: {{ printf "%s-js-pvc" $name }}
mountPath: {{ .Values.nats.jetstream.fileStorage.storageDirectory }}
{{- end }}
livenessProbe:
@@ -86,12 +87,12 @@ spec:
command:
- "/bin/sh"
- "-c"
- "nats-server -sl=ldm=/var/run/nats/nats.pid && /bin/sleep 120"
- "nats-server -sl=ldm=/var/run/nats/nats.pid && /bin/sleep {{ .Values.nats.terminationGracePeriodSeconds }}"
{{- if .Values.nats.reloader.enabled }}
- name: reloader
image: natsio/nats-server-config-reloader:0.6.2
imagePullPolicy: IfNotPresent
image: {{ .Values.image.natsConfigReloader | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
command:
- "nats-server-config-reloader"
- "-pid"
@@ -108,15 +109,15 @@ spec:
{{- if .Values.nats.jetstream.fileStorage.enabled }}
volumeClaimTemplates:
- metadata:
name: {{ .Chart.Name }}-nats-js-pvc
name: {{ printf "%s-js-pvc" $name }}
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: {{ .Values.nats.jetstream.fileStorage.size }}
{{- if .Values.nats.jetstream.fileStorage.storageClassName }}
storageClassName: {{ .Values.nats.jetstream.fileStorage.storageClassName }}
{{- with .Values.nats.jetstream.fileStorage.storageClassName }}
storageClassName: {{ . | quote }}
{{- end }}
{{- end }}
@@ -124,15 +125,14 @@ spec:
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}-nats
name: {{ $name }}
labels:
app.kubernetes.io/name: nats
app.kubernetes.io/instance: {{ .Release.Name }}
{{- include "hrbox.labels" (dict "ctx" . "name" "nats") | nindent 4 }}
spec:
clusterIP: None
selector:
app.kubernetes.io/name: nats
app.kubernetes.io/instance: {{ .Release.Name }}
clusterIP: None
ports:
- name: client
port: 4222
@@ -142,3 +142,4 @@ spec:
port: 8222
- name: metrics
port: 7777
{{- end }}
+15
View File
@@ -0,0 +1,15 @@
{{- $replicas := .Values.app.replicas.web | int }}
{{- if gt $replicas 1 }}
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ include "hrbox.component" (dict "ctx" . "name" "web") }}-pdb
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "web") | nindent 4 }}
spec:
minAvailable: 75%
selector:
matchLabels:
{{- include "hrbox.selectorLabels" (dict "ctx" . "name" "web") | nindent 6 }}
{{- end }}
+83 -16
View File
@@ -1,48 +1,59 @@
{{- $name := include "hrbox.component" (dict "ctx" . "name" "web") -}}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name }}-web
name: {{ $name }}
labels:
app: web
{{- include "hrbox.labels" (dict "ctx" . "name" "web") | nindent 4 }}
spec:
minReadySeconds: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
maxUnavailable: 0
replicas: {{ .Values.app.replicas.web }}
selector:
matchLabels:
service: {{ .Chart.Name }}-web
{{- include "hrbox.selectorLabels" (dict "ctx" . "name" "web") | nindent 6 }}
template:
metadata:
labels:
service: {{ .Chart.Name }}-web
{{- include "hrbox.selectorLabels" (dict "ctx" . "name" "web") | nindent 8 }}
app: web
annotations:
prometheus.io/scrape: 'true'
prometheus.io/port: '9253'
checksum/nginx-config: {{ include (print $.Template.BasePath "/01-nginx-configmap.yaml") . | sha256sum }}
checksum/app-secret: {{ include (print $.Template.BasePath "/00-app-secret.yaml") . | sha256sum }}
spec:
imagePullSecrets:
- name: regsecret
terminationGracePeriodSeconds: 30
{{- include "hrbox.imagePullSecrets" . | nindent 6 }}
containers:
- name: fpm
image: {{ .Values.app.image }}
image: {{ .Values.image.hrbox | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
command: ["/usr/local/sbin/php-fpm", "-c", "/usr/local/etc/php-fpm"]
ports:
- name: fpm
containerPort: 9000
env:
{{- include "envs_all" . | indent 12 }}
{{- include "app_resources" . | indent 10 }}
{{- include "envs_nanabush_player_signing" . | indent 12 }}
- name: VIDEO_CONVERTER_ENABLED
value: {{ .Values.app.videoConverter.enabled | quote }}
{{- if .Values.app.tracing.enabled }}
{{- include "envs_tracing" . | indent 12 }}
{{- end }}
{{- include "hrbox.resources" (dict "ctx" . "component" "app") | indent 10 }}
lifecycle:
preStop:
exec:
command:
- sh
- '-c'
- sleep 5 && kill -SIGQUIT 1
- sleep 15 && kill -SIGQUIT 1
readinessProbe:
tcpSocket:
port: 9000
@@ -53,47 +64,103 @@ spec:
port: 9000
initialDelaySeconds: 5
periodSeconds: 10
- name: nginx
image: {{ .Values.app.image }}
image: {{ .Values.image.hrbox | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
command: ["/usr/sbin/nginx", "-g", "daemon off;"]
ports:
- name: http
containerPort: 80
env:
{{- include "envs_all" . | indent 12 }}
{{- include "nginx_resources" . | indent 10}}
{{- include "hrbox.resources" (dict "ctx" . "component" "nginx") | indent 10 }}
lifecycle:
preStop:
exec:
command:
- sh
- '-c'
- sleep 5 && /usr/sbin/nginx -s quit
- sleep 15 && /usr/sbin/nginx -s quit
livenessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 10
failureThreshold: 5
readinessProbe:
httpGet:
path: /health-check
port: 80
initialDelaySeconds: 15
periodSeconds: 5
timeoutSeconds: 10
failureThreshold: 3
volumeMounts:
- name: configs
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
- name: assetlinks
mountPath: /var/www/assetlinks/assetlinks.json
subPath: assetlinks.json
- name: assetlinks
mountPath: /var/www/assetlinks/apple-app-site-association
subPath: apple-app-site-association
{{- if .Values.app.tracing.enabled }}
- name: otel-collector
image: {{ .Values.image.otelCollector | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
args: ["--config=/etc/otel-config/otel-collector-config.yaml"]
ports:
- containerPort: 4317
name: otlp-grpc
- containerPort: 4318
name: otlp-http
volumeMounts:
- name: otel-config
mountPath: /etc/otel-config/otel-collector-config.yaml
subPath: config.yml
{{- end }}
- name: phpfpm-exporter
image: cr.yandex/crpnn6fi85p3dauha6nc/php-fpm_exporter:latest
image: {{ .Values.image.phpFpmExporter | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: 9253
name: metrics
env:
- name: PHP_FPM_SCRAPE_URI
value: "tcp://127.0.0.1:9000/hrbox-fpm-status"
volumes:
- name: configs
configMap:
name: web-configuration
defaultMode: 420
- name: assetlinks
configMap:
name: assetlinks
defaultMode: 420
{{- if .Values.app.tracing.enabled }}
- name: otel-config
configMap:
name: otel-config
defaultMode: 420
{{- end }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}-web
name: {{ $name }}
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "web") | nindent 4 }}
spec:
selector:
service: {{ .Chart.Name }}-web
{{- include "hrbox.selectorLabels" (dict "ctx" . "name" "web") | nindent 4 }}
ports:
- name: http
port: 80
+57
View File
@@ -0,0 +1,57 @@
{{- $name := include "hrbox.component" (dict "ctx" . "name" "chatbox") -}}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ $name }}
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "chatbox") | nindent 4 }}
spec:
minReadySeconds: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
replicas: {{ .Values.app.replicas.chatbox }}
selector:
matchLabels:
{{- include "hrbox.selectorLabels" (dict "ctx" . "name" "chatbox") | nindent 6 }}
template:
metadata:
labels:
{{- include "hrbox.selectorLabels" (dict "ctx" . "name" "chatbox") | nindent 8 }}
app: chatbox
annotations:
prometheus.io/scrape: 'true'
prometheus.io/port: '9797'
prometheus.io/path: "/info"
checksum/app-secret: {{ include (print $.Template.BasePath "/00-app-secret.yaml") . | sha256sum }}
spec:
{{- include "hrbox.imagePullSecrets" . | nindent 6 }}
containers:
- name: chatbox
image: {{ .Values.image.chatbox | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 9797
env:
{{- include "envs_all" . | indent 12 }}
{{- include "hrbox.resources" (dict "ctx" . "component" "chatbox") | indent 10 }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ $name }}
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "chatbox") | nindent 4 }}
spec:
selector:
{{- include "hrbox.selectorLabels" (dict "ctx" . "name" "chatbox") | nindent 4 }}
ports:
- name: http
port: 80
targetPort: 9797
protocol: TCP
-51
View File
@@ -1,51 +0,0 @@
---
# Chatbox (WebSocket server) - обеспечивает real-time функционал
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name }}-chatbox
labels:
app: chatbox
spec:
minReadySeconds: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
replicas: {{ .Values.app.replicas.chatbox }}
selector:
matchLabels:
service: {{ .Chart.Name }}-chatbox
template:
metadata:
labels:
service: {{ .Chart.Name }}-chatbox
annotations:
prometheus.io/scrape: 'true'
prometheus.io/port: '9797'
prometheus.io/path: "/info"
spec:
imagePullSecrets:
- name: regsecret
containers:
- name: chatbox
image: {{ .Values.app.chatbox.image }}
imagePullPolicy: Always
env:
{{- include "envs_all" . | indent 12 }}
{{- include "chatbox_resources" . | indent 10 }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}-chatbox
spec:
selector:
service: {{ .Chart.Name }}-chatbox
ports:
- name: http
port: 80
targetPort: 9797
protocol: TCP
+13 -8
View File
@@ -1,10 +1,11 @@
{{- $name := include "hrbox.component" (dict "ctx" . "name" "worker") -}}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name }}-worker
name: {{ $name }}
labels:
app: worker
{{- include "hrbox.labels" (dict "ctx" . "name" "worker") | nindent 4 }}
spec:
minReadySeconds: 5
strategy:
@@ -12,22 +13,26 @@ spec:
replicas: {{ .Values.app.replicas.worker }}
selector:
matchLabels:
service: {{ .Chart.Name }}-worker
{{- include "hrbox.selectorLabels" (dict "ctx" . "name" "worker") | nindent 6 }}
template:
metadata:
labels:
service: {{ .Chart.Name }}-worker
{{- include "hrbox.selectorLabels" (dict "ctx" . "name" "worker") | nindent 8 }}
app: worker
annotations:
prometheus.io/scrape: 'true'
prometheus.io/port: '9253'
checksum/app-secret: {{ include (print $.Template.BasePath "/00-app-secret.yaml") . | sha256sum }}
spec:
imagePullSecrets:
- name: regsecret
{{- include "hrbox.imagePullSecrets" . | nindent 6 }}
containers:
- name: worker
image: {{ .Values.app.image }}
image: {{ .Values.image.hrbox | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
command: ["hrbox-worker"]
env:
{{- include "envs_all" . | indent 12 }}
{{- include "worker_resources" . | indent 10}}
- name: VIDEO_CONVERTER_ENABLED
value: {{ .Values.app.videoConverter.enabled | quote }}
{{- include "hrbox.resources" (dict "ctx" . "component" "worker") | indent 10 }}
terminationGracePeriodSeconds: 600
+41 -9
View File
@@ -1,21 +1,28 @@
{{- $name := include "hrbox.component" (dict "ctx" . "name" "conductor") -}}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name }}-conductor
name: {{ $name }}
labels:
app: conductor
{{- include "hrbox.labels" (dict "ctx" . "name" "conductor") | nindent 4 }}
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
service: {{ .Chart.Name }}-conductor
{{- include "hrbox.selectorLabels" (dict "ctx" . "name" "conductor") | nindent 6 }}
template:
metadata:
labels:
service: {{ .Chart.Name }}-conductor
{{- include "hrbox.selectorLabels" (dict "ctx" . "name" "conductor") | nindent 8 }}
app: conductor
annotations:
prometheus.io/scrape: 'true'
prometheus.io/port: '8080'
prometheus.io/path: '/metrics'
checksum/app-secret: {{ include (print $.Template.BasePath "/00-app-secret.yaml") . | sha256sum }}
spec:
affinity:
podAntiAffinity:
@@ -29,12 +36,37 @@ spec:
values:
- conductor
topologyKey: kubernetes.io/hostname
imagePullSecrets:
- name: regsecret
{{- include "hrbox.imagePullSecrets" . | nindent 6 }}
containers:
- name: conductor
{{- include "conductor_resources" . | indent 10 }}
image: cr.yandex/crphnph9c2s776j32sjd/hrbox-conductor:2.2.0
imagePullPolicy: IfNotPresent
image: {{ .Values.image.conductor | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 8080
env:
{{- include "envs_all" . | indent 12 }}
{{- include "hrbox.resources" (dict "ctx" . "component" "conductor") | indent 10 }}
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- "sleep 5"
@@ -0,0 +1,49 @@
{{- if .Values.app.videoConverter.enabled }}
{{- $name := include "hrbox.component" (dict "ctx" . "name" "file-processor-video") -}}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ $name }}
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "file-processor-video") | nindent 4 }}
spec:
minReadySeconds: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
replicas: {{ .Values.app.replicas.fileProcessorVideo }}
selector:
matchLabels:
{{- include "hrbox.selectorLabels" (dict "ctx" . "name" "file-processor-video") | nindent 6 }}
template:
metadata:
labels:
{{- include "hrbox.selectorLabels" (dict "ctx" . "name" "file-processor-video") | nindent 8 }}
app: file-processor
annotations:
checksum/app-secret: {{ include (print $.Template.BasePath "/00-app-secret.yaml") . | sha256sum }}
spec:
{{- with .Values.app.videoConverter.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.app.videoConverter.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- include "hrbox.imagePullSecrets" . | nindent 6 }}
containers:
- name: file-processor
image: {{ .Values.image.fileProcessor | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
{{- include "envs_all" . | indent 12 }}
- name: VIDEO_CONVERTER_ENABLED
value: "true"
- name: IMAGE_RESIZE_MAX_PARALLEL
value: {{ .Values.app.fileProcessor.imageResizeMaxParallel | quote }}
{{- include "hrbox.resources" (dict "ctx" . "component" "fileProcessorVideo") | indent 10 }}
{{- end }}
+12 -9
View File
@@ -1,11 +1,11 @@
{{- $name := include "hrbox.component" (dict "ctx" . "name" "file-processor") -}}
---
{{ $name := printf "%s-file-processor" (.Chart.Name) }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ $name }}
labels:
app: file-processor
{{- include "hrbox.labels" (dict "ctx" . "name" "file-processor") | nindent 4 }}
spec:
minReadySeconds: 5
strategy:
@@ -13,22 +13,25 @@ spec:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
replicas: {{ .Values.app.file_processor.replicas }}
replicas: {{ .Values.app.replicas.fileProcessor }}
selector:
matchLabels:
service: {{ $name }}
{{- include "hrbox.selectorLabels" (dict "ctx" . "name" "file-processor") | nindent 6 }}
template:
metadata:
labels:
service: {{ $name }}
{{- include "hrbox.selectorLabels" (dict "ctx" . "name" "file-processor") | nindent 8 }}
app: file-processor
annotations:
checksum/app-secret: {{ include (print $.Template.BasePath "/00-app-secret.yaml") . | sha256sum }}
spec:
imagePullSecrets:
- name: regsecret
{{- include "hrbox.imagePullSecrets" . | nindent 6 }}
containers:
- name: file-processor
image: {{ .Values.app.file_processor.image }}
imagePullPolicy: Always
image: {{ .Values.image.fileProcessor | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
{{- include "envs_all" . | indent 12 }}
- name: VIDEO_CONVERTER_ENABLED
value: "false"
{{- include "hrbox.resources" (dict "ctx" . "component" "fileProcessor") | indent 10 }}
+57
View File
@@ -0,0 +1,57 @@
{{- $name := include "hrbox.component" (dict "ctx" . "name" "geonames") -}}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ $name }}
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "geonames") | nindent 4 }}
spec:
minReadySeconds: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
replicas: {{ .Values.app.replicas.geonames }}
selector:
matchLabels:
{{- include "hrbox.selectorLabels" (dict "ctx" . "name" "geonames") | nindent 6 }}
template:
metadata:
labels:
{{- include "hrbox.selectorLabels" (dict "ctx" . "name" "geonames") | nindent 8 }}
app: geonames
annotations:
prometheus.io/scrape: 'true'
prometheus.io/port: '9797'
prometheus.io/path: "/info"
checksum/app-secret: {{ include (print $.Template.BasePath "/00-app-secret.yaml") . | sha256sum }}
spec:
{{- include "hrbox.imagePullSecrets" . | nindent 6 }}
containers:
- name: geonames
image: {{ .Values.image.geonames | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 8181
env:
{{- include "envs_all" . | indent 12 }}
{{- include "hrbox.resources" (dict "ctx" . "component" "geonames") | indent 10 }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ $name }}
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "geonames") | nindent 4 }}
spec:
selector:
{{- include "hrbox.selectorLabels" (dict "ctx" . "name" "geonames") | nindent 4 }}
ports:
- name: http
port: 8181
targetPort: 8181
protocol: TCP
-50
View File
@@ -1,50 +0,0 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name }}-geonames
labels:
app: geonames
spec:
minReadySeconds: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
replicas: {{ .Values.app.replicas.geonames }}
selector:
matchLabels:
service: {{ .Chart.Name }}-geonames
template:
metadata:
labels:
service: {{ .Chart.Name }}-geonames
annotations:
prometheus.io/scrape: 'true'
prometheus.io/port: '9797'
prometheus.io/path: "/info"
spec:
imagePullSecrets:
- name: regsecret
containers:
- name: geonames
{{- include "geonames_resources" . | indent 10}}
image: cr.yandex/crphnph9c2s776j32sjd/hrbox-geonames:3.1
imagePullPolicy: Always
env:
{{- include "envs_all" . | indent 12 }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}-geonames
spec:
selector:
service: {{ .Chart.Name }}-geonames
ports:
- name: http
port: 8181
targetPort: 8181
protocol: TCP
+20 -12
View File
@@ -1,10 +1,11 @@
{{- $name := include "hrbox.component" (dict "ctx" . "name" "goworker") -}}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name }}-goworker
name: {{ $name }}
labels:
app: goworker
{{- include "hrbox.labels" (dict "ctx" . "name" "goworker") | nindent 4 }}
spec:
minReadySeconds: 5
strategy:
@@ -12,39 +13,46 @@ spec:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
replicas: {{ .Values.app.goworker.replicas }}
replicas: {{ .Values.app.replicas.goworker }}
selector:
matchLabels:
service: {{ .Chart.Name }}-goworker
{{- include "hrbox.selectorLabels" (dict "ctx" . "name" "goworker") | nindent 6 }}
template:
metadata:
labels:
service: {{ .Chart.Name }}-goworker
{{- include "hrbox.selectorLabels" (dict "ctx" . "name" "goworker") | nindent 8 }}
app: goworker
annotations:
checksum/app-secret: {{ include (print $.Template.BasePath "/00-app-secret.yaml") . | sha256sum }}
spec:
imagePullSecrets:
- name: regsecret
{{- include "hrbox.imagePullSecrets" . | nindent 6 }}
containers:
- name: goworker
image: {{ .Values.image.goworker | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
stdin: true
tty: true
image: {{ .Values.app.goworker.image }}
imagePullPolicy: Always
ports:
- name: http
containerPort: 8686
env:
{{- include "envs_all" . | indent 12 }}
{{- include "goworker_resources" . | indent 10 }}
{{- include "hrbox.resources" (dict "ctx" . "component" "goworker") | indent 10 }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}-goworker
name: {{ $name }}
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "goworker") | nindent 4 }}
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8686"
prometheus.io/path: "/metrics"
spec:
selector:
service: {{ .Chart.Name }}-goworker
{{- include "hrbox.selectorLabels" (dict "ctx" . "name" "goworker") | nindent 4 }}
ports:
- name: http
port: 8686
+33 -17
View File
@@ -1,10 +1,11 @@
{{- $name := include "hrbox.component" (dict "ctx" . "name" "kedoca") -}}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name }}-kedoca
name: {{ $name }}
labels:
app: kedoca
{{- include "hrbox.labels" (dict "ctx" . "name" "kedoca") | nindent 4 }}
spec:
minReadySeconds: 5
strategy:
@@ -12,27 +13,42 @@ spec:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
replicas: {{ .Values.app.kedoca.replicas }}
replicas: {{ .Values.app.replicas.kedoca }}
selector:
matchLabels:
service: {{ .Chart.Name }}-kedoca
{{- include "hrbox.selectorLabels" (dict "ctx" . "name" "kedoca") | nindent 6 }}
template:
metadata:
labels:
service: {{ .Chart.Name }}-kedoca
{{- include "hrbox.selectorLabels" (dict "ctx" . "name" "kedoca") | nindent 8 }}
app: kedoca
annotations:
checksum/app-secret: {{ include (print $.Template.BasePath "/00-app-secret.yaml") . | sha256sum }}
spec:
imagePullSecrets:
- name: regsecret
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- kedoca
topologyKey: kubernetes.io/hostname
{{- include "hrbox.imagePullSecrets" . | nindent 6 }}
containers:
- name: kedoca
image: {{ .Values.app.kedoca.image }}
imagePullPolicy: Always
image: {{ .Values.image.kedoca | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 8558
env:
{{- include "envs_all" . | indent 12 }}
{{- include "envs_kedoca" . | indent 12 }}
ports:
- containerPort: 8558
{{- include "kedoca_resources" . | indent 10 }}
{{- include "hrbox.resources" (dict "ctx" . "component" "kedoca") | indent 10 }}
{{- if .Values.app.kedoca.secretName }}
volumeMounts:
- name: ca-cert
@@ -42,22 +58,22 @@ spec:
volumes:
- name: ca-cert
secret:
secretName: {{ .Values.app.kedoca.secretName }}
secretName: {{ .Values.app.kedoca.secretName | quote }}
{{- end }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}-kedoca
name: {{ $name }}
labels:
app: kedoca
{{- include "hrbox.labels" (dict "ctx" . "name" "kedoca") | nindent 4 }}
spec:
type: ClusterIP
selector:
service: {{ .Chart.Name }}-kedoca
{{- include "hrbox.selectorLabels" (dict "ctx" . "name" "kedoca") | nindent 4 }}
ports:
- name: http
port: 8558
targetPort: 8558
protocol: TCP
type: ClusterIP
+203
View File
@@ -0,0 +1,203 @@
{{- $cfg := .Values.app.nanabushPlayer.deployment }}
{{- if $cfg.enabled }}
{{- $name := include "nanabushPlayer.serviceName" . -}}
{{- $secretName := include "nanabushPlayer.secretName" . -}}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ $name }}
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "nanabush-player") | nindent 4 }}
spec:
replicas: {{ $cfg.replicas }}
revisionHistoryLimit: 3
minReadySeconds: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
service: {{ $name }}
template:
metadata:
labels:
service: {{ $name }}
app: nanabush-player
{{- if $cfg.metrics.enabled }}
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: {{ $cfg.metrics.port | quote }}
prometheus.io/path: "/metrics"
{{- end }}
spec:
automountServiceAccountToken: false
terminationGracePeriodSeconds: {{ $cfg.terminationGracePeriodSeconds }}
{{- include "hrbox.imagePullSecrets" . | nindent 6 }}
securityContext:
runAsNonRoot: true
runAsUser: 65532
runAsGroup: 65532
seccompProfile:
type: RuntimeDefault
containers:
- name: player
image: {{ .Values.image.nanabushPlayer | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
ports:
- name: http
containerPort: {{ .Values.app.nanabushPlayer.service.port }}
protocol: TCP
{{- if $cfg.metrics.enabled }}
- name: metrics
containerPort: {{ $cfg.metrics.port }}
protocol: TCP
{{- end }}
env:
- name: PLAYER_HTTP_ADDR
value: {{ printf ":%d" (.Values.app.nanabushPlayer.service.port | int) | quote }}
- name: PLAYER_METRICS_ADDR
{{- if $cfg.metrics.enabled }}
value: {{ printf ":%d" ($cfg.metrics.port | int) | quote }}
{{- else }}
value: "off"
{{- end }}
- name: PLAYER_BASE_PATH
value: {{ include "nanabushPlayer.basePath" . | quote }}
- name: PLAYER_AUDIENCE
value: {{ include "nanabushPlayer.publicOrigin" . | quote }}
- name: PLAYER_ALLOWED_ISSUERS
value: {{ include "nanabushPlayer.issuer" . | quote }}
- name: PLAYER_HS256_SECRET
valueFrom:
secretKeyRef:
name: {{ $secretName | quote }}
key: {{ required "Нужно имя ключа ticketHs256 в секрете Nanabush Player" $cfg.existingSecret.keys.ticketHs256 | quote }}
{{- with $cfg.existingSecret.keys.ticketKeys }}
- name: PLAYER_TICKET_KEYS
valueFrom:
secretKeyRef:
name: {{ $secretName | quote }}
key: {{ . | quote }}
{{- with $cfg.ticket.keylessAcceptedUntil }}
- name: PLAYER_TICKET_KEYLESS_UNTIL
value: {{ . | quote }}
{{- end }}
{{- end }}
- name: PLAYER_SESSION_SECRET
valueFrom:
secretKeyRef:
name: {{ $secretName | quote }}
key: {{ required "Нужно имя ключа sessionHs256 в секрете Nanabush Player" $cfg.existingSecret.keys.sessionHs256 | quote }}
- name: PLAYER_SESSION_ISSUER
value: {{ include "nanabushPlayer.publicOrigin" . | quote }}
- name: PLAYER_SESSION_TTL
value: {{ $cfg.ticket.sessionTtl | quote }}
- name: PLAYER_MAX_TICKET_LIFETIME
value: {{ $cfg.ticket.maxLifetime | quote }}
- name: REDIS_URL
{{- if $cfg.redis.urlFromExistingSecret }}
valueFrom:
secretKeyRef:
name: {{ $secretName | quote }}
key: {{ required "Нужно имя ключа redisUrl, когда redis.urlFromExistingSecret: true" $cfg.existingSecret.keys.redisUrl | quote }}
{{- else }}
value: {{ include "nanabushPlayer.redisUrl" . | quote }}
{{- end }}
- name: PLAYER_REDIS_KEY_PREFIX
value: {{ include "nanabushPlayer.redisKeyPrefix" . | quote }}
- name: PLAYER_PACKAGE_FETCH_SCHEMES
value: {{ $cfg.backchannel.schemes | quote }}
- name: PLAYER_PACKAGE_FETCH_ALLOWLIST
value: {{ include "nanabushPlayer.backchannelHost" . | quote }}
- name: PLAYER_PACKAGE_FETCH_ALLOW_PRIVATE_IPS
value: {{ $cfg.backchannel.allowPrivateIps | quote }}
- name: PLAYER_PACKAGE_FETCH_MAX_BYTES
value: {{ $cfg.backchannel.packageFetchMaxBytes | quote }}
- name: PLAYER_PACKAGE_CONTENT_MAX_BYTES
value: {{ $cfg.backchannel.packageContentMaxBytes | quote }}
- name: PLAYER_PACKAGE_FETCH_TIMEOUT
value: {{ $cfg.backchannel.packageFetchTimeout | quote }}
- name: PLAYER_PACKAGE_STREAM_TIMEOUT
value: {{ $cfg.backchannel.packageStreamTimeout | quote }}
- name: PLAYER_REPORT_ENDPOINT_SCHEMES
value: {{ $cfg.backchannel.schemes | quote }}
- name: PLAYER_REPORT_ENDPOINT_ALLOWLIST
value: {{ include "nanabushPlayer.backchannelHost" . | quote }}
- name: PLAYER_REPORT_ENDPOINT_ALLOW_PRIVATE_IPS
value: {{ $cfg.backchannel.allowPrivateIps | quote }}
- name: PLAYER_REPORT_ENDPOINT_TIMEOUT
value: {{ $cfg.backchannel.reportTimeout | quote }}
- name: PLAYER_FORWARDER_QUEUE_SIZE
value: {{ $cfg.forwarder.queueSize | quote }}
- name: PLAYER_FORWARDER_WORKERS
value: {{ $cfg.forwarder.workers | quote }}
- name: PLAYER_FORWARDER_MAX_ATTEMPTS
value: {{ $cfg.forwarder.maxAttempts | quote }}
- name: PLAYER_FORWARDER_INITIAL_BACKOFF
value: {{ $cfg.forwarder.initialBackoff | quote }}
- name: PLAYER_FORWARDER_MAX_BACKOFF
value: {{ $cfg.forwarder.maxBackoff | quote }}
- name: PLAYER_FORWARDER_PER_ATTEMPT_TIMEOUT
value: {{ $cfg.forwarder.perAttemptTimeout | quote }}
- name: PLAYER_FORWARDER_DEAD_LETTER_SIZE
value: {{ $cfg.forwarder.deadLetterSize | quote }}
- name: PLAYER_FORWARDER_CLAIM_IDLE
value: {{ $cfg.forwarder.claimIdle | quote }}
- name: PLAYER_FORWARDER_POLL_INTERVAL
value: {{ $cfg.forwarder.pollInterval | quote }}
- name: PLAYER_FORWARDER_RATE_LIMIT_EVENTS_PER_MINUTE
value: {{ $cfg.forwarder.rateLimitEventsPerMinute | quote }}
- name: PLAYER_FORWARDER_RATE_LIMIT_BATCHES_PER_MINUTE
value: {{ $cfg.forwarder.rateLimitBatchesPerMinute | quote }}
- name: PLAYER_FORWARDER_RATE_LIMIT_PAYLOAD_BYTES_PER_MINUTE
value: {{ $cfg.forwarder.rateLimitPayloadBytesPerMinute | quote }}
- name: LOG_LEVEL
value: {{ $cfg.log.level | quote }}
- name: LOG_FORMAT
value: {{ $cfg.log.format | quote }}
- name: PLAYER_DEV_HMR
value: "false"
readinessProbe:
httpGet:
path: {{ $cfg.probes.readiness.path }}
port: http
initialDelaySeconds: {{ $cfg.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ $cfg.probes.readiness.periodSeconds }}
timeoutSeconds: {{ $cfg.probes.readiness.timeoutSeconds }}
failureThreshold: {{ $cfg.probes.readiness.failureThreshold }}
livenessProbe:
httpGet:
path: {{ $cfg.probes.liveness.path }}
port: http
initialDelaySeconds: {{ $cfg.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ $cfg.probes.liveness.periodSeconds }}
timeoutSeconds: {{ $cfg.probes.liveness.timeoutSeconds }}
failureThreshold: {{ $cfg.probes.liveness.failureThreshold }}
{{- include "hrbox.resources" (dict "ctx" . "component" "nanabushPlayer") | indent 10 }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ $name }}
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "nanabush-player") | nindent 4 }}
spec:
type: ClusterIP
selector:
service: {{ $name }}
ports:
- name: http
port: {{ .Values.app.nanabushPlayer.service.port }}
targetPort: http
protocol: TCP
{{- end }}
+25 -7
View File
@@ -1,35 +1,52 @@
{{- if .Values.ingress.enabled }}
{{- $player := .Values.app.nanabushPlayer -}}
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Chart.Name }}-ingress
name: {{ include "hrbox.component" (dict "ctx" . "name" "ingress") }}
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "web") | nindent 4 }}
annotations:
kubernetes.io/ingress.class: {{ .Values.ingress.className }}
nginx.org/websocket-services: {{ .Chart.Name }}-chatbox
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
kubernetes.io/ingress.class: {{ .Values.ingress.className | quote }}
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"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_set_header X-Forwarded-For "$remote_addr, $server_addr";
proxy_set_header X-Forwarded-Port $pass_port;
proxy_set_header X-Forwarded-Proto $pass_access_scheme;
{{- with .Values.ingress.annotations }}
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
ingressClassName: {{ .Values.ingress.className | quote }}
rules:
- host: {{ .Values.ingress.host | quote }} # something.example.com
- host: {{ .Values.ingress.host | quote }}
http:
paths:
{{- if and $player.ingress.enabled $player.deployment.enabled }}
- path: {{ include "nanabushPlayer.basePath" . | quote }}
pathType: Prefix
backend:
service:
name: {{ include "nanabushPlayer.serviceName" . }}
port:
number: {{ $player.service.port }}
{{- end }}
- path: /chatbox/
pathType: Prefix
backend:
service:
name: {{ .Chart.Name }}-chatbox
name: {{ include "hrbox.component" (dict "ctx" . "name" "chatbox") }}
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: {{ .Chart.Name }}-web
name: {{ include "hrbox.component" (dict "ctx" . "name" "web") }}
port:
number: 80
{{- if .Values.ingress.tls.enabled }}
@@ -38,3 +55,4 @@ spec:
- {{ .Values.ingress.host | quote }}
secretName: {{ .Values.ingress.tls.secretName | quote }}
{{- end }}
{{- end }}
+17 -11
View File
@@ -2,20 +2,28 @@
apiVersion: batch/v1
kind: Job
metadata:
name: {{ .Chart.Name }}-migrate
name: {{ include "hrbox.component" (dict "ctx" . "name" "migrate") }}-{{ .Release.Revision }}
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "jobs") | nindent 4 }}
annotations:
"helm.sh/hook": post-install,post-upgrade
"helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation
"helm.sh/hook-delete-policy": before-hook-creation
"helm.sh/hook-weight": "10"
spec:
ttlSecondsAfterFinished: 100
ttlSecondsAfterFinished: 3600
backoffLimit: 6
template:
metadata:
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "jobs") | nindent 8 }}
spec:
imagePullSecrets:
- name: regsecret
restartPolicy: OnFailure
terminationGracePeriodSeconds: 5
{{- include "hrbox.imagePullSecrets" . | nindent 6 }}
initContainers:
- name: wait-postgres
image: postgres:14-alpine
image: {{ .Values.image.postgresClient | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
command:
- bash
- /wait.sh
@@ -27,16 +35,14 @@ spec:
{{- include "envs_all" . | indent 12 }}
containers:
- name: run-migrations
image: {{ .Values.app.image }}
image: {{ .Values.image.hrbox | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
command: ["php", "yii", "migrate", "--interactive=0"]
env:
{{- include "envs_all" . | indent 12 }}
{{- include "jobs_resources" . | indent 10 }}
{{- include "hrbox.resources" (dict "ctx" . "component" "jobs") | indent 10 }}
volumes:
- name: configs
configMap:
name: job-scripts
defaultMode: 420
restartPolicy: OnFailure
terminationGracePeriodSeconds: 5
backoffLimit: 6
-24
View File
@@ -1,24 +0,0 @@
---
apiVersion: batch/v1
kind: Job
metadata:
name: {{ .Chart.Name }}-update-cluster
annotations:
"helm.sh/hook": post-install,post-upgrade
"helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation
"helm.sh/hook-weight": "20"
spec:
template:
spec:
imagePullSecrets:
- name: regsecret
containers:
- name: run-update
image: {{ .Values.app.image }}
command: [ "php", "yii", "util/update-cluster" ]
env:
{{- include "envs_all" . | indent 12 }}
{{- include "jobs_resources" . | indent 10 }}
restartPolicy: OnFailure
terminationGracePeriodSeconds: 5
backoffLimit: 6
@@ -0,0 +1,30 @@
---
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "hrbox.component" (dict "ctx" . "name" "update-cluster") }}-{{ .Release.Revision }}
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "jobs") | nindent 4 }}
annotations:
"helm.sh/hook": post-install,post-upgrade
"helm.sh/hook-delete-policy": before-hook-creation
"helm.sh/hook-weight": "20"
spec:
ttlSecondsAfterFinished: 3600
backoffLimit: 6
template:
metadata:
labels:
{{- include "hrbox.labels" (dict "ctx" . "name" "jobs") | nindent 8 }}
spec:
restartPolicy: OnFailure
terminationGracePeriodSeconds: 5
{{- include "hrbox.imagePullSecrets" . | nindent 6 }}
containers:
- name: run-update
image: {{ .Values.image.hrbox | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
command: ["php", "yii", "util/update-cluster"]
env:
{{- include "envs_all" . | indent 12 }}
{{- include "hrbox.resources" (dict "ctx" . "component" "jobs") | indent 10 }}
+72
View File
@@ -0,0 +1,72 @@
HRBox {{ .Chart.AppVersion }} — релиз «{{ .Release.Name }}» в namespace «{{ .Release.Namespace }}».
Адрес: http{{ if .Values.ingress.tls.enabled }}s{{ end }}://{{ .Values.ingress.host }}
Что происходит прямо сейчас
---------------------------
1. Поднимаются поды приложения и сервисов.
2. Джоба {{ include "hrbox.component" (dict "ctx" . "name" "migrate") }}-{{ .Release.Revision }} накатывает миграции базы.
3. Джоба {{ include "hrbox.component" (dict "ctx" . "name" "update-cluster") }}-{{ .Release.Revision }} обновляет данные кластера.
Дождитесь завершения миграций:
kubectl -n {{ .Release.Namespace }} wait --for=condition=complete --timeout=30m \
job/{{ include "hrbox.component" (dict "ctx" . "name" "migrate") }}-{{ .Release.Revision }}
Проверка состояния
------------------
kubectl -n {{ .Release.Namespace }} get pods
kubectl -n {{ .Release.Namespace }} logs -l app=web -c fpm --tail=100
{{ if .Release.IsInstall -}}
Первый запуск: создайте администратора
--------------------------------------
kubectl -n {{ .Release.Namespace }} exec -it \
deploy/{{ include "hrbox.component" (dict "ctx" . "name" "worker") }} -- \
php yii user/create-admin admin@example.com --tenant-id=1
{{ end -}}
{{- if .Values.app.nanabushPlayer.deployment.enabled }}
Nanabush Player
---------------
Смонтирован на {{ include "nanabushPlayer.publicOrigin" . }}{{ include "nanabushPlayer.basePath" . }}
Версия: {{ include "nanabushPlayer.version" . }}
Проверить, что плеер отвечает сам, а не HRBox через общий маршрут
(настоящий плеер возвращает JSON, HTML означает, что запрос ушёл в HRBox):
kubectl -n {{ .Release.Namespace }} exec deploy/{{ include "hrbox.component" (dict "ctx" . "name" "worker") }} -- \
curl -s http://{{ include "nanabushPlayer.serviceName" . }}:{{ .Values.app.nanabushPlayer.service.port }}{{ .Values.app.nanabushPlayer.deployment.probes.readiness.path }}
Служебные пути (/healthz, /readyz, метрики) живут в корне и не зависят от
basePath; под basePath публикуются /start, статика и API плеера.
{{- if not .Values.app.nanabushPlayer.enabled }}
ВНИМАНИЕ: плеер развёрнут, но выключен для пользователей
(app.nanabushPlayer.enabled: false). Учебные материалы открываются старым
проигрывателем.
{{- end }}
{{- else }}
Nanabush Player выключен (app.nanabushPlayer.deployment.enabled: false).
Учебные материалы открываются старым проигрывателем.
{{- end }}
{{ if not .Values.dragonfly.enabled -}}
Dragonfly выключен — проверьте, что app.redis указывает на внешний Redis:
{{ include "hrbox.redisHost" . }}:{{ .Values.app.redis.port }}
{{ end -}}
{{- if not .Values.nats.enabled }}
NATS выключен — проверьте, что app.nats.url указывает на внешний брокер:
{{ include "hrbox.natsUrl" . }}
{{ end -}}
{{- if eq .Values.app.debug "1" }}
ВНИМАНИЕ: включён режим отладки (app.debug: "1"). Для production верните "0".
{{- end }}
{{- if not .Values.ingress.tls.enabled }}
ВНИМАНИЕ: TLS выключен (ingress.tls.enabled: false). Приложение отдаёт
cookie сессий по открытому HTTP.
{{- end }}
+216 -158
View File
@@ -1,61 +1,50 @@
{{- define "envs_all" }}
# Основные настройки приложения
# Приложение
- name: INSTANCE_NAME
value: {{ .Values.app.instanceName | quote }}
- name: DEFAULT_TENANT_HOSTNAME
value: {{ .Values.app.defaultTenantHostname | quote }}
- name: APP_ENV
value: "prod"
- name: APP_PORT
value: "80"
- name: YII_DEBUG
value: {{ .Values.app.debug | quote }}
- name: YII_TRACE_LEVEL
value: "0"
- name: APP_COOKIE_VALIDATION_KEY
value: {{ .Values.secrets.cookieValidationKey | quote }}
- name: APP_ENCRYPTION_KEY
value: {{ .Values.secrets.encryptionKey | quote }}
- name: APP_PORT
value: "80"
- name: AUTH_HOST
value: {{ .Values.app.authHost | quote }}
- name: SINGLE_TENANT
value: {{ .Values.app.singleTenant | quote }}
- name: FORCE_HTTPS
value: {{ .Values.app.forceHttps | quote }}
- name: ENABLE_FRONTEND
value: {{ .Values.app.front2 | quote }}
- name: LAUNCHPAD_TOPICS
value: {{ .Values.app.launchpad.topics | quote }}
- name: SMS_SENDER_NAME
value: "HRBOX"
- name: ENABLE_FRONTEND
value: {{ .Values.app.front2 | quote }}
{{- if .Values.app.custom_policy }}
value: {{ .Values.app.sms.senderName | quote }}
- name: IFRAME_FULL_ACCESS
value: {{ .Values.app.security.iframeFullAccess | quote }}
- name: LDAPTLS_REQCERT
value: "never"
{{- include "hrbox.secretEnv" (dict "ctx" . "env" "APP_COOKIE_VALIDATION_KEY" "key" "cookie-validation-key") | nindent 0 }}
{{- 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.custom_policy.enabled | default false | quote }}
{{- if .Values.app.custom_policy.enabled }}
value: {{ .Values.app.customPolicy.enabled | quote }}
{{- if .Values.app.customPolicy.enabled }}
- name: CUSTOM_PRIVACY_POLICY_URL
value: {{ .Values.app.custom_policy.privacy_url | default "https://hrbox.io/policy" | quote }}
value: {{ .Values.app.customPolicy.privacyUrl | default "https://hrbox.io/policy" | quote }}
- name: CUSTOM_TERMS_URL
value: {{ .Values.app.custom_policy.terms_url | default "https://hrbox.io/terms" | quote }}
{{- end }}
value: {{ .Values.app.customPolicy.termsUrl | default "https://hrbox.io/terms" | quote }}
{{- else }}
- name: ALLOW_CUSTOM_POLICY_URLS
value: "false"
{{- end }}
# NATS
- name: BROKER
value: "nats"
- name: NATS_URL
value: {{ .Values.app.nats.url | quote }}
# Sentry (опционально)
{{- if .Values.app.sentry.dsn }}
- name: SENTRY_DSN
value: {{ .Values.app.sentry.dsn | quote }}
- name: SENTRY_DSN_LAUNCHPAD
value: {{ .Values.app.sentry.dsn | quote }}
- name: SENTRY_DSN_CONDUCTOR
value: {{ .Values.app.sentry.dsn | quote }}
- name: SENTRY_ENVIRONMENT
value: {{ .Values.app.sentry.environment | quote }}
- name: SENTRY_RELEASE
value: {{ .Values.app.image | quote }}
- name: CUSTOM_PRIVACY_POLICY_URL
value: ""
- name: CUSTOM_TERMS_URL
value: ""
{{- end }}
# PostgreSQL
@@ -67,74 +56,35 @@
value: {{ .Values.app.postgres.database | quote }}
- name: POSTGRES_USER
value: {{ .Values.app.postgres.user | quote }}
- name: POSTGRES_PASSWORD
value: {{ .Values.app.postgres.password | quote }}
- name: POSTGRES_CITUS
value: {{ .Values.app.postgres.citus | quote }}
{{- include "hrbox.secretEnv" (dict "ctx" . "env" "POSTGRES_PASSWORD" "key" "postgres-password") | nindent 0 }}
# Redis
# Redis / Dragonfly
- name: REDIS_HOST
value: {{ .Values.app.redis.host | quote }}
value: {{ include "hrbox.redisHost" . | quote }}
- name: REDIS_PORT
value: {{ .Values.app.redis.port | quote }}
- name: REDIS_CONNECTION
value: {{ printf "tcp://%s:%s" .Values.app.redis.host .Values.app.redis.port | quote }}
value: {{ printf "tcp://%s:%s" (include "hrbox.redisHost" .) (.Values.app.redis.port | toString) | quote }}
# Yandex API (опционально)
{{- if .Values.app.yandex.clientId }}
- name: YANDEX_CLIENT_ID
value: {{ .Values.app.yandex.clientId | quote }}
- name: YANDEX_CLIENT_PASSWORD
value: {{ .Values.app.yandex.clientPassword | quote }}
# NATS
- name: BROKER
value: "nats"
- name: NATS_URL
value: {{ include "hrbox.natsUrl" . | quote }}
- name: NATS_STREAM_REPLICAS
{{- if and .Values.nats.enabled .Values.nats.cluster.enabled }}
value: {{ .Values.nats.cluster.replicas | quote }}
{{- else }}
value: "1"
{{- end }}
# Slack (опционально)
{{- if .Values.app.slack.clientId }}
- name: SLACK_APP_CLIENT_ID
value: {{ .Values.app.slack.clientId | quote }}
- name: SLACK_APP_CLIENT_SECRET
value: {{ .Values.app.slack.clientSecret | quote }}
- name: SLACK_APP_VERIFICATION_TOKEN
value: {{ .Values.app.slack.verificationToken | quote }}
{{- end }}
# Email
- name: SENDER_ENV
value: "PROD"
- name: SENDER_DEV_EMAIL
value: ""
- name: SENDGRID_API_KEY
value: ""
# SMTP
- name: SMTP_USERNAME
value: {{ .Values.app.smtp.user | quote }}
- name: SMTP_PASSWORD
value: {{ .Values.app.smtp.password | quote }}
- name: SMTP_HOST
value: {{ .Values.app.smtp.host | quote }}
- name: SMTP_PORT
value: {{ .Values.app.smtp.port | quote }}
- name: SMTP_FROM
value: {{ .Values.app.smtp.from | quote }}
- name: SMTP_FROM_HOST
value: {{ .Values.app.smtp.fromHost | quote }}
- name: SMTP_SECURITY_TYPE
value: {{ .Values.app.smtp.securityType | quote }}
- name: SMTP_SSL_ALLOW_SELF_SIGNED
value: {{ .Values.app.smtp.sslAllowSelfSigned | quote }}
- name: SMTP_SSL_VERIFY_PEER
value: {{ .Values.app.smtp.sslVerifyPeer | quote }}
# S3
- name: S3_ENDPOINT
value: {{ .Values.app.s3.endpoint | quote }}
- name: S3_BUCKET
value: {{ .Values.app.s3.bucket | quote }}
- name: S3_KEY
value: {{ .Values.app.s3.key | quote }}
- name: S3_SECRET
value: {{ .Values.app.s3.secret | quote }}
- name: S3_PREFIX
value: {{ .Values.app.s3.prefix | quote }}
- name: S3_REGION
@@ -145,38 +95,118 @@
value: {{ .Values.app.s3.pathStyle | quote }}
- name: S3_MULTIPART
value: {{ .Values.app.s3.multipart | quote }}
{{- 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 }}
# Hub
- name: HUB_URL
value: {{ .Values.app.hub.url | quote }}
- name: HUB_TOKEN
value: {{ .Values.app.hub.token | quote }}
# Почта
- name: SENDER_ENV
value: "PROD"
- name: SENDER_DEV_EMAIL
value: ""
- name: SMTP_HOST
value: {{ .Values.app.smtp.host | quote }}
- name: SMTP_PORT
value: {{ .Values.app.smtp.port | quote }}
- name: SMTP_USERNAME
value: {{ .Values.app.smtp.user | quote }}
- name: SMTP_FROM
value: {{ .Values.app.smtp.from | quote }}
- name: SMTP_FROM_HOST
value: {{ .Values.app.smtp.fromHost | quote }}
- name: SMTP_SECURITY_TYPE
value: {{ .Values.app.smtp.securityType | quote }}
- name: SMTP_SSL_ALLOW_SELF_SIGNED
value: {{ .Values.app.smtp.sslAllowSelfSigned | quote }}
- name: SMTP_SSL_VERIFY_PEER
value: {{ .Values.app.smtp.sslVerifyPeer | quote }}
{{- include "hrbox.secretEnv" (dict "ctx" . "env" "SMTP_PASSWORD" "key" "smtp-password") | nindent 0 }}
# Geonames
# Внутренние сервисы
- name: GEONAMES_URL
value: {{ .Values.app.geonames.url | quote }}
# Статические переменные
value: {{ include "hrbox.geonamesUrl" . | quote }}
- name: GOWORKER_URL
value: {{ include "hrbox.goworkerUrl" . | quote }}
- name: GOWORKER_PUSH_MIN_INTERVAL
value: {{ .Values.app.goworker.pushMinInterval | quote }}
- name: GOWORKER_SENTRY_DSN
value: {{ .Values.app.sentry.goworkerDsn | quote }}
- name: KEDOCA_URL
value: {{ include "hrbox.kedocaUrl" . | quote }}
- name: TSP_URL
value: {{ .Values.app.kedoca.tspUrl | quote }}
- name: APP_INTERNAL_URL
value: {{ .Values.app.videoConverter.internalUrl | default (printf "http://%s" (include "hrbox.component" (dict "ctx" . "name" "web"))) | quote }}
- name: CONDUCTOR_TOPIC_CONDUCTOR
value: "conductor"
- name: CONDUCTOR_TOPIC_LAUNCHER
value: "launcher"
- name: PYTHONUNBUFFERED
value: "1"
- name: DOCCONV_MAX_PARALLEL_TASKS
value: {{ .Values.app.fileProcessor.docconvMaxParallelTasks | quote }}
# 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
{{- 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)
- 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
- name: AI_DOCS_INDEX_ENABLED
value: {{ .Values.app.ai.docsIndexEnabled | quote }}
- name: AI_SEMANTIC_INDEX_ENABLED
value: {{ .Values.app.ai.semanticIndexEnabled | quote }}
- name: AI_SEMANTIC_INDEX_USE_BATCH
value: {{ .Values.app.ai.semanticIndexUseBatch | quote }}
- name: YANDEX_CLOUD_AI_FOLDER_ID
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
value: {{ .Values.app.integrations.google.enabled | quote }}
- name: GOOGLE_CLIENT_ID
value: "692956564858-ccapt35khk7tmadl17b3879ht2cropp6.apps.googleusercontent.com"
- name: GOOGLE_CLIENT_SECRET
value: "nW4pl4YsVWAR371gMRStW9qg"
- name: SINGLE_TENANT
value: "0"
- name: FORCE_HTTPS
value: "true"
value: {{ .Values.app.integrations.google.clientId | quote }}
{{- include "hrbox.secretEnv" (dict "ctx" . "env" "GOOGLE_CLIENT_SECRET" "key" "google-client-secret") | nindent 0 }}
- name: YANDEX_CLIENT_ID
value: {{ .Values.app.yandex.clientId | quote }}
{{- include "hrbox.secretEnv" (dict "ctx" . "env" "YANDEX_CLIENT_PASSWORD" "key" "yandex-client-password") | nindent 0 }}
- name: SLACK_APP_CLIENT_ID
value: {{ .Values.app.slack.clientId | quote }}
{{- include "hrbox.secretEnv" (dict "ctx" . "env" "SLACK_APP_CLIENT_SECRET" "key" "slack-client-secret") | nindent 0 }}
{{- include "hrbox.secretEnv" (dict "ctx" . "env" "SLACK_APP_VERIFICATION_TOKEN" "key" "slack-verification-token") | nindent 0 }}
- name: GLAGOL_PARTNER_ID
value: "ab230ee1-6946-43b9-8kfa-447beb8348b0"
value: {{ .Values.app.integrations.glagol.partnerId | quote }}
- name: GLAGOL_API_URL
value: "https://api.glagol.online/glagol-api"
- name: LDAPTLS_REQCERT
value: "never"
- name: IFRAME_FULL_ACCESS
value: {{ .Values.app.security.iframeFullAccess | default "false" | quote }}
value: {{ .Values.app.integrations.glagol.apiUrl | quote }}
# Sentry
- name: SENTRY_DSN
value: {{ .Values.app.sentry.dsn | quote }}
- name: SENTRY_DSN_LAUNCHPAD
value: {{ .Values.app.sentry.launchpadDsn | default .Values.app.sentry.dsn | quote }}
- name: SENTRY_DSN_CONDUCTOR
value: {{ .Values.app.sentry.conductorDsn | default .Values.app.sentry.dsn | quote }}
- name: SENTRY_ENVIRONMENT
value: {{ .Values.app.sentry.environment | quote }}
- name: SENTRY_RELEASE
value: {{ .Values.image.hrbox | quote }}
# Метаданные пода
- name: POD_NAME
@@ -195,56 +225,67 @@
valueFrom:
fieldRef:
fieldPath: spec.nodeName
# AI (опционально)
{{- if .Values.app.ai.yandex.folderId }}
- name: YANDEX_CLOUD_AI_FOLDER_ID
value: {{ .Values.app.ai.yandex.folderId | quote }}
- name: YANDEX_CLOUD_AI_API_KEY
value: {{ .Values.app.ai.yandex.apiKey | quote }}
{{- end }}
# Integration Services
- name: GOWORKER_URL
value: {{ .Values.app.goworker.url | quote }}
{{- if .Values.app.goworker.sentryDsn }}
- name: GOWORKER_SENTRY_DSN
value: {{ .Values.app.goworker.sentryDsn | quote }}
{{- define "envs_nanabush_player" -}}
{{- $player := .Values.app.nanabushPlayer }}
- name: NANABUSH_PLAYER_ENABLED
value: {{ $player.enabled | quote }}
{{- if $player.deployment.enabled }}
- name: NANABUSH_PLAYER_PUBLIC_ORIGIN
value: {{ include "nanabushPlayer.publicOrigin" . | quote }}
- name: NANABUSH_PLAYER_AUDIENCE
value: {{ include "nanabushPlayer.publicOrigin" . | quote }}
- name: NANABUSH_PLAYER_BASE_PATH
value: {{ include "nanabushPlayer.basePath" . | quote }}
- name: NANABUSH_PLAYER_INTERNAL_ORIGIN
value: {{ include "nanabushPlayer.internalOrigin" . | quote }}
- name: NANABUSH_PLAYER_ISSUER
value: {{ include "nanabushPlayer.issuer" . | quote }}
- name: NANABUSH_PLAYER_CONTRACT_VERSION
value: {{ $player.contractVersion | quote }}
- name: NANABUSH_PLAYER_PLAYER_VERSION
value: {{ include "nanabushPlayer.version" . | quote }}
- name: NANABUSH_PLAYER_TICKET_TTL_SECONDS
value: {{ $player.deployment.ticket.ttlSeconds | quote }}
- name: NANABUSH_PLAYER_SESSION_TTL_SECONDS
value: {{ $player.deployment.ticket.credentialTtlSeconds | quote }}
- name: NANABUSH_PLAYER_PACKAGE_CONTENT_MAX_BYTES
value: {{ $player.deployment.backchannel.packageContentMaxBytes | quote }}
- name: NANABUSH_PLAYER_SCORM_PACKAGE_MAX_BYTES
value: {{ $player.deployment.backchannel.scormPackageMaxBytes | quote }}
{{- with $player.deployment.backchannel.scormPackageMaxFiles }}
- name: NANABUSH_PLAYER_SCORM_PACKAGE_MAX_FILES
value: {{ . | quote }}
{{- end }}
- name: GOWORKER_PUSH_MIN_INTERVAL
value: {{ .Values.app.goworker.pushMinInterval | quote }}
- name: KEDOCA_URL
value: {{ .Values.app.kedoca.url | quote }}
- name: TSP_URL
value: "http://qs.cryptopro.ru/tsp/tsp.srf"
# Internal Services
- name: SERVICES_INTERNAL_SECRET
value: {{ .Values.secrets.servicesInternalSecret | default "QrrH84K2dG5JfoYxi4ZNPyg5p2LEj6rN69z5tIQYkfY6SJZ8GDXOcRjTNnefbOx5" | quote }}
# Document Converter
- name: PYTHONUNBUFFERED
value: "1"
- name: DOCCONV_MAX_PARALLEL_TASKS
value: "1"
# AI Features
- name: AI_DOCS_INDEX_ENABLED
value: "true"
- name: AI_SEMANTIC_INDEX_ENABLED
value: "true"
- name: AI_SEMANTIC_INDEX_USE_BATCH
value: "true"
# Chatbox
{{- if .Values.app.chatbox.sentryDsn }}
- name: CHATBOX_SENTRY_DSN
value: {{ .Values.app.chatbox.sentryDsn | quote }}
- name: NANABUSH_PLAYER_LEGACY_FALLBACK
value: {{ $player.rollout.legacyFallback | quote }}
- name: NANABUSH_PLAYER_ALLOWED_CONTENT_TYPES
value: {{ join "," $player.rollout.allowedContentTypes | quote }}
- name: NANABUSH_PLAYER_ALLOWED_TENANTS
value: {{ join "," $player.rollout.allowedTenants | quote }}
- name: NANABUSH_PLAYER_ROLLOUT_PERCENT
value: {{ $player.rollout.percent | quote }}
{{- end }}
- name: CHATBOX_WRAPPING_KEY
value: {{ .Values.app.chatbox.wrappingKey | quote }}
{{- end -}}
{{- define "envs_nanabush_player_signing" -}}
{{- $cfg := .Values.app.nanabushPlayer.deployment }}
{{- if $cfg.enabled }}
- name: NANABUSH_PLAYER_HS256_SECRET
valueFrom:
secretKeyRef:
name: {{ include "nanabushPlayer.secretName" . | quote }}
key: {{ required "Нужно имя ключа ticketHs256 в секрете Nanabush Player" $cfg.existingSecret.keys.ticketHs256 | quote }}
{{- with $cfg.existingSecret.keys.ticketKeys }}
- name: NANABUSH_PLAYER_TICKET_KEYS
valueFrom:
secretKeyRef:
name: {{ include "nanabushPlayer.secretName" $ | quote }}
key: {{ . | quote }}
{{- end }}
{{- end }}
{{- end -}}
{{- define "envs_kedoca" }}
- name: ROOT_CA_PASS
@@ -256,3 +297,20 @@
- name: KEDO_PKEYOPT
value: {{ .Values.app.kedoca.pkeyopt | quote }}
{{- end }}
{{- define "envs_tracing" }}
- name: OTEL_PHP_AUTOLOAD_ENABLED
value: "true"
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: {{ .Values.app.tracing.exporterUrl | quote }}
- name: OTEL_SERVICE_NAME
value: "hrbox-app"
- name: OTEL_TRACES_EXPORTER
value: "otlp"
- name: OTEL_EXPORTER_OTLP_PROTOCOL
value: "http/protobuf"
- name: OTEL_PROPAGATORS
value: "baggage,tracecontext"
- name: OTEL_LOG_LEVEL
value: "info"
{{- end }}
+114 -94
View File
@@ -1,110 +1,130 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "nats.name" -}}
{{- default .Chart.Name .Values.nats.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- define "hrbox.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "hrbox.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "nats.fullname" -}}
{{- if .Values.nats.fullnameOverride -}}
{{- .Values.nats.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- define "hrbox.component" -}}
{{- printf "%s-%s" (include "hrbox.name" .ctx) .name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "hrbox.selectorLabels" -}}
service: {{ include "hrbox.component" (dict "ctx" .ctx "name" .name) }}
{{- end -}}
{{- define "hrbox.labels" -}}
app: {{ .name }}
app.kubernetes.io/name: {{ include "hrbox.name" .ctx }}
app.kubernetes.io/component: {{ .name }}
app.kubernetes.io/instance: {{ .ctx.Release.Name }}
app.kubernetes.io/managed-by: {{ .ctx.Release.Service }}
app.kubernetes.io/version: {{ .ctx.Chart.AppVersion | quote }}
helm.sh/chart: {{ include "hrbox.chart" .ctx }}
{{- end -}}
{{- define "hrbox.imagePullSecrets" -}}
{{- if .Values.imagePullSecrets }}
imagePullSecrets:
{{- range .Values.imagePullSecrets }}
- name: {{ .name | quote }}
{{- end }}
{{- end }}
{{- end -}}
{{- define "hrbox.imageTag" -}}
{{- $lastSegment := splitList "/" . | last -}}
{{- if contains ":" $lastSegment -}}
{{- splitList ":" $lastSegment | last -}}
{{- end -}}
{{- end -}}
{{- define "hrbox.geonamesUrl" -}}
{{- .Values.app.geonames.url | default (printf "http://%s:8181" (include "hrbox.component" (dict "ctx" . "name" "geonames"))) -}}
{{- end -}}
{{- define "hrbox.goworkerUrl" -}}
{{- .Values.app.goworker.url | default (printf "http://%s:8686" (include "hrbox.component" (dict "ctx" . "name" "goworker"))) -}}
{{- end -}}
{{- define "hrbox.kedocaUrl" -}}
{{- .Values.app.kedoca.url | default (printf "http://%s:8558" (include "hrbox.component" (dict "ctx" . "name" "kedoca"))) -}}
{{- end -}}
{{- define "hrbox.redisHost" -}}
{{- .Values.app.redis.host | default (include "hrbox.component" (dict "ctx" . "name" "dragonfly")) -}}
{{- end -}}
{{- define "hrbox.natsUrl" -}}
{{- .Values.app.nats.url | default (printf "nats://%s:4222" (include "hrbox.component" (dict "ctx" . "name" "nats"))) -}}
{{- end -}}
{{- define "hrbox.secretName" -}}
{{- if .Values.secrets.existingSecret -}}
{{- .Values.secrets.existingSecret -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nats.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- include "hrbox.component" (dict "ctx" . "name" "secrets") -}}
{{- end -}}
{{- end -}}
{{- define "hrbox.secretEnv" -}}
- name: {{ .env }}
valueFrom:
secretKeyRef:
name: {{ include "hrbox.secretName" .ctx | quote }}
key: {{ .key | quote }}
{{- end -}}
{{- define "nanabushPlayer.host" -}}
{{- required "Для Nanabush Player нужен непустой ingress.host" .Values.ingress.host -}}
{{- end -}}
{{- define "nanabushPlayer.publicOrigin" -}}
{{- printf "https://%s" (include "nanabushPlayer.host" .) -}}
{{- end -}}
{{- define "nanabushPlayer.basePath" -}}
{{- $path := .Values.app.nanabushPlayer.basePath | default "/nanabush-player" -}}
{{- $path | trimSuffix "/" -}}
{{- end -}}
{{- define "nanabushPlayer.issuer" -}}
{{- if .Values.app.nanabushPlayer.issuer -}}
{{- .Values.app.nanabushPlayer.issuer -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- printf "https://%s/lms/player" (include "nanabushPlayer.host" .) -}}
{{- end -}}
{{- end -}}
{{- define "nanabushPlayer.serviceName" -}}
{{- .Values.app.nanabushPlayer.service.name | default (include "hrbox.component" (dict "ctx" . "name" "nanabush-player")) -}}
{{- end -}}
{{- define "nats.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "nats.labels" -}}
app.kubernetes.io/name: nats
app.kubernetes.io/instance: {{ .Release.Name }}
helm.sh/chart: {{ include "nats.chart" . }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "nats.selectorLabels" -}}
app.kubernetes.io/name: nats
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Return the proper NATS image name
*/}}
{{- define "nats.clusterAdvertise" -}}
{{- printf "$(POD_NAME).%s.$(POD_NAMESPACE).svc.%s" (include "nats.fullname" . ) $.Values.nats.k8sClusterDomain }}
{{- end }}
{{/*
Return the NATS cluster routes.
*/}}
{{- define "nats.clusterRoutes" -}}
{{- $name := (include "nats.fullname" . ) -}}
{{- range $i, $e := until (.Values.nats.cluster.replicas | int) -}}
{{- printf "nats://%s-%d.%s.%s.svc.%s:6222," $name $i $name $.Release.Namespace $.Values.nats.k8sClusterDomain -}}
{{- define "nanabushPlayer.backchannelHost" -}}
{{- .Values.app.nanabushPlayer.deployment.backchannel.host | default (include "hrbox.component" (dict "ctx" . "name" "web")) -}}
{{- end -}}
{{- end }}
{{- define "nats.extraRoutes" -}}
{{- range $i, $url := .Values.nats.cluster.extraRoutes -}}
{{- printf "%s," $url -}}
{{- define "nanabushPlayer.internalOrigin" -}}
{{- printf "http://%s" (include "nanabushPlayer.backchannelHost" .) -}}
{{- end -}}
{{- end }}
{{- define "nats.tlsConfig" -}}
tls {
{{- if .cert }}
cert_file: {{ .secretPath }}/{{ .secret.name }}/{{ .cert }}
{{- end }}
{{- if .key }}
key_file: {{ .secretPath }}/{{ .secret.name }}/{{ .key }}
{{- end }}
{{- if .ca }}
ca_file: {{ .secretPath }}/{{ .secret.name }}/{{ .ca }}
{{- end }}
{{- if .insecure }}
insecure: {{ .insecure }}
{{- end }}
{{- if .verify }}
verify: {{ .verify }}
{{- end }}
{{- if .verifyAndMap }}
verify_and_map: {{ .verifyAndMap }}
{{- end }}
{{- if .curvePreferences }}
curve_preferences: {{ .curvePreferences }}
{{- end }}
{{- if .timeout }}
timeout: {{ .timeout }}
{{- end }}
}
{{- end }}
{{- define "nanabushPlayer.version" -}}
{{- $tag := include "hrbox.imageTag" .Values.image.nanabushPlayer -}}
{{- if not $tag -}}
{{- fail "image.nanabushPlayer должен содержать явный тег версии, например nanabush-player:1.0.11" -}}
{{- end -}}
{{- $tag -}}
{{- end -}}
{{- define "nanabushPlayer.secretName" -}}
{{- required "app.nanabushPlayer.deployment.existingSecret.name обязателен, когда Player включён" .Values.app.nanabushPlayer.deployment.existingSecret.name -}}
{{- end -}}
{{/*
Renders a value that contains template.
Usage:
{{ include "tplvalues.render" ( dict "value" .Values.nats.path.to.the.Value "context" $) }}
*/}}
{{- define "tplvalues.render" -}}
{{- if typeIs "string" .value }}
{{- tpl .value .context }}
{{- else }}
{{- tpl (.value | toYaml) .context }}
{{- end }}
{{- define "nanabushPlayer.redisUrl" -}}
{{- printf "redis://%s:%s/%d" (include "hrbox.redisHost" .) (.Values.app.redis.port | toString) (.Values.app.nanabushPlayer.deployment.redis.database | int) -}}
{{- end -}}
{{- define "nanabushPlayer.redisKeyPrefix" -}}
{{- printf "hrbox:%s:%s" .Release.Namespace .Values.app.nanabushPlayer.deployment.redis.keyPrefixSuffix -}}
{{- end -}}
+18 -107
View File
@@ -1,119 +1,30 @@
{{- define "app_resources" }}
{{- define "hrbox.resources" -}}
{{- $r := index .ctx.Values.resources .component -}}
{{- if $r }}
resources:
{{- with $r.requests }}
requests:
memory: {{ .Values.resources.app.requests.memory | quote }}
cpu: {{ .Values.resources.app.requests.cpu | quote }}
ephemeral-storage: {{ .Values.resources.app.requests.ephemeralStorage | quote }}
limits:
memory: {{ .Values.resources.app.limits.memory | quote }}
cpu: {{ .Values.resources.app.limits.cpu | quote }}
ephemeral-storage: {{ .Values.resources.app.limits.ephemeralStorage | quote }}
{{- with .cpu }}
cpu: {{ . | quote }}
{{- end }}
{{- define "nginx_resources" }}
resources:
requests:
memory: {{ .Values.resources.nginx.requests.memory | quote }}
cpu: {{ .Values.resources.nginx.requests.cpu | quote }}
ephemeral-storage: {{ .Values.resources.nginx.requests.ephemeralStorage | quote }}
limits:
memory: {{ .Values.resources.nginx.limits.memory | quote }}
cpu: {{ .Values.resources.nginx.limits.cpu | quote }}
ephemeral-storage: {{ .Values.resources.nginx.limits.ephemeralStorage | quote }}
{{- with .memory }}
memory: {{ . | quote }}
{{- end }}
{{- define "worker_resources" }}
resources:
requests:
memory: {{ .Values.resources.worker.requests.memory | quote }}
cpu: {{ .Values.resources.worker.requests.cpu | quote }}
ephemeral-storage: {{ .Values.resources.worker.requests.ephemeralStorage | quote }}
limits:
memory: {{ .Values.resources.worker.limits.memory | quote }}
cpu: {{ .Values.resources.worker.limits.cpu | quote }}
ephemeral-storage: {{ .Values.resources.worker.limits.ephemeralStorage | quote }}
{{- with .ephemeralStorage }}
ephemeral-storage: {{ . | quote }}
{{- end }}
{{- define "websocket_resources" }}
resources:
requests:
memory: {{ .Values.resources.websocket.requests.memory | quote }}
cpu: {{ .Values.resources.websocket.requests.cpu | quote }}
ephemeral-storage: {{ .Values.resources.websocket.requests.ephemeralStorage | quote }}
limits:
memory: {{ .Values.resources.websocket.limits.memory | quote }}
cpu: {{ .Values.resources.websocket.limits.cpu | quote }}
ephemeral-storage: {{ .Values.resources.websocket.limits.ephemeralStorage | quote }}
{{- end }}
{{- define "geonames_resources" }}
resources:
requests:
memory: {{ .Values.resources.geonames.requests.memory | quote }}
cpu: {{ .Values.resources.geonames.requests.cpu | quote }}
ephemeral-storage: {{ .Values.resources.geonames.requests.ephemeralStorage | quote }}
{{- with $r.limits }}
limits:
memory: {{ .Values.resources.geonames.limits.memory | quote }}
cpu: {{ .Values.resources.geonames.limits.cpu | quote }}
ephemeral-storage: {{ .Values.resources.geonames.limits.ephemeralStorage | quote }}
{{- with .cpu }}
cpu: {{ . | quote }}
{{- end }}
{{- define "conductor_resources" }}
resources:
requests:
memory: {{ .Values.resources.conductor.requests.memory | quote }}
cpu: {{ .Values.resources.conductor.requests.cpu | quote }}
ephemeral-storage: {{ .Values.resources.conductor.requests.ephemeralStorage | quote }}
limits:
memory: {{ .Values.resources.conductor.limits.memory | quote }}
cpu: {{ .Values.resources.conductor.limits.cpu | quote }}
ephemeral-storage: {{ .Values.resources.conductor.limits.ephemeralStorage | quote }}
{{- with .memory }}
memory: {{ . | quote }}
{{- end }}
{{- define "jobs_resources" }}
resources:
requests:
memory: {{ .Values.resources.jobs.requests.memory | quote }}
cpu: {{ .Values.resources.jobs.requests.cpu | quote }}
ephemeral-storage: {{ .Values.resources.jobs.requests.ephemeralStorage | quote }}
limits:
memory: {{ .Values.resources.jobs.limits.memory | quote }}
cpu: {{ .Values.resources.jobs.limits.cpu | quote }}
ephemeral-storage: {{ .Values.resources.jobs.limits.ephemeralStorage | quote }}
{{- with .ephemeralStorage }}
ephemeral-storage: {{ . | quote }}
{{- end }}
{{- define "goworker_resources" }}
resources:
requests:
memory: {{ .Values.resources.goworker.requests.memory | quote }}
cpu: {{ .Values.resources.goworker.requests.cpu | quote }}
ephemeral-storage: {{ .Values.resources.goworker.requests.ephemeralStorage | quote }}
limits:
memory: {{ .Values.resources.goworker.limits.memory | quote }}
cpu: {{ .Values.resources.goworker.limits.cpu | quote }}
ephemeral-storage: {{ .Values.resources.goworker.limits.ephemeralStorage | quote }}
{{- end }}
{{- define "kedoca_resources" }}
resources:
requests:
memory: {{ .Values.resources.kedoca.requests.memory | quote }}
cpu: {{ .Values.resources.kedoca.requests.cpu | quote }}
ephemeral-storage: {{ .Values.resources.kedoca.requests.ephemeralStorage | quote }}
limits:
memory: {{ .Values.resources.kedoca.limits.memory | quote }}
cpu: {{ .Values.resources.kedoca.limits.cpu | quote }}
ephemeral-storage: {{ .Values.resources.kedoca.limits.ephemeralStorage | quote }}
{{- end }}
{{- define "chatbox_resources" }}
resources:
requests:
memory: {{ .Values.resources.chatbox.requests.memory | quote }}
cpu: {{ .Values.resources.chatbox.requests.cpu | quote }}
ephemeral-storage: {{ .Values.resources.chatbox.requests.ephemeralStorage | quote }}
limits:
memory: {{ .Values.resources.chatbox.limits.memory | quote }}
cpu: {{ .Values.resources.chatbox.limits.cpu | quote }}
ephemeral-storage: {{ .Values.resources.chatbox.limits.ephemeralStorage | quote }}
{{- end }}
{{- end -}}
+517 -280
View File
@@ -1,464 +1,701 @@
---
# Основные настройки приложения HRBox
app:
# Docker образ приложения HRBox
# Получите доступ к реестру у сотрудников HRBox
image: "cr.yandex/crphnph9c2s776j32sjd/hrbox:latest"
# Конфигурация 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.* два ключа плеера курсов
#
# Если что-то из этого не заполнено, helm остановится и напишет, чего не
# хватает. Команды генерации ключей — в README, раздел «Секреты».
# Настройки домена
# defaultTenantHostname - основной домен вашей HRBox инсталляции
# Например: hrbox.company.com
# Должен совпадать с ingress.host
# Базовое имя объектов. Пусто — имя чарта: hrbox-web, hrbox-worker и т.д.
# На уже работающей установке менять нельзя.
nameOverride: ""
# --- Образы ---------------------------------------------------------------
image:
# Приложение: 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"
# Плеер курсов. Нужен конкретный тег, latest не подходит:
# из тега берётся версия плеера.
nanabushPlayer: "cr.yandex/crphnph9c2s776j32sjd/nanabush-player:1.0.11"
# Инфраструктура
dragonfly: "cr.yandex/crpnn6fi85p3dauha6nc/dragonflydb:1.15.1"
nats: "nats:2.10"
natsConfigReloader: "natsio/nats-server-config-reloader:0.6.2"
phpFpmExporter: "cr.yandex/crpnn6fi85p3dauha6nc/php-fpm_exporter:latest"
# Только для джобы миграций: ждёт готовности базы
postgresClient: "postgres:14-alpine"
# Только при app.tracing.enabled
otelCollector: "otel/opentelemetry-collector:0.109.0"
pullPolicy: IfNotPresent
# Secret с доступом к реестру образов. Создаётся отдельно, см. README, шаг 1.
imagePullSecrets:
- name: regsecret
# --- Приложение -----------------------------------------------------------
app:
# Домен установки. Должен совпадать с ingress.host.
defaultTenantHostname: "hrbox.example.com"
# Уникальное имя инстанса для идентификации
# Имя установки, видно в логах и метриках
instanceName: "onprem"
# Включение/отключение frontend2.
# "true" - включено
# "false" или пустое значение - отключено
# "1" — установка обслуживает одну компанию
singleTenant: "0"
# "true" — новый интерфейс, "false" — старый
front2: "true"
# Домен для авторизации
# Оставьте пустым
# Отдельный домен авторизации. Для on-premise обычно пусто.
authHost: ""
# Режим отладки
# "0" - production режим (рекомендуется)
# "1" - debug режим (только для разработки)
# "0" — production, "1" — отладка
debug: "0"
# Количество реплик для каждого сервиса
# Рекомендации по масштабированию:
# - web: 2-10 реплик в зависимости от количества пользователей
# - worker: 2-5 реплик для обработки фоновых задач
# - chatbox: 2-6 реплик для real-time функционала (WebSocket)
# - geonames: 1-2 реплики (сервис не требователен к ресурсам)
# Подставлять https во всех ссылках, которые генерирует приложение
forceHttps: "true"
# Число подов каждого сервиса.
# web — по числу пользователей, worker — по объёму фоновых задач,
# chatbox — по числу открытых вкладок, остальным обычно хватает 1-2.
replicas:
web: 2
worker: 2
chatbox: 2
geonames: 1
goworker: 2
kedoca: 1
fileProcessor: 1
fileProcessorVideo: 1
# Показ собственной политики конфиденциальности и terms of use
# privacy_url и terms_url передаются в приложение только если enabled установлена в true
# если enabled = true, но privacy_url и terms_url закомментированы,
# то будут использоваться ссылки https://hrbox.io/policy и https://hrbox.io/terms
custom_policy:
# Свои ссылки на политику конфиденциальности и правила использования.
# false — показываются ссылки HRBox.
customPolicy:
enabled: false
# privacy_url: "https://example.com/privacy"
# terms_url: "https://example.com/terms"
# 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, в спеке подов пароля нет
password: ""
# Использование Citus для шардирования
# "true" - если используете Citus расширение
# "false" - для обычного PostgreSQL
# "true", если база на Citus (шардирование)
citus: "false"
# Redis для кеша и сессий
# Потеря данных не критична - хранятся только сессии и кеш
# При перезапуске Redis все пользователи будут разлогинены
# --- Redis / Dragonfly --------------------------------------------------
# Сессии, кеш и очереди плеера курсов.
# Пусто — используется Dragonfly из этого же релиза.
# Адрес указывают, если Redis внешний (тогда обычно dragonfly.enabled: false).
redis:
host: "hrbox-dragonfly"
host: ""
port: "6379"
# NATS JetStream - брокер сообщений
# Для отказоустойчивости используйте кластер из 3+ нод
# Формат для кластера: nats://node1:4222,nats://node2:4222,nats://node3:4222
# --- NATS ---------------------------------------------------------------
# Пусто — используется NATS из этого же релиза.
# Внешний кластер: nats://node1:4222,nats://node2:4222,nats://node3:4222
nats:
url: "nats://hrbox-nats:4222"
url: ""
# S3-совместимое хранилище для файлов
# Поддерживаются: AWS S3, MinIO, Yandex Object Storage, VK Cloud Storage
# --- S3 -----------------------------------------------------------------
# Подойдёт AWS S3, MinIO, Yandex Object Storage, VK Cloud Storage.
s3:
# URL endpoint вашего S3
# Примеры:
# - MinIO: http://minio:9000
# - Yandex: https://storage.yandexcloud.net
# - VK Cloud: https://hb.vkcloud-storage.ru
# MinIO: http://minio:9000
# Yandex: https://storage.yandexcloud.net
# VK: https://hb.vkcloud-storage.ru
endpoint: "https://storage.example.com"
# Название bucket для хранения файлов
bucket: "hrbox"
# Access Key для доступа к S3
key: ""
# Secret Key для доступа к S3
secret: ""
# Регион S3 (обычно us-east-1 для MinIO)
region: "us-east-1"
# Версия API (оставьте "latest")
version: "latest"
# Использовать path-style URLs
# "true" для MinIO и self-hosted S3
# "false" для AWS S3
# "true" для MinIO и своего S3, "false" для AWS S3
pathStyle: "true"
# Включить multipart загрузку для больших файлов
multipart: "true"
# Префикс для всех объектов (опционально)
prefix: ""
# SMTP настройки для отправки email
# --- Почта --------------------------------------------------------------
smtp:
# SMTP сервер
host: "smtp.example.com"
# Порт SMTP
# 25 - без шифрования
# 587 - STARTTLS
# 465 - SSL/TLS
# 25 — без шифрования, 587 — STARTTLS, 465 — SSL/TLS
port: "587"
# Логин для SMTP авторизации
user: "noreply@example.com"
# Пароль для SMTP
password: ""
# Email отправителя
from: "noreply@example.com"
# Домен отправителя для HELO/EHLO
fromHost: "example.com"
# Тип шифрования: "", "tls", "ssl"
# "", "tls" или "ssl"
securityType: "tls"
# Разрешить самоподписанные сертификаты
sslAllowSelfSigned: "true"
# Проверять SSL сертификат сервера
sslVerifyPeer: "false"
# Sentry для мониторинга ошибок
# --- Sentry -------------------------------------------------------------
sentry:
# DSN для отправки ошибок
dsn: ""
# Окружение для группировки ошибок
environment: "production"
# Пусто — используется общий dsn
launchpadDsn: ""
conductorDsn: ""
# Здесь пусто означает «не отправлять»
goworkerDsn: ""
chatboxDsn: ""
# Настройки сервиса геолокации
# --- Внутренние сервисы -------------------------------------------------
# Пустой url — сервис берётся из этого же релиза.
# Заполняют, если сервис вынесен наружу.
geonames:
# URL внутреннего сервиса geonames
# Не меняйте, если используете стандартный деплой
url: "http://hrbox-geonames:8181"
url: ""
# Goworker
goworker:
# URL внутреннего сервиса goworker
url: "http://hrbox-goworker:8686"
# Docker образ goworker
image: "cr.yandex/crphnph9c2s776j32sjd/hrbox-goworker:2.7"
# Количество реплик
replicas: 2
# Минимальный интервал между push
url: ""
# Не чаще одного push одному пользователю за этот интервал
pushMinInterval: "5m"
# Sentry DSN для мониторинга ошибок
sentryDsn: ""
# Kedoca - сервис электронной подписи (ГОСТ, OpenSSL)
kedoca:
# URL внутреннего сервиса kedoca
url: "http://hrbox-kedoca:8558"
# Docker образ kedoca
image: "cr.yandex/crphnph9c2s776j32sjd/hrbox-kedoca:0.8"
# Количество реплик
replicas: 1
# Пароль для корневого сертификата
url: ""
# Пароль корневого сертификата, который сервис создаёт при первом запуске
rootCaPass: "hrbox"
# Криптографический движок: "gost" или "openssl"
# "gost" (КриптоПро) или "openssl"
engine: "gost"
# Алгоритм: "gost2012_256" для GОСТ или "rsa" для OpenSSL
# "gost2012_256" для ГОСТ, "rsa" для OpenSSL
algorithm: "gost2012_256"
# Параметры ключа: "paramset:A" для ГОСТ или "rsa_keygen_bits:2048" для RSA
# "paramset:A" для ГОСТ, "rsa_keygen_bits:2048" для RSA
pkeyopt: "paramset:A"
# Имя Kubernetes Secret с сертификатами
# Если указано, будет смонтирован в /ca-cert
# Служба меток времени
tspUrl: "http://qs.cryptopro.ru/tsp/tsp.srf"
# Secret с сертификатами УЦ. Если задан, монтируется в /ca-cert.
secretName: ""
# File Processor - обработка файлов (конвертация, ресайз изображений)
file_processor:
# Docker образ file processor
image: "cr.yandex/crphnph9c2s776j32sjd/hrbox-file-processor:1.0"
# Количество реплик
replicas: 1
fileProcessor:
# Сколько документов конвертировать одновременно в одном поде
docconvMaxParallelTasks: "1"
# Сколько изображений обрабатывать одновременно в поде конвертации видео
imageResizeMaxParallel: "20"
# Chatbox - WebSocket сервер для real-time функционала
# --- Конвертация видео --------------------------------------------------
# Отдельные поды: видео требует много CPU и диска, поэтому его выносят
# на выделенные ноды.
videoConverter:
enabled: false
# Внутренний адрес HRBox, откуда конвертер берёт и куда отдаёт файлы.
# Пусто — http://<релиз>-web.
internalUrl: ""
# На каких нодах запускать. Пусто — на любых.
# Пример: role: converter-dedicated
nodeSelector: {}
# Пример:
# - key: dedicated
# operator: Equal
# value: video-converter
# effect: NoSchedule
tolerations: []
# --- Чат ----------------------------------------------------------------
chatbox:
# Docker образ chatbox
image: "cr.yandex/crphnph9c2s776j32sjd/hrbox-chatbox:3.0"
# Wrapping key - часть ключа для ассиметричного шифрования чатов
# Сгенерируйте произвольную строку из 32 символов
# openssl rand -hex 16
# Часть ключа шифрования чатов, 32 символа.
# Генерация: openssl rand -hex 16
wrappingKey: ""
# Sentry DSN для мониторинга ошибок
sentryDsn: ""
# Настройки воркеров Launchpad
# Формат: "название-топика:количество-воркеров"
# --- Видеозвонки --------------------------------------------------------
livekit:
url: ""
apiKey: ""
apiSecret: ""
# --- Nanabush Player ----------------------------------------------------
# Плеер учебных материалов: SCORM 1.2 и 2004, cmi5, xAPI, нативные курсы,
# тесты и эссе.
#
# В облаке плеер стоит на отдельном поддомене, в on-premise поддомена нет,
# поэтому он живёт на основном домене по пути basePath. Домен общий, значит
# cookie HRBox доступны содержимому курсов. Если нужна изоляция сторонних
# SCORM-пакетов, обратитесь в HRBox за схемой с отдельным поддоменом.
nanabushPlayer:
# false — материалы открываются старым проигрывателем
enabled: true
# Путь на основном домене. Корень "/" занят самим HRBox.
basePath: "/nanabush-player"
# Issuer launch-тикета. Пусто — https://<ingress.host>/lms/player
issuer: ""
contractVersion: "v0-current"
# Оба ключа обязательны и должны быть разными.
# Генерация каждого: openssl rand -base64 48
keys:
ticketHs256: ""
sessionHs256: ""
# Набор ключей для ротации без простоя, необязательный:
# "kid1:секрет1,kid2:секрет2". Первым идёт ключ, которым подписывают
# сейчас. Запятая внутри секрета недопустима, используйте base64url.
ticketKeys: ""
rollout:
# Какая доля запусков идёт в новый плеер. 100 — все.
percent: 100
# Открывать материал старым проигрывателем, если плеер не ответил
legacyFallback: true
allowedContentTypes:
- document
- presentation
- video
- audio
- scorm
- cmi5
- xapi
- course
- native
- native_quiz
- native_essay
# "*" — все компании, включая новые
allowedTenants:
- "*"
service:
# Пусто — <релиз>-nanabush-player
name: ""
port: 8094
# Публиковать ли путь плеера на общем Ingress.
# Выключают только при откате.
ingress:
enabled: true
deployment:
# Создавать ли Deployment и Service плеера
enabled: true
replicas: 1
terminationGracePeriodSeconds: 120
# Умеет ли образ плеера работать не в корне домена, а по пути.
# Со старым образом без такой поддержки поставьте false — чарт
# остановит установку вместо того, чтобы развернуть нерабочий плеер.
binaryCapabilities:
basePath: true
# Secret с ключами плеера.
# create: false — Secret заводят вне чарта, чарт только ссылается.
existingSecret:
create: true
name: "hrbox-nanabush-player"
keys:
ticketHs256: player-hs256-secret
sessionHs256: player-session-hs256-secret
redisUrl: redis-url
ticketKeys: ""
ticket:
# Сколько живёт launch-тикет, секунды
ttlSeconds: 300
# Сколько живут учётные данные обратного канала: сессия плеера плюс
# 2 минуты на расхождение часов
credentialTtlSeconds: 28920
maxLifetime: "5m"
sessionTtl: "8h"
# Нужно только при первом переходе на набор ключей: до какого момента
# (RFC3339) принимать тикеты без kid. Пусто — не принимать.
keylessAcceptedUntil: ""
redis:
# false — адрес собирается из app.redis.
# true — полный redis:// или rediss:// URL берётся из внешнего Secret
# (нужно для пароля, ACL или TLS), требует existingSecret.create: false
urlFromExistingSecret: false
database: 0
# Полный префикс: hrbox:<namespace>:<суффикс>.
# Разделяет несколько установок HRBox на одном Redis.
keyPrefixSuffix: "nanabush-player"
# Обращения плеера обратно в HRBox: за пакетом курса и с отчётами
backchannel:
# Внутренний адрес HRBox. Пусто — <релиз>-web.
host: ""
schemes: "http"
allowPrivateIps: true
packageFetchMaxBytes: "5242880"
# Должно вмещать самый большой медиафайл курса
packageContentMaxBytes: "2147483648"
scormPackageMaxBytes: "1073741824"
# Пусто — берётся значение приложения (4000 файлов)
scormPackageMaxFiles: ""
packageFetchTimeout: "15s"
# Считается до конца передачи файла в браузер учащегося, поэтому
# зависит от скорости его интернета
packageStreamTimeout: "20m"
reportTimeout: "30s"
# Очередь отправки учебных событий в HRBox
forwarder:
queueSize: "1000"
workers: "4"
maxAttempts: "5"
initialBackoff: "1s"
maxBackoff: "30s"
perAttemptTimeout: "30s"
deadLetterSize: "256"
claimIdle: "5m"
pollInterval: "1s"
rateLimitEventsPerMinute: "600"
rateLimitBatchesPerMinute: "120"
rateLimitPayloadBytesPerMinute: "8388608"
log:
level: info
format: json
# Метрики отдаются на отдельном порту и наружу не публикуются
metrics:
enabled: true
port: 9090
probes:
readiness:
path: /readyz
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 6
liveness:
path: /healthz
initialDelaySeconds: 20
periodSeconds: 20
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"
# Интеграция с Yandex (опционально)
sms:
senderName: "HRBOX"
# --- Внешние интеграции -------------------------------------------------
integrations:
telegram:
# Если прямого доступа к Telegram нет, укажите адрес прокси
apiUrl: "https://api.telegram.org/bot"
google:
enabled: "false"
# clientId уходит в браузер, clientSecret попадает в Secret
clientId: "692956564858-ccapt35khk7tmadl17b3879ht2cropp6.apps.googleusercontent.com"
clientSecret: ""
glagol:
partnerId: "ab230ee1-6946-43b9-8kfa-447beb8348b0"
apiUrl: "https://api.glagol.online/glagol-api"
# Привязка мобильных приложений к домену: без неё ссылки из писем и пушей
# открываются в браузере, а не в приложении. По умолчанию указаны
# приложения HRBox, меняйте только для своей сборки.
mobileApps:
android:
- packageName: "io.hrbox.hrboxapp"
sha256CertFingerprints:
- "DE:75:B2:CA:CB:50:96:02:8D:E1:07:4F:CE:E9:92:1C:4C:ED:87:B0:64:12:1A:69:27:7B:D2:B5:1A:23:31:70"
apple:
appIds:
- "DX67FD7GQJ.io.hrbox.app"
yandex:
# Client ID для Yandex OAuth
clientId: ""
# Client Secret для Yandex OAuth
clientPassword: ""
# Интеграция со Slack (опционально)
slack:
# Slack App Client ID
clientId: ""
# Slack App Client Secret
clientSecret: ""
# Slack Verification Token
verificationToken: ""
# HRBox Hub
# --- HRBox Hub ----------------------------------------------------------
# Лицензирование и общие сервисы. Токен выдают сотрудники HRBox.
hub:
# URL Hub сервиса
url: "https://hub.hrbox.io"
# Токен для авторизации в Hub, получите от сотрудника HrBox
token: ""
# AI интеграции (опционально)
# --- AI -----------------------------------------------------------------
ai:
# Yandex Cloud AI
docsIndexEnabled: "true"
semanticIndexEnabled: "true"
semanticIndexUseBatch: "true"
yandex:
# ID каталога в Yandex Cloud
folderId: ""
# API ключ для Yandex Cloud
apiKey: ""
# Настройки безопасности
# --- Трассировка --------------------------------------------------------
# Включает OpenTelemetry в PHP и коллектор рядом с подом web
tracing:
enabled: false
exporterUrl: "http://localhost:4318"
# Куда коллектор отправляет трейсы: Tempo, Jaeger
tempoUrl: ""
# --- Безопасность -------------------------------------------------------
security:
# Разрешить полный доступ к iframe
# "true" - разрешает iframe полный доступ к родительскому окну
# "false" - ограничивает доступ iframe (рекомендуется для production)
# "true" даёт встроенному iframe полный доступ к родительскому окну.
# Для production лучше "false".
iframeFullAccess: "false"
# Секретные ключи приложения
# --- Секреты --------------------------------------------------------------
# Эти значения вместе с паролями из app.* складываются в один Kubernetes
# Secret, поды читают их оттуда.
secrets:
# Ключ валидации cookie (32 символа)
# Используется для защиты от XSS атак
# Имя Secret, созданного вне чарта. Если задано, чарт свой Secret не
# создаёт и берёт ключи из указанного. Список ключей — в README.
existingSecret: ""
# Защита cookie от подделки, 32 символа.
# Генерация: openssl rand -hex 16
cookieValidationKey: ""
# Ключ шифрования данных (64 символа)
# ВАЖНО: Не теряйте этот ключ! Без него невозможно расшифровать данные
# Используется для шифрования паролей интеграций (S3, ADFS и др.)
# Шифрование паролей интеграций в базе.
# Без этого ключа данные не расшифровать, храните копию отдельно.
# Генерация: openssl rand -base64 48
encryptionKey: ""
# Секрет для внутренней коммуникации микросервисов (64 символа)
# Используется для защиты внутренних API вызовов между сервисами
# Проверка вызовов между сервисами внутри релиза.
# Генерация: openssl rand -base64 48
servicesInternalSecret: ""
# Настройки Ingress
# --- Ingress --------------------------------------------------------------
ingress:
# Включить Ingress
enabled: true
# Класс Ingress контроллера
# Обычно: "nginx" или "traefik"
# Обычно "nginx" или "traefik"
className: "nginx"
# Хост для доступа к приложению
# Используйте wildcard для мультитенантности: *.hrbox.company.com
# Или конкретный домен: hrbox.company.com
# Домен установки. Для мультиарендности можно wildcard: *.hrbox.company.com
host: "hrbox.example.com"
# Настройки TLS/HTTPS
tls:
# Включить HTTPS
# ВАЖНО: В production всегда используйте HTTPS
enabled: true
# Дополнительные аннотации ingress-контроллера
annotations: {}
# Имя Kubernetes Secret с SSL сертификатом
# Создайте через: kubectl create secret tls hrbox-tls --cert=cert.pem --key=key.pem
# Или используйте cert-manager для автоматического получения сертификатов
# Максимальный размер загружаемого файла.
# У загрузки оргструктуры из 1С отдельный лимит, он выше.
maxBodySize: "10m"
tls:
enabled: true
# kubectl create secret tls hrbox-tls --cert=cert.pem --key=key.pem
# или выпустить сертификат через cert-manager
secretName: "hrbox-tls"
# --- Кластер --------------------------------------------------------------
cluster:
domain: "cluster.local" # Укажите домен текущего k8s-кластера, если используется нестандартный
# Домен служебных имён Kubernetes. Менять только если он нестандартный.
domain: "cluster.local"
# Настройки ресурсов для контейнеров
# Указаны рекомендуемые значения для production
# --- Ресурсы --------------------------------------------------------------
# Значения близки к тем, что используются в облаке HRBox.
# Можно оставить только requests: незаполненные поля в манифест не попадают.
resources:
# Web-сервер (PHP-FPM + Nginx)
# Обрабатывает HTTP запросы, API, веб-интерфейс
# PHP-FPM: веб-интерфейс и API
app:
requests:
cpu: "0.5" # Минимум 0.5 CPU
memory: "512Mi" # Минимум 512MB RAM
cpu: "1"
memory: "2Gi"
ephemeralStorage: "64Mi"
limits:
cpu: "2" # Максимум 2 CPU
memory: "2Gi" # Максимум 2GB RAM
ephemeralStorage: "256Mi"
# Nginx (в составе web pod)
# Проксирует запросы к PHP-FPM
nginx:
requests:
cpu: "0.1"
memory: "64Mi"
ephemeralStorage: "64Mi"
limits:
cpu: "0.5"
memory: "256Mi"
ephemeralStorage: "64Mi"
# Worker - обработчик фоновых задач
# Выполняет: конвертацию файлов, отправку email, генерацию отчетов
worker:
requests:
cpu: "0.5"
memory: "512Mi"
ephemeralStorage: "1Gi" # Больше места для обработки файлов
limits:
cpu: "2"
memory: "2Gi"
ephemeralStorage: "2Gi"
# Geonames - сервис геолокации
# Определяет города, страны, часовые пояса
geonames:
# nginx в поде web
nginx:
requests:
cpu: "0.1"
memory: "196Mi"
memory: "256Mi"
ephemeralStorage: "64Mi"
limits:
cpu: "0.5"
memory: "256Mi"
ephemeralStorage: "64Mi"
ephemeralStorage: "512Mi"
# Conductor - планировщик задач
# ВАЖНО: Всегда должен быть в единственном экземпляре!
# Запускает задачи по расписанию (cron)
conductor:
# Фоновые задачи: файлы, почта, отчёты
worker:
requests:
cpu: "0.1"
memory: "64Mi"
ephemeralStorage: "64Mi"
limits:
cpu: "0.5"
memory: "256Mi"
ephemeralStorage: "64Mi"
# Jobs - одноразовые задачи (миграции, инициализация)
jobs:
requests:
cpu: "0.5"
memory: "512Mi"
ephemeralStorage: "64Mi"
cpu: "0.6"
memory: "640Mi"
ephemeralStorage: "4Gi"
limits:
cpu: "2"
memory: "2Gi"
ephemeralStorage: "256Mi"
memory: "4Gi"
ephemeralStorage: "8Gi"
# Goworker - интеграции с мессенджерами
goworker:
# Планировщик задач по расписанию
conductor:
requests:
cpu: "0.1"
memory: "196Mi"
cpu: "0.25"
memory: "128Mi"
ephemeralStorage: "64Mi"
limits:
cpu: "0.3"
memory: "256Mi"
cpu: "0.7"
memory: "384Mi"
ephemeralStorage: "64Mi"
# Kedoca - электронная подпись
kedoca:
requests:
cpu: "0.1"
memory: "196Mi"
ephemeralStorage: "64Mi"
limits:
cpu: "0.5"
memory: "256Mi"
ephemeralStorage: "64Mi"
# Chatbox - WebSocket сервер
chatbox:
requests:
cpu: "0.3"
memory: "96Mi"
ephemeralStorage: "128Mi"
limits:
cpu: "0.3"
memory: "192Mi"
cpu: "1"
memory: "256Mi"
ephemeralStorage: "256Mi"
# Настройки NATS JetStream
geonames:
requests:
cpu: "0.1"
memory: "196Mi"
ephemeralStorage: "64Mi"
limits:
cpu: "0.5"
memory: "256Mi"
ephemeralStorage: "64Mi"
goworker:
requests:
cpu: "0.1"
memory: "196Mi"
ephemeralStorage: "64Mi"
limits:
cpu: "0.5"
memory: "256Mi"
ephemeralStorage: "64Mi"
kedoca:
requests:
cpu: "0.1"
memory: "196Mi"
ephemeralStorage: "64Mi"
limits:
cpu: "1"
memory: "256Mi"
ephemeralStorage: "64Mi"
fileProcessor:
requests:
cpu: "0.5"
memory: "512Mi"
ephemeralStorage: "2Gi"
limits:
cpu: "1"
memory: "1Gi"
ephemeralStorage: "2Gi"
# Конвертация видео: тяжёлые поды на отдельных нодах
fileProcessorVideo:
requests:
cpu: "4"
memory: "4Gi"
ephemeralStorage: "40Gi"
limits:
cpu: "8"
memory: "8Gi"
ephemeralStorage: "44Gi"
nanabushPlayer:
requests:
cpu: "0.1"
memory: "192Mi"
limits:
memory: "512Mi"
dragonfly:
requests:
cpu: "0.1"
memory: "256Mi"
limits:
cpu: "1"
memory: "1Gi"
nats:
# Включить NATS (обязательно для работы HRBox)
requests:
cpu: "0.1"
memory: "256Mi"
limits:
cpu: "1"
memory: "2Gi"
# Разовые задачи: миграции и обновление кластера
jobs:
requests:
cpu: "0.5"
memory: "1Gi"
ephemeralStorage: "64Mi"
limits:
cpu: "2"
memory: "4Gi"
ephemeralStorage: "256Mi"
# --- NATS -----------------------------------------------------------------
nats:
# HRBox без брокера не работает. Выключают, только если NATS ставят
# отдельно и указали его адрес в app.nats.url.
enabled: true
# Docker образ NATS
image: "nats:2.10"
# Количество реплик NATS
# Для отказоустойчивости используйте 3 или 5
# Для отказоустойчивости 3 или 5. Должно совпадать с cluster.replicas.
replicas: 3
# JetStream - персистентные очереди сообщений
jetstream:
enabled: true
# Хранилище в памяти (для небольших инсталляций)
# Хранение в памяти: подходит небольшим установкам
memStorage:
enabled: true
size: "2Gi"
# Хранилище на диске (для production)
# Хранение на диске: для production
fileStorage:
enabled: false
size: "4Gi"
storageDirectory: "/data"
# StorageClass для PVC
# Используйте SSD для лучшей производительности
# JetStream нужен быстрый диск
storageClassName: "fast-ssd"
# Настройки кластера NATS
cluster:
# Включить кластеризацию для отказоустойчивости
enabled: true
# Должно совпадать с replicas
# Должно совпадать с nats.replicas
replicas: 3
# Prometheus exporter (для мониторинга)
exporter:
enabled: false
# Config reloader - автоматическая перезагрузка конфигурации
# Подхватывает изменения конфигурации без перезапуска NATS
reloader:
enabled: true
# Время ожидания graceful shutdown в секундах
terminationGracePeriodSeconds: 120
# --- Dragonfly ------------------------------------------------------------
# Redis-совместимый кеш.
dragonfly:
# Включает или отключает развёртывание Dragonfly (Redis).
# Установите в true, чтобы активировать, или в false, чтобы отключить.
# false — если Redis ставят отдельно; тогда заполните app.redis.host
enabled: true
# По умолчанию параметр не используется. По необходимости, раскомментируйте и установите требуемое значение maxmemory
# Ограничение памяти
# maxmemory: "256M"
+262
View File
@@ -5,6 +5,267 @@ All notable changes to the HRBox Helm chart will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [3.0.0] - 2026-09-02
Чарт подтянут к тому, как HRBox работает в облаке: добавлен плеер учебных
материалов, пароли и ключи уехали в Secret, повторяющиеся куски шаблонов
заменены одним хелпером, а неполная конфигурация теперь останавливает
установку с понятным сообщением.
### Added
#### Nanabush Player
Плеер учебных материалов: SCORM 1.2 и 2004, cmi5, xAPI, нативные курсы,
тесты и эссе.
- `09-nanabush-player.yaml` — Deployment и Service, образ `nanabush-player:1.0.11`
- `00-nanabush-player-secret.yaml` — ключи launch-тикета и сессии
- маршрут `/nanabush-player` на общем Ingress
- настройки `app.nanabushPlayer`, ресурсы `resources.nanabushPlayer`
- переменные `NANABUSH_PLAYER_*` в HRBox и `PLAYER_*` в самом плеере
В on-premise выделенного поддомена нет, поэтому плеер стоит на основном
домене по пути. Отсюда два ограничения, они описаны в README: cookie HRBox
доступны содержимому курсов, и нужны синхронизированные часы — тикет живёт
5 минут.
Для быстрого отката оставлены три переключателя: `enabled`,
`ingress.enabled`, `deployment.enabled`. Маршрут без пода или включённый
плеер без маршрута чарт не пропустит.
#### Проверка конфигурации
- `00-validate.yaml` — объектов не создаёт, но останавливает установку, если
не заполнены обязательные значения, остались ключи из версии 2.x или
нарушены требования плеера. Сообщения на русском, с указанием, что править.
#### Secret приложения
- `00-app-secret.yaml` — один Secret со всеми паролями и ключами. Поды
читают его через `secretKeyRef`, поэтому `kubectl get deploy -o yaml`
больше не показывает пароль базы и ключ шифрования.
- `secrets.existingSecret` — можно подставить Secret, созданный вне чарта.
#### Новые компоненты
- `09-file-processor-video.yaml` — отдельные поды для конвертации видео,
со своими ресурсами, `nodeSelector` и `tolerations`
(`app.videoConverter.enabled`, по умолчанию выключено)
- `02-web-pdb.yaml` — PodDisruptionBudget для `web`, создаётся от двух реплик
- `01-otel-configmap.yaml` и коллектор OpenTelemetry рядом с `web`
(`app.tracing.enabled`)
- `01-nginx-assetlinks.yaml` — привязка мобильных приложений к домену
(`app.mobileApps`)
- `templates/NOTES.txt` — что делать после установки, с готовыми командами
#### Новые переменные окружения
- `NATS_STREAM_REPLICAS` — число реплик стримов JetStream по размеру кластера
- `TELEGRAM_API_URL`, `AUTH_GOOGLE_ENABLED`
- `LIVEKIT_URL`, `LIVEKIT_API_KEY`, `LIVEKIT_API_SECRET` — видеозвонки
- `APP_INTERNAL_URL` — внутренний адрес HRBox для конвертера
- `VIDEO_CONVERTER_ENABLED` — отдельно для каждого пула
- `SENDER_ENV`, `IMAGE_RESIZE_MAX_PARALLEL`, `OTEL_*`
### Changed
#### values.yaml
- все образы собраны в блок `image`: сменить версию сервиса — это правка
одной строки
- `imagePullSecrets` вынесен в настройки, `regsecret` больше не зашит
в шаблоны
- ключи приведены к camelCase: `file_processor` → `fileProcessor`,
`custom_policy` → `customPolicy`
- в `resources` добавлены `fileProcessor`, `fileProcessorVideo`,
`nanabushPlayer`, `dragonfly`, `nats`
- адреса внутренних сервисов (`app.geonames.url`, `app.goworker.url`,
`app.kedoca.url`, `app.redis.host`, `app.nats.url`) по умолчанию пустые
и собираются из имён, которые создаёт сам чарт. Раньше это были строки
`hrbox-*`, зашитые в шаблоны: при `nameOverride` они молча переставали
работать. Заданное значение по-прежнему используется как есть, поэтому
старые values работают без правок. Выключить `dragonfly` или `nats`,
не указав внешний адрес, теперь нельзя.
#### Шаблоны
- `_resources.tpl`: девять почти одинаковых define заменены одним
`hrbox.resources`, незаполненные поля не рендерятся
- `_helpers.tpl`: удалено около сотни строк неиспользуемого кода (хелперы
`nats.*`, которые ссылались на несуществующий `nats.k8sClusterDomain`);
добавлены `hrbox.component`, `hrbox.labels`, `hrbox.selectorLabels`,
`hrbox.secretEnv` и хелперы плеера
- у всех объектов появились стандартные лейблы `app.kubernetes.io/*`;
лейбл `app` остался, привычный `kubectl get pods -l app=worker` работает
- селекторы Deployment не менялись, обновление 2.x → 3.0 не пересоздаёт
объекты
- аннотации `checksum/*` перезапускают поды при изменении конфигурации
nginx или Secret приложения
- образы, порты и таймауты вынесены из шаблонов в values.yaml
#### nginx
Набор `location` синхронизирован с облачным:
- `/healthz` и `/health-check` — на них настроены пробы пода `web`
(раньше проверялся только TCP-порт)
- `/api/v1/integration/commit` — потоковая загрузка оргструктуры из 1С
без буферизации, лимит 500 МБ
- `/ai/` — увеличенные таймауты
- `/.well-known/assetlinks.json` и `/.well-known/apple-app-site-association`
- `/frontend/sw.js` — правильные заголовки service worker
- кеширование шрифтов
- убраны TLSv1 и TLSv1.1
#### Версии образов
| Компонент | Было | Стало |
|---|---|---|
| chatbox | 3.0 | 5.18 |
| goworker | 2.7 | 3.21.2 |
| kedoca | 0.8 | 1.0.6 |
| geonames | 3.1 | 3.3 |
| file-processor | 1.0 | 1.4.6 |
| nanabush-player | — | 1.0.11 |
#### Джобы
- в имени появился номер ревизии релиза: в `kubectl get jobs` видно, какой
выкат запускал миграции, а повторный `upgrade` не спотыкается о старый
объект
- `ttlSecondsAfterFinished` — час у обеих джоб (было 100 секунд у миграций
и ничего у обновления кластера), логи упавшей джобы успевают дожить до
того, как их посмотрят
#### Переименования файлов
- `03-websocket.yaml` → `03-chatbox.yaml` (внутри был chatbox с версии 2.0)
- `09-geonames.yml` → `09-geonames.yaml`
- `99-update-cluser-job.yaml` → `99-update-cluster-job.yaml`
### Fixed
- **Общий секрет у всех установок.** У `SERVICES_INTERNAL_SECRET` было
значение по умолчанию, зашитое в чарт: все, кто его не заполнил, работали
на одном и том же секрете межсервисной аутентификации. Теперь значение
обязательное.
- **`cluster.domain` не использовался.** Маршруты кластера NATS были зашиты
на `cluster.local`, хотя параметр в values был и применялся в соседней
переменной. На кластере с другим доменом ноды не находили друг друга.
- **`CLUSTER_ADVERTISE` без сегмента `svc`.** Имя не резолвилось, ноды NATS
сообщали соседям недостижимый адрес.
- **Разные значения реплик NATS.** `nats.replicas` и `nats.cluster.replicas`
задавались независимо, при расхождении часть нод не входила в кластер.
Теперь чарт требует, чтобы они совпадали.
- **Пробы `web` не проверяли приложение.** Проверялся TCP-порт 9000, поэтому
под с неработающим PHP считался готовым. Теперь readiness ходит
в `/health-check` через FPM.
- Пароли и ключи больше не видны в спеках Deployment.
- У части сервисов `imagePullPolicy` был `Always` на фиксированных тегах —
лишние обращения к реестру при каждом рестарте пода.
### Removed
- у `secrets` больше нет значений по умолчанию: с пустыми ключами установка
останавливается, а не запускается на предсказуемых секретах
- удалены неиспользуемые хелперы `nats.name`, `nats.fullname`, `nats.chart`,
`nats.labels`, `nats.selectorLabels`, `nats.clusterAdvertise`,
`nats.clusterRoutes`, `nats.extraRoutes`, `nats.tlsConfig`,
`tplvalues.render`
- `SENDGRID_API_KEY` — в on-premise не используется
### Migration Guide
#### Переход с 2.x на 3.0
Все переехавшие ключи чарт узнаёт и пишет, куда именно они переехали,
поэтому проще всего запустить `helm template` и править по сообщениям.
**Шаг 1. Перенесите образы в блок `image`**
```yaml
# было
app:
image: "cr.yandex/.../hrbox:latest"
chatbox:
image: "cr.yandex/.../hrbox-chatbox:3.0"
goworker:
image: "cr.yandex/.../hrbox-goworker:2.7"
kedoca:
image: "cr.yandex/.../hrbox-kedoca:0.8"
file_processor:
image: "cr.yandex/.../hrbox-file-processor:1.0"
replicas: 1
# стало
image:
hrbox: "cr.yandex/.../hrbox:latest"
chatbox: "cr.yandex/.../hrbox-chatbox:5.18"
goworker: "cr.yandex/.../hrbox-goworker:3.21.2"
kedoca: "cr.yandex/.../hrbox-kedoca:1.0.6"
fileProcessor: "cr.yandex/.../hrbox-file-processor:1.4.6"
app:
replicas:
fileProcessor: 1
```
**Шаг 2. Переименуйте ключи**
| Было | Стало |
|---|---|
| `app.file_processor` | `app.fileProcessor` |
| `app.custom_policy` | `app.customPolicy` |
| `app.custom_policy.privacy_url` | `app.customPolicy.privacyUrl` |
| `app.custom_policy.terms_url` | `app.customPolicy.termsUrl` |
| `app.goworker.replicas` | `app.replicas.goworker` |
| `app.kedoca.replicas` | `app.replicas.kedoca` |
| `app.goworker.sentryDsn` | `app.sentry.goworkerDsn` |
| `app.chatbox.sentryDsn` | `app.sentry.chatboxDsn` |
| `nats.image` | `image.nats` |
**Шаг 3. Сгенерируйте ключи плеера**
```bash
openssl rand -base64 48 # app.nanabushPlayer.keys.ticketHs256
openssl rand -base64 48 # app.nanabushPlayer.keys.sessionHs256
```
Ключи должны быть разными.
**Шаг 4. Проверьте `secrets.servicesInternalSecret`**
Если поле было пустым, установка работала на общем значении по умолчанию.
Сгенерируйте своё: `openssl rand -base64 48`. После обновления перезапустятся
все сервисы, которые ходят друг к другу, — это нормально.
**Шаг 5. Проверьте рендер и обновитесь**
```bash
helm template hrbox .helm -f values.yaml > /dev/null
helm upgrade hrbox .helm -n hrbox -f values.yaml
```
**Шаг 6. Включите плеер**
Порядок включения и проверки — в README, раздел «Nanabush Player».
#### Breaking Changes
1. **Ключи `values.yaml` переехали.** Установка останавливается с подсказкой,
молча ничего не игнорируется.
2. **`secrets.*` стали обязательными.** Без них установка не пройдёт.
3. **Появился Nanabush Player.** Это дополнительный под, Service, Secret и
маршрут, учтите ресурсы кластера. Плееру нужно синхронизированное время
на нодах.
4. **Пробы `web` стали строже.** Под, где PHP-FPM не отвечает по HTTP, теперь
не проходит readiness. Если приложение отдаёт `/health-check` иначе,
проверьте это до обновления.
5. **В `app.replicas` добавились** `goworker`, `kedoca`, `fileProcessor`,
`fileProcessorVideo`; поля `replicas` внутри самих сервисов удалены.
---
## [2.0.0] - 2026-01-30
Масштабная актуализация чарта на основе продового окружения hrbox. Синхронизированы версии компонентов, добавлены новые микросервисы, обновлены переменные окружения.
@@ -227,5 +488,6 @@ kubectl get pods -n hrbox -l app=websocket # Должно быть пусто
---
[3.0.0]: #300---2026-09-02
[2.0.0]: #200---2026-01-30
[1.2.0]: #120---previous-version
+304 -43
View File
@@ -1,14 +1,47 @@
# HRBox Helm Chart
Helm-чарт для развертывания приложения HRBox в Kubernetes.
Чарт для установки HRBox в свой Kubernetes-кластер.
Сам чарт лежит в [`.helm`](.helm), настройки — в [`.helm/values.yaml`](.helm/values.yaml).
Каждый параметр там подписан, ниже — только то, что нужно сделать руками.
---
## Быстрый старт
## Что разворачивается
### 1. Создайте секрет для доступа к Docker-реестру
| Компонент | Что делает | Порт |
|---|---|---|
| `web` | PHP-FPM и nginx: веб-интерфейс и API | 80 |
| `worker` | фоновые задачи: файлы, почта, отчёты | — |
| `conductor` | задачи по расписанию, всегда 1 под | 8080 |
| `chatbox` | вебсокеты чата и уведомлений | 80 → 9797 |
| `nanabush-player` | плеер курсов: SCORM, cmi5, xAPI, нативные материалы | 8094 |
| `goworker` | мессенджеры, пуши, синхронизация оргструктуры с 1С | 8686 |
| `kedoca` | электронная подпись: КЭДО, МЧД | 8558 |
| `geonames` | справочник городов, стран, часовых поясов | 8181 |
| `file-processor` | конвертация документов и изображений | — |
| `file-processor-video` | конвертация видео, по умолчанию выключен | — |
| `dragonfly` | Redis-совместимый кеш и сессии | 6379 |
| `nats` | брокер сообщений JetStream | 4222 |
Для скачивания приватного Docker-образа создайте Kubernetes Secret с помощью предоставленного JSON-ключа:
Публичных маршрутов три, и все на одном домене: `/` — приложение,
`/chatbox/` — вебсокеты, `/nanabush-player` — плеер курсов.
## Что нужно подготовить
- Kubernetes 1.21+ и ingress-контроллер (nginx или traefik);
- PostgreSQL 14+ (с Citus, если планируется шардирование);
- S3-совместимое хранилище: AWS S3, MinIO, Yandex Object Storage, VK Cloud;
- SMTP-сервер;
- доступ к реестру образов HRBox и токен HRBox Hub — выдают сотрудники HRBox.
Базу и S3 чарт не разворачивает: эти хранилища содержат постоянные данные и должны быть развёрнуты вручную.
---
## Установка
### 1. Доступ к реестру образов
```bash
kubectl create secret docker-registry regsecret \
@@ -18,57 +51,285 @@ kubectl create secret docker-registry regsecret \
--docker-email=not@used.com
```
### 2. Создайте TLS-секрет с сертификатом для домена
### 2. TLS-сертификат
```bash
kubectl create secret tls hrbox-tls --cert=cert.pem --key=key.pem
```
### 3. Сгенерируйте секретные ключи приложения
Либо выпустите сертификат через cert-manager и укажите его Secret
в `ingress.tls.secretName`.
Для обеспечения безопасности необходимо сгенерировать и указать в `values.yaml` следующие ключи:
- **cookieValidationKey** — 32 байта в hex (например: `openssl rand -hex 16`)
- **encryptionKey** — 64 байта в base64 (например: `openssl rand -base64 48`)
- **servicesInternalSecret** — 64 байта в base64 (например: `openssl rand -base64 48`)
- **chatbox.wrappingKey** — 32 символа в hex (например: `openssl rand -hex 16`)
Эти ключи используются для защиты cookie, шифрования данных, межсервисной коммуникации и шифрования чатов.
### 4. Заполните обязательные параметры в `values.yaml`
Обязательно укажите в конфигурации:
- Параметры подключения к базе данных PostgreSQL (`app.postgres.host`, `port`, `database`, `user`, `password`).
- Настройки S3-совместимого хранилища (`app.s3.endpoint`, `bucket`, `key`, `secret`).
- SMTP-настройки для отправки почты (`app.smtp.host`, `port`, `user`, `password`, `from`, `fromHost`).
- Домен для приложения (`app.defaultTenantHostname`) и Ingress (`ingress.host`).
- Имя TLS-секрета для HTTPS (`ingress.tls.secretName`), созданного на шаге 2.
- Ключ для hrbox hub (предоставляется сотрудниками hrbox)
Прочие настройки и пример `values.yml` с подробным описанием ключей вы можете найти [тут](.helm/values.yaml)
### 5. Установите или обновите Helm-релиз
### 3. Ключи
```bash
helm upgrade --install hrbox . -f values.yaml
# 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
```
### 6. Дождитесь завершения миграций
`secrets.encryptionKey` шифруют пароли, в случае его утери придётся заново задавать пароли в настройках интеграций.
Job `hrbox-migrate-xxxxx` должна завершиться
Ключи плеера `ticketHs256` и `sessionHs256` должны быть разными, одинаковые чарт не примет.
### 7. При первом развертывании - создайте суперпользователя
### 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
kubectl get pods
NAME READY STATUS RESTARTS AGE
...
hrbox-worker-7898b77b85-5ppwm 1/1 Running 0 53s
hrbox-worker-7898b77b85-9fnkc 1/1 Running 0 53s
kubectl exec -it hrbox-worker-7898b77b85-5ppwm bash
php yii user/create-admin youremail@hrbox.io --tenant-id=1
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`,
поды читают их оттуда. В спеке Deployment паролей нет.
Если секретами управляет внешний инструмент, создайте Secret сами и укажите
имя в `secrets.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 на одном домене, поэтому 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%`),
чтобы вывод ноды из обслуживания не уронил приложение целиком.
### Конвертация видео
Выключена по умолчанию, потому что требует заметно больше ресурсов, чем
остальная обработка файлов. Включение:
```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).