В рамках подготовки переезда RTFM на нормальную инфраструктуру (вместо простого EC2 с NGINX/PHP-FPM/MySQL) – описание процесса ручного создания этой ифрастуктуры.
По сути – тут описываются те же шаги, что и в посте AWS: VPC – EC2 в public и private подсетях, NAT и Internet Gateway, плюс интересные примеры применения AWS CLI.
Использоваться будут EC2 с OpenBSD и CoreOS.
Причина выбора – интерес.
FreeBSD я не использовал со времени переезда RTFM на CentOS – осени 2014, когда в версии 9.1 ввели pkgng
.
А CoreOS – из-за Docker. Хотя вполне вероятно, что окончательным решением для обеих (и будущих) машин станет всё-таки Debian. Почитать о CoreOS можно тут>>>, а найти номер последней стабильной версии – тут>>>.
EC2 инстансы:
Схема планируется следующая:
- В публичной сети:
- EC2:
- Name: Bastion
- OS: OpenBSD;
- Soft: NGINX, Fail2ban, PSAD
- EC2:
- В приватной сети:
- EC2:
- Name: Zeus
- OS: CoreOS
- Soft: Docker:
- PHP-FPM (WordPress)
- EC2:
Позже – к EC2 подключим EBS-разделы и создадим RDS MySQL и S3 корзины для файлов и CloudFront CDN.
Процесс создания инфраструктуры включает в себя шаги:
- [AWS] ручное создание “голого” окружения с помощью AWS CLI (этот пост);
- [AWS] обновление “голого” окружения – добавление S3, CND, RDS;
- [AWS] создание шаблона CloudFormation для автоматизации развёртывания “голого” окружения;
- [CHEF] (Ansible?) создание рецептов для провижена Bastion;
- [AWS] обновление шаблона CloudFormation для провижена Bastion (NGINX, Fail2Ban, PSAD с хранением логов в S3);
- [CHEF] создание рецептов для провижена Zeus;
- [AWS] обновление шаблона CloudFormation для провижена Zeus;
- …
Содержание этой части:
Содержание
VPC
Начинаем с создания VPC:
$ aws ec2 create-vpc --cidr-block 10.0.0.0/16 { "Vpc": { "VpcId": "vpc-1cc04778", "InstanceTenancy": "default", "State": "pending", "DhcpOptionsId": "dopt-8645a9e3", "CidrBlock": "10.0.0.0/16", "IsDefault": false } }
И сразу ставим теги:
$ aws ec2 create-tags --resources vpc-1cc04778 --tags Key=Name,Value=rtfm_migrate_vpc
Проверяем:
$ aws ec2 describe-vpcs --vpc-ids vpc-1cc04778 { "Vpcs": [ { "VpcId": "vpc-1cc04778", "InstanceTenancy": "default", "Tags": [ { "Value": "rtfm_migrate_vpc", "Key": "Name" } ], "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-1cc04778 --query '[SecurityGroups[*].GroupId]' --output text sg-914e7ef6
Используя ID группы sg-914e7ef6
добавляем три правила – доступ по 80, 443 и 22 портам:
$ aws ec2 authorize-security-group-ingress --group-id sg-914e7ef6 --protocol tcp --port 80 --cidr 0.0.0.0/0 $ aws ec2 authorize-security-group-ingress --group-id sg-914e7ef6 --protocol tcp --port 443 --cidr 0.0.0.0/0 $ aws ec2 authorize-security-group-ingress --group-id sg-914e7ef6 --protocol tcp --port 22 --cidr 194.***.***.0/24
Subnets
Создаём новую подсеть в VPC – это будет наша публичная сеть для Bastion-хоста:
$ aws ec2 create-subnet --vpc-id vpc-1cc04778 --cidr-block 10.0.1.0/24 { "Subnet": { "VpcId": "vpc-1cc04778", "CidrBlock": "10.0.1.0/24", "State": "pending", "AvailabilityZone": "eu-west-1b", "SubnetId": "subnet-04ae0d5c", "AvailableIpAddressCount": 251 } }
Добавляем теги:
$ aws ec2 create-tags --resources subnet-04ae0d5c --tags Key=Name,Value=rtfm_migrate_pub_net
Добавляем приватную сеть, для RDS и Docker-хоста Zeus:
$ aws ec2 create-subnet --vpc-id vpc-1cc04778 --cidr-block 10.0.2.0/24 { "Subnet": { "VpcId": "vpc-1cc04778", "CidrBlock": "10.0.2.0/24", "State": "pending", "AvailabilityZone": "eu-west-1b", "SubnetId": "subnet-f7ae0daf", "AvailableIpAddressCount": 251 } }
Добавляем теги:
$ aws ec2 create-tags --resources subnet-f7ae0daf --tags Key=Name,Value=rtfm_migrate_priv_net
Проверяем:
$ aws ec2 describe-subnets --filters Name=vpc-id,Values=vpc-1cc04778 { "Subnets": [ { "VpcId": "vpc-1cc04778", "Tags": [ { "Value": "rtfm_migrate_pub_net", "Key": "Name" } ], "CidrBlock": "10.0.1.0/24", "MapPublicIpOnLaunch": false, "DefaultForAz": false, "State": "available", "AvailabilityZone": "eu-west-1b", "SubnetId": "subnet-04ae0d5c", "AvailableIpAddressCount": 251 }, { "VpcId": "vpc-1cc04778", "Tags": [ { "Value": "rtfm_migrate_priv_net", "Key": "Name" } ], "CidrBlock": "10.0.2.0/24", "MapPublicIpOnLaunch": false, "DefaultForAz": false, "State": "available", "AvailabilityZone": "eu-west-1b", "SubnetId": "subnet-f7ae0daf", "AvailableIpAddressCount": 251 } ] }
VPC Internet Gateway
Создаём Internet Gateway дл VPC – через него будет работать Bastion:
$ aws ec2 create-internet-gateway { "InternetGateway": { "Tags": [], "InternetGatewayId": "igw-a82821cd", "Attachments": [] } }
Подключаем его к VPC:
$ aws ec2 attach-internet-gateway --internet-gateway-id igw-a82821cd --vpc-id vpc-1cc04778
Добавляем теги:
$ aws ec2 create-tags --resources igw-a82821cd --tags Key=Name,Value=rtfm_migrate_igw
NAT Gateway
Для доступа из private подсети в Интернет – добавляем NAT Gateway. Его создаём в открытой подсети (subnet-04ae0d5c
), в которой будет и Bastion-хост.
Получаем новый публичный IP для этого шлюза:
$ aws ec2 allocate-address --domain vpc { "PublicIp": "52.210.114.208", "Domain": "vpc", "AllocationId": "eipalloc-f86a5c9d" }
И создаём шлюз, указав allocation-id
и subnet-id
для подключения к шлюзу внешнего IP:
$ aws ec2 create-nat-gateway --subnet-id subnet-04ae0d5c --allocation-id eipalloc-f86a5c9d { "NatGateway": { "NatGatewayAddresses": [ { "AllocationId": "eipalloc-f86a5c9d" } ], "VpcId": "vpc-1cc04778", "State": "pending", "NatGatewayId": "nat-0c045849a609991d3", "SubnetId": "subnet-04ae0d5c", "CreateTime": "2016-08-24T11:58:18.868Z" } }
Route table
Создаём новую таблицу маршрутизации в VPC:
$ aws ec2 create-route-table --vpc-id vpc-1cc04778 { "RouteTable": { "Associations": [], "RouteTableId": "rtb-b7f8bcd3", "VpcId": "vpc-1cc04778", "PropagatingVgws": [], "Tags": [], "Routes": [ { "GatewayId": "local", "DestinationCidrBlock": "10.0.0.0/16", "State": "active", "Origin": "CreateRouteTable" } ] } }
Используя route-table-id
и gateway-id
– добавляем правило маршрутизации для публичной сети (10.0.1.0/24, subnet-04ae0d5c
) в сеть 0.0.0.0/0 (весь Интернет) через Internet Gateway:
$ aws ec2 create-route --route-table-id rtb-b7f8bcd3 --destination-cidr-block 0.0.0.0/0 --gateway-id igw-a82821cd { "Return": true }
Подключаем эту таблицу к публичной подсети (не забываем это делать 🙂 ):
$ aws ec2 associate-route-table --route-table-id rtb-b7f8bcd3 --subnet-id subnet-04ae0d5c { "AssociationId": "rtbassoc-c84194af" }
Аналогично – создаём таблицу маршрутизации для приватной сети:
$ aws ec2 create-route-table --vpc-id vpc-1cc04778 { "RouteTable": { "Associations": [], "RouteTableId": "rtb-dbf9bdbf", "VpcId": "vpc-1cc04778", "PropagatingVgws": [], "Tags": [], "Routes": [ { "GatewayId": "local", "DestinationCidrBlock": "10.0.0.0/16", "State": "active", "Origin": "CreateRouteTable" } ] } }
Добавляем правило для маршрутизации трафика из приватной сети через созданный NAT Gateway в интернет:
$ aws ec2 create-route --route-table-id rtb-dbf9bdbf --nat-gateway-id nat-0c045849a609991d3 --destination-cidr-block 0.0.0.0/0 { "Return": true }
Подключаем таблицу к приватной подсети:
$ aws ec2 associate-route-table --route-table-id rtb-dbf9bdbf --subnet-id subnet-f7ae0daf { "AssociationId": "rtbassoc-6f409508" }
EC2
Переходим к созданию инстансов. Сначала – Bastion.
Создаём пару ключей для использования в этом стеке:
$ aws ec2 create-key-pair --key-name rtfm_migrate --query 'KeyMaterial' --output text > ~/.ssh/rtfm_migrate.pem
Проверяем и устанавливаем права доступа:
$ head -n 3 ~/.ssh/rtfm_migrate.pem -----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEAgM5rUbkNL8ZYaZayRVNy7Vf1Gqp0bGgv9W7IEHGvFcQbomFxQ2DekfVgjvet HRCfPR+hJ42+r+BE3J1fC+Iae5oBQb7b6z0TZX3oSHq36ehrSTTocvlkl9K8QGWHZ7kcB+xGFi4g $ chmod 400 ~/.ssh/rtfm_migrate.pem
--image-id ami-170b6064
: AMI с OpenBSD;--instance-type t2.nano:
описание типов можно посмотреть тут>>>;--subnet-id subnet-04ae0d5c:
созданная публичная подсеть.
Запускаем:
$ aws ec2 run-instances --image-id ami-170b6064 --count 1 --instance-type t2.nano --key-name rtfm_migrate --security-group-ids sg-914e7ef6 --subnet-id subnet-04ae0d5c { "OwnerId": "264418146286", "ReservationId": "r-40bbd9cd", "Groups": [], "Instances": [ { "Monitoring": { "State": "disabled" }, "PublicDnsName": "", "RootDeviceType": "ebs", "State": { "Code": 0, "Name": "pending" }, "EbsOptimized": false, "LaunchTime": "2016-08-24T12:22:33.000Z", "PrivateIpAddress": "10.0.1.243", "ProductCodes": [], "VpcId": "vpc-1cc04778", "StateTransitionReason": "", "InstanceId": "i-8c7a7c00", "ImageId": "ami-170b6064", "PrivateDnsName": "ip-10-0-1-243.eu-west-1.compute.internal", "KeyName": "rtfm_migrate", "SecurityGroups": [ { "GroupName": "default", "GroupId": "sg-914e7ef6" } ], "ClientToken": "", "SubnetId": "subnet-04ae0d5c", "InstanceType": "t2.nano", "NetworkInterfaces": [ { "Status": "in-use", "MacAddress": "0a:a7:06:1e:63:97", "SourceDestCheck": true, "VpcId": "vpc-1cc04778", "Description": "", "NetworkInterfaceId": "eni-cbbcd994", "PrivateIpAddresses": [ { "Primary": true, "PrivateIpAddress": "10.0.1.243" } ], "Attachment": { "Status": "attaching", "DeviceIndex": 0, "DeleteOnTermination": true, "AttachmentId": "eni-attach-a194877e", "AttachTime": "2016-08-24T12:22:33.000Z" }, "Groups": [ { "GroupName": "default", "GroupId": "sg-914e7ef6" } ], "SubnetId": "subnet-04ae0d5c", "OwnerId": "264418146286", "PrivateIpAddress": "10.0.1.243" } ], "SourceDestCheck": true, "Placement": { "Tenancy": "default", "GroupName": "", "AvailabilityZone": "eu-west-1b" }, "Hypervisor": "xen", "BlockDeviceMappings": [], "Architecture": "x86_64", "StateReason": { "Message": "pending", "Code": "pending" }, "RootDeviceName": "/dev/sda1", "VirtualizationType": "hvm", "AmiLaunchIndex": 0 } ] }
Добавляем теги:
$ aws ec2 create-tags --resources i-8c7a7c00 --tags Key=Name,Value=rtfm_migrate_ec2_bastion
Получаем Elastic IP для этой машины:
$ aws ec2 allocate-address --domain vpc { "PublicIp": "52.31.64.42", "Domain": "vpc", "AllocationId": "eipalloc-3f5e685a" }
Подключаем его к созданному инстансу:
$ aws ec2 associate-address --public-ip 52.31.64.42 --instance-id i-8c7a7c00 { "AssociationId": "eipassoc-df2670b9" }
После того, как машина поднялась – проверяем её доступность:
$ aws ec2telnet 52.31.64.42 22 Trying 52.31.64.42... Connected to 52.31.64.42. Escape character is '^]'. SSH-2.0-OpenSSH_7.3 ...
Находим подходящий AMI:
$ aws ec2 describe-images --owners aws-marketplace --filters Name=architectureValues=x86_64,Name=virtualization-type,Values=hvm,Name=name,Values=CoreOS-stable-1068* --query '[Images[*].{Name:Name,ImageId:ImageId,Description:Description}]' --output text CoreOS stable 1068.6.0 (HVM) ami-03cea870 CoreOS-stable-1068.6.0-hvm-0d1e0bd0-eaea-4397-9a3a-c56f861d2a14-ami-edc744fa.3 CoreOS stable 1068.9.0 (HVM) ami-068bfe75 CoreOS-stable-1068.9.0-hvm-0d1e0bd0-eaea-4397-9a3a-c56f861d2a14-ami-6d138f7a.3
Запускаем второй инстанс – Zeus, в приватной подсети subnet-f7ae0daf
:
$ aws ec2 run-instances --image-id ami-068bfe75 --count 1 --instance-type t2.nano --key-name rtfm_migrate --security-group-ids sg-914e7ef6 --subnet-id subnet-f7ae0daf { "OwnerId": "264418146286", "ReservationId": "r-b589eb38", "Groups": [], "Instances": [ { "Monitoring": { "State": "disabled" }, "PublicDnsName": "", "RootDeviceType": "ebs", "State": { "Code": 0, "Name": "pending" }, "EbsOptimized": false, "LaunchTime": "2016-08-24T13:51:21.000Z", "PrivateIpAddress": "10.0.2.101", "ProductCodes": [], "VpcId": "vpc-1cc04778", "StateTransitionReason": "", "InstanceId": "i-0113158d", "ImageId": "ami-068bfe75", "PrivateDnsName": "ip-10-0-2-101.eu-west-1.compute.internal", "KeyName": "rtfm_migrate", "SecurityGroups": [ { "GroupName": "default", "GroupId": "sg-914e7ef6" } ], "ClientToken": "", "SubnetId": "subnet-f7ae0daf", "InstanceType": "t2.nano", "NetworkInterfaces": [ { "Status": "in-use", "MacAddress": "0a:26:1d:6a:a4:cf", "SourceDestCheck": true, "VpcId": "vpc-1cc04778", "Description": "", "NetworkInterfaceId": "eni-03c3a65c", "PrivateIpAddresses": [ { "Primary": true, "PrivateIpAddress": "10.0.2.101" } ], "Attachment": { "Status": "attaching", "DeviceIndex": 0, "DeleteOnTermination": true, "AttachmentId": "eni-attach-be5a4861", "AttachTime": "2016-08-24T13:51:21.000Z" }, "Groups": [ { "GroupName": "default", "GroupId": "sg-914e7ef6" } ], "SubnetId": "subnet-f7ae0daf", "OwnerId": "264418146286", "PrivateIpAddress": "10.0.2.101" } ], "SourceDestCheck": true, "Placement": { "Tenancy": "default", "GroupName": "", "AvailabilityZone": "eu-west-1b" }, "Hypervisor": "xen", "BlockDeviceMappings": [], "Architecture": "x86_64", "StateReason": { "Message": "pending", "Code": "pending" }, "RootDeviceName": "/dev/xvda", "VirtualizationType": "hvm", "AmiLaunchIndex": 0 } ] }
Добавляем теги:
$ aws ec2 create-tags --resources i-0113158d --tags Key=Name,Value=rtfm_migrate_ec2_zeus
Проверяем.
Копируем ключ на Bastion:
$ scp -i ~/.ssh/rtfm_migrate.pem ~/.ssh/rtfm_migrate.pem [email protected]:/root/.ssh/ The authenticity of host '52.31.64.42 (52.31.64.42)' can't be established. ECDSA key fingerprint is 50:e8:0f:71:e2:68:55:18:d5:eb:49:e8:75:ae:ee:ce. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '52.31.64.42' (ECDSA) to the list of known hosts. rtfm_migrate.pem 100% 1671 1.6KB/s 00:00
Подключаемся на Bastion, и с него – на Zeus:
# ssh [email protected] -i .ssh/rtfm_migrate.pem The authenticity of host '10.0.2.101 (10.0.2.101)' can't be established. ECDSA key fingerprint is SHA256:AuRuYQ10QzHAMU9C3mDu2AxVK4ruajAHMrjZR2O5OfQ. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '10.0.2.101' (ECDSA) to the list of known hosts. CoreOS stable (1068.9.0) Last login: Wed Aug 24 13:57:57 2016 from 10.0.1.243 core@ip-10-0-2-101 ~ $
Проверяем Интернет с Zeus:
core@ip-10-0-2-101 ~ $ ping ya.ru PING ya.ru (213.180.204.3) 56(84) bytes of data. 64 bytes from www.yandex.ru (213.180.204.3): icmp_seq=1 ttl=46 time=67.0 ms 64 bytes from www.yandex.ru (213.180.204.3): icmp_seq=2 ttl=46 time=66.9 ms
Готово.
Продолжение – AWS: миграция RTFM, часть #2: ручное создание инфраструктуры – AIM, S3, RDS и EBS.