Ansible – Primeiros passos e exemplos

O Ansible é uma ferramenta para orquestração, automação e configuração de servidores, é mantida pela Red-Hat e é open-source. Atualmente o Ansible é uma das ferramentas preferidas para automação da comunidade DevOps. A grande vantagem do Ansible em relação as outras ferramentas é que o Ansible não requer que nenhum agente seja instalado nos servidores, apenas com uma conexão SSH já é possível desfrutar de sua vasta funcionalidade.

Fora isso, ele vem com uma extensa biblioteca de módulos pra controlar praticamente tudo aquilo que você deseja automatizar. Por padrão ele é uma ferramenta push, mas pode ser configurado para trabalhar de forma pull.

Sua instalação é muito simples, na máquina host (Ubuntu 17.04 – zesty):

[root@notebook ~]# apt-get install -y ansible

Por padrão o pacote ansible instala os seguinte binários:

[root@notebook ~]# dpkg -L ansible | grep bin | grep -v python | tail -n7
/usr/bin/ansible
/usr/bin/ansible-console
/usr/bin/ansible-doc
/usr/bin/ansible-galaxy
/usr/bin/ansible-playbook
/usr/bin/ansible-pull
/usr/bin/ansible-vault

O básico de seu funcionamento e operação está dividido da seguinte maneira:

  • INVENTÁRIO;
  • MÓDULOS;
  • TAREFAS;
  • PLAYBOOKS

O inventário nada mais é que o arquivo que contém a lista de hosts que serão administrados pelo ansible;

Os módulos são os recursos que serão utilizados para controlar os hosts;

Tarefas, da tradução do inglês, são as tarefas que serão executadas, também podem ser executadas em modo ad-hoc, ou seja, são os comandos que desejamos executar no(s) host(s) uma única vez.

Os playbooks diferentemente das tarefas (tasks) são armazenados em arquivos no formato YAML (Yet Another Markup Language) que tem por finalidade armazenar as tarefas que irão ser executadas nos hosts. Sua sintaxe é bem simples e sua intenção é ser um modelo de configuração e não uma linguagem de programação.

Exemplos:
Para executar nossos primeiros comandos ansible precisamos primeiro fazer a conexão com os nossos hosts. Para este exemplo vamos utilizar o arquivo default do ansible /etc/ansible/hosts.

Esse arquivo é o nosso inventário e nele especificamos nossos hosts, ex:

[root@notebook ansible]# cat hosts
[hosts]
ansible2
ansible3
ansible4

Agora vamos fazer um simples teste de conexão “pingando” esses hosts utilizando o ansible:

[root@notebook ansible]# ansible all -m ping
ansible4 | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).\r\n",
"unreachable": true
}
ansible3 | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).\r\n",
"unreachable": true
}
ansible2 | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).\r\n",
"unreachable": true
}

FAMMMMMM – Deu erro…isso aconteceu porque não especificamos para o ansible o nome de usuário e senha para estes hosts. Exemplo:

[root@notebook ansible]# ansible all -m ping -u vagrant -k
SSH password:
ansible3 | SUCCESS => {
"changed": false,
"ping": "pong"
}
ansible2 | SUCCESS => {
"changed": false,
"ping": "pong"
}
ansible4 | SUCCESS => {
"changed": false,
"ping": "pong"
}

Como estou utilizando um ambiente com Vagrant o usuário é vagrant e senha vagrant.
Para não precisamos ter que ficarmos digitando nossa senha SSH a todo momento simplesmente fazemos uma troca de chaves, no nosso host:

[root@notebook ansible]# ssh-keygen

E copiamos a chave para nossos hosts:

[root@notebook ansible]# ssh-copy-id ansible2

E fazemos isto para os hosts ansible3 e ansible4.

Agora com as chaves inseridas:

[root@notebook ansible]# ansible all -m ping
ansible2 | SUCCESS => {
"changed": false,
"ping": "pong"
}
ansible3 | SUCCESS => {
"changed": false,
"ping": "pong"
}
ansible4 | SUCCESS => {
"changed": false,
"ping": "pong"
}

Um outro método também de controlar os hosts e armazenar as senhas de forma segura é utilizando o ansible-vault:

[root@notebook ansible]# mkdir group_vars

Nesse diretório é possível especificar variáveis para um grupo de hosts sem precisar alterar o arquivo de inventário. Podemos criar um arquivo de nome all.yml que toda a configuração será aplicada para os hosts no inventário, essa é a melhor maneira de gerenciar esses hosts que possuem a mesma senha SSH.

Para hosts com senhas diferentes podemos especificar no diretório hosts_vars, lembrando que esses diretórios precisam ser criados e precisam estar dentro do arquivo de inventário.

Para esse exemplo vou remover as chaves dessas máquinas de lab:

[root@notebook group_vars]# vim ~root/.ssh/known_hosts

Por padrão o Ansible já vem configurado para checar as chaves SSH, vamos descomentar essa diretiva para evitar o erro “msg: “Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support this.”

[root@notebook ansible]# vim ansible.cfg
...
...
...
# uncomment this to disable SSH key host checking
host_key_checking = False

E dentro do group_vars criamos o arquivo all.yml com o usuário e senha SSH dos nossos hosts:

[root@notebook group_vars]# cat all.yml
ansible_ssh_user: vagrant
ansible_ssh_pass: vagrant

E executamos o ping novamente em todos hosts:

[root@notebook group_vars]# ansible all -m ping
ansible4 | SUCCESS => {
"changed": false,
"ping": "pong"
}
ansible3 | SUCCESS => {
"changed": false,
"ping": "pong"
}
ansible2 | SUCCESS => {
"changed": false,
"ping": "pong"
}

Ele leu o conteúdo do nosso arquivo all.yml e executou o modulo ping, porém essa abordagem não é recomendada porque a senha está em texto plano.

Vamos criptografar nosso arquivo utilizando o ansible-vault:

[root@notebook group_vars]# ansible-vault encrypt all.yml
Vault password:
Encryption successful

Podemos verificar que a criptografia foi efetuada com sucesso utilizando AES256:

[root@notebook group_vars]# cat all.yml
$ANSIBLE_VAULT;1.1;AES256
30636234613364313364643062383031336537643262663238666137383334663437306234336432
3961663064383835346263346634663734313439343237390a636639333535663536333930336237
61633161363239393865326164376561363239623939343439656430333435353564383736363539
3061616663656235630a353666333132323566383265366130626232306261393532656439643137
33303666663132313038303165303735333661323334626532353734623432343163306436333637
34656665323864643964343436346437626364313639656161643261356330643166333632393034
653933393465363639623334623832316565

Agora se tentarmos pingar novamente nossos hosts com o mesmo comando:

[root@notebook ansible]# ansible all -m ping
ERROR! Decryption failed on /etc/ansible/group_vars/all.yml

FAMMMMMM – Precisamos passar para o comando o seguinte argumento:

[root@notebook ansible]# ansible all -m ping --ask-vault-pass
Vault password:
ansible2 | SUCCESS => {
"changed": false,
"ping": "pong"
}
ansible3 | SUCCESS => {
"changed": false,
"ping": "pong"
}
ansible4 | SUCCESS => {
"changed": false,
"ping": "pong"
}

Para editarmos nosso arquivo criptografado:

[root@notebook group_vars]# ansible-vault edit all.yml

Para listarmos nossos hosts no inventário:

[root@notebook ansible]# ansible all --list-hosts --ask-vault-pass
Vault password:
hosts (3):
ansible2
ansible3
ansible4

EXEMPLOS

Para ilustrar a praticidade e simplicidade de funcionamento do Ansible vamos criar um playbook de exemplo para desabilitar o SeLinux, remover o Firewalld, ajustar o timezone, o idioma, o teclado, e instalar e configurar o NTP em servidores CentOS7.

[root@notebook tadeu]# ansible-galaxy init exemplo
- exemplo was created successfully

Verificamos a estrutura:

[root@notebook tadeu]# cd exemplo/
[root@notebook exemplo]# tree . 
├── defaults
│   └── main.yml
├── files
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── README.md
├── tasks
│   └── main.yml
├── templates
├── tests
│   ├── inventory
│   └── test.yml
└── vars
└── main.yml

No nosso diretório tasks vamos criar nosso arquivo de inventário e inserir nossos hosts, lembrando que também podemos definir nossos hosts no arquivo /etc/ansible/hosts.

[root@notebook tasks]# touch hosts
[root@notebook tasks]# cat hosts
[exemplo]
ansible1
ansible2
ansible3

Com as chaves SSH trocadas vamos efetuar um teste de conectividade com os hosts:

[root@notebook tasks]# ansible all -i hosts -m ping
ansible2 | SUCCESS => {
"changed": false,
"ping": "pong"
}
ansible1 | SUCCESS => {
"changed": false,
"ping": "pong"
}
ansible3 | SUCCESS => {
"changed": false,
"ping": "pong"
}

Tudo OK.

Agora no nosso diretório tasks vamos criar um playbook de nome ajusta.yml:

[root@notebook exemplo]# cd tasks/
[root@notebook tasks]# touch ajusta.yml
[root@notebook tasks]# ls
ajusta.yml main.yml

Com o seguinte conteúdo:

Agora no nosso main.yml:

[root@notebook tasks]# cat main.yml
---
# tasks file for exemplo
- name: "Hosts"
  hosts: [exemplo]
  become: true
  tasks:
  - include: ajusta.yml
    tags: ajusta

E executamos o playbook:

[root@notebook tasks]# ansible-playbook -i hosts main.yml

Se tudo ocorreu bem teremos no final esse output:

...
...
...
PLAY RECAP *********************************************************************
ansible1 : ok=11 changed=8 unreachable=0 failed=0
ansible2 : ok=11 changed=8 unreachable=0 failed=0
ansible3 : ok=11 changed=8 unreachable=0 failed=0
[root@notebook tasks]#

Referências:
https://www.ansible.com/
http://docs.ansible.com/