Эксплуатация кластера: апгрейд, узлы, бэкап etcd

Рейтинг: 73% · 17 голосов
Kubernetes для разработчиков: поды, деплойменты, сервисы, ingress, конфиги и отладка. Уроки по главам с обсуждением.
Ответить
Аватара пользователя
anton_k8s
Сообщения: 55
Зарегистрирован: 12 май 2026, 03:23

Эксплуатация кластера: апгрейд, узлы, бэкап etcd

Сообщение anton_k8s »

Оглавление курса (48)
  1. Зачем нужен Kubernetes и из чего состоит кластер
  2. Поднимаем локальный кластер: minikube и kind
  3. Поды: базовая единица запуска
  4. Deployment и ReplicaSet: управляем репликами
  5. Service: сетевой доступ к подам
  6. ConfigMap и Secret: выносим конфигурацию
  7. Ingress: пускаем трафик снаружи
  8. Хранилище: Volumes и PersistentVolumeClaim
  9. Namespaces, requests и limits
  10. Health checks: liveness и readiness пробы
  11. Отладка: почему под не стартует
  12. Helm: пакетный менеджер для Kubernetes
  13. Базовая безопасность: RBAC и доступы
  14. Job и CronJob: разовые и периодические задачи
  15. StatefulSet и DaemonSet: stateful-нагрузки и системные агенты
  16. Стратегии обновления и планирование: rollout и rollback, graceful shutdown, nodeSelector, affinity, taints
  17. Автомасштабирование: HPA по метрикам, обзор VPA и Cluster Autoscaler
  18. Наблюдаемость: логи, метрики, events, обзор Prometheus и Grafana
  19. Безопасность глубже: securityContext, Pod Security Standards, NetworkPolicy, шифрование секретов
  20. Архитектура control plane: api-server, etcd, scheduler
  21. Узел кластера: kubelet, kube-proxy и container runtime
  22. Декларативная модель: reconciliation, контроллеры, CRD
  23. kubectl профессионально: get, describe, explain, jsonpath
  24. Под глубже: init, sidecar, lifecycle hooks и QoS
  25. Метки, селекторы и организация ресурсов
  26. Сервисы и kube-proxy глубже: типы, IPVS, EndpointSlices
  27. Ingress, ingress-контроллеры и Gateway API
  28. Секреты в кластере: шифрование, External Secrets, Vault
  29. Хранилище глубже: PV, StorageClass, CSI и StatefulSet
  30. Сеть кластера: CNI, NetworkPolicy и CoreDNS
  31. Планировщик: affinity, taints, topology spread
  32. Ресурсы, QoS и вытеснение: requests, limits, eviction
  33. Helm глубже: шаблоны, хуки, зависимости, OCI
  34. Kustomize и управление конфигурацией без шаблонов
  35. RBAC и аутентификация глубже
  36. Admission и Pod Security: контроль на входе
  37. Policy as code: Kyverno и OPA Gatekeeper
  38. GitOps: Argo CD и Flux
  39. Операторы и CRD: расширяем Kubernetes
  40. Сервис-меш: Istio, Linkerd и когда он нужен
  41. Эксплуатация кластера: апгрейд, узлы, бэкап etcd (вы здесь)
  42. Где запускать кластер: managed, self-hosted, k3s, Deckhouse
  43. Траблшутинг кластера: Pending, CrashLoop, узлы NotReady
  44. Стоимость и эффективность кластера: FinOps
  45. CI/CD в Kubernetes: сборка, деплой, прогрессивные релизы
  46. Лучшие практики и антипаттерны Kubernetes
  47. Сквозной проект и путь дальше: от манифеста до прода
  48. Деплой приложений через Argo CD: Application, sync и App-of-Apps
День-2: когда кластер уже работает, а тебе с ним жить

Развернуть кластер - это день один. Дальше начинается то, ради чего платят senior-инженерам: день-2 операции. Версия Kubernetes устаревает каждые 3-4 месяца, узлы надо патчить и перезагружать под ядро, диски умирают, сертификаты протухают через год, а однажды кто-то выкатывает кривой манифест и ложит весь namespace. И тогда выясняется, есть ли у тебя бэкап etcd или нет.

Эксплуатация kubernetes - это не про "развернул и забыл". Это набор повторяемых, отрепетированных процедур, каждую из которых ты должен уметь выполнить ночью под телефонный звонок, не уронив прод. В этом уроке разберём четыре кита: апгрейд версии без даунтайма, обслуживание узлов через cordon и drain, бэкап и disaster recovery (etcd snapshot + Velero), и ротацию сертификатов. С механикой под капотом, реальным выводом команд и граблями, на которые наступали все.

Сразу важная развилка. Если у тебя managed-кластер - Yandex Managed Service for Kubernetes, VK Cloud, GKE - то апгрейд control plane и бэкап etcd за тебя делает провайдер, ты их даже не видишь. Твоя зона ответственности там сужается до апгрейда node group, drain узлов и бэкапа приложений через Velero. Если кластер self-managed (kubeadm, k3s, Deckhouse on-prem) - всё на тебе, включая etcd. Дальше я отмечаю, что где.

Изображение

Апгрейд версии: skew policy и почему порядок священен

Kubernetes - распределённая система из компонентов разных версий, которые должны уметь говорить друг с другом во время апгрейда. Это описывает version skew policy, и её надо знать наизусть, иначе кластер развалится посреди процедуры.

Главные правила skew (актуально и для свежих релизов 2026):
  • kube-apiserver - точка отсчёта, самый новый компонент в кластере.
  • kubelet может отставать от apiserver на 3 минорных версии, но не должен быть новее. То есть apiserver 1.33 терпит kubelet 1.30-1.33.
  • kube-controller-manager, kube-scheduler - на одну минорную версию старше apiserver максимум.
  • kubectl - плюс-минус одна минорная версия от apiserver.
Из этого следует железное правило: сначала апгрейдим control plane, потом узлы. Новый apiserver обратно совместим со старыми kubelet, а вот наоборот - нет. И второе: минорные версии нельзя перепрыгивать. С 1.31 на 1.33 напрямую нельзя, только 1.31 -> 1.32 -> 1.33. Патч-версии (1.33.1 -> 1.33.4) прыгать можно свободно.

Шаг ноль, который пропускают и потом плачут - deprecated API. Каждый релиз выпиливает старые apiVersion. Классика: networking.k8s.io/v1beta1 для Ingress, policy/v1beta1 для PodDisruptionBudget давно удалены. Если в кластере или в твоих Helm-чартах остались манифесты на мёртвых версиях, после апгрейда они просто перестанут применяться, а CI выкатки начнут падать. Прогоняй проверку ЗАРАНЕЕ:

Код: Выделить всё

# pluto - сканит манифесты и Helm-релизы на deprecated/removed API
pluto detect-helm -o wide --target-versions k8s=v1.33.0

# kubent (kube-no-trouble) - то же по живым ресурсам в кластере
kubent --target-version 1.33.0
Вывод kubent выглядит так, и читать его надо построчно:

Код: Выделить всё

KIND          NAMESPACE   NAME          API_VERSION              REPLACE_WITH (SINCE)
Ingress       shop        web-ingress   networking.k8s.io/v1beta1 networking.k8s.io/v1 (1.22.0)
PodDisruptionBudget  ci   runner-pdb    policy/v1beta1            policy/v1 (1.21.0)
Колонка REPLACE_WITH говорит, на какую версию мигрировать, в скобках - с какого релиза доступна замена. Чинишь манифесты, передеплоиваешь, и только потом трогаешь сам кластер.

Сама процедура kubernetes upgrade на kubeadm. Делается по одному control plane узлу за раз. На первом мастере:

Код: Выделить всё

# 1. Поднимаем пакет kubeadm до целевой версии (пример для deb)
apt-get update && apt-get install -y kubeadm=1.33.1-1.1

# 2. План: kubeadm покажет, что и куда можно обновить
kubeadm upgrade plan

# 3. Применяем - kubeadm перекатывает статик-поды control plane
kubeadm upgrade apply v1.33.1

Код: Выделить всё

[upgrade/successful] SUCCESS! Your cluster was upgraded to "v1.33.1". Enjoy!
Что произошло под капотом: kubeadm обновил манифесты статик-подов в /etc/kubernetes/manifests/ (apiserver, controller-manager, scheduler, etcd), kubelet увидел изменённые файлы и пересоздал поды с новыми образами. Control plane мигнул, но рабочие нагрузки на узлах этого не заметили.

Дальше - kubelet и kubectl НА КАЖДОМ узле, и вот тут впервые появляется drain (о нём ниже):

Код: Выделить всё

kubectl drain master-1 --ignore-daemonsets
apt-get install -y kubelet=1.33.1-1.1 kubectl=1.33.1-1.1
systemctl daemon-reload && systemctl restart kubelet
kubectl uncordon master-1
Повторяешь для остальных control plane узлов (на них уже

Код: Выделить всё

kubeadm upgrade node
вместо apply), затем по очереди для воркеров. Проверяешь итог:

Код: Выделить всё

kubectl get nodes
NAME       STATUS   ROLES           AGE    VERSION
master-1   Ready    control-plane   210d   v1.33.1
worker-1   Ready    <none>          210d   v1.33.1
worker-2   Ready    <none>          210d   v1.32.4   <- ещё не обновлён, это нормально по skew
Колонка VERSION - это версия kubelet на узле. Пока worker-2 на 1.32.4 при control plane 1.33.1 - всё легально (отставание 1 минор). Главное - не оставлять воркеры отставшими надолго и закрыть апгрейд до выхода следующего релиза.

Обслуживание узлов: cordon, kubectl drain и uncordon

Узел надо вывести из строя десятки раз за жизнь кластера: апгрейд kubelet, патч ядра и ребут, замена диска, вывод старого железа. Делать это грубо (просто выключить машину) нельзя - поды умрут жёстко, посреди обработки запросов. Для аккуратного вывода есть три команды.

cordon - помечает узел unschedulable. Новые поды на него больше не поедут, но существующие продолжают работать. Это "перестань мне сюда что-либо планировать". Под капотом - просто проставляется поле spec.unschedulable: true и taint node.kubernetes.io/unschedulable.

Код: Выделить всё

kubectl cordon worker-1
kubectl get node worker-1
NAME       STATUS                     ROLES    AGE    VERSION
worker-1   Ready,SchedulingDisabled   <none>   210d   v1.33.1
kubectl drain - это cordon ПЛЮС аккуратное выселение всех подов с узла. Каждому поду отправляется сигнал graceful termination (через Eviction API), он получает свой terminationGracePeriodSeconds на корректное завершение, контроллеры (Deployment, StatefulSet) поднимают замену на других узлах. Типичный вызов:

Код: Выделить всё

kubectl drain worker-1 --ignore-daemonsets --delete-emptydir-data --timeout=300s
Разбор флагов, потому что без них drain просто откажется работать:
  • --ignore-daemonsets - поды DaemonSet (kube-proxy, CNI-агент, агент логов) привязаны к узлу и пересоздадутся им же, выселять их бессмысленно. Без этого флага drain встанет с ошибкой.
  • --delete-emptydir-data - разрешает грохнуть поды с томом emptyDir (данные в нём потеряются - ты подтверждаешь, что не против).
  • --timeout - сколько ждать, прежде чем сдаться. Без таймаута зависший под подвесит тебе всю операцию.
И вот здесь - самые опасные грабли всего урока. drain выселяет поды через Eviction API, который УВАЖАЕТ PodDisruptionBudget (PDB). PDB - это твоя страховка: он говорит "у этого приложения должно оставаться живым минимум N реплик". Если PDB настроен правильно (minAvailable: 2 при 3 репликах), drain выселит поды по одному, дожидаясь, пока replacement встанет и пройдёт readiness. Если же PDB НЕТ - drain вынесет все реплики приложения разом, и пользователи получат 503 прямо в лицо. Минимальный PDB, который спасает прод:

Код: Выделить всё

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: web-pdb
  namespace: shop
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: web
Когда узел обслужен (ребут прошёл, диск заменён), возвращаешь его в строй:

Код: Выделить всё

kubectl uncordon worker-1
Снимается unschedulable, планировщик снова видит узел. Поды сами обратно не переедут - они балансируются естественным образом по мере новых выкаток и масштабирования.

Добавление и вывод узлов. Новый воркер в kubeadm-кластере вводится токеном:

Код: Выделить всё

# на control plane генерим команду join (токен живёт 24ч)
kubeadm token create --print-join-command
# на новом узле выполняем то, что она выдала:
kubeadm join 10.0.0.10:6443 --token <...> --discovery-token-ca-cert-hash sha256:<...>
Полный вывод узла из кластера - это drain, затем

Код: Выделить всё

kubectl delete node worker-9
, и

Код: Выделить всё

kubeadm reset
на самой машине, чтобы стереть её состояние. В Yandex Managed Kubernetes и VK Cloud всё это спрятано за node group: меняешь желаемый размер группы или версию - облако само корректно дрейнит и пересоздаёт узлы, а с Karpenter/KEDA узлы вообще приезжают и уезжают под нагрузку без твоего участия.

Бэкап и DR: etcd backup как единственная страховка от катастрофы

Всё состояние кластера - каждый Deployment, Secret, ConfigMap, ServiceAccount - живёт в etcd. Потеряешь etcd без бэкапа - потеряешь кластер целиком, восстановить будет неоткуда. Поэтому etcd backup это не "хорошо бы", а абсолютный минимум для self-managed. Снимок снимается одной командой с control plane узла:

Код: Выделить всё

ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-$(date +%F-%H%M).db \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key
Три сертификата обязательны - etcd шифрует mTLS, без них доступа нет. Это та же причина, почему бэкап нельзя забыть: ключи лежат рядом, под /etc/kubernetes/pki/etcd. Проверяем снимок - битый бэкап хуже отсутствия бэкапа, потому что создаёт ложное чувство безопасности:

Код: Выделить всё

etcdutl snapshot status /backup/etcd-2026-06-15-0300.db -w table
+----------+----------+------------+------------+
|   HASH   | REVISION | TOTAL KEYS | TOTAL SIZE |
+----------+----------+------------+------------+
| a1b2c3d4 |  4821990 |       1843 |     38 MB  |
+----------+----------+------------+------------+
TOTAL KEYS и SIZE должны быть ненулевые и похожие на реальный объём кластера. Восстановление (DR): останавливаешь etcd, разворачиваешь снимок в новую data-директорию через

Код: Выделить всё

etcdutl snapshot restore
, правишь манифест статик-пода etcd на новый путь, поднимаешь. Это разрушительная операция - откатывает ВЕСЬ кластер к моменту снимка, поэтому репетируй её на тестовом стенде ДО того, как припрёт.

Практика 2026: снимать каждые 2-6 часов (чаще - если много выкаток в день), хранить 7 дней локально и 30 дней в объектном хранилище (S3, Yandex Object Storage) с версионированием бакета, и обязательно гонять snapshot status в пайплайне. Версия etcdctl должна совпадать с версией работающего etcd.

Velero - второй слой, для того, что etcd не покрывает. etcd-снимок - это всё-или-ничего на уровне control plane: им нельзя восстановить один namespace и нельзя забэкапить данные в персистентных томах (PV). Здесь работает velero: он бэкапит Kubernetes-ресурсы (через apiserver, в объектное хранилище) ПЛЮС снапшотит тома через CSI. Им делают гранулярное восстановление и миграцию между кластерами.

Код: Выделить всё

# бэкап одного namespace вместе с томами
velero backup create shop-daily --include-namespaces shop --snapshot-volumes

velero backup describe shop-daily
Phase:  Completed
Namespaces:  included: shop
Resources:   included: *
Volume Snapshots: 4 of 4 completed

Код: Выделить всё

# восстановление в тот же или другой кластер
velero restore create --from-backup shop-daily
Правильная модель - оба слоя сразу: etcd snapshot для катастрофы control plane, Velero для гранулярных восстановлений, защиты данных в PV и переезда между кластерами. На managed-кластерах etcd за тебя бэкапит провайдер, так что Velero там обычно единственный инструмент, который тебе реально нужен.

Сертификаты и здоровье control plane

kubeadm выдаёт сертификаты компонентов сроком на один год. Это самая тихая и самая злая граница: кластер работает, никто его не трогает, и ровно через год apiserver перестаёт пускать kubelet и kubectl - "x509: certificate has expired". Прод стоит, а ты в три часа ночи гуглишь. Проверяй срок заранее:

Код: Выделить всё

kubeadm certs check-expiration
CERTIFICATE                EXPIRES                  RESIDUAL TIME   EXTERNALLY MANAGED
apiserver                  Jun 14, 2026 09:12 UTC   29d             no
etcd-server                Jun 14, 2026 09:12 UTC   29d             no
RESIDUAL TIME 29d - пора шевелиться. Ротация:

Код: Выделить всё

kubeadm certs renew all
# затем перезапустить статик-поды control plane (kubelet их подхватит)
Хорошая новость: апгрейд через kubeadm upgrade apply ротирует сертификаты автоматически. Поэтому регулярный апгрейд кластера сам по себе закрывает проблему протухания - ещё один аргумент не тянуть с обновлениями. Здоровье control plane мониторь постоянно:

Код: Выделить всё

kubectl get --raw='/readyz?verbose'
[+]etcd ok
[+]poststarthook/start-kube-apiserver-admission-initializer ok
[+]shutdown ok
readyz check passed
В проде на это вешают алерты Prometheus: доступность apiserver, etcd fsync latency (если растёт - диск под etcd не тянет, ему нужен быстрый SSD), задержки etcd. Медленный диск под etcd - частая причина флапающего кластера, которую долго не могут локализовать.

Типичные грабли и антипаттерны
  • drain без PDB - выносит все реплики разом, ловишь даунтайм. Антипаттерн номер один. PDB на каждое stateful/важное приложение.
  • Перепрыгнул минорную версию (1.31 -> 1.33) - нарушение skew, апгрейд ломается. Только по одной.
  • Забыл прогнать pluto/kubent - после апгрейда отвалились Ingress и PDB на мёртвых API, CI красный.
  • Снимок etcd есть, но его никто не проверял через snapshot status и никто не репетировал restore - в момент DR выясняется, что бэкап битый или процедура не работает.
  • Сертификаты протухли через год на тихом кластере. Лечится регулярным апгрейдом или плановым certs renew.
  • Сначала апгрейднул узлы, потом control plane - kubelet новее apiserver, всё развалилось. Порядок: control plane первым, всегда.
Мини-лаба: отрепетируй день-2 руками

Подними тестовый kubeadm-кластер (или kind/minikube для drain-части) и пройди цикл:
  • Прогони и

    Код: Выделить всё

    pluto detect-helm
    , найди хоть один deprecated API.
  • Создай Deployment на 3 реплики и PDB с minAvailable: 2. Сделай

    Код: Выделить всё

    kubectl drain
    узла и параллельно следи

    Код: Выделить всё

    kubectl get pods -w
    - убедись, что приложение ни на секунду не упало ниже двух живых реплик. Потом убери PDB и повтори - почувствуй разницу.
  • Сними etcd snapshot, проверь его через

    Код: Выделить всё

    etcdutl snapshot status -w table
    , прочитай число ключей.
  • Установи Velero (с MinIO или Yandex Object Storage как backend), сделай backup namespace, удали из него Deployment и восстанови через restore.
  • Глянь

    Код: Выделить всё

    kubeadm certs check-expiration
    и

    Код: Выделить всё

    kubectl get --raw='/readyz?verbose'
    .
Контрольные вопросы
  • Почему control plane апгрейдят раньше воркеров и на сколько минорных версий kubelet может отставать от apiserver?
  • Чем kubectl drain отличается от cordon и какую роль в drain играет PodDisruptionBudget?
  • Что хранится в etcd, почему etcd snapshot - это последняя линия обороны и зачем после снимка делать snapshot status?
  • Какую задачу решает Velero, которую не закрывает снимок etcd?
Итог

Эксплуатация kubernetes держится на отрепетированных процедурах. Апгрейд - строго по skew, control plane раньше узлов, с предварительной проверкой deprecated API через pluto/kubent. Узлы обслуживаются через cordon/drain/uncordon, и drain без PDB рвёт доступность. etcd backup плюс Velero - два слоя страховки, и оба надо ПРОВЕРЯТЬ, а restore - репетировать. Сертификаты живут год, регулярный апгрейд их ротирует сам. Делай это руками на тесте, пока не дошло до автоматизма - в проде времени читать доку не будет.
👍7 ❤️3 🔥 😄 🤔
Аватара пользователя
carlos26
Сообщения: 1
Зарегистрирован: 27 май 2026, 21:12

Re: Эксплуатация кластера: апгрейд, узлы, бэкап etcd

Сообщение carlos26 »

Про drain без PDB прям больно - год назад уронил так платёжку на деплое узлов, теперь PDB на всё что важнее статики. Зря думал что Deployment сам разрулит.
👍2 ❤️ 🔥 😄 🤔
Аватара пользователя
daleman
Сообщения: 1
Зарегистрирован: 13 май 2026, 17:33

Re: Эксплуатация кластера: апгрейд, узлы, бэкап etcd

Сообщение daleman »

А на Yandex Managed реально не надо самому etcd снимать? То есть из бэкапов мне остаётся только Velero на приложения и тома, я правильно понял раскладку?
👍 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
Сервис-меш: Istio, Linkerd и когда он нужен
Следующая глава →
Где запускать кластер: managed, self-hosted, k3s, Deckhouse

Все главы курса «Kubernetes: оркестрация контейнеров от основ до продакшена»

Поделиться темой: ✈ Telegram VK
Похожие запросы: minikube или kind что выбрать для локального кластера kuberneteskubectl get describe logs основные команды для работы с кластеромnamespace в kubernetes зачем нужен и как разделить ресурсыhelm chart как установить приложение в kuberneteshelm глубже шаблоны хуки и зависимости чартовkustomize или helm что выбрать для конфигов kubernetes

Вернуться в «Kubernetes на практике»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость