Источник: Nuances of Programming
Цель этой статьи предоставить вам наглядные примеры и инструкции по разработке динамического модуля ECS (Elastic Container Service ) с помощью Terraform. При этом предполагается наличие у вас базового представления о данном инструменте.
“Динамический” в данном случае означает, что Terraform может легко масштабироваться для обработки большего числа сервисов и задач.
0. Сеть
Определение самого кластера ECS не содержит сетевых требований.
Здесь нужно многое объяснить, и я начну с конфигурации сети.
network_configuration {
subnets = var.ecs_service_subnets
security_groups = [aws_security_group.ecs_security_groups[each.value["security_group_mapping" ]].id]
}
В этом инстансе (закрытые) подсети наследуются от модуля, определяющего наш VPC. При создании динамического кластера мы просто ссылаемся на список подсетей в модуле VPC, чтобы создать сервисы в собственном предпочтительном VPC.
Группы безопасности задействуют функцию for_each в Terraform, лежащую в основе многих механик этого модуля.
for_each
Данная функциональность позволяет создавать несколько ресурсов, использующих общие аргументы. Модулю требуется только, чтобы for_each был определен внутри ресурса, и переменная карты передавалась в указанный аргумент.
for_each = var.create_microservices == true ? var.fargate_microservices : {}
В этом случае мы указали, что для создания сервисов create_microservice должен быть true. Далее переменная fargate_microservices выступает в качестве карты, содержащей все необходимые сервису аргументы (пример приводится в разделе “Динамические сервисы”).
Группы безопасности (security groups)
Теперь давайте рассмотрим код модуля, позволяющий определять необходимое число групп безопасности:
Этот блок ресурса будет перебирать определенный вне модуля объект var.security_groups и выбирать переменную для каждой переменной с приставкой each.value .
Вот как определяется одна группа безопасности внутри модуля:
Затем эти инструкции отображаются в соответствующие им сервисы через переменную security_group_mapping внутри каждого сервиса. Эта переменная сопоставляет id группы безопасности (pro-ecs-sg ) с указанным сервисом. Таким образом можно легко добавить еще одну группу безопасности, просто присоединив этот объект карты.
Теперь мы еще раз вернемся к определению сервиса, чтобы посмотреть, как переменная security_group_mapping используется совместно с другими динамическими переменными.
1. Динамические сервисы
Разобравшись с настройками сети, давайте еще раз взглянем на определение сервиса :
Обратили внимание на переменную depends_on ? Этот список переменных обеспечивает создание задач и кластера до создания сервиса. В отсутствие любого из этих ресурсов собрать сервисы не удастся.
Как уже говорилось, для создания сервисов необходимо, чтобы переменная create_microsrvices была установлена как true . Если так и есть, то далее мы передаем карту переменных для определения наших сервисов:
Это похоже на магию, потому что теперь для создания нового сервиса потребуется лишь добавить в эту карту новый элемент.
Переменные определяются следующим образом:
В launch_type вы определяете, как ваш кластер должен запускать контейнеры: используя AWS Fargate или EC2. Чтобы понять, какой тип запуска лучше подходит под ваши требования, ознакомьтесь с руководством по этой ссылке (англ.)
Так как же мы создаем динамические задачи?
2. Динамические задачи
Вот полное определение динамических задач (англ.):
В самом верху снова задействуется for_each , для чего var.create_tasks должна быть установлена как true , чтобы прочитать объект карты var.ecs_tasks .
Сложнейшая часть этого проекта в создании динамических container_definitions . Эти переменные определяют выполняющие ваши задачи образы.
Аргумент container_defintions получает объект JSON, где определено, какой образ docker запускать, а также дополнительные необходимые переменные среды.
Все это помещается в тот же динамический цикл for_each , что и остальная часть ресурса, а затем совмещается с extra_template_variables в объекте JSON, позволяя динамическое обнаружение переменных среды.
Передаваемый в модуль объект карты для задач очень похож на объект сервиса:
Опять же здесь легко внедрить новую задачу и отобразить ее обратно на соответствующий сервис (имя семейства), просто добавив дополнительный элемент.
Объект карты для задач определяется в переменных схожим с динамическими сервисами образом:
3. Динамические логи
Вы могли заметить, что блок ресурса, определяющий задачи, содержит аргумент log_group .
aws_logs_group = "/aws/fargate/${aws_ecs_cluster.cluster.name} /${each .value["family" ]}/${var.environment} ",
Здесь указывается, куда должны отправляться логи для соответствующей задачи. Чтобы гарантировать правильную настройку этих групп логов, нужно определить динамический ресурс Cloudwatch:
Пока имя этих ресурсов будет соответствовать значению ключа aws_log_group внутри определения задач, мы будем получать логи в Cloudwatch.
4. IAM
Если для вас это полностью новый этап настройки, то в реализуемом нами решении понадобится определить две новых роли IAM. Ими являются task role и execution role динамических задач.
Task role определяет, с какими ресурсами AWS ваша задача может взаимодействовать. Ниже приведены соответствующие блоки данных и ресурса.
Данные (policy):
Ресурс (role):
Мы позволили задаче также вызывать AssumeRole через Security Token Service , чтобы она могла задействовать временные учетные данные для доступа к другим сервисам.
Execution role устанавливает доступ для агента контейнера ECS и демона Docker:
Этой роли мы дали те же разрешения, что и предыдущей, с помощью того же объекта данных (ecs_task_policy ).
Заключение
Рассмотренный модуль позволил моей команде ускоренно развертывать новые задачи и сервисы ECS, не требуя ручного изменения всех настроек через GUI.
Сам модуль активно задействует аргумент for_each внутри Terraform для масштабирования по мере необходимости. Такая структура оказывается очень полезна при реализации широкомасштабных решений Terraform.
Читайте также:
Перевод статьи Liam Hartley : How To Create a Dynamic ECS Cluster With Terraform