Тема развертывания Spring Boot приложения в Kubernetes кластере уже не новая и на многих ресурсах, включая данный , написано немало уже статей с примерами. Сегодня я бы хотел рассказать не только развертывании самого приложения ,но и о сопутствующих сервисах ,а именно : база данных, балансировщик нагрузки ,а также система сбора и агрегирования логов.
Более подробно о всех компонентах :
Spring Boot приложение, использующее в качестве БД PostgreSQL
Docker образ сервера базы данных
Docker Grafana (dashboard для отображения логов)
Docker образ Loki
(система сбора логов)Promtail (агент для отсылки логов в Loki).
Kubernetes cluster будет развернут при помощи microk8s. В качестве балансировщика нагрузки и по совместительству web-сервера будет выступать nginx, а точнее nginx-ingress-controller, который есть в microk8s.

Рассмотрим развертывания каждого компонента по отдельности.
Шаг 1: База данных
Для базы данных используем следующий yaml
apiVersion: v1 kind: Service metadata: name: db spec: ports: - port: 5432 selector: app: db clusterIP: None --- apiVersion: apps/v1 kind: Deployment metadata: name: db spec: selector: matchLabels: app: db strategy: type: Recreate template: metadata: labels: app: db spec: containers: - image: postgres:9.6 name: db env: - name: POSTGRES_USER value: admin - name: POSTGRES_PASSWORD value: admin - name: POSTGRES_DB value: dbname ports: - containerPort: 5432 name: db
В файле сразу описан и сервис, и развертывание базы. Как образ ,использован образ Postgres 9.6
Для создания развертывания исполним командуkubectl apply -f db.yaml
Шаг 2: Grafana
Для Grafana используем следующий yaml
apiVersion: v1 kind: Service metadata: name: grafana spec: ports: - port: 3000 selector: app: grafana clusterIP: None --- apiVersion: apps/v1 kind: Deployment metadata: name: grafana spec: selector: matchLabels: app: grafana strategy: type: Recreate template: metadata: labels: app: grafana spec: containers: - image: grafana/grafana:master name: grafana ports: - containerPort: 3000 name: grafana
Развертывание похоже на то, что использовано для базы данных. Разница в образе (grafana/grafana:master) и в выставляемом порте.
Аналогично выполним командуkubectl apply -f grafana.yaml
Шаг 3: Loki
Как и выше yaml
apiVersion: v1 kind: Service metadata: name: loki spec: ports: - port: 3100 selector: app: loki clusterIP: None --- apiVersion: apps/v1 kind: Deployment metadata: name: loki spec: selector: matchLabels: app: loki strategy: type: Recreate template: metadata: labels: app: loki spec: containers: - image: grafana/loki:latest name: loki ports: - containerPort: 3100 name: loki
И командаkubectl apply -f grafana.yaml
Шаг 4: Promtail
Для promtail понадобится следующий yaml
apiVersion: v1 kind: List items: - apiVersion: apps/v1 kind: DaemonSet metadata: name: promtail-daemonset spec: selector: matchLabels: name: promtail template: metadata: labels: name: promtail spec: serviceAccount: SERVICE_ACCOUNT serviceAccountName: SERVICE_ACCOUNT volumes: - name: logs hostPath: path: HOST_PATH - name: promtail-config configMap: name: promtail-configmap containers: - name: promtail-container image: grafana/promtail args: - -config.file=/etc/promtail/promtail.yaml volumeMounts: - name: logs mountPath: MOUNT_PATH - name: promtail-config mountPath: /etc/promtail - apiVersion: v1 kind: ConfigMap metadata: name: promtail-config data: promtail.yaml: | clients: - url: http://loki:3100/loki/api/v1/push - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: promtail-clusterrole rules: - apiGroups: [ "" ] resources: - nodes - services - pods verbs: - get - watch - list - apiVersion: v1 kind: ServiceAccount metadata: name: promtail-serviceaccount - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: promtail-clusterrolebinding subjects: - kind: ServiceAccount name: promtail-serviceaccount namespace: default roleRef: kind: ClusterRole name: promtail-clusterrole apiGroup: rbac.authorization.k8s.io
И командаkubectl apply -f promtail.yaml
Шаг 5: Ingress
Для nginx используем следующий файл.
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: server spec: rules: - http: paths: - path: /server pathType: Prefix backend: service: name: server port: number: 8024 - path: /grafana pathType: Prefix backend: service: name: grafana port: number: 3000
И команду kubectl apply -f ingress.yaml
Шаг 7: Приложение
Этот шаг не похож ни на один предыдущий. Здесь не будет использовано ни одного yaml и ни одного готового Docker образа. Нужное нам развертывание будет создано сразу после процесса сборки. Для этого используется Maven + jkube maven plugin

Рассмотрим процесс сборки . Сначала install собирает jar с приложением, затем k8s:resource генерирует ресурсы, потом k8s:build создаст Docker oбраз и k8s:deploy сделает развертывание.
Ниже пример конфигурации плагина для данного процесса
<profile> <id>kube</id> <properties> <spring.profiles.active>docker</spring.profiles.active> </properties> <build> <plugins> <plugin> <groupId>org.eclipse.jkube</groupId> <artifactId>kubernetes-maven-plugin</artifactId> <version>1.1.1</version> <configuration> <verbose>true</verbose> <images> <image> <name>imagename:latest</name> <alias>some-alias/alias> <build> <maintainer>John Smith</maintainer> <from>fabric8/java-centos-openjdk11-jre</from> <assembly> <inline> <baseDirectory>/deployments</baseDirectory> </inline> </assembly> </build> </image> </images> </configuration> <executions> <execution> <id>run</id> <goals> <goal>resource</goal> <goal>build</goal> <goal>deploy</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </profile>
Образ описан под тегом image. Также можно использовать один из генераторов.
Также необходимо создать сервис. Для этого выполнимkubectl expose deployment server --type=LoadBalancer --name=server --port=<some-port>
Почему был использован данный способ создания сервиса, ведь сервис можно сконфигурировать также в плагине? На данный момент, была обнаружена ошибка в момент старта приложения в поде : вместо ip сервиса, приходит строка tcp://<ip-service>. Это приводит к NumberFormatException.
Шаг 8: Проверка доступа
В браузере или с помощью curl проверить, что localhost возвращает страницу приложения, localhost/grafana покажет страницу входа в Grafana.
Шаг 9: Отобразить логи
Для этого необходимо войти в Grafana с помощью логина/пароля admin . После необходимо указать ,в качестве источника данных Loki(http://loki:3000). Затем в explore ввести {app="название-приложения"} .
P.S.
Сбор логов был основан на данной статье.