This commit is contained in:
Vlad Bubnov
2025-05-27 08:03:13 +03:00
parent f47d4feeac
commit fb638f1988
21 changed files with 1639 additions and 68 deletions
+1
View File
@@ -0,0 +1 @@
charts/
+24
View File
@@ -0,0 +1,24 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.wait
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/
+19
View File
@@ -0,0 +1,19 @@
apiVersion: v2
name: hrbox
description: Simplified HRBox application Helm chart
type: application
# Версия самого чарта. Увеличивается при каждом изменении чарта
version: 1.0.0
# Версия приложения, которое деплоится. Не обязательно увеличивать при изменении чарта
appVersion: "1.0.0"
# Ключевые слова для поиска
keywords:
- hrbox
- hr
- application
# Домашняя страница проекта
home: https://hrbox.io
+58
View File
@@ -0,0 +1,58 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Chart.Name }}-nats-config
data:
nats.conf: |
# PID file shared with configuration reloader.
pid_file: "/var/run/nats/nats.pid"
###############
# #
# Monitoring #
# #
###############
http: 8222
server_name: $POD_NAME
{{- if .Values.nats.jetstream.enabled }}
###################################
# #
# NATS JetStream #
# #
###################################
jetstream {
{{- if .Values.nats.jetstream.memStorage.enabled }}
max_mem: {{ .Values.nats.jetstream.memStorage.size }}
{{- end }}
{{- if .Values.nats.jetstream.fileStorage.enabled }}
store_dir: {{ .Values.nats.jetstream.fileStorage.storageDirectory }}
max_file: {{ .Values.nats.jetstream.fileStorage.size }}
{{- end }}
}
{{- end }}
{{- if .Values.nats.cluster.enabled }}
###################################
# #
# NATS Full Mesh Clustering Setup #
# #
###################################
cluster {
port: 6222
name: {{ .Chart.Name }}-nats-cluster
routes = [
{{- range $i := until (int .Values.nats.cluster.replicas) }}
nats://{{ $.Chart.Name }}-nats-{{ $i }}.{{ $.Chart.Name }}-nats.{{ $.Release.Namespace }}.svc.cluster.local:6222,
{{- end }}
]
cluster_advertise: $CLUSTER_ADVERTISE
connect_retries: 120
}
{{- end }}
lame_duck_duration: 120s
+154
View File
@@ -0,0 +1,154 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: web-configuration
data:
nginx.conf: |
user www-data;
worker_processes 6;
pid /run/nginx.pid;
events {
worker_connections 10000;
use epoll;
multi_accept on;
}
http {
set_real_ip_from 10.0.0.0/8;
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 0;
types_hash_max_size 2048;
server_tokens off;
limit_conn_zone $binary_remote_addr zone=addr:10m;
server_names_hash_bucket_size 64;
server_name_in_redirect off;
fastcgi_buffer_size 256k;
fastcgi_buffers 4 256k;
client_body_buffer_size 1024k;
client_max_body_size 20m;
client_header_buffer_size 1024k;
large_client_header_buffers 16 1024k;
default_type application/octet-stream;
include /etc/nginx/mime.types;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
rewrite_log on;
##
# Gzip Settings
##
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
server {
listen 80 default;
server_name _ ;
root /app/web;
index index.php;
keepalive_timeout 70;
client_body_temp_path /nginx-tmp 1 2;
client_body_timeout 22s;
client_header_timeout 22s;
limit_conn addr 10000;
location ~ /\.(git) {
deny all;
}
location / {
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;
# 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;
add_header Vary Accept-Encoding;
add_header Pragma "public";
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
access_log off;
tcp_nodelay off;
try_files $uri $uri/ /index.php?$args;
open_file_cache max=3000 inactive=120s;
open_file_cache_valid 45s;
open_file_cache_min_uses 2;
open_file_cache_errors off;
}
#error_page 404 /404.html;
}
location ~ \.php$ {
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";
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
if (!-f $document_root$fastcgi_script_name) {
return 404;
}
fastcgi_pass localhost:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
include fastcgi_params;
fastcgi_read_timeout 20s;
fastcgi_connect_timeout 60s;
fastcgi_send_timeout 60s;
}
}
}
+12
View File
@@ -0,0 +1,12 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: job-scripts
data:
wait.sh: |
until pg_isready -h $POSTGRES_VHOST -p $POSTGRES_PORT -U $POSTGRES_USER
do
echo "Waiting for postgres"
sleep 2;
done
+42
View File
@@ -0,0 +1,42 @@
{{- if .Values.dragonfly.enabled }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name }}-dragonfly
spec:
revisionHistoryLimit: 3
strategy:
type: Recreate
selector:
matchLabels:
component: {{ .Chart.Name }}-dragonfly
template:
metadata:
labels:
component: {{ .Chart.Name }}-dragonfly
spec:
imagePullSecrets:
- name: regsecret
containers:
- name: dragonfly
image: cr.yandex/crpnn6fi85p3dauha6nc/dragonflydb:1.15.1
ports:
- name: dragonfly
containerPort: 6379
livenessProbe:
tcpSocket:
port: 6379
initialDelaySeconds: 30
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}-dragonfly
spec:
clusterIP: None
selector:
component: {{ .Chart.Name }}-dragonfly
ports:
- port: 6379
{{- end }}
+144
View File
@@ -0,0 +1,144 @@
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ .Chart.Name }}-nats
labels:
app.kubernetes.io/name: nats
app.kubernetes.io/instance: {{ .Release.Name }}
spec:
selector:
matchLabels:
app.kubernetes.io/name: nats
app.kubernetes.io/instance: {{ .Release.Name }}
replicas: {{ .Values.nats.replicas }}
serviceName: {{ .Chart.Name }}-nats
template:
metadata:
labels:
app.kubernetes.io/name: nats
app.kubernetes.io/instance: {{ .Release.Name }}
spec:
volumes:
- name: config-volume
configMap:
name: {{ .Chart.Name }}-nats-config
- name: pid
emptyDir: {}
shareProcessNamespace: true
terminationGracePeriodSeconds: {{ .Values.nats.terminationGracePeriodSeconds }}
imagePullSecrets:
- name: regsecret
containers:
- name: nats
image: {{ .Values.nats.image }}
imagePullPolicy: IfNotPresent
ports:
- containerPort: 4222
name: client
- containerPort: 6222
name: cluster
- containerPort: 8222
name: monitor
- containerPort: 7777
name: metrics
command:
- "nats-server"
- "--config"
- "/etc/nats-config/nats.conf"
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: SERVER_NAME
value: $(POD_NAME)
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: CLUSTER_ADVERTISE
value: $(POD_NAME).{{ .Chart.Name }}-nats.$(POD_NAMESPACE).svc.cluster.local
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
mountPath: {{ .Values.nats.jetstream.fileStorage.storageDirectory }}
{{- end }}
livenessProbe:
httpGet:
path: /
port: 8222
initialDelaySeconds: 10
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /
port: 8222
initialDelaySeconds: 10
timeoutSeconds: 5
lifecycle:
preStop:
exec:
command:
- "/bin/sh"
- "-c"
- "nats-server -sl=ldm=/var/run/nats/nats.pid && /bin/sleep 120"
{{- if .Values.nats.reloader.enabled }}
- name: reloader
image: natsio/nats-server-config-reloader:0.6.2
imagePullPolicy: IfNotPresent
command:
- "nats-server-config-reloader"
- "-pid"
- "/var/run/nats/nats.pid"
- "-config"
- "/etc/nats-config/nats.conf"
volumeMounts:
- name: config-volume
mountPath: /etc/nats-config
- name: pid
mountPath: /var/run/nats
{{- end }}
{{- if .Values.nats.jetstream.fileStorage.enabled }}
volumeClaimTemplates:
- metadata:
name: {{ .Chart.Name }}-nats-js-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: {{ .Values.nats.jetstream.fileStorage.size }}
{{- if .Values.nats.jetstream.fileStorage.storageClassName }}
storageClassName: {{ .Values.nats.jetstream.fileStorage.storageClassName }}
{{- end }}
{{- end }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}-nats
labels:
app.kubernetes.io/name: nats
app.kubernetes.io/instance: {{ .Release.Name }}
spec:
selector:
app.kubernetes.io/name: nats
app.kubernetes.io/instance: {{ .Release.Name }}
clusterIP: None
ports:
- name: client
port: 4222
- name: cluster
port: 6222
- name: monitor
port: 8222
- name: metrics
port: 7777
+100
View File
@@ -0,0 +1,100 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name }}-web
labels:
app: web
spec:
minReadySeconds: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
replicas: {{ .Values.app.replicas.web }}
selector:
matchLabels:
service: {{ .Chart.Name }}-web
template:
metadata:
labels:
service: {{ .Chart.Name }}-web
annotations:
prometheus.io/scrape: 'true'
prometheus.io/port: '9253'
spec:
imagePullSecrets:
- name: regsecret
containers:
- name: fpm
image: {{ .Values.app.image }}
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 }}
lifecycle:
preStop:
exec:
command:
- sh
- '-c'
- sleep 5 && kill -SIGQUIT 1
readinessProbe:
tcpSocket:
port: 9000
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 9000
initialDelaySeconds: 5
periodSeconds: 10
- name: nginx
image: {{ .Values.app.image }}
command: ["/usr/sbin/nginx", "-g", "daemon off;"]
ports:
- name: http
containerPort: 80
env:
{{- include "envs_all" . | indent 12 }}
{{- include "nginx_resources" . | indent 10}}
lifecycle:
preStop:
exec:
command:
- sh
- '-c'
- sleep 5 && /usr/sbin/nginx -s quit
volumeMounts:
- name: configs
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
- name: phpfpm-exporter
image: cr.yandex/crpnn6fi85p3dauha6nc/php-fpm_exporter:latest
ports:
- containerPort: 9253
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
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}-web
spec:
selector:
service: {{ .Chart.Name }}-web
ports:
- name: http
port: 80
protocol: TCP
+50
View File
@@ -0,0 +1,50 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name }}-websocket
labels:
app: websocket
spec:
minReadySeconds: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
replicas: {{ .Values.app.replicas.websocket }}
selector:
matchLabels:
service: {{ .Chart.Name }}-websocket
template:
metadata:
labels:
service: {{ .Chart.Name }}-websocket
annotations:
prometheus.io/scrape: 'true'
prometheus.io/port: '9797'
prometheus.io/path: "/info"
spec:
imagePullSecrets:
- name: regsecret
containers:
- name: websocket
{{- include "websocket_resources" . | indent 10}}
image: cr.yandex/crphnph9c2s776j32sjd/hrbox-websocket:2.1
imagePullPolicy: Always
env:
{{- include "envs_all" . | indent 12 }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}-websocket
spec:
selector:
service: {{ .Chart.Name }}-websocket
ports:
- name: http
port: 80
targetPort: 9797
protocol: TCP
+33
View File
@@ -0,0 +1,33 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name }}-worker
labels:
app: worker
spec:
minReadySeconds: 5
strategy:
type: Recreate
replicas: {{ .Values.app.replicas.worker }}
selector:
matchLabels:
service: {{ .Chart.Name }}-worker
template:
metadata:
labels:
service: {{ .Chart.Name }}-worker
annotations:
prometheus.io/scrape: 'true'
prometheus.io/port: '9253'
spec:
imagePullSecrets:
- name: regsecret
containers:
- name: worker
image: {{ .Values.app.image }}
command: ["hrbox-worker"]
env:
{{- include "envs_all" . | indent 12 }}
{{- include "worker_resources" . | indent 10}}
terminationGracePeriodSeconds: 600
+40
View File
@@ -0,0 +1,40 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name }}-conductor
labels:
app: conductor
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
service: {{ .Chart.Name }}-conductor
template:
metadata:
labels:
service: {{ .Chart.Name }}-conductor
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- conductor
topologyKey: kubernetes.io/hostname
imagePullSecrets:
- name: regsecret
containers:
- name: conductor
{{- include "conductor_resources" . | indent 10 }}
image: cr.yandex/crphnph9c2s776j32sjd/hrbox-conductor:2.1.1
imagePullPolicy: IfNotPresent
env:
{{- include "envs_all" . | indent 12 }}
+50
View File
@@ -0,0 +1,50 @@
---
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
+40
View File
@@ -0,0 +1,40 @@
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Chart.Name }}-ingress
annotations:
kubernetes.io/ingress.class: {{ .Values.ingress.className }}
nginx.org/websocket-services: {{ .Chart.Name }}-websocket
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
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;
spec:
rules:
- host: {{ .Values.ingress.host | quote }} # something.example.com
http:
paths:
- path: /ws/
pathType: Prefix
backend:
service:
name: {{ .Chart.Name }}-websocket
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: {{ .Chart.Name }}-web
port:
number: 80
{{- if .Values.ingress.tls.enabled }}
tls:
- hosts:
- {{ .Values.ingress.host | quote }}
secretName: {{ .Values.ingress.tls.secretName | quote }}
{{- end }}
+42
View File
@@ -0,0 +1,42 @@
---
apiVersion: batch/v1
kind: Job
metadata:
name: {{ .Chart.Name }}-migrate
annotations:
"helm.sh/hook": post-install,post-upgrade
"helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation
"helm.sh/hook-weight": "10"
spec:
ttlSecondsAfterFinished: 100
template:
spec:
imagePullSecrets:
- name: regsecret
initContainers:
- name: wait-postgres
image: postgres:14-alpine
command:
- bash
- /wait.sh
volumeMounts:
- name: configs
mountPath: /wait.sh
subPath: wait.sh
env:
{{- include "envs_all" . | indent 12 }}
containers:
- name: run-migrations
image: {{ .Values.app.image }}
command: ["php", "yii", "migrate", "--interactive=0"]
env:
{{- include "envs_all" . | indent 12 }}
{{- include "jobs_resources" . | indent 10 }}
volumes:
- name: configs
configMap:
name: job-scripts
defaultMode: 420
restartPolicy: OnFailure
terminationGracePeriodSeconds: 5
backoffLimit: 6
+24
View File
@@ -0,0 +1,24 @@
---
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
+197
View File
@@ -0,0 +1,197 @@
{{- 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: 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: LAUNCHPAD_TOPICS
value: {{ .Values.app.launchpad.topics | quote }}
- name: SMS_SENDER_NAME
value: "HRBOX"
# 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 }}
{{- end }}
# PostgreSQL
- name: POSTGRES_VHOST
value: {{ .Values.app.postgres.host | quote }}
- name: POSTGRES_PORT
value: {{ .Values.app.postgres.port | quote }}
- name: POSTGRES_DB
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 }}
# Redis
- name: REDIS_HOST
value: {{ .Values.app.redis.host | 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 }}
# 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 }}
{{- 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
value: {{ .Values.app.s3.region | quote }}
- name: S3_VERSION
value: {{ .Values.app.s3.version | quote }}
- name: S3_PATH_STYLE
value: {{ .Values.app.s3.pathStyle | quote }}
- name: S3_MULTIPART
value: {{ .Values.app.s3.multipart | quote }}
# Hub
- name: HUB_URL
value: {{ .Values.app.hub.url | quote }}
- name: HUB_TOKEN
value: {{ .Values.app.hub.token | quote }}
# Geonames
- name: GEONAMES_URL
value: {{ .Values.app.geonames.url | quote }}
# Статические переменные
- name: CONDUCTOR_TOPIC_CONDUCTOR
value: "conductor"
- name: CONDUCTOR_TOPIC_LAUNCHER
value: "launcher"
- name: GOOGLE_CLIENT_ID
value: "692956564858-ccapt35khk7tmadl17b3879ht2cropp6.apps.googleusercontent.com"
- name: GOOGLE_CLIENT_SECRET
value: "nW4pl4YsVWAR371gMRStW9qg"
- name: FORCE_HTTPS
value: "true"
- name: GLAGOL_PARTNER_ID
value: "ab230ee1-6946-43b9-8kfa-447beb8348b0"
- name: GLAGOL_API_URL
value: "https://api.glagol.online/glagol-api"
- name: LDAPTLS_REQCERT
value: "never"
- name: IFRAME_FULL_ACCESS
value: "false"
# Метаданные пода
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: NODE_NAME
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 }}
{{- if .Values.app.ai.openai.apiKey }}
- name: OPENAI_API_KEY
value: {{ .Values.app.ai.openai.apiKey | quote }}
- name: OPENAI_PROXY_KEY
value: {{ .Values.app.ai.openai.proxyKey | quote }}
- name: OPENAI_BASE_URI
value: {{ .Values.app.ai.openai.baseUri | quote }}
{{- end }}
{{- end }}
+110
View File
@@ -0,0 +1,110 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "nats.name" -}}
{{- default .Chart.Name .Values.nats.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "nats.fullname" -}}
{{- if .Values.nats.fullnameOverride -}}
{{- .Values.nats.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nats.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- 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 -}}
{{- end -}}
{{- end }}
{{- define "nats.extraRoutes" -}}
{{- range $i, $url := .Values.nats.cluster.extraRoutes -}}
{{- printf "%s," $url -}}
{{- 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 }}
{{/*
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 }}
{{- end -}}
+83
View File
@@ -0,0 +1,83 @@
{{- define "app_resources" }}
resources:
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 }}
{{- 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 }}
{{- 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 }}
{{- 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 }}
limits:
memory: {{ .Values.resources.geonames.limits.memory | quote }}
cpu: {{ .Values.resources.geonames.limits.cpu | quote }}
ephemeral-storage: {{ .Values.resources.geonames.limits.ephemeralStorage | 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 }}
{{- 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 }}
{{- end }}
+371
View File
@@ -0,0 +1,371 @@
---
# Основные настройки приложения HRBox
app:
# Docker образ приложения HRBox
# Получите доступ к реестру у сотрудников HRBox
image: "cr.yandex/crphnph9c2s776j32sjd/hrbox:latest"
# Настройки домена
# defaultTenantHostname - основной домен вашей HRBox инсталляции
# Например: hrbox.company.com
# Должен совпадать с ingress.host
defaultTenantHostname: "hrbox.example.com"
# Уникальное имя инстанса для идентификации
instanceName: "onprem"
# Домен для единой авторизации (SSO)
# Оставьте пустым, если не используете единую точку входа
authHost: ""
# Режим отладки
# "0" - production режим (рекомендуется)
# "1" - debug режим (только для разработки)
debug: "0"
# Количество реплик для каждого сервиса
# Рекомендации по масштабированию:
# - web: 2-10 реплик в зависимости от количества пользователей
# - worker: 2-5 реплик для обработки фоновых задач
# - websocket: 2-6 реплик для real-time функционала
# - geonames: 1-2 реплики (сервис не требователен к ресурсам)
replicas:
web: 2
worker: 2
websocket: 2
geonames: 1
# PostgreSQL база данных
# Требования: PostgreSQL 14+
# Рекомендуется настроить репликацию для отказоустойчивости
postgres:
host: "postgresql"
port: "5432"
database: "hrbox"
user: "hrbox"
# Пароль для подключения к БД
password: ""
# Использование Citus для шардирования
# "true" - если используете Citus расширение
# "false" - для обычного PostgreSQL
citus: "false"
# Redis для кеша и сессий
# Потеря данных не критична - хранятся только сессии и кеш
# При перезапуске Redis все пользователи будут разлогинены
redis:
host: "hrbox-dragonfly"
port: "6379"
# NATS JetStream - брокер сообщений
# Для отказоустойчивости используйте кластер из 3+ нод
# Формат для кластера: nats://node1:4222,nats://node2:4222,nats://node3:4222
nats:
url: "nats://hrbox-nats:4222"
# 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
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
pathStyle: "true"
# Включить multipart загрузку для больших файлов
multipart: "true"
# Префикс для всех объектов (опционально)
prefix: ""
# SMTP настройки для отправки email
smtp:
# SMTP сервер
host: "smtp.example.com"
# Порт SMTP
# 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"
securityType: "tls"
# Разрешить самоподписанные сертификаты
sslAllowSelfSigned: "true"
# Проверять SSL сертификат сервера
sslVerifyPeer: "false"
# Sentry для мониторинга ошибок (опционально)
sentry:
# DSN для отправки ошибок
# Оставьте пустым, чтобы отключить
dsn: ""
# Окружение для группировки ошибок
environment: "production"
# Настройки сервиса геолокации
geonames:
# URL внутреннего сервиса geonames
# Не меняйте, если используете стандартный деплой
url: "http://hrbox-geonames:8181"
# Настройки воркеров Launchpad
# Формат: "название-топика:количество-воркеров"
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 (опционально)
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
hub:
# URL Hub сервиса
url: "https://hub.hrbox.io"
# Токен для авторизации в Hub, получите от сотрудника HrBox
token: ""
# AI интеграции (опционально)
ai:
# Yandex Cloud AI
yandex:
# ID каталога в Yandex Cloud
folderId: ""
# API ключ для Yandex Cloud
apiKey: ""
# OpenAI интеграция
openai:
# API ключ OpenAI
apiKey: ""
# Прокси ключ (если используете прокси)
proxyKey: ""
# Base URL для API (можно указать прокси)
baseUri: ""
# Секретные ключи приложения
secrets:
# Ключ валидации cookie (32 символа)
# Используется для защиты от XSS атак
# Генерация: openssl rand -hex 16
cookieValidationKey: ""
# Ключ шифрования данных (64 символа)
# ВАЖНО: Не теряйте этот ключ! Без него невозможно расшифровать данные
# Используется для шифрования паролей интеграций (S3, ADFS и др.)
# Генерация: openssl rand -base64 48
encryptionKey: ""
# Настройки Ingress
ingress:
# Включить Ingress
enabled: true
# Класс Ingress контроллера
# Обычно: "nginx" или "traefik"
className: "nginx"
# Хост для доступа к приложению
# Используйте wildcard для мультитенантности: *.hrbox.company.com
# Или конкретный домен: hrbox.company.com
host: "hrbox.example.com"
# Настройки TLS/HTTPS
tls:
# Включить HTTPS
# ВАЖНО: В production всегда используйте HTTPS
enabled: true
# Имя Kubernetes Secret с SSL сертификатом
# Создайте через: kubectl create secret tls hrbox-tls --cert=cert.pem --key=key.pem
# Или используйте cert-manager для автоматического получения сертификатов
secretName: "hrbox-tls"
# Настройки ресурсов для контейнеров
# Указаны рекомендуемые значения для production
resources:
# Web-сервер (PHP-FPM + Nginx)
# Обрабатывает HTTP запросы, API, веб-интерфейс
app:
requests:
cpu: "0.5" # Минимум 0.5 CPU
memory: "512Mi" # Минимум 512MB RAM
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"
# WebSocket сервер
# Обеспечивает real-time функционал, например, уведомления
websocket:
requests:
cpu: "0.1"
memory: "128Mi"
ephemeralStorage: "64Mi"
limits:
cpu: "1"
memory: "512Mi"
ephemeralStorage: "64Mi"
# Geonames - сервис геолокации
# Определяет города, страны, часовые пояса
geonames:
requests:
cpu: "0.1"
memory: "196Mi"
ephemeralStorage: "64Mi"
limits:
cpu: "0.5"
memory: "256Mi"
ephemeralStorage: "64Mi"
# Conductor - планировщик задач
# ВАЖНО: Всегда должен быть в единственном экземпляре!
# Запускает задачи по расписанию (cron)
conductor:
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"
limits:
cpu: "2"
memory: "2Gi"
ephemeralStorage: "256Mi"
# Настройки NATS JetStream
nats:
# Включить NATS (обязательно для работы HRBox)
enabled: true
# Docker образ NATS
image: "nats:2.8"
# Количество реплик NATS
# Для отказоустойчивости используйте 3 или 5
replicas: 3
# JetStream - персистентные очереди сообщений
jetstream:
enabled: true
# Хранилище в памяти (для небольших инсталляций)
memStorage:
enabled: true
size: "2Gi"
# Хранилище на диске (для production)
fileStorage:
enabled: false
size: "4Gi"
storageDirectory: "/data"
# StorageClass для PVC
# Используйте SSD для лучшей производительности
storageClassName: "fast-ssd"
# Настройки кластера NATS
cluster:
# Включить кластеризацию для отказоустойчивости
enabled: true
# Должно совпадать с replicas
replicas: 3
# Prometheus exporter (для мониторинга)
exporter:
enabled: false
# Config reloader - автоматическая перезагрузка конфигурации
reloader:
enabled: true
# Время ожидания graceful shutdown в секундах
terminationGracePeriodSeconds: 120
dragonfly:
# Включает или отключает развёртывание Dragonfly (Redis).
# Установите в true, чтобы активировать, или в false, чтобы отключить.
enabled: true