Продвинутый уровень

Основы освоены — переходим к инструментам, которые превращают учебный кластер в production-платформу: управление правами доступа, публикация сервисов через Ingress, пакетный менеджер Helm, операторы, автомасштабирование и StatefulSet для приложений с состоянием.

Содержание раздела

RBAC — кто и что может в кластере

RBAC (Role-Based Access Control, управление доступом на основе ролей) — встроенный механизм авторизации Kubernetes. Он отвечает на вопрос: какому субъекту (пользователю, группе или сервисному аккаунту) какие действия над какими ресурсами разрешены. Модель строго разрешительная: всё, что не разрешено явно, — запрещено.

Четыре объекта RBAC

Объект Область действия Назначение
Role Один namespace Набор разрешений внутри конкретного пространства имён
ClusterRole Весь кластер Разрешения на кластерные ресурсы (узлы, PV) или на объекты во всех namespace
RoleBinding Один namespace Привязывает Role или ClusterRole к субъекту внутри namespace
ClusterRoleBinding Весь кластер Привязывает ClusterRole к субъекту на уровне всего кластера

Пример: роль «только чтение подов» и её привязка

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: team-payments
  name: pod-reader
rules:
  - apiGroups: [""]              # пустая строка = core API
    resources: ["pods", "pods/log"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods-binding
  namespace: team-payments
subjects:
  - kind: User
    name: ivan                # имя пользователя из сертификата/токена
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

ServiceAccount — удостоверение для приложений

Приложениям внутри кластера тоже нужны права — например, контроллеру, который читает список подов через API. Для этого служит ServiceAccount:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-sa
  namespace: team-payments
---
# в Deployment указываем, под каким аккаунтом работают поды:
spec:
  template:
    spec:
      serviceAccountName: app-sa
# проверить, есть ли право у субъекта:
kubectl auth can-i create deployments --as=ivan -n team-payments
kubectl auth can-i list secrets --as=system:serviceaccount:team-payments:app-sa
Принцип минимальных привилегий Никогда не выдавайте cluster-admin приложениям и не используйте wildcard-правила ("*" в resources и verbs) в боевых кластерах. Каждому ServiceAccount — ровно те права, которые нужны его приложению, и не больше.

Ingress — публикация HTTP-сервисов наружу

Service типа LoadBalancer удобен, но создаёт отдельный облачный балансировщик на каждый сервис — дорого и негибко. Ingress решает это иначе: один внешний вход (ingress-контроллер) принимает весь HTTP/HTTPS-трафик и маршрутизирует его по внутренним сервисам на основе доменного имени и пути URL. Плюс здесь же терминируется TLS.

Ingress-манифест — это только правила. Исполняет их ingress-контроллер, который нужно установить в кластер отдельно: популярны ingress-nginx, Traefik, HAProxy Ingress, а также реализации на базе Gateway API.

Манифест Ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: main-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - app.example.ru
      secretName: app-tls        # Secret с сертификатом
  rules:
    - host: app.example.ru
      http:
        paths:
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: api-service
                port:
                  number: 8080
          - path: /
            pathType: Prefix
            backend:
              service:
                name: frontend-service
                port:
                  number: 80
Связка с cert-manager В связке с ingress-контроллером почти всегда используют cert-manager: он автоматически выпускает и продлевает TLS-сертификаты (например, от Let's Encrypt) и складывает их в указанный Secret. Ручное управление сертификатами в кластере — путь к просроченным TLS.

Helm — пакетный менеджер Kubernetes

Реальное приложение — это десяток манифестов: Deployment, Service, Ingress, ConfigMap, ServiceAccount... Копировать их между окружениями и править руками неудобно. Helm упаковывает набор манифестов в чарт (chart) — шаблонизированный пакет с параметрами.

Ключевые понятия

Термин Определение
Chart Пакет: шаблоны манифестов + файл значений по умолчанию values.yaml + метаданные
Release Экземпляр чарта, установленный в кластер. Один чарт — много релизов (dev, stage, prod)
Repository Хранилище чартов (HTTP-сервер с индексом), например репозитории проектов и venдоров
Values Параметры релиза: количество реплик, теги образов, домены — переопределяются при установке

Типичный рабочий процесс

# добавить репозиторий чартов и обновить индекс
# (URL берите из документации проекта-владельца чарта)
helm repo add example <URL-репозитория-чартов>
helm repo update

# поискать чарт
helm search repo postgresql

# установить релиз со своими значениями
helm install mydb example/postgresql \
  --namespace databases --create-namespace \
  --set auth.database=appdb \
  --set primary.persistence.size=20Gi

# посмотреть релизы и историю
helm list -n databases
helm history mydb -n databases

# обновить релиз новой версией чарта/значений
helm upgrade mydb bitnami/postgresql -f my-values.yaml -n databases

# откатиться на ревизию 1
helm rollback mydb 1 -n databases

Фрагмент values.yaml

replicaCount: 3
image:
  repository: registry.example.ru/myapp
  tag: "1.4.2"
service:
  type: ClusterIP
  port: 80
ingress:
  enabled: true
  host: app.example.ru
resources:
  requests:
    cpu: 200m
    memory: 256Mi
Helm или Kustomize? Helm — шаблонизатор и менеджер релизов: удобен для распространяемых приложений и параметризации. Kustomize (встроен в kubectl, kubectl apply -k) работает через наложение «патчей» на базовые манифесты без шаблонов. Часто их комбинируют: чарт рендерят Helm-ом, а окружение-специфичные правки вносят Kustomize.

Операторы — автоматизация сложных приложений

Оператор — это паттерн (и программа), который расширяет Kubernetes знаниями о конкретном приложении. Оператор вводит собственный тип объекта — CRD (Custom Resource Definition), например PostgreSQL или KafkaCluster, — и контроллер, который следит за такими объектами и выполняет операционную логику: развёртывание, бэкапы, обновление версий, восстановление после сбоев.

# Пример декларативного заказа кластера PostgreSQL через оператор
# (схема зависит от конкретного оператора)
apiVersion: acid.zalan.do/v1
kind: postgresql
metadata:
  name: main-db
  namespace: databases
spec:
  teamId: "main"
  numberOfInstances: 3      # оператор сам настроит репликацию
  postgresql:
    version: "16"
  volume:
    size: 50Gi

Идея оператора — перенести знания опытного администратора в код контроллера. Существуют зрелые операторы для PostgreSQL, MySQL, Kafka, Redis, Elasticsearch, мониторинга (Prometheus Operator) и многого другого. Пишут операторы на Go с библиотекой client-go, а также через фреймворки Operator SDK и Kubebuilder.

Когда нужен оператор Если приложение требует нетривиальных «ручных» процедур — инициализация кластера СУБД, переключение мастера, последовательное обновление нод — оператор окупается. Если приложение stateless, достаточно Deployment и Helm-чарта.

HPA и VPA — автомасштабирование

Kubernetes умеет подстраивать ресурсы под нагрузку автоматически, в трёх независимых измерениях:

Механизм Что меняет По какому сигналу
HPA (Horizontal Pod Autoscaler) Количество реплик пода CPU, память, внешние и пользовательские метрики (например, длина очереди)
VPA (Vertical Pod Autoscaler) Requests/limits контейнеров История фактического потребления
Cluster Autoscaler Количество узлов кластера Наличие подов в состоянии Pending из-за нехватки ресурсов

Манифест HPA

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 60   # целевая загрузка CPU, % от requests
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 75
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300  # не спешить уменьшать
Обязательные условия для HPA Во-первых, в кластере должен быть установлен metrics-server (или адаптер пользовательских метрик). Во-вторых, у контейнеров должны быть заданы resources.requests — utilization считается именно от них. Без requests HPA работать не будет.

StatefulSet — приложения с состоянием

Deployment идеален для stateless-приложений: реплики взаимозаменяемы. Но базы данных, очереди и распределённые системы требуют стабильной идентичности: предсказуемого имени, постоянного сетевого адреса и закреплённого тома. Это даёт StatefulSet:

Манифест StatefulSet с headless-сервисом

apiVersion: v1
kind: Service
metadata:
  name: db-headless
spec:
  clusterIP: None           # headless: без виртуального IP
  selector:
    app: db
  ports:
    - port: 5432
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: db
spec:
  serviceName: db-headless
  replicas: 3
  selector:
    matchLabels:
      app: db
  template:
    metadata:
      labels:
        app: db
    spec:
      containers:
        - name: postgres
          image: postgres:16
          ports:
            - containerPort: 5432
          volumeMounts:
            - name: data
              mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:      # у каждого пода — свой том
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 20Gi
StatefulSet ≠ «готовая база данных» StatefulSet даёт идентичность и хранилище, но не настраивает репликацию СУБД, кворум и переключение мастера — это зона ответственности самого приложения или оператора. Для production-СУБД чаще используют зрелые операторы, которые строятся поверх StatefulSet.

DaemonSet, Job и CronJob

DaemonSet — по поду на каждом узле

DaemonSet гарантирует, что на каждом (или выбранном селекторами) узле кластера работает ровно один экземпляр пода. Классические применения: агенты сбора логов (Fluent Bit), мониторинга (Node Exporter), сетевые плагины, агенты безопасности.

Job — разовая задача до завершения

Job запускает под и дожидается его успешного завершения, при необходимости перезапуская. Подходит для миграций БД, пакетной обработки, разовых вычислений.

apiVersion: batch/v1
kind: CronJob
metadata:
  name: nightly-backup
spec:
  schedule: "0 3 * * *"        # каждую ночь в 03:00, формат cron
  concurrencyPolicy: Forbid    # не запускать, пока не завершился предыдущий
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 3
  jobTemplate:
    spec:
      backoffLimit: 2          # две попытки при сбое
      template:
        spec:
          restartPolicy: Never
          containers:
            - name: backup
              image: backup-tool:1.2
              args: ["--target", "s3://backups"]

Свой Helm-чарт шаг за шагом

Чужие чарты устанавливать вы уже умеете. Следующий шаг — упаковать собственное приложение: чарт превращает набор манифестов в параметризуемый пакет, который ставится одной командой и обновляется без ручной правки YAML.

Шаг 1. Создать скелет чарта

# генерация типового чарта
helm create myapp

# структура каталогов
myapp/
├── Chart.yaml            # имя, версия, описание чарта
├── values.yaml           # значения параметров по умолчанию
├── charts/               # зависимости (другие чарты)
└── templates/            # шаблоны манифестов
    ├── deployment.yaml
    ├── service.yaml
    ├── ingress.yaml
    ├── serviceaccount.yaml
    ├── _helpers.tpl      # вспомогательные шаблонные функции
    └── NOTES.txt         # подсказка, выводимая после установки

Сгенерированный чарт — рабочий пример: он сразу устанавливает nginx. Удалите из templates/ лишнее и адаптируйте под своё приложение.

Шаг 2. Параметризовать шаблоны

В шаблонах доступны значения из values.yaml через .Values и служебные данные релиза через .Release:

# values.yaml
replicaCount: 2
image:
  repository: myregistry/myapp
  tag: "1.4"
# templates/deployment.yaml (фрагмент)
spec:
  replicas: {{ .Values.replicaCount }}
  template:
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"

Шаг 3. Проверить рендер без установки

# линт: синтаксис и типовые ошибки чарта
helm lint myapp

# отрендерить манифесты и посмотреть глазами
helm template myapp ./myapp

# пробный прогон против реального кластера
helm install myapp ./myapp --dry-run

Шаг 4. Установить, обновить и при необходимости откатить

# установка релиза
helm install myapp ./myapp -n myapp --create-namespace

# список релизов и состояние
helm list -n myapp

# обновление с переопределением параметров
helm upgrade myapp ./myapp -n myapp --set replicaCount=3

# обновление из файла значений для окружения
helm upgrade myapp ./myapp -n myapp -f values-prod.yaml

# история ревизий релиза
helm history myapp -n myapp

# откат на предыдущую ревизию
helm rollback myapp -n myapp

# откат на конкретную ревизию
helm rollback myapp 2 -n myapp

# удаление релиза
helm uninstall myapp -n myapp
Правила хорошего чарта Все изменяемые величины — в values.yaml, а не захардкожены в шаблонах; значения по умолчанию должны давать рабочую установку «из коробки»; файлы значений для окружений (values-dev, values-prod) хранятся в git рядом с чартом. И помните: каждый helm upgrade — новая ревизия, поэтому любая неудачная правка откатывается одной командой helm rollback.

Итог: карта контроллеров нагрузки

Задача Объект
Stateless-приложение, N реплик, обновления без простоя Deployment
СУБД, очередь — стабильные имена и тома StatefulSet
Агент на каждом узле: логи, метрики, сеть DaemonSet
Разовая задача до успешного завершения Job
Периодическая задача по расписанию CronJob
Публикация HTTP/HTTPS с маршрутизацией и TLS Ingress + контроллер
Права пользователей и приложений Role / ClusterRole + bindings
Автоматическое число реплик по нагрузке HPA

Следующий логичный шаг — понять, как устроена сеть, связывающая всё это воедино: раздел «Сети в Kubernetes».