Как многократно развертывать инфраструктуру с помощью CloudFormation
Я никогда больше не буду развертывать инфраструктуру в AWS без использования CloudFormation (инфраструктура как код) для этого. Раньше я рылся в графическом интерфейсе пользователя, чтобы настроить инфраструктуру или сервисы в AWS, и нелегко запомнить каждый шаг от одного проекта к другому. Теперь я просматриваю документацию и использую простой текстовый редактор для реализации своих приложений.
В этом блоге я объясню, почему я так сильно отношусь к инфраструктуре как к коду (IaC). Затем я опишу, как с помощью CloudFormation создать простое бессерверное приложение, показанное ниже. Это приложение состоит из лямбда-функции, которая имеет разрешения на чтение и запись в таблицу DynamoDB и запись в поток CloudWatch Logs.
Почему CloudFormation?
Несомненно, я мог бы развернуть одну таблицу DynamoDB через графический интерфейс быстрее, чем через CloudFormation. Но что произойдет, если я поставлю стол не в то место и мне придется его переместить? Что происходит, когда мне нужно развернуть вторую или третью таблицу? Как я могу задокументировать свои действия, чтобы другие могли следить за моей работой или копировать ее?
Я решил впервые использовать CloudFormation в большом проекте. Я создавал бота, который использовал несколько ресурсов AWS. После создания бота мне пришлось развернуть вторую копию бота для взаимодействия с другим API. Развертывание второго бота с CloudFormation заняло около 10 минут - значительно быстрее, чем кто-либо мог бы сделать через веб-консоль, - потому что все было определено в коде. Спустя несколько месяцев другая команда взяла мой код и развернула свою собственную копию моего бота с небольшим опытом работы с AWS - именно так должен работать IaC!
Некоторые из моих любимых преимуществ CloudFormation:
- CloudFormation развертывает ресурсы в правильном порядке. Я могу определить их в файле шаблона в любом порядке, и CloudFormation это поймет. Он знает, что база данных должна быть создана до роли IAM, которая разрешает доступ к базе данных, и что роль IAM должна быть создана до ресурса, который ее использует. Иногда он может правильно не понимать все зависимости, но я никогда этого не видел.
- CloudFormation отлично уничтожает созданные им ресурсы. Удаление всех компонентов устаревшего приложения не только утомительно, но и чревато ошибками. Очень легко забыть роль или политику IAM или журнал CloudWatch. Со временем эти потерянные ресурсы вызывают беспорядок в моем аккаунте. Хуже того, некоторые потерянные ресурсы могут стоить денег (например, оставить запущенным ненужный инстанс EC2).
- CloudFormation предоставляется бесплатно. Я должен платить за ресурсы, которые он развертывает, но за сам CloudFormation плата не взимается. Учитывая, что он отслеживает мой файл шаблона и запоминает каждый созданный им ресурс, бесплатно - это отличная цена.
- Файл шаблона CloudFormation служит для моей команды документацией о том, что требуется для развертывания каждого приложения. Если я пишу свои шаблоны в YAML, я даже могу добавлять комментарии к файлу.
- Я могу добавить свой шаблон CloudFormation в репозиторий моего проекта на GitHub (или другой механизм управления версиями), чтобы включить отслеживание изменений и историю версий.
Начало работы с CloudFormation
Шаблоны CloudFormation можно писать в YAML или JSON. Я использую YAML, так как он более компактный, чем JSON, и позволяет оставлять комментарии. Я не хочу углубляться в эту статью, не углубляясь сразу в развертывание чего-либо. Ниже приведен полный шаблон CloudFormation, который просто развертывает одну таблицу DynamoDB.
Если приведенный выше код сохранен как файл mystack.yml
, я могу использовать AWS CLI для развертывания этого очень простого стека с помощью этой команды:
aws cloudformation create-stack --stack-name medium --template-body file://mystack.yml
Также могу сразу проверить статус создания:
aws cloudformation describe-stacks --stack-name medium
Если я наберу указанную выше команду достаточно быстро, я получу ответ, в котором указано, что статус - CREATE_IN_PROGRESS, но если я подожду 60 секунд, я увижу вместо этого CREATE_COMPLETE.
{ "StackId": "arn:aws:cloudformation:...:stack/medium/...", "StackName": "medium", "Description": "demo CloudFormation for medium.com", "CreationTime": "2021-07-21T02:48:57.657000+00:00", "RollbackConfiguration": {}, "StackStatus": "CREATE_IN_PROGRESS", "DisableRollback": false, "NotificationARNs": [], "Tags": [], "EnableTerminationProtection": false, "DriftInformation": { "StackDriftStatus": "NOT_CHECKED" } }
Точно так же я могу сделать это через веб-интерфейс, перейдя в CloudFormation, выбрав «Создать стек с новыми ресурсами (стандарт)», а затем указав его на этот файл YAML.
Этот стек довольно прост (без входов и без ролей IAM), поэтому, назвав его, я могу просто нажимать «Далее» на каждом этапе пользовательского интерфейса.
На развертывание этого очень простого стека уходит меньше минуты. На данный момент у меня есть одна таблица базы данных с первичным ключом product_id
, которой CloudFormation присвоила имя. Позже в этой статье я объясню, как CloudFormation дает имена неназванным ресурсам и почему я решил не называть эту таблицу специально.
Далее я расскажу об анатомии самого шаблона CloudFormation, а также об используемых нами параметрах.
Анатомия шаблона CloudFormation
Файл шаблона состоит из нескольких основных компонентов, большинство из которых являются необязательными. Вот компоненты, которые будут использоваться в этой статье:
- Версия формата (необязательно). Хотя формат файла CloudFormation остался неизменным на
2010-09-09
, рекомендуется указать его на тот случай, если формат изменится в будущем. Я хочу быть уверен, что AWS правильно интерпретирует мой файл в будущем. - Описание (необязательно). Рекомендуется включать описание стека, чтобы моим коллегам не приходилось гадать о назначении стека, основываясь исключительно на присвоенном ему имени.
- Ресурсы (обязательные). В этот раздел входит определение каждого объекта или службы, которые будут реализованы. Пример: таблица DynamoDB, экземпляр EC2, лямбда-функция и даже лямбда-функция псевдоним. Все они называются ресурсами в шаблоне CloudFormation. На данный момент я определил только один ресурс: таблицу DynamoDB.
Параметры DynamoDB
Вот снова шаблон CloudFormation (идентичный тому, который был опубликован выше):
Полная документация для ресурса DynamoDB находится здесь, но есть несколько элементов, которые следует выделить в шаблоне выше.
- Текст
MyProductTable
- это понятное имя ресурса, имеющее отношение только к CloudFormation. Его можно использовать в шаблоне для ссылки на эту таблицу, и на самом деле я буду использовать его позже для назначения разрешений IAM. DeletionPolicy
определяет, удаляется ли таблица при удалении стека. В производственной среде я устанавливаюRetain
, чтобы мои данные сохранялись, даже если я случайно удалю стек. Во время разработки я люблю комментировать это или устанавливать значениеDelete
.TableName
не является обязательным. Если он не указан, CloudFormation присвоит таблице имя в форме[StackName]-[ResourceName]-[random characters]
. Я мог бы жестко привязать его к любой строке, но это вызовет ошибку, если я когда-нибудь решу развернуть второй стек на основе того же шаблона, поскольку AWS не позволяет двум таблицам иметь одно и то же имя. Я мог бы использовать специальный синтаксис CloudFormation, например!Sub "${AWS::StackName}-products"
, чтобы назвать таблицу, но я опущу параметр в этом примере и позволю CloudFormation выбрать имя за меня.
Погружение в более сложное
Вот весь шаблон CloudFormation для приложения, которое я обещал в начале этой статьи. Это расширенный набор первого файла шаблона, который включал только таблицу DynamoDB (строки 1–16 идентичны).
В следующих разделах этой статьи я расскажу о каждом ресурсе в файле YAML. Обновление текущего стека (который содержит только таблицу DynamoDB) моим недавно созданным файлом шаблона (который содержит четыре ресурса) может занять несколько минут, поэтому сначала я сделаю это:
aws cloudformation update-stack --stack-name medium --template-body file://step2.yml --capabilities CAPABILITY_NAMED_IAM
Независимо от того, выполняю ли я обновление через интерфейс командной строки, как показано выше, или через веб-консоль, я должен подтвердить AWS, что я понимаю, что этот шаблон создаст новую роль IAM. Я делаю это с помощью флага CAPABILITY_NAMED_IAM
в интерфейсе командной строки или с помощью флажка на последнем экране операции обновления в веб-консоли.
Роль IAM
Если приложение собирается читать и записывать базу данных, ему потребуется роль IAM. Роль состоит из одной или нескольких политик. Я создам роль IAM со следующими политиками:
- настраиваемая политика, которая разрешает
GetItem
,Scan
иUpdateItem
разрешения для таблицы DynamoDB - настраиваемая политика, которая разрешает
CreateLogStream
иPutLogEvents
в мою учетную запись, что позволяет создавать и регистрировать события в журналах CloudWatch.
Полная документация ресурса IAM Role находится здесь, но следует выделить несколько моментов.
RoleName
- необязательный параметр, и, как и DynamoDBTableName
, я опущу его и позволю CloudFormation называть этот ресурс. CloudFormation назовет роль IAM в формате[StackName]-[ResourceName]-[random characters]
.AssumeRolePolicyDocument
позволяет использовать эту роль IAM лямбда-функцией (о которой я еще не упоминал).PolicyName
для каждой политики требуется, но не обязательно должен быть уникальным в глобальном масштабе, поскольку это просто встроенная политика.
Обратите внимание, что определение роли IAM относится к таблице DynamoDB как !GetAtt MyProductTable.Arn
. Язык шаблонов CloudFormation позволяет мне указывать ARN для ресурса перед его созданием!
Мне не нужно знать имя таблицы, чтобы создать относящуюся к ней роль IAM. CloudFormation понимает это за меня!
Лямбда-функция
Приложению нужна лямбда-функция. Мне нужно указать код приложения, который будет выполнять эта функция, а также любые переменные среды, которые необходимы функции. Переменные среды - отличный способ передать имена других ресурсов (которые генерируются динамически) в лямбда-функцию. Например, когда я пишу свой лямбда-код, я не знаю имя таблицы DynamoDB, к которой буду обращаться, потому что она еще не создана!
Полная документация к ресурсу Лямбда-функция находится здесь, но следует выделить несколько моментов.
- Свойство
FunctionName
является необязательным, как и предыдущие ресурсы в этом шаблоне. - Я могу указать код, который Lambda выполняет в файле шаблона (как показано выше), но на самом деле он не масштабируется. Лучший способ развернуть Lambda-функции в CloudFormation - указать
S3Bucket
иS3Key
, содержащие пакет развертывания (zip-файл) для Lambda. - К лямбда-выражению можно добавить несколько
Environment
переменных. В примере показано только одно: имя таблицы DynamoDB. Код Lambda в примере показывает, как получить доступ к этой переменной среды, позволяя писать код, который обращается к ресурсу, еще до того, как ресурс будет создан.
Код лямбда-функции можно указать прямо в файле шаблона.
CloudWatch LogGroup
Создавать группу журналов не требуется. Я добавляю его в свои шаблоны CloudFormation по двум причинам:
- Я могу указать свойство
RetentionInDays
на другое значение, кроме неопределенного. - Когда я удаляю стек, удаляются и журналы. Я не люблю журналы, которые больше не нужно загромождать мой аккаунт.
Полная документация по ресурсу CloudWatch LogGroup находится здесь, но включение ресурса в мой файл шаблона совершенно необязательно.
Запустите лямбда-функцию
На вкладке «Ресурсы» стека CloudFormation показаны все ресурсы, созданные шаблоном CloudFormation. Я могу щелкнуть лямбда-функцию, чтобы перейти к ней и выполнить ее вручную.
Код Python, который я указал в файле шаблона CloudFormation, уже заполнен лямбда-функцией.
Тестируя лямбда-функцию, я вижу, что в выходных данных отображается имя таблицы DynamoDB, как и ожидалось.
Удалить стек
Проект завершен, и теперь пора удалить весь стек. Это довольно просто. На самом деле это очень просто.
aws cloudformation delete-stack --stack-name medium
Что произойдет, если я удалю неправильный стек?
CloudFormation запоминает детали даже удаленных стопок. Я могу просто перейти к стекам CloudFormation, выбрать «Удалено» и по-прежнему иметь возможность отозвать весь свой файл шаблона. Если бы я выбрал Retain
для DynamoDB DeletionPolicy
, тогда у меня все еще были бы мои данные, а CloudFormation сохранила мою конфигурацию. Неплохо для бесплатного сервиса!
Что теперь?
В этой статье я показал вам, как развернуть очень простой стек, обновить его и удалить.
Лучший способ начать работу с CloudFormation - создать очень простой файл шаблона (как я сделал в начале этой статьи) и развернуть его. Для меня проще всего было внести дополнительные изменения и выполнить update
операцию для проверки моего синтаксиса. Наконец, я часто обращался к документации, всегда проверяя наличие необходимых свойств для каждого ресурса.
Надеюсь, вы согласитесь, что развертывание инфраструктуры с помощью CloudFormation дает огромные преимущества.
Больше контента на plainenglish.io