CloudFormation: Infraestrutura como código na AWS
05 de nov. de 2019
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 awscliCaso 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: VPCO 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::VPCA 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: TipoDoRecursoEntã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 "sudoEstá tudo perfeito, então como foi só um teste. Vamos deletar o stack para não gastar dinheiro haha.". See "man sudo_root" for details. ubuntu@ip-192-168-0-47:~$
PS C:\Users\1511 MXTI> aws cloudformation delete-stack --stack-name blogPronto! 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 =)