Best Practices для разработчиков

Паттерны, антипаттерны и лучшие практики работы с Kubernetes.

Управление манифестами

См. также: быстрый старт разработчика, доставка через Argo CD.

Версионирование

  • Храните манифесты в Git — все изменения должны быть отслеживаемыми. Применяется всегда: и для внутренних сервисов, и для production.
  • Используйте теги версий для образов вместо latest (например, v1.0.0, git-sha). Тег latest не даёт понять, какая версия задеплоена при инциденте и усложняет откат.
  • Применяйте изменения через GitOps (Argo CD) для production-окружений — когда в команде есть процесс code review и нужно избежать ручных правок «на проде».

См. также: подготовка образов и манифестов, GitOps-поток изменений.

Структура репозитория

Выбирайте структуру под размер приложения и количество окружений.

Используйте, когда один и тот же набор манифестов (Deployment, Service, Ingress) отличается только значениями для dev, stage и prod: репликами, ресурсами, доменами.

my-app/
├── k8s/
│   ├── base/
│   │   ├── deployment.yaml
│   │   ├── service.yaml
│   │   └── ingress.yaml
│   └── overlays/
│       ├── dev/
│       ├── stage/
│       └── prod/
├── Dockerfile
└── .gitlab-ci.yml

Используйте, когда нужно переиспользовать чарты и параметризовать несколько приложений или микросервисов через values: образ, реплики, ресурсы, feature flags.

my-app/
├── charts/
│   └── my-app/
│       ├── Chart.yaml
│       ├── values.yaml
│       └── templates/
└── Dockerfile

См. также: shturval-cd, импорт манифестов.


Ресурсы контейнеров

Requests и Limits

Всегда указывайте requests и limits:

resources:
  requests:
    cpu: "100m"
    memory: "128Mi"
  limits:
    cpu: "500m"
    memory: "512Mi"

Почему это важно:

  • позволяет планировщику Kubernetes правильно размещать поды;
  • необходимо для работы HPA;
  • предотвращает исчерпание ресурсов узла.

См. также: requests/limits при деплое, квоты и лимиты.

Рекомендации по значениям

  • Requests — минимальные ресурсы, гарантированные поду. Limits — максимум, который может использовать под. Соотношение limits/requests обычно 2–3×.
  • Ориентиры по типу нагрузки (на один под):
    • Лёгкий API / веб-сервер (Go, Node, быстрые ответы): requests: cpu: 50–100m, memory: 128–256Mi, limits: cpu: 200–500m, memory: 256–512Mi.
    • Нагруженный бэкенд (много CPU или памяти на запрос): requests: cpu: 200–500m, memory: 256–512Mi, limits: cpu: 1–2, memory: 1–2Gi.
    • Фоновые воркеры / очереди (постоянная нагрузка): замерьте пиковое потребление в dev/stage и задайте requests чуть ниже среднего, limits — на пик с запасом ~20%.
    • Init-контейнеры и одноразовые джобы: задавайте requests/limits под реальное потребление, чтобы не блокировать узлы.
  • Если не знаете цифры: начните с requests: cpu: 100m, memory: 128Mi и limits: cpu: 500m, memory: 512Mi, затем смотрите метрики использования и поднимайте при стабильной утилизации >70%.

См. также: метрики неймспейса, траблшутинг ресурсов.


Health Checks

Проверяет, готов ли контейнер принимать трафик. Когда применять: у любого сервиса, который получает трафик через Service/Ingress (чтобы трафик не шёл в поды, которые ещё не поднялись или отключились).

readinessProbe:
  httpGet:
    path: /health/ready
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 10
  • initialDelaySeconds: 5 — подходит, если приложение поднимается за 1–3 секунды (например, лёгкий API).
  • Если приложение долго инициализируется (подключение к БД, загрузка кэша), поставьте 15–30 секунд, иначе под будет помечен NotReady и трафик не пойдёт до первого успешного probe.

См. также: диагностика readiness, Service и балансировка.

Проверяет, что контейнер живой; при повторных неудачах kubelet перезапускает контейнер. Когда применять: для долгоживущих сервисов, которые могут «зависнуть» (deadlock, бесконечный цикл) без падения процесса.

livenessProbe:
  httpGet:
    path: /health/live
    port: 8080
  initialDelaySeconds: 30
  periodSeconds: 10
  failureThreshold: 3
  • initialDelaySeconds должен быть больше времени холодного старта приложения (иначе под будет перезапускаться сразу после старта). Для быстрого старта — 15–30 с, для тяжёлого приложения — 60–120 с.
  • failureThreshold: 3 при periodSeconds: 10 даёт ~30 секунд до перезапуска — достаточно, чтобы отличить кратковременный лаг от реального зависания.
  • Не делайте liveness по тому же endpoint, что и тяжёлые операции: если /health/live делает запрос в БД и БД тормозит, kubelet будет перезапускать под без необходимости.

См. также: CrashLoopBackOff и probes.


Безопасность

Когда применять: образы хранятся в приватном registry (например, внутренний Harbor, GitLab Registry). Без Secret образ не удастся pull’ить с узлов.

Храните учётные данные реестра в Secrets:

apiVersion: v1
kind: Secret
metadata:
  name: registry-secret
type: kubernetes.io/dockerconfigjson
data:
  .dockerconfigjson: <base64-encoded>

См. также: подключение приватного registry, ImagePullBackOff.

Не храните секреты в манифестах в открытом виде:

# Плохо
env:
- name: DB_PASSWORD
  value: "my-password"

# Хорошо
env:
- name: DB_PASSWORD
  valueFrom:
    secretKeyRef:
      name: db-secret
      key: password

См. также: управление секретами через Vault, политики Kyverno для секретов.

Ограничьте права контейнера. Когда применять: для любого приложения, которое не требует root или специальных capability (сборка образов, отладка с ptrace и т.п.). Платформа и политики безопасности (например, Kyverno) могут требовать runAsNonRoot и запрет привилегированных контейнеров.

securityContext:
  runAsNonRoot: true
  runAsUser: 1000
  allowPrivilegeEscalation: false
  capabilities:
    drop:
    - ALL
  • runAsUser: 1000 — типичный непривилегированный UID. Если образ уже собирается с другим пользователем (например, node в официальном Node-образе), укажите его UID или не задавайте runAsUser, чтобы не сломать права на файлы в контейнере.

См. также: security context при деплое, политики Seccomp.


Конфигурация

Когда применять: настройки приложения отличаются по окружениям (dev/stage/prod) или между деплоями (URL бэкенда, feature flags, лимиты). Не используйте ConfigMap для секретов — для них только Secret.

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  config.yaml: |
    database:
      host: db.example.com
      port: 5432    

Подключение в Deployment:

volumes:
- name: config
  configMap:
    name: app-config
containers:
- name: app
  volumeMounts:
  - name: config
    mountPath: /etc/config

См. также: ConfigMap в нагрузках.

Используйте переменные окружения для параметров, зависящих от окружения:

env:
- name: ENVIRONMENT
  value: "production"
- name: LOG_LEVEL
  valueFrom:
    configMapKeyRef:
      name: app-config
      key: log-level

См. также: переменные окружения при деплое.


Масштабирование

HPA для автоматического масштабирования

Когда применять: нагрузка на сервис переменная (веб, API, воркеры по очереди), и вы хотите автоматически увеличивать/уменьшать число подов по CPU или кастомным метрикам. Для стабильной нагрузки с фиксированным числом подов HPA можно не использовать.

Пример для типичного веб-сервиса:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-app
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        averageUtilization: 70

См. также: HPA при деплое.

Как выбрать minReplicas и maxReplicas

  • minReplicas: минимум подов, которые всегда работают.
    • 1 — допустимо только для dev/test (при падении одного пода сервис недоступен).
    • 2 — типичный минимум для production (доступность при падении одного пода или обновлении).
    • 3 и больше — если нужна высокая доступность или равномерное распределение по зонам.
  • maxReplicas: верхняя граница, чтобы не исчерпать ресурсы кластера.
    • Оцените: «максимум подов × limits одного пода» не должно превышать доступные ресурсы нод. Например, при limit 500m CPU и 512Mi памяти на под и 10 нодах по 4 CPU можно ориентироваться на maxReplicas порядка 50–60 (с запасом на системные поды).
    • Для малого кластера часто достаточно maxReplicas: 10–20; для высоконагруженных сервисов считайте по фактической ёмкости.
  • target averageUtilization: 70 — типичное значение: масштабирование начнётся до полной загрузки, остаётся запас на всплески. Для более «агрессивного» масштабирования можно 80; для более плавного — 60.

Развёртывание

Стратегии развёртывания

  • Rolling Update (по умолчанию в Deployment) — по одному/несколько подов заменяются новыми. Когда применять: почти всегда по умолчанию; низкий риск, не требует дополнительных ресурсов. Важно иметь readiness probe, иначе трафик может уходить в поды, которые ещё не готовы.
  • Blue-Green — два одинаковых окружения, переключение трафика целиком с одного на другое. Когда применять: когда нужен быстрый откат «одной кнопкой» и есть ресурсы на два полных набора подов.
  • Canary — новая версия получает небольшую долю трафика (5–10%), затем доля увеличивается. Когда применять: когда нужен плавный выход на новую версию и проверка метрик/ошибок до полного переключения (часто через Ingress или service mesh).

См. также: стратегии Argo Rollouts.

Рекомендации

  • Обязательно настройте readiness probes: при rolling update новые поды не получают трафик, пока не станут Ready, что снижает риск ошибок при деплое.
  • Сначала прогоняйте ту же версию образа в dev/stage (например, тот же тег), затем деплой в production — так вы проверите манифесты и политики, а не только код.
  • Если используете Argo CD или GitOps-инструмент, настройте автоматический rollback при падении здоровья приложения (если инструмент это поддерживает).

Мониторинг и диагностика

Проверяйте манифесты до деплоя, а после релиза смотрите не только статус подов, но и фактическое потребление ресурсов, рестарты и события Kubernetes.

# Проверка манифестов перед деплоем
kubectl apply --dry-run=server -f deployment.yaml
# Просмотр реального использования ресурсов
kubectl top pods -n production
kubectl top nodes

# Проверка рестартов и статуса HPA
kubectl get pods -n production -o wide
kubectl get hpa -n production -w
# Диагностика пода, который не стартует
kubectl describe pod <pod-name> -n production
kubectl logs <pod-name> -n production --previous
kubectl get events -n production --sort-by='.lastTimestamp'

См. также: алерты, просмотр логов, подготовка к деплою.


Хранилище

PersistentVolumes для stateful-приложений

Когда применять: данные должны пережить перезапуск или пересоздание пода (БД, кэши на диске, очереди с персистентным хранилищем). Для временных данных и кэша в памяти PVC не нужны.

Пример для StatefulSet (например, очередь или приложение с одной БД на под):

volumeClaimTemplates:
- metadata:
    name: data
  spec:
    accessModes: ["ReadWriteOnce"]
    resources:
      requests:
        storage: 10Gi
  • ReadWriteOnce — том привязан к одной ноде, под может переезжать только на эту ноду. Подходит для большинства БД и stateful-приложений.
  • ReadWriteMany — том может монтироваться на несколько подов одновременно. Нужен только если несколько подов действительно пишут в один том (общие файлы, NFS-сценарии). Часто медленнее и дороже; не используйте «на всякий случай».
  • Размер storage: задавайте по реальной оценке роста данных (например, логи за 7 дней, объём БД + запас 20–30%). Заниженный request потом придётся увеличивать (зависит от StorageClass и политик кластера).

См. также: создание PVC, StatefulSet при деплое.


Сеть

Services для внутреннего доступа

Когда применять: один сервис обращается к другому внутри кластера (например, API → БД, фронтенд → бэкенд). ClusterIP даёт стабильное DNS-имя и порт, трафик не выходит за пределы кластера.

apiVersion: v1
kind: Service
metadata:
  name: my-app
spec:
  type: ClusterIP
  selector:
    app: my-app
  ports:
  - port: 80
    targetPort: 8080

Обращение к сервису из другого пода: http://my-app или http://my-app.<namespace>.svc.cluster.local (порт 80 по умолчанию).

См. также: создание Service, диагностика сетевых проблем.

Ingress для внешнего доступа

Когда применять: пользователи или внешние системы обращаются к приложению по HTTP/HTTPS извне кластера. Один Ingress может обслуживать много хостов или путей (разные приложения за одним балансировщиком).

  • TLS: для production обязательно используйте TLS (в Ingress указывается Secret с сертификатом). Иначе пароли и токены передаются в открытом виде.
  • Доменные имена: задавайте host в Ingress (например, app.example.com), а не полагайтесь на IP — при смене окружения или кластера конфигурация остаётся той же.
  • Rate limiting и WAF настраиваются на уровне Ingress-контроллера или провайдера (зависит от платформы).

См. также: создание Ingress, Gateway API routes.


Антипаттерны

# Плохо
image: my-app:latest

# Хорошо
image: my-app:v1.0.0

См. также: подготовка образов, подпись образов.

# Плохо
containers:
- name: app
  image: my-app:v1.0.0

# Хорошо
containers:
- name: app
  image: my-app:v1.0.0
  resources:
    requests:
      cpu: "100m"
      memory: "128Mi"
    limits:
      cpu: "500m"
      memory: "512Mi"
# Плохо
env:
- name: PASSWORD
  value: "secret-password"

# Хорошо
env:
- name: PASSWORD
  valueFrom:
    secretKeyRef:
      name: app-secret
      key: password
Избегайте hostNetwork: true — под использует сеть ноды, видит весь трафик ноды и может конфликтовать по портам с другими подами. Исключения: специализированные поды (например, некоторые CNI, мониторинг сети), когда это явно требуется документацией компонента.
Избегайте privileged: true — контейнер получает доступ к ноде почти как к хосту. Исключения: системные компоненты (драйверы, отладка ядра). Для обычного приложения используйте securityContext с runAsNonRoot и без дополнительных capabilities.

Чек-лист перед production

  • Все манифесты хранятся в Git и версионированы.
  • Указаны requests и limits для всех контейнеров.
  • Настроены readiness и liveness probes.
  • Секреты хранятся в Kubernetes Secrets, а не в манифестах.
  • Настроен TLS для всех внешних сервисов.
  • Развёртывание протестировано в dev/stage.
  • Настроены мониторинг, сбор логов и алерты.
  • Настроен backup для критичных данных.
  • Подготовлена команда отката: kubectl rollout undo deployment/<name>.
  • Документированы зависимости и требования приложения.