Имеется стек, в котором среди прочего создаются VPC Peerings, в данном примере — пиринг между VPC нового кластера Elastic Kubernetes Service и VPC стека с Prometheus.
Создание стеков и вообще всей этой автоматизации есть в постах AWS Elastic Kubernetes Service: автоматизация создания кластера, часть 1 — CloudFormation и AWS Elastic Kubernetes Service: — автоматизация создания кластера, часть 2 — Ansible, eksctl.
Задача: добавить возможность выбора — создавать пиринги, или нет.
Решение: используем AWS CloudFormation Conditions: создадим параметр VPCPeering, который из параметров Jenkins-джобы будет принимать значение true или false, а затем в зависимости от этого значения — будем принимать решение о создании ресурсов пиринга (сам пиринг и два роута).
Пример немного усложняется тем, что у нас используются Nested Stacks, и ресурсы разбросаны между ними, например, для создания пиринга:
- root-stack:
- создаёт Region и AZ-стеки
- Region-stack:
- создаёт ресурс
AWS::EC2::VPCPeeringConnection - обновляет RouteTable в стеке Мониторинга — добавляет ресурс
AWS::EC2::Route(маршрут из сети монитоирнга в сеть EKS) - в
Outputsвозвращает MonitoringProdVPCPeeringConnectionID
- создаёт ресурс
- AZ-стек:
- принимает MonitoringProdVPCPeeringConnectionID
- создаёт ресурс
AWS::EC2::Route(маршрут из приватных подсетей EKS в сеть мониторинга)
Доабвляем по очереди, и смотрим как всё это можно организовать.
Содержание
Root stack
В Parameters добавляем новый параметр, назовём его VPCPeeringCreate, который принимает true или false:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "AWS CloudFormation stack for Kubernetes cluster",
"Parameters": {
...
"VPCPeeringCreate": {
"Description": "Create or not VPC peering connections",
"Type": "String",
"Default": true,
"AllowedValues": [
"true",
"false"
]
}
Обновляем ресурс region-стека — добавляем передачу параметра VPCPeeringCreate:
"Resources": {
"RegionNetworkStack": {
"Type": "AWS::CloudFormation::Stack",
"Properties": {
"TemplateURL": "eks-region-networking.json",
"Parameters": {
"VPCCIDRBlock": { "Ref": "VPCCIDRBlock" },
"VPCPeeringCreate": { "Ref": "VPCPeeringCreate"}
}
}
},
...
Region Stack
Принимаем параметр VPCPeeringCreate — тут уже убираем дефолтное значение, так как оно должно придти из рутового стека:
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "AWS CloudFormation Region Networking stack for Kubernetes cluster",
"Parameters" : {
...
"VPCPeeringCreate": {
"Description": "Create or not VPC peering connections",
"Type": "String",
"AllowedValues": [
"true",
"false"
]
}
},
...
Создаём Conditions, в котором с помощью Fn::Equals проверяем значение из параметра VPCPeeringCreate, и если оно true — то возвращаем тоже true:
...
"VPCPeeringCreate": {
"Description": "Create or not VPC peering connections",
"Type": "String",
"AllowedValues": [
"true",
"false"
]
}
},
"Conditions" : {
"DoVPCPeeringCreate" : {"Fn::Equals" : [ {"Ref" : "VPCPeeringCreate"}, true] }
},
...
Добавляем проверку обоим ресурсам MonitoringProdVPCPeeringConnection и MonitoringToEksProdPeeringRoute — "Condition" : "DoVPCPeeringCreate":
...
"MonitoringProdVPCPeeringConnection": {
"Type": "AWS::EC2::VPCPeeringConnection",
"Condition" : "DoVPCPeeringCreate",
"Properties": {
"VpcId": {
"Ref": "VPC"
},
"PeerVpcId": { "Fn::ImportValue" : "monitoring-production-VPC-ID" },
"PeerRegion": { "Fn::ImportValue" : "monitoring-production-StackRegion" },
"Tags": [
{
"Key": "Name",
"Value": { "Fn::Join": [ "-", [ {"Ref": "AWS::StackName"}, "vpc-monitoring-prod"] ] }
}
]
}
},
"MonitoringToEksProdPeeringRoute": {
"Type": "AWS::EC2::Route",
"Condition" : "DoVPCPeeringCreate",
"Properties": {
"RouteTableId": { "Fn::ImportValue" : "monitoring-production-VPC-PublicRouteTable" },
"DestinationCidrBlock": { "Ref": "VPCCIDRBlock" },
"VpcPeeringConnectionId": {
"Ref": "MonitoringProdVPCPeeringConnection"
}
}
},
...
Запускаем — всё создалось:
Unresolved resource dependencies и Fn::If
А теперь запускаем с VPCPeeringCreate == false, и получаем ошибку отсутствия данных для Outputs нашего регион-стека:
Логично — мы отключили создание пиринга, а в Outpus шаблона регион-стека по-прежнему пытаемся получить ID не созданного пиринга:
...
"MonitoringProdVPCPeeringConnectionID": {
"Description" : "MonitoringProdVPCPeeringConnection ID",
"Value" : {"Ref" : "MonitoringProdVPCPeeringConnection" }
}
...
Обновляем Outputs, и используем Fn::If для выбора того — какие данные возвращать:
...
"Outputs" : {
"VPCID" : {
"Description" : "EKS VPC ID",
"Value" : { "Ref" : "VPC" }
},
"IGWID" : {
"Description" : "InternetGateway ID",
"Value" : { "Ref" : "InternetGateway" }
},
"MonitoringProdVPCPeeringConnectionID": {
"Description" : "MonitoringProdVPCPeeringConnection ID",
"Value" : { "Fn::If" : [ "DoVPCPeeringCreate", {"Ref" : "MonitoringProdVPCPeeringConnection" }, "Zero" ] }
}
}
}
...
Т.е. если DoVPCPeeringCreate вернёт значение false — просто передаём что угодно, тут для примера слово «Zero».
AvailabilityZones stack
В AZ-стеке надо сделать тоже самое — добавит параметр, добавить Conditions — копируем из region-стека, и для ресурса AWS::EC2::Route добавляем проверку условия "Condition" : "DoVPCPeeringCreate":
...
"EksToMonitoringProdPeeringRoute": {
"Type": "AWS::EC2::Route",
"Condition" : "DoVPCPeeringCreate",
"Properties": {
"RouteTableId": {
"Ref": "PrivateRouteTable"
},
"DestinationCidrBlock": {
"Fn::ImportValue" : "monitoring-production-VPC-CIDR"
},
"VpcPeeringConnectionId": {
"Ref": "MonitoringProdVPCPeeringConnectionID"
}
}
},
...
В root-стеке добавляем передчу параметра VPCPeeringCreate в AZ-стек:
...
"AZNetworkStackA": {
"Type": "AWS::CloudFormation::Stack",
"Properties": {
"TemplateURL": "eks-azs-networking.json",
"Parameters": {
"VPCID": { "Fn::GetAtt": ["RegionNetworkStack", "Outputs.VPCID"] },
"AZ": { "Fn::Select": [ "0", { "Ref": "AvailabilityZones" } ] },
"IGWID": { "Fn::GetAtt": ["RegionNetworkStack", "Outputs.IGWID"] },
"VPCPeeringCreate": { "Ref": "VPCPeeringCreate"},
...
Создаём стеки — и нашего пиринга нет:
Для проверки — ещё раз запускаем с VPCPeeringCreate == true:
Готово.



