Сервис развивается: тестируем формат, собираем идеи, улучшаем сервис. Есть идеи?

Написать
Войти
Дайджесты
Схема переноса контроллера ArgoCD с отказавшего узла Kubernetes на рабочий узел

Отказоустойчивый запуск ArgoCD в режиме Stateless на bare-metal Kubernetes

Перевод контроллера ArgoCD в режим без сохранения состояния (stateless) устраняет проблему его зависания при сбоях физических узлов. Карточка предлагает пошаговый разбор настройки Kubernetes-кластера на Proxmox с переходом от StatefulSet к Deployment для повышения мобильности управляющей инфраструктуры.

Отказоустойчивый запуск ArgoCD в режиме Stateless на bare-metal Kubernetes

В bare-metal (физических) окружениях Kubernetes проектирование отказоустойчивости управляющих сервисов требует иного подхода по сравнению с облаком, где хосты резервируются переподключаемыми дисками. В собственном дата-центре на «голом железе» инженеры часто ищут баланс между скоростью диска и сложностью распределенного хранения.

Рассмотрим сценарий: кластер развернут поверх Proxmox. Входящий трафик принимает балансировщик HAProxy, перенаправляя его на ноды за NAT к Ingress Controller. В кластере выполняются задачи с интенсивной записью (Kafka, ClickHouse, PostgreSQL). Для обеспечения максимальной пропускной способности NVMe без сетевых задержек вместо Ceph или Longhorn выбрано локальное хранилище через local-path-provisioner. Это привязывает тома данных (Persistent Volumes) к конкретным физическим серверам.

Если для баз данных потеря ноды компенсируется репликацией, то для управляющей инфраструктуры это критично. ArgoCD — инструмент GitOps: он следит за Git, сравнивает состояние и применяет обновления. Если контроллер перестает работать, приложения функционируют, но доставка изменений останавливается. Для непрерывности GitOps необходимо сделать ArgoCD независимым от дисков (stateless), чтобы его компоненты могли перезапускаться на любом исправном узле.

В чем проблема стандартного StatefulSet для ArgoCD?

По умолчанию Helm-чарт разворачивает основной управляющий компонент — контроллер приложений argocd-application-controller — как StatefulSet.

StatefulSet — это тип рабочей нагрузки в Kubernetes для приложений, требующих стабильных сетевых идентификаторов и постоянных дисков. Это нужно для баз данных, но для контроллера ArgoCD с одной репликой жесткая идентичность не имеет смысла.

Когда физический узел выходит из строя, Kubernetes Node Controller видит лишь отсутствие связи с агентом kubelet. Под контроллера переходит в состояние Terminating. API-сервер ожидает подтверждения от упавшего узла о том, что процесс остановлен, чтобы избежать ситуации «расщепленного мозга» (split-brain), когда две копии контроллера пишут в одни и те же ресурсы.

Поскольку подтверждение от зависшего узла не приходит, старый под бесконечно остается в системе со статусом Terminating. StatefulSet строго гарантирует уникальность пода: он никогда не запустит новый под argocd-application-controller-0 на другой ноде, пока старый под не будет удален. В результате процесс синхронизации блокируется до тех пор, пока администратор вручную не выполнит принудительное удаление пода через kubectl delete pod argocd-application-controller-0 --force --grace-period=0 -n argocd.

Переход к Stateless-архитектуре через Deployment

Для решения этой проблемы необходимо перевести контроллер ArgoCD на использование Deployment вместо StatefulSet.

Deployment — это тип рабочей нагрузки, который управляет группой взаимозаменяемых подов без жесткой привязки к имени или узлу. Если под оказывается на упавшей ноде, Kubernetes без лишних проверок создает новый под с уникальным случайным суффиксом на любом доступном здоровом узле.

В официальном Helm-чарте argo-cd для этого предусмотрен параметр controller.dynamicClusterDistribution. Данная опция создавалась для динамического распределения (шардирования) обслуживаемых кластеров между несколькими репликами контроллера. Однако ее ключевое следствие: активация этого флага автоматически меняет тип создаваемого ресурса со StatefulSet на Deployment. Для одиночной реплики контроллера (replicas: 1) шардирование не требуется, но смена типа рабочей нагрузки на Deployment дает необходимую мобильность.

Настройка быстрого выселения подов (Taint-based Eviction)

Сама по себе смена типа ресурса на Deployment не решает проблему долгого ожидания по умолчанию. Если узел отключается, Kubernetes помечает его метками неработоспособности (taints): node.kubernetes.io/unreachable или node.kubernetes.io/not-ready.

По умолчанию поды имеют стандартную толерантность (toleration) к этим меткам, равную 5 минутам. Это означает, что Kubernetes будет ждать 5 минут перед выселением (eviction) и пересозданием подов на других хостах. Решением является сокращение времени ожидания до 90 секунд через переопределение глобальных толерантностей ArgoCD в файле конфигурации Helm:

global:
  tolerations:
    - key: "node.kubernetes.io/unreachable"
      operator: "Exists"
      effect: "NoExecute"
      tolerationSeconds: 90
    - key: "node.kubernetes.io/not-ready"
      operator: "Exists"
      effect: "NoExecute"
      tolerationSeconds: 90

Параметр NoExecute означает, что при обнаружении проблемы под будет принудительно удален с узла. Значение в 90 секунд является оптимальным компромиссом для bare-metal: оно предотвращает ложные срабатывания при кратковременных сетевых колебаниях, но гарантирует автоматическое восстановление управляющего контура менее чем за две минуты.

Инфраструктурная схема высокой доступности (HA)

Для построения отказоустойчивой схемы необходимо сбалансировать и остальные компоненты ArgoCD:

  1. Репозиторий-сервер (repoServer): Отвечает за взаимодействие с Git-репозиториями и генерацию итоговых манифестов. Мы масштабируем его до 3 реплик и задаем правила анти-аффинити (pod anti-affinity) с ключом kubernetes.io/hostname. Это гарантирует, что Kubernetes распределит поды repoServer строго по разным физическим узлам.
  2. Сервер интерфейса и API (server): Обеспечивает работу веб-панели управления. Мы также масштабируем его до 3 реплик с аналогичными правилами anti-affinity.
  3. Кэш Redis (redis): Используется для кэширования манифестов. Все постоянные данные конфигурации хранятся в стандартных объектах Kubernetes и etcd. Кэш Redis можно безопасно удалить и запустить заново. Чтобы избежать привязки Redis к локальному диску упавшего узла, мы полностью отключаем для него использование постоянных томов данных (redis.persistentVolume.enabled: false). При перезапуске Redis запустится в оперативной памяти (in-memory) на любой свободной ноде.

Руководство по развертыванию

Параметры официального Helm-чарта доступны в репозитории argo-helm, а общие принципы резервирования описаны в руководстве Argo CD High Availability.

1. Предварительные требования
  • Наличие как минимум 3 рабочих узлов в кластере.
  • Установленная утилита Helm и права администратора в кластере.
2. Подключение репозитория и установка

Выполните команды в терминале:

helm repo add argo https://argoproj.github.io/argo-helm
helm repo update

Создайте файл values-ha-stateless.yaml с описанной выше конфигурацией (секция global.tolerations, controller.dynamicClusterDistribution: true, repoServer и server по 3 реплики с anti-affinity, redis.persistentVolume.enabled: false).

Запустите установку:

helm upgrade --install argocd argo/argo-cd --namespace argocd --create-namespace -f values-ha-stateless.yaml
3. Проверка успешности развертывания

Убедитесь, что контроллер изменил тип рабочей нагрузки на Deployment:

kubectl get deploy,statefulset -n argocd | grep application-controller

Проверьте распределение подов по узлам кластера:

kubectl get pods -n argocd -o wide

Для получения пароля администратора (храните его в защищенном менеджере!) выполните:

kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
echo

При симуляции аварии (выключении узла с контроллером) Kubernetes по истечении 90 секунд автоматически перенесет под контроллера на одну из оставшихся здоровых нод.

Риски и эксплуатационные ограничения

Использование параметра dynamicClusterDistribution для изменения типа рабочей нагрузки контроллера является недокументированным поведением Helm-чарта. Перед обновлением версии чарта обязательно выполняйте генерацию локальных шаблонов через helm template для проверки изменений.

Сокращение tolerationSeconds до 90 секунд требует стабильности сети между узлами. Если в вашей инфраструктуре случаются кратковременные потери пакетов, частый незапланированный перенос подов ArgoCD (churn) создаст нагрузку на API-сервер.

Жесткие правила распределения подов (requiredDuringSchedulingIgnoredDuringExecution) требуют контроля емкости. Если в кластере останется менее 3 работающих узлов, Kubernetes не сможет запустить избыточные реплики repoServer и server. Для небольших сред замените required на мягкое правило preferred.

Наконец, важно понимать границы применимости. Перевод управляющей плоскости (control plane) ArgoCD в stateless-режим решает проблему доступности инструментов развертывания, но не решает проблему сохранности данных ваших пользовательских stateful-приложений. Разделяйте зоны ответственности управляющих и прикладных сервисов.