Кластер Kubernetes

В данном разделе описывается развертывание приложения Jmix в кластер Kubernetes. Мы будем использовать одноузловой кластер, предоставляемый minikube. Вы можете установить его на свою машину разработки, чтобы протестировать развертывание локально.

Руководство сфокусировано на настройке приложения для запуска в Kubernetes. По нему вы можете подготовить свое приложение к такому развертыванию без каких-либо предварительных знаний о Kubernetes. Однако для запуска реальных производственных развертываний вы должны быть знакомы с этой технологией.

Настройка приложения

Настройка создания образа

Плагин Spring Boot Gradle предоставляет задачу bootBuildImage, которая собирает ваше приложение и создает образ Docker. Чтобы указать имя образа, добавьте следующий раздел в файл build.gradle:

bootBuildImage {
    imageName = 'mycompany/sample-app'
}

Настройка Hazelcast

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

Все кэши Jmix поддерживают координацию через Hazelcast. В данном руководстве мы будем использовать Hazelcast во встроенном режиме вместе с плагином hazelcast-kubernetes для автоматического обнаружения в среде Kubernetes.

Следуйте приведенной ниже инструкции, чтобы настроить Hazelcast для координации кэшей в кластере Kubernetes.

  1. Добавьте зависимости Hazelcast в build.gradle:

    implementation 'com.hazelcast:hazelcast'
    implementation 'com.hazelcast:hazelcast-kubernetes:2.2.3'
  2. Укажите Hazelcast в качестве провайдера JCache, добавив следующее свойство в файл application.properties:

    spring.cache.jcache.provider = com.hazelcast.cache.HazelcastMemberCachingProvider
  3. Создайте файл hazelcast.yaml в корне resources:

    hazelcast:
      network:
        join:
          multicast:
            enabled: false
          kubernetes:
            enabled: false
            service-name: sample-app-service

Свойство hazelcast.network.join.kubernetes.service-name должно указывать на службу приложения, определенную в файле конфигурации сервиса приложения.

Обратите внимание, что свойству hazelcast.network.join.kubernetes.enabled в этом файле присвоено значение false. Это сделано для того, чтобы иметь возможность запускать приложение локально без Kubernetes. Свойство устанавливается в true, когда приложение фактически запускается в Kubernetes, используя переменную окружения HZ_NETWORK_JOIN_KUBERNETES_ENABLED в файле конфигурации сервиса приложения.

Создание конфигурационных файлов Kubernetes

Создайте папку k8s в корне проекта и добавьте в нее перечисленные ниже файлы.

Конфигурация службы базы данных

Этот файл определяет службу базы данных PostgreSQL с именем sample-db-service.

k8s/db.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: sample-db-config
data:
  db_user: root
  db_password: root
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: sample-db-pvclaim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-db
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sample-db
  strategy: {}
  template:
    metadata:
      labels:
        app: sample-db
    spec:
      volumes:
        - name: sample-db-storage
          persistentVolumeClaim:
            claimName: sample-db-pvclaim
      containers:
      - image: postgres
        name: sample-db
        env:
          - name: POSTGRES_USER
            valueFrom:
              configMapKeyRef:
                name: sample-db-config
                key: db_user
          - name: POSTGRES_PASSWORD
            valueFrom:
              configMapKeyRef:
                name: sample-db-config
                key: db_password
          - name: PGDATA
            value: /var/lib/postgresql/data/pgdata
          - name: POSTGRES_DB
            value: sample
        ports:
          - containerPort: 5432
            name: sample-db
        volumeMounts:
          - name: sample-db-storage
            mountPath: /var/lib/postgresql/data
---
apiVersion: v1
kind: Service
metadata:
  name: sample-db-service
spec:
  type: ClusterIP
  ports:
    - port: 5432
  selector:
    app: sample-db

Конфигурация службы приложения

Этот файл определяет службу приложения с именем sample-app-service. Использует образ Docker mycompany/sample-app для приложения.

k8s/app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
        - image: mycompany/sample-app
          imagePullPolicy: IfNotPresent
          name: sample-app
          env:
            - name: DB_USER
              valueFrom:
                configMapKeyRef:
                  name: sample-db-config
                  key: db_user
            - name: DB_PASSWORD
              valueFrom:
                configMapKeyRef:
                  name: sample-db-config
                  key: db_password
            - name: DB_HOST
              value: sample-db-service
            - name: SPRING_PROFILES_ACTIVE
              value: k8s
            - name: HZ_NETWORK_JOIN_KUBERNETES_ENABLED
              value: "true"
          lifecycle:
            preStop:
              exec:
                command: [ "sh", "-c", "sleep 10" ]
          ports:
            - containerPort: 8080
            - containerPort: 5701
---
apiVersion: v1
kind: Service
metadata:
  name: sample-app-service
spec:
  type: NodePort
  ports:
    - port: 8080
      name: sample-app-app
    - port: 5701
      name: sample-app-hazelcast
  selector:
    app: sample-app

Настройка балансировщика нагрузки

Пользовательский интерфейс Jmix ожидает, что все запросы от пользователя поступают на один и тот же сервер. В кластерной среде с несколькими серверами для этого требуется балансировщик нагрузки с "липкими сессиями" (sticky sessions). Ниже приведена конфигурация NGINX Ingress Controller с session affinity.

k8s/balancer.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: balancer
  annotations:
    nginx.ingress.kubernetes.io/affinity: "cookie"
    nginx.ingress.kubernetes.io/affinity-mode: "persistent"
    nginx.ingress.kubernetes.io/session-cookie-hash: "sha1"
    nginx.ingress.kubernetes.io/session-cookie-expires: "172800"
    nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"
spec:
  defaultBackend:
    service:
      name: sample-app-service
      port:
        number: 8080
  rules:
    - http:
        paths:
          - backend:
              service:
                name: sample-app-service
                port:
                  number: 8080
            path: /
            pathType: Prefix

Настройка контроля доступа Hazelcast

k8s/hazelcast-rbac.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: hazelcast-cluster-role
rules:
  - apiGroups:
      - ""
    resources:
      - endpoints
      - pods
      - nodes
      - services
    verbs:
      - get
      - list

---

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

  1. Убедитесь, что на вашей машине запущен Docker. Приведенная ниже команда отображает версию Docker:

    docker -v

    Если команда не выполняется, обратитесь к документации Docker Docker о том, как установить и запустить Docker.

  2. Установите Minikube, как описано в инструкции по установке.

  3. Запустите Minikube:

    minikube start --vm-driver=virtualbox
  4. Включите Ingress Controller:

    minikube addons enable ingress
  5. Настройте инструмент командной строки Kubernetes для использования Minikube:

    kubectl config use-context minikube
  6. Чтобы открыть панель мониторинга Kubernetes в веб-браузере, выполните следующую команду в отдельном терминале:

    minikube dashboard

Сборка и запуск приложения

  1. Соберите образ Docker:

    ./gradlew bootBuildImage
  2. Загрузите образ в Minikube:

    minikube image load mycompany/sample-app:latest
  3. Примените конфигурационные файлы Kubernetes:

    kubectl apply -f ./k8s
  4. Чтобы увеличить количество экземпляров приложения, используйте следующую команду:

    kubectl scale deployment sample-app --replicas=2
  5. Определите IP-адрес кластера:

    minikube ip

    Используйте этот адрес для открытия приложения в веб-браузере, например: http://192.168.99.100