Kairos upgrade pipeline показал, как собрать предсказуемый процесс обновления control plane без ручного SSH и ночных дежурств. Важен не сам факт автоматизации, а то, как система переживает ошибки, quorum и неверные изменения в CR.
В этой схеме система упёрлась в старую проблему Kubernetes-обслуживания. Когда control plane обновляют вручную, оператору приходится держать в голове порядок действий, состояние etcd и риск одновременной перезагрузки узлов. В исходном сценарии это был mgmt cluster на K3s HA с тремя control plane nodes, где root платформы должен быть максимально устойчивым. На фоне роста CVE-рисков важнее всего стал не новый функционал, а простой и надёжный upgrade process для Kubernetes.
Выбран был прагматичный путь: immutable OS, GitOps и цепочка CNCF-инструментов вокруг Kairos Hadron. Kairos не патчит систему на месте. Он пишет новый образ в неактивный A/B partition и загружается в него после reboot. Это снижает риск неудачного обновления, а rollback сводится к возврату на старый partition. Компромисс здесь понятен: модель требует строгой дисциплины вокруг образов, CR и сигнатур, зато даёт предсказуемое поведение при сбоях.
Архитектура получилась узкой по ролям, и в этом её сила. Gitea хранит manifests, policies и upgrade spec. Renovate отслеживает новые теги quay.io/kairos/hadron и открывает PR, где меняются image tag и metadata.name у upgrade CR. Kyverno отсекает неверные upgrade CR на admission stage, если image не совпадает с шаблоном. Cosign проверяет подпись образа через GitHub Actions OIDC identity, то есть проверяется не только тег, но и происхождение артефакта. ArgoCD затем применяет merged PR как drift, а kairos-operator исполняет upgrade: cordon, pull image, запись нового A/B slot, reboot, ожидание rejoin и переход к следующему узлу.
Главная инженерная деталь оказалась не в самих компонентах, а в их связке. Изначально в upgrade spec стояло concurrency: 0, и это было ошибкой интерпретации. Вместо «один узел за раз» параметр означал одновременную обработку всех узлов. В homelab test это привело к reboot сразу трёх control plane nodes. etcd quorum уцелел, но это был вопрос удачи, а не устойчивого дизайна. После исправления на concurrency: 1 pipeline стал вести себя так, как и должен вести себя production-minded процесс обновления.
Дальше проявилась ещё одна типичная проблема GitOps-автоматизации: система может выглядеть корректной, но не иметь триггера для реального действия. NodeOpUpgrade — одноразовый custom resource. kairos-operator помечает его завершённым и больше не переобрабатывает. Поэтому простое изменение spec.image на существующем CR ничего не даёт. Renovate должен был менять и metadata.name, чтобы ArgoCD удалил старый CR и создал новый. Здесь же выяснилось, что в custom regex manager был использован несуществующий extractVersionTemplate. Он ничего не сделал. Renovate обновлял image, но оставлял name прежним. Оператор не видел нового CR, а значит не запускал upgrade.
Исправление было точечным, но важным. Переход на currentValueTemplate позволил корректно преобразовывать формат версии из v0-3-0 в v0.3.0 для сравнения, а затем собирать dash-формат обратно. Это хороший пример того, почему human review в такой схеме не убирают полностью. Человек больше не исполняет upgrade вручную. Но он всё ещё нужен, чтобы заметить место, где automation молча делает не то, что кажется на первый взгляд.
Результат уже зафиксирован. На upgrade до Hadron v0.4.0 полный wall-clock time составил 11 minutes. После merge не потребовалось ручного вмешательства. etcd quorum не был нарушен, а workload disruption не было вовсе. Gateway cluster на отдельном single-node Kairos deployment с Netbird теперь подключён к тому же ArgoCD GitOps loop. Следующий логичный шаг уже обозначен: CI dry-run stage с kairos-agent upgrade —recovery перед merge, чтобы ловить ошибки ещё до попадания на live node.