Тема развертывания 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.
Сбор логов был основан на данной статье.