Provisionando Instâncias EC2 na AWS com Ansible

Provisionando Instâncias EC2 na AWS com Ansible

Introdução

O Ansible é uma ferramenta extremamente poderosa, podendo fazer todo a orquestração da sua infraestrutura. Porém, o que mais me chama atenção nele, é que podemos provisionar todo nosso ambiente na nuvem primeiramente e já configurá-lo em seguida.

Aqui vou demonstrar como conseguimos provisionar a seguinte infraestrutura e configurá-la com apenas uma playbook.

Iremos provisionar 4 instâncias, uma que será nosso LoadBalancer (LB01), duas que serão nossos servidores web (APP01 e APP02) e por fim o nosso banco de dados (Database).

Sem mais delongas vamos provisionar nosso ambiente:

Pré-requisitos:
Ansible, Python 2.7, Pip, Pip modules (boto, botocore), EC2 Key Pair, AWS Access key e Secret Key

Começamos baixando nossos arquivos

git clone https://github.com/guilhermewolf/aws-loadbalancer-ansible.git

Vamos editar dois arquivos

group_vars/all.yml
│
└──key_name: lb-ansible

Altere o nome da chave com a que você criou na AWS

inventory/hosts
│
└──ansible_ssh_private_key_file= ~/lb-ansible.pem

Altere para o local onde está a chave na sua máquina

Vamos colocar nossas credenciais da AWS como variáveis de ambiente

export AWS_ACCESS_KEY_ID=#SUA ACESSKEY#
export AWS_SECRET_ACCESS_KEY=#SUA SECRET KEY#

All set, let’s go!

ansible-playbook playbook.yml

Agora veja toda a infraestrutura ser provisionada diante de seus olhos.

Mas como tudo isso funciona?

O Ansible precisa primeiramente de uma relação de confiança tanto com a AWS quanto com as máquinas que foram provisionadas, por isso provemos para ele as credenciais da AWS e a chave privada que criamos para as instâncias. O Ansible precisa de um inventário, que é composto pelo endereço IP das máquinas das quais ele irá configurar, porém nossa playbook primeiramente irá criar as máquinas e só depois de criadas teremos os IP’s delas, precisamos então criar um inventário que seja dinâmico – por nossa sorte o próprio Ansible oferece um script em Python no Github para facilitar nossa vida, onde ele acessa a AWS pelas credenciais que oferecemos via variável de ambiente e lista nossas instâncias e seus devidos IP’s.

Essa é a estrutura de diretórios da nossa playbook.

.
├── group_vars
├── inventory
├── playbooks
└── roles
    ├── apache2
    ├── aws
    ├── demo_app
    ├── mysql
    └── nginx

O arquivo playboook.yml contêm 8 etapas, em cada etapa ele irá chamar alguma role que está dentro do diretório roles.

Nossa playbook se inicia chamando o diretório roles/aws e criando as instâncias na AWS com o módulo ec2_instance, para o módulo oferecemos alguns itens, as credenciais da AWS, um nome para a máquina, a chave que ela irá usar, o security group, tipo da instância, id da imagem a ser usada e a região que ela irá ficar.

- name: Provision Loadbalancer
  ec2_instance:
    aws_access_key: "{{ aws_access_key }}"
    aws_secret_key: "{{ aws_secret_key }}"
    name: lb01
    key_name: "{{ key_name }}"
    security_group: "{{ security_group }}"
    instance_type: "{{ instance_type }}"
    image_id: "{{ image }}"
    wait: true
    state: running
    region: "{{ region }}"

Fazemos isso para as 4 máquinas, porém nosso inventário iniciou na playbook sem nenhum IP dentro dele, pois as máquinas ainda não existiam, agora precisamos atualizar nosso inventário para receber esses IP’s

- name: refresh_inventory
  hosts: localhost
  gather_facts: False
  tasks:
    - meta: refresh_inventory

Agora com nosso inventário populado aguardamos nossas máquinas ficarem 100% provisionadas, iremos fazer isso aguardando que o Ansible consiga fazer uma conexão SSH nelas.

- name: Wait vms go up
  hosts: all
  user: ubuntu
  gather_facts: False
  tasks:
    - wait_for_connection:
        timeout: 300

Quando as máquinas são provisionadas na AWS um script é chamado pelo CloudInit para fazer uma configuração inicial das máquinas, adicionando alguns pacotes essenciais para a máquinas, nós precisamos garantir que esse script tenha encerrado a sua execução antes de começarmos a configurar as máquinas

- name: Wait for cloud init to finish
  hosts: cluster
  gather_facts: False
  become: yes
  tasks:
    - cloud_init_data_facts:
        filter: status
      register: res
      until: "res.cloud_init_data_facts.status.v1.stage is defined and not res.cloud_init_data_facts.status.v1.stage"
      retries: 50
      delay: 5

Após concluir esse estágio, daremos início as configurações das máquinas. Primeiramente, vamos atualizar a lista de pacotes disponíveis nos repositórios:

- hosts: all
  become: true
  user: ubuntu
  gather_facts: true
  tasks:
    - name: update apt cache
      apt: update_cache=yes cache_valid_time=86400

Agora vamos configurar cada máquina individualmente, começaremos pela database, dentro de nossa estrutura de diretórios temos a seguinte pasta:

roles
  ├── apache2
  │   ├── handlers
  │   ├── tasks
  │   └── tests
  ├── aws
  │   └── tasks
  ├── demo_app
  │   ├── files
  │   ├── handlers
  │   ├── tasks
  │   ├── templates
  │   └── tests
  ├── mysql
  │   ├── handlers
  │   ├── tasks
  │   └── tests
  └── nginx
      ├── defaults
      ├── handlers
      ├── tasks
      ├── templates
      └── test

Dentro de cada pasta das roles temos algumas outras dentro, a mais importante delas é a tasks, que será onde encontramos quais etapas de configurações serão feitas em nossas máquinas.

A pasta handlers contém ações a serem executadas caso sejam chamadas no arquivo main.yml dentro da pasta tasks (como por exemplo o comando systemctl restart caso alguma configuração seja feita).

Na pasta files podemos deixar arquivos que desejamos enviar para as máquinas remotas, em nosso exemplo temos os arquivos de configuração e o site em python. No arquivo main.yml dentro de tasks passamos o diretório do nosso arquivo e o destino dele na máquina remota.

 - name: copy demo app source
   copy: src=demo/app/ dest=/var/www/demo mode=0755

Dentro de templates podemos encontrar arquivos com o formato .j2 que é uma linguagem de templates para o python. Podemos criar arquivos de configurações para ele e ao invés de colocarmos os valores específicos que precisaremos, deixamos variáveis que o Ansible utiliza para serem populadas na hora de escrever o arquivo na máquina remota. Como é o caso em roles/demo_app/templates/demo.wsgi.j2

import os
os.environ['DATABASE_URI'] = 'mysql://{{ db_user }}:{{ db_pass }}@{{ groups.database[0] }}/{{ db_name }}'

Passamos as variáveis dbuser, dbpass e dbname para serem escritas no arquivo, assim podemos alterar apenas as variáveis que o Ansible utiliza no arquivo groupvars e todo nosso parque será provisionado corretamente.

Explore essa playbook e a utilize para criar suas futuras playbooks. Ela irá servir tanto para aprender a configurar seu parque de máquinas, tanto para aprender a provisionamento de instâncias na AWS.

 

Até mais pessoal!

CURSOSCONSULTORIA    CONTATO

Anterior Qual o nível de maturidade DevOps da sua empresa?
Próxima Recrutamento e seleção rápida e certeira usando a Rankdone

About author

Você pode gostar também

DevOps

Web Scraping: Python, Selenium e BeautifulSoup

Muita gente na internet tem dúvida de como fazer robôs que buscam coisas em sites, baixam conteúdo ou simplesmente executam ações para testar alguma funcionalidade do site, sistema ou algo

DevOps

Repositórios locais com Nexus Sonatype

Administradores de sistemas, sempre que operam em ambiente GNU/Linux,  realizam a instalação de pacotes pré-compilados localizados em repositórios remotos. Para distribuições baseadas em Red Hat, fazemos uso de repositórios Yum

DevOps

Novidades para build de aplicações no Jenkins

Jenkins é um servidor de automação, independente e de código aberto, usado para automatizar todos os tipos de tarefas relacionadas à criação, teste e distribuição ou implementação de software. Recentemente,