Aprendendo Ansible

Estou usando esta publicação como revisão do conhecimento adquirido e de forma introdutória, para quem quiser aprender a usar o Ansible. 😁👍

👉 Utilize a seção CONTEÚDOS para navegar diretamente ao ponto que desejas estudar.

👉 A parte prática só funcionará se você ler e interpretar o que está escrito. Caso sinta dificuldade ou em dúvida, me manda um comentário que deixarei mais claro nesta aula e respondo seu comentário também.

👉 A documentação tem praticamente tudo que você precisa e tudo que não estará aqui, também.

🤷 Qual a vantagem de aprender aqui? 👉 Farei da maneira mais didática possível, seguindo uma ordem e em pt-BR.

Segundo o próprio site da documentação (e em tradução livre):

  • Ansible é um projeto comunitário de código aberto patrocinado pela Red Hat, e é o jeito mais simples de automatizar a TI.
  • Ansible é a única linguagem de automação que é usada por toda a equipe de TI, do administrador de sistemas e redes aos desenvolvedores e gerentes.

Com serviços e produtos. Alguns exemplos:

O Ansible é de código aberto e sob a licença GPL3. Em outras palavras, é software livre… ou seja, você pode modificá-lo a vontade, não há coleta seus dados para nenhum fim e nunca será pago (mesmo se você alterá-lo, não pode cobrar pela sua versão do ansible).

Você pode encontrar o código fonte e outros detalhes aqui.

Se não possuir interesse em pagar pela licença pelo motivo que for, você pode utilizar o projeto AWX 😀. Este utiliza a licença Apache, que é um pouco mais permissiva que a GPL3.

Há dois pontos de pré-requisitos. No nó de controle (onde ficará instalado o Ansible) e nos nós gerenciáveis (dispositivos fins que queira automatizar).

Precisa de Python 3.8+ instalado. Isso vale para sistema operacional com kernel linux (CentOS, Debian, Red Hat, …), macOS, qualquer BSD e via WSL do Windows.

Neste curso, utilizaremos o pip por ser recomendado pela documentação (é onde tem a versão mais recente e com correções mais rápidas)

É necessário ter Python 2.6+ ou 3.5+, SSH e SFTP ou SCP.

Nota
No momento, a última versão do ansible é DoIt NOVO | 5.7 e o ansible-core é DoIt NOVO | 2.12.5.

Se seu sistema não possui o pip para instalar (via apt, yum, brew, etc…), segundo a documentação oficial, pode-se instalar, com seu usuário sem privilégios (para que não faça instalação globalmente), da seguinte forma:

1
2
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python get-pip.py --user

Usando este método, ao invés de executar apenas pip (ou pip3), você deverá executar python -m pip. Seguirei explicando com o comando simplificado.

Primeiro atualize o seu pip3, com seu usuário sem privilégios, utilizando o comando (o -U é de upgrade):

1
pip3 install -U pip

Agora instale o Ansible com o comando:

1
pip3 install -U ansible

Caso você tenha um Ansible muito antigo instalado via pip e apresentou erro ao fazer a instalação, desinstale-o e instale a nova versão, visto que atualizações de pacotes muito antigos podem causar problemas:

1
2
pip3 uninstall ansible
pip3 install ansible

Vamos precisar do virtualbox para hospedar as máquinas criadas pelo Vagrant.

Para baixar e instalar, utilize seu gerenciador de pacotes ou siga as orientações em https://www.virtualbox.org/wiki/Downloads.

Acesse https://www.vagrantup.com/downloads, baixe e instale o Vagrant conforme seu sistema operacional.

Vamos precisar dele para montar nosso laboratório.

Baixe este arquivo Vagrantfile e coloque na pasta que você usará para seus estudos de Ansible.

O conteúdo deste arquivo é:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/env ruby

maquinas = {
  "maquina01" => "192.168.56.10",
  "maquina02" => "192.168.56.20",
  "maquina03" => "192.168.56.30"
}


Vagrant.configure("2") do |config|
  maquinas.each do |name, ip|
    config.vm.define name do |maquina|
      maquina.vm.box = "bento/debian-11"
      maquina.vm.hostname = "%s" % name
      maquina.vm.network :private_network, ip: ip
      maquina.vm.provider "virtualbox" do |vb|
          vb.name = name
          vb.customize ["modifyvm", :id, "--memory", 384]
      end
    end
  end
end

Em outras palavras, levantará três VMs para ser o lab em seu virtualbox.

Informação
As bento boxes são indicadas pela própria Hashicorp (que é quem desenvolve e mantém o Vagrant).

Para levantar seu ambiente Vagrant, vá para a pasta onde está o Vagrantfile e execute vagrant up. Basta esperar e tudo será configurado e estará funcionando.

Informação
O usuário e a senha padrão são: vagrant

Primeiramente gere sua chave pública com o comando:

1
ssh-keygen -t rsa

Para simplificar/facilitar, não forneça uma senha nessa etapa (deixe para usar a senha nessa etapa quando for pra valer).

Para instalar as chaves nos hosts, execute o seguinte comando:

1
for i in 192.168.56.10 192.168.56.20 192.168.56.30; do ssh-copy-id vagrant@$i; done

E, ao perguntar a senha, forneça a senha padrão do Vagrant (três vezes 👀, um para cada host).

Agora o laboratório está pronto para começarmos nossas atividades.

Basicamente são os tipos de arquivo que você terá de lidar no ansible e que irei abordar aqui no curso.

Usado no arquivo de hosts e configuração do Ansible.

Observe este exemplo para entender melhor.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# Esta linha está comentada e é possível com "#" apenas no início da linha
; Esta linha também está comentada e é possível com ";" no início e em linhas com algum conteúdo

arquivo=caminho/arquivo.txt             ; A variável arquivo recebe (com o sinal de igua "=") o valor "caminho/arquivo.txt"
outroarquivo=caminho/outroarquivo.txt   ; Só é possível comentar na mesma linha se usando o ";". Comentar com "#" não funciona

[novasecao]                             ; Usando [] é possível criar seções dentro do mesmo arquivo INI
porta=1080                              ; Foi definido que a variável porta tem o valor 1080 e está dentro da seção novasecao

[outrasecao]                            ; Aqui começa a outrasecao, sendo o fim da novasecao
protocolo=tcp                           ; Foi definido que a variável protocolo tem o valor tcp e está dentro da seção outrasecao

[grupodesecoes:childrens]               ; É assim que se agrupa seções, colocando o nome da seção maior seguido de ":childrens"
novasecao
outrasecao

Exemplo 1:

1
2
3
4
5
6
[debian]
maquina01

[centos]
maquina02
maquina03

Exemplo 2, escolhendo sequências:

1
2
3
4
5
[debian]
maquina01

[centos]
maquina0[2:3]         ; Funciona como um regex que junta do número 2 ao 3. Se fosse de 1 a 3, seria [1:3]

Exemplo 3, agrupando seções:

1
2
3
4
5
6
7
8
9
[debian]
maquina01

[centos]
maquina0[2:3]

[linux:childrens]     ; Agrupando as seções debian e centos, podendo ser invocados ao usar "linux"
debian
centos

Caso você não tenha definido uma configuração de acesso em seu ~/.ssh/config, você pode fazer assim como no arquivo ini do Exemplo 4:

1
2
[debian]
maquina01 ansible_host=192.168.56.10 ansible_port=22

Há várias variáveis que podem ser usadas no arquivo de hosts, cabendo vários exemplos e, inclusive, pode ser usado yaml ao invés de ini. Mais detalhes na documentação oficial sobre inventário.

Como já explicado anteriormente, utiliza o padrão INI.

Quanto ao exemplo de arquivo de configuração, algo rápido, seria assim:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[defaults]
inventory = hosts                 ; onde se encontra o arquivo de inventário
host_key_checking = False         ; para não ter de aceitar o certificado do primeiro ssh
ask_pass = True                   ; para perguntar a senha do usuário do ssh
nocows = True                     ; não exibir vacas... é sério... não é brincadeira
log_path = ./logs/ansible.log     ; caminho para gravação do log de atividades do ansible

[privilege_escalation]
become = True                     ; se vai usar sudo
become_ask_pass = True            ; perguntar a senha de sudo

Para mais parâmetros de configuração, acesse aqui na documentação.

O arquivo yaml normalmente é utilizado na escrita dos playbooks. O arquivo segue o seguinte padrão (versão traduzida de Aprenda Yaml em Y minutos):

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
---  # início do documento

# Comentários em YAML são como este.

###################
# TIPOS ESCALARES #
###################

# Nosso objeto raiz (que continua por todo o documento) será um mapa,
# o que equivale a um dicionário, hash ou objeto em outras linguagens.
chave: valor
outra_chave: Outro valor vai aqui.
u_valor_numerico: 100
notacao_cientifica: 1e+12
boleano: true
valor_nulo: null
chave com espaco: valor
# Observe que strings não precisam de aspas. Porém, elas podem ter.
porem: 'Uma string, entre aspas.'
'Chaves podem estar entre aspas tambem.': "É útil se você quiser colocar um ':' na sua chave."
aspas simples: 'possuem ''um'' padrão de escape'
aspas duplas: "possuem vários: \", \0, \t, \u263A, \x0d\x0a == \r\n, e mais."
# Caracteres UTF-8/16/32 precisam ser codificados
Superscript dois: \u00B2

# Seqüências de várias linhas podem ser escritas como um 'bloco literal' (utilizando |),
# ou em um 'bloco compacto' (utilizando '>').
bloco_literal: |
  Todo esse bloco de texto será o valor da chave 'bloco_literal',
  preservando a quebra de com linhas.

  O literal continua até 'des-indentar', e a primeira identação é 
  removida.

    Quaisquer linhas que são 'mais identadas' mantém o resto de suas identações - 
    estas linhas serão identadas com 4 espaços.  
estilo_compacto: >
  Todo esse bloco de texto será o valor de 'estilo_compacto', mas esta
  vez, todas as novas linhas serão substituídas com espaço simples.

  Linhas em branco, como acima, são convertidas em um carater de nova linha.

    Linhas 'mais-indentadas' mantém suas novas linhas também -
    este texto irá aparecer em duas linhas.  

####################
# TIPOS DE COLEÇÃO #
####################

# Texto aninhado é conseguido através de identação.
um_mapa_aninhado:
  chave: valor
  outra_chave: Outro valor
  outro_mapa_aninhado:
    ola: ola

# Mapas não tem que ter chaves com string.
0.25: uma chave com valor flutuante

# As chaves podem ser também objetos multi linhas, utilizando ? para indicar o começo de uma chave.
? |
  Esta é uma chave
  que tem várias linhas  
: e este é o seu valor

# YAML também permite o mapeamento entre sequências com a sintaxe chave complexa
# Alguns analisadores de linguagem de programação podem reclamar
# Um exemplo
? - Manchester United
  - Real Madrid
: [2001-01-01, 2002-02-02]

# Sequências (equivalente a listas ou arrays) semelhante a isso:
uma_sequencia:
  - Item 1
  - Item 2
  - 0.5 # sequencias podem conter tipos diferentes.
  - Item 4
  - chave: valor
    outra_chave: outro_valor
  -
    - Esta é uma sequencia
    - dentro de outra sequencia
  - - - Indicadores de sequência aninhadas
      - podem ser recolhidas

# Como YAML é um super conjunto de JSON, você também pode escrever mapas JSON de estilo e
# sequências:
mapa_json: {"chave": "valor"}
json_seq: [3, 2, 1, "decolar"]
e aspas são opcionais: {chave: [3, 2, 1, decolar]}

###########################
# RECURSOS EXTRAS DO YAML #
###########################

# YAML também tem um recurso útil chamado "âncoras", que permitem que você facilmente duplique
# conteúdo em seu documento. Ambas estas chaves terão o mesmo valor:
conteudo_ancora: &nome_ancora Essa string irá aparecer como o valor de duas chaves.
outra_ancora: *nome_ancora

# Âncoras podem ser usadas para duplicar/herdar propriedades
base: &base
  name: Todos possuem o mesmo nome

# O regexp << é chamado Mesclar o Tipo Chave Independente-de-Idioma. É usado para
# indicar que todas as chaves de um ou mais mapas específicos devam ser inseridos
# no mapa atual.

foo:
  <<: *base
  idade: 10

bar:
  <<: *base
  idade: 20

# foo e bar terão o mesmo nome: Todos possuem o mesmo nome

# YAML também tem tags, que você pode usar para declarar explicitamente os tipos.
string_explicita: !!str 0.5
# Alguns analisadores implementam tags específicas de linguagem, como este para Python de
# Tipo de número complexo.
numero_complexo_em_python: !!python/complex 1+2j

# Podemos utilizar chaves YAML complexas com tags específicas de linguagem
? !!python/tuple [5, 7]
: Fifty Seven
# Seria {(5, 7): 'Fifty Seven'} em Python

####################
# YAML TIPOS EXTRA #
####################

# Strings e números não são os únicos que escalares YAML pode entender.
# Data e 'data e hora' literais no formato ISO também são analisados.
datetime: 2001-12-15T02:59:43.1Z
datetime_com_espaços: 2001-12-14 21:59:43.10 -5
date: 2002-12-14

# A tag !!binary indica que a string é na verdade um base64-encoded (codificado)
# representação de um blob binário.
gif_file: !!binary |
  R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5
  OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+
  +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC
  AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs=  

# YAML também tem um tipo de conjunto, o que se parece com isso:
conjunto:
  ? item1
  ? item2
  ? item3
ou: {item1, item2, item3}

# Como Python, são apenas conjuntos de mapas com valores nulos; o acima é equivalente a:
conjunto2:
  item1: null
  item2: null
  item3: null

...  # fim do documento
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
- name: Atualizar apache2
  hosts: web

  tasks:
  - name: Garantindo que o apache2 está atualizado
    apt:
      name: apache2
      state: latest
  - name: Escrevendo o arquivo de configuração do apache2
    template:
      src: ./templates/apache.j2
      dest: /etc/apache/apache.conf

Há considerações de segurança relacionada ao ansible.cfg.

  • Não deixá-lo numa pasta com permissão de escrita para qualquer um. (um usuário poderia substituir o arquivo e rodar código malicioso localmente e remotamente)
  • Se o arquivo estiver dentro de um Vagrant ou WSL, montar a partição com as configurações do Ansible limitadas a certos usuários e grupos.

As configurações no Ansible segue uma sequência de prioridades na busca das informações de configuração. A ordem de prioridade da maior para a menor é a seguinte:

  • ANSIBLE_CONFIG (variável de ambiente se definida)
  • ansible.cfg (no diretório atual, precisa de permissão de escrita apenas para o usuário, chmod 644 ansible.cfg)
  • ~/.ansible.cfg (na pasta do usuário, precisa de permissão de escrita apenas para o usuário, chmod 644 ~/.ansible.cfg)
  • /etc/ansible/ansible.cfg

Já apresentei um arquivo de configuração do Ansible, anteriormente, mas agora será apresentado o que utilizaremos no nosso laboratório.

Na pasta em que você reservou para o Ansible, utilize o seguinte arquivo ansible.cfg:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[defaults]
inventory = hosts                     ; arquivo padrão de hosts
host_key_checking = False             ; para não dar problema nos hosts que não foram feitos os primeiros acessos ssh, que pede para aceitar o certificado
ask_pass = True                       ; perguntar a senha de acesso
nocows = True                         ; não gosto das vacas aparecendo
deprecation_warnings = False          ; podem aparecer alguns avisos de deprecation
forks = 10                            ; paralelizando a coisa quando houver vários hosts
log_path = ./logs/ansible.log         ; gravar um log em arquivo

[privilege_escalation]
become = True                         ; tornar-se root
become_ask_pass = True                ; pedir senha para virar root

Se deseja saber TODAS as opções possíveis, acesse o link da documentação.

No laboratório, utilizaremos as seguintes configurações (conforme o arquivo do Vagrant):

1
2
3
maquina01 ansible_host=192.168.56.10 ansible_user=vagrant
maquina02 ansible_host=192.168.56.20 ansible_user=vagrant
maquina03 ansible_host=192.168.56.30 ansible_user=vagrant

Perceba que foram passados os parâmetros junto aos hosts. Poderíamos preencher, também, da seguinte forma, separando em arquivos.

Observação

Fica a seu critério escolher como configurará seus hosts, seja separando ou junto com suas respectivas variáveis.

Nota

Arquivo de hosts:

1
2
3
4
[laboratorio]
maquina01
maquina02
maquina03

No arquivo ./group_vars/laboratorio para definir o que é comum ao grupo de nome laboratorio:

1
ansible_user=vagrant

No arquivo ./host_vars/maquina01 para definir o que é exclusivo ao host de nome maquina01:

1
ansible_host=192.168.56.10

No arquivo ./host_vars/maquina02 para definir o que é exclusivo ao host de nome maquina02:

1
ansible_host=192.168.56.20

No arquivo ./host_vars/maquina03 para definir o que é exclusivo ao host de nome maquina03:

1
ansible_host=192.168.56.30

Vamos instalar o servidor web/proxy nginx na maquina01.

Crie o arquivo nginx.yml com o seguinte conteúdo:

1
2
3
4
5
6
7
- hosts: maquina01
  tasks:
    - name: Instalar o nginx
      apt:
        pkg: nginx
        state: present
        update_cache: true

Com o arquivo ansible.cfg e hosts criados como orientado anteriormente, execute no terminal:

1
ansible-playbook nginx.yml

Pronto! Será instalado o nginx na maquina01.

Se você executar novamente, perceberá que não existirá mais nenhum evento marcado como changed.

🌱🌱🌱🌱🌱🌱