Control plane для CI и инфраструктуры:
процесс и ресурс в одной модели.
Проблема
Любая автоматизация вокруг разработки состоит из двух вещей: процессов (собрать, протестировать, выкатить) и инфраструктуры, на которой эти процессы живут (машины, кластеры, базы, стенды). Инструменты исторически разделились по этой границе: CI-системы взяли себе процессы, IaC-инструменты — инфраструктуру. Граница прошла ровно по самому интересному месту.
CI-системы управляют графом шагов выполнения — и только им. Время жизни и состояние инфраструктуры, которую эти шаги создают, остаются за пределами модели.
Типичный сценарий: job создаёт машину, запускает на ней тесты и последним шагом её удаляет. В этой модели существует только одно понятие — шаг. Машина, которую шаг создал, для CI не существует: у неё нет состояния, нет владельца, нет времени жизни. Поэтому модель не отвечает на вопросы:
- Что происходит с машиной прямо сейчас? У CI есть статус шага — состояния ресурса не существует.
- Кто владеет машиной и до какого момента она должна жить?
- Кто гарантирует удаление, если job отменён, истёк таймаут или
умер раннер?
destroy— просто ещё один шаг, который может не наступить. - Как описать параметры машины контрактом для вызывающего? Вход у workflow — нетипизированные строки.
- Как другой job или пайплайн переиспользует уже созданную машину? Передать ресурс между запусками нечем.
- Как продолжить упавший ран с места падения, не пересоздавая машину? Retry запускает всё с начала.
- Как выполнить шаг на самой машине, не открывая на ней порты и не раздавая ssh-ключи через секреты CI?
Всё, что модель не умеет, дописывается скриптами вокруг скриптов — их назначение растворяется в объёме, а поддержка превращается в отдельную полноценную разработку.
IaC-инструменты (Terraform, Pulumi, Crossplane) — зеркальная противоположность. В их модели существует только одно понятие — желаемое состояние. Ресурс описан, но процесс, который его создал и ради которого он существует, для IaC не существует. Поэтому и эта модель не отвечает на свои вопросы:
- Для чего ресурс существует и кто его потребитель? В state есть «что», но нет «зачем» — использование ресурса лежит целиком вне модели.
- Сколько ресурс должен жить? Ресурс существует, пока его описание есть в коде: временный стенд на один ран модели чужд.
- Кто владеет ресурсом, когда пайплайнов много? State один на всех — параллельные раны выстраиваются в очередь за lock или делят чужие ресурсы.
- Что будет, если
applyумер посередине? Это команда, а не восстанавливаемый процесс: state рассинхронизирован, разбор ручной. - Кто заметит, что реальность разошлась с описанием? Drift
обнаруживается на следующем
plan— то есть тогда, когда кто-то вручную его запустит.
CI владеет процессом без ресурсов; IaC — ресурсами без процесса. Связка «этот процесс создал этот ресурс, использует его и отвечает за его смерть» не существует ни в одной из моделей — она и есть предмет graphene.
Graphene
Graphene — control plane для CI и инфраструктуры: процесс и ресурс существуют в одной модели.
Идея
Два решения определяют graphene:
-
Пайплайн — программа. Не YAML со встроенным shell, а код на языке общего назначения: типы, управляющие конструкции, библиотеки, тесты. Объявление ресурса и действие на машине — обычные вызовы функций. Ран пайплайна — восстанавливаемый процесс, а не последовательность шагов.
-
Всё созданное — учтено. Каждый ресурс получает durable-запись: состояние, владелец, время жизни. Владельцы образуют дерево; удаление владельца каскадно удаляет его поддерево. Нельзя создать ресурс, не записав его, — осиротевшая инфраструктура невозможна по построению.
Из этих двух решений следуют ответы на оба списка вопросов:
- Что с ресурсом сейчас? Запись хранит состояние и фазу; её можно спросить в любой момент, не трогая сам ресурс.
- Кто владеет и сколько жить? Владелец и срок жизни — часть записи, а не комментарий в коде.
- Кто удалит? Смерть владельца удаляет всё его поддерево; отмена и падение рана — тоже смерть.
- Контракт параметров? Вход пайплайна типизирован — контракт проверяет компилятор, а не соглашение о строках.
- Переиспользование? Ресурс передаётся другому владельцу явно — другому ресурсу или стенду пайплайна, со сроком жизни.
- Продолжить с места падения? Ран восстановим: после сбоя он продолжается с места остановки, не пересоздавая ресурсы.
- Выполнить на машине? Агент на машине сам подключается к серверу наружу; открытые порты и раздача ssh-ключей не нужны.
- Зачем ресурс существует и что если
applyумер? Ресурс создан конкретным раном и записан на него; создание — часть восстанавливаемого процесса, а не команда, которую можно потерять на середине. - Кто заметит drift? Запись ресурса — живой процесс: он сам периодически сверяет реальность с желаемым состоянием.