Установка системы в кластер Kubernetes

Общая информация

Статья предназначена для системных администраторов и DevOps-инженеров, которые хотят развернуть Юниверс DG в заранее созданном кластере Kubernetes. В статье описаны шаги по настройке и развертыванию компонентов системы, включая базу данных PostgreSQL и поисковую систему OpenSearch.

Внимание

Дисклеймер.

Инструкция содержит примерный порядок действий по установке. Инструкция призвана описать логику действий по установке на простом абстрактном примере.

Предварительные требования

  1. Наличие кластера Kubernetes, минимальная конфигурация которого включает в себя одну master-node и две worker-node.

  2. Отдельный сервер (или несколько) для установки PostgreSQL и OpenSearch с предустановленным Docker и Docker Compose.

  3. Учетные данные для доступа к приватному Docker registry для скачивания образов контейнеров.

Системные требования к кластеру и компонентам

  1. Юниверс DG 2.10 или новее;

  2. Helm v3.14.0 или новее;

  3. Kubernetes v1.27 или новее;

  4. Минимальные требования к кластеру, позволяющие запустить пакет-приложение (helm-chart) с настройками по умолчанию;

  • Три узла Kubernetes с настройками, близкими к настройкам по умолчанию.

  • 4GB RAM для JVM.

  • Все параметры кластера настраиваются.

Подготовка сервера для PostgreSQL и OpenSearch

Создание файла docker-compose.yml

Создайте файл docker-compose.yml на отдельном сервере с указанным ниже содержанием. Этот файл определяет конфигурацию для запуска PostgreSQL и OpenSearch с использованием Docker Compose.

docker-compose
 setup_json:
 image: docker.universe-data.ru/mirror/chatwork/jq:latest
 entrypoint: >
  sh -c "
  cd /opt/json_configs;
  [ ! -z ${OVERRIDE_JSON:-''} ] &&
  jq -s '.[0] * .[1]' customer.json ${OVERRIDE_JSON}
  > ../customer.json ||
  cp customer.json ../customer.json"
 volumes:
  - ./:/opt

 ui:
 image: ${FRONTEND_IMAGE}
 restart: always
 ports:
  - ${FRONTEND_PORT}:80
 networks:
  - dg_network
 links:
  - dg
 volumes:
  - ${FRONTEND_UE:-/dev/null}:/usr/share/nginx/html/CUX
  - ./customer.json:/usr/share/nginx/html/customer.json
 environment:
  BACKEND_ADDRESS: ${BACKEND_ADDRESS}
  CLIENT_MAX_BODY_SIZE: ${CLIENT_MAX_BODY_SIZE}
  PROXY_SEND_TIMEOUT: ${PROXY_SEND_TIMEOUT}
  PROXY_READ_TIMEOUT: ${PROXY_READ_TIMEOUT}
  SEND_TIMEOUT: ${SEND_TIMEOUT}
  TZ: ${TIMEZONE:-UTC}
 depends_on:
  setup_json:
    condition: service_completed_successfully

dg:
 image: ${BACKEND_IMAGE}
 restart: always
 ports:
  - ${BACKEND_PORT}:8080
 networks:
  - dg_network
 environment:
  GUEST_MODE: ${GUEST_MODE}
  POSTGRES_ADDRESS: postgres-dg:5432
  POSTGRES_USERNAME: ${DG_POSTGRES_USER}
  POSTGRES_PASSWORD: ${DG_POSTGRES_PASSWORD}
  DATABASE_NAME: ${DG_POSTGRES_DB_NAME}
  SEARCH_CLUSTER_ADDRESS: opensearch-dg:9200
  SEARCH_CLUSTER_NAME: docker-cluster
  EMAIL_ENABLED: ${RESTORE_EMAIL_ENABLED}
  EMAIL_SERVER_HOST: ${RESTORE_EMAIL_SERVER_HOST}
  EMAIL_SERVER_PORT: ${RESTORE_EMAIL_SERVER_PORT}
  EMAIL_USERNAME: ${RESTORE_EMAIL_USERNAME}
  EMAIL_PASSWORD: ${RESTORE_EMAIL_PASSWORD}
  EMAIL_FRONTEND_URL: ${RESTORE_EMAIL_FRONTEND_URL}
  EMAIL_SSL_ENABLE: ${RESTORE_EMAIL_SSL_ENABLE}
  EMAIL_STARTTLS_ENABLE: ${RESTORE_EMAIL_STARTTLS_ENABLE}
  TZ: ${TIMEZONE:-UTC}
 depends_on:
  postgres-dg:
    condition: service_healthy
  opensearch-dg:
    condition: service_healthy

postgres-dg:
 image: docker.universe-data.ru/mirror/postgres16.3-tsdb2.15.2
 restart: always
 environment:
  POSTGRES_DB: ${DG_POSTGRES_DB_NAME}
  POSTGRES_USER: postgres
  POSTGRES_PASSWORD: postgres
  DG_POSTGRES_USER: ${DG_POSTGRES_USER}
  DG_POSTGRES_PASSWORD: ${DG_POSTGRES_PASSWORD}
  TZ: ${TIMEZONE:-UTC}
 ports:
  - ${POSTGRES_OUTER_PORT}:5432
 networks:
  - dg_network
 volumes:
  - ./init-db.sh:/docker-entrypoint-initdb.d/initdb.sh
  - dg-postgres-data:/var/lib/postgresql/data
 command: postgres -N 350 -c max_prepared_transactions=350 -c shared_preload_libraries='timescaledb'
 healthcheck:
  test: ["CMD-SHELL", "pg_isready -U postgres -d $DG_POSTGRES_DB_NAME"]
  interval: 10s
  timeout: 1s
  retries: 20

 opensearch-dg:
 image: docker.universe-data.ru/mirror/opensearchproject/opensearch:2.14.0
 restart: always
 environment:
  - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m"
  - "discovery.type=single-node"
  - "DISABLE_SECURITY_PLUGIN=true"
  - "TZ=${TIMEZONE:-UTC}"
 volumes:
  - dg-opensearch-data:/usr/share/opensearch/data
  - ./hunspell:/usr/share/opensearch/config/hunspell/
  - ./synonyms.txt:/usr/share/opensearch/config/synonyms.txt
  - ./plugins:/usr/share/opensearch/plugins-for-install/
  - ./opensearch-install-plugins-and-start.sh:/usr/share/opensearch/opensearch-install-plugins-and-start.sh
 entrypoint: /usr/share/opensearch/opensearch-install-plugins-and-start.sh
 ulimits:
  memlock:
    soft: -1
    hard: -1
 ports:
  - ${OPENSEARCH_HTTP_OUTER_PORT}:9200
 networks:
  - dg_network
 healthcheck:
  test: >
    bash -c "curl http://localhost:9200 | grep '\"cluster_name\"'"
  interval: 10s
  timeout: 2s
  retries: 20

 volumes:
 dg-postgres-data:
 driver: local
 dg-opensearch-data:
 driver: local

 networks:
  dg_network:
  driver: bridge

Параметры command: postgres -c max_prepared_transactions=1000 -c max_connections=1000 представлены из расчёта 4 реплик backend-приложений (dg-deployment).

Примечание: Перед запуском Docker Compose убедитесь, что все используемые файлы и каталоги (init-db.sh, hunspell, plugins, opensearch-install-plugins-and-start.sh) присутствуют в соответствующих директориях. Это стандартный набор поставки для разворачивания DG через Docker.

Запуск Docker Compose

  1. Запустите сервисы, используя следующую команду: docker-compose up -d

  2. Команда запустит PostgreSQL и OpenSearch в отдельных контейнерах, сеть для их взаимодействия, и настроит необходимые параметры, такие как размеры памяти и переменные окружения.

  3. Проверьте работоспособность сервисов командой: docker ps

Статус контейнера

Рисунок 1 – Статус контейнера

Развертывание Юниверс DG в Kubernetes

Подготовка конфигурационных файлов для Kubernetes

Для развертывания Юниверс DG в вашем кластере Kubernetes необходимо подготовить набор манифестов, описывающих необходимые ресурсы: StatefulSets, ConfigMaps, Services, Secrets, Deployments, а также ресурсы для настройки RBAC (Role-Based Access Control).

Создание ConfigMap и Secret

Перед созданием основных компонентов системы, вам необходимо создать ConfigMap и Secret, которые будут содержать конфигурационные параметры и секретные данные соответственно.

Создайте файлы config-map-be.yaml для определения ConfigMap для BE.

ConfigMap для dg-deployment:

configmap be
apiVersion: v1
kind: ConfigMap
metadata:
name: config-dg-deployment
data:
GUEST_MODE: "false"
POSTGRES_ADDRESS: "192.168.1.109:15431"
POSTGRES_USERNAME: "postgres"
POSTGRES_PASSWORD: "postgres"
DATABASE_NAME: "postgres"
SEARCH_CLUSTER_ADDRESS: "192.168.1.109:19201"
SEARCH_CLUSTER_NAME: "docker-cluster"
EMAIL_ENABLED: "false"
EMAIL_SERVER_HOST: "localhost"
EMAIL_SERVER_PORT: "5025"
EMAIL_USERNAME: "universe@example.com"
EMAIL_PASSWORD: "password"
EMAIL_FRONTEND_URL: "''"
EMAIL_SSL_ENABLE: "true"
EMAIL_STARTTLS_ENABLE: "false"
JAVA_TOOL_OPTIONS: ""
CACHE_AUTO_DETECTION_ENABLED: "true"
CACHE_GROUP: "unidata"
CACHE_PASSWORD: "password"
CACHE_PORT: "5701"
CACHE_PORT_AUTOINCREMENT: "true"
SYSTEM_NODE_ID: ""
CACHE_PUBLIC_ADDRESS: ""
TZ: "MSK"
CACHE_KUBERNETES_ENABLED: "true"
CACHE_KUBERNETES_SERVICE_NAME: "dg-service"
CACHE_TCP_IP_ENABLED: "false"
CACHE_TCP_IP_MEMBERS: ""
CACHE_DIAGNOSTICS_ENABLED: "true"
CACHE_SECURITY_RECOMMENDATIONS: "DEBUG"
CACHE_JET_ENABLED: "true"
CACHE_SOCKET_BIND_ANY: "false"
CACHE_REST_ENABLED: "true"
CACHE_INTEGRITY_CHECKER_ENABLED: "true"

Необходимо указать корректные настройки для подключения к базе PostgreSQL и OpenSearch (указать ip и порт сервера,на которым мы ранее запустили docker-compose с указанными сервисами).

  • POSTGRES_ADDRESS: "192.168.1.109:15431"

  • SEARCH_CLUSTER_ADDRESS: "192.168.1.109:19201"

Остальные параметры являются базовыми и их можно не трогать.

Создайте файлы config-map-fe.yaml для определения ConfigMap для FE.

ConfigMap для ui-deployment:

configmap fe
apiVersion: v1
kind: ConfigMap
metadata:
name: ui-configmap
data:
customer.json: |
   "APP_TYPE": "systemAdmin,dataSteward,dataAdmin",
   "serverUrl": "/universe-backend/api/",
   "BULK_SELECTION_LIMIT": 30,
   "CHECK_SOURCESYSTEM_WEIGHT_UNIQUE": false,
   "OVERRIDES": [],
   "EXTERNAL_MODULES": [],
   "WIKI_HOST": null,
   "WIKI_ENABLED": true

Создайте файл secret.yaml для определения Secret, содержащего данные для доступа к Docker registry:

secret
apiVersion: v1
kind: Secret
metadata:
name: my-regcred
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: ewogICAgImF1dGhzIjogewoJImRvY2tlci50ZXN0LnJ1IjogewoJICAgICJhdXRoIjogIlpHOWphMlZ5T21oVmFXODNOalUxUjJKbGRFQlBUM0F3TW0wOVBRPT0iCgl9CiAgICB9Cn0=

Для получения <base64-encoded-json>, необходимо выполнить команду echo -n '{"auths": {"docker.test.ru": {"username": "docker", "password": "hUio7655Gbet@OOp02m=="}}}' | base64.

Пример:

  • Адрес Docker registry: docker.test.ru

  • Пользователь: docker

  • Пароль: hUio7655Gbet@OOp02m==

Кодируем связку "docker:hUio7655Gbet@OOp02m==" в base64, получаем ZG9ja2VyOmhVaW83NjU1R2JldEBPT3AwMm09PQ==.

Далее кодируем по примеру связку в base64:

   {
   "auths": {
   "docker.test.ru": {
       "auth": "ZG9ja2VyOmhVaW83NjU1R2JldEBPT3AwMm09PQ=="
   }
   }
}

Получаем .dockerconfigjson:

  • ewogICAgImF1dGhzIjogewoJImRvY2tlci50ZXN0LnJ1IjogewoJICAgICJhdXRoIjogIlpHOWphMlZ5T21oVmFXODNOalUxUjJKbGRFQlBUM0F3TW0wOVBRPT0iCgl9CiAgICB9Cn0=

Развертывание основного приложения и его зависимостей

StatefulSet для Юниверс DG: Создайте файл dg-statefulset.yaml для определения StatefulSet, который будет запускать основные компоненты Юниверс DG.

dg-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: dg-deployment
spec:
replicas: 2
selector:
matchLabels:
 app: dg
template:
 metadata:
 labels:
   app: dg
 spec:
 containers:
 - name: dg-container
   image: docker.test.ru/universe/backend:v2.10.0
   ports:
   - containerPort: 8080
   envFrom:
   - configMapRef:
       name: config-dg-deployment
   env:
   - name: SYSTEM_NODE_ID
     valueFrom:
       fieldRef:
         fieldPath: metadata.name
   resources:
     requests:
       ephemeral-storage: "2Gi"
     limits:
       ephemeral-storage: "3Gi"
 imagePullSecrets:
 - name: my-regcred

http://docker.test.ru/universe/backend:v2.10.0 - образ с необходимой версией платформы.

Service для обнаружения подов Hazelcast: Создайте файл dg-service.yaml для определения Service, который будет использоваться для обнаружения подов Hazelcast в кластере.

dg-service.yaml
apiVersion: v1
kind: Service
metadata:
name: dg-service
spec:
selector:
app: dg
ports:
- protocol: TCP
port: 5701

Service для балансировки трафика на порт 8080 подов dg-deployment:

Создайте файл dg-ui-service.yaml

dg-ui-service.yaml
apiVersion: v1
kind: Service
metadata:
name: dg-ui-service
spec:
selector:
app: dg
ports:
- protocol: TCP
port: 9081
targetPort: 8080
type: LoadBalancer

Deployment для пользовательского интерфейса (UI):

Создайте файл ui-deployment.yaml для определения Deployment, который будет запускать пользовательский интерфейс Юниверс DG.

ui-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: ui-deployment
spec:
replicas: 1
selector:
matchLabels:
 app: ui
template:
 metadata:
 labels:
   app: ui
 spec:
 containers:
 - name: ui
   image: docker.test.ru/universe/frontend:v2.10.0
   ports:
   - containerPort: 80
   env:
   - name: BACKEND_ADDRESS
     value: "http://dg-ui-service:9081"
   - name: CLIENT_MAX_BODY_SIZE
     value: "100m"
   - name: PROXY_SEND_TIMEOUT
     value: "600s"
   - name: PROXY_READ_TIMEOUT
     value: "600s"
   - name: SEND_TIMEOUT
     value: "600s"
   - name: TIMEZONE
     value: "Europe/Moscow"
   volumeMounts:
   - name: customer-config
     mountPath: /usr/share/nginx/html/customer.json
     subPath: customer.json
   resources:
     requests:
       ephemeral-storage: "500Mi"
     limits:
       ephemeral-storage: "800Mi"
 volumes:
 - name: customer-config
   configMap:
     name: ui-configmap
 imagePullSecrets:
 - name: my-regcred

http://docker.test.ru/universe/frontend:v2.10.0 - образ с необходимой версией платформы.

RBAC для Hazelcast:

Создайте файлы hazelcast-cluster-role.yaml и hazelcast-cluster-role-binding.yaml для настройки доступа Hazelcast к ресурсам кластера Kubernetes.

hazelcast-cluster-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: hazelcast-cluster-role
rules:
- apiGroups:
- ""
 # Access to apps API is only required to support automatic cluster state management
 # when persistence (hot-restart) is enabled.
- apps
resources:
 - endpoints
 - pods
 - nodes
 - services
 # Access to statefulsets resource is only required to support automatic cluster state management
 # when persistence (hot-restart) is enabled.
 - statefulsets
verbs:
 - get
 - list
 # Watching resources is only required to support automatic cluster state management
 # when persistence (hot-restart) is enabled.
 - watch
 - apiGroups:- "discovery.k8s.io"resources:
 - endpointslices verbs:
 - get
 - list
hazelcast-cluster-role-binding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: hazelcast-cluster-role-binding
roleRef:
 apiGroup: rbac.authorization.k8s.io
 kind: ClusterRole
 name: hazelcast-cluster-role
subjects:
- kind: ServiceAccount
  name: default
  namespace: default

Применение конфигурации в Kubernetes

После подготовки всех необходимых файлов, примените их в вашем кластере Kubernetes с помощью одной команды "kubectl apply -f . " находясь в корне с файлами.

При ручном применении или изменении манифеста можно запускать с параметром -f:

kubectl apply -f config-map-be.yaml
kubectl apply -f config-map-fe.yaml
kubectl apply -f secret.yaml
kubectl apply -f dg-statefulset.yaml
kubectl apply -f dg-service.yaml
kubectl apply -f dg-ui-service.yaml
kubectl apply -f ui-deployment.yaml
kubectl apply -f ui-service.yaml
kubectl apply -f hazelcast-cluster-role.yaml
kubectl apply -f hazelcast-cluster-role-binding.yaml

Проверка статуса развертывания

После применения всех конфигураций и манифестов важно убедиться, что все компоненты были успешно развернуты и работают корректно. Используйте следующие команды для проверки статуса подов и сервисов:

  • kubectl get pods

  • kubectl get services

Команды покажут список всех запущенных подов и сервисов в вашем кластере, а также их текущее состояние. Убедитесь, что все компоненты имеют статус Running и нет ошибок в их логах.

Примечания:

  • Для доступа к фронтенду используйте адрес, предоставляемый сервисом ui-service(порт nodePort: 30082), и убедитесь, что все компоненты системы функционируют корректно.

  • В случае возникновения проблем, проверьте логи соответствующих компонентов и конфигурации Kubernetes, чтобы выявить и устранить причину неполадок.

Масштабирование

Для масштабирования (scaling) вашего развертывания dg-deployment в Kubernetes, вы можете изменить количество реплик в StatefulSet. Масштабирование поможет вам увеличить или уменьшить количество подов в вашем развертывании в зависимости от нагрузки или требований к доступности.

Изменение количества реплик в StatefulSet

  1. Использование kubectl для обновления StatefulSet

  • Вы можете обновить количество реплик в вашем StatefulSet с помощью команды kubectl scale. Допустим, вы хотите увеличить количество реплик до 3. Для этого используйте следующую команду:

    • kubectl scale statefulsets dg-deployment --replicas=3

  • Эта команда изменит количество реплик в StatefulSet на 3. Kubernetes автоматически запустит дополнительные поды, чтобы соответствовать заданному числу реплик.

  1. Редактирование манифеста StatefulSet

  • Альтернативный способ масштабирования — изменение самого манифеста StatefulSet. Для этого откройте ваш манифест в текстовом редакторе и найдите секцию spec.replicas. Измените значение на желаемое количество реплик. Например:

dg-backend-deployment.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: dg-deployment
spec:
replicas: 3
selector:
matchLabels:
 app: dg

После внесения изменений сохраните файл и примените обновленный манифест в вашем кластере Kubernetes с помощью команды: kubectl apply -f dg-deployment.yaml.

Проверка масштабирования

После масштабирования вы можете проверить статус StatefulSet, чтобы убедиться, что количество подов соответствует заданному числу реплик. Используйте команду: kubectl get statefulsets dg-deployment. Команда отобразит информацию о StatefulSet, включая количество реплик и текущее состояние подов.

При выполнении команды kubectl get pods мы увидим статус контейнера:

NAME                                                READY STATUS              RESTARTS                   AGE
dg-deployment-0                                  1/1        Running              0                      20m
dg-deployment-1                                  1/1        Running              0                      20m
dg-deployment-2                                  1/1        Running              0                       1m
ui-deployment-577c48448b-gz77p                    1/1        Running              0                      20m

Посмотреть логи нового пода "dg-deployment-2" можно введя команду "kubectl logs -f dg-deployment-2".

При выполнении команды, будет видно, что новый под в кластере с остальными:

Members {size:3, ver:3} [
Member [10.233.68.230]:5701 - 7a699622-a7e0-4985-93a9-962e17b02964
Member [10.233.87.94]:5701 - 28ff1db4-6e11-4044-bccd-e5193fafcaf3
Member [10.233.68.231]:5701 - cdc7bed2-0c0c-4413-91bb-408f1e59de16 this

Важные замечания при масштабировании StatefulSet

  • Состояние подов: Поды в StatefulSet управляются индивидуально, и каждый из них имеет свое собственное постоянное хранилище. Убедитесь, что ваша система хранения и приложение корректно обрабатывают масштабирование.

  • Порядок запуска и остановки: StatefulSet гарантирует порядок запуска и остановки подов. Поды создаются и удаляются строго в обратном порядке к их порядковым номерам. Учитывайте это поведение при планировании масштабирования.

Масштабирование StatefulSet — эффективный способ адаптации к изменениям в нагрузке и обеспечения высокой доступности вашего приложения.