BT

Disseminando conhecimento e inovação em desenvolvimento de software corporativo.

Contribuir

Tópicos

Escolha a região

Início Artigos Construindo um VPC com CloudFormation - Parte 1

Construindo um VPC com CloudFormation - Parte 1

Favoritos

Pontos Principais

  • O AWS CloudFormation nos permite implementar "Infraestrutura como código" em um ambiente da AWS.
  • Um Virtual Private Cloud (VPC) sofisticado é fácil para criar e atualizar de forma automatizada com o CloudFormation.
  • Podemos reutilizar modelos do CloudFormation para criar várias pilhas de recursos com várias finalidades.
  • O CloudFormation fornece pseudo-parâmetros e funções intrínsecas.
  • Podemos deletar pilhas quando forem finalizadas.

Você pode encontrar a parte 2 deste artigo aqui.

Este artigo descreve como usar o AWS CloudFormation para criar e gerenciar uma Virtual Private Cloud (VPC), completa com sub-redes, NAT, tabelas de rotas, etc. A ênfase é o uso do CloudFormation e da infraestrutura como código para criar e gerenciar recursos na AWS, menos sobre os problemas de design no VPC.

Especialistas em rede: Como o foco será principalmente no CloudFormation, é possível ter opiniões divergentes sobre bloqueios do CIDR, tabelas de roteamento, etc. Tudo bem, é possível alterar esse modelo conforme necessário para suas finalidades.

Vá direto ao assunto: O modelo de código-fonte do CloudFormation descrito por este artigo é encontrado no GitHub. Sinta-se à vontade para fazer o download, modificar e usar esse modelo da maneira que desejar (embora eu não aceite a responsabilidade por mau uso).

Por que o CloudFormation?

Você pode estar se perguntando por que usaríamos o CloudFormation para construir nosso VPC quando podemos criar um através do assistente de VPC no console de gerenciamento. Existem vários motivos pelos quais:

Infraestrutura como Código: CloudFormation nos permite criar uma "pilha" de "recursos" em uma única etapa. Recursos são as coisas que criamos (Instâncias do EC2, VPCs, sub-redes, etc.), um conjunto deles é chamado de pilha. Podemos escrever um modelo que pode facilmente levantar uma pilha de rede, exatamente como gostamos, em uma etapa. Isso é mais rápido, repetitivo e mais consistente do que criar manualmente nossa rede por meio do console de gerenciamento ou da CLI. Podemos verificar nosso modelo na origem do controle e usá-lo a qualquer momento que desejarmos para qualquer finalidade desejada.

Atualizável: Podemos modificar nossa pilha de rede fazendo alterações em nosso modelo CloudFormation e, em seguida, modificando a pilha com base em nosso modelo revisado. O CloudFormation é inteligente o suficiente para alterar a pilha para corresponder ao modelo.

Reutilizável: Podemos reutilizar esse modelo para criar várias redes em várias regiões para vários propósitos e tempos diferentes.

Detecção de desvio: Um novo recurso (a partir de novembro de 2018), o CloudFormation pode informar-nos se os recursos se "afastaram" de sua configuração original. Isso pode acontecer quando os administradores alteraram manualmente os recursos, algo que as organizações maduras normalmente desencorajam.

Descartável: Podemos facilmente excluir a pilha quando finalizarmos ela.

O que você precisa

Para seguir este tutorial, será necessário:

Uma conta da AWS. É possível acompanhar como um usuário do IAM, mas deve ter permissões para criar VPCs, sub-redes, tabelas de rota, instâncias do EC2, etc. Minha recomendação geral é criar sua própria conta pessoal da AWS que você controle completamente. Dentro dessa conta, crie um usuário do IAM com permissões completas e use-o diariamente. Os custos associados a este tutorial serão extremamente leves, especialmente se excluir a pilha quando terminar.

Um editor de texto. Quase tudo funciona: Sublime, Atom, nano, IDEs completos como o Eclipse, IntelliJ, Visual Studio, etc.. Certifique-se de NÃO usar um processador de texto - ele irá incorporar caracteres especiais que causarão erros de sintaxe. Usarei o Visual Studio Code, meu editor favorito (esta semana).

Começando

Crie um arquivo YML vazio: Comece criando um arquivo vazio. Salve-o sob o nome "MyNetwork.YML". É possível usar qualquer nome que queira, mas vou me referir a este nome de arquivo mais tarde. Certifique-se de fornecer uma extensão YML. O CloudFormation suporta JSON ou YAML e usaremos o último. A principal razão é 1) menos cerimónia sintática e 2) a capacidade de PÔR COMENTÁRIO no meu trabalho, sem o qual não me lembraria o que estava fazendo há uma semana.

Adicione o Cabeçalho: Depois de ter seu arquivo vazio, copie e cole essa estrutura em: este é o cabeçalho necessário para qualquer modelo do CloudFormation:

AWSTemplateFormatVersion: 2010-09-09
# Este modelo do CloudFormation implanta uma VPC / Rede básica. 
Resources:

Comentários: Tudo após um caractere "#" é um comentário. Encorajo-vos a escrever seus próprios comentários enquanto fazem suas próprias descobertas. Minha sugestão: limitar os comentários a descrever qualquer coisa incomum que tenha que fazer para que algo funcione, especialmente algo que levou algum tempo para descobrir. Imagine um colega de trabalho lendo seu modelo e tendo perguntas que não podem ser respondidas via documentação publicada, é isso que se comenta.

Adicionando um VPC

O primeiro recurso a adicionar será o próprio VPC. Copie essas linhas para que sua seção de Resources seja assim:

Resources:
  # Primeiro, um VPC:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.1.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
      - Key: Name
        Value:  !Join ['', [!Ref "AWS::StackName", "-VPC" ]]

Indentação: Primeira coisa a ser feita, mantenha o espaçamento correto. "Resources:" não deve ter espaços anteriores. "VPC:" deve ser recuado dois espaços, "Type" e "Properties" devem ser recuadas mais dois espaços, etc. NÃO use tabs, a menos que seu editor converta tabulações em espaços duplos. A maioria dos editores listados acima faz, uma vez que eles entendem o tipo de arquivo que está sendo criado. A indentação é fundamental no YAML, é o preço que pagamos para evitar os parênteses e vírgulas do JSON.

VPC: Este recurso instrui o CloudFormation a criar um recurso de VPC, juntamente com algumas propriedades essenciais e um nome. A primeira linha é simplesmente "VPC" - este é um nome arbitrário que damos a este recurso para identificá-lo dentro da pilha. Confusamente, muitos tipos de recursos têm propriedades separadas por "nome" que NÃO são a mesma coisa. O primeiro é apenas para referenciar recursos dentro da pilha, o último é o nome público do recurso resultante.

Tipo: AWS::EC2::VPC: Caso utilize o Google, verá um link que o leva diretamente para a documentação do CloudFormation com recursos de VPC. Aqui encontrará uma amostra parecida com a que está acima. Pessoalmente eu encontrei as páginas de referência do CloudFormation indispensáveis. Admito livremente copiar e colar amostras de fragmentos como ponto de partida para meus próprios recursos.

Aspas Duplas: Uma coisa que verá na documentação oficial é o uso desnecessário de aspas duplas e simples. O valor AWS :: EC2 :: VPC NÃO precisam ser colocados entre aspas duplas, nem aos valores "true" abaixo. Minha teoria é que a equipe de Documentação da AWS copiou originalmente os exemplos YAML do JSON, sendo o último exigindo quase tudo entre aspas.

Properties: Todo recurso possui propriedades. A documentação lhe dirá quais são necessárias e quais não são. A documentação não informa os valores padrão das configurações opcionais e explica o significado e os valores permitidos para a maioria das configurações. Geralmente se espera ter total compreensão do significado, sintaxe e ramificações de todos os valores possíveis - o que ninguém faz. Provavelmente, 90% do meu tempo de desenvolvimento foi gasto nesta pesquisa auxiliar. Ugh.

CIDR Block: Eu estou dando meu VPC um CIDR de 10.1.0.0/16. Se for preciso de uma explicação detalhada sobre o significado desse valor, consulte esta descrição. Mas resumidamente, estou dando ao VPC uma gama de mais de 65.000 endereços IP privados possíveis para usar, todos os quais começarão com "10.1". Isso é mais do que endereços suficientes para a maioria dos casos de uso.

DNS Suporte / Hostnames: Essas configurações permitem que os nomes de host DNS sejam atribuídos automaticamente às instâncias do EC2 criadas dentro da VPC. Sem entrar em muitos detalhes sobre o DNS, isso fornecerá um nome pelo qual podemos alcançar nossas instâncias do EC2, em vez de apenas um endereço IP. Não é estritamente necessário, mas geralmente é útil.

Tags / Chave / Valor: Nosso VPC precisa de um nome. Curiosamente, não há propriedade "nome" para definir. Então, em vez disso, usaremos uma tag com a "Chave" de "Nome" e um "Valor" de tudo o que quisermos chamar de VPC. O valor será usado como o nome de exibição em muitas situações (mas não em todas).

O valor é onde fica divertido. Poderíamos simplesmente codificar um nome, mas se fôssemos criar duas pilhas com este modelo na mesma região, teríamos duas VPCs com o mesmo nome (o que é realmente permitido, tente!). Isso seria desnecessariamente confuso, então, em vez disso, daremos um passo extra para atribuir dinamicamente um nome.

!Join: O "!Join" é o que o CloudFormation chama de função intrínseca. O CloudFormation tem cerca de 15 dessas funções e veremos várias neste artigo. Resumidamente, !Join é usado para concatenar ou "colar" uma string de texto. As '' (aspas dupla) dentro do primeiro colchete identifica o caractere a ser colocado entre os valores concatenados. Nós não queremos nenhum, então colocamos uma string vazia.

!: Ao ler a documentação, observe que as funções têm um formato longo (fn::Join) e um formato curto alternativo que pode ser usado em YAML (!Join). Isso geralmente é o que irei usar. No entanto, esteja ciente de que há algumas situações em que o formulário de atalho está indisponível, geralmente nos casos em que está aninhando uma função em outra.

!Ref: A próxima função, é a função de referência. Ela faz referência a algo definido em algum outro lugar, geralmente dentro do modelo. Isso é MUITO usado como recursos e comumente fazem referência uns aos outros.

AWS::StackName - Isso é o que o CloudFormation chama de pseudo-parâmetro. Ele será resolvido para o nome da pilha quando executarmos o modelo. Existem cerca de 7 pseudo-parâmetros disponíveis para determinar dinamicamente coisas como a região atual, usuário atual, etc. O uso desses pseudo-parâmetros aumenta muito a flexibilidade do nosso modelo. Neste caso, o nome do nosso VPC irá ecoar a pilha do CloudFormation a que pertence. Isso é muito útil quando se tem muitos recursos criados por várias pilhas diferentes - confie em mim.

-VPC: Este é simplesmente o sufixo do nome do nosso VPC. Portanto, se executarmos esse modelo por meio do CloudFormation com o nome da pilha "MyNetwork", nosso VPC será marcado com o nome "MyNetwork-VPC". Esse nome será usado em muitos, mas não em todos, os locais onde os VPCs são listados ou exibidos.

Executando o modelo

Salve seu trabalho. Estamos longe de terminar, mas é possível verificar seu trabalho executando este modelo por meio do CloudFormation. Existem duas opções para fazer isso, o AWS Management Console ou o AWS Command Line Interface (CLI).

Do console de gerenciamento da AWS

De um navegador, abra o AWS Management Console. Entre, selecione qualquer região. Encontre o CloudFormation nos menus, use o recurso de pesquisa, se necessário. Uma vez dentro, clique em "Criar pilha". Selecione a opção para enviar seu próprio modelo e clique em próximo.

Você tem algum erro? Se assim for, o CloudFormation está reclamando de um problema de sintaxe. Corrija isso antes de continuar. Geralmente, os erros resultam do uso de tabs em vez de espaços, espaços em locais incomuns, = em vez de: ou caracteres especiais incorporados pelo editor que está utilizando.

Quando estiver na próxima página, dê um nome à sua pilha. Vou me referir a "MyNetwork" para o restante deste artigo, mas pode usar o que quiser. Continue com o assistente sem fazer outras entradas e crie sua pilha.

Enquanto sua pilha está em execução, dê uma olhada nas guias "Eventos" e "Recursos". Os eventos mostrarão quando o seu VPC está sendo criado e quando ele foi criado com sucesso. Quando o último recurso na sua pilha é criado, o status da pilha mudará para criado com sucesso.

Você tem algum erro? A pilha mostrará um status de criação com falha. Os problemas encontrados nesse momento são não-sintáticos, geralmente relacionados à lógica do que está tentando criar. Use a guia Eventos para encontrar o erro mais rapidamente. Eu achei a maior parte do tempo que a mensagem é clara o suficiente para me guiar para o problema, e definitivamente identifica o recurso em questão.

Um conceito importante a ser observado é que, se houver algum erro ao criar sua pilha, a pilha inteira (todos os recursos) será revertida. Esse comportamento pode ser substituído, mas geralmente é bom! Geralmente é muito mais fácil começar cada corrida em uma pista limpa.

Se sua pilha falhar, ela ainda será exibida na lista de pilhas, mesmo que não haja recursos na pilha. Isso é feito para lhe dar tempo para investigar o erro. Use a ação 'delete stack' quando tiver determinado o problema.

A partir do CLI:

Caso tenha o AWS CLI instalado e já esteja configurado com sua chave de acesso e chave secreta (qualquer região) e SE seu prompt de comando estiver localizado no mesmo diretório que seu arquivo YML, poderá executar este comando:

aws cloudformation create-stack --stack-name MyNetwork --template-body file://MyNetwork.yml

--stack-name: Pode ser o que desejar, mas vamos referir a "MyNetwork" ao longo do artigo.

--template-body: O arquivo em que está trabalhando.

Caso tenha algum erro sintático, ele será imediatamente apresentado. Corrija isso antes de continuar. Geralmente, os erros resultam do uso de tabs em vez de espaços, espaços em locais incomuns, = em vez de : ou caracteres especiais incorporados pelo editor que está usando.

NO ENTANTO, nem todos podem ser descobertos pelo CloudFormation imediatamente. Pode ser que encontre um erro não-sintático (por exemplo, permissões) vários minutos em uma criação de pilha. Quando isso acontecer, precisará verificar o status periodicamente para detectar problemas. Uma alternativa conveniente é aguardar que a pilha termine ou elimine erros com esta função:

aws cloudformation wait stack-create-complete --stack-name MyNetwork

... e quando a pilha estiver completa, é possível usar o seguinte trecho para examiná-la:

aws cloudformation describe-stacks

Verifique o VPC: De dentro do console de gerenciamento, vá para a seção VPC. Lá encontrará o seu VPC na lista, com o nome que forneceu. Claro, não há nada neste VPC, mas vamos chegar a esse próximo. Caso esteja curioso, não há custo para este VPC, ou o uso do CloudFormation, embora isso mude abaixo (veja a seção NAT).

Atualizando uma pilha

Antes de prosseguirmos no nosso modelo, vamos experimentar atualizar a pilha. Um dos recursos fantásticos do CloudFormation é a capacidade de modificar uma pilha com base nas alterações feitas em um modelo. Para demonstrar isso, retorne ao seu modelo e faça uma ou mais das seguintes alterações:

  • Altere as configurações "enableDns *" do VPC para false.
  • Altere a tag / valor da VPC para ! Participe de ['', [! Ref "AWS :: StackName", "-VPC2"]]

Salve suas alterações

A partir do AWS Management Console

Selecione a pilha existente na lista e escolha a ação "Atualizar pilha". Selecione a opção para enviar seu próprio modelo e clique em próximo. Continue pressionando próximo até chegar na tela que pede para criar um conjunto de mudanças. O conjunto de mudanças é literalmente as alterações que o CloudFormation pretende aplicar aos seus recursos. O CloudFormation pode fazer algumas alterações com facilidade, mas algumas exigem que os recursos existentes sejam excluídos e recriados. Escolha a opção final para executar o conjunto de alterações e observe as alterações feitas.

A partir do CLI

A partir da CLI, atualize a pilha usando este comando:

aws cloudformation update-stack --stack-name MyNetwork --template-body file://MyNetwork.yml

E como antes, é possível usar o comando wait para monitorar o progresso:

aws cloudformation wait stack-update-complete --stack-name MyNetwork

Nossa pilha é pequena, então a atualização deve levar apenas alguns minutos. É possível que encontre um erro, se isso acontecer, o resultado será ainda mais fascinante, pois o CloudFormation reverte sua pilha para o formato anterior. Erros podem acontecer por vários motivos, como quando uma exclusão/recriação de um recurso afetaria outro recurso fora da pilha.

Excluir a pilha

Um conceito nativo da nuvem para se sentir confortável é que os Recursos são Descartáveis. Podemos criar e excluir a pilha sempre que desejarmos agora que temos nosso modelo.

No console, selecione a pilha e execute a ação "delete stack". Ou, a partir da CLI, execute o comando awsystemformation delete-stack --stack-name MyNetwork .

Gateway de Internet e anexo

Voltando ao nosso modelo: A maioria dos VPCs precisam se conectar à internet. Faremos isso adicionando recursos para InternetGatway e GatewayAttachment. Copie estas linhas abaixo do seu recurso de VPC:

   # Nosso VPC precisará de acesso à internet:     
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    DependsOn: VPC
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
     # Observe como você não pode anexar um IGW a um VPC, a menos que ambos sejam criados:
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

AWS::EC2::InternetGateway: Nosso VPC não poderá interagir com a internet pública sem ele. É importante notar que não precisa conectar um VPC à Internet, muitas organizações criam VPCs totalmente desconectados do mundo público, utilizando conexões VPN ou AWS Direct Connect para se conectar exclusivamente a suas redes locais existentes.

VPCGatewayAttachment: Mais interessante é o recurso AttachGateway. Este é o gancho real entre o VPC e o InternetGateway. Observe as propriedades; o VpcId faz referência ao recurso VPC definido acima e o InternetGatewayId faz referência ao nosso InternetGateway. Quando o CloudFormation executa uma pilha, ele tentará criar todos os recursos simultaneamente. No entanto, a função !Ref implica uma ordenação, o CloudFormation criará o VPC e o InternetGateway simultaneamente, mas ambos devem estar completos antes de usá-los para criar o anexo.

!Ref: Como mencionado anteriormente, essa é a função "referência" do CloudFormation incorporada. Esta é a maneira principal pela qual os recursos se referem uns aos outros.

Mais tarde, quando executar esse modelo, descobrirá que leva vários segundos para criar esses três primeiros recursos, especialmente o anexo. Esse atraso se tornará um problema mais tarde. Não poderemos fazer nenhuma interação com a Internet pública até que o anexo seja concluído. Aguarde o uso de "DependsOn" mais tarde para resolver esse problema.

Algumas sub-redes

Agora vemos uma sub-rede com este código:

PublicSubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.1.10.0/24
      AvailabilityZone: !Select [ 0, !GetAZs ]    # Obtenha o primeiro AZ na lista      
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-Public-A

PublicSubnetA: Novamente, e nome arbitrário para o recurso, aplicável somente dentro da pilha. Chame como quiser. O objetivo de "A" no nome é a associação dessa sub-rede à zona de disponibilidade "A", (abaixo)

!Ref VPC: As sub-redes devem existir dentro de um VPC, então é assim que associamos as duas. O VPC será criado primeiro e, em seguida, essa sub-rede será anexada a ele.

CidrBlock: Este CIDR é um sub-intervalo do 10.1.0.0/16 da VPC. Essencialmente, ele diz que esta sub-rede irá manter todos os endereços começando com 10.1.10. *. Isso também significa que a sub-rede terá apenas 256 endereços totais disponíveis (na verdade, 251 porque a AWS reserva 5 para seus próprios propósitos, veja esta explicação). Isso é um pouco pequeno, e estou cometendo uma boa quantidade de desperdício, de modo real no cenário mundial talvez deseje ajustar este valor. Para obter informações detalhadas sobre o CIDR, consulte o link acima na seção sobre VPC.

Availability Zone: cada sub-rede tem o escopo para uma zona de disponibilidade específica. Poderíamos simplesmente colocar hard-coded um valor como "us-east-1a", mas isso limita nosso modelo para N. Virginia. A melhor prática é construir modelos o mais agnóstico de região possível, então estamos determinando dinamicamente o AZ:

!GetAZs: Outra função interna do CloudFormation. Ele retorna uma lista de todas as zonas de disponibilidade na região atual (ou seja, onde quer que estejamos executando nossa pilha). Por exemplo, se estivermos em execução no Oregon, a lista retornada será {us-west-2a, us-west-2b, us-west-2c}. Criticamente, a lista sempre apresentará esses AZs na mesma ordem sempre que for chamado.

!Select: Esta função interna seleciona o índice especificado na lista específica. O 0 é o primeiro item da lista, 1 é o segundo, etc. Portanto, esta expressão está dizendo "me dê o primeiro AZ na lista". Isso resultará em "us-west-2a" se estiver em execução no Oregon, "us-east-1a" se estiver em execução em N. Virginia, etc. Conclusão: nossa sub-rede se associará automaticamente ao 'primeiro' AZ na região em que está sendo executado.

Tags: Como antes, queremos dar à nossa sub-rede um nome simples, mas nenhuma propriedade de nome está disponível. O uso de uma tag "Nome" fornece um nome usado em muitos, mas não em todos, locais em que essa sub-rede será listada ou exibida.

!Sub: Esta é a função substituta. Ele pega a string que segue e substitui dinamicamente qualquer valor encontrado dentro de tags ${}. Como o AWS::StackName renderizará "MyNetwork" em nosso exemplo, o nome da sub-rede resultante será "MyNetwork-Public-A"

Pode ser notado que isso está alcançando o mesmo resultado que a função !Join mostrada anteriormente. Eu fiz isso intencionalmente para mostrar duas técnicas alternativas. Qual é melhor? Isso é para você decidir. !Join geralmente funciona melhor se precisarmos inserir um caractere delimitador entre os itens sendo concatenados.

Mais sub-redes

Vamos fazer mais algumas sub-redes seguindo o padrão acima:

  PublicSubnetB:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.1.20.0/24
      AvailabilityZone: !Select [ 1, !GetAZs ]    # Obtenha o segundo AZ na lista
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-Public-B
  PrivateSubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.1.50.0/24
      AvailabilityZone: !Select [ 0, !GetAZs ]    # Obtenha o primeiro AZ na lista
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-Private-A
  PrivateSubnetB:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.1.60.0/24
      AvailabilityZone: !Select [ 1, !GetAZs ]    # Obtenha o segundo AZ na lista
      Tags:
      - Key: Name
        Value: !Sub ${AWS::StackName}-Private-B

As diferenças entre essas sub-redes são 1) os nomes lógicos 2) os nomes físicos (por meio dos valores de tag) 3) os intervalos CIDR (nada sobrepõe) e 4) a zona de disponibilidade atribuída. O resultado são quatro sub-redes, dois são "públicos", dois são "privados", dois na zona de disponibilidade "A", dois na zona de disponibilidade "B". Agradável.

É possível salvar e executar este modelo agora, se quiser. Eu recomendo desenvolvimento iterativo como este para verificar erros.

Adicionando Tabelas de Roteamento

Apesar de seus nomes, as sub-redes são apenas "públicas" ou "privadas" com base nas definições de suas tabelas de roteamento associadas. As tabelas de roteamento definem onde o tráfego pode ser roteado em uma sub-rede. O tópico é complexo e eu não cobrirei todos os detalhes aqui, veja esta informação sobre roteamento de sub-rede para obter informações completas.

   # Algumas tabelas de rotas para nossas sub-redes:
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: Public
  PublicRoute1:   # A tabela de rotas públicas têm roteamento direto para o IGW:
    Type: AWS::EC2::Route
    DependsOn: AttachGateway
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway  

PublicRouteTable: Outro nome arbitrário usado apenas na pilha, mas estamos tentando ser descritivos aqui. Tabelas de rota devem estar associadas a um VPC. Marcação de um nome de pilha prefixado para facilitar a identidade mais tarde.

PublicRoute1: Descreve uma entrada na tabela de rotas. A entrada está associada à tabela de rotas públicas (!Ref PublicRouteTable) e direciona qualquer tráfego com destino à Internet (DestinationCidrBlock: 0.0.0.0/0) para o gateway da Internet (!Ref InternetGateway). O que não é visível é a primeira entrada implícita incluída em todas as tabelas de roteamento chamadas de rota "local", todas as rotas para 10.1.0.0/16 permanecem dentro da VPC. Veja esta informação detalhada sobre rotas locais.

DependsOn: AttachGateway: Este é o bit crítico. Um erro ocorrerá se tentarmos criar uma entrada da tabela de rotas para um gateway não conectado. Para eliminar esse erro, informamos ao CloudFormation sobre essa dependência. Durante a criação da pilha, ele não tentará construir essa entrada na tabela de rotas até que o recurso AttachGateway seja criado. Ele também irá aguardar o VPC e o gateway da Internet, mas eles terminarão cedo.

Você ainda pode estar se perguntando por que o CloudFormation não consegue descobrir essa dependência por si só - é uma boa pergunta. O CloudFormation definitivamente sabe sobre quaisquer referências explícitas que forem feitas via !Ref ou !GetAtt (abordadas posteriormente), mas realmente não é possível saber se esses recursos irão funcionar quando forem referenciados. O CloudFormation está simplesmente fazendo chamadas de API em seu nome. Poderia encontrar o mesmo erro se usasse a CLI para criar uma entrada na tabela de rotas referenciando um gateway desanexado. Essa situação também ocorre em um ou dois outros locais, como quando uma instância do EC2 precisa se conectar a uma instância de RDS não concluída.

Salve seu trabalho e execute esse modelo, e ele deve ser bem-sucedido. É possível notar que a maior parte do tempo de criação é gasto pelo recurso AttachGateway.

Neste ponto, suas sub-redes públicas são utilizáveis. Se iniciar uma instância do EC2 com um endereço IP público em uma dessas sub-redes, ela poderá ser acessada pela Internet pública. Agora as sub-redes privadas ...

Tabela de Rota Privada

A tabela de rotas privadas será semelhante na maioria dos aspectos, exceto se não fizermos referência ao InternetGateway:

   # Aqui está uma tabela de rotas privadas:
  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
      - Key: Name
        Value: Private
  PrivateRoute1:           # A tabela de rota privada pode acessar a web via NAT (criada abaixo)
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      # Rotear o tráfego através do Gateway NAT:
      NatGatewayId: ???

NatGatewayId: Caso tenha imaginado que "???" não é um valor válido, está correto. Para explicar isso, primeiro precisamos fazer um rápido desvio e explicar esse conceito de NAT (sinta-se à vontade para pular adiante).

Que é esse negócio de NAT?

NAT significa conversão de endereço de rede. Para uma explicação completa, veja estas informações básicas sobre o NAT. Mas, em poucas palavras: não queremos instâncias dentro de nossas sub-redes privadas para serem acessadas pela internet pública. Mas queremos que essas instâncias possam iniciar conexões de saída, como downloads. Além disso, queremos que eles sejam capazes de fazer isso sem terem endereços IP públicos.

Um NAT facilita isso. Ele terá um endereço IP público e será associado a uma sub-rede pública. Instâncias particulares em sub-redes privadas poderão usar isso para iniciar conexões de saída. Mas o NAT não permitirá o contrário, uma parte da internet pública não poderá usar o NAT para se conectar às nossas instâncias privadas.

Existem duas opções básicas para NATting (é uma palavra?) na AWS, uma instância do EC2 configurada como um NAT ou um recurso da AWS relativamente novo chamado de NAT Gateway. Nós vamos usar o último.

Criando um NAT gateway

Adicione estas linhas para criar um NAT gateway:

 # A NAT Gateway:
 NATGateway:
   Type: AWS::EC2::NatGateway
   Properties:
     AllocationId: !GetAtt ElasticIPAddress.AllocationId
     SubnetId: !Ref PublicSubnetA
     Tags:
     - Key: Name
       Value: !Sub NAT-${AWS::StackName}
 ElasticIPAddress:
   Type: AWS::EC2::EIP
   Properties:
     Domain: VPC

O NAT Gateway está associado a uma das sub-redes públicas. Poderíamos (e provavelmente deveríamos) criar um NAT Gateway para cada uma de nossas sub-redes públicas, mas um único gateway mantém as coisas simples por enquanto. O nome é configurado dinamicamente, como antes.

AllocationId: o NAT requer um endereço IP público fixo. Isso é fornecido por um endereço Elastic IP, explicado abaixo.

!GetAtt: Outra função implícita descrita aqui. Isso faz referência a uma propriedade/atributo específico de outro recurso. Usando !Ref aqui não funcionaria, o recurso NAT Gateway precisa do allocationId do endereço Elastic IP, não do endereço em si. Vale a pena notar que a documentação mostra os atributos que podem ser "obtidos" para cada recurso, geralmente é um subconjunto das propriedades de um recurso.

Endereço Elastic IP: O EIP é um endereço IP público cujo valor permanece constante, independentemente do que está anexado. A explicação completa está aqui, mas basta dizer que é necessário para o nosso NAT.

Custo: Até agora, todos os recursos da nossa pilha são gratuitos. NAT Gateways não são. Eles são cobrados por hora e o quanto tráfego passa por eles, consulte Preços de VPC. Os EIPs são incomuns, pois não há custo para eles, desde que estejam sendo usados. A AWS cobra uma quantia nominal por hora quando não estão vinculados a recursos em execução, o que impede que os clientes os acumulem. Isso faz com que o custo dessa pilha seja em torno de 1 ou 2 centavos de dólar por hora, dependendo da região em que está concorrendo. Barato, mas certifique-se de excluir a pilha quando terminar para evitar que esses centavos sejam somados em dólares.

Agora que temos um NAT Gateway, podemos referenciá-lo em nossa tabela de rotas. A rota revisada deve ficar assim:

PrivateRoute1:             # A tabela de rota privada pode acessar a web via NAT (criada abaixo)
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      DestinationCidrBlock: 0.0.0.0/0
     # Rotear o tráfego através do Gateway NAT:
      NatGatewayId: !Ref NATGateway

Esta rota envia o tráfego com destino à Internet (DestinationCidrBlock: 0.0.0.0/0) para o NAT gateway (!Ref NATGateway). Por sua vez, o NAT enviará o Gateway da Internet devido à sua colocação na sub-rede pública. O tráfego de resposta recebido pelo NAT é encaminhado para a instância que fez a solicitação. Novamente, não é mostrado a rota local implícita descrita acima.

Anexando Tabelas de Rota a Sub-redes

A última peça, precisamos associar as sub-redes às suas respectivas tabelas de rotas. Os que chamamos de "públicos" precisam se conectar à tabela de rotas públicas e aos "particulares" à tabela de rotas particulares:

# Anexar as sub-redes públicas às tabelas de rotas públicas
 # e anexar as sub-redes privadas às tabelas de rotas privadas:     
  PublicSubnetARouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetA
      RouteTableId: !Ref PublicRouteTable
  PublicSubnetBRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnetB
      RouteTableId: !Ref PublicRouteTable
  PrivateSubnetARouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnetA
      RouteTableId: !Ref PrivateRouteTable
  PrivateSubnetBRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PrivateSubnetB
      RouteTableId: !Ref PrivateRouteTable

AWS::EC2::SubnetRouteTableAssociation: Os recursos de associação simplesmente associam sub-redes a tabelas de roteamento. Sem essas associações, suas sub-redes usarão uma tabela de rota "principal" associada ao VPC, que não faz parte do nosso modelo. Melhor ter tabelas de rotas e associações explícitas.

Parabéns! Neste ponto, temos um modelo 100% funcional do CloudFormation para criar um VPC típico. Salve-o para uso futuro, salve/expanda conforme necessário. Use-o para criar uma pilha usando as instruções descritas anteriormente, atualizando e excluindo-a como achar melhor.

Se desejar, pode testar seu VPC executando instâncias do EC2 dentro dele. Instâncias com endereços IP públicos anexados às sub-redes públicas estarão acessíveis a partir da Internet pública. As instâncias nas sub-redes privadas estarão inacessíveis da Internet pública, mas poderão fazer chamadas de saída. Eu vou encaminhá-lo para outros artigos para isso.

Lembre-se de que o NAT Gateway custará um centavo ou dois por hora, então é melhor excluir a pilha se não a estiver usando.

Conclusão

A capacidade de criar, modificar e excluir uma pilha de recursos por meio do CloudFormation é um exemplo poderoso do conceito Infraestrutura como Código. Não precisamos mais usar telas ou comandos para configurar manualmente a infraestrutura, agora que temos um sistema mais rápido, repetível e reutilizável.

No próximo artigo, mostraremos como tornar esse modelo mais flexível, usando parâmetros e condições para criar um número variável de sub-redes, tornando as sub-redes privadas opcionais e explorando outras opções de NAT. Também veremos como as saídas podem permitir que os recursos produzidos por essa pilha possam ser facilmente consumidos como entradas para outras pilhas.

Sobre o autor

A declaração de missão profissional de Ken Krueger é "orientar organizações e indivíduos para o sucesso comercial através da aplicação da tecnologia moderna". Ele tem mais de 30 anos de experiência como desenvolvedor de software, líder de projeto, gerente de projeto, mestre de scrum e instrutor, abrangendo as épocas de mainframe, cliente-servidor e web. Ele tem muita experiência em Java, Spring, SQL, desenvolvimento da Web, nuvem e tecnologias relacionadas. A experiência do setor inclui telecomunicações, finanças, imóveis, varejo, geração de energia, transporte, hospitalidade e desenvolvimento de software. Ele possui um MIS da University of South Florida, um MBA da Crummer Graduate School of Business na Rollins College, além de certificações Scrum Master, PMP, AWS e Java.

Você pode encontrar a parte 2 deste artigo aqui.

Avalie esse artigo

Relevância
Estilo/Redação

Conteúdo educacional

BT