Перейти к основному содержимому

Control plane для CI и инфраструктуры:
процесс и ресурс в одной модели.

Проблема

Любая автоматизация вокруг разработки состоит из двух вещей: процессов (собрать, протестировать, выкатить) и инфраструктуры, на которой эти процессы живут (машины, кластеры, базы, стенды). Инструменты исторически разделились по этой границе: CI-системы взяли себе процессы, IaC-инструменты — инфраструктуру. Граница прошла ровно по самому интересному месту.

CI-системы управляют графом шагов выполнения — и только им. Время жизни и состояние инфраструктуры, которую эти шаги создают, остаются за пределами модели.

Типичный сценарий: job создаёт машину, запускает на ней тесты и последним шагом её удаляет. В этой модели существует только одно понятие — шаг. Машина, которую шаг создал, для CI не существует: у неё нет состояния, нет владельца, нет времени жизни. Поэтому модель не отвечает на вопросы:

  1. Что происходит с машиной прямо сейчас? У CI есть статус шага — состояния ресурса не существует.
  2. Кто владеет машиной и до какого момента она должна жить?
  3. Кто гарантирует удаление, если job отменён, истёк таймаут или умер раннер? destroy — просто ещё один шаг, который может не наступить.
  4. Как описать параметры машины контрактом для вызывающего? Вход у workflow — нетипизированные строки.
  5. Как другой job или пайплайн переиспользует уже созданную машину? Передать ресурс между запусками нечем.
  6. Как продолжить упавший ран с места падения, не пересоздавая машину? Retry запускает всё с начала.
  7. Как выполнить шаг на самой машине, не открывая на ней порты и не раздавая ssh-ключи через секреты CI?

Всё, что модель не умеет, дописывается скриптами вокруг скриптов — их назначение растворяется в объёме, а поддержка превращается в отдельную полноценную разработку.

IaC-инструменты (Terraform, Pulumi, Crossplane) — зеркальная противоположность. В их модели существует только одно понятие — желаемое состояние. Ресурс описан, но процесс, который его создал и ради которого он существует, для IaC не существует. Поэтому и эта модель не отвечает на свои вопросы:

  1. Для чего ресурс существует и кто его потребитель? В state есть «что», но нет «зачем» — использование ресурса лежит целиком вне модели.
  2. Сколько ресурс должен жить? Ресурс существует, пока его описание есть в коде: временный стенд на один ран модели чужд.
  3. Кто владеет ресурсом, когда пайплайнов много? State один на всех — параллельные раны выстраиваются в очередь за lock или делят чужие ресурсы.
  4. Что будет, если apply умер посередине? Это команда, а не восстанавливаемый процесс: state рассинхронизирован, разбор ручной.
  5. Кто заметит, что реальность разошлась с описанием? Drift обнаруживается на следующем plan — то есть тогда, когда кто-то вручную его запустит.
к сведению

CI владеет процессом без ресурсов; IaC — ресурсами без процесса. Связка «этот процесс создал этот ресурс, использует его и отвечает за его смерть» не существует ни в одной из моделей — она и есть предмет graphene.

Graphene

Graphene — control plane для CI и инфраструктуры: процесс и ресурс существуют в одной модели.

Идея

Два решения определяют graphene:

  1. Пайплайн — программа. Не YAML со встроенным shell, а код на языке общего назначения: типы, управляющие конструкции, библиотеки, тесты. Объявление ресурса и действие на машине — обычные вызовы функций. Ран пайплайна — восстанавливаемый процесс, а не последовательность шагов.

  2. Всё созданное — учтено. Каждый ресурс получает durable-запись: состояние, владелец, время жизни. Владельцы образуют дерево; удаление владельца каскадно удаляет его поддерево. Нельзя создать ресурс, не записав его, — осиротевшая инфраструктура невозможна по построению.

Из этих двух решений следуют ответы на оба списка вопросов:

  • Что с ресурсом сейчас? Запись хранит состояние и фазу; её можно спросить в любой момент, не трогая сам ресурс.
  • Кто владеет и сколько жить? Владелец и срок жизни — часть записи, а не комментарий в коде.
  • Кто удалит? Смерть владельца удаляет всё его поддерево; отмена и падение рана — тоже смерть.
  • Контракт параметров? Вход пайплайна типизирован — контракт проверяет компилятор, а не соглашение о строках.
  • Переиспользование? Ресурс передаётся другому владельцу явно — другому ресурсу или стенду пайплайна, со сроком жизни.
  • Продолжить с места падения? Ран восстановим: после сбоя он продолжается с места остановки, не пересоздавая ресурсы.
  • Выполнить на машине? Агент на машине сам подключается к серверу наружу; открытые порты и раздача ssh-ключей не нужны.
  • Зачем ресурс существует и что если apply умер? Ресурс создан конкретным раном и записан на него; создание — часть восстанавливаемого процесса, а не команда, которую можно потерять на середине.
  • Кто заметит drift? Запись ресурса — живой процесс: он сам периодически сверяет реальность с желаемым состоянием.