Infraestrutura como Código é uma pratica utilizada para fazer o versionamento da infraestrutura assim como fazemos como os códigos em programação, existem diversas ferramentas que podem ser utilizadas, uma das mais famosas é o Terraform, Com ele é possível provisionar infraestruturas em praticamente todas as Clouds , ferramentas de virtualização e diversas outras tecnologias que existem atualmente.
Mas se o foco é AWS, existe um serviço específico chamado CloudFormation, nele podemos descrever toda na nossa infraestrutura no formato YAML ou JSON e depois provisionar usando os comandos AWS CLI, sem ter qualquer iteração com o o painel.
Então vamos lá, primeiro passo é instalar o aws cli.
python3 -m pip install awscli
Caso tenha sido instalado corretamente você pode executar esse comando:
PS C:\Users\1511 MXTI> aws --version aws-cli/1.16.274 Python/3.7.3 Windows/10 botocore/1.13.10
PS C:\Users\1511 MXTI>
No meu caso estou usando a versão 3.7 do Python e 1.13 do Boto3.
Como esse tutorial é relativamente mais avançado, estou levando como premissa de que você já sabe os comandos e os recursos básicos da AWS.
Agora vamos definir as credenciais de acesso.
PS C:\Users\1511 MXTI> aws configure AWS Access Key ID [****************AB4Z]:
AWS Secret Access Key [****************GuGW]:
Default region name [us-west-1]:
Default output format [None]:
PS C:\Users\1511 MXTI>
Esses dados de acesso vocês pode cria-los utilizando o IAM da AWS ( https://aws.amazon.com/pt/iam/ ).
Com tudo configurado e o aws cli instalado, vamos direto ao CloudFormation.
Um comando básico é fazer a listagem dos Stacks que você já tem criado, como acabamos de iniciar não temos nenhum.
PS C:\Users\1511 MXTI> aws cloudformation list-stacks {
{
"StackSummaries": []
}
Sabemos que para criar uma instância EC2, precisamos dos seguintes recursos que antecedem o EC2, que seriam.
- VPC
- Subnet
- SecurityGroup
- InternetGateway
- RoutingTable
Então já vamos cria-los como código, ficando da seguinte maneira.
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: "192.168.0.0/24"
EnableDnsHostnames: true
EnableDnsSupport: true
# InternetAccess to VPC
InternetGateway:
Type: "AWS::EC2::InternetGateway"
VPCGatewayAttachment:
Type: "AWS::EC2::VPCGatewayAttachment"
Properties:
InternetGatewayId:
Ref: InternetGateway
VpcId:
Ref: VPC
#Creating routes
RouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId:
Ref: VPC
Tags:
- Key: Name
Value: Public Subnets
- Key: Network
Value: Public
Route:
DependsOn: VPCGatewayAttachment
Type: AWS::EC2::Route
Properties:
RouteTableId:
Ref: RouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId:
Ref: InternetGateway
# Security group that allows all traffic
SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow http to client host
VpcId:
Ref: VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 0
ToPort: 65535
CidrIp: 0.0.0.0/0
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: 0
ToPort: 65535
CidrIp: 0.0.0.0/0
Subnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: "us-east-2a"
CidrBlock: "192.168.0.0/24"
VpcId:
Ref: VPC
O RoutingTable o InternetGateway são itens praticamente obrigatórios para acessar a instância de fora da AWS, pois ao efetuar uma conexão SSH para a máquina, ela precisa ter acesso a internet e as rotas devem estar configuradas para que a comunicação de resposta possa ser estabelecida. Sendo assim foi criada a seguinte rota:
Destination: 0.0.0.0/0 - Que seria a rota default, passa pelo InternetGateway, que no nosso caso também recebeu o nome de InternetGateway.
Note que para fazer o vinculo entre os recursos criados é utilizada a instrução Ref, essa instrução serve para pegar o ID do recursos que foi especificado através do Nome, se você reparar no começo do YAML, está da seguinte maneira.
Resources:
VPC:
Type: AWS::EC2::VPC
A primeira linha é definida a instrução Resources, pois abaixo desse nível teremos os recursos criados, que ficam na seguinte sequência.
Resources:
NomeDoRecurso:
Type: TipoDoRecurso
Então apesar de eu ter usado VPC no nome do meu recurso, o que define efetivamente que é uma VPC é a instrução Type, que no caso foi AWS::EC2::VPC.
Agora que o arquivo já tem os itens básicos para provisionar as instancias EC2, vamos fazer a criação do Stack.
PS C:\Users\1511 MXTI> aws cloudformation create-stack --stack-name blog --template-body file://tutorial.yml
{
"StackId": "arn:aws:cloudformation:us-east-2:360560397478:stack/blog/0d2f8500-0016-11ea-a411-0608930970a0"
}
Para saber se a criação foi executada com sucesso, você pode executar o seguinte comando:
PS C:\Users\1511 MXTI> aws cloudformation list-stacks
{
"StackSummaries": [
{
"StackId": "arn:aws:cloudformation:us-east-2:360560397478:stack/blog/0d2f8500-0016-11ea-a411-0608930970a0",
"StackName": "blog",
"CreationTime": "2019-11-05T21:48:49.984Z",
"StackStatus": "CREATE_COMPLETE",
"DriftInformation": {
"StackDriftStatus": "NOT_CHECKED"
}
},
Veja que o status está como CREATE_COMPLETE, com o StackName blog.
Ou seja, toda a parte de comunicação e redes está funcionando.
Agora vamos criar uma EC2 dentro dessa infraestrututura.
Para isso no mesmo arquivo YAML , vamos editar e inserir um recurso do tipo EC2.
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: "192.168.0.0/24"
EnableDnsHostnames: true
EnableDnsSupport: true
# InternetAccess to VPC
InternetGateway:
Type: "AWS::EC2::InternetGateway"
VPCGatewayAttachment:
Type: "AWS::EC2::VPCGatewayAttachment"
Properties:
InternetGatewayId:
Ref: InternetGateway
VpcId:
Ref: VPC
#Creating routes
RouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId:
Ref: VPC
Tags:
- Key: Name
Value: Public Subnets
- Key: Network
Value: Public
Route:
DependsOn: VPCGatewayAttachment
Type: AWS::EC2::Route
Properties:
RouteTableId:
Ref: RouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId:
Ref: InternetGateway
# Security group that allows all traffic
SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow http to client host
VpcId:
Ref: VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 0
ToPort: 65535
CidrIp: 0.0.0.0/0
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: 0
ToPort: 65535
CidrIp: 0.0.0.0/0
Subnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: "us-east-2a"
CidrBlock: "192.168.0.0/24"
VpcId:
Ref: VPC
# EC2 comeca aqui
EC2Blog:
Type: AWS::EC2::Instance
Properties:
ImageId: "ami-0d5d9d301c853a04a" # ubuntu 18.04
KeyName: "blog"
InstanceType: "t2.micro"
NetworkInterfaces:
- AssociatePublicIpAddress: "true"
DeviceIndex: "0"
GroupSet:
- Ref: "SecurityGroup"
SubnetId:
Ref: "Subnet"
BlockDeviceMappings:
- DeviceName: "/dev/sdm"
Ebs:
VolumeType: "io1"
Iops: "200"
DeleteOnTermination: "true"
VolumeSize: "20"
Note que as linhas de código referente a instância EC2 foram colocadas no final.
Agora para atualizar o nosso Stack, utilize o comando:
PS C:\Users\1511 MXTI> aws cloudformation update-stack --stack-name blog --template-body file://tutorial.yml
{
"StackId": "arn:aws:cloudformation:us-east-2:360560397478:stack/blog/0d2f8500-0016-11ea-a411-0608930970a0"
}
Vamos rodar o list-stacks novamente e ver se está tudo pronto:
PS C:\Users\1511 MXTI> aws cloudformation list-stacks
{
"StackSummaries": [
{
"StackId": "arn:aws:cloudformation:us-east-2:360560397478:stack/blog/0d2f8500-0016-11ea-a411-0608930970a0",
"StackName": "blog",
"CreationTime": "2019-11-05T21:48:49.984Z",
"LastUpdatedTime": "2019-11-05T22:13:33.799Z",
"StackStatus": "UPDATE_COMPLETE",
"DriftInformation": {
"StackDriftStatus": "NOT_CHECKED"
}
},
Veja que o resultado é um UPDATE_COMPLETE.
Para confirmar se a instância está criada.
PS C:\Users\1511 MXTI> aws ec2 describe-instances
{
"Reservations": [
{
"Groups": [],
"Instances": [
{
"AmiLaunchIndex": 0,
"ImageId": "ami-0d5d9d301c853a04a",
"InstanceId": "i-056111b27163add35",
"InstanceType": "t2.micro",
"KeyName": "blog",
"LaunchTime": "2019-11-05T22:13:38.000Z",
"Monitoring": {
"State": "disabled"
},
"Placement": {
"AvailabilityZone": "us-east-2a",
"GroupName": "",
"Tenancy": "default"
},
"PrivateDnsName": "ip-192-168-0-47.us-east-2.compute.internal",
"PrivateIpAddress": "192.168.0.47",
"ProductCodes": [],
"PublicDnsName": "ec2-3-15-149-77.us-east-2.compute.amazonaws.com",
"PublicIpAddress": "3.15.149.77",
"State": {
"Code": 16,
"Name": "running"
},
Veja que o State dela está como Running e o endereço de IP Publico é: 3.15.149.77.
Agora vamos acessar e ver se tudo está funcionando corretamente:
PS C:\Users\1511 MXTI> ssh -i .\Downloads\blog.pem ubuntu@3.15.149.77
The authenticity of host '3.15.149.77 (3.15.149.77)' can't be established.
ECDSA key fingerprint is SHA256:3hhSOEHmTIXem31Bo4nRYkxi1PjtaF3zxuGW9yEOKrk.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '3.15.149.77' (ECDSA) to the list of known hosts.
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-1051-aws x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Tue Nov 5 22:22:41 UTC 2019
System load: 0.0 Processes: 85
Usage of /: 13.6% of 7.69GB Users logged in: 0
Memory usage: 15% IP address for eth0: 192.168.0.47
Swap usage: 0%
0 packages can be updated.
0 updates are security updates.
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
To run a command as administrator (user "root"), use "sudo ".
See "man sudo_root" for details.
ubuntu@ip-192-168-0-47:~$
Está tudo perfeito, então como foi só um teste.
Vamos deletar o stack para não gastar dinheiro haha.
PS C:\Users\1511 MXTI> aws cloudformation delete-stack --stack-name blog
Pronto!
Lembrando que tudo o que foi executado aqui também aparece no Dashboard da AWS, eu costumo manter ele aberto para visualizar os logs da criação do ambiente, então fica ai a dica =)