Создание бессерверного перфоратора NAT в AWS CloudFormation - подробное описание реальной бессерверной конфигурации

Недавно я потратил более 15 часов на расшифровку рабочего приложения, которое я создал с помощью пользовательского интерфейса консоли AWS и сценариев командной строки, в декларативное развертывание с использованием AWS CloudFormation.

Большинство примеров в официальных документах AWS обычно довольно «микро», и мое приложение - хотя и невелико - достаточно сложное, поэтому потребовалось много поисков и экспериментов, чтобы собрать все вместе и для запуска внутри AWS CodeStar.

Я получил много пользы от других, у кого были решения и руководства для блогов, поэтому, желая отдать должное, вот оно: аннотированный тур по нетривиальному шаблону CloudFormation, который развертывает веб-сокет Amazon API Gateway, поддерживаемый AWS Lambda и Amazon DynamoDB. стол. Думайте об этом как о «CRUD для бессерверных веб-узлов».

Далее следует все подробности ... если вы хотите узнать о веб-сокетах, услышать, почему Serverless - это круто, или хотите прочитать еще один религиозный трактат о декларативном и императивном споре - это не те дроиды, которых вы ищете (tm) .

Хотя этот пост посвящен спецификации развертывания как таковой, если вы хотите узнать больше о кодируемом шаблоне проектирования, ознакомьтесь с моей сопутствующей статьей о Бессерверных архитектурах Mullet:

CloudFormation - это сложно

Хорошо, давайте сразу же закончим с плохими новостями: AWS CloudFormation (он же CF) может быть сложной задачей. И многословно. И трудно отлаживать. И хотя инструментарий мог бы быть лучше (я попытался найти расширение VS Code, которое может проверять ссылки CF), факт в том, что и у разработчиков CloudFormation, и у нас, пользователей, есть некоторые трудные проблемы, которые необходимо решить.

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

Включая комментарии, этот шаблон CF содержит более 500 строк, что превышает длину кода в Lambda-функции, которую он развертывает. Соберитесь.

Чего нет в моем шаблоне CloudFormation

Давайте начнем с рассмотрения «негативного пространства» - чего я не вложил в этот шаблон и почему:

Одноразовая настройка на уровне учетной записи
Когда я начинал, мне казалось интуитивно понятным, что я должен попробовать «CloudFormation все»; Другими словами, создайте один сценарий, который возьмет новую учетную запись AWS и превратит ее в учетную запись, в которой работает мой бессерверный NAT Puncher.

К сожалению, я быстро понял, что одноразовую настройку сложно включить. CloudFormation имеет сильную тенденцию к созданию и управлению развертываниями, которые можно снова свернуть.

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

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

Трудно отменить (или повторно запустить) действия
Тот же урок, что и выше, но вы узнали, пытаясь встроить диспетчер сертификатов Amazon (также известный как «ACM») в шаблон. Я надеялся, что смогу сослаться на простое имя сертификата и заставить CF использовать ACM для поиска его ARN (поскольку простое имя было немного меньше похоже на волшебную строку), но, увы, это не удалось.

Извлеченный урок: согласитесь, что будут места, где вам придется вручную согласовывать строки.

Упаковка тестового клиента
В моем первоначальном подходе «все в одном» я попробовал объединить как тесты, так и сервис, который они тестировали, в один шаблон CF. В то время как YMMV для меня это было ошибкой - мне пришлось гораздо чаще разбирать, реструктурировать и воссоздавать стеки для тестов и быстро вытаскивать их из шаблона службы.

Извлеченный урок: создайте шаблоны тестов (или других дорожек для плавания с разной скоростью) отдельно.

CodeStar (и его собратья)
Я также очень быстро переосмыслил еще одно важное ограничение: AWS CodeStar создает только проекты; он не «перенимает» существующие проекты.

Если вам нравится использовать пакет Code * для создания, управления, развертывания и совместного использования ваших проектов, вам нужно решить, что ВПРАВО ВВЕРХ, сгенерировать образец CodeStar для одного из проектов на основе Lambda, а затем отредактировать CF шаблон, который он генерирует.

[Так было 30 сентября 2019 г., когда я писал это, но AWS быстро развивается, поэтому к тому времени, когда вы это прочтете, все могло измениться.]

В любом случае построение конвейера с самим собой - это слишком мета, чтобы работать, поэтому вы должны сначала выдержать это, а затем использовать его для своего (реального) развертывания приложения.

Давайте перейдем к Кодексу

Хорошо, хватит предварительных сведений. Давайте перейдем к коду, который * здесь *!

Раздел Transform довольно прост; просто помните, что единственный работоспособный способ получить это AWS::CodeStar - это позволить CodeStar сгенерировать ваш шаблон для вас. Преобразователь CodeStar должен поддерживать границы разрешений в актуальном состоянии ... подробнее об этом ниже, но обратите внимание, что он не работает так, как рекламируется.

Я включил преобразователь Serverless, чтобы я мог использовать упрощенный синтаксис SAM для таких элементов, как лямбда-функция, хотя он не сильно снижает общую сложность шаблона, и вы можете легко отказаться от него здесь и пишите в чистом CF, если хотите.

Раздел Parameters представляет «аргументы» для шаблона CF. Я предполагал, что это будет тесно интегрировано в семейство Code * - например, я предполагал, что мне нужно настроить конвейер CodePipeline для «разработки» (а другой - для «производства»), а затем он, в свою очередь, сообщит CodeBuild и CloudFormation сцена.

Увы, это не происходит автоматически ... все это настраивается вручную, и на самом деле получить дополнительную контекстную информацию для передачи между CodePipeline и CodeBuild довольно сложно. Обратите внимание, что ProjectId и CodeDeployRole вставляются CodeStar.

Раздел Globals лучше рассматривать как раздел «по умолчанию» - он устанавливает значения по умолчанию, которые будут применяться, если вы не переопределите их для каждого объекта. Они вставляются для нас CodeStar при создании проекта и позволяют выполнять инкрементные развертывания Lambda.

Канарейка в названии намекает на то, что это означает: AWS CodeDeploy будет отслеживать метрику функции и отказываться от развертывания, если частота ошибок возрастает. Вы можете узнать больше о поддержке CodeDeploy для переключения трафика при развертывании Lambda здесь.

Operational aside: этот параметр означает, что помимо накладных расходов на CodeStar, github (или CodeCommit, если вы выбрали это), CodePipeline, CodeBuild и CloudFormation, у вас также будет минимум 5 минут для развертываний Lambda Canary при каждом успешном развертывании. Возможно, вы захотите выключить (или выключить) этот параметр во время разработки, а затем восстановить его во время производства.

Определение функции AWS Lambda

Ура - наше первое актуальное определение!

Здесь много распаковок, поэтому мы рассмотрим их поэтапно:

Сравнение SAM и CloudFormation
Здесь я использовал тип SAM, как видно из ::Serverless:: в этом типе. Для простых бессерверных проектов SAM может сэкономить много времени на вводе текста. В этом проекте столько многословия, что SAM (в настоящее время) не учитывает, что его использование не имеет большого значения. Тем не менее, если вы хотите использовать варианты SAM, вам понадобится трансформатор (см. Выше).

Изменение имени вручную
CloudFormation будет изменять имя (с помощью некоторых неприятных суффиксов хеша) создаваемых им выходных данных, а CodeStar заставляет вносить небольшие изменения в имя конкретного проекта. шаблон, но в основном вы сами по себе, чтобы имена не противоречили друг другу.

Я поставил перед (многими) элементами, связанными с NAT Puncher, название проекта, чтобы в дальнейшем можно было добавлять элементы в этот шаблон, не беспокоясь о коллизиях. Может показаться, что наименования вашей функции «function» или API-интерфейса «api» легко избежать, но по мере увеличения количества развертываемых объектов становится легко ослабить манипуляции.

CodeUri
Это немного сбивает с толку - в этом шаблоне это локальный файл или каталог. Шаг упаковки, который происходит в конце фазы CodeBuild, создаст другую копию этого шаблона, и в этой копии CodeUri будет переписан, чтобы вместо этого ссылаться на объект. в ведре S3 (так что термин «uri» в конечном итоге имеет смысл).

Мне нужно было включить только один файл, но если у вас несколько файлов, я бы посоветовал поместить их в каталог src и присвоить этому свойству значение src /. Обратите внимание на важную завершающую косую черту.

Слои
Я решил упаковать необходимые компоненты Python в слой. Будет ли это хорошей идеей, зависит от вашей практики разработки. Этот проект начал свою жизнь как прототип, в котором я использовал пользовательский интерфейс консоли API Gateway для создания веб-сокета и ввел код непосредственно в консоль AWS Lambda.

На этом этапе проектирования отсутствие необходимости повторно загружать зависимости Python каждый раз, когда я вносил небольшие изменения, было огромной победой. Это не так уж важно, когда вы выполняете накладные расходы на полную сборку проекта CodeStar, особенно если единственным потребителем вашего слоя является ваш собственный проект.

Тем не менее, мне легче разделить «настоящую» функцию и модули Python, которые она использует. Если вы все же решите сохранить свои предварительные требования Python в слоях, я настоятельно рекомендую написать собственный скрипт, чтобы заархивировать их ... есть масса мусора, который вы можете подобрать, если не будете осторожны (например, PIP), который действительно раздувает вашу лямбда-функцию !

Я предлагаю просмотреть каждый объект в ZIP-архиве и убедиться, что он вам действительно нужен во время выполнения, а не просто заархивировать все, что попало в пакеты сайта.

Роль
Подробнее об этом позже, когда мы определим фактическую роль, но это хорошее место, чтобы отметить разницу между «ref» и «getattr» для объекта: первое возвращает его простое имя, но вам нужно последнее, чтобы получить его ARN.

Переменные среды
Лямбда-коду необходимо знать имя базы данных Amazon DynamoDB, чтобы он мог получить доступ к таблице во время выполнения. Мы также могли бы изменить таблицу здесь, но в остальном функция не зависит от существования таблицы во время развертывания.

Ref’s, sub’s и getattr встраивают DAG зависимости в ваш шаблон CF. CF выполняет топологическую сортировку в этой группе DAG, чтобы определить жизнеспособный порядок развертывания. Здесь я упростил задачу, избегая ложных дополнительных ребер в этом графе за счет дублирования формулы изменения имен для БД. YMMV… выберите меньшее зло :).

Перерыв на кофе!

Устали еще? Мы только начинаем, так что сейчас хорошее время для кофе-брейка! Теперь перейдем к ролям и веб-сокетам API Gateway!

Лямбда-слой - модули Python

Это довольно просто, хотя проблема фильтрации уровня до того, что требуется, здесь не отражена - она ​​находится в сценарии bash, который тщательно редактирует то, что заархивировано. Обратите внимание на косую черту в конце имени каталога, которую легко пропустить.

Наконец, чтобы избежать нехватки места в процессе разработки (если только вы не тип «дырка в одном»), вам, вероятно, потребуется установить для политики хранения слоев значение Delete. Я подумал, что это будет просто повторное использование версии слоя №1 снова и снова, поскольку именно это происходит, когда вы удаляете и повторно загружаете одно и то же имя версии с помощью пользовательского интерфейса Lambda. Но вместо этого я обнаружил, что он увеличил номер версии, исключив предыдущую.

Роль IAM лямбда-функции

Хорошо, вот один из длинных разделов: роль IAM лямбда-функции. В чистом шаблоне SAM это может быть в основном невидимым, но сложность, необходимая для CodeStar, заставляет это отображаться во всей своей многословной красе:

AssumeRole
Это шаблон, который необходим каждой лямбда-функции - разрешение на запуск в качестве роли, определенной здесь.

Управляемые политики
Это «мясо» роли - то, что может делать функция Lambda. LambdaBasicExecutionRole позволяет функции Lambda отправлять журналы в AWS CloudWatch, «минимальные» разрешения роли для нормальной функции.

Кстати, я не знаю, почему у этой роли в названии есть "служебная роль", а у других ролей, предоставляемых службами, нет. Кто-нибудь в AWS знает об этом соглашении об именах?

Роль DynamoDB довольно понятна, хотя она избыточна для того, что нам нужно ... и это обоюдоострый меч управляемых политик: вы в конечном итоге используете их, потому что они просты, но они должны сделать ресурс, которым они управляют, «*» поскольку они являются общими и обычно предоставляют гораздо больше действий, чем строго требуется.

Здесь совершаются оба этих греха, «намного превышающих минимальные привилегии». Положительным моментом является то, что роль управляется AWS и работает над первым кадром без необходимости писать ASPEN (язык разрешений IAM) самостоятельно, что экономит мне кучу времени.

Я оставил создание санкционированной вручную, тесно связанной пользовательской политики в качестве будущего элемента TODO для этого проекта. Для использования конечной точки HTTPS для асинхронных обратных вызовов требуется разрешение на вызов API-шлюза; это часть того, что делает веб-сокеты более мощными, чем просто запрос / ответ на лямбда-функцию, но должно быть отражено в роли IAM, иначе это не будет разрешено.

Граница разрешений
О, CodeStar, вам приходилось все усложнять, не так ли? Границы разрешений - это функция безопасности AWS IAM, которая не позволяет разработчикам (или другим лицам) создавать разрешения для себя путем создания функции Lambda, экземпляра EC2 или другого развертывания с предполагаемой ролью, которое повышает их привилегии до уровня суперпользователя в AWS. положение дел.

Предполагается, что преобразователь CodeStar отслеживает роль функции Lambda и автоматически управляет границей разрешений, чтобы обеспечить использование дополнительных ресурсов, по крайней мере, в соответствии с документами CodeStar.

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

Вы можете увидеть, что разрешено, перейдя в консоль IAM и просмотрев границу разрешений. Для меня это было настоящим горючим ... Я потерял несколько часов на отладку этого конкретного грубого пятна, тем более, что границы разрешений для функции и CodeBuild могут быть разными, что приводит к разному опыту в зависимости от того, как вы тестируете.

AWS :: Partition против «aws»
В некоторых регионах AWS вместо «aws» в ARN выделяется разделение облака AWS, например «aws-cn» (Китай) или « aws-us-gov »(GovCloud). Использование AWS::Partition в вашем шаблоне вместо «aws» позволяет повторно использовать его прозрачно в этих других регионах… хотя в некоторых случаях, как это верно для API, правовые или нормативные ограничения препятствуют этому по другим причинам.

Чтобы сделать его более переносимым, я мог (должен) пройти через этот шаблон и исправить оставшиеся ARN, чтобы использовать нотацию разделов, но на данный момент у меня нет планов развертывать его в какой-либо из этих областей. Копирование из документации и блогов AWS приведет к противоречивым результатам, из-за чего я оказался в состоянии несоответствующей обработки разделов.

Определение таблицы DynamoDB

Это просто, и использование AWS::Serverless::SimpleTable немного снижает беспорядок. См. Примечание о согласованности имен по сравнению с исх.

Кстати, я хотел, чтобы мои модульные тесты могли читать и записывать фактические данные в базу данных, что создало интересную проблему с курицей и яйцом - мне нужно развернуть этот шаблон для создания базы данных, но обычно развертывание не происходит, если модульные тесты на этапе сборки проходят успешно.

Я просто отключаю модульные тесты в (очень редком) случае, когда мне нужно что-то настроить в определении таблицы, но в противном случае мне пришлось бы запускать модульные тесты чисто автономно и добавить еще один этап CodePipeline для выполнения пост-развертывания интеграционный тест.

В конце концов, стоило бы иметь, но это был уровень сложности поверх ванильного конвейера CodeStar, который я получил «бесплатно», а не место, где я изначально хотел проводить много времени.

Все еще со мной? Готовы к сложному?

Веб-сокет шлюза API

Веб-сокет API Gateway вместе с маршрутами, интеграциями, ответами и моделями.

Это медведь, и суть выше даже не включает в себя детали развертывания и сцены (см. Ниже), только функциональные элементы.

Давайте сначала поговорим о структуре бессерверного веб-сокета AWS, как она представлена ​​в CF:

AWS :: ApiGatewayV2 :: Api
Это «основное» определение; здесь «Api» означает веб-сокет, хотя вы узнаете это только по свойству ProtocolType. Обратите внимание, что все, что связано с веб-сокетом, должно использовать V2 API-интерфейса Amazon API Gateway; несколько раз я ошибался и получал загадочные ошибки, пока не понял, что в одной из частей не указан V2. К сожалению, ничто не предупредит вас явно.

Маршруты
Маршруты - это «команды», о которых знает веб-узел. API Gateway играет роль диспетчера, формируя данные, а затем выясняя, какую лямбда-функцию следует вызвать. $connect, $disconnect и $default - это встроенные маршруты, а в нашем случае pair и status - это настраиваемые маршруты.

Интеграции
Каждый пользовательский маршрут имеет обязательную интеграцию, которая представляет собой функцию Lambda, вызываемую, когда этот маршрут обнаруживается во фрейме данных API-шлюзом. Для встроенных маршрутов интеграция необязательна. Если API Gateway не может найти собственный маршрут для использования, он использует $default; в нашем случае мы просто связываем это с ошибкой.

Существует множество вероисповеданий относительно того, следует ли использовать одну лямбду для каждого маршрута или одну лямбду для всех маршрутов; Я считаю, что последним легче управлять, когда ваши функции тесно связаны и разделение ролей имеет смысловой смысл.

Здесь все мои маршруты являются частью «CRUD» - все они читают и / или записывают в общую таблицу DynamoDB, поэтому их разделение ничего не дает; он не развивается быстрее и не обеспечивает дополнительной безопасности, но значительно усложняет ситуацию. Итак, одна функция это.

Ответы
Интеграции в веб-сокеты бывают двух видов: с ответом или без него. С ответом происходит примерно то, что вы ожидаете: вызываемая функция Lambda может возвращать значение; API Gateway ожидает завершения функции, а затем помещает свой результат в веб-сокет, отправляя его обратно клиенту, который предположительно блокирует, ожидает его, хотя это выходит за рамки нашего обсуждения здесь.

В нашем случае status - хороший пример маршрута с ответом: это, по сути, REST API, замаскированный под пару сообщений веб-сокета. Pair - пример маршрута без ответа; «ответ» на запрос на сопряжение отправляется асинхронно через URL-адрес обратного вызова, предоставленный шлюзом API.

Чтобы включить ответ для данного маршрута, вы используете выражение RouteResponseSelectionExpression: $default. $default здесь немного не интуитивно понятен, так как это, по сути, просто логический переключатель, и никакие другие значения не допускаются.

Модели и проверка формованности

Встроенные маршруты не имеют параметров, но пользовательские маршруты могут дополнительно иметь модели, как показано здесь для pair и status. Модели позволяют связать схему JSON с маршрутом, что позволяет шлюзу API проверять синтаксис сообщения за вас.

Это усложняет ваш сценарий развертывания, но избавляет вас от необходимости писать код проверки синтаксиса самостоятельно, поскольку вы можете рассчитывать на получение только правильно сформированных сообщений. Я объединяю это с использованием $default в качестве обработчика ошибок.

Таким образом, мой код должен обрабатывать только ожидаемые, правильно сформированные запросы и может игнорировать крайние случаи, если он не имеет дело с $ default, и в этом случае он может просто проигнорировать сообщение и сообщить вызывающей стороне, что оно искажено. (FWIW, когда я писал это, у меня не было возможности заставить Refing модели работать правильно, поэтому просто использовал его имя в шаблоне. Маршрут действительно должен ссылаться на модель.)

Разные предметы Websocket

В остальном это просто, хотя и немного привередливо. Кстати, название операции, похоже, предназначено только для отображения и не несет никакого семантического значения, которое я мог бы найти.

Учитывая, сколько у нас было в предыдущем блоке для определения веб-сокета, казалось бы, с этим мы закончили ... но на самом деле требуется много неструктурного материала, чтобы превратить предыдущие объекты в работающее развертывание веб-сокета.

Однако прежде чем мы углубимся в подробности, давайте поговорим о многих понятиях развертывания в бессерверных сервисах AWS:

  1. Развертывание CloudFormation. В этой статье мы в основном говорим о стеке (наборе связанных ресурсов), созданном CloudFormation по рецепту в шаблоне.
  2. Развертывание шлюза API. API шлюза API (включая веб-сокеты) существуют в двух или более состояниях: состоянии разработки и одном или нескольких этапах развертывания. Развертывания API Gateway могут выполняться как инкрементные (канареечные) развертывания, хотя здесь мы этого не делаем.
  3. Лямбда-псевдонимы. Строго говоря, лямбда-псевдонимы не имеют никакой семантики развертывания, но обычно они используются для выполнения контролируемого развертывания или отката.
  4. CodeBuild / CodePipeline. Эти инструменты управляют сборкой и выпуском развертывания, обычно (хотя и не обязательно) путем интеграции с CloudFormation для фактического создания ресурсов. При желании их можно обернуть в CodeStar, как в нашем проекте.
  5. CodeDeploy. CodeDeploy широко используется для развертывания инфраструктуры, но в нашем случае он используется для управляемого развертывания функции Lambda после завершения CloudFormation. Он полагается на (3) выше для обработки низкоуровневого формирования трафика.

Итак, в нашем случае у нас есть командный проект CodeStar (4), состоящий из CodeBuild (4), CloudFormation (1) и CodeDeploy (5), работающий внутри CodePipeline (4), который включает развертывание Lambda Canary (5) с использованием трафика формирование (3) и развертывание на этапе производства (2) веб-сокета API Gateway, развернутого CloudFormation (1). Уф.

Развертывание веб-сокета
Вернемся к нашей регулярной программе: развертывание веб-сокета несложно, за исключением того, что оно включает в себя набор DependsOn операторов.

Теперь я собираюсь сказать, что это кажется мне неправильным - ценность декларативной спецификации должна заключаться в том, что она дает правильные вещи без необходимости кодирования императивных понятий, таких как то, что от чего зависит.

Практически во всех случаях CF понимает это правильно - типы объектов службы в CF «знают» свою семантику развертывания, а вхождения Ref и GetAttr предоставляют необходимую дополнительную информацию о зависимостях для CF, чтобы создать допустимый общий порядок в последовательности развертывания.

Но здесь не применяется ни один из обычных механизмов, и информация о порядке строительства может быть потеряна, что приведет к возникновению состояния гонки. Надеюсь, в следующем выпуске это будет исправлено, и DependsOns можно будет удалить.

логирование

Вот необходимое предложение в блоге Medium, в котором вас просят включить ведение журнала: Пожалуйста, включите… включите… ведение журнала.

Синтаксис для ведения журнала доступа сложен, но вы можете увидеть здесь рабочий пример, и он того стоит, особенно для веб-сокетов. Когда запрос не проходит, действительно полезно иметь возможность определить причину сбоя - авторизация, проверка модели, интеграция и т. Д.

Обратной стороной того, что вы так много сделали для вас, является то, что вам действительно нужна дополнительная прозрачность, когда все идет к черту. В отличие от Lambda, вы не получите журнал для своего веб-узла, если не попросите об этом.

Вам также необходимо убедиться, что вы настроили свою учетную запись, чтобы включить API-шлюз с ролью ведения журнала - это нужно делать только один раз для каждой учетной записи, а не в каждом шаблоне развертывания приложения, но без нее вы ничего не получите, когда пойдете. в CloudWatch.

Еще один незначительный момент: такие элементы, как формат регистрации доступа здесь, действительно выигрывают от возможности разбить буквальную строку в вашем шаблоне на несколько строк. Вы можете либо скопировать это дословно из известного рабочего примера, либо освоиться с мельчайшими подробностями лексических правил YAML.

Обработка пользовательского домена

Если вас устраивает прозвище, предоставленное API-шлюзом для вашего веб-узла (что, вероятно, нормально, если это внутренняя служба), пропустите этот раздел. Но если вы ожидаете, что клиенты или партнеры будут иметь дело с этим, вам, вероятно, понадобится какой-то тщеславный URL, который немного легче запомнить, чем случайно сгенерированный GUID.

Пользовательские домены объединяют шесть частей информации: домен и субдомен, API (в нашем случае - веб-сокет), путь, сертификат и этап развертывания.

Обратите внимание, что домен (и необязательный субдомен) отображается как буквальная строка, когда вы создаете AWS::ApiGatewayV2::DomainName, но AWS::ApiGatewayV2::ApiMapping относится к объекту ... даже если оба называют это свойство «DomainName».

Сертификация доменного имени и тестирование DNS - это само по себе удовольствие; достаточно сказать, что вы хотите, чтобы это полностью работало и отлажено, прежде чем пытаться использовать собственный домен в шаблоне CF.

И последнее, но не менее важное: вам нужны двунаправленные разрешения между функцией Lambda, служащей интеграцией, и веб-сокетом, пытающимся интегрироваться с ней.

У этого есть две стороны:

Сторона №1: API-шлюз вызывает Lambda
Это роль (без слов) объекта theAWS::Lambda::Permission; он добавляет политику ресурсов, которая позволяет веб-сокету NATPunch вызывать функцию Lambda, служащую целью интеграции.

Чтобы он не был более подробным, мы используем «*» для имени маршрута. Это сильно отличается от использования «*» для всего этого - мы хотим, чтобы вызовы были ограничены этим конкретным API, но мы знаем, что все маршруты (текущие и будущие) будут идти к этой лямбда-функции.

Между прочим, если вы хотите перечислить эти ARN явно, а не то, что вы не можете их получить (ребята из AWS, обратите внимание на эту ошибку!), Вам придется создавать их вручную, и есть настоящая неизбежная звездочка даже в полный ARN. Кажется сомнительной идея для чего-то, что должно завершиться в заявлении ASPEN, но это так.

Сторона №2: лямбда-вызов API-шлюза
Синхронные ответы веб-сокетов не требуют дополнительных разрешений; Ответ функции Lambda автоматически преобразуется в сообщение веб-сокета API-шлюзом, который авторизован (1).

Но асинхронное сообщение, отправленное обратно клиенту требует, чтобы лямбда-функция использовала для этой цели конечную точку обратного вызова, предоставляемую веб-сокетом. Помните !Sub ‘arn:${AWS::Partition}:iam::aws:policy/AmazonAPIGatewayInvokeFullAccess’ в роли лямбда-функции? Вот где происходит это волшебство.

Выходные данные CloudFormation

Раздел выходов очень полезен по двум причинам: диагностика и последующие действия. Они могут вам понадобиться, если на последующих этапах CodePipeline необходимо знать имена созданных вещей ... особенно имена, которые искажаются CF, которые иначе вы не сможете записать в сценарии сборки, так как вы не сможете предсказать их заранее.

Совет на прощание

И это подводит нас почти к концу нашей истории.

Два напутственных совета:

  1. Во-первых, используйте имя объекта CloudFormation для поиска документации (например, «AWS :: ApiGatewayV2 :: Stage», а не «API Gateway stage»), чтобы избежать поиска случайных страниц несвязанных документов.
  2. Во-вторых, может быть проще создать работающее приложение (или, по крайней мере, его части) вне CF, а затем «транскрибировать» это приложение в CF. Это позволяет использовать пользовательский интерфейс консоли AWS, интерфейс командной строки AWS или любой из нескольких пакетов SDK AWS, чтобы увидеть, как AWS думает, что ваше приложение настроено. Часто одного или нескольких из них будет достаточно, чтобы помочь вам правильно угадать, как настроить свойства CF для воссоздания того же поведения.

Если вы добрались до конца, поздравляю и удачного создания CloudFormation!