decorate image
decorate image
В связи с техническими работами сайт будет недоступен 21 декабря 2024 с 12:00 до 12:30 МСК
Все статьи

Разработка системы документооборота: выходим в облако

В прошлой статье мы создали приложение с функциями документооборота на фреймворке Jmix и развернули его, используя сервисы хостера Облакотека с применением системы хранения файлов S3, которая обеспечивает высокий уровень отказоустойчивости и масштабируемости. Однако, само приложение и база данных были развернуты на отдельных виртуальных серверах, что по современным представлениям само по себе не дает гарантий отказоустойчивости и масштабируемости. Поэтому в рамках данной статьи развернем то же приложение на Kubernetes-кластере, как системе, предназначенной для обеспечения бесперебойной работы. Облакотека предоставляет сервис развертывания кластеров на основе Deckhouse. Она разрабатывается российским разработчиками и сертифицирована по 4-му классу безопасности, что позволяет использовать ее для гражданских объектов критической инфраструктуры. Deckhouse поставляется сразу со средствами мониторинга нагрузки и рядом других собственных решений, в том числе для развертывания приложений. Это делает его, с одной стороны, узнаваемым для специалистов, имеющих опыт с Kubernetes, а с другой — обладающим своей особенной изюминкой, впрочем, как и любая другая реализация или конфигурация.

Создаем кластер

Для пробы нам подойдет Single-master с параметрами чуть выше минимальных: RAM от 8 Гб, от 100 Гб Системный диск, от 8-и ядер CPU и т.д.

Настраиваем тулинг

На локальный компьютер устанавливаем утилиты для работы с кластером, такие как kubectl. Для этого лучше воспользоваться официальной инструкцией.
Подключаем kubeconfig из панели кластера в панели управления Облакотеки.

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

Там же приведены ссылки на инструкции по настройке.

Устанавливаем утилиту krew, которая позволяет управлять плагинами kubectl.

export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH" 

Эта строчка будет добавлять путь к исполняемым файлам утилиты в системную переменную PATH при начале каждой консольной сессии. Без этого пришлось бы выполнять ее каждый раз или вводить полный путь до программ krew в своих командах.
Теперь мы можем установить плагин oidc-login.

kubectl krew install oidc-login

Значения из панели управления, которые можно увидеть за кнопкой “Получить kubeconfig”, копируем в файл ~/.kube/config. Выглядят они примерно вот так:

Теперь надо установить werf. Это утилита для развертывания образов приложений в кластере. Она надстраивает helm чарты и подключает некоторые дополнительные возможности, объединяя функционал управления Docker-образами и их конфигурацию и развертывание в кластере.
Устанавливаем werf также по официальной инструкции
В ~/.bashrc добавляем строчку:

source $("/home/oblozhko/bin/trdl" use werf "2" "stable")

При помощи kubectl создаем неймспейс для приложения

kubectl create namespace sm-dms

Для развертывания приложений из собственных образов нам потребуется сервис docker-registry. Во многих компаниях уже используют on-premise решения, такие как Artifactory, Nexus, Harbor. Также некоторые хостинг-провайдеры начали предоставлять подобную услугу.
Для кластера важно, чтобы реестр образов использовал HTTPS с доверенным SSL-сертификатом.
Для наших пробных развертываний мы воспользуемся бесплатным сервисом на GitHub.com.
Надо создать ключ доступа в профиле с настройками вашей учетной записи.
В Settings заходим в Developer settings и выбираем Personal access tokens и в нем Tokens(classic) создаем новый, копируем из него пароль
В кластере создаем секрет для авторизации, не забыв указать только что созданный неймспейс.

kubectl create secret docker-registry registrysecret --docker-server='docker.pkg.github.com' --docker-username='syncro' --docker-password='ghp_tbRlsgCEXXXXXXXXXXXXXXXX2Y72Qq' --namespace sm-dms

Настраиваем конфигурации развертывания

Werf сам собирает образы, для чего использует Dockerfil’ы, которые почти совместимы с обычными.
Добавляем Dockerfile для сборки проекта

FROM openjdk:21-slim as builder 
# Copy the source code of the application. 
COPY . /src 
WORKDIR /src 
# Run Maven to build the project. 
RUN ./gradlew -Pvaadin.productionMode=true clean build -x test --no-daemon
# The image to deploy to the cluster. 
FROM openjdk:21-slim 
WORKDIR /app 
# Copy the project executable from the build image. 
COPY --from=builder /src/build/libs/sm-dms-0.0.1-SNAPSHOT.jar app.jar 
EXPOSE 8080 
ENTRYPOINT ["java","-jar","/app/app.jar"] 

Создаем конфигурацию werf.yaml

project: sm-dms  
configVersion: 1 
---
image: docker.pkg.github.com/syncro/sm-dms 
dockerfile: Dockerfile 

Она как раз описывает, какие образы и реестры будут собираться и из каких Dockefile’ов.
docker.pkg.github.com — это адрес реестра Docker-образов сервиса GitHub, если у вас другой реестр, то надо использовать его адрес там, где указывается имя Docker-образа.
syncro — это логин моей учетной записи на GitHub, у вас будет свое значение
sm-dms — имя репозитория проекта нашего приложения, т.е. по адресу https://github.com/syncro/sm-dms должен находиться проект, который мы развертываем.
Кажется, чтобы реестр github’a работал надо пушить все в его репозиторий. Сделайте его приватным, чтобы секреты не утекли в публичный доступ.
Werf также дает возможность подключать внешние репозитории исходных кодов, что позволяет отделить конфигурации от исходных кодов приложений, но мы для простоты будем делать все в одном проекте.
Добавляем шаблоны Helm в /.helm/templates
В файле deployment.yaml

apiVersion: apps/v1 
kind: Deployment 
metadata: 
  name: sm-dms 
spec: 
  replicas: 1 
  selector: 
    matchLabels: 
      app: sm-dms 
  template: 
    metadata: 
      labels: 
        app: sm-dms 
    spec: 
      imagePullSecrets: 
      - name: registrysecret 
      containers: 
      - name: sm-dms 
        image: docker.pkg.github.com/syncro/sm-dms 
        env: 
          - name: SPRING_PROFILES_ACTIVE 
            value:  
          - name: DB_HOST 
            value:  
          - name: DB_PORT 
            value: "" 
          - name: DB_NAME 
            value:  
          - name: DB_PARAMS 
            value:  
          - name: DB_USER 
            value:  
          - name: DB_PASSWORD 
            value:  
          - name: S3_KEY 
            value:  
          - name: S3_SECRET_KEY 
            value:  
          - name: S3_REGION 
            value:  
          - name: S3_BUCKET 
            value:  
          - name: S3_ENDPOINT_URL 
            value:  
        ports: 
          - containerPort: 8080 

В файле service.yaml

apiVersion: v1 
kind: Service
metadata:
  name: sm-dms
spec:
  selector:
    app: sm-dms
  ports:
  - name: http
    port: 8080 

В файле ingress.yaml

apiVersion: networking.k8s.io/v1 
kind: Ingress 
metadata: 
  annotations: 
    kubernetes.io/ingress.class: nginx 
  name: sm-dms 
spec: 
  rules: 
  - host: app.smdmsk.k8s.oblakoteka.ru 
    http: 
      paths: 
      - path: / 
        pathType: Prefix 
        backend: 
          service: 
            name: sm-dms 
            port: 
              number: 8080 

Реальные значения параметров мы указываем в отдельном файле values.yaml, который должен располагаться в .helm

env: 
  SPRING_PROFILES_ACTIVE: prod 
  DB_HOST: 192.168.0.38 
  DB_PORT: 5432 
  DB_NAME: smdms 
  DB_PARAMS: stringtype=unspecified 
  DB_USER: smdms 
  DB_PASSWORD: A9m5uo5W 
  S3_KEY: 7L1A11111111110KAOJWD 
  S3_SECRET_KEY: cfi25111111111G+Q+GWdr8bc7O 
  S3_REGION: ee-east 
  S3_BUCKET: smdmsdocs 
  S3_ENDPOINT_URL: https://storage.oblakoteka.ru:443 

Параметры S3 мы берем из данных предыдущей статьи, воспользовавшись преимуществом масштабируемости данного сервиса: нам не надо будет переносить файлы, которые мы насоздавали за все время.
Параметры базы данных указаны с опережением, т.к. вопрос используемой СУБД мы решим далее.

Доменное имя

Чтобы наше приложение открывалось отовсюду из интернета, ему надо определить доменное имя. Пусть это будет app.smdmsk.k8s.oblakoteka.ru — в Облакотеке при создании кластера Kubernetes бесплатно выделяется указанное пользователем доменное имя четвёртого уровня и wildcard A-запись в этой DNS зоне. Это позволит нам использовать для публикации любое доменное имя на пятом уровне. При желании (и наличии) можно использовать и своё доменное имя второго уровня — порядок действий будет аналогичный.
Сначала проверим, что доменное имя работает:

nslookup  app.smdmsk.k8s.oblakoteka.ru 
Name:   app.smdmsk.k8s.oblakoteka.ru 
Address: 37.18.77.154 

Подключаем базу данных

В рамках этой статьи мы будем разворачивать базу данных на отдельном обычном виртуальном сервере, чтобы оставить пока развлечения с настройкой аутентификации в базе с использованием ключей за кадром.
Когда виртуалка создалась, прокидываем порты для ssh(22) и порты базы данных (5432) вовне.
Заходим на сервер по ssh и устанавливаем пакет для PostgreSQL.

apt install postgresql

В /etc/postgresql/16/main/postgresql.conf раскомментировал listen_address и устанавливаем значение в *
В /etc/postgresql/16/main/pg_hba.conf добавляем строчку, разрешающую подключения извне, для реального кейса вам следует заменить маску на внутреннюю подсеть.

host    all     all     0.0.0.0/0       md5

Перезапускаем сервер

systemctl restart postgresql

Залогинимся в базу локально и создадим базу и пользователя

su - postgres 
psql 
postgres=# create user smdms with password 'A9m5uo5W'; 
postgres=# create database smdms owner smdms; 

Проверим подключение с локального компьютера:

psql -U smdms -h 37.18.77.154 -W 

Увидев пустую базу, jmix-проект сам развернет на ней все необходимые скрипты для свой работы. Также вы можете развернуть дамп базы из предыдущей статьи, получив его из веб-интерфейса панели управления Облакотеки.

Деплоим

Добавляем /.helm/ в .dockergnore, чтобы его содержимое не попадало в образы.
Werf требует обязательного коммита всех изменений в репозиторий, но пушить каждый раз, кажется, не обязательно

git add . 
git commit --am “werf” 

Теперь собственно команда развертывания, она проверит изменения относительно предыдущей версии и оптимизированно соберет и развернет обновления.

werf converge --repo docker.pkg.github.com/syncro/sm-dms/sm-dms

Обратите внимание, тут кроме адреса реестра используется формат :owner/:repo_name/:image_name, что отличается от того, что мы прописывали везде ранее.
Иногда у меня создавалось впечатление, что Werf не пушит образ в реестр и деплоится старый образ, и тогда я собирал и пушил образ в реестр вручную такими командами:

docker build . -t docker.pkg.github.com/syncro/sm-dms/sm-dms 
docker push docker.pkg.github.com/syncro/sm-dms/sm-dms 

Проверяем

После того, как все задеплоилось и развернулось (что займет некоторое время), мы можем зайти из браузера на адрес http://app.smdmsk.k8s.oblakoteka.ru и увидеть экран входа в приложение.

Если что-то пошло не так, подробности можно узнать, скачав и просмотрев логи за бургер-меню у пода.

Итоги

Таким образом, нам удалось развернуть наше приложение в кластере, создав базу для обеспечения его отказоустойчивости, масштабируемости и управляемости. Для реализации этих возможностей следует обратиться к официальной документации фреймворка Jmix, Deckhouse и Werf.

Jmix - это open-source платформа быстрой разработки бизнес-приложений на Java