Напомню – описывается процесс создания инфрастуктуры для миграции 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