ГлавнаяБлогМультиоблачные агенты: три облака, ноль секретов
Алгоритмы

Мультиоблачные агенты: три облака, ноль секретов

Как соединить агентов Google, AWS и Azure через A2A без единого долгоживущего ключа. Практический гайд с кодом и разбором подводных камней. Читайте и внедряйте!

Al
Редакция Algolitalgolit.ru
12 мин чтения11 августа 2026 г.

Зачем соединять агентов из разных облаков и почему это сложно

Представьте: у вас есть агент на Google Cloud, и его нужно связать с агентом на AWS или Azure. Первая мысль — создать сервисный ключ, положить в секретницу и забыть. Но так вы получаете вечный долгоживущий секрет, который нужно ротировать, ограничивать, аудировать и в итоге объяснять, почему в проде лежит статический ключ. Есть другой путь — федерация удостоверений через OIDC. Он не сложнее, просто требует решений на раннем этапе. В этой статье разберём, как соединить три облака без единого долгоживущего секрета, используя протокол A2A и временные токены.

Ключевое решение: где запускать координатор

Вся архитектура сводится к одному вопросу: какой рантайм может выпускать OIDC-токены для произвольной аудитории? Если может — он может федеративно доверять другим облакам. Если нет — придётся хранить секрет. Сравним три облака:

  • Cloud Run (GCP) — метаданные выдают ID-токен для любой аудитории, которую вы укажете. Это идеально для федерации.
  • AgentCore (AWS) — поддержка не подтверждена, возможно, потребуется секрет.
  • Foundry (Azure) — как минимум один секрет, оба варианта непроверенные.

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

  1. Один лега перестаёт быть межоблачным: GCP вызывает GCP. Пересекаются две границы вендоров, а не три.
  2. Локально это не запустить: пользовательский токен не может выпустить ID-токен с произвольной аудиторией. gcloud auth print-identity-token --audiences=... откажется, требуя сервисный аккаунт.

Три лега, три механизма, один шов

Каждый лега выглядит по-разному:

  • GCP → GCP: ID-токен с audience = URL целевого сервиса, плюс роль roles/run.invoker.
  • GCP → AWS: выпускаем ID-токен, передаём в STS AssumeRoleWithWebIdentity, получаем временные ключи, подписываем запрос SigV4.
  • GCP → Azure: выпускаем ID-токен, используем как client assertion для Federated Identity Credential, получаем access token.

Разные формы: два bearer-токена и подпись запроса. Чтобы унифицировать, используем интерфейс httpx.Auth. Для httpx bearer-заголовок и подпись тела — одинаковые объекты. Все SDK принимают httpx.AsyncClient, поэтому credential присоединяется один раз и передаётся через клиент:

auth = credentials_for(peer, endpoint)  # httpx.Auth или None
client = load_client(stack, endpoint, auth=auth)

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

Пять ловушек, которые выглядят как рабочая конфигурация

Аудитория — не авторизация

Аудиторию выбирает вызывающий. Если проверять только audience, вы докажете, что кто-то в IdP выпустил токен, но не что это ваш сервис. Привязывайте subject — и привязывайте к числовому ID, а не к email, потому что email можно освободить и перепривязать к другому пользователю.

AWS и Azure инвертируют один и тот же шаг

AWS федеративно работает с accounts.google.com из коробки. Если создать явный OIDC-провайдер для него — сломаете с InvalidIdentityToken. В Azure нужно явно создавать Federated Credential. Одна и та же задача, противоположные требования, и ни одна ошибка не подскажет, на каком вы правиле.

Ключи условий IAM не соответствуют названиям

accounts.google.com:oaud — это audience токена, а accounts.google.com:aud — azp, то есть число. Если в :aud подставить строку audience — условие никогда не совпадёт, и отказ не будет об этом упоминать.

Запрашивайте полный токен

Метаданные GCP требуют format=full. Без него Google обрезает claims, включая email, и условия, читающие этот claim, молча перестают работать.

Два кода ошибки — два разных дня

От STS: InvalidIdentityToken — токен не прошёл валидацию, проблема с провайдером. AccessDenied — токен валиден, но условия не совпали, проблема с политикой. Это разные проблемы, и их нужно различать.

Как запустить демо локально

Клонируйте репозиторий и установите зависимости:

git clone https://github.com/xbill9/multicloud-adk-a2a-currency
cd multicloud-adk-a2a-currency
uv pip install --system "a2a-sdk[http-server]" google-adk agent-framework-a2a agent-framework-core pydantic httpx uvicorn pytest pytest-asyncio
uv pip install --system -e .

Запустите три агента и задайте вопрос:

./infra/run_mesh.sh start
python3 -m coordinator.cli 100 USD EUR JPY

Вывод:

participants: gcp, aws, azure
100 USD = 92 EUR @ 0.92 [3/3 clouds, agreed]
gcp 92 (164ms) aws 92 (25ms) azure 92 (12ms)

Демо с ошибками:

./infra/demo.sh

Четыре акта: три облака отвечают, матрица совместимости 3×3, облако уходит офлайн, облако врёт. Последние два — суть: что угодно может показать три зелёные галочки.

Развёртывание: скрипты, которые переживают пересоздание

Развёртывание — это команды, а не runbook. Каждый идентификатор живёт в одном месте — в скрипте, который его создал. Остальные скрипты читают его оттуда. Это проверено: я полностью снёс и пересоздал инфраструктуру с нуля.

AWS вернул другой ARN, Azure — другой client ID, Container App — другой FQDN. Ничего не правилось вручную. wire прочитал всё заново, и меш снова ответил: 100 USD = 92 EUR @ 0.92 [3/3 clouds, agreed].

Полная проверка прошла на инфраструктуре, которой час назад не существовало: три консенсуса и восемь проверок авторизации — каждая лега отвечает со своим credential, каждая лега отклонена без него, неаутентифицированный запрос отклонён, запрос с правильной идентичностью, но неправильной аудиторией отклонён.

Пересоздание также выявило два бага, которые не видны при обычном деплое:

  1. Ретрай-обёртка в AWS-скрипте превращала «нет рантайма» в ошибку вместо None, и первый деплой умирал молча.
  2. Azure soft-delete для Cognitive Services: удаление resource group не удаляет аккаунт, и повторное создание падает с FlagMustBeSetForRestore.

Вывод: пересоздавайте с нуля хотя бы раз, прежде чем называть систему воспроизводимой.

Масштабирование до нуля и холодные старты

Вся система простаивает с нулём реплик. Но первый вызов в легу платит холодный старт: холодная Azure-лега — 27.8 секунд против 0.5 секунд в тёплом состоянии. Если смешать эти режимы в одной таблице, все выводы будут ложными. Указывайте, какой режим измеряли.

Структуры, которые стоит позаимствовать

  • Единый шов credential (httpx.Auth) — вызывающие не знают, какой из трёх механизмов используется.
  • Единый интерфейс участника (convert()) — облако это реализация, а не ветка.
  • Инструмент, а не демо — каждая ошибка типизирована по слою.
  • Контроли, ограниченные одной легой — деградирующая система не скроет отказ.

Последний пункт — самый важный. Меш берёт медиану по трём облакам и деградирует намеренно. Потеря одного облака — остальные два достигают кворума, и запуск завершается с кодом 0. Если тестировать авторизацию, удалив credential одной леги из трёхоблачного запуска, он всё равно выйдет с 0. Это читается как «отказа не было», а на самом деле «отказ был поглощён». Поэтому каждую легу проверяем отдельно: восемь проверок — каждая лега отвечает со своим credential, каждая отклонена без него, неаутентифицированный запрос отклонён, и запрос с правильной идентичностью, но неправильной аудиторией отклонён.

Практический вывод

Прямо сейчас: склонируйте репозиторий, запустите локальный меш и посмотрите, как три облака отвечают согласованно. Затем разверните хотя бы одну легу в облаке и проверьте, как работает федерация. И главное — пересоздайте всё с нуля, чтобы убедиться, что ваша система действительно воспроизводима. Это займёт меньше часа, но сэкономит дни отладки в будущем.

#мультиоблачные агенты#A2A протокол#OIDC федерация#безопасность#Cloud Run
Al
Редакция Algolit

Пишем про алгоритмы, подготовку к собеседованиям и карьеру в IT — так, чтобы было понятно и полезно.

Хочешь закрепить знания на практике?

Решай задачи на Algolit — интерактивная платформа для обучения

Начать бесплатно →