![](https://habrastorage.org/webt/rm/kg/ul/rmkgulmixvseevwk59lefqpmtsg.jpeg)
Команда VK Cloud перевела статью о том, почему возникают сбои в кластерах Kubernetes и как с ними бороться.
Осознанные прерывания и непреднамеренные сбои
Поды исчезают по причинам двух категорий: если возникает неустранимая ошибка аппаратного обеспечения или системы либо если их уничтожает человек или контроллер. Первую категорию необратимых ситуаций мы называем непреднамеренными сбоями приложения. Например:
- происходит сбой оборудования, на котором размещен узел;
- администратор кластера по ошибке удаляет (экземпляр) виртуальную машину;
- ВМ исчезает из-за сбоя в работе гипервизора или облачного провайдера;
- возникает критическая ошибка ядра;
- узел исчезает из кластера из-за сетевого раздела кластера;
- происходит выселение пода с ноды из-за отсутствия ресурсов на ней или превышения лимитов.
Примечание: Большинство этих событий неспецифичны для Kubernetes и должны быть знакомы пользователю, за исключением проблемы с отсутствием ресурсов на ноде.
Ситуации из второй категории мы называем осознанными прерываниями. Это действия, инициируемые владельцем приложения или администратором кластера.
Стандартные действия владельца приложения:
- удаление деплоймента или другого контроллера, который управляет подом;
- обновление шаблона пода деплоймента, в связи с которым требуется перезапуск;
- непосредственное, например случайное удаление пода.
Действия администратора кластера:
- операция Drain ноды для ремонта или обновления;
- Drain ноды из кластера для уменьшения его размера;
- удаление пода из ноды, чтобы разместить на ней что-то другое.
Что делать со сбоями
Вот как можно смягчить последствия непреднамеренных сбоев:
- Убедитесь, что под явным образом запрашивает ресурсы, которые ему нужны.
- Продумайте репликацию приложения, если нужно повысить отказоустойчивость. Здесь можно почитать о том, как работать с репликацией Stateless- и Stateful-приложений.
- Чтобы еще больше повысить отказоустойчивость при работе реплицированных приложений, разместите приложение на нескольких стойках с помощью правил Anti-affinity или в нескольких зонах с помощью Multi-zone Cluster.
PodDisruptionBudget
Владелец приложения может создать для каждого приложения объект PodDisruptionBudget (PDB). PDB ограничивает количество подов реплицированного приложения, которые могут одновременно выходить из строя в результате осознанных прерываний. Например, для приложения на базе кластера можно указать количество работающих реплик, необходимое для кворума. Возможно, для Web-frontend-приложения понадобится, чтобы количество обслуживающих нагрузку реплик никогда не становилось меньше, чем определенная доля от их общего количества.
Менеджеры кластеров и поставщики хостинг-услуг должны использовать инструменты, которые соблюдают Pod Disruption Budgets, не удаляя поды напрямую, а вызывая Eviction API. В качестве примеров можно привести команду
kubectl drain
и скрипт обновления кластера Kubernetes-on-GCE (cluster/gce/upgrade.sh).Когда администратору кластера нужно выполнить операцию Drain для ноды, он использует команду
kubectl drain
. Этот инструмент пытается расселить все поды, размещенные на машине. Запрос на расселение может быть временно отклонен, и инструмент периодически повторяет все невыполненные запросы, пока все поды не расселятся или пока не будет достигнут заданный тайм-аут.Пример PDB с использованием minAvailable:
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: zk-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: zookeeper
Пример PDB с использованием maxUnavailable (Kubernetes 1.7 или выше):
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: zk-pdb
spec:
maxUnavailable: 1
selector:
matchLabels:
app: zookeeper
Helm
Используйте в Chart! templates/pdb.yaml:
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name:
namespace:
labels:
app:
chart: -
release:
heritage:
spec:
selector:
matchLabels:
app:
env:
minAvailable:
Представьте, что у вас есть сервис двумя репликами и вам нужно, чтобы одна из них была доступна даже во время обновления ноды и других задач.
Установить или обновить релиз:
helm upgrade --install --debug "$RELEASE_NAME" -f helm/values.yaml \
--set replicas=2,budget.minAvailable=1 myrepo/mychart
kubectl describe pdb "$RELEASE_NAME"
Name: mysvc-prod
Namespace: prod
Min available: 1
Selector: app=myservice,env=prod
Status:
Allowed disruptions: 1
Current: 2
Desired: 1
Total: 2
Events: <none>
Выполнить операцию Drain для ноды, когда один из подов работает:
kubectl drain --delete-local-data --force --ignore-daemonsets gke-mycluster-prod-pool-2fca4c85-k6g5node
"gke-mycluster-prod-pool-2fca4c85-k6g5" already cordoned
WARNING: Deleting pods with local storage: sqlproxy-67f695889d-t778w;
Ignoring DaemonSet-managed pods: fluentd-gcp-v3.0.0-llp5s;
Deleting pods not managed by ReplicationController, ReplicaSet, Job, DaemonSet or StatefulSet:
kube-proxy-gke-testing-dev-pool-2fca4c85-k6g5
pod "tiller-deploy-7b7b795779-rcvkd" evicted
pod "mysvc-prod-6856d59f9b-lzrtf" evicted
node "gke-mycluster-prod-pool-2fca4c85-k6g5" drained
again: kubectl describe pdb "$RELEASE_NAME"
Name: mysvc-prod
Namespace: prod
Min available: 1
Selector: app=myservice,env=prod
Status:
Allowed disruptions: 0
Current: 1
Desired: 1
Total: 2
Events: <none>
Та-дам! Мы выполнили операцию Drain для ноды без сбоев в работе службы.
PDB только с одной репликой
Если бы у нас была только одна реплика, то
kubectl drain
застопорился бы в любом случае. Операции Drain или обновления ноды пришлось бы делать вручную.
Вы, возможно, ожидаете, что Eviction API попытается всеми силами сохранить реплику, чтобы выполнить условие minAvailable; вместо этого операция Drain застревает и вам нужно разрешить эту ситуацию самостоятельно. Что это, баг или фича? Сообщество Kubernetes говорит, что вообще не следует использовать одну реплику в продакшене, если вам нужна высокая доступность. Так и есть :)
API делает то, что от него ожидают. Поэтому если вы не хотите, чтобы kubectl drains застрял, возможно, для деплоймента с более чем одной репликой лучше использовать PDB.
Отредактируйте templates/pdb.yaml:
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
...
Как выполнить Disruptive Actions в кластере
Если вы администратор кластера и вам нужно в нем на всех нодах выполнить Disruptive Action, например обновить ноду или системное ПО, то вот несколько вариантов.
Примите как данность простой во время обновления. Переведите всю реплику на другой резервный кластер. Обойдетесь без простоя, но решение недешевое в плане дублированных нод и трудозатрат на оркестрацию при переключении.
Напишите отказоустойчивые приложения и используйте PDB. В чём достоинства этого решения:
- без простоя;
- минимальное дублирование ресурсов;
- высокая степень автоматизации администрирования кластера;
- написать отказоустойчивое приложение непросто, но трудозатраты на добровольные прерывания в основном сопоставимы с усилиями на поддержку автомасштабирования и устранение неудобств, сопряженных с непреднамеренными сбоями.
Попробуйте Kubernetes as a Service на платформе VK Cloud. Мы даем новым пользователям 3000 бонусных рублей на тестирование кластера или любых других сервисов.
Что еще почитать: