В прошлой статье мы создали приложение с функциями документооборота на фреймворке 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.