Задача – создать два EC2 инстанса в одной VPC: один Master в public (с внешним IP) подсети, и второй, Slave – в private подсети, доступ к которому будет только с Master-хоста. Master будет “ходить в интернеты” через Internet Gateway, Slave – через NAT Gateway.
Схема примерно такая:
Без особых деталей, просто step-by-step HowTo. Документация – тут>>>.
Фактически – это пост для подготовки инфраструктуры для дальнейшего развёртывания Swarm-кластера, которая описана в посте Docker: Docker Swarm кластер в AWS step-by-step. А “третья часть” – AWS: CloudFormation – создание шаблона для VPC, EC2, NAT и Internet Gateway.
Все действи выполняются через AWS CLI.
Содержание
VPC
Подробнее о VPC в AWS – в посте AWS: VPC – введение, примеры и документации.
Создаём VPC для EC2 инстансов:
$ aws ec2 create-vpc --cidr-block 10.0.0.0/16 { "Vpc": { "VpcId": "vpc-24279540", "InstanceTenancy": "default", "State": "pending", "DhcpOptionsId": "dopt-8645a9e3", "CidrBlock": "10.0.0.0/16", "IsDefault": false } }
Проверяем:
$ aws ec2 describe-vpcs --vpc-ids vpc-24279540 { "Vpcs": [ { "VpcId": "vpc-24279540", "InstanceTenancy": "default", "State": "available", "DhcpOptionsId": "dopt-8645a9e3", "CidrBlock": "10.0.0.0/16", "IsDefault": false } ] }
Security Group
По vpc-id
находим Security Group, которая была создана при добавлении VPC:
$ aws ec2 describe-security-groups --filters Name=vpc-id,Values=vpc-24279540 { "SecurityGroups": [ { "IpPermissionsEgress": [ { "IpProtocol": "-1", "IpRanges": [ { "CidrIp": "0.0.0.0/0" } ], "UserIdGroupPairs": [], "PrefixListIds": [] } ], "Description": "default VPC security group", "IpPermissions": [ { "IpProtocol": "-1", "IpRanges": [], "UserIdGroupPairs": [ { "UserId": "264418146286", "GroupId": "sg-cbbb9eac" } ], "PrefixListIds": [] } ], "GroupName": "default", "VpcId": "vpc-24279540", "OwnerId": "264418146286", "GroupId": "sg-cbbb9eac" } ] }
Используя ID группы sg-cbbb9eac
добавляем два правила – доступ по 80 и 22 портам:
$ aws ec2 authorize-security-group-ingress --group-id sg-cbbb9eac --protocol tcp --port 80 --cidr 0.0.0.0/0 $ aws ec2 authorize-security-group-ingress --group-id sg-cbbb9eac --protocol tcp --port 22 --cidr 194.***.***.0/24
Или просто “всё и всем”:
$ aws ec2 authorize-security-group-ingress --group-id sg-cbbb9eac --protocol all --port all --cidr 0.0.0.0/0
Подсети
Создаём новую подсеть в нашем VPC – это будет наша публичная сеть для Master-хоста:
$ aws ec2 create-subnet --vpc-id vpc-24279540 --cidr-block 10.0.1.0/24 { "Subnet": { "VpcId": "vpc-24279540", "CidrBlock": "10.0.1.0/24", "State": "pending", "AvailabilityZone": "eu-west-1c", "SubnetId": "subnet-f6c35d92", "AvailableIpAddressCount": 251 } }
И вторую подсеть – приватную, для Slave:
$ aws ec2 create-subnet --vpc-id vpc-24279540 --cidr-block 10.0.2.0/24 { "Subnet": { "VpcId": "vpc-24279540", "CidrBlock": "10.0.2.0/24", "State": "pending", "AvailabilityZone": "eu-west-1c", "SubnetId": "subnet-c3c35da7", "AvailableIpAddressCount": 251 } }
Проверяем:
$ aws ec2 describe-subnets --filters Name=vpc-id,Values=vpc-24279540 { "Subnets": [ { "VpcId": "vpc-24279540", "CidrBlock": "10.0.2.0/24", "MapPublicIpOnLaunch": false, "DefaultForAz": false, "State": "available", "AvailabilityZone": "eu-west-1c", "SubnetId": "subnet-c3c35da7", "AvailableIpAddressCount": 251 }, { "VpcId": "vpc-24279540", "CidrBlock": "10.0.1.0/24", "MapPublicIpOnLaunch": false, "DefaultForAz": false, "State": "available", "AvailabilityZone": "eu-west-1c", "SubnetId": "subnet-f6c35d92", "AvailableIpAddressCount": 251 } ] }
Ещё раз про подсети.
Public, для Master:
"CidrBlock": "10.0.1.0/24", ... "SubnetId": "subnet-f6c35d92",
Private, для Slave:
"CidrBlock": "10.0.2.0/24", ... "SubnetId": "subnet-c3c35da7",
VPC Internet Gateway
Создаём Internet Gateway для нашей VPC – через него будет работать Master:
$ aws ec2 create-internet-gateway { "InternetGateway": { "Tags": [], "InternetGatewayId": "igw-6e6d6e0b", "Attachments": [] } }
Подключаем его к VPC:
$ aws ec2 attach-internet-gateway --internet-gateway-id igw-6e6d6e0b --vpc-id vpc-24279540
Проверяем:
$ aws ec2 describe-internet-gateways --filters Name=internet-gateway-id,Values=igw-6e6d6e0b { "InternetGateways": [ { "Tags": [], "InternetGatewayId": "igw-6e6d6e0b", "Attachments": [ { "State": "available", "VpcId": "vpc-24279540" } ] } ] }
NAT Gateway
Для доступа из private подсети в Интернет – добавляем NAT Gateway. Его создаём в открытой подсети (subnet-f6c35d92
), в которой будет и Master-хост.
Получаем новый публичный IP для этого шлюза:
$ aws ec2 allocate-address --domain vpc { "PublicIp": "52.209.79.242", "Domain": "vpc", "AllocationId": "eipalloc-8d96c8e8" }
И создаём шлюз, указав allocation-id
для подключения к шлюзу внешнего IP:
$ aws ec2 create-nat-gateway --subnet-id subnet-f6c35d92 --allocation-id eipalloc-8d96c8e8 { "NatGateway": { "NatGatewayAddresses": [ { "AllocationId": "eipalloc-8d96c8e8" } ], "VpcId": "vpc-24279540", "State": "pending", "NatGatewayId": "nat-02b563f68fb5e6bce", "SubnetId": "subnet-f6c35d92", "CreateTime": "2016-08-11T07:29:47.287Z" } }
Route table
Находим ID таблицы машрутизации нашей VPC:
$ aws ec2 describe-route-tables --filters Name=vpc-id,Values=vpc-24279540 { "RouteTables": [ { "Associations": [ { "RouteTableAssociationId": "rtbassoc-b9be45de", "Main": true, "RouteTableId": "rtb-b90271dd" } ], "RouteTableId": "rtb-b90271dd", "VpcId": "vpc-24279540", "PropagatingVgws": [], "Tags": [], "Routes": [ { "GatewayId": "local", "DestinationCidrBlock": "10.0.0.0/16", "State": "active", "Origin": "CreateRouteTable" } ] } ] }
Добавляем правило маршрутизации для публичной сети (10.0.1.0/24) в сеть 0.0.0.0/0 (весь Интернет) через Intenet Gateway:
$ aws ec2 create-route --route-table-id rtb-b90271dd --destination-cidr-block 0.0.0.0/0 --gateway-id igw-6e6d6e0b { "Return": true }
Для закрытой подсети – создаём новую таблицу маршрутизации:
$ aws ec2 create-route-table --vpc-id vpc-24279540 { "RouteTable": { "Associations": [], "RouteTableId": "rtb-03007367", "VpcId": "vpc-24279540", "PropagatingVgws": [], "Tags": [], "Routes": [ { "GatewayId": "local", "DestinationCidrBlock": "10.0.0.0/16", "State": "active", "Origin": "CreateRouteTable" } ] } }
В ней создаём новое правило для маршрутизации трафика из подсети 10.0.2.0/24 через NAT Gateway, который расположен в публичной посети:
$ aws ec2 create-route --route-table-id rtb-03007367 --nat-gateway-id nat-02b563f68fb5e6bce --destination-cidr-block 0.0.0.0/0 { "Return": true }
Подключаем эту таблицу маршрутизации к закрытой подсети 10.0.2.0/24, в которой будет наш Slave-хост:
$ aws ec2 associate-route-table --route-table-id rtb-03007367 --subnet-id subnet-c3c35da7 { "AssociationId": "rtbassoc-e0b34887" }
Проверяем.
NAT Gateway:
$ aws ec2 describe-route-tables --route-table-ids rtb-03007367 --query '[RouteTables[*].Routes[*].NatGatewayId]' [ [ [ "nat-02b563f68fb5e6bce" ] ] ]
Internet Gateway:
$ aws ec2 describe-route-tables --route-table-ids rtb-b90271dd --query '[RouteTables[*].Routes[*].GatewayId]' [ [ [ "local", "igw-6e6d6e0b" ] ] ]
Про форматирование вывода с помощью --query
– см. тут>>>.
EC2 инстансы
Создаём 2 инстанса – Master и Slave.
Key pairs
Создаём RSA ключи доступа:
$ aws ec2 create-key-pair --key-name my-cluster --query 'KeyMaterial' --output text > my-cluster.pem
Проверяем:
$ head -n 3 my-cluster.pem -----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAgpBI74z1uF5rN6JVtvcNnZZj5t4Pv931Vc61nda/ItUpCppqB5Z1ZBTevhZr jSXN7bXtBabl+CUJ82N1IYyevdzFMlKVfmylnVa4U8E4vocXiygm3jMyMthHrRkRs025Hs7wGMPw
Устанавливаем права доступа:
$ chmod 400 my-cluster.pem
Создание EC2
Далее – создаём Master EC2 инстанс в публичной подсети.
Нам потребуется указать AMI ID, который будем запускать, имя ключей, которые мы создали, ID Security-группы и ID подсети в VPC.
Находим AMI, например – Ubuntu:
$ aws ec2 describe-images --owners 099720109477 --filters Name=platform,Values=linux,Name=name,Values='*ubuntu-trusty-14.04-amd64-server*' --query 'sort_by(Images, &Name)' --output text | head x86_64 2014-06-27T00:18:25.000Z xen ami-8f6ca7f8 099720109477/ubuntu/images/ebs-io1/ubuntu-trusty-14.04-amd64-server-20140607.1 machine aki-52a34525 ubuntu/images/ebs-io1/ubuntu-trusty-14.04-amd64-server-20140607.1 099720109477 True /dev/sda1 ebs available paravirtual BLOCKDEVICEMAPPINGS /dev/sda1 EBS True False 300 snap-e20ed21b 8 io1 BLOCKDEVICEMAPPINGS /dev/sdb ephemeral0 x86_64 2014-07-25T08:25:51.000Z xen ami-f36cbd84 099720109477/ubuntu/images/ebs-io1/ubuntu-trusty-14.04-amd64-server-20140724 machine aki-52a34525 ubuntu/images/ebs-io1/ubuntu-trusty-14.04-amd64-server-20140724 099720109477 True /dev/sda1 ebs available paravirtual BLOCKDEVICEMAPPINGS /dev/sda1 EBS True False 200 snap-5ed700a6 8 io1 BLOCKDEVICEMAPPINGS /dev/sdb ephemeral0 x86_64 2014-08-14T00:44:47.000Z xen ami-7615c901 099720109477/ubuntu/images/ebs-io1/ubuntu-trusty-14.04-amd64-server-20140813 machine aki-52a34525 ubuntu/images/ebs-io1/ubuntu-trusty-14.04-amd64-server-20140813 099720109477 True /dev/sda1 ebs available paravirtual BLOCKDEVICEMAPPINGS /dev/sda1
Теперь можно создать Master в сети 10.0.1.0/24 (ID subnet-f6c35d92
):
$ aws ec2 run-instances --image-id ami-7615c901 --count 1 --instance-type t1.micro --key-name my-cluster --security-group-ids sg-cbbb9eac --subnet-id subnet-f6c35d92 { "OwnerId": "264418146286" "ReservationId": "r-43ef55fa", "Groups": [], "Instances": [ { "Monitoring": { "State": "disabled" }, "PublicDnsName": "", "KernelId": "aki-52a34525", "State": { "Code": 0, "Name": "pending" }, "EbsOptimized": false, "LaunchTime": "2016-08-11T07:36:54.000Z", "PrivateIpAddress": "10.0.1.103", "ProductCodes": [], "VpcId": "vpc-24279540", "StateTransitionReason": "", "InstanceId": "i-46e228cd", "ImageId": "ami-7615c901", "PrivateDnsName": "ip-10-0-1-103.eu-west-1.compute.internal", "KeyName": "my-cluster", "SecurityGroups": [ { "GroupName": "default", "GroupId": "sg-cbbb9eac" } ], "ClientToken": "", "SubnetId": "subnet-f6c35d92", "InstanceType": "t1.micro", "NetworkInterfaces": [ { "Status": "in-use", "MacAddress": "02:f7:03:6e:6c:45", "SourceDestCheck": true, "VpcId": "vpc-24279540", "Description": "", "NetworkInterfaceId": "eni-c839beb3", "PrivateIpAddresses": [ { "Primary": true, "PrivateIpAddress": "10.0.1.103" } ], "Attachment": { "Status": "attaching", "DeviceIndex": 0, "DeleteOnTermination": true, "AttachmentId": "eni-attach-9c924e40", "AttachTime": "2016-08-11T07:36:54.000Z" }, "Groups": [ { "GroupName": "default", "GroupId": "sg-cbbb9eac" } ], "SubnetId": "subnet-f6c35d92", "OwnerId": "264418146286", "PrivateIpAddress": "10.0.1.103" } ], "SourceDestCheck": true, "Placement": { "Tenancy": "default", "GroupName": "", "AvailabilityZone": "eu-west-1c" }, "Hypervisor": "xen", "BlockDeviceMappings": [], "Architecture": "x86_64", "StateReason": { "Message": "pending", "Code": "pending" }, "RootDeviceName": "/dev/sda1", "VirtualizationType": "paravirtual", "RootDeviceType": "ebs", "AmiLaunchIndex": 0 } ] }
Создаём Slave в сети 10.0.2.0/24 (ID subnet-c3c35da7)
:
$ aws ec2 run-instances --image-id ami-7615c901 --count 1 --instance-type t1.micro --key-name my-cluster --security-group-ids sg-cbbb9eac --subnet-id subnet-c3c35da7 { "OwnerId": "264418146286", "ReservationId": "r-92ef552b", "Groups": [], "Instances": [ { "Monitoring": { "State": "disabled" }, "PublicDnsName": "", "KernelId": "aki-52a34525", "State": { "Code": 0, "Name": "pending" }, "EbsOptimized": false, "LaunchTime": "2016-08-11T07:38:10.000Z", "PrivateIpAddress": "10.0.2.33", "ProductCodes": [], "VpcId": "vpc-24279540", "StateTransitionReason": "", "InstanceId": "i-f9e32972", "ImageId": "ami-7615c901", "PrivateDnsName": "ip-10-0-2-33.eu-west-1.compute.internal", "KeyName": "my-cluster", "SecurityGroups": [ { "GroupName": "default", "GroupId": "sg-cbbb9eac" } ], "ClientToken": "", "SubnetId": "subnet-c3c35da7", "InstanceType": "t1.micro", "NetworkInterfaces": [ { "Status": "in-use", "MacAddress": "02:39:e4:a0:03:0b", "SourceDestCheck": true, "VpcId": "vpc-24279540", "Description": "", "NetworkInterfaceId": "eni-03c74178", "PrivateIpAddresses": [ { "Primary": true, "PrivateIpAddress": "10.0.2.33" } ], "Attachment": { "Status": "attaching", "DeviceIndex": 0, "DeleteOnTermination": true, "AttachmentId": "eni-attach-33914def", "AttachTime": "2016-08-11T07:38:10.000Z" }, "Groups": [ { "GroupName": "default", "GroupId": "sg-cbbb9eac" } ], "SubnetId": "subnet-c3c35da7", "OwnerId": "264418146286", "PrivateIpAddress": "10.0.2.33" } ], "SourceDestCheck": true, "Placement": { "Tenancy": "default", "GroupName": "", "AvailabilityZone": "eu-west-1c" }, "Hypervisor": "xen", "BlockDeviceMappings": [], "Architecture": "x86_64", "StateReason": { "Message": "pending", "Code": "pending" }, "RootDeviceName": "/dev/sda1", "VirtualizationType": "paravirtual", "RootDeviceType": "ebs", "AmiLaunchIndex": 0 } ] }
Public IP
Для доступа к мастеру – нам потребуется Public IP.
Получаем адрес:
$ aws ec2 allocate-address --domain vpc { "PublicIp": "52.210.51.198", "Domain": "vpc", "AllocationId": "eipalloc-e595cb80" }
Подключаем его к созданному master инстансу:
$ aws ec2 associate-address --public-ip 52.210.51.198 --instance-id i-46e228cd { "AssociationId": "eipassoc-60196c06" }
Проверяем доступность мастера:
$ telnet 52.210.51.198 22 Trying 52.210.51.198... Connected to 52.210.51.198. Escape character is '^]'. SSH-2.0-OpenSSH_6.6.1p1 Ubuntu-2ubuntu2 quit Protocol mismatch. Connection closed by foreign host.
Копируем созданный ранее RSA ключ на мастер:
$ scp -i my-cluster.pem my-cluster.pem [email protected]:/home/ubuntu The authenticity of host '52.210.51.198 (52.210.51.198)' can't be established. ECDSA key fingerprint is cf:09:53:70:9b:8d:07:51:3b:16:f5:33:c4:bf:4c:0c. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '52.210.51.198' (ECDSA) to the list of known hosts. my-cluster.pem 100% 1675 1.6KB/s 00:00
Подключаемся к мастеру:
$ ssh [email protected] -i my-cluster.pem ... ubuntu@ip-10-0-1-103:~$
И с него – на slave ("PrivateIpAddress": "10.0.2.33"
):
$ ssh [email protected] -i my-cluster.pem ... ubuntu@ip-10-0-2-33:~$
Проверяем доступ в Интернет со слейва:
$ ping ya.ru PING ya.ru (213.180.193.3) 56(84) bytes of data. 64 bytes from www.yandex.ru (213.180.193.3): icmp_seq=1 ttl=51 time=63.1 ms
Готово.