Напомню — описывается процесс создания инфрастуктуры для миграции RTFM — два EC2 (Bastion и Zeus) в VPC, приватная и публичная сети, NAT-gateway.
Первая часть: AWS: миграция RTFM, часть #1: ручное создание инфраструктуры – VPC, подсети, IGW, NAT GW, маршруты и EC2.
Вторая часть: AWS: миграция RTFM, часть #2: ручное создание инфраструктуры – AIM, S3, RDS и EBS.
В этой части — возьмём CloudFormation шаблон из поста AWS: CloudFormation – создание шаблона для VPC, EC2, NAT и Internet Gateway — пару ресурсов удалим, пару параметров добавим.
Сейчас создадим шаблон для сети, а шаблоны для создания и провижена всяких EC2/RDS etc — потом сделаем вложенными, что бы потом можно будет подключать их в основной по мере готовности/необходимости.
Аналогичный пост — AWS: CloudFormation – создание шаблона для VPC, EC2, NAT и Internet Gateway.
Шаблон, получившийся в результате написания этого поста — доступен тут>>>.
Содержание
Подготовка шаблона
Копируем старый шаблон:
[simterm]
$ mkdir ~/Work/RTFM/rtfm/12615 $ cp ./Work/RTFM/rtfm/11990/vpc_ec2_nat_private_public_subnets.template ~/Work/RTFM/rtfm/12615/rtfm_migrate_p3.template
[/simterm]
Добавим несколько параметров.
Добавим возможность указать AMI-ID (без mapping):
...
"BastionAMIid": {
"Description" : "OpenBSD AMI ID for the StackRegion",
"Type" : "String",
"Default": "ami-170b6064"
},
"ZeusAMIid": {
"Description" : "CoreOS AMI ID for the StackRegion",
"Type" : "String",
"Default": "ami-068bfe75"
},
...
Так же обновим подсети — 12.*/24 вместо 11.*/24:
...
"VPCCidrBlock" : {
...
"Default": "12.0.0.0/16",
...
"PubNetCIDR": {
... "Default": "12.0.1.0/24",
"PrivNetCIDR": {
...
"Default": "12.0.2.0/24",
...
И пробуем со всем этим завестись.
[simterm]
$ cd Work/RTFM/rtfm/12615/
$ aws cloudformation create-stack --stack-name VPConly1 --template-body file://rtfm_migrate_p3.template
{
"StackId": "arn:aws:cloudformation:eu-west-1:264418146286:stack/VPConly1/c995de90-83cf-11e6-bc28-50fae9b818d2"
}
[/simterm]
Структура шаблона
Вспомним стуктуру шаблона:
- Parameters
- StackRegion: регион для создания стека;
- BastionAMIid: AMI ID для OpenBSD на Bastion;
- ZeusAMIid: AMI ID для CoreOS на Zeus;
- VPCCidrBlock: сеть для VPC (/16);
- SSHLocation: сеть, из которой доступен SSH;
- PubNetCIDR: публичная подсеть в VPC (/24);
- PrivNetCIDR: приватная подсеть в VPC (/24);
- InstanceType: тип инстанса (
t2.nano);
- Resources
- VPC:
- тип ресурса:
AWS::EC2::VPC - используемые параметры:
- «CidrBlock» : { «Ref» : «VPCCidrBlock» }
- «Tags»: { «Ref» : «AWS::StackId»}
- тип ресурса:
- MasterSecurityGroup:
- тип ресурса:
AWS::EC2::SecurityGroup - используемые параметры:
- «VpcId» : { «Ref»: «VPC»}
- «SecurityGroupIngress»: { «Ref» : «SSHLocation»}
- тип ресурса:
- NodesSecurityGroup:
- тип ресурса:
AWS::EC2::SecurityGroup - используемые параметры:
- «VpcId» : { «Ref» : «VPC» }
- тип ресурса:
- PubSubnet:
- тип ресурса:
AWS::EC2::Subnet - используемые параметры:
- VpcId»: { «Ref»: «VPC»}
- «CidrBlock»: { «Ref»: «PubNetCIDR»}
- «AvailabilityZone»: { «Fn::Select» : [ «0», { «Fn::GetAZs» : «» } ]}
- тип ресурса:
- PrivSubnet:
- тип ресурса:
AWS::EC2::Subnet - используемые параметры:
- VpcId»: { «Ref»: «VPC»}
- «CidrBlock»: { «Ref»: «PrivNetCIDR»},
- «AvailabilityZone»: { «Fn::Select» : [ «0», { «Fn::GetAZs» : «» } ]}
- тип ресурса:
- PubNetIGW:
- тип ресурса:
AWS::EC2::InternetGateway
- тип ресурса:
- AttachGateway:
- тип ресурса:
AWS::EC2::VPCGatewayAttachment - используемые параметры:
- «VpcId»: { «Ref»: «VPC» }
- «InternetGatewayId»: { «Ref»: «PubNetIGW» }
- тип ресурса:
- NatEIP:
- тип ресурса:
AWS::EC2::NatGateway - используемые параметры:
- «AllocationId» : { «Fn::GetAtt» : [«NatEIP», «AllocationId»]}
- «SubnetId» : { «Ref»: «PubSubnet»}
- тип ресурса:
- NatGW:
- тип ресурса:
AWS::EC2::NatGateway
- тип ресурса:
- PublicRouteTable:
- тип ресурса:
AWS::EC2::RouteTable - используемые параметры:
- «AllocationId» : { «Fn::GetAtt» : [«NatEIP», «AllocationId»]}
- «SubnetId» : { «Ref»: «PubSubnet»}
- тип ресурса:
- PrivateRouteTable:
- тип ресурса:
AWS::EC2::RouteTable - используемые параметры:
- «VpcId»: { «Ref»: «VPC» }
- тип ресурса:
- PublicRoute:
- тип ресурса:
AWS::EC2::Route - используемые параметры:
- «RouteTableId»: { «Ref»: «PublicRouteTable» },
- «GatewayId»: { «Ref»: «PubNetIGW» }
- тип ресурса:
- PrivateRoute:
- тип ресурса:
AWS::EC2::Route - используемые параметры:
- «RouteTableId»: { «Ref»: «PrivateRouteTable» }
- «NatGatewayId»: { «Ref»: «NatGW» }
- тип ресурса:
- PublicRouteAssociate:
- тип ресурса:
AWS::EC2::SubnetRouteTableAssociation - используемые параметры:
- «RouteTableId» : { «Ref»: «PublicRouteTable»}
- «SubnetId» : { «Ref»: «PubSubnet»}
- тип ресурса:
- PrivateRouteAssociate:
- тип ресурса:
AWS::EC2::SubnetRouteTableAssociation - используемые параметры:
- «RouteTableId» : { «Ref»: «PrivateRouteTable»},
- «SubnetId» : { «Ref»: «PrivSubnet»}
- тип ресурса:
- SwarmMaster0EIP:
- тип ресурса:
AWS::EC2::EIP - используемые параметры:
- «Properties» : { «Domain» : «vpc» }
- тип ресурса:
- SwarmMaster0:
- тип ресурса:
AWS::EC2::Instance - используемые параметры:
- «Tags» : [ {«Key» : «Name», «Value» : «SwarmMaster0» } ],
- «InstanceType» : { «Ref» : «InstanceType» }
- «ImageId» : «ami-a7412ad4» (сейчас указан явно, переделаем через параметры ,которые определили выше)
- «AvailabilityZone»: { «Fn::Select» : [ «0», { «Fn::GetAZs» : «» } ]}
- «KeyName»: «my-cluster» (тоже надо вынести в параметры)
- «SubnetId»: {«Ref»: «PubSubnet»}
- «GroupSet»: [ {«Ref»: «MasterSecurityGroup»} ]
- тип ресурса:
- SwarmMaster0AssociateEIP:
- тип ресурса:
AWS::EC2::EIPAssociation - используемые параметры:
- «EIP»: { «Ref»: «SwarmMaster0EIP»},
- «InstanceId»: { «Ref»: «SwarmMaster0»}
- тип ресурса:
- SwarmMaster1EIP: удалить
- SwarmMaster1: удалить
- SwarmMaster1AssociateEIP: удалить
- SwarmNode0:
- тип ресурса:
AWS::EC2::Instance - используемые параметры:
- «Tags» : [ {«Key» : «Name», «Value» : «SwarmNode0» } ]
- «InstanceType» : { «Ref» : «InstanceType» }
- «ImageId» : «ami-a7412ad4» (в параметры)
- «AvailabilityZone»: { «Fn::Select» : [ «0», { «Fn::GetAZs» : «» } ]}
- «KeyName»: «my-cluster» (в параметры)
- «SubnetId»: {«Ref»: «PrivSubnet»}
- «GroupSet»: [ {«Ref»: «NodesSecurityGroup»} ]
- тип ресурса:
- SwarmNode1: удалить
- Consul0: удалить
- VPC:
- Outputs
- «Value» : { «Fn::GetAtt» : [«VPC», «CidrBlock»] }
Обновление шаблона
Удаляем ресурсы: SwarmMaster1EIP, SwarmMaster1, SwarmMaster1AssociateEIP, SwarmNode1, Consul0.
Проверяем:
[simterm]
$ aws cloudformation validate-template --template-body file://rtfm_migrate_p3.template
{
"Description": "AWS CloudFormation Template Docker Swarm cluster in VPC",
"Parameters": [
{
"DefaultValue": "ami-170b6064",
...
[/simterm]
Вроде ОК, удаляем первый стек, создаём новый:
[simterm]
$ aws cloudformation delete-stack --stack-name VPConly1
$ aws cloudformation create-stack --stack-name MigrateP3 --template-body file://rtfm_migrate_p3.template
{
"StackId": "arn:aws:cloudformation:eu-west-1:264418146286:stack/MigrateP3/f1019220-83da-11e6-a465-500c426596d2"
}
[/simterm]
Stack Update
Следующим шагом — переименовываем всё 🙂
Общая идея — всё, что касается SwarmMaster0 — становится Bastion, всё касаемо SwarmNode0 — в Zeus. SwarmMaster1, SwarmNode1 и Consul0 — уже удалены:
- MasterSecurityGroup > BastionSecurityGroup
- NodesSecurityGroup > ZeusSecurityGroup
- SwarmMaster0EIP > BastionEIP
- SwarmMaster0 > BastionEC2
- SwarmMaster0AssociateEIP > BastionAssociateEIP
- SwarmNode0 > ZeusEC2
Используем Update Stack:
[simterm]
$ aws cloudformation update-stack --stack-name MigrateP3 --template-body file://rtfm_migrate_p3.template
{
"StackId": "arn:aws:cloudformation:eu-west-1:264418146286:stack/MigrateP3/f1019220-83da-11e6-a465-500c426596d2"
}
[/simterm]
Проверяем.
Создаются новые ресурсы:
Удаляются старые:
Или так:
[simterm]
$ aws cloudformation describe-stack-events --stack-name MigrateP3 --query 'StackEvents[*].{ResourceName:LogicalResourceId,Type:ResourceType,Status:ResourceStatus}'
[
{
"Status": "UPDATE_COMPLETE",
"ResourceName": "MigrateP3",
"Type": "AWS::CloudFormation::Stack"
},
{
"Status": "DELETE_COMPLETE",
"ResourceName": "MasterSecurityGroup",
"Type": "AWS::EC2::SecurityGroup"
},
{
"Status": "DELETE_COMPLETE",
"ResourceName": "SwarmMaster0EIP",
"Type": "AWS::EC2::EIP"
},
{
"Status": "DELETE_IN_PROGRESS",
"ResourceName": "MasterSecurityGroup",
"Type": "AWS::EC2::SecurityGroup"
},
{
"Status": "DELETE_IN_PROGRESS",
"ResourceName": "SwarmMaster0EIP",
"Type": "AWS::EC2::EIP"
},
{
...
[/simterm]
Готово:
Теперь — добавим параметров.
Обновление параметров
Два новых параметра уже добавили в начале (BastionAMIid, ZeusAMIid).
Создадим новый — для указания ключа:
...
"StackKeyName": {
"Description" : "Existing key pair name to used with stack resources",
"Type" : "String",
"Default": "my-cluster"
},
...
Далее — обновляем ресурсы.
Используем созданные ранее параметры. Из *подготовки* нам требуется изменить:
«ImageId» : «ami-a7412ad4»
«KeyName»: «my-cluster» (в параметры)
Добавляем параметр для ключа:
...
"KeyName": {
"Description" : "Name of an existing EC2 KeyPair to enable SSH access to the instance",
"Type": "AWS::EC2::KeyPair::KeyName",
"ConstraintDescription" : "must be the name of an existing EC2 KeyPair.",
"Default": "my-cluster"
},
...
Редактируем ресурс BastionEC2:
...
"BastionEC2": {
"DependsOn" : "BastionEIP",
"Type" : "AWS::EC2::Instance",
...
"ImageId" : { "Ref" : "BastionAMIid"},
"AvailabilityZone": { "Fn::Select" : [ "0", { "Fn::GetAZs" : "" } ]},
"KeyName": { "Ref" : "KeyName" },
...
},
...
Аналогично — ZeusEC2:
...
"ZeusEC2": {
"Type" : "AWS::EC2::Instance",
"Properties" : {
"Tags" : [ {"Key" : "Name", "Value" : "Zeus" } ],
"InstanceType" : { "Ref" : "InstanceType" },
"ImageId" : { "Ref" : "ZeusAMIid" },
"AvailabilityZone": { "Fn::Select" : [ "0", { "Fn::GetAZs" : "" } ]},
"KeyName": { "Ref" : "KeyName" },
...
Проверяем:
[simterm]
$ aws cloudformation validate-template --template-body file://rtfm_migrate_p3.template
{
"Description": "AWS CloudFormation Template Docker Swarm cluster in VPC",
"Parameters": [
{
"DefaultValue": "ami-170b6064",
"NoEcho": false,
"Description": "OpenBSD AMI ID for the StackRegion",
"ParameterKey": "BastionAMIid"
},
{
...
[/simterm]
Обновляем стек:
[simterm]
$ aws cloudformation update-stack --stack-name MigrateP3 --template-body file://rtfm_migrate_p3.template
[/simterm]
Проверяем доступность инстансов.
Bastion должен быть достпен «снаружи»:
[simterm]
$ ssh [email protected] -i ~/.ssh/my-cluster.pem The authenticity of host '52.18.160.157 (52.18.160.157)' can't be established. ECDSA key fingerprint is 8c:77:df:71:4e:3c:8f:c5:0b:10:fe:39:72:15:b6:1f. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '52.18.160.157' (ECDSA) to the list of known hosts. OpenBSD 6.0-current (GENERIC.MP) #2337: Mon Aug 8 16:15:11 MDT 2016 Welcome to OpenBSD: The proactively secure Unix-like operating system. ... #
[/simterm]
И Zeus:
[simterm]
$ scp -i ~/.ssh/my-cluster.pem /home/setevoy/.ssh/my-cluster.pem [email protected]:/root my-cluster.pem 100% 1675 1.6KB/s 00:00
$ ssh [email protected] -i ~/.ssh/my-cluster.pem Last login: Mon Sep 26 13:40:18 2016 from 194.105.145.45 OpenBSD 6.0-current (GENERIC.MP) #2337: Mon Aug 8 16:15:11 MDT 2016 Welcome to OpenBSD: The proactively secure Unix-like operating system. ... # ssh [email protected] -i /root/my-cluster.pem "uname -a" The authenticity of host '12.0.2.42 (12.0.2.42)' can't be established. ECDSA key fingerprint is SHA256:kovFZFRr/11VrJnVQKqnGiBEdqFzMdykq0IafQDkfF4. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '12.0.2.42' (ECDSA) to the list of known hosts. Linux ip-12-0-2-42.eu-west-1.compute.internal 4.6.3-coreos #2 SMP Fri Aug 5 04:51:16 UTC 2016 x86_64 Intel(R) Xeon(R) CPU E5-2676 v3 @ 2.40GHz GenuineIntel GNU/Linux
[/simterm]
Добавление тегов
Добавляем теги для создаваемых ресурсов:
- Name: имя ресурса;
- stack_name: для добавления всем ресурсам, создаваемым в этом стеке;
- rtfm_env: все ресурсы в рамках подготовки к миграции — будут со значением «migrate«.
Например:
...
"Type" : "AWS::EC2::VPC",
...
"Tags" : [
{ "Key" : "Name", "Value" : "Migrate-VPC"},
{ "Key" : "stack_name", "Value" : { "Ref" : "AWS::StackName"} },
{ "Key" : "rtfm_env", "Value" : "migrate" }
]
...
"Type" : "AWS::EC2::SecurityGroup"
...
"Tags" : [
{ "Key" : "Name", "Value" : "Migrate-BastionSecurityGroup"},
{ "Key" : "stack_name", "Value" : { "Ref" : "AWS::StackName"} },
{ "Key" : "rtfm_env", "Value" : "migrate" }
]
...
Повторяем для всех ресурсов, которые поддерживают теги, в данном шаблоне это будут: AWS::EC2::VPC, AWS::EC2::SecurityGroup, AWS::EC2::Subnet, AWS::EC2::InternetGateway, AWS::EC2::RouteTable, AWS::EC2::Instance.
Обновляем стек:
[simterm]
$ aws cloudformation validate-template --template-body file://rtfm_migrate_p3.template
$ aws cloudformation update-stack --stack-name MigrateP3 --template-body file://rtfm_migrate_p3.template
{
"StackId": "arn:aws:cloudformation:eu-west-1:264418146286:stack/MigrateP3/a1548350-83f7-11e6-8f33-50a68645b236"
}
[/simterm]
Проверяем:
[simterm]
$ aws cloaws ec2 describe-instances --instance-ids i-cea2b90f --query '[Reservations[*].Instances[*].Tags[*]]' --output text Name MigrateZeusEC2 aws:cloudformation:stack-id arn:aws:cloudformation:eu-west-1:264418146286:stack/MigrateP3/a1548350-83f7-11e6-8f33-50a68645b236 aws:cloudformation:logical-id ZeusEC2 aws:cloudformation:stack-name MigrateP3 stack_name MigrateP3 rtfm_env migrate
[/simterm]
Добавление outputs
Добавим немного больше информации в вывод после создания стека.
Текущий блок Outputs:
...
"Outputs" : {
"VPCCIDR" : {
"Value" : { "Fn::GetAtt" : ["VPC", "CidrBlock"] },
"Description" : "VPC CIDR IP addresses block"
}
}
...
Выведем:
- VPC — ID, блок IP
- Bastion —Intance ID, Public DNS, Public IP, Private IP
- Zeus — Intance ID, Private DNS, Private IP
Например, возвращаемые данные для EC2 можно найти тут>>>.
Public DNS для Bastion сейчас будет пустым, т.к. созданная VPC не назначет имена автоматом.
Добавляем:
...
"Outputs" : {
"VPCID" : {
"Value" : { "Ref" : "VPC"},
"Description" : "VPC ID"
},
"VPCCIDR" : {
"Value" : { "Fn::GetAtt" : ["VPC", "CidrBlock"] },
"Description" : "VPC CIDR IP addresses block"
},
"BastionID" : {
"Value" : { "Ref" : "BastionEC2"},
"Description": "Bastion EC2 instance ID"
},
"BastionPublicDNS" : {
"Value" : { "Fn::GetAtt" : ["BastionEC2", "PublicDnsName"] },
"Description": "Bastion public DNS name"
},
"BastionPublicIP" : {
"Value" : { "Fn::GetAtt" : ["BastionEC2", "PublicIp"] },
"Description": "Bastion public DNS IP address"
},
"BastionPrivateIP" : {
"Value" : { "Fn::GetAtt" : ["BastionEC2", "PrivateIp"] },
"Description": "Bastion private DNS IP address"
},
"ZeusID" : {
"Value" : { "Ref" : "ZeusEC2" },
"Description": "Zeus EC2 instance ID"
},
"ZeusPrivateDNS" : {
"Value" : { "Fn::GetAtt" : ["ZeusEC2", "PrivateDnsName"] },
"Description": "Zeus private DNS name "
},
"ZeusPrivateI" : {
"Value" : { "Fn::GetAtt" : ["ZeusEC2", "PrivateIp"] },
"Description": "Zeus private IP address"
}
}
...
Запускаем:
[simterm]
$ aws cloudformation update-stack --stack-name MigrateP3 --template-body file://rtfm_migrate_p3.template
{
"StackId": "arn:aws:cloudformation:eu-west-1:264418146286:stack/MigrateP3/a1548350-83f7-11e6-8f33-50a68645b236"
}
[/simterm]
Проверяем:
[simterm]
$ aws cloudformation describe-stacks --stack-name MigrateP3 --query '[Stacks[*].Outputs[*]]' --output text Bastion private DNS IP address BastionPrivateIP 12.0.1.20 Bastion EC2 instance ID BastionID i-cda2b90c Zeus private IP address ZeusPrivateI 12.0.2.199 VPC ID VPCID vpc-bd53c1d9 VPC CIDR IP addresses block VPCCIDR 12.0.0.0/16 Zeus private DNS name ZeusPrivateDNS ip-12-0-2-199.eu-west-1.compute.internal Bastion public DNS name BastionPublicDNS Zeus EC2 instance ID ZeusID i-cea2b90f Bastion public DNS IP address BastionPublicIP 52.211.232.8
[/simterm]
Готово.
Почитать по теме:
19 Best Practices for Creating Amazon CloudFormation Templates
AWS CloudFormation Best Practices
Using Tags to Organize AWS Resources









