Задача — создать два 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
Готово.





