В продолжение темы AWS: Elastic Kubernetes Service — автоматизация создания кластера, часть 1 — CloudFormation – теперь надо добавить передачу в стек параметра в виде списка.
Идея в том, что бы в Ansible получать все AvailabilityZones, а потом этот список использовать для eksctl
, который будет создавать WorkerNodes в разных AvailabilityZones, и для CloudFormation – что бы создавать дочерние стеки в нужных AvailabilityZone, используя этот же список.
А с Jenkins Ansible всё будет так:
- из параметров Jenkins-джобы передаём $REGION в виде eu-west-3
- джоба в Jenkins запускает Docker-контейнер с Ansible, и ему передаёт $REGION в виде переменной окружения
- Ansible, используя переменную {{ region }} получает список всех AvailabilityZones этого региона
- и передаёт список в роль cloudformation для создания двух стеков в двух AZ
- и в роль eksctl для создания NodeGroups в двух AZ
Сейчас в CloudFormation AvailabilityZones получаются прямо в шаблоне через вызов Fn::GetAZs - { "Fn::Select": [ "0", { "Fn::GetAZs": "" } ] }
:
... "AZNetworkStackA": { "Type": "AWS::CloudFormation::Stack", "Properties": { "TemplateURL": "eks-azs-networking.json", "Parameters": { "VPCID": { "Fn::GetAtt": ["RegionNetworkStack", "Outputs.VPCID"] }, "AZ": { "Fn::Select": [ "0", { "Fn::GetAZs": "" } ] }, ...
Итак, вместо выборки { "Fn::Select": [ "0", { "Fn::GetAZs": "" } ] }
– в Ansible добавим задачу, которая будет получать список AvailabilityZone региона, в котором разворачивается стек, а потом обе роли – eksctl и cloudformation – будут использовать единый список из единого источника.
Пример со списками в CloudForamtion был когда-то нагуглен тут – How do I use multiple values for individual parameters in an AWS CloudFormation template, теперь появился повод его попробовать в деле.
Содержание
Ansible
Больше всего сложности было с формированием списка в таком виде, что бы его скушал CloudFromation, поэтому – начнём с него.
Причём, так как этот список будет использоваться в двух разных ролях – имеет смысл вынести получение списка AvailabilityZones в отдельную задачу, и её вызывать из обеих ролей, что бы не дублировать код и задачи.
Создаём роль common:
В roles/common/tasks/main.yml
создаём таску на получение AvailabilityZones и создание переменной cluster_azs
, в которую сохраняем результат:
- name: "Getting AvailabilityZones list" command: "aws ec2 describe-availability-zones --region {{ region }} --query 'AvailabilityZones[*].ZoneName' --output text" register: cluster_azs
cluster_azs
вернём нам структуру типа такого:
Теперь из строки stdout
со значением “eu-west-2a\teu-west-2b\teu-west-2c” надо сформировать список, разделённый запятой.
Элементы у нас разделены табуляцией – используем split()
, что бы разделить их, а затем через pipe вызовем join()
, который сформирует новый список – но с разделителем в виде запятой:
... - set_fact: cluster_azs_names: "{{ cluster_azs.stdout.split('\t') | join(',') }}"
И в конце для дебага выведем получившуюся строку:
... - debug: msg: "cluster_azs_names: {{ cluster_azs_names }}"
Проверяем:
Отлично.
CloudFormation Parameter
List<AWS::EC2::AvailabilityZone::Name
Добавляем новый параметр AvailabilityZones, ему указываем тип “List<AWS::EC2::AvailabilityZone::Name>
“, см. полный список тут – AWS-Specific Parameter Types:
{ "AWSTemplateFormatVersion": "2010-09-09", "Description": "AWS CloudFormation stack for Kubernetes cluster", "Parameters": { "VPCCIDRBlock": { "Description": "VPC CidrBlock", "Type": "String", "Default": "10.0.0.0/16" }, "AvailabilityZones": { "Type": "List<AWS::EC2::AvailabilityZone::Name>", "Description": "The list of the AvailabilityZones in a current Region", "Default": "eu-west-2a, eu-west-2b" } }, ...
Далее – обновляем создаваемые стеки, и вместо { "Fn::Select": [ "0", { "Fn::GetAZs": "" } ] }
выполняем { "Fn::Select": [ "0", { "Ref": "AvailabilityZones" } ] }
– получаем первый элемент из переданного списка:
... "AZNetworkStackA": { "Type": "AWS::CloudFormation::Stack", "Properties": { "TemplateURL": "eks-azs-networking.json", "Parameters": { "VPCID": { "Fn::GetAtt": ["RegionNetworkStack", "Outputs.VPCID"] }, "AZ": { "Fn::Select": [ "0", { "Ref": "AvailabilityZones" } ] }, ...
Упаковываем (см. AWS: CloudFormation — вложенные стеки и Import/Export параметров):
Запускаем:
Проверяем:
Готово.