Через Helm до Kubernetes

💡 Усі статті, обговорення, новини про DevOps — в одному місці. Приєднуйтесь до DevOps спільноти!

Взято пара мікросервісів і для них прописуються Helm конфіги щоб розгорнути їх в Kubernetes

Для кого

Java-розробники, які починають роботу з Kubernetes.

Що ви знайдете в статті

  • Покроковий розбір міграції з прикладами коду та посиланнями на коміти
  • Як уникнути типових помилок при переході в Kubernetes
  • Практичні рішення проблем, що виникають

Частина I: Знайомство з мікросервісами

Вихідна архітектура системи

Перед міграцією система складалася з двох ключових сервісів, що взаємодіють як партнери у музичному дуеті. Посилання на код

1. Resource Service: Зберігач музики

Роль:

  • Обробляє та зберігає MP3-файли
  • Відповідає за завантаження, скачування та потокову передачу аудіо

Технічні деталі:

  • Мова: Java 17 (LTS)
  • Фреймворк: Spring Boot 3.4.0
  • База даних: PostgreSQL (таблиця resources)
  • Збірка: Maven + двоетапний Dockerfile (на основі eclipse-temurin-17-alpine)

Коміт: Dockerfile

# Етап збірки
FROM maven:3.9.9-eclipse-temurin-17-alpine AS build
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn clean package -Dmaven.test.skip=true

# Етап запуску
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]

2. Song Service: Диригент метаданих

Роль:

  • Керує метаданими треків (назва, автор, жанр)
  • Кожному запису в PostgreSQL-таблиці songs відповідає MP3-файл у Resource Service

Особливості:

  • Зв’язок 1:1: У кожного треку є resource_id, який посилається на файл у Resource Service
  • Каскадне видалення: При видаленні файлу в Resource Service метадані автоматично видаляються

Приклад запиту: DELETE /resources?id=1,2 → (Resource Service видаляє файл) → Song Service видаляє пов’язані метадані

Інфраструктура: Що пов’язує сервіси

Eureka: Диспетчер оркестру

Роль:

  • Реєструє всі мікросервіси (як музикантів в оркестрі)
  • Дозволяє сервісам знаходити один одного за іменем без жорстких IP-адрес

Налаштування:

# docker-compose.yml Song Service
environment:
  EUREKA_CLIENT_SERVICEURL_DEFAULTZONE: ${EUREKA_CLIENT_SERVICEURL}

Spring Cloud Gateway: Маршрутизатор запитів

Роль:

  • Перенаправляє запити до потрібного сервісу (наприклад, /songs/** → Song Service)
  • Спрощує безпеку та моніторинг

Приклад маршруту:

spring.cloud.gateway.routes[0].id=song-service_route
spring.cloud.gateway.routes[0].uri=lb://song-service
spring.cloud.gateway.routes[0].predicates[0]=Path=/songs/**

Чому ці технології?

PostgreSQL:

  • Надійність для транзакцій (каскадне видалення)
  • Підтримка JSON-полів для гнучкості метаданих

Двоетапний Dockerfile:

  • Зменшує розмір фінального образу (видаляються непотрібні залежності після збірки)
  • Безпека: У продакшені використовується мінімалістичний alpine-образ

Spring Boot 3.4:

  • Вбудована інтеграція з Eureka та Spring Cloud Gateway
  • Підтримка Java 17 (патерни запису, покращена продуктивність)

Частина II: Міграція в Kubernetes

Як працювати з цією статтею

🔗 Коміти в репозиторії — кожен етап містить посилання на конкретні коміти

📦 Вихідний код — Репозиторій проекту

💡 Інтерактивність — використовуйте команди та сніппети у формі посилань на коміти зі статті, щоб повторити кроки

Завдання 1: Створити Helm-чарт для resource-service

Створимо Helm-чарт для деплою resource-service в Kubernetes:

  • Chart.yaml — метаінформація
  • values.yaml — параметри (репліки, образ, порт, змінні оточення)
  • templates/deployment.yaml — шаблон Deployment
  • templates/service.yaml — шаблон Service

Посилання на код

Перевірка працездатності

helm install resource ./helm/resource-service --dry-run --debug

Якщо ви бачите приблизно такий результат, значить Helm-чарт валідний:

NAME: resource
LAST DEPLOYED: Thu Apr 24 12:12:00 2025
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None

USER-SUPPLIED VALUES:
{}

COMPUTED VALUES:
env:
- name: EXAMPLE_ENV
  value: default-value
- name: SPRING_DATASOURCE_URL
  value: ""
...

MANIFEST:
---
# Source: resource-service/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: resource-resource-service
spec:
  type: ClusterIP
  ports:
    - port: 8081
      targetPort: 8081
      protocol: TCP
      name: http
  selector:
    app: resource-service
---
# Source: resource-service/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: resource-resource-service
...

Можливі проблеми

Якщо ви бачите zsh: command not found: helm:

Встановіть Helm: macOS (через Homebrew):

brew install helm

Linux (через скрипт):

curl raw.githubusercontent.com/...​m/main/scripts/get-helm-3 | bash

Windows (через Chocolatey):

choco install kubernetes-helm

Після встановлення перевірте:

helm version

Помилка: INSTALLATION FAILED: Kubernetes cluster unreachable

Ця помилка означає, що Helm не може підключитися до Kubernetes-кластера. Можливі рішення:

Варіант 1: Локальний кластер (Minikube)

minikube start
kubectl config use-context minikube

Варіант 2: Docker Desktop Увімкніть Kubernetes в налаштуваннях Docker Desktop.

Варіант 3: Azure AKS


az aks get-credentials --resource-group <your-rg> --name <your-aks-name>

Перевірте підключення:


kubectl get nodes

Завдання 2: Docker push + Helm deploy у кластер

Зберіть та запушіть Docker-образ:


docker build -t your-registry/resource-service:latest .
docker push your-registry/resource-service:latest

Або для локального Docker Desktop:


docker build -t resource-service:latest -f resource-service/Dockerfile resource-service

Оновіть values.yaml з правильним image.repository

Запустіть деплой:


helm upgrade --install resource ./helm/resource-service

Перевірте деплой:


kubectl get pods
kubectl logs <ім'я-пода>

Успішний запуск повинен показати ASCII-арт з котиком в логах:

                 ___
         |\__/,|   (`\        _.-|   |          |\__/,|   (`\
         |o o  |__ _) )      {   |   |          |o o  |__ _) )
       _.( T   )  `  /        "-.|___|        _.( T   )  `  /
 n n._    ((_ `^--' /_<  \         .--'-`-.     _((_ `^--' /_<  \
 <" _ }=- `` `-'(((/  (((/       .+|______|__.-||__)`-'(((/  (((/

Оновлений код: Коміт 47bbb51

Можлива проблема: ImagePullBackOff

Якщо ви бачите статус ImagePullBackOff, Kubernetes не може завантажити вказаний образ. Рішення:

  • Перевірте ім’я образу в values.yaml
  • Встановіть imagePullPolicy: Never для локальних образів:
image:
  repository: resource-service
  tag: latest
  pullPolicy: Never

Оновлений код: Коміт

Завдання 3: Helm-чарт для song-service

Створіть директорію чарту:

helm create helm/song-service

Спростіть структуру (як в resource-service):

  • Видаліть непотрібні шаблони (hpa.yaml, tests/*, serviceaccount.yaml)
  • Оновіть Chart.yaml, values.yaml, і deployment.yaml
  • Ім’я образу: song-service
  • Порт: 8082
  • Змінні оточення: ті, що потрібні для song-service

Протестуйте чарт:

helm install song ./helm/song-service --dry-run --debug

Зберіть Docker-образ:

docker build -t song-service:latest -f song-service/Dockerfile song-service

Задеплойте чарт:

helm upgrade --install song ./helm/song-service

Перевірте деплой:

kubectl get pods
kubectl logs <ім'я-пода>

Успішний запуск повинен показати ASCII-арт з котиком та багом:

                \`*-.
                 )  _`-.
                .  : `. .
                : _   '  \
                ; *` _.   `*-._
                `-.-'          `-.
                  ;       `       `.
                  :.       .        \
                  . \  .   :   .-'   .
                  '  `+.;  ;  '      :
                  :  '  |    ;       ;-.
                  ; '   : :`-:     _.`* ;
         [bug] .*' /  .*' ; .*`- +'  `*'
               `*-*   `*-*  `*-*'

Оновлений код: Коміт

Завдання 4: Відлагодження помилок (Bug-hunt)

При перевірці статусу подів, ви могли помітити CrashLoopBackOff:

kubectl get pods
NAME                                       READY   STATUS             RESTARTS   AGE
resource-resource-service-5cf85c8b7-gxrnr  0/1     CrashLoopBackOff   9 (31s ago) 23m
song-song-service-86c4cc9c5b-8gd8b         0/1     CrashLoopBackOff   4 (46s ago) 3m5s

Аналіз логів показує проблему:

*
************************** APPLICATION FAILED TO START *************************** Description: Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured. Reason: Failed to determine a suitable driver class

Рішення: Додавання змінних оточення для баз даних

Оновіть values.yaml:

env:
  - name: SPRING_DATASOURCE_URL
    value: jdbc:postgresql://postgres-song:5432/song-db
  - name: POSTGRES_DB_USER
    value: user
  - name: POSTGRES_DB_PASSWORD
    value: password

Створіть чарт для PostgreSQL:

  • templates/postgres-resource.yaml (Service + StatefulSet для resource-db)
  • templates/postgres-song.yaml (Service + StatefulSet для song-db)
  • templates/secret.yaml (для зберігання паролів)

Застосуйте зміни:


helm upgrade --install song ./helm/song-service
helm upgrade --install resource ./helm/resource-service

Перевірте стан подів:


kubectl get pods
NAME                                         READY   STATUS    RESTARTS   AGE
postgres-resource-0                          1/1     Running   0          5m50s
postgres-song-0                              1/1     Running   0          5m50s
resource-resource-service-5f864df4bf-n6fgd   1/1     Running   0          41s
song-song-service-7774dc448f-pgdf9           1/1     Running   0          54s
song-song-service-7774dc448f-rdqxn           1/1     Running   0          52s

Оновлений код: Коміт

Завдання 5: Розгортання discovery-service та gateway-service

Створіть Helm-чарти для discovery та gateway:

  • helm/discovery та helm/gateway з Chart.yaml, values.yaml
  • templates/deployment.yaml та templates/service.yaml

Налаштуйте конфігурацію:

  • discovery: порт 8761
  • gateway: порт 8080

Оновіть залежності мікросервісів:


env:
  - name: EUREKA_CLIENT_SERVICEURL_DEFAULTZONE
    value: "discovery-discovery:8761/eureka"
  - name: SONG_SERVICE_URL
    value: "gateway:8080/songs"

Задеплойте сервіси:


helm install discovery ./helm/discovery
helm install gateway ./helm/gateway

Перевірте стан:


kubectl get pods
NAME                                         READY   STATUS    RESTARTS   AGE
discovery-discovery-8595d87dc4-rx55t         1/1     Running   0          15m
gateway-gateway-7dd96958fd-mr7h9             1/1     Running   0          24s
postgres-resource-0                          1/1     Running   0          95m
postgres-song-0                              1/1     Running   0          95m
resource-resource-service-5f864df4bf-n6fgd   1/1     Running   0          89m
song-song-service-7774dc448f-pgdf9           1/1     Running   0          90m
song-song-service-7774dc448f-rdqxn           1/1     Running   0          90m

Оновлений код: Коміт

Можливі проблеми:

1. ErrImageNeverPull

kubectl get pods
NAME                                         READY   STATUS              RESTARTS   AGE
discovery-discovery-8595d87dc4-jndmg         0/1     ErrImageNeverPull   0          38s
gateway-discovery-8595d87dc4-bkb8c           0/1     ErrImageNeverPull   0          30s

Рішення: Зберіть локальні образи

docker build -t discovery-service:latest -f discovery/Dockerfile discovery
docker build -t gateway-service:latest -f gateway/Dockerfile gateway

2. Сервіси не знаходять discovery та Eureka

Перевірте, що імена сервісів у змінних оточення відповідають іменам сервісів у Kubernetes:

EUREKA_CLIENT_SERVICEURL_DEFAULTZONE: "discovery-discovery:8761/eureka"

3. Доступ до веб-інтерфейсу Eureka

Щоб отримати доступ до веб-інтерфейсу Eureka:


kubectl port-forward svc/discovery-discovery 8761:8761

Потім відкрийте localhost:8761 у браузері.

Після виправлення всіх помилок та перезапуску сервісів:

helm upgrade --install gateway ./helm/gateway
helm upgrade --install song ./helm/song-service
helm upgrade --install resource ./helm/resource-service

Всі мікросервіси повинні успішно зареєструватися в Eureka.

Оновлений код: Коміт

Висновок

У цій статті я показав повний шлях міграції мікросервісів у Kubernetes з використанням Helm:

  • Створили Helm-чарти для двох основних сервісів
  • Налаштували бази даних PostgreSQL
  • Розгорнули інфраструктурні компоненти (Eureka, Gateway)
  • Вирішили типові проблеми, що виникають при міграції

Повний код доступний у репозиторії проекту.

Питання? Задавайте в Issues або коментарях.

Фінальний код: Коміт 30862b0

Плани на майбутнє:

Повний план складається з шести частин:

  • Через Helm до Kubernetes.
  • TBD Azure Key Vault + CSI
  • TBD AKS + PostgreSQL
  • TBD Логування & моніторинг
  • TBD Terraform & CI/CD
  • TBD AI-диригент через Azure ML
👍ПодобаєтьсяСподобалось1
До обраногоВ обраному1
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Безпека: У продакшені використовується мінімалістичний alpine-образ

Чому не distroless?

До речі посилання не працюють

ООпс поправив, та ще кота розкатало в одну лінію )

Підписатись на коментарі