Хочу поделиться историей о том, как вайб-кодинг решил мою вполне реальную инженерную боль. Без пафоса, без “я построил SaaS стартап за вечер” — просто задача, Claude и пару вечеров свободного времени.

Делюсь мыслями о работе, жизни и небольшими наблюдениями из будней разработчика в своём Telegram-канале

После показа драфта статьи Николай М. подсказал готовый репозиторий GitHub Action для деплоя Serverless Container в Yandex Cloud, который решает проблему с автоматизацией. Этот факт меня не остановил, чтобы поделиться своим опытом

О чём эта статья

В этой статье покажу, как автоматизировать деплой Serverless Container в Yandex Cloud при пуше нового образа в Container Registry.

Что вы получите:

  • готовый подход к автоматическому редеплою контейнеров на Go

  • пример архитектуры на CR Trigger + Cloud Function

  • Terraform-конфигурацию для развёртывания

  • список необходимых IAM-ролей (самая болезненная часть)

Проблема

Я использую Yandex Cloud как площадку для экспериментов. Быстро поднять сервис, проверить гипотезу, поиграться — для этого облако подходит отлично.

Но одна вещь меня бесила.

После каждого docker push приходилось:

  • открыть веб консоль

  • найти контейнер

  • вручную обновить образ

  • создать новую ревизию

Кадый. Раз.

Всё до Container Registry уже автоматизировано (CI/CD) с помощью github actions, а дальше — ручной труд. Как и большинство инженеров, я ленив в правильном смысле этого слова. А ещё у меня есть почётное звание вайб-кодера и бейдж в подтверждение.

Официальный Vibe coder 😄
Официальный Vibe coder 😄

Поиск готового решения

Первым делом пошёл искать готовое решение в официальных примерах Yandex Cloud. Ничего. Для меня до сих пор странно, что такой базовой функции нет из коробки — пушнул образ, контейнер обновился. Казалось бы.

Обратился в саппорт. К слову, по моему опыту поддержка в облаке Yandex Cloud — один из самых профессиональных. Они не раз выручали в различных вопросах. В этот раз ответ был честный:

“Готового решения нет, стройте своё”

Ближе всего по архитектуре оказался yc-cr-image-scanning — пример с триггером на Container Registry и облачной функцией. Как раз данный функционал уже доступен из коробки облака. Отправная точка нашлась.

Окей.

Сдул пыль с подписки на Claude, заварил чай и отправился в увлекательное путешествие с бездушной машиной в качестве напарника.

Задача

Дано: образ пушится в Container Registry Нужно: Serverless Container автоматически переезжает на новую версию образа. Ограничения: подписка Claude Code за $20, примерно час вечернего времени и горящее нетерпение от ручных кликов.

Архитектура решения

Решение состоит из двух частей.

1. Развёртывание инфраструктуры (один раз)

  • Terraform

  • Cloud Function (Go)

  • Container Registry Trigger

  • Service Account + IAM роли

2. Автоматический редеплой (каждый push)

Сценарий:

  1. docker push

  2. Container Registry генерирует событие

  3. Trigger вызывает Cloud Function

  4. Функция создаёт новую ревизию Serverless Container

Итог — ноль ручных действий.

Как было

Состояние до автоматизации
Состояние до автоматизации

Всё до Container Registry работало автоматически. А дальше — ручной труд: открыть консоль, найти контейнер, обновить образ, раскатить. Пунктиром — то, что хотелось автоматизировать.

Как стало

Решение состоит из двух workflow:

  1. Создание автоматизации (один раз)

Создание функции и триггера
Создание функции и триггера

Terraform через GitHub Actions создаёт всю инфраструктуру: облачную функцию, триггер на Container Registry и сервисный аккаунт с нужными ролями.

  1. Автоматический редеплой (событие обновления версии образа)

Схема автоматизации
Схема автоматизации

Теперь после docker push всё происходит само: триггер ловит событие, функция подхватывает и создаёт новую ревизию контейнера с обновлённым образом. Ноль кликов.

Реализация

В место моих рук, за меня практически все кодогенерировал мой напарник - Claude Code.

Cloud Function (Go)

Функция получает событие о пуше образа и извлекает имя образа:

// упрощённый пример
func Handler(ctx context.Context, event TriggerEvent) (string, error) {
    // Парсинг события обновлении версии образа
    eventData, _ := parseEvent()

    // Получение данных для ЧТО хотим обновить
    config, _ := parseConfig()

    // Получение IAM-токена для проведения операции
    token, _ := getIAMToken()

    // Получение текущей ревизии контейнера для обновления
    rev, _ := getCurrentRevision()

    // вызов API для создания новой ревизии
    return deployRevision(token, containerID, rev)
}

Ключевая задача — корректно распарсить событие.

Terraform

Terraform создаёт всю инфраструктуру:

  • Cloud Function

  • Trigger

  • Service Account

Подробнее можно ознакомиться в исходниках

Роли и доступы

Это основная болененная часть.

Этот этап занял достаточно времени на вымайнивание правильных ролей для сервисного аккаунта. Тот, кто работал с IAM в облаках, знает это ощущение — когда всё логично, но permission denied сыплется на каждом шагу без какой-либо дополнительной инфомарции.

Claude справился, но не с первого раза. Несколько итераций с походами в документацию, уточнениями — и нужный набор ролей собрался. Для запуска функции, создания ревизий контейнера, работы с триггерами. Терраформ всё это аккуратно описал. Важный момент, который легко упустить: наборов ролей здесь два, и принадлежат они разным сервисным аккаунтам. Путать их не стоит — это разница между «функция умеет обновить контейнер» и «функция умеет всё в каталоге».

1. Сервисные аккаунты рантайма — то, с чем живёт решение постоянно

Role

Кому

Зачем

serverless-containers.editor

SA функции

Создание новой ревизии контейнера

iam.serviceAccounts.user

SA функции

Ревизия копируется вместе с serviceAccountId, и облако проверяет, что вызывающий имеет право «использовать» этот сервисный аккаунт

vpc.user

SA функции

Если в конфигурации ревизии есть подключение к VPC — облако проверяет vpc.user у вызывающего

serverless.functions.invoker

SA триггера

Вызов функции по событию из Container Registry

Никакого resource-manager.* тут нет и не нужно.

2. Сервисный аккаунт, от которого выполняется terraform apply — живёт в CI и нужен только в момент раскатки:

Role

Зачем

functions.editor

Создание функции и триггеров

iam.serviceAccounts.admin

Создание runtime-аккаунтов и привязка их к функции и триггеру

resource-manager.admin

Назначение ролей на каталог

И вот resource-manager.admin — ровно то место, где хочется поставить роль поуже и не получится. resource-manager.editor умеет создавать и удалять каталоги, но не умеет менять права доступа: разрешения setAccessBindings / updateAccessBindings есть только у resource-manager.admin (и у примитивной admin). А Terraform здесь как раз раздаёт роли сервисным аккаунтам через yandex_resourcemanager_folder_iam_member — то есть на editor всё упадёт с permission denied прямо на apply.

Если широкая роль в CI смущает — а смущать она должна — лечится это не заменой роли, а выносом IAM за пределы CI: один раз создать оба сервисных аккаунта и выдать им роли отдельно, под администратором, а из основного конфига yandex_resourcemanager_folder_iam_member убрать. Тогда деплойному аккаунту хватит functions.editor плюс iam.serviceAccounts.user на два конкретных SA — и resource-manager.* не нужен вовсе. Цена — роли перестают быть частью того же terraform apply.

Рантайм тоже можно сузить. serverless-containers.editor на каталог означает, что функция может переписать ревизию любого контейнера в нём. Список целевых контейнеров у функции и так зафиксирован в IMAGE_CONTAINER_MAP, так что роль выдаётся на конкретный контейнер через yandex_serverless_container_iam_binding, а iam.serviceAccounts.user — на конкретный аккаунт через yandex_iam_service_account_iam_member. Для vpc.user ресурса уровня сети в провайдере нет, она остаётся на каталоге.

Ещё одна роль, которую регулярно приписывают не туда, — container-registry.images.puller. Ни деплойному аккаунту, ни функции она не нужна: образ тянет сервисный аккаунт самого контейнера при старте ревизии.

Без нужных ролей вы почти гарантированно получите permission denied на каждом шаге — причём облако не подскажет, какой именно роли не хватает.

Формат событий

А вот тут было интересно.

Агент не знал формат событий Container Registry. Это не SQS, хотя Yandex Message Queue рядом и API вроде бы “SQS-compatible”. На практике формат событий для обработчиков отличается, и примеров в открытом доступе мало.

Формат событий для YMQ:

{
  "messages": [
    {
      "event_metadata": {
        "event_id": "abc123",
        "event_type": "yandex.cloud.events.containerregistry.ImagePushed",
        "created_at": "2026-03-19T18:12:00Z",
        "cloud_id": "b1g...",
        "folder_id": "b1f..."
      },
      "details": {
        "registry_id": "crp123",
        "repository_name": "crp123/urlshortener",
        "tag": "latest",
        "image_id": "sha256:abc...",
        "image_digest": "sha256:def..."
      }
    }
  ]
}

Как я дошел до того, что события имеют формат YMQ? Мне помогло немного наблюдения. В интерфейсе заметил, что у триггеров есть dead letter queue. Предположил, что под капотом события летят через очередь. А так как раньше уже писал обработку событий из Yandex Message Queue, формат был примерно знаком. Для того чтобы мне получить полный формат сообщений ранее смотрел скринкаст от сотрудников облака, в котором они показали полный пример обработки сообщений с описанием контракта.

Докинул агенту контекст про структуру событий, подправил обработчик — и получил рабочую версию.

Момент, когда после пуша образа контейнер сам обновился, ощущался как маленькая победа.

Как повторить

  1. Клонировать репозиторий

  2. Настроить переменные Terraform

  3. Запустить:

terraform init
terraform apply
  1. Запушить новый образ:

docker push $your_image_name

После этого контейнер обновится автоматически. Подробнее по шагам все есть в README.md проекта

Роль AI (и где он не справился)

Большая часть кода была сгенерирована с помощью LLM.

Что он сделал хорошо:

  • ускорил написание Terraform

  • помог с IAM ролями

  • сократил цикл «гипотеза → проверка»

Где не справился:

  • не знал формат событий

  • не хватало контекста по Yandex Cloud. Пришлось явно указывать документации на terraform ресурса

Результат

Получился открытый репозиторий — облачная функция на Go + Terraform для всей инфраструктуры. При пуше образа в Container Registry функция автоматически создаёт новую ревизию Serverless Container с обновлённым образом. Надеюсь, что коллеги из облака возьмут мои наработки и по умолчанию добавят возможность обновления контейнеров по аналогии с функцией проверки образов на уязвмости.

Бонусом — деплой новой функции и триггера через GitHub Actions, без необходимости запускать Terraform локально.

Послесловие

Справился бы я без ИИ-агента? Конечно. Но потратил бы пару не вечеров, а скорее все выходные. Основная ценность — не в том, что агент написал код за меня, а в том, что он сократил цикл «гипотеза → проверка → исправление».

Особенно в работе с IAM-ролями и незнакомым API, где каждый шаг — это поход в документацию, которой может в природе еще и не существовать.

При этом агент не справился бы сам. Контекста про формат событий Yandex Cloud в открытом интернете недостаточно. Понадобился мой опыт и инженерная интуиция — подсказать, куда смотреть и как интерпретировать то, что вижу в интерфейсе. Вайб-кодинг — это не «агент делает всё», это совместная работа. Ты ведёшь, он ускоряет.

AI — это инструмент получения быстрого прототипа, но не замена инженера.


Если вы используете Yandex Cloud и сталкивались с ручным редеплоем контейнеров — этот подход может сэкономить вам время.

Что думаете — вайб-кодинг делает нас эффективнее, или пора бросать IT и уходить на ферму растить гусей?

Кому интересно, как трансформируется сфера разработки, рекомендую посмотреть:


UPD: Спасибо за комментарий от @KonkovDV Полез самостоятельно в доку, чтобы верифицировать набор ролей и предоставляемых доступов, которые нужны для все процесса. Актуализировал блок про Роли и доступы и подробнее описал что и кому нужно