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 под реальное потребление, чтобы не блокировать узлы.
- Лёгкий API / веб-сервер (Go, Node, быстрые ответы):
- Если не знаете цифры: начните с
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>. - Документированы зависимости и требования приложения.