Como provisionar EC2 com Ansible - Mateus Müller
Como provisionar EC2 com Ansible
Oi pessoal! Como todos sabem, estou estudando forte para a certificação AWS Solutions Architect e de vez em quando estou brincando com Terraform e Ansible.
Durante os estudos descobri que o Ansible também pode ser usado (não que seja recomendado) para provisionar infraestrutura e já fiz o teste diretamente com as instâncias EC2 da Amazon e decidi escrever mais sobre isso hoje.
É importante que você tenha claro alguns conceitos que vou usar, mas não se preocupe, vou deixar alguns links complementares para vocês estudarem.
Ter a Access Key e a Secret Key da conta AWS
Entender autenticação usando chaves assimétricas (se não souber veja esse vídeo)
Confortável com a linha de comando
Saber o que é o Ansible e como pode ser usado (veja esse vídeo).
Para trabalhar com Ansible, você precisa do Ansible (obviamente) e um ambiente com Python. Além disso, você precisa da biblioteca Boto 3, que é basicamente o SDK da AWS para Python. Essa biblioteca que vai permitir a comunicação com a AWS.
$ pip3 install boto boto3 ansible
Você também pode instalar o Ansible pelo apt se quiser.
Você precisa de um par de chaves que são usados para se autenticar nas instâncias.
Em poucas palavras, sua chave pública será colocada dentro das EC2 e você poderá se autenticar com a chave privada sem precisar digitar usuário e senha.
Crie um diretório qualquer para organizar o seu projeto dentro dele. Este link mostra as boas práticas para organizar um projeto com Ansible.
$ mkdir -p $HOME/projeto-ec2/groups_vars/all/
$ cd $HOME/projeto-ec2/
Usando ansible-vault
O Vault já é um produto conhecido por prover segurança para o gerenciamento de chaves. O Ansible já trás um componente chamado de ansible-vault que faz isso também.
Bom, vamos estar lidando com chaves estritamente secretas e que dão acesso diretamente para a conta da AWS. É claro que queremos proteger isso, certo?
Vamos criar um arquivo com as chaves e encriptar ele.
$ echo "senha" >> vault.pass
$ ansible-vault create group_vars/all/pass.yml --vault-password-file vault.pass
Basicamente, ele vai encriptar o arquivo pass.yaml e você só pode ler quando digitar a senha que está no arquivo vault.pass. Mas, como somos preguiçosos, já colocamos a senha em um arquivo pra não precisar digitar toda a vez!
É claro que em um ambiente real, você não vai colocar em um arquivo. E mesmo que colocasse, pode utilizar alguma ferramenta para gerar uma senha mais segura.
$ openssl rand -base64 2048 > vault.pass
Depois de gerar a senha segura (se preferir), você precisa encriptar o arquivo de senha de novo, ok?
Assim que executar o comando de create, ele deve abrir um editor de texto, seja o Nano ou Vim para você editar o arquivo e adicionar as variáveis.
O editor virá da variável ambiente EDITOR. Se não funcionar, exporte ela.
Quando estiver editando o arquivo, adicione algo como:
ec2_access_key: "sua_access_key"
ec2_secret_key: "sua_secret_key"
Depois é só salvar e sair.
Agora é hora de editar o playbook.yaml.
Vou explicar por partes o arquivo e por último vou postar o arquivo final.
O primeiro passo é garantir que as informações estão apontando para localhost. Isso é algo que me confundia muito. Basicamente, estamos dizendo para o Ansible… "Cara, usa a Boto 3 que eu instalei localmente para se conectar na AWS, não tem nada externo aqui".
O gather_facts definido para False não vai buscar informações antes de executar (até porque elas nem existem). Vai simplesmente executar. Esse módulo pode te ajudar a entender mais sobre isso.
Vamos declarar também uma sessão de variáveis. Lembre-se que as variáveis que declaramos dentro do group_vars já serão adicionadas automaticamente.
Isso é mais para uma perspectiva de reuso de código.
Por exemplo, como tudo será criado na mesma região, se um dia precisar mudar, não preciso mudar manualmente em cada item, apenas mudo na variável.
Sem contar que as variáveis ficam no começo do código, que facilita muito.
image: ami-04ac550b78324f651 # ubuntu 16.04
id: "mm"
sec_group: "{{ id }}-sec"
Agora começamos a parte de tasks que é justamente o que deve ser executado.
Primeiro, vamos criar um par de chaves na AWS usando a chave que criamos nos passos iniciais.
Note que basicamente tudo vem das variáveis que criamos na parte acima e também as variáveis que estão encriptadas no outro arquivo.
O key_material vai buscar dinamicamente a minha chave pública, então caso eu mude, ele vai pegar sempre a nova.
- name: Upload public key to AWS
name: "{{ id }}_key"
key_material: "{{ lookup('file', '/home/mateus/.ssh/id_rsa.pub') }}"
Depois de uma chave, precisamos de um security group que permita o acesso via SSH.
Note que ao final estou criando um register que é uma variável que armazena a saída do módulo. Cada módulo possui uma saída. Vou deixar uns links complementares ao final do artigo.
description: "Sec group for app {{ id }}"
register: sec_group_id
E por último, usamos os recursos criados acima para subir o EC2.
key_name: "{{ id }}_key"
id: "{{ id }}_instance"
group_id: "{{ sec_group_id.group_id }}"
O arquivo final ficou:
# AWS playbook
$ ansible-playbook playbook.yml --vault-password-file vault.pass
Aí é só conectar:
$ ssh user@dns-da-instancia
E boom! Olha na AWS e deve ter criado o que você precisa já.
Vou deixar mais algumas recomendações abaixo:
Se quiser debugar, por exemplo, receber o DNS da instância já na saída do ansible-playbook, você precisa deste módulo