Helm + ArgoCD (GitOps)
Цель урока
Сделать полноценный CRUD для задач с правильными HTTP-статусами (201/204/404), обработкой ошибок, и подготовить Task к подключению JPA.
Теория · для собеса
1 Helm — зачем ▸
Helm — это пакетный менеджер для K8s (apt для k8s):
- Шаблоны + параметры (один чарт → разные окружения)
- Версионирование (
helm install taskflow-0.1.0) - Управление зависимостями (PostgreSQL как subchart)
2 Структура чарта ▸
taskflow-chart/
├── Chart.yaml # метаданные (name, version, appVersion)
├── values.yaml # значения по умолчанию
├── charts/ # зависимости (subcharts)
├── templates/
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── configmap.yaml
│ ├── secret.yaml
│ ├── _helpers.tpl # reusable snippets
│ └── NOTES.txt # что выведется после helm install
└── values-dev.yaml # override для dev
values-prod.yaml # override для prod
3 ArgoCD — GitOps ▸
GitOps = Git — единственный источник истины. ArgoCD:
- Следит за Git-репозиторием
- Если манифесты в Git изменились → применяет в кластер
- Если в кластере что-то поменяли руками → показывает OutOfSync
- Можно auto-sync или manual
Преимущества:
- История изменений = git log
- Rollback =
git revert - Code review для K8s-изменений
---
Практика: улучшаем CRUD
-
1Установка Helm
# macOS brew install helm # Linux curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash -
2Создание чарта
helm create taskflow-chart # Создаст скелет с примерами -
3
Chart.yamlapiVersion: v2 name: taskflow description: TaskFlow — Spring Boot task management service type: application version: 0.1.0 # версия чарта appVersion: "0.1.0" # версия приложения -
4
values.yaml(дефолтные)replicaCount: 2 image: repository: taskflow tag: "0.1.0" pullPolicy: IfNotPresent imagePullSecrets: [] nameOverride: "" fullnameOverride: "" service: type: ClusterIP port: 80 targetPort: 8080 ingress: enabled: true className: nginx annotations: nginx.ingress.kubernetes.io/rewrite-target: / hosts: - host: taskflow.local paths: ["/"] resources: limits: cpu: 500m memory: 512Mi requests: cpu: 250m memory: 256Mi autoscaling: enabled: true minReplicas: 2 maxReplicas: 10 targetCPUUtilizationPercentage: 70 env: SPRING_PROFILES_ACTIVE: "prod" SPRING_DATASOURCE_URL: "jdbc:postgresql://postgres:5432/taskflow" SPRING_KAFKA_BOOTSTRAP_SERVERS: "kafka:9092" secrets: SPRING_DATASOURCE_PASSWORD: "REPLACE_ME" JWT_SECRET: "REPLACE_ME_with_32+_chars" probes: liveness: path: /actuator/health/liveness readiness: path: /actuator/health/readiness -
5
values-dev.yamlreplicaCount: 1 env: SPRING_PROFILES_ACTIVE: "dev" ingress: hosts: - host: taskflow-dev.local -
6
values-prod.yamlreplicaCount: 5 autoscaling: enabled: true maxReplicas: 20 ingress: hosts: - host: taskflow.example.com secrets: SPRING_DATASOURCE_PASSWORD: "PROD_PASSWORD_HERE" JWT_SECRET: "PROD_JWT_SECRET_HERE" -
7
templates/_helpers.tpl{{- define "taskflow.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} {{- end -}} {{- define "taskflow.fullname" -}} {{- if .Values.fullnameOverride -}} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} {{- else -}} {{- $name := default .Chart.Name .Values.nameOverride -}} {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} {{- end -}} {{- end -}} -
8
templates/deployment.yamlapiVersion: apps/v1 kind: Deployment metadata: name: {{ include "taskflow.fullname" . }} labels: app: {{ include "taskflow.name" . }} chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} spec: replicas: {{ .Values.replicaCount }} selector: matchLabels: app: {{ include "taskflow.name" . }} template: metadata: labels: app: {{ include "taskflow.name" . }} spec: containers: - name: {{ .Chart.Name }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" imagePullPolicy: {{ .Values.image.pullPolicy }} ports: - containerPort: {{ .Values.service.targetPort }} env: {{- range $key, $value := .Values.env }} - name: {{ $key }} value: {{ $value | quote }} {{- end }} env: - name: SPRING_DATASOURCE_PASSWORD valueFrom: secretKeyRef: name: {{ include "taskflow.fullname" . }} key: SPRING_DATASOURCE_PASSWORD - name: JWT_SECRET valueFrom: secretKeyRef: name: {{ include "taskflow.fullname" . }} key: JWT_SECRET resources: {{ toYaml .Values.resources | nindent 8 }} readinessProbe: httpGet: path: {{ .Values.probes.readiness.path }} port: {{ .Values.service.targetPort }} initialDelaySeconds: 10 periodSeconds: 5 livenessProbe: httpGet: path: {{ .Values.probes.liveness.path }} port: {{ .Values.service.targetPort }} initialDelaySeconds: 30 periodSeconds: 10 -
9
templates/secret.yamlapiVersion: v1 kind: Secret metadata: name: {{ include "taskflow.fullname" . }} type: Opaque stringData: SPRING_DATASOURCE_PASSWORD: {{ .Values.secrets.SPRING_DATASOURCE_PASSWORD }} JWT_SECRET: {{ .Values.secrets.JWT_SECRET }} -
10
templates/hpa.yaml(autoscaling){{- if .Values.autoscaling.enabled }} apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: {{ include "taskflow.fullname" . }} spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: {{ include "taskflow.fullname" . }} minReplicas: {{ .Values.autoscaling.minReplicas }} maxReplicas: {{ .Values.autoscaling.maxReplicas }} metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} {{- end }} -
11Команды Helm
# Установить с values-prod helm install taskflow ./taskflow-chart -f values-prod.yaml -n taskflow --create-namespace # Проверить что сгенерируется helm template taskflow ./taskflow-chart -f values-prod.yaml # Обновить helm upgrade taskflow ./taskflow-chart -f values-prod.yaml -n taskflow # Откатить helm history taskflow -n taskflow helm rollback taskflow 1 -n taskflow # Удалить helm uninstall taskflow -n taskflow -
12Установка ArgoCD
kubectl create namespace argocd kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml # Получить пароль admin kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d # Port-forward UI kubectl port-forward svc/argocd-server -n argocd 8080:443 # Открыть: https://localhost:8080 (admin /) -
13ArgoCD Application
# argocd-app.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: taskflow namespace: argocd spec: project: default source: repoURL: https://github.com/YOUR_USER/taskflow.git targetRevision: main path: k8s/ helm: valueFiles: - values-prod.yaml destination: server: https://kubernetes.default.svc namespace: taskflow syncPolicy: automated: prune: true # удалять ресурсы, которых нет в Git selfHeal: true # синхронизировать при drift syncOptions: - CreateNamespace=truekubectl apply -f argocd-app.yaml -
14GitOps workflow
- Developer пушит изменение в Git (
k8s/deployment.yamlилиvalues-prod.yaml) - CI запускает тесты
- После merge в main — ArgoCD замечает изменение
- ArgoCD применяет в кластер (auto-sync)
- UI показывает статус
Synced/Healthy
---
- Developer пушит изменение в Git (
Зачем это на собесе
После урока ты должен уметь ответить на:
values-{env}.yaml для dev/staging/prod.5 вопросов на углубление
Раскрой вопрос и нажми «🤔 Хочу разобрать подробнее» — он попадёт в страницу ответов.
Разбор внутри: релизы, values, history и rollback.
Разбор внутри: Secret генерируется из values и GitOps требует источник правды.
Разбор внутри: удаление ресурсов, которых больше нет в Git.
Разбор внутри: шаблоны + values против overlay поверх base.
Разбор внутри: хранение chart-ов как OCI-артефактов рядом с image.
Готов идти дальше?
Выбери вопросы, которые тебе интересны, и изучи их. Потом — к следующему уроку.
🚀 Перейти к Уроку 25Сначала пройди все секции и выбери хотя бы 1 вопрос
⬅️ Назад к Уроку 23