В рамках подготовки переезда 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.




