add loop helm chart

This commit is contained in:
2025-07-22 09:25:50 +03:00
parent c00d31ebc4
commit 82b4aee59d
18 changed files with 1909 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
# 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
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj

View File

@@ -0,0 +1,18 @@
apiVersion: v2
appVersion: 9.11.1
description: LOOP Enterprise server with high availability.
home: https://loop.ru
icon: https://artifacts.wilix.dev/repository/loop-files/assets/loop-icon.png
keywords:
- loop
- communication
- messaging
- team colaboration
maintainers:
- email: dmitry.aloyan@wilix.org
name: dmitry.aloyan
- email: stuart.armstrong@wilix.org
name: stuart.armstrong
name: loop-enterprise-edition
type: application
version: 1.0.1

View File

@@ -0,0 +1,472 @@
---
## ⚠️ **This chart is not compatible with the previous loop chart and should not be used to upgrade the previous chart. All data can be lost**
## ⚠️ **Этот chart несовместим с предыдущим loop chart, и не должн использоваться для обновления предыдущей версии loop chart. Все данные могут быть потеряны
---
Loop Enterprise Edition Helm Chart
====================================================
This is the Helm chart for the Loop Enterprise Edition. It is subject to changes. To learn more about Helm charts, [see the Helm docs](https://helm.sh/docs/).
# 1. Prerequisites
## 1.1 Kubernetes Cluster
You need a running Kubernetes cluster v1.8+. If you do not have one, find options and installation instructions here:
https://kubernetes.io/docs/setup/pick-right-solution/
## 1.2 Helm
See: https://docs.helm.sh/using_helm/#quickstart
We recommend installing Helm v2.13.1 or later.
Once Helm is installed and initialized, run the following:
```bash
helm repo add loop https://artifacts.wilix.dev/repository/helm-loop
helm repo add incubator https://charts.helm.sh/incubator
```
## 1.3 Ingress
To expose the application outside of your network, configure an ingress if your Kubernetes cluster doesn't already have one.
We suggest using [nginx-ingress](https://github.com/kubernetes/ingress-nginx), which we use internally at Loop.
To install nginx-ingress in your Kubernetes cluster, follow [the official installation documentation](https://kubernetes.github.io/ingress-nginx/deploy/). You may also use the [helm charts](https://github.com/kubernetes/ingress-nginx/tree/main/charts/ingress-nginx) directly.
To get the nginx cache to work, adjust the ConfigMap and Deployment from the above instructions.
## 1.4 Certificate Manager
If you do not want to manually add SSL/TLS certificates, install [cert-manager](https://github.com/jetstack/cert-manager). You can follow [this documentation](https://cert-manager.io/docs/) or install the [helm charts](https://artifacthub.io/packages/helm/cert-manager/cert-manager).
To run with HTTPS you will need to add a Kubernetes secret for your SSL/TLS certificate, whether you use cert-manager or not.
## 1.5 Database
A Postgresql server and database is required for this chart. The loop-enterpise-stack chart provides and all-in-one solution.
## 1.6 Object Storage
A Minio server or ay S3 compatible object storage is required for this chart. The loop-enterpise-stack chart provides and all-in-one solution.
# 2. Configuration
## 2.1 Required Settings
At minimum the following settings must be updated:
* `global.siteURL` - set this to the URL your users will use to access Loop, e.g. `https://loop.example.com`
* `global.loopLicense` - set this to the contents of your license file or provide an existing secret. `global.existingLicenseSecret.name` `global.existingLicenseSecret.key`
* 2.4.1 Database Configuration
* 2.6.1 Object Storage Configuration
Without these settings, Loop will not run correctly.
## 2.2 Application Version
To set the Loop application version, update:
* `loopApp.image.tag` - set this to the Loop server version you wish to install (e.g. `7.10.2`)
## 2.3 Ingress
If you are using nginx-ingress, set the following under `loopApp`:
```yaml
ingress:
enabled: true
hosts:
- loop.example.com
```
where `loop.example.com` is your domain name and matches `global.siteURL`.
### 2.3.1 HTTPS
To run with HTTPS, add an SSL/TLS certificate as a secret to your Kubernetes cluster, either manually or [using cert-manager](#14-certificate-manager).
Set the following under `loopApp` to enable HTTPS:
```yaml
ingress:
enabled: true
hosts:
- loop.example.com
tls:
- secretName: your-tls-secret-name
hosts:
- loop.example.com
```
### 2.3.2 DNS
To route users from your domain name to your Loop installation, point your domain name at the external IP or domain that your ingress exposes.
Depending on the DNS service and Ingress you're using, the steps can vary. If you are using nginx-ingress, you would do something like this:
1. Run `kubectl describe svc your-nginx-ingress-controller`
* Replace `your-nginx-ingress-controller` with the name of your ingress controller service
2. Copy the domain name beside `LoadBalancer Ingress:`
3. On your DNS service, create a CNAME record pointing from the domain you'd like to use to the domain name you just copied
4. Save, and wait 10-15 minutes for the DNS change to propagate
## 2.4 Database
For database configuration, use an external database not managed by the Loop Helm chart.
### 2.4.1 Database Configuration
* Set `global.features.database.external.driver` to `postgres`
* Set `global.features.database.external.dataSource` to your master DB connection string
* (Optional) Set `global.features.database.external.dataSourceReplicas` to an array of read replica connection strings
* (Optional) use an external provided secret. `global.features.database.external.existingDatabaseSecret.name` `global.features.database.external.existingDatabaseSecret.key`.
## 2.5 Push Notifications
By default push notifications are enabled using HPNS.
### 2.5.1
#### Settings for pushproxy
## 2.6 Object Storage
S3 or any S3 compatible object storage like minio.
### 2.6.1 Object Storage Configuration
* - name: MM_FILESETTINGS_AMAZONS3ACCESSKEYID
value: "" # Access key для S3
* - name: MM_FILESETTINGS_AMAZONS3SECRETACCESSKEY
value: "" # Secret key для S3
* - name: MM_FILESETTINGS_AMAZONS3BUCKET
value: "" # Название бакета S3
* - name: MM_FILESETTINGS_AMAZONS3ENDPOINT
value: "" # Endpoint для доступа к S3
# 3. Install
After adding the Loop repo (see section 1.2) you can install a version of the preferred chart by running:
```bash
$ helm repo add loop https://artifacts.wilix.dev/repository/helm-loop
$ helm install <name> loop/loop-enterprise-edition --version <version_number>
```
For example:
```bash
$ helm repo add loop https://artifacts.wilix.dev/repository/helm-loop
$ helm install <name> loop/loop-enterprise-edition --version v0.8.2
```
If no Helm Chart version is specified the latest version will be installed.
To run with your custom `config.yaml`, install using:
```bash
$ helm install -f config.yaml loop/loop-enterprise-edition
```
To upgrade an existing release, modify the `config.yaml` with your desired changes and then use:
```bash
$ helm upgrade -f config.yaml <your-release-name> loop/loop-enterprise-edition
```
## 3.1 Uninstalling Loop Enterprise Helm Chart
If you are done with your deployment and want to delete it, use `helm delete <your-release-name>`. If you don't know the name of your release, use `helm ls` to find it.
# 4. Upgrading to Loop Chart Version 1.0.0
This chart version uses the configuration in the database and environment variables for non-changing configuration.
To upgrade from a previous chart version to 1.0.0 you need to follow theses steps before applying the new chart:
- Get the existing configuration from your Loop instance. For that you can use the CLI command. You need to connect to the running pod and run the Loop CLI to get the existing config
```bash
$ kubectl exec -it -n <YOUR_loop_NAMESPACE> <loop_APP_POD> -- /bin/sh
in the pod:
$ ./bin/loop config show --json
```
- Save this config in your local file system
- Perform the Helm Chart upgrade
- Copy the config you saved before to the new pod
- Perform the config migration
```bash
$ ./bin/loop config migrate <CONFIG_THAT_YOU_COPY_TO_THE_POD.json> $MM_CONFIG
...
{"level":"info","msg":"Successfully migrated config."}
```
- Kill the pods to apply the configs
---
**[RU Русская версия]**
---
Helm-чарт Loop Enterprise Edition
====================================================
Это Helm-чарт для Loop Enterprise Edition. Структура чарта может изменяться. Подробнее о Helm-чартах см. в [официальной документации Helm](https://helm.sh/docs/).
# 1. Предварительные требования
## 1.1 Кластер Kubernetes
Требуется работающий кластер Kubernetes версии **1.8 или выше**. Если кластер отсутствует, варианты развёртывания и инструкции доступны здесь:
https://kubernetes.io/ru/docs/setup/production-environment/
## 1.2 Установка Helm
Официальная инструкция: [Quickstart Guide](https://helm.sh/docs/intro/quickstart/)
**Рекомендуемая версия Helm**: **v3.8.0 или новее** (актуально на момент написания).
После установки выполните:
```bash
helm repo add loop https://artifacts.wilix.dev/repository/helm-loop
helm repo add incubator https://charts.helm.sh/incubator
```
## 1.3 Настройка Ingress
Для внешнего доступа к приложению требуется контроллер Ingress.
**Рекомендуемое решение**: [ingress-nginx](https://github.com/kubernetes/ingress-nginx).
**Установка через Helm**:
```bash
helm upgrade --install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
--namespace ingress-nginx --create-namespace
```
**Важно**:
- Для активации кеширования NGINX отредактируйте `ConfigMap` и `Deployment` контроллера.
- Полная инструкция: [Официальный гайд](https://kubernetes.github.io/ingress-nginx/deploy/).
## 1.4 Управление TLS-сертификатами
**Автоматизация**: Используйте [cert-manager](https://cert-manager.io/) для автоматической выдачи сертификатов (Let's Encrypt).
**Установка через Helm**:
```bash
helm upgrade --install cert-manager cert-manager \
--repo https://charts.jetstack.io \
--namespace cert-manager --create-namespace \
--set installCRDs=true
```
**Ручная настройка**: Если cert-manager не используется, создайте Kubernetes Secret с вашим TLS-сертификатом:
```bash
kubectl create secret tls your-tls-secret --cert=path/to/cert.crt --key=path/to/cert.key
```
## 1.5 База данных
Для работы этого Helm-чарта требуется сервер и база данных **PostgreSQL**.
Чарт `loop-enterprise-stack` предоставляет **комплексное решение**, включая базу данных.
## 1.6 Объектное хранилище
Для работы этого Helm-чарта требуется сервер **MinIO** или любое другое **S3-совместимое объектное хранилище**.
Чарт `loop-enterprise-stack` также предоставляет объектное хранилище.
# 2. Конфигурация
## 2.1 Обязательные параметры
**Минимально необходимые настройки:**
- `global.siteURL` — установите в адрес, по которому пользователи будут получать доступ к Loop, например:
`https://loop.example.com`
- `global.loopLicense` — укажите содержимое лицензионного файла **или** задайте существующий Kubernetes-секрет с ключом:
`global.existingLicenseSecret.name` и `global.existingLicenseSecret.key`
- **Конфигурация базы данных (раздел 2.4.1)** — необходимо настроить подключение к PostgreSQL.
Это может быть внешний сервер или встроенный через Helm-чарт.
- **Конфигурация объектного хранилища (раздел 2.6.1)** — нужно подключить MinIO или другое совместимое S3-хранилище.
Без этих параметров Loop **не будет работать корректно** — сервис не сможет ни загрузить данные, ни сохранить файлы.
## 2.2 Версия приложения
Укажите тег образа Loop:
```yaml
loopApp:
image:
tag: "7.10.2" # Например, 7.10.2
```
## 2.3 Настройка Ingress
### **2.3.1 Ingress с nginx ingress controller**
Если вы используете `nginx-ingress-controller`, добавьте следующее в секцию `loopApp`:
```yaml
ingress:
enabled: true
hosts:
- loop.example.com
```
Здесь `loop.example.com` — это ваш домен, и он **должен совпадать с `global.siteURL`** (например, `https://loop.example.com`).
### **2.3.2 HTTPS (SSL/TLS)**
Чтобы использовать HTTPS:
1. Создайте TLS-секрет в Kubernetes **вручную** или с помощью **cert-manager**.
2. Укажите его в `values.yaml` Helm-чарта:
```yaml
ingress:
enabled: true
hosts:
- loop.example.com
tls:
- secretName: your-tls-secret-name
hosts:
- loop.example.com
```
`secretName` — это имя созданного TLS-секрета с ключом и сертификатом (например, от Let's Encrypt или корпоративного CA).
### **2.3.3 DNS**
Чтобы пользователи с вашего доменного имени могли получить доступ к Loop, сделайте запись в вашем DNS-сервисе на внешний IP-адрес или домен, который предоставляет ваш ingress.
В зависимости от используемого DNS-сервиса и Ingress-контроллера шаги могут отличаться. Если вы используете nginx-ingress, выполните следующее:
* Выполните команду `kubectl describe svc your-nginx-ingress-controller`
* Замените `your-nginx-ingress-controller` на имя сервиса вашего ingress-контроллера
* Скопируйте доменное имя, указанное рядом с LoadBalancer Ingress
* В вашем DNS-сервисе создайте CNAME-запись, указывающую с нужного доменного имени на только что скопированное доменное имя
* Сохраните изменения и подождите 1015 минут, чтобы изменения DNS вступили в силу
## 2.4 Внешняя база данных
### **2.4.1 Конфигурация базы данных**
> **Используйте внешнюю базу данных**
#### Обязательные параметры:
* `global.features.database.external.driver`**установите в** `"postgres"`
Это означает, что будет использоваться PostgreSQL как драйвер СУБД.
* `global.features.database.external.dataSource`**укажите строку подключения к мастер-базе**
Пример строки подключения:
```
postgres://username:password@hostname:5432/database_name?sslmode=require
```
#### Необязательные параметры:
* `global.features.database.external.dataSourceReplicas`
Список строк подключения к **репликам** БД (если они используются для масштабирования чтения).
* `global.features.database.external.existingDatabaseSecret.name`
`global.features.database.external.existingDatabaseSecret.key`
Если вы не хотите **хранить логины/пароли в открытом виде**, можно использовать Kubernetes Secret, где будут зашиты данные подключения.
## 2.5 Push-уведомления
**По умолчанию**: Используется встроенная служба HPNS (High-Performance Notification Service).
## 2.6 Объектное хранилище
S3 или любое другое хранилище объектов, совместимое с S3, например minio.
### 2.6.1 Конфигурация объектного хранилища
- name: MM_FILESETTINGS_AMAZONS3ACCESSKEYID
value: "" # Access key для S3
- name: MM_FILESETTINGS_AMAZONS3SECRETACCESSKEY
value: "" # Secret key для S3
- name: MM_FILESETTINGS_AMAZONS3BUCKET
value: "" # Название бакета S3
- name: MM_FILESETTINGS_AMAZONS3ENDPOINT
value: "" # Endpoint для доступа к S3
# 3. Установка и управление
**Установка последней версии**:
```bash
helm upgrade --install loop-ee loop/loop-enterprise-edition \
--namespace loop --create-namespace
```
**Установка конкретной версии**:
```bash
helm upgrade --install loop-ee loop/loop-enterprise-edition \
--version 0.8.2 \
--namespace loop --create-namespace
```
**Применение конфигурации из файла**:
```bash
helm upgrade --install loop-ee loop/loop-enterprise-edition \
--namespace loop -f config.yaml
```
**Обновление релиза**:
```bash
helm upgrade loop-ee loop/loop-enterprise-edition \
--namespace loop -f updated-config.yaml
```
**Удаление**:
```bash
helm uninstall loop-ee --namespace loop
```
# 4. Миграция на версию чарта 1.0.0+
**Критические изменения**:
- Конфигурация теперь хранится в базе данных.
- Настройки через переменные окружения устарели.
**Шаги миграции**:
1. **Экспорт текущей конфигурации**:
```bash
kubectl exec -n loop loop-app-pod -- ./bin/loop config export --json > loop-config.json
```
2. **Обновление чарта**:
```bash
helm upgrade loop-ee loop/loop-enterprise-edition --version 1.0.0
```
3. **Импорт конфигурации**:
```bash
kubectl cp loop-config.json loop/loop-app-pod:/tmp/config.json
kubectl exec -n loop loop-app-pod -- ./bin/loop config migrate /tmp/config.json $MM_CONFIG
```
4. **Рестарт пода**:
```bash
kubectl rollout restart deployment/loop-app -n loop
```
---
**Примечания**:
- Все команды предполагают, что namespace для Loop — `loop`. Замените при необходимости.
- Для production-среды используйте отдельные секреты (не храните пароли в `values.yaml`).

View File

@@ -0,0 +1,19 @@
1. Get the application URL by running these commands:
{{- if .Values.loopApp.ingress.enabled }}
{{- range $host := .Values.loopApp.ingress.hosts }}
http://{{ $host }}
{{- end}}
{{- else if contains "NodePort" .Values.loopApp.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.loopApp.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get svc -w {{ include "fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo http://$SERVICE_IP:{{ .Values.loopApp.service.externalPort }}
{{- else if contains "ClusterIP" .Values.loopApp.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "loop-enterprise-edition.name" . }}" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl port-forward $POD_NAME 8080:{{ .Values.loopApp.service.externalPort }}
{{- end }}

View File

@@ -0,0 +1,61 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "loop-enterprise-edition.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "loop-enterprise-edition.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "loop-enterprise-edition.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a fully qualified jobserver name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "loop-enterprise-edition.jobserver.fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s-%s" .Release.Name $name .Values.global.features.jobserver.name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Return the appropriate apiVersion for ingress. Based on
1) Helm Version (.Capabilities has been changed in v3)
2) Kubernetes Version
*/}}
{{- define "loop-enterprise-edition.ingress.apiVersion" -}}
{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" -}}
"networking.k8s.io/v1"
{{- else if .Capabilities.APIVersions.Has "networking.k8s.io/v1beta1" -}}
"networking.k8s.io/v1beta1"
{{- else -}}
"extensions/v1beta1"
{{- end -}}
{{- end -}}
{{- define "loop-enterprise-edition.deployment.apiVersion" -}}
"apps/v1"
{{- end -}}

View File

@@ -0,0 +1,157 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "loop-enterprise-edition.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/component: server
helm.sh/chart: {{ include "loop-enterprise-edition.chart" . }}
spec:
{{- if not .Values.loopApp.autoscaling.enabled }}
replicas: {{ .Values.loopApp.replicaCount }}
{{- end }}
{{- with .Values.loopApp.strategy }}
strategy:
{{- . | toYaml | nindent 4 }}
{{- end }}
revisionHistoryLimit: {{ .Values.loopApp.revisionHistoryLimit }}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/component: server
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/component: server
helm.sh/chart: {{ include "loop-enterprise-edition.chart" . }}
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "{{ .Values.loopApp.service.metricsPort }}"
prometheus.io/path: "/metrics"
{{- if .Values.loopApp.extraPodAnnotations }}
{{- .Values.loopApp.extraPodAnnotations | toYaml | nindent 8 }}
{{- end }}
spec:
{{- if .Values.loopApp.nodeSelector }}
nodeSelector:
{{- toYaml .Values.loopApp.nodeSelector | nindent 8 }}
{{- end }}
{{- if .Values.loopApp.affinity }}
affinity:
{{- toYaml .Values.loopApp.affinity | nindent 8 }}
{{- end }}
{{- if .Values.loopApp.tolerations }}
tolerations:
{{- toYaml .Values.loopApp.tolerations | nindent 6 }}
{{- end }}
{{- if .Values.loopApp.securityContext }}
securityContext:
{{- toYaml .Values.loopApp.securityContext | nindent 8 }}
{{- end }}
{{- if .Values.serviceAccount.create }}
serviceAccountName: {{ .Values.serviceAccount.name }}
{{- end }}
initContainers:
{{- if .Values.loopApp.extraInitContainers }}
{{- .Values.loopApp.extraInitContainers | toYaml | nindent 6 }}
{{- end }}
containers:
- name: {{ include "loop-enterprise-edition.name" . }}
image: "{{ .Values.loopApp.image.repository }}:{{ .Values.loopApp.image.tag }}"
imagePullPolicy: {{ .Values.loopApp.image.pullPolicy }}
ports:
- containerPort: {{ .Values.loopApp.service.internalPort }}
name: api
- containerPort: {{ .Values.loopApp.service.metricsPort }}
name: metrics
- containerPort: {{ .Values.loopApp.service.clusterPort }}
name: cluster
- containerPort: {{ .Values.loopApp.service.gossipPort }}
name: gossip
env:
- name: MM_CONFIG
valueFrom:
secretKeyRef:
{{- if .Values.global.features.database.existingDatabaseSecret }}
name: {{ .Values.global.features.database.existingDatabaseSecret.name }}
key: {{ .Values.global.features.database.existingDatabaseSecret.key }}
{{- else }}
name: {{ include "loop-enterprise-edition.fullname" . }}-loop-dbsecret
key: loop.dbsecret
{{- end }}
- name: MM_SERVICESETTINGS_LICENSEFILELOCATION
value: {{ printf "/mattermost/%s" (.Values.global.existingLicenseSecret | default "loop.loop-license") | squote }}
- name: MM_SERVICESETTINGS_SITEURL
value: "{{ .Values.global.siteUrl }}"
- name: MM_SERVICESETTINGS_LISTENADDRESS
value: ":{{ .Values.loopApp.service.internalPort }}"
- name: MM_SERVICESETTINGS_ENABLELINKPREVIEWS
value: "{{ .Values.global.enableLinkPreviews }}"
- name: MM_SERVICESETTINGS_ENABLECUSTOMEMOJI
value: "{{ .Values.global.enableCustomEmoji }}"
{{- if .Values.global.features.jobserver.enabled }}
- name: MM_JOBSETTINGS_RUNJOBS
value: "false"
- name: MM_JOBSETTINGS_RUNSCHEDULER
value: "false"
{{- end }}
{{- with .Values.loopApp.extraEnv }}
{{ toYaml . | indent 8 }}
{{- end }}
livenessProbe:
initialDelaySeconds: 90
timeoutSeconds: 5
periodSeconds: 15
httpGet:
path: /api/v4/system/ping
port: {{ .Values.loopApp.service.internalPort }}
readinessProbe:
initialDelaySeconds: 15
timeoutSeconds: 5
periodSeconds: 15
httpGet:
path: /api/v4/system/ping
port: {{ .Values.loopApp.service.internalPort }}
volumeMounts:
{{- if .Values.global.existingLicenseSecret.name }}
- mountPath: /mattermost/{{.Values.global.existingLicenseSecret.key }}
name: loop-license
subPath: {{.Values.global.existingLicenseSecret.key }}
{{- else }}
- mountPath: /mattermost/loop.loop-license
name: loop-license
subPath: loop.loop-license
{{- end }}
- mountPath: /mattermost/plugins/
name: loop-plugins
- mountPath: /mattermost/client/plugins/
name: loop-plugins-client
{{- if .Values.loopApp.extraVolumeMounts }}
{{ toYaml .Values.loopApp.extraVolumeMounts | indent 8 }}
{{- end }}
resources:
{{ toYaml .Values.loopApp.resources | indent 10 }}
volumes:
- name: loop-plugins
emptyDir: {}
- name: loop-plugins-client
emptyDir: {}
- name: loop-config
emptyDir: {}
- name: loop-license
secret:
{{- if .Values.global.existingLicenseSecret.name }}
secretName: {{ .Values.global.existingLicenseSecret.name }}
{{- else }}
secretName: {{ include "loop-enterprise-edition.fullname" . }}-loop-license
{{- end }}
{{- if .Values.loopApp.extraVolumes }}
{{ toYaml .Values.loopApp.extraVolumes | indent 6 }}
{{- end }}

View File

@@ -0,0 +1,31 @@
{{- if .Values.loopApp.autoscaling.enabled }}
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
labels:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ include "loop-enterprise-edition.chart" . }}
name: {{ include "loop-enterprise-edition.fullname" . }}
spec:
scaleTargetRef:
apiVersion: {{ template "loop-enterprise-edition.deployment.apiVersion" . }}
kind: Deployment
name: {{ include "loop-enterprise-edition.fullname" . }}
minReplicas: {{ .Values.loopApp.autoscaling.minReplicas }}
maxReplicas: {{ .Values.loopApp.autoscaling.maxReplicas }}
metrics:
{{- with .Values.loopApp.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
targetAverageUtilization: {{ . }}
{{- end }}
{{- with .Values.loopApp.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
targetAverageUtilization: {{ . }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,101 @@
{{- if .Values.global.features.jobserver.enabled -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "loop-enterprise-edition.jobserver.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/component: {{ .Values.global.features.jobserver.name }}
helm.sh/chart: {{ include "loop-enterprise-edition.chart" . }}
spec:
replicas: {{ .Values.global.features.jobserver.replicaCount }}
{{- with .Values.global.features.jobserver.strategy }}
strategy:
{{- . | toYaml | nindent 4 }}
{{- end }}
revisionHistoryLimit: {{ .Values.global.features.jobserver.revisionHistoryLimit }}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/component: {{ .Values.global.features.jobserver.name }}
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/component: {{ .Values.global.features.jobserver.name }}
helm.sh/chart: {{ include "loop-enterprise-edition.chart" . }}
annotations:
{{- if .Values.loopApp.extraPodAnnotations }}
{{- .Values.loopApp.extraPodAnnotations | toYaml | nindent 8 }}
{{- end }}
spec:
{{- if .Values.global.features.jobserver.nodeSelector }}
nodeSelector:
{{- toYaml .Values.global.features.jobserver.nodeSelector | nindent 8 }}
{{- end }}
{{- if .Values.global.features.jobserver.affinity }}
affinity:
{{- toYaml .Values.global.features.jobserver.affinity | nindent 8 }}
{{- end }}
{{- if .Values.global.features.jobserver.tolerations }}
tolerations:
{{- toYaml .Values.global.features.jobserver.tolerations | nindent 6 }}
{{- end }}
{{- if .Values.loopApp.securityContext }}
securityContext:
{{- toYaml .Values.loopApp.securityContext | nindent 8 }}
{{- end }}
initContainers:
- name: "init-loop-app"
image: "{{ .Values.initContainerImage.repository }}:{{ .Values.initContainerImage.tag }}"
imagePullPolicy: {{ .Values.initContainerImage.imagePullPolicy }}
command: [
"sh",
"-c",
"until curl --max-time 5 http://{{ include "loop-enterprise-edition.fullname" . }}.{{ .Release.Namespace }}:{{ .Values.loopApp.service.internalPort }}/api/v4/system/ping ; do echo waiting for LOOP App come up; sleep 5; done; echo init-loop-app finished"
]
containers:
- name: {{ include "loop-enterprise-edition.name" . }}-jobserver
image: "{{ .Values.loopApp.image.repository }}:{{ .Values.loopApp.image.tag }}"
imagePullPolicy: {{ .Values.loopApp.image.pullPolicy }}
command: ["mattermost", "jobserver"]
env:
- name: MM_CONFIG
valueFrom:
secretKeyRef:
{{- if .Values.global.features.database.existingDatabaseSecret }}
name: {{ .Values.global.features.database.existingDatabaseSecret.name }}
key: {{ .Values.global.features.database.existingDatabaseSecret.key }}
{{- else }}
name: {{ include "loop-enterprise-edition.fullname" . }}-loop-dbsecret
key: loop.dbsecret
{{- end }}
{{- with .Values.global.features.jobserver.extraEnv }}
{{- toYaml . | nindent 8 }}
{{- end }}
volumeMounts:
{{- if .Values.global.existingLicenseSecret.name }}
- mountPath: /mattermost/{{.Values.global.existingLicenseSecret.key }}
name: loop-license
subPath: {{.Values.global.existingLicenseSecret.key }}
{{- else }}
- mountPath: /mattermost/loop.loop-license
name: loop-license
subPath: loop.loop-license
{{- end }}
volumes:
- name: loop-license
secret:
{{- if .Values.global.existingLicenseSecret.name }}
secretName: {{ .Values.global.existingLicenseSecret.name }}
{{- else }}
secretName: {{ include "loop-enterprise-edition.fullname" . }}-loop-license
{{- end }}
{{- end -}}

View File

@@ -0,0 +1,50 @@
{{- if .Values.loopApp.ingress.enabled -}}
{{- $serviceName := include "loop-enterprise-edition.fullname" . -}}
{{- $servicePort := .Values.loopApp.service.externalPort -}}
apiVersion: {{ include "loop-enterprise-edition.ingress.apiVersion" . }}
kind: Ingress
metadata:
name: {{ include "loop-enterprise-edition.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ include "loop-enterprise-edition.chart" . }}
annotations:
{{- if .Values.loopApp.ingress.annotations }}
{{ toYaml .Values.loopApp.ingress.annotations | indent 4 }}
{{- end }}
{{- if .Values.loopApp.ingress.tls }}
kubernetes.io/tls-acme: 'true'
nginx.ingress.kubernetes.io/ssl-redirect: "true"
{{- else }}
nginx.ingress.kubernetes.io/ssl-redirect: "false"
{{- end }}
spec:
{{- if .Values.loopApp.ingress.ingressClassName }}
ingressClassName: {{ .Values.loopApp.ingress.ingressClassName }}
{{- end }}
rules:
{{- range $host := .Values.loopApp.ingress.hosts }}
- host: {{ $host }}
http:
paths:
- path: /
{{- if $.Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
backend:
service:
name: {{ $serviceName }}
port:
number: {{ $servicePort }}
pathType: Prefix
{{- else }}
backend:
serviceName: {{ $serviceName }}
servicePort: {{ $servicePort }}
{{- end }}
{{- end -}}
{{- if .Values.loopApp.ingress.tls }}
tls:
{{ toYaml .Values.loopApp.ingress.tls | indent 4 }}
{{- end -}}
{{- end -}}

View File

@@ -0,0 +1,14 @@
{{- if not .Values.global.features.database.existingDatabaseSecret.name }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "loop-enterprise-edition.fullname" . }}-loop-dbsecret
labels:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ include "loop-enterprise-edition.chart" . }}
type: Opaque
data:
loop.dbsecret: {{ tpl "{{ .Values.global.features.database.external.driver }}://{{ .Values.global.features.database.external.dataSource }}" . | b64enc }}
{{- end }}

View File

@@ -0,0 +1,14 @@
{{- if not .Values.global.existingLicenseSecret.name }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "loop-enterprise-edition.fullname" . }}-loop-license
labels:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ include "loop-enterprise-edition.chart" . }}
type: Opaque
data:
loop.loop-license: {{ .Values.global.loopLicense | b64enc | quote }}
{{- end -}}

View File

@@ -0,0 +1,17 @@
{{ if .Values.serviceAccount.create }}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ .Values.serviceAccount.name }}
namespace: {{ .Release.Namespace }}
{{- if .Values.serviceAccount.annotations }}
annotations:
{{ tpl (toYaml .Values.serviceAccount.annotations) . | indent 4 }}
{{- end }}
labels:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/component: server
helm.sh/chart: {{ include "loop-enterprise-edition.chart" . }}
{{ end }}

View File

@@ -0,0 +1,25 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "loop-enterprise-edition.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ include "loop-enterprise-edition.chart" . }}
spec:
selector:
app.kubernetes.io/name: {{ include "loop-enterprise-edition.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
app.kubernetes.io/component: server
type: {{ .Values.loopApp.service.type }}
ports:
- port: {{ .Values.loopApp.service.externalPort }}
targetPort: {{ .Values.loopApp.service.internalPort }}
protocol: TCP
name: {{ .Values.loopApp.service.name }}
- port: {{ .Values.loopApp.service.metricsPort }}
targetPort: {{ .Values.loopApp.service.metricsPort }}
protocol: TCP
name: {{ .Values.loopApp.service.metricsName }}

View File

@@ -0,0 +1,248 @@
global:
siteUrl: "" # ОБЯЗАТЕЛЬНЫЙ ПАРАМЕТР, НЕОБХОДИМО УКАЗАТЬ: например, "https://loop.example.com"
# Использовать уже существующий секрет для лицензии (см. secret-loop-license.yaml для необходимых полей)
existingLicenseSecret: {}
# name: ""
# key: ""
# ОБЯЗАТЕЛЬНЫЙ ПАРАМЕТР, если не указан существующий секрет.
loopLicense: ""
# Настройки конфигурации LOOP
# Подробнее: https://docs.loop.ru/administration/config-settings
siteName: ""
enableTeamCreation: true # Разрешить создание команд
enableUserCreation: true # Разрешить создание пользователей
enableOpenServer: true # Открытый доступ к серверу
enableLinkPreviews: true # Предпросмотр ссылок
enableCustomEmoji: true # Поддержка пользовательских эмодзи
# Настройки email-уведомлений
sendEmailNotifications: false # Включить email-уведомления
requireEmailVerification: false # Требовать подтверждение email
feedbackName: "" # Имя отправителя отзывов
feedbackEmail: "" # Email для отзывов
feedbackOrganization: "" # Организация
smtpUsername: "" # SMTP логин
smtpPassword: "" # SMTP пароль
smtpServer: "" # SMTP сервер
smtpPort: "" # SMTP порт
connectionSecurity: "" # тип подключения: пусто, TLS или STARTTLS
features:
database:
external:
driver: "postgresql"
dataSource: "" # <username>:<password>@<postgresql-server>:5432/<dbname>?connect_timeout=10&sslmode=disable
dataSourceReplicas: ""
existingDatabaseSecret: {}
# name: ""
# key: ""
jobserver:
name: jobserver
enabled: true
replicaCount: 1
strategy:
type: Recreate
rollingUpdate: null
revisionHistoryLimit: 2
service:
name: loop-app-jobserver
type: ClusterIP
nodeSelector: {}
affinity: {}
tolerations: []
extraEnv: {}
notifications:
push:
enabled: true
# Хостинговый сервис push-уведомлений. Требует корпоративную лицензию.
# Подробнее: https://docs.loop.ru/mobile/mobile-hpns
useHPNS: true
initContainerImage:
repository: appropriate/curl
tag: latest
imagePullPolicy: IfNotPresent
# Секция деплоя LOOP
# Используется для настройки развертывания серверов LOOP
loopApp:
replicaCount: 1
image:
repository: registry.loop.ru/loop/server
tag: "2025-03-03-1"
pullPolicy: IfNotPresent
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 0
maxUnavailable: 1
revisionHistoryLimit: 2
service:
name: loop-app
type: ClusterIP
externalPort: 8065
internalPort: 8065
metricsPort: 8067
clusterPort: 8075
gossipPort: 8074
metricsName: loop-app-metrics
ingress:
# Используется для создания записи Ingress (работает с service.type: ClusterIP).
enabled: false
hosts: []
tls:
# Секреты должны быть созданы вручную в пространстве имён.
# - secretName: chart-example-tls
# hosts:
# - loop.example.com
annotations: {}
# kubernetes.io/ingress.class: nginx
# nginx.ingress.kubernetes.io/proxy-buffering: "on"
# nginx.ingress.kubernetes.io/proxy-body-size: 50m
# nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
# nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
# nginx.ingress.kubernetes.io/configuration-snippet: |
# proxy_cache loop_cache;
# proxy_cache_revalidate on;
# proxy_cache_min_uses 2;
# proxy_cache_use_stale timeout;
# proxy_cache_lock on;
ingressClassName: nginx
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 1
targetMemoryUtilizationPercentage: 50
targetCPUUtilizationPercentage: 50
nodeSelector: {}
affinity: {}
tolerations: []
securityContext:
fsGroup: 2000
resources: {}
# limits:
# cpu: 100m
# memory: 300Mi
# requests:
# cpu: 100m
# memory: 300Mi
extraInitContainers: []
# Дополнительный контейнер для загрузки и установки плагинов
# - name: init-plugins-config
# image: busybox
# imagePullPolicy: IfNotPresent
# command:
# - sh
# - "-c"
# - |
# PLUGINS_TAR="hovercardexample.tar.gz"
# PLUGINS_TAR="${PLUGINS_TAR} https://<ПУТЬ_К_ПЛАГИНАМ_1>"
# PLUGINS_TAR="${PLUGINS_TAR} https://<ПУТЬ_К_ПЛАГИНАМ_2>"
# PLUGINS_TAR="${PLUGINS_TAR} https://<ПУТЬ_К_ПЛАГИНАМ_N>"
# for plugin_tar in ${PLUGINS_TAR};
# do
# wget ${plugin_tar} -P /loop/plugins
# cd /loop/plugins
# tar -xzvf ${plugin_tar##*/} # распаковка
# rm -f ${plugin_tar##*/} # удаление архива
# done
# volumeMounts:
# # Должны соответствовать volume'ам для плагинов
# - name: loop-plugins
# mountPath: loop/plugins/
# - name: loop-plugins-client
# mountPath: /loop/client/plugins/
# Добавление дополнительных томов и точек монтирования — например, для SAML ключей или других файлов
extraVolumes: []
# - hostPath:
# path: /var/log
# name: varlog
extraVolumeMounts: []
# - name: varlog
# mountPath: /host/var/log
# readOnly: true
## Дополнительные переменные окружения для LOOP
extraEnv:
- name: MM_ELASTICSEARCHSETTINGS_USERNAME
value: "" # Username Elasticsearch
- name: MM_ELASTICSEARCHSETTINGS_PASSWORD
value: "" # Password Elasticsearch
- name: MM_ELASTICSEARCHSETTINGS_CONNECTIONURL
value: "" # URL к Elasticsearch
- name: MM_ELASTICSEARCHSETTINGS_ENABLEINDEXING
value: "false" # Использовать Elasticsearch для индексации
- name: MM_ELASTICSEARCHSETTINGS_ENABLESEARCHING
value: "false" # Использовать Elasticsearch для поиска
- name: MM_FILESETTINGS_AMAZONS3SSE
value: "false" # Использовать шифрование SSE для S3
- name: MM_FILESETTINGS_AMAZONS3SSL
value: "false" # Использовать SSL-подключение к S3
- name: MM_FILESETTINGS_DRIVERNAME
value: "amazons3" # Драйвер для файлового хранилища
- name: MM_FILESETTINGS_AMAZONS3ACCESSKEYID
value: "" # Access key для S3
- name: MM_FILESETTINGS_AMAZONS3SECRETACCESSKEY
value: "" # Secret key для S3
- name: MM_FILESETTINGS_AMAZONS3BUCKET
value: "" # Название бакета S3
- name: MM_FILESETTINGS_AMAZONS3ENDPOINT
value: "" # Endpoint для доступа к S3
- name: MM_FILESETTINGS_MAXFILESIZE
value: "1048576000" # Максимальный размер файла в байтах
- name: MM_LOGSETTINGS_CONSOLEJSON
value: "false" # Вывод логов в JSON-формате в консоль
- name: MM_LOGSETTINGS_CONSOLELEVEL
value: "DEBUG" # Уровень логирования в консоли
- name: MM_LOGSETTINGS_FILELEVEL
value: "DEBUG" # Уровень логирования в файл
- name: MM_PLUGINSETTINGS_AUTOMATICPREPACKAGEDPLUGINS
value: "true" # Автоматическая загрузка встроенных плагинов
- name: MM_PLUGINSETTINGS_ENABLEUPLOADS
value: "true" # Разрешить загрузку плагинов
- name: MM_SERVICEENVIRONMENT
value: "production" # Среда окружения, например "production"
- name: MM_SERVICESETTINGS_ENABLEBOTACCOUNTCREATION
value: "true" # Разрешить создание бот-аккаунтов
- name: MM_SERVICESETTINGS_ENABLEOAUTHSERVICEPROVIDER
value: "true" # Разрешить использование внешних OAuth-провайдеров
- name: MM_SERVICESETTINGS_ENABLETESTING
value: "true" # Разрешить тестирование
- name: MM_SERVICESETTINGS_ENABLEUSERACCESSTOKENS
value: "true" # Разрешить создание access токенов для пользователей
- name: MM_SERVICESETTINGS_GIPHYSDKKEY
value: "" # Ключ SDK Giphy для интеграции с Giphy
## Следующие переменные требуются для запуска в кластерном режиме:
# - name: MM_CLUSTERSETTINGS_ENABLE
# value: "true"
# - name: MM_CLUSTERSETTINGS_CLUSTERNAME
# value: "loop-example-cluster"
# - name: MM_CLUSTERSETTINGS_ENABLEEXPERIMENTALGOSSIPENCRYPTION
# value: "true"
# - name: MM_CLUSTERSETTINGS_ENABLEGOSSIPCOMPRESSION
# value: "false"
# - name: MM_CLUSTERSETTINGS_READONLYCONFIG
# value: "false"
## Дополнительные аннотации для Pod'а
extraPodAnnotations: {}
serviceAccount:
create: false
name:
annotations: {}