Open britho opened 3 years ago
Neste exercício, você escreverá e executará um playbook do Ansible.
Resultados
Você deverá ser capaz de escrever um playbook usando a sintaxe YAML básica e a estrutura de playbook do Ansible e executá-lo com êxito usando o comando ansible-playbook.
Faça login na workstation como student usando a senha student.
Na workstation, execute o comando lab playbook-basic start. Essa função garante que os hosts gerenciados serverc.lab.example.com e serverd.lab.example.com estejam acessíveis na rede. Ela também garante que o arquivo de configuração e o arquivo de inventário corretos do Ansible sejam instalados no nó de controle.
[student@workstation ~]$ lab playbook-basic start
O diretório de trabalho /home/student/playbook-basic foi criado na workstation para este exercício. Esse diretório já foi preenchido com um arquivo de configuração ansible.cfg e também um arquivo de inventário inventory, o que define um grupo web que inclui ambos os hosts gerenciados listados acima como membros.
Nesse diretório, use um editor de texto para criar um playbook chamado site.yml. Esse playbook contém uma ação, que deve se direcionar a membros do grupo de host web. O playbook deve usar tarefas para garantir que as seguintes condições sejam atendidas nos hosts gerenciados:
O pacote httpd esteja presente, usando o módulo yum.
O arquivo files/index.html local seja copiado para /var/www/html/index.html em cada host gerenciado usando o módulo copy.
O serviço httpd seja iniciado e esteja habilitado, usando o módulo service.
Você pode usar o comando ansible-doc para ajudá-lo a entender as palavras-chave necessárias para cada um dos módulos.
Depois que o playbook for criado, verifique sua sintaxe e, então, use ansible-playbook para executá-lo e implementar a configuração.
Mude para o diretório /home/student/playbook-basic.
[student@workstation ~]$ cd ~/playbook-basic
Use o editor de texto para criar um novo playbook chamado /home/student/playbook-basic/site.yml. Comece a escrever uma ação direcionada aos hosts no grupo de hosts web.
Crie e abra o ~/playbook-basic/site.yml. A primeira linha do arquivo deve ter três traços para indicar o início do playbook.
---
A próxima linha inicia a ação. Ela precisa começar com um traço e um espaço antes da primeira palavra-chave da ação. Nomeie a ação com uma string arbitrária documentando qual é o propósito da ação, usando a palavra-chave name.
---
- name: Install and start Apache HTTPD
Adicione um par de palavra-chave e valor hosts para especificar que a ação é executada no grupo de hosts web do inventário. Certifique-se de que a palavra-chave hosts tem recuo de dois espaços de modo que se alinhe à palavra-chave name na linha anterior.
O arquivo site.yml completo deve aparecer com o seguinte conteúdo:
---
- name: Install and start Apache HTTPD
hosts: web
Continue para editar o arquivo /home/student/playbook-basic/site.yml e adicione uma palavra-chave tasks e as três tarefas para sua ação que foram especificadas nas instruções.
Adicione uma palavra-chave tasks com um recuo de dois espaços (alinhada com a palavra-chave hosts) para iniciar a lista de tarefas. Seu arquivo deve aparecer com o seguinte conteúdo:
---
- name: Install and start Apache HTTPD
hosts: web
tasks:
Adicione a primeira tarefa. Faça um recuo de quatro espaços e inicie a tarefa com um traço e um espaço e, então, dê um nome à tarefa, como httpd package is present. Use o módulo yum para essa tarefa. Recue mais dois espaços nas palavras-chave do módulo; configure o nome do pacote como httpd e o estado do pacote como present. A tarefa deverá aparecer com o seguinte conteúdo:
- name: httpd package is present
yum:
name: httpd
state: present
Adicione a segunda tarefa: Corresponda ao formato da tarefa anterior e dê um nome à tarefa, como correct index.html is present. Use o módulo copy. As palavras-chave do módulo devem definir a chave src como files/index.html e a chave dest como /var/www/html/index.html. A tarefa deverá aparecer com o seguinte conteúdo:
- name: correct index.html is present
copy:
src: files/index.html
dest: /var/www/html/index.html
Adicione a terceira tarefa para iniciar e habilitar o serviço httpd. Corresponda ao formato das duas tarefas anteriores e dê um nome à nova tarefa, como httpd is started. Use o módulo service para essa tarefa. Defina a chave name do serviço como httpd, a chave state como started e a chave enabled como true. A tarefa deverá aparecer com o seguinte conteúdo:
- name: httpd is started
service:
name: httpd
state: started
enabled: true
Todo o seu playbook do Ansible site.yml deve corresponder ao seguinte exemplo: Certifique-se de que o recuo das palavras-chave da ação, a lista de tarefas e as palavras-chave de cada tarefa estejam todas corretas.
---
- name: Install and start Apache HTTPD
hosts: web
tasks:
- name: httpd package is present
yum:
name: httpd
state: present
- name: correct index.html is present
copy:
src: files/index.html
dest: /var/www/html/index.html
- name: httpd is started
service:
name: httpd
state: started
enabled: true
Salve o arquivo e feche o editor de texto.
Antes de executar seu playbook, execute o comando ansible-playbook --syntax-check site.yml para verificar sua sintaxe. Se ela relatar algum erro, corrija-o antes de ir para a próxima etapa. Você verá uma saída semelhante à seguinte:
[student@workstation playbook-basic]$ ansible-playbook --syntax-check site.yml
playbook: site.yml
Execute o playbook. Leia a saída gerada para garantir que todas as tarefas foram concluídas com êxito.
[student@workstation playbook-basic]$ ansible-playbook site.yml
PLAY [Install and start Apache HTTPD] ******************************************
TASK [Gathering Facts] *********************************************************
ok: [serverd.lab.example.com]
ok: [serverc.lab.example.com]
TASK [httpd package is present] ************************************************
changed: [serverd.lab.example.com]
changed: [serverc.lab.example.com]
TASK [correct index.html is present] *******************************************
changed: [serverd.lab.example.com]
changed: [serverc.lab.example.com]
TASK [httpd is started] ********************************************************
changed: [serverd.lab.example.com]
changed: [serverc.lab.example.com]
PLAY RECAP *********************************************************************
serverc.lab.example.com : ok=4 changed=3 unreachable=0 failed=0
serverd.lab.example.com : ok=4 changed=3 unreachable=0 failed=0
Se tudo tiver corrido bem, você deverá ser capaz de executar o playbook uma segunda vez e ver todas as tarefas concluídas sem nenhuma alteração nos hosts gerenciados.
[student@workstation playbook-basic]$ ansible-playbook site.yml
PLAY [Install and start Apache HTTPD] ******************************************
TASK [Gathering Facts] *********************************************************
ok: [serverd.lab.example.com]
ok: [serverc.lab.example.com]
TASK [httpd package is present] ************************************************
ok: [serverd.lab.example.com]
ok: [serverc.lab.example.com]
TASK [correct index.html is present] *******************************************
ok: [serverc.lab.example.com]
ok: [serverd.lab.example.com]
TASK [httpd is started] ********************************************************
ok: [serverd.lab.example.com]
ok: [serverc.lab.example.com]
PLAY RECAP *********************************************************************
serverc.lab.example.com : ok=4 changed=0 unreachable=0 failed=0
serverd.lab.example.com : ok=4 changed=0 unreachable=0 failed=0
Use o comando curl para verificar se ambos serverc e serverd estão configurados como um servidor HTTPD.
[student@workstation playbook-basic]$ curl serverc.lab.example.com
This is a test page.
[student@workstation playbook-basic]$ curl serverd.lab.example.com
This is a test page.
Encerramento
Em workstation, execute o script lab playbook-basic finish para limpar os recursos criados neste exercício.
[student@workstation ~]$ lab playbook-basic finish
Isso conclui o exercício orientado.
Objetivos
Depois de concluir esta seção, você deverá ser capaz de:
Escrever um playbook que usa múltiplas ações e escalonamento de privilégios por ação.
Usar ansible-doc de maneira eficaz a fim de aprender a usar novos módulos para implementar tarefas em uma ação.
Criação de múltiplas ações
Um playbook é um arquivo YAML que contém uma lista de uma ou mais ações. Lembre-se de que uma única ação é uma lista ordenada de tarefas a serem executadas em hosts selecionados no inventário. Portanto, se um playbook contém múltiplas ações, cada ação deve aplicar suas tarefas a um conjunto separado de hosts.
Isso pode ser muito útil ao orquestrar uma implantação complexa que possa envolver diferentes tarefas em diferentes hosts. Você pode escrever um playbook que execute uma ação em um conjunto de hosts e, quando concluir, ele executa outra ação em outro conjunto de hosts.
Criar um playbook que contenha várias ações é algo muito simples. Cada ação no playbook é criada como um item de lista de nível superior no playbook. Cada ação é uma lista contendo as palavras-chave comuns de ação.
O exemplo a seguir mostra um playbook simples com duas ações. A primeira ação é executada em web.example.com e a segunda ação é executada em database.example.com.
name: first play hosts: web.example.com tasks:
name: first task yum: name: httpd status: present
name: second task service: name: httpd enabled: true
name: second play hosts: database.example.com tasks:
Usuários remotos e o escalonamento de privilégios em ações
As ações podem usar usuários remotos ou configurações de escalonamento de privilégios para uma ação diferentes do que é especificado pelos padrões no arquivo de configuração. Eles são definidos na própria ação no mesmo nível que as palavras-chave hosts ou tasks.
Atributos de usuário
As tarefas nos playbooks normalmente são executadas por meio de uma conexão de rede com os hosts gerenciados. Assim como com os comandos ad hoc, a conta de usuário usada para a execução de tarefas depende de várias palavras-chave do arquivo de configuração do Ansible, /etc/ansible/ansible.cfg. O usuário que executa as tarefas pode ser definido pela palavra-chave remote_user. Entretanto, se o escalonamento de privilégios estiver habilitado, outras palavras-chave, como become_user, também poderão causar impacto.
Se o usuário remoto definido na configuração do Ansible para a execução de tarefas não for adequado, ele poderá ser substituído usando a palavra-chave remote_user em uma ação.
remote_user: remoteuser
Atributos de escalonamento de privilégios
Palavras-chave adicionais também estão disponíveis para definir os parâmetros de escalonamento de privilégios dentro de um playbook. A palavra-chave booleana become pode ser utilizada para habilitar ou desabilitar o escalonamento de privilégios, independentemente de como estiver definida no arquivo de configuração do Ansible. Ela pode usar yes ou true para habilitar o escalonamento de privilégios, ou no ou false para desabilitá-lo.
become: true
Se o escalonamento de privilégios estiver habilitado, a palavra-chave become_method poderá ser usada para definir o método de escalonamento de privilégios a ser usado durante uma ação específica. O exemplo abaixo especifica que sudo deve ser usado para o escalonamento de privilégios.
become_method: sudo
Além disso, com o escalonamento de privilégios habilitado, a palavra-chave become_user pode definir a conta de usuário a ser utilizada para o escalonamento de privilégios no contexto de uma ação específica.
become_user: privileged_user
Este exemplo demonstra o uso dessas palavras-chave em uma ação:
name: /etc/hosts is up to date hosts: datacenter-west remote_user: automation become: yes
tasks:
Pesquisa de módulos para tarefas
Documentação dos módulos
O grande número de módulos que vêm integrados com o Ansible fornece aos administradores muitas ferramentas para tarefas administrativas comuns. No início deste curso, discutimos sobre o site da documentação do Ansible em http://docs.ansible.com. O Modules Index no site é uma maneira fácil de navegar pela lista dos módulos incluídos no Ansible. Por exemplo, os módulos para o gerenciamento de usuários e serviços podem ser encontrados nos Systems Modules, e os módulos para administração de bancos de dados podem ser encontrados nos Database Modules.
Para cada módulo, o site de documentação do Ansible fornece um sumário de suas funções e instruções sobre como cada função específica pode ser invocada com opções para o módulo. A documentação também fornece exemplos úteis que mostram como usar cada módulo e como definir suas palavras-chave em uma tarefa.
Você já trabalhou com o comando ansible-doc para procurar informações sobre módulos instalados no sistema local. Como revisão, para ver uma lista dos módulos disponíveis em um nó de controle, execute o comando ansible-doc -l. Ele exibe uma lista de nomes de nós e uma sinopse de suas funções.
[student@workstation modules]$ ansible-doc -l a10_server Manage A10 Networks ... devices' server object. a10_server_axapi3 Manage A10 Networks ... devices a10_service_group Manage A10 Networks ... devices' service groups. a10_virtual_server Manage A10 Networks ... devices' virtual servers. ...output omitted... zfs_facts Gather facts about ZFS datasets. znode Create, ... and update znodes using ZooKeeper zpool_facts Gather facts about ZFS pools. zypper Manage packages on SUSE and openSUSE zypper_repository Add and remove Zypper repositories
Use o comando ansible-doc [module name] para exibir a documentação detalhada de um módulo. Do mesmo modo que o site de documentação do Ansible, o comando fornece uma sinopse da função do módulo, detalhes de suas várias opções e exemplos. O exemplo a seguir mostra a documentação exibida para o módulo yum.
[student@workstation modules]$ ansible-doc yum
YUM (/usr/lib/python3.6/site-packages/ansible/modules/packaging/os/yum.py)
Installs, upgrade, downgrades, removes, and lists packages and groups with the `yum' package manager. This module only works on Python 2. If you require Python
3 support see the [dnf] module.
OPTIONS (= is mandatory):
allow_downgrade Specify if the named package and version is allowed to downgrade a maybe already installed higher version of that package. Note that setting allow_downgrade=True can make this module behave in a non-idempotent way. The task could end up with a set of packages that does not match the complete list of specified packages to install (because dependencies between the downgraded package and others can cause changes to the packages which were in the earlier transaction). [Default: no] type: bool version_added: 2.4
autoremove
If yes', removes all "leaf" packages from the system that were originally installed as dependencies of user-installed packages but which are no longer required by any such package. Should be used alone or when state is
absent'
NOTE: This feature requires yum >= 3.4.3 (RHEL/CentOS 7+)
[Default: no]
type: bool
version_added: 2.7
bugfix
If set to yes', and
state=latest' then only installs updates that have been marked bugfix related.
[Default: no]
version_added: 2.6
conf_file The remote yum configuration file to use for the transaction. [Default: (null)] version_added: 0.6
disable_excludes
Disable the excludes defined in YUM config files.
If set to all', disables all excludes. If set to
main', disable excludes defined in [main] in yum.conf.
If set to `repoid', disable excludes defined for given repo id.
[Default: (null)]
version_added: 2.7
disable_gpg_check
Whether to disable the GPG checking of signatures of packages being installed. Has an effect only if state is present' or
latest'.
[Default: no]
type: bool
version_added: 1.2
disable_plugin `Plugin' name to disable for the install/update operation. The disabled plugins will not persist beyond the transaction. [Default: (null)] version_added: 2.5
disablerepo
Repoid' of repositories to disable for the install/update operation. These repos will not persist beyond the transaction. When specifying multiple repos, separate them with a
","'.
As of Ansible 2.7, this can alternatively be a list instead of `","' separated string
[Default: (null)]
O comando ansible-doc também oferece a opção -s, que produz um exemplo de saída que pode servir como um modelo de como usar um módulo específico em um playbook. Essa saída pode servir como um modelo de início que pode ser incluído em um playbook para implementar o módulo para execução das tarefas. Os comentários estão incluídos na saída para lembrar os administradores do uso de cada opção. O exemplo a seguir mostra a saída para o módulo yum.
[student@workstation ~]$ ansible-doc -s yum
yum' package manager yum: allow_downgrade: # Specify if the named package ... autoremove: # If
yes', removes all "leaf" packages ...
bugfix: # If set to yes', ... conf_file: # The remote yum configuration file ... disable_excludes: # Disable the excludes ... disable_gpg_check: # Whether to disable the GPG ... disable_plugin: #
Plugin' name to disable ...
disablerepo: # Repoid' of repositories ... download_only: # Only download the packages, ... enable_plugin: #
Plugin' name to enable ...
enablerepo: # Repoid' of repositories to enable ... exclude: # Package name(s) to exclude ... installroot: # Specifies an alternative installroot, ... list: # Package name to run ... name: # A package name or package specifier ... releasever: # Specifies an alternative release ... security: # If set to
yes', ...
skip_broken: # Skip packages with ...
state: # Whether to install ... or remove ... a package.
update_cache: # Force yum to check if cache ...
update_only: # When using latest, only update ...
use_backend: # This module supports `yum' ...
validate_certs: # This only applies if using a https url ...Módulo de manutenção
O Ansible inclui um grande número de módulos que podem ser usados em muitas tarefas. A comunidade upstream é muita ativa e esses módulos podem estar em diferentes estágios de desenvolvimento. A documentação ansible-doc para o módulo deve especificar quem mantém esse módulo na comunidade upstream do Ansible e qual é seu status de desenvolvimento. Isso é indicado na seção METADATA no final da saída de ansible-doc desse módulo.
O campo status registra o status de desenvolvimento do módulo:
stableinterface: as palavras-chave do módulo são estáveis e todos os esforços serão feitos para não removê-las ou alterar seus significados.
preview: o módulo é uma amostra de tecnologia e pode ser instável, suas palavras-chave podem ser alteradas ou ele pode precisar de bibliotecas ou serviços web que estejam sujeitos a alterações incompatíveis.
deprecated: o módulo é obsoleto e não estará mais disponível em uma versão futura.
removed: o módulo foi removido da versão, mas existe um stub para propósito de documentação e para ajudar antigos usuários a migrarem para novos módulos.
O status stableinterface apenas indica que a interface de um módulo é estável, mas não classifica a qualidade do código do módulo.
O campo supported_by registra quem mantém o módulo na comunidade upstream do Ansible. Os valores possíveis são:
core: mantido pelo upstream "core" de desenvolvedores do Ansible e é sempre incluído com o Ansible.
curated: módulos enviados e mantidos por parceiros ou empresas na comunidade. Os mantenedores desses módulos devem monitorar qualquer problema relatado ou extrair solicitações feitas em relação ao módulo. Os desenvolvedores "core" do upstream revisam alterações propostas a módulos curados depois que os mantenedores da comunidade aprovarem as alterações. Confirmadores core também garantem que qualquer problema com esses módulos devido a alterações no Ansible seja remediado. Esses módulos atualmente estão incluídos com o Ansible, mas podem ser empacotados separadamente em algum momento no futuro.
community: módulos não suportados por desenvolvedores core, parceiros ou empresas upstream, mas que são totalmente mantidos pela comunidade de open source em geral. Os módulos dessa categoria ainda são totalmente utilizáveis, mas o índice de resposta a problemas depende inteiramente da comunidade. Esses módulos atualmente também estão incluídos com o Ansible, mas provavelmente serão empacotados separadamente em algum ponto no futuro.
A comunidade upstream do Ansible tem um rastreador de problemas para o Ansible e os módulos integrados em https://github.com/ansible/ansible/issues.
Às vezes, não existe um módulo para algo que você deseja fazer. Como um usuário final, você pode escrever seus módulos privados ou obter módulos de terceiros. O Ansible procura por módulos personalizados na localização especificada pela variável de ambiente ANSIBLE_LIBRARY, ou, se ela não estiver definida, por uma palavra-chave library no arquivo de configuração atual do Ansible. O Ansible também procura por módulos personalizados no diretório ./library relativo ao playbook atualmente sendo executado.
library = /usr/share/my_modules
Informações sobre a criação de módulos estão além do escopo deste curso. A documentação que explica como fazer isso está disponível em https://docs.ansible.com/ansible/latest/dev_guide/developing_modules.html.
Use o comando ansible-doc para descobrir e aprender como usar os módulos para suas tarefas.
Sempre que possível, tente evitar os módulos command, shell e raw em playbooks, por mais simples de usar que eles pareçam. Como eles têm comandos arbitrários, é muito fácil escrever playbooks não idempotentes com esses módulos.
Por exemplo, a tarefa a seguir usando o módulo shell não é idempotente. Cada vez que a ação é executada, ela sobrescreve /etc/resolv.conf, mesmo se já consistir na linha nameserver 192.0.2.1.
Há várias maneiras de escrever tarefas usando o módulo shell de uma forma idempotente e, às vezes, fazer tais alterações e usar shell é a melhor abordagem. Uma solução mais rápida pode ser utilizar ansible-doc para detectar o módulo copy e depois utilizá-lo para obter o efeito desejado.
O exemplo a seguir não sobrescreve o arquivo /etc/resolv.conf se ele já tiver o conteúdo correto:
O módulo copy testa se o estado já foi atingido e, nesse caso, não faz nenhuma alteração. O módulo shell permite muita flexibilidade, mas também exige mais atenção para garantir que seja executado de forma idempotente.
Os playbooks idempotentes também podem ser executados repetidas vezes para garantir que os sistemas se encontrem em determinado estado sem interromper os sistemas caso a resposta seja afirmativa. Variações da sintaxe de playbooks
A última parte deste capítulo investiga algumas variações da sintaxe YAML ou de playbooks do Ansible com os quais você poderá se deparar.
Comentários YAML
Os comentários também podem ser usados para melhorar a legibilidade. Em YAML, tudo que estiver à direita do símbolo de cerquilha ou símbolo de hash (#) é um comentário. Se houver conteúdo à esquerda do comentário, insira um espaço antes da cerquilha.
some data # This is also a YAML comment
Strings do YAML
As strings no YAML normalmente não precisam estar entre aspas, mesmo que haja espaços na string. Você pode colocar as strings entre aspas duplas ou simples.
this is a string
'this is another string'
"this is yet another a string"
Há duas maneiras de escrever strings multilinhas. Você pode usar o caractere de barra vertical (|) para denotar que caracteres de nova linha dentro da string devem ser preservados.
include_newlines: | Example Company 123 Main Street Atlanta, GA 30303
Você também pode escrever strings multilinhas usando o caractere de maior que (>) para indicar que os caracteres de nova linha devem ser convertidos em espaços e que espaços em branco à esquerda nas linhas devem ser removidos. Esse método é usado com frequência para quebrar strings longas em espaços de caracteres para que elas possam ser distribuídas em várias linhas para maior legibilidade.
fold_newlines: > This is an example of a long string, that will become a single sentence once folded.
Dicionário de YAML
Você viu coleções de pares de chave-valor escritos como um bloco com recuo, conforme a seguir:
name: svcrole svcservice: httpd svcport: 80
Os dicionários também podem ser escritos em formato de bloco entre chaves em linha, conforme a seguir:
{name: svcrole, svcservice: httpd, svcport: 80}
Na maioria dos casos, o formato de bloco em linha deve ser evitado, pois é mais difícil de ser lido. Contudo, há, no mínimo, uma situação na qual ele é mais comumente usado. O uso de roles é discutido em detalhes mais adiante neste curso. Quando um playbook inclui uma lista de funções, é mais comum usar essa sintaxe para facilitar a distinção entre as funções incluídas em uma ação e as variáveis sendo passadas para uma função.
Listas de YAML
Você também viu listas escritas com sintaxe normal com um traço:
hosts:
As listas também têm um formato embutido entre colchetes, como a seguir:
hosts: [servera, serverb, serverc]
Você deve evitar essa sintaxe porque ela geralmente é mais difícil de ler.
Abreviação obsoleta de playbook com chave=valor
Alguns playbooks podem usar um método antigo de abreviação para definir tarefas colocando os pares de chave-valor do módulo na mesma linha que o nome do módulo. Por exemplo, você pode ver esta sintaxe:
tasks:
Normalmente, você escreveria a mesma tarefa assim:
tasks:
Em geral, você deve evitar a forma abreviada e usar a forma normal.
A forma normal tem mais linhas, mas é mais fácil de se trabalhar. As palavras-chave da tarefa são empilhadas verticalmente e são mais fáceis de diferenciar. Seus olhos podem descer ao longo da ação com menos movimentos da esquerda para a direita. Além disso, a sintaxe normal é nativa de YAML, enquanto a abreviada não é. Ferramentas de destaque de sintaxe em editores de texto modernos podem ajudar de forma mais eficaz se você utilizar o formato abreviado.
Você pode ver essa sintaxe em documentos e playbooks antigos de outras pessoas e ver que a sintaxe ainda funciona.
Páginas do man ansible-playbook(1) e ansible-doc(1)
Intro to Playbooks — Ansible Documentation
Playbooks — Ansible Documentation
Developing Modules — Ansible Documentation
Module Support — Ansible Documentation
YAML Syntax — Ansible Documentation
Neste exercício, você criará um playbook que contém várias ações. Depois, você usará esse playbook para realizar tarefas de configuração em hosts gerenciados.
Resultados
Você deverá ser capaz de criar e executar um playbook para gerenciar a configuração e realizar a administração de um host gerenciado.
Faça login na workstation
como student
usando a senha student
.
Na workstation
, execute o comando lab playbook-multi start. Essa função garante que o host gerenciado, servera.lab.example.com
,
esteja acessível na rede. Ela também garante que o arquivo de
configuração e o arquivo de inventário corretos do Ansible sejam
instalados no nó de controle.
[student@workstation ~]$
lab playbook-multi start
Um diretório de trabalho, /home/student/playbook-multi
, foi criado na workstation
para o projeto do Ansible. O diretório já foi preenchido com um arquivo de configuração ansible.cfg
e um arquivo de inventário inventory
. O host gerenciado, servera.lab.example.com
, já está definido nesse arquivo de inventário. Crie um novo playbook, /home/student/playbook-multi/intranet.yml
, e adicione as linhas necessárias para iniciar a primeira ação. Ele deve ser direcionado ao host gerenciado servera.lab.example.com
e habilitar o escalonamento de privilégios.
Altere o diretório para o diretório de trabalho /home/student/playbook-multi
.
[student@workstation ~]$
cd ~/playbook-multi
[student@workstation playbook-multi]$
Crie e abra um novo playbook, /home/student/playbook-multi/intranet.yml
, e adicione uma linha composta por três traços no início do arquivo para indicar o começo do arquivo YAML.
---
Adicione a linha a seguir ao arquivo /home/student/playbook-multi/intranet.yml
para denotar o início de uma ação com o nome Enable intranet services
.
- name: Enable intranet services
Adicione a linha a seguir para indicar que a ação se aplica ao host gerenciado servera.lab.example.com
. Certifique-se de fazer o recuo da linha com dois espaços (alinhando com a palavra-chave name
acima dela) para indicar que ela faz parte da primeira ação.
hosts: servera.lab.example.com
Adicione a linha a seguir para habilitar o escalonamento de privilégios. Certifique-se de fazer o recuo da linha com dois espaços (alinhando com as palavras-chave acima dela) para indicar que ela faz parte da primeira ação.
become: yes
Adicione a linha a seguir para definir o início da lista tasks
.
Faça o recuo da linha com dois espaços (alinhando com as
palavras-chave acima dela) para indicar que ela faz parte da primeira
ação.
tasks:
Como a primeira tarefa na primeira ação, defina a tarefa que garanta que os pacotes httpd e firewalld estejam atualizados.
Certifique-se de fazer o recuo da primeira linha da tarefa com quatro espaços. Abaixo da palavra-chave tasks
na primeira ação, adicione as linhas a seguir.
- name: latest version of httpd and firewalld installed yum: name: - httpd - firewalld state: latest
A primeira linha fornece um
nome descritivo para a tarefa. A segunda linha é recuada com
seis espaços e chama o módulo yum
. A primeira linha é recuada com oito espaços e é uma palavra-chave name
. Ela especifica quais pacotes o módulo yum
deve garantir que estejam atualizados. A palavra-chave name
(que é diferente do nome da tarefa) do módulo yum
pode receber uma lista de pacotes, que é recuada com dez espaços nas
duas linhas a seguir. Depois da lista, a palavra-chave state
com recuo de oito espaços especifica que o módulo yum
deve garantir que a versão mais recente dos pacotes está instalada.
Adicione uma tarefa à lista da primeira ação que garante que o conteúdo correto esteja em /var/www/html/index.html
.
Adicione as linhas a seguir para definir o conteúdo para /var/www/html/index.html
. Certifique-se de fazer o recuo da primeira linha com quatro espaços.
- name: test html page is installed copy: content: "Welcome to the example.com intranet!\n" dest: /var/www/html/index.html
A primeira
entrada fornece um nome descritivo para a tarefa. A segunda
entrada é recuada com seis espaços e chama o módulo copy
.
As entradas restantes são recuadas com oito espaços e passam
os argumentos necessários para garantir que o conteúdo correto está na
página da web.
Defina mais duas tarefas na ação para garantir que o serviço firewalld
esteja em execução e será iniciado durante o boot e, além disso, permitirá conexões ao serviço httpd
.
Adicione as linhas a seguir ao arquivo para garantir que o serviço firewalld
esteja ativado e funcionando. Certifique-se de fazer o recuo da primeira linha com quatro espaços.
- name: firewalld enabled and running service: name: firewalld enabled: true state: started
A primeira entrada fornece
um nome descritivo para a tarefa. A segunda entrada é
recuada com oito espaços e chama o módulo service
.
As entradas restantes são recuadas com dez espaços e
passam os argumentos necessários para garantir que o serviço firewalld
esteja ativado e funcionando.
Adicione as linhas a seguir para garantir que o firewalld
permita conexões HTTP a partir de sistemas remotos.
Certifique-se de fazer o recuo da primeira linha com quatro espaços.
- name: firewalld permits access to httpd service firewalld: service: http permanent: true state: enabled immediate: yes
A primeira entrada fornece
um nome descritivo para a tarefa. A segunda entrada é
recuada com seis espaços e chama o módulo firewalld
.
As entradas restantes são recuadas com oito espaços e
passam os argumentos necessários para garantir que as conexões HTTP
remotas sejam permitidas permanentemente.
Adicione uma tarefa à primeira ação que tenha o objetivo de garantir que o serviço httpd
esteja em execução e iniciará durante o boot.
Adicione as linhas a seguir ao arquivo para garantir que o serviço httpd
esteja ativado e funcionando. Certifique-se de fazer o recuo da primeira linha com quatro espaços.
- name: httpd enabled and running service: name: httpd enabled: true state: started
A primeira entrada fornece um
nome descritivo para a tarefa. A segunda entrada é recuada com
seis espaços e chama o módulo service
. As entradas restantes são recuadas com oito espaços e passam os argumentos necessários para garantir que o serviço httpd
esteja ativado e funcionando.
Em /home/student/playbook-multi/intranet.yml
, defina uma segunda ação direcionada ao localhost
que testará o servidor web da intranet. Ela não precisa de privilégios de escalonamento.
Adicione a linha a seguir para definir o início de uma segunda ação. Observe que não há recuo.
- name: Test intranet web server
Adicione a linha a seguir para indicar que a ação se aplica ao host gerenciado localhost
. Certifique-se de recuar a linha com dois espaços para indicar que é contida pela segunda ação.
hosts: localhost
Adicione a linha a seguir para desabilitar o
escalonamento de privilégios. Certifique-se de alinhar o
recuo com a palavra-chave hosts
acima dele.
become: no
Adicione a linha a seguir ao arquivo /home/student/playbook-multi/intranet.yml
para definir o início da lista tasks
. Certifique-se de recuar a linha com dois espaços para indicar que é contida pela segunda ação.
tasks:
Adicione uma única tarefa à segunda ação e use o módulo uri
para solicitar conteúdo de http://servera.lab.example.com
. A tarefa deve verificar um código de status HTTP de retorno 200
. Configure a tarefa para colocar o conteúdo retornado na variável de resultados da tarefa.
Adicione as linhas a seguir para criar a tarefa de modo a verificar o serviço web a partir do nó de controle. Certifique-se de fazer o recuo da primeira linha com quatro espaços.
- name: connect to intranet web server uri: url: http://servera.lab.example.com return_content: yes status_code: 200
A primeira linha fornece um
nome descritivo para a tarefa. A segunda linha é recuada com
seis espaços e chama o módulo uri
.
As linhas restantes são recuadas com oito espaços e passam os
argumentos necessários para executar uma consulta para o conteúdo da web
a partir do nó de controle para o host gerenciado e verificam o código
do status recebido. A palavra-chave return_content
garante que a resposta do servidor seja adicionada aos resultados da tarefa.
Verifique se o playbook final /home/student/playbook-multi/intranet.yml
reflete o conteúdo estruturado a seguir. Em seguida, salve e feche o arquivo.
--- - name: Enable intranet services hosts: servera.lab.example.com become: yes tasks: - name: latest version of httpd and firewalld installed yum: name: - httpd - firewalld state: latest - name: test html page is installed copy: content: "Welcome to the example.com intranet!\n" dest: /var/www/html/index.html - name: firewalld enabled and running service: name: firewalld enabled: true state: started - name: firewalld permits access to httpd service firewalld: service: http permanent: true state: enabled immediate: yes - name: httpd enabled and running service: name: httpd enabled: true state: started - name: Test intranet web server hosts: localhost become: no tasks: - name: connect to intranet web server uri: url: http://servera.lab.example.com return_content: yes status_code: 200
Execute o comando ansible-playbook --syntax-check para verificar a sintaxe do playbook /home/student/playbook-multi/intranet.yml
.
[student@workstation playbook-multi]$
ansible-playbook --syntax-check intranet.yml
playbook: intranet.yml
Execute o playbook usando a opção -v
para produzir resultados detalhados para cada tarefa. Leia a
saída gerada para garantir que todas as tarefas foram concluídas com
êxito. Verifique se uma solicitação HTTP GET para http://servera.lab.example.com
fornece o conteúdo correto.
[student@workstation playbook-multi]$
ansible-playbook -v intranet.yml
...output omitted... PLAY [Enable intranet services] ************************************************* TASK [Gathering Facts] ********************************************************** ok: [servera.lab.example.com] TASK [latest version of httpd and firewalld installed] ************************** changed: [servera.lab.example.com] => {"changed": true, ...output omitted... TASK [test html page is installed] ********************************************** changed: [servera.lab.example.com] => {"changed": true, ...output omitted... TASK [firewalld enabled and running] ******************************************** ok: [servera.lab.example.com] => {"changed": false, ...output omitted... TASK [firewalld permits http service] ******************************************* changed: [servera.lab.example.com] => {"changed": true, ...output omitted... TASK [httpd enabled and running] ************************************************ changed: [servera.lab.example.com] => {"changed": true, ...output omitted... PLAY [Test intranet web server] ************************************************* TASK [Gathering Facts] ********************************************************** ok: [localhost] TASK [connect to intranet web server] ******************************************* ok: [localhost] => {"accept_ranges": "bytes", "changed": false, "connection": "cl ose", "content": "Welcome to the example.com intranet!\n", "content_length": "37", "content_type": "text/html; charset=UTF-8", "cookies": {}, "cookies_string ": "", "date": "...output omitted...", "etag": "\"25-5790ddbcc5a48\"", "last_modified": "...output omitted...", "msg": "OK (37 bytes)", "redir ected": false, "server": "Apache/2.4.6 (Red Hat Enterprise Linux)", "status"
: 200, "url": "http://servera.lab.example.com"} PLAY RECAP ********************************************************************** localhost : ok=2 changed=0 unreachable=0 failed=0 servera.lab.example.com : ok=6 changed=4 unreachable=0 failed=0
Lista de verificação de desempenho
Neste laboratório, você configurará e realizará tarefas administrativas em hosts gerenciados usando um playbook.
Resultados
Você deve construir e executar um playbook para instalar, configurar e verificar o status dos serviços da web e de banco de dados em um host gerenciado.
Faça login na workstation como student usando a senha student.
Na workstation, execute o comando lab playbook-review start. Essa função garante que o host gerenciado, serverb.lab.example.com, esteja acessível na rede. Ela também garante que o arquivo de configuração e o arquivo de inventário corretos do Ansible sejam instalados no nó de controle.
[student@workstation ~]$ lab playbook-review start
Um diretório de trabalho, /home/student/playbook-review, foi criado na workstation para o projeto do Ansible. O diretório já foi preenchido com um arquivo de configuração ansible.cfg e um arquivo inventory. O host gerenciado, serverb.lab.example.com, já está definido nesse arquivo de inventário.
O playbook usado neste laboratório é muito semelhante ao que você escreveu no exercício orientado anterior deste capítulo. Se não quiser criar o playbook deste laboratório do zero, você pode usar o playbook deste exercício como um ponto inicial deste laboratório.
Se você o fizer, tenha cuidado ao direcionar aos hosts corretos e alterar as tarefas para corresponderem às instruções deste exercício.
Crie um novo playbook, /home/student/playbook-review/internet.yml, e adicione as entradas necessárias para iniciar uma primeira ação chamada Enable internet services e especifique seu host gerenciado pretendido, serverb.lab.example.com. Adicione uma entrada para habilitar o escalonamento de privilégios e outra para iniciar uma lista de tarefas.
Adicione as entradas necessárias ao arquivo /home/student/playbook-review/internet.yml para definir uma tarefa que instale as versões mais recentes dos pacotes firewalld, httpd, mariadb-server, php e php-mysqlnd.
- name: latest version of all required packages installed
yum:
name:
- firewalld
- httpd
- mariadb-server
- php
- php-mysqlnd
state: latest
Adicione as entradas necessárias ao arquivo /home/student/playbook-review/internet.yml para definir as tarefas de configuração do firewall. Eles devem garantir que o serviço firewalld esteja ativado e em execução, e esse acesso é permitido ao serviço httpd.
- name: firewalld enabled and running
service:
name: firewalld
enabled: true
state: started
- name: firewalld permits http service
firewalld:
service: http
permanent: true
state: enabled
immediate: yes
Adicione as tarefas necessárias para garantir que os serviços httpd e mariadb estejam ativados e em execução.
- name: httpd enabled and running
service:
name: httpd
enabled: true
state: started
- name: mariadb enabled and running
service:
name: mariadb
enabled: true
state: started
Adicione as entradas necessárias para definir a tarefa final com o objetivo de gerar conteúdo da web para teste. Use o módulo get_url para copiar http://materials.example.com/labs/playbook-review/index.php para /var/www/html/ no host gerenciado.
- name: test php page is installed
get_url:
url: "http://materials.example.com/labs/playbook-review/index.php"
dest: /var/www/html/index.php
mode: 0644
Em /home/student/playbook-review/internet.yml, defina outra ação para a tarefa ser realizada no nó de controle. Essa ação testará o acesso ao servidor web que deve estar em execução no host gerenciado serverb. Essa ação não exige escalonamento de privilégios e será executada no host gerenciado localhost.
Adicione a entrada a seguir para denotar o início de uma segunda ação com o nome de Test internet web server.
- name: Test internet web server
Adicione a entrada a seguir para indicar que a ação se aplica ao host gerenciado localhost.
hosts: localhost
Adicione a linha a seguir após a palavra-chave hosts para desabilitar o escalonamento de privilégios para a segunda ação.
become: no
Adicione uma entrada ao arquivo /home/student/playbook-review/internet.yml para definir o início da lista tasks.
tasks:
Adicione uma tarefa que testa o serviço web em execução mp serverb a partir do nó de controle usando o módulo uri. Verifique se há um código de status de retorno 200.
- name: connect to internet web server
uri:
url: http://serverb.lab.example.com
status_code: 200
Verifique a sintaxe do playbook internet.yml.
[student@workstation playbook-review]$ ansible-playbook --syntax-check \
internet.yml
playbook: internet.yml
Use o comando ansible-playbook para executar o playbook. Leia a saída gerada para garantir que todas as tarefas foram concluídas com êxito.
[student@workstation playbook-review]$ ansible-playbook internet.yml PLAY [Enable internet services] ****
TASK [Gathering Facts] ***** ok: [serverb.lab.example.com]
TASK [latest version of all required packages installed] *** changed: [serverb.lab.example.com]
TASK [firewalld enabled and running] *** ok: [serverb.lab.example.com]
TASK [firewalld permits http service] ** changed: [serverb.lab.example.com]
TASK [httpd enabled and running] *** changed: [serverb.lab.example.com]
TASK [mariadb enabled and running] ***** changed: [serverb.lab.example.com]
TASK [test php page installed] ***** changed: [serverb.lab.example.com]
PLAY [Test internet web server] ****
TASK [Gathering Facts] ***** ok: [localhost]
TASK [connect to internet web server] ** ok: [localhost]
PLAY RECAP ***** localhost : ok=2 changed=0 unreachable=0 failed=0 serverb.lab.example.com : ok=7 changed=5 unreachable=0 failed=0
Avaliação
Classifique seu trabalho executando o comando lab playbook-review grade a partir da sua máquina workstation. Corrija todas as falhas relatadas e execute novamente o script até que ele seja concluído com êxito.
[student@workstation ~]$ lab playbook-review grade
Encerramento
Na workstation, execute o script lab playbook-review finish para limpar os recursos criados neste laboratório.
[student@workstation ~]$ lab playbook-review finish
Isso conclui o laboratório.
Neste capítulo, você aprendeu que:
Uma ação é uma lista ordenada de tarefas que é executada em hosts selecionados no inventário.
Um playbook é um arquivo de texto que contém uma lista de uma ou mais ações a serem executadas em ordem.
Os playbooks do Ansible são escritos no formato YAML.
Os arquivos YAML são estruturados usando recuo de espaço para representar a hierarquia de dados.
As tarefas são implementadas usando codificações padronizadas empacotadas como módulos do Ansible.
O comando ansible-doc pode listar módulos instalados, além de fornecer documentação e trechos de código de exemplos de como usá-los em playbooks.
O comando ansible-playbook é usado para verificar a sintaxe de playbooks e executar playbooks.
Depois de concluir esta seção, você deverá ser capaz de criar e fazer referência a variáveis do playbooks que afetam determinados hosts ou grupos de hosts, a ação ou o ambiente global, além de descrever como funciona a precedência de variáveis.
O Ansible suporta variáveis que podem ser usadas para armazenar valores que podem ser reutilizados entre todos os arquivos de um projeto do Ansible. Isso poderá simplificar a criação e a manutenção de um projeto e reduzir o número de erros.
As variáveis proporcionam um modo conveniente de gerenciar valores dinâmicos para um dado ambiente em seu projeto do Ansible. Os exemplos de valores que as variáveis podem conter incluem:
Usuários a criar
Pacotes a instalar
Serviços a reiniciar
Arquivos a remover
Arquivos a recuperar da internet
Os nomes das variáveis devem começar com uma letra e podem conter apenas letras, números e sublinhados.
A tabela a seguir ilustra a diferença entre os nomes de variáveis válidos e os inválidos.
As variáveis podem ser definidas em uma variedade de lugares em um projeto do Ansible. No entanto, isso pode ser simplificado para três níveis básicos de escopo:
Escopo global: variáveis definidas na linha de comando ou na configuração Ansible
Escopo da ação: variáveis definidas nas estruturas da ação e relacionadas
Escopo do host: variáveis definidas em grupos de host e hosts individuais pelo inventário, por coleta de fatos ou tarefas registradas
Se o mesmo nome de variável estiver definido em mais de um nível, vencerá o de maior precedência. O escopo restrito tem precedência sobre o escopo maior: as variáveis definidas pelo inventário são substituídas pelas definidas pelo playbook, que, por sua vez, são substituídas pelas definidas na linha de comando.
Há uma discussão detalhada sobre a precedência das variáveis disponível na documentação do Ansible. O respectivo link é fornecido nas Referências ao fim deste artigo.
As variáveis têm uma função importante nos playbooks do Ansible porque facilitam o gerenciamento de dados das variáveis em um playbook.
Definição de variáveis em playbooks
Ao escrever playbooks, você pode definir suas próprias variáveis e
invocar esses valores em uma tarefa. Por exemplo, uma variável
chamada web_package
pode ser definida com o valor httpd
. Em seguida, uma tarefa pode chamar a variável usando o módulo yum
para instalar o pacote httpd.
As variáveis do playbook podem ser definidas de várias maneiras. Um método comum é colocar a variável em um bloco vars
no início de um playbook:
- hosts: all vars: user: joe home: /home/joe
Também é possível definir as variáveis do playbook em arquivos externos. Nesse caso, em vez de usar o bloco vars
no playbook, é possível utilizar a diretriz vars_files
, seguida de uma lista de arquivos de variáveis externos relacionados ao local do playbook:
- hosts: all vars_files: - vars/users.yml
As variáveis do playbook são, então, definidas nesse arquivo ou nesses arquivos em formato YAML:
user: joe home: /home/joe
Uso de variáveis em playbooks
Assim que as variáveis forem declaradas, os administradores poderão usá-las em tarefas. As variáveis são referenciadas colocando-se o nome da variável entre chaves duplas ({{}}). O Ansible substitui a variável pelo seu valor quando a tarefa é executada.
vars: user: joe tasks: # This line will read: Creates the user joe - name: Creates the user {{ user }} user: # This line will create the user named Joe name: "{{ user }}"
Quando uma variável for usada como o primeiro elemento para iniciar um valor, as aspas serão obrigatórias. Isso impedirá que o Ansible interprete que a referência da variável está iniciando um dicionário YAML. Se as aspas estiverem ausentes, a seguinte mensagem será exibida:
yum: name: {{ service }} ^ here We could be wrong, but this one looks like it might be an issue with missing quotes. Always quote template expression brackets when they start a value. For instance: with_items: - {{ foo }} Should be written as: with_items: - "{{ foo }}"
As variáveis de inventário que se aplicam diretamente aos hosts se enquadram em duas categorias amplas: as variáveis de host se aplicam a um host específico; e as variáveis de grupo se aplicam a todos os hosts em um grupo de hosts ou em um grupo de grupos de hosts. As variáveis de host têm precedência sobre as de grupo, mas as variáveis definidas por um playbook têm precedência sobre ambas.
Uma forma de definir as variáveis de host e de grupo é fazer isso diretamente no arquivo de inventário. Essa é uma abordagem antiga e não é a preferencial, mas você ainda pode encontrá-la.
Definição da variável de host ansible_user
para demo.example.com
:
[servers] demo.example.com ansible_user=joe
Definição da variável de grupo user
para o grupo de hosts servers
.
[servers] demo1.example.com demo2.example.com [servers:vars] user=joe
Definição da variável de grupo user
para o grupo servers
, que consiste em dois grupos de hosts, cada um com dois servidores.
[servers1] demo1.example.com demo2.example.com [servers2] demo3.example.com demo4.example.com [servers:children] servers1 servers2 [servers:vars] user=joe
Algumas desvantagens dessa abordagem são: ela torna o arquivo de inventário mais difícil de trabalhar, ela mistura as informações sobre os hosts e as variáveis no mesmo arquivo e usa sintaxe obsoleta.
Uso de diretórios para preencher variáveis de host e grupo
A abordagem preferencial para definir variáveis de hosts e grupos de hosts é criar dois diretórios, group_vars
e host_vars
,
no mesmo diretório de trabalho como o arquivo ou o diretório do
inventário. Esses diretórios contêm arquivos que definem as
variáveis de grupo e de host respectivamente.
A prática recomendada é definir as variáveis de inventário usando os diretórios host_vars
e group_vars
, e não as definir diretamente nos arquivos do inventário.
Para definir variáveis de grupo para o grupo servers
, crie um arquivo YAML chamado group_vars/servers
. O conteúdo desse arquivo definirá os valores das variáveis usando a mesma sintaxe de um playbook:
user: joe
Da mesma forma, para definir as variáveis de host para um host
específico, crie um arquivo com o nome correspondente ao do host no
diretório host_vars
para conter as variáveis do host.
Os exemplos a seguir ilustram essa abordagem com mais detalhes.
Imagine um cenário em que há dois data centers para gerenciar e os
hosts do data center estão definidos no arquivo de inventário ~/project/inventory
:
[admin@station project]$
cat ~/project/inventory
[datacenter1] demo1.example.com demo2.example.com [datacenter2] demo3.example.com demo4.example.com [datacenters:children] datacenter1 datacenter2
Se você precisar definir um valor geral para todos os
servidores nos dois data centers, estabeleça uma variável de grupo para o
grupo de hosts datacenters
:
[admin@station project]$
cat ~/project/group_vars/datacenters
package: httpd
Se o valor a ser definido variar para cada data center, estabeleça uma variável de grupo para cada grupo de hosts do data center:
[admin@station project]$
cat ~/project/group_vars/datacenter1
package: httpd[admin@station project]$
cat ~/project/group_vars/datacenter2
package: apache
Se o valor a ser definido variar para cada host em cada data center, estabeleça as variáveis em arquivos de variáveis de host separados:
[admin@station project]$
cat ~/project/host_vars/demo1.example.com
package: httpd[admin@station project]$
cat ~/project/host_vars/demo2.example.com
package: apache[admin@station project]$
cat ~/project/host_vars/demo3.example.com
package: mariadb-server[admin@station project]$
cat ~/project/host_vars/demo4.example.com
package: mysql-server
A estrutura do diretório para o projeto de exemplo, project
, se contiver todos os arquivos de exemplo acima, aparecerá da seguinte forma:
project ├── ansible.cfg ├── group_vars │ ├── datacenters │ ├── datacenters1 │ └── datacenters2 ├── host_vars │ ├── demo1.example.com │ ├── demo2.example.com │ ├── demo3.example.com │ └── demo4.example.com ├── inventory └── playbook.yml
As variáveis de inventário são substituídas pelas definidas em um playbook, mas ambos os tipos de variáveis podem ser substituídos por argumentos passados para os comandos ansible ou ansible-playbook na linha de comando. As variáveis definidas na linha de comando são consideradas variáveis extras.
As variáveis extras podem ser úteis quando você precisa substituir o valor definido para uma variável por uma execução única de um playbook. Por exemplo:
[user@demo ~]$
ansible-playbook main.yml -e "package=apache"
Em vez de atribuir os dados de configuração que estão relacionados ao mesmo elemento (uma lista de pacotes, uma de serviços, uma de usuários e assim por diante) a muitas variáveis, os administradores podem usar matrizes. Uma consequência disso é que uma matriz pode ser buscada.
Por exemplo, considere o seguinte trecho:
user1_first_name: Bob user1_last_name: Jones user1_home_dir: /users/bjones user2_first_name: Anne user2_last_name: Cook user2_home_dir: /users/acook
Isso pode ser reescrito como uma matriz chamada users
:
users: bjones: first_name: Bob last_name: Jones home_dir: /users/bjones acook: first_name: Anne last_name: Cook home_dir: /users/acook
Então, você pode usar as seguintes variáveis para acessar os dados do usuário:
# Returns 'Bob' users.bjones.first_name # Returns '/users/acook' users.acook.home_dir
Como a variável é definida como um dicionário do Python, uma sintaxe alternativa está disponível.
# Returns 'Bob' users['bjones']['first_name'] # Returns '/users/acook' users['acook']['home_dir']
A notação de ponto poderá causar problemas se os nomes das chaves
forem os mesmos dos métodos ou dos atributos do Python, como discard
, copy
, add
e assim por diante. Usar a notação de colchetes pode ajudar a evitar conflitos e erros.
As duas sintaxes são válidas, mas, para facilitar a solução de problemas, a RedHat recomenda usar uma sintaxe consistentemente em todos os arquivos em qualquer projeto do Ansible.
Os administradores podem usar a instrução register
para capturar a saída de um comando. A saída é salva em uma
variável temporária que poderá ser usada posteriormente no playbook para
depuração ou para outros fins, como uma configuração específica com
base na saída de um comando.
O seguinte playbook demonstra como capturar a saída de um comando para fins de depuração:
--- - name: Installs a package and prints the result hosts: all tasks: - name: Install the package yum: name: httpd state: installed register: install_result - debug: var=install_result
Quando você executa o playbook, o módulo debug
é usado para despejar o valor da variável registrada, install_result
, no terminal.
[user@demo ~]$
ansible-playbook playbook.yml
PLAY [Installs a package and prints the result] **************************** TASK [setup] *************************************************************** ok: [demo.example.com] TASK [Install the package] ************************************************* ok: [demo.example.com] TASK [debug] *************************************************************** ok: [demo.example.com] => { "install_result": { "changed": false, "msg": "", "rc": 0, "results": [ "httpd-2.4.6-40.el7.x86_64 providing httpd is already installed" ] } } PLAY RECAP ***************************************************************** demo.example.com : ok=3 changed=0 unreachable=0 failed=0
Neste exercício, você definirá e usará variáveis em um playbook.
Resultados
Você deverá ser capaz de:
Definir variáveis em um playbook.
Criar tarefas que usem as variáveis definidas.
Faça login na workstation
como student
usando a senha student
.
Na workstation
, execute o comando lab data-variables start. Essa função cria o diretório de trabalho data-variables
e o preenche com um arquivo de configuração e um inventário de host do Ansible.
[student@workstation ~]$
lab data-variables start
Em workstation
, com o usuário student
, acesse o diretório /home/student/data-variables
.
[student@workstation ~]$
cd ~/data-variables
Nas próximas várias etapas, você criará um playbook que instalará o servidor Web da Apache e abrirá as portas para que o serviço seja acessível. O playbook consulta o servidor web para garantir que esteja funcionando corretamente.
Crie o playbook playbook.yml
e defina as seguintes variáveis na seção vars
:
--- - name: Deploy and start Apache HTTPD service hosts: webserver vars: web_pkg: httpd firewall_pkg: firewalld web_service: httpd firewall_service: firewalld python_pkg: python3-PyMySQL rule: http
Crie o bloco tasks
e a primeira tarefa que usará o módulo yum
para garantir que as versões mais recentes dos pacotes necessários estejam instaladas.
tasks: - name: Required packages are installed and up to date yum: name: - "{{ web_pkg }}" - "{{ firewall_pkg }}" - "{{ python_pkg }}" state: latest
Você pode usar ansible-doc yum para revisar a sintaxe do módulo yum
. A sintaxe mostra que sua diretiva name
pode selecionar uma lista de pacotes com os quais o módulo deve
funcionar para que você não precise separar tarefas para garantir que
todos os pacotes estejam atualizados.
Crie duas tarefas para garantir que os serviços httpd
e firewalld
foram iniciados e habilitados.
- name: The {{ firewall_service }} service is started and enabled service: name: "{{ firewall_service }}" enabled: true state: started - name: The {{ web_service }} service is started and enabled service: name: "{{ web_service }}" enabled: true state: started
O módulo service
funciona de modo diferente do módulo yum
, como documentado por ansible-doc service. A diretiva name
usa o nome de exatamente um serviço para trabalhar com ele.
Você pode escrever uma tarefa única que garanta que os dois
serviços sejam iniciados e ativados, usando a palavra-chave loop
explicada posteriormente neste curso.
Adicione uma tarefa que garanta que um conteúdo específico exista no arquivo /var/www/html/index.html
.
- name: Web content is in place copy: content: "Example web content" dest: /var/www/html/index.html
Adicione uma tarefa que use o módulo firewalld
para garantir que as portas do firewall estejam abertas para o serviço firewalld
chamado na variável rule
.
- name: The firewall port for {{ rule }} is open firewalld: service: "{{ rule }}" permanent: true immediate: true state: enabled
Crie uma
nova ação que consulte o serviço Web para garantir que tudo tenha sido
configurado corretamente. Deve ser executado em localhost
. Por causa desse fato do Ansible, o Ansible não precisa alterar a identidade, então defina o módulo become
como false
. Você pode usar o módulo uri
para verificar uma URL. Para esta tarefa, procure um código de status de 200 para confirmar que o servidor web em servera.lab.example.com
está em execução e foi configurado corretamente.
- name: Verify the Apache service hosts: localhost become: false tasks: - name: Ensure the webserver is reachable uri: url: http://servera.lab.example.com status_code: 200
Quando completo, o playbook deverá ficar da forma a seguir. Analise o playbook e confirme que as ações estão corretas.
--- - name: Deploy and start Apache HTTPD service hosts: webserver vars: web_pkg: httpd firewall_pkg: firewalld web_service: httpd firewall_service: firewalld python_pkg: python3-PyMySQL rule: http tasks: - name: Required packages are installed and up to date yum: name: - "{{ web_pkg }}" - "{{ firewall_pkg }}" - "{{ python_pkg }}" state: latest - name: The {{ firewall_service }} service is started and enabled service: name: "{{ firewall_service }}" enabled: true state: started - name: The {{ web_service }} service is started and enabled service: name: "{{ web_service }}" enabled: true state: started - name: Web content is in place copy: content: "Example web content" dest: /var/www/html/index.html - name: The firewall port for {{ rule }} is open firewalld: service: "{{ rule }}" permanent: true immediate: true state: enabled - name: Verify the Apache service hosts: localhost become: false tasks: - name: Ensure the webserver is reachable uri: url: http://servera.lab.example.com status_code: 200
Antes de executar o playbook, use o comando ansible-playbook --syntax-check para verificar sua sintaxe. Se ela relatar algum erro, corrija-o antes de ir para a próxima etapa. Você verá uma saída semelhante à seguinte:
[student@workstation data-variables]$
ansible-playbook --syntax-check playbook.yml
playbook: playbook.yml
Use o comando ansible-playbook para executar o playbook. Observe a saída enquanto o Ansible instala os pacotes, inicializa e ativa os serviços e garante que o servidor Web esteja acessível.
[student@workstation data-variables]$
ansible-playbook playbook.yml
PLAY [Deploy and start Apache HTTPD service] *********************************** TASK [Gathering Facts] ********************************************************* ok: [servera.lab.example.com] TASK [Required packages are installed and up to date] ************************** changed: [servera.lab.example.com] TASK [The firewalld service is started and enabled] **************************** ok: [servera.lab.example.com] TASK [The httpd service is started and enabled] ******************************** changed: [servera.lab.example.com] TASK [Web content is in place] ************************************************* changed: [servera.lab.example.com] TASK [The firewall port for http is open] ************************************** changed: [servera.lab.example.com] PLAY [Verify the Apache service] *********************************************** TASK [Gathering Facts] ********************************************************* ok: [localhost] TASK [Ensure the webserver is reachable] *************************************** ok: [localhost] PLAY RECAP ********************************************************************* localhost : ok=2 changed=0 unreachable=0 failed=0 servera.lab.example.com : ok=6 changed=4 unreachable=0 failed=0
Isso conclui o exercício orientado.
Objetivos
Depois de concluir esta seção, você deverá ser capaz de criptografar variáveis confidenciais usando o Ansible Vault e executar os playbooks que fazem referência aos arquivos com variáveis criptografadas pelo Vault. Introdução ao Ansible Vault
O Ansible pode precisar de acesso a dados confidenciais, como senhas ou chaves de API, para configurar os hosts gerenciados. Normalmente, essas informações podem ser armazenadas em texto simples nas variáveis de inventário ou em outros arquivos do Ansible. No entanto, nesse caso, qualquer usuário com acesso aos arquivos do Ansible ou um sistema de controle de versão que armazena arquivos do Ansible pode acessar esses dados confidenciais. Isso apresenta um risco óbvio à segurança.
O Ansible Vault, que está incluído no Ansible, pode ser usado para criptografar ou descriptografar qualquer arquivo de dados estruturados usado pelo Ansible. Para usar o Ansible Vault, uma ferramenta de linha de comando denominada ansible-vault é usada para criar, editar, criptografar, descriptografar e visualizar arquivos. O Ansible Vault pode criptografas qualquer arquivo de dados estruturados usado pelo Ansible. Isso inclui variáveis de inventário, arquivos de variáveis de um playbook, arquivos de variáveis aprovados como argumentos quando o playbook é executado ou variáveis definidas nas funções do Ansible.
O Ansible Vault não implementa suas próprias funções criptográficas, mas usa um conjunto de ferramentas externo do Python. Os arquivos são protegidos com criptografia simétrica usando AES256 com uma senha como a chave secreta. O modo como isso é feito não foi auditado por um terceiro.
Criação de um arquivo criptografado
Para criar um novo arquivo criptografado, use o comando ansible-vault create filename. O comando solicita a nova senha do Vault e abre um arquivo usando o editor padrão, vi. Você pode definir e exportar a variável de ambiente EDITOR para especificar um editor padrão diferente por meio da definição e exportação. Por exemplo, para definir o editor padrão como nano, export EDITOR=nano.
[student@demo ~]$ ansible-vault create secret.yml New Vault password: redhat Confirm New Vault password: redhat
Em vez de inserir a senha do Vault por entrada padrão, você pode usar um arquivo de senha do Vault para armazenar a senha. Proteja esse arquivo usando permissões de arquivo e outros meios.
[student@demo ~]$ ansible-vault create --vault-password-file=vault-pass secret.yml
A cifra usada para proteger arquivos é AES256 em versões recentes do Ansible, mas os arquivos criptografados com versões anteriores podem ainda usar AES de 128 bits.
Exibição de um arquivo criptografado
Você pode usar o comando ansible-vault view filename para visualizar o arquivo criptografado pelo Ansible Vault sem abri-lo para edição.
[student@demo ~]$ ansible-vault view secret1.yml Vault password: secret less 458 (POSIX regular expressions) Copyright (C) 1984-2012 Mark Nudelman
less comes with NO WARRANTY, to the extent permitted by law. For information about the terms of redistribution, see the file named README in the less distribution. Homepage: http://www.greenwoodsoftware.com/less my_secret: "yJJvPqhsiusmmPPZdnjndkdnYNDjdj782meUZcw"
Edição de um arquivo criptografado existente
Para editar um arquivo criptografado existente, o Ansible Vault fornece o comando ansible-vault edit filename. Esse comando descriptografa o arquivo em um arquivo temporário e permite a edição. Quando salvo, ele copia o conteúdo e remove o arquivo temporário.
[student@demo ~]$ ansible-vault edit secret.yml Vault password: redhat
O subcomando edit sempre grava o arquivo novamente, por isso deve ser usado somente ao fazer alterações. Isso pode ter implicações quando o arquivo é mantido sob controle de versão. Sempre use o subcomando view para exibir o conteúdo do arquivo sem fazer alterações.
Criptografia de um arquivo existente
Para criptografar um arquivo existente, use o comando ansible-vault encrypt filename. Esse comando pode usar o nome de vários arquivos para que sejam criptografados como argumentos.
[student@demo ~]$ ansible-vault encrypt secret1.yml secret2.yml New Vault password: redhat Confirm New Vault password: redhat Encryption successful
Use a opção --output=OUTPUT_FILE para salvar o arquivo criptografado com um novo nome. Você só pode usar um arquivo de entrada com a opção --output.
Descriptografia de um arquivo existente
Um arquivo criptografado existente pode ser permanentemente descriptografado usando o comando ansible-vault decrypt filename. Quando você descriptografar um único arquivo, a opção --output pode ser usada para salvar o arquivo descriptografado com um nome diferente.
[student@demo ~]$ ansible-vault decrypt secret1.yml --output=secret1-decrypted.yml Vault password: redhat Decryption successful
Alteração da senha de um arquivo criptografado
Você pode usar o comando ansible-vault rekey filename para alterar a senha de um arquivo criptografado. Esse comando pode realizar o rechaveamento em vários arquivos de dados de uma só vez. Ele pede a senha original e a nova senha.
[student@demo ~]$ ansible-vault rekey secret.yml Vault password: redhat New Vault password: RedHat Confirm New Vault password: RedHat Rekey successful
Ao usar um arquivo de senha do Vault, escolha a opção --new-vault-password-file:
[student@demo ~]$ ansible-vault rekey \
--new-vault-password-file=NEW_VAULT_PASSWORD_FILE secret.yml
Playbooks e o Ansible Vault
Para executar um playbook que acesse arquivos criptografados com o Ansible Vault, você precisa fornecer a senha de criptografia ao comando ansible-playbook. Se você não fornecer a senha, o playbook retornará um erro:
[student@demo ~]$ ansible-playbook site.yml ERROR: A vault password must be specified to decrypt vars/api_key.yml
Para fornecer a senha do Vault para o playbook, use a opção --vault-id. Por exemplo, para fornecer a senha do Vault de forma interativa, use --vault-id @prompt conforme ilustrado neste exemplo:
[student@demo ~]$ ansible-playbook --vault-id @prompt site.yml Vault password (default): redhat
Se você estiver usando uma versão do Ansible anterior à versão2.4, precisará usar a opção --ask-vault-pass para fornecer a senha do Vault de forma interativa. Também poderá usar essa opção se todos os arquivos criptografados pelo Vault e usados pelo playbook tiverem sido criptografados com a mesma senha.
[student@demo ~]$ ansible-playbook --ask-vault-pass site.yml Vault password: redhat
Como alternativa, você pode usar a opção --vault-password-file para especificar um arquivo que armazene a senha de criptografia em texto simples. A senha deve ser uma string armazenada como uma só linha no arquivo. Como o arquivo contém a senha confidencial de texto simples, é vital que ele esteja protegido por permissões de arquivo e outras medidas de segurança.
[student@demo ~]$ ansible-playbook --vault-password-file=vault-pw-file site.yml
Você também pode usar a variável de ambiente ANSIBLE_VAULT_PASSWORD_FILE para especificar o local padrão do arquivo de senha.
A partir do Ansible2.4, você pode usar várias senhas do Ansible Vault com ansible-playbook. Para usar várias senhas, aprove várias opções --vault-id ou --vault-password-file para o comando ansible-playbook.
[student@demo ~]$ ansible-playbook \
--vault-id one@prompt --vault-id two@prompt site.yml Vault password (one): Vault password (two): ...output omitted...
As IDs do Vault one e two que precedem @prompt podem ser qualquer coisa, e você pode até omiti-las completamente. No entanto, se você usar a opção --vault-id id ao criptografar um arquivo com o comando ansible-vault, quando executar ansible-playbook, a senha da ID correspondente será usada antes de qualquer outra. Se ela não corresponder, as outras senhas fornecidas serão tentadas em seguida. A ID do Vault @prompt sem ID é, na verdade, uma abreviação de default@prompt, o que significa solicitar a senha da ID do Vault default.
Práticas recomendadas para o gerenciamento de arquivos de variáveis
Para simplificar o gerenciamento, é sensato configurar o projeto do Ansible para que as variáveis confidenciais e todas as demais variáveis sejam mantidas em arquivos separados. Os arquivos que contêm variáveis confidenciais podem ser protegidos com o comando ansible-vault.
Lembre-se de que a forma preferencial de gerenciar variáveis de grupos e variáveis de host é criando diretórios no playbook. O diretório group_vars normalmente consiste em vários arquivos de variáveis com nomes que correspondem aos grupos de host aos quais se aplicam. O diretório host_vars normalmente consiste em vários arquivos de variáveis com nomes que correspondem aos nomes dos hosts gerenciados aos quais se aplicam.
Entretanto, em vez de usar os arquivos em group_vars ou host_vars, você também pode utilizar os diretórios para cada grupo de hosts ou host gerenciado. Esses diretórios podem conter diversos arquivos de variáveis, e todos são utilizados pelo grupo de host ou host gerenciado. Por exemplo, neste diretório de projeto para playbook.yml, os membros do grupo de hosts webservers usam variáveis do arquivo group_vars/webservers/vars, e demo.example.com usa variáveis tanto de host_vars/demo.example.com/vars quanto de host_vars/demo.example.com/vault:
. ├── ansible.cfg ├── group_vars │ └── webservers │ └── vars ├── host_vars │ └── demo.example.com │ ├── vars │ └── vault ├── inventory └── playbook.yml
Nesse cenário, a vantagem é que a maioria das variáveis de demo.example.com pode ser colocada no arquivo vars, mas as variáveis confidenciais podem ser mantidas em segredo ao colocá-las separadamente no arquivo vault. Assim, o administrador pode usar ansible-vault para criptografar o arquivo vault e deixar o arquivo vars como texto simples.
Não há nada de especial quanto aos nomes de arquivo usados nesse exemplo dentro do diretório host_vars/demo.example.com. O diretório poderia conter mais arquivos, alguns criptografados pelo Ansible Vault e outros não.
As variáveis de playbook (ao contrário das variáveis de inventário) também podem ser protegidas com o Ansible Vault. As variáveis confidenciais do playbook também podem ser colocadas em um arquivo separado criptografado pelo Ansible Vault e incluídas no playbook por meio de uma diretiva vars_files. Isso pode ser útil, já que as variáveis de playbook têm precedência sobre as variáveis de inventário.
Se você estiver usando várias senhas do Vault com seu playbook, verifique se cada arquivo criptografado recebeu uma ID do Vault. Insira a senha correspondente à ID do Vault ao executar o playbook. Isso garante que a senha correta seja selecionada primeiro ao descriptografar o arquivo criptografado pelo Vault, o que é mais rápido do que forçar o Ansible a testar todas as senhas do Vault que você forneceu até encontrar a senha certa.
Páginas do man ansible-playbook(1) e ansible-vault(1)
Vault — Ansible Documentation
Variables and Vaults — Ansible Documentation
Neste exercício, você criptografará variáveis confidenciais com o Ansible Vault para protegê-las e, em seguida, executará um playbook que use essas variáveis.
Resultados
Você deverá ser capaz de:
Executar um playbook usando variáveis definidas em um arquivo criptografado.
Faça login na workstation como student usando a senha student.
Na workstation, execute o comando lab data-secret start. Esse script garante que o Ansible está instalado na workstation e cria um diretório de trabalho para este exercício. O diretório inclui um arquivo de inventário que aponta para o servera.lab.example.com como um host gerenciado que faz parte do grupo devservers.
[student@workstation ~]$ lab data-secret start
Na workstation, como usuário student, mude para o diretório de trabalho /home/student/data-secret.
[student@workstation ~]$ cd ~/data-secret
Edite o conteúdo do arquivo criptografado fornecido, secret.yml. O arquivo pode ser descriptografado usando a senha redhat. Remova o comentário das entradas das variáveis username e pwhash.
Edite o arquivo criptografado /home/student/data-secret/secret.yml. Forneça a senha redhat para o Vault quando solicitado. O arquivo criptografado é aberto no editor padrão, vim.
[student@workstation data-secret]$ ansible-vault edit secret.yml
Vault password: redhat
Remova o comentário das duas entradas de variáveis, salve o arquivo e saia do editor. Elas devem aparecer da seguinte maneira:
username: ansibleuser1
pwhash: $6$jf...uxhP1
Crie um playbook chamado /home/student/data-secret/create_users.yml que use as variáveis definidas no arquivo criptografado /home/student/data-secret/secret.yml.
Configure o playbook para usar o grupo de hosts devservers. Execute esse playbook com o usuário devops no host gerenciado remoto. Configure o playbook para criar o usuário ansibleuser1 definido pela variável username. Defina a senha do usuário usando o hash de senha armazenado na variável pwhash.
---
- name: create user accounts for all our servers
hosts: devservers
become: True
remote_user: devops
vars_files:
- secret.yml
tasks:
- name: Creating user from secret.yml
user:
name: "{{ username }}"
password: "{{ pwhash }}"
Use o comando ansible-playbook --syntax-check para verificar a sintaxe do playbook create_users.yml. Use a opção --ask-vault-pass para solicitar a senha do Vault que descriptografa o secret.yml. Resolva os erros de sintaxe antes de continuar.
[student@workstation data-secret]$ ansible-playbook --syntax-check \
> --ask-vault-pass create_users.yml
Vault password (default): redhat
playbook: create_users.yml
Em vez de usar --ask-vault-pass, escolha a nova opção --vault-id @prompt para fazer a mesma coisa.
Em vez de pedir uma senha, crie um arquivo de senha chamado vault-pass para usar com a execução do playbook. O arquivo deve conter o texto simples redhat como a senha do vault. Altere as permissões do arquivo para 0600.
[student@workstation data-secret]$ echo 'redhat' > vault-pass
[student@workstation data-secret]$ chmod 0600 vault-pass
Execute o playbook do Ansible usando o arquivo vault-pass, para criar o usuário ansibleuser1 em um sistema remoto, e usando as senhas armazenadas como variáveis no arquivo criptografado secret.yml do Ansible Vault.
[student@workstation data-secret]$ ansible-playbook \
> --vault-password-file=vault-pass create_users.yml
PLAY [create user accounts for all our servers] ********************************
TASK [Gathering Facts] *********************************************************
ok: [servera.lab.example.com]
TASK [Creating users from secret.yml] ******************************************
changed: [servera.lab.example.com]
PLAY RECAP *********************************************************************
servera.lab.example.com : ok=2 changed=1 unreachable=0 failed=0
Verifique se o playbook foi executado corretamente. O usuário ansibleuser1 deve existir e ter a senha correta em servera.lab.example.com. Teste isso usando ssh para fazer login com esse usuário em servera.lab.example.com. A senha para ansibleuser1 é redhat. Para ter certeza de que o SSH apenas tentará fazer a autenticação por senha e não por uma chave SSH, use a opção -o PreferredAuthentications=password ao fazer login.
Faça logoff do servera quando você tiver feito login com êxito.
[student@workstation data-secret]$ ssh -o PreferredAuthentications=password \
> ansibleuser1@servera.lab.example.com
ansibleuser1@servera.lab.example.com's password: redhat
Activate the web console with: systemctl enable --now cockpit.socket
[ansibleuser1@servera ~]$ exit
logout
Connection to servera.lab.example.com closed.
Encerramento
Na workstation, execute o script lab data-secret finish para limpar este exercício.
[student@workstation ~]$ lab data-secret finish
Isso conclui o exercício orientado.
Depois de concluir esta seção, você deverá ser capaz de fazer referência a dados sobre hosts gerenciados usando fatos do Ansible e configurar fatos personalizados em hosts gerenciados.
Os fatos do Ansible são variáveis que são automaticamente descobertas pelo Ansible em um host gerenciado. Fatos contendo informações específicas de host que podem ser usados como variáveis comuns em ações, condicionais, loops ou qualquer outra declaração que depende de um valor coletado de um host gerenciado.
Entre os fatos coletados para um host gerenciado, estão:
O nome do host
A versão do kernel
As interfaces de rede
Os endereços IP
A versão do sistema operacional
Diversas variáveis de ambiente
O número de CPUs
A memória disponível ou livre
O espaço em disco disponível
Os fatos são uma maneira conveniente de recuperar o estado de um host gerenciado e determinar que ação tomar com base nesse estado. Por exemplo:
Um servidor pode ser reiniciado por uma tarefa condicional executada com base em um fato que contém a versão atual de kernel do host gerenciado.
O arquivo de configuração do MySQL pode ser personalizado dependendo da memória disponível relatada por um fato.
O endereço IPv4 usado em um arquivo de configuração pode ser baseado no valor de um fato.
Normalmente, todas as ações executam o módulo setup
automaticamente antes da primeira tarefa para coletar fatos. Isso é relatado como a tarefa Gathering Facts
no Ansible2.3 e posterior ou, simplesmente, como setup
nas versões mais antigas do Ansible. Por padrão, você não precisa ter uma tarefa para executar setup
em sua ação. Normalmente, o comando é executado automaticamente para você.
Uma maneira de ver quais fatos são coletados para seus hosts
gerenciados é executar um pequeno playbook que reúna fatos e use o
módulo debug
para imprimir o valor da variável ansible_facts
.
- name: Fact dump hosts: all tasks: - name: Print all facts debug: var: ansible_facts
Quando você executa o playbook, os fatos são exibidos na saída do trabalho:
[user@demo ~]$
ansible-playbook facts.yml
PLAY [Fact dump] *************************************************************** TASK [Gathering Facts] ********************************************************* ok: [demo1.example.com] TASK [Print all facts] ********************************************************* ok: [demo1.example.com] => { "ansible_facts": { "all_ipv4_addresses": [ "172.25.250.10" ], "all_ipv6_addresses": [ "fe80::5054:ff:fe00:fa0a" ], "ansible_local": {}, "apparmor": { "status": "disabled" }, "architecture": "x86_64", "bios_date": "01/01/2011", "bios_version": "0.5.1", "cmdline": { "BOOT_IMAGE": "/boot/vmlinuz-3.10.0-327.el7.x86_64", "LANG": "en_US.UTF-8", "console": "ttyS0,115200n8", "crashkernel": "auto", "net.ifnames": "0", "no_timer_check": true, "ro": true, "root": "UUID=2460ab6e-e869-4011-acae-31b2e8c05a3b" }, ...output omitted...
O playbook exibe o conteúdo da variável ansible_facts
no formato JSON como um hash/dicionário de variáveis. Você pode
navegar na saída para ver quais fatos são coletados, para descobrir
quais fatos gostaria de usar nas suas ações.
A tabela a seguir mostra alguns dos fatos que podem ser reunidos de um nó gerenciado e que podem ser úteis em um playbook:
Quando o valor de uma variável é um hash/dicionário, há duas sintaxes que podem ser usadas para recuperar o valor. Estes são dois exemplos da tabela anterior:
ansible_facts['default_ipv4']['address']
também pode ser escrito como ansible_facts.default_ipv4.address
ansible_facts['dns']['nameservers']
também pode ser escrito como ansible_facts.dns.nameservers
Quando um fato é usado em um playbook, o Ansible substitui dinamicamente o nome da variável para o fato pelo valor correspondente:
--- - hosts: all tasks: - name: Prints various Ansible facts debug: msg: > The default IPv4 address of {{ ansible_facts.fqdn }} is {{ ansible_facts.default_ipv4.address }}
A saída a seguir mostra como o Ansible conseguiu consultar o nó gerenciado e usar a informação do sistema dinamicamente para atualizar a variável. Os fatos também podem ser usados para criar grupos dinâmicos de hosts que correspondam a critérios específicos.
[user@demo ~]$
ansible-playbook playbook.yml
PLAY *********************************************************************** TASK [Gathering Facts] ***************************************************** ok: [demo1.example.com] TASK [Prints various Ansible facts] **************************************** ok: [demo1.example.com] => { "msg": "The default IPv4 address of demo1.example.com is 172.25.250.10" } PLAY RECAP ***************************************************************** demo1.example.com : ok=2 changed=0 unreachable=0 failed=0
Antes do Ansible2.5, os fatos eram inseridos como variáveis individuais com o prefixo ansible_
em vez de fazer parte da variável ansible_facts
. Por exemplo, o fato ansible_facts['distribution']
teria sido chamado de ansible_distribution
.
Muitos playbooks mais antigos ainda usam fatos inseridos como variáveis, em vez da nova sintaxe com namespace na variável ansible_facts
. Você pode usar um comando ad hoc para executar o módulo setup
a fim de imprimir o valor de todos os fatos desse formulário. No
exemplo a seguir, um comando ad hoc é usado para executar o módulo setup
no host gerenciado demo1.example.com
:
[user@demo ~]$
ansible demo1.example.com -m setup
demo1.example.com | SUCCESS => { "ansible_facts": { "ansible_all_ipv4_addresses": [ "172.25.250.10" ], "ansible_all_ipv6_addresses": [ "fe80::5054:ff:fe00:fa0a" ], "ansible_apparmor": { "status": "disabled" }, "ansible_architecture": "x86_64", "ansible_bios_date": "01/01/2011", "ansible_bios_version": "0.5.1", "ansible_cmdline": { "BOOT_IMAGE": "/boot/vmlinuz-3.10.0-327.el7.x86_64", "LANG": "en_US.UTF-8", "console": "ttyS0,115200n8", "crashkernel": "auto", "net.ifnames": "0", "no_timer_check": true, "ro": true, "root": "UUID=2460ab6e-e869-4011-acae-31b2e8c05a3b" } ...output omitted...
A tabela a seguir compara os nomes de fatos antigos e novos.
Atualmente, o Ansible reconhece tanto o novo sistema de nomenclatura de fatos (que usam ansible_facts
) e o sistema de nomenclatura antigo, anterior à versão 2.5, com fatos inseridos como variáveis separadas.
Você pode desativar o sistema de nomes antigo definindo o parâmetro inject_facts_as_vars
na seção [default]
do arquivo de configuração do Ansible como false
. No momento, a configuração padrão é true
.
O valor padrão de inject_facts_as_vars
provavelmente mudará para false
em uma versão futura do Ansible. Se estiver configurado como false
, você só poderá fazer referência a fatos do Ansible usando o novo sistema de nomenclatura ansible_facts.*
. Nesse caso, as tentativas de fazer referência a fatos por meio do namespace antigo resulta no seguinte erro:
...output omitted... TASK [Show me the facts] ************************************************* fatal: [demo.example.com]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'ansible_distribution' is undefined\n\nThe error appears to have been in '/home/student/demo/playbook.yml': line 5, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n tasks:\n - name: Show me the facts\n ^ here\n"} ...output omitted...
Algumas vezes, você pode não querer coletar fatos para sua ação.
Há alguns motivos por que isso pode acontecer. Pode ser que você
não esteja usando fatos e queira agilizar a ação e reduzir a carga
causada pela ação nos hosts gerenciados. Pode ser que os hosts
gerenciados não possam executar o módulo setup
por algum motivo ou precisem instalar um software de pré-requisito antes de coletar dados.
Para desativar a coleta de fatos em uma ação, defina a palavra-chave gather_facts
como no
:
--- - name: This play gathers no facts automatically hosts: large_farm gather_facts: no
Mesmo se gather_facts: no
estiver definido para uma ação, você poderá coletar manualmente fatos a
qualquer momento, executando uma tarefa que use o módulo setup
:
tasks:
- name: Manually gather facts
setup:
...output omitted...
Os administradores podem criar fatos personalizados,
que são armazenados localmente em cada host gerenciado. Esses fatos
são integrados à lista de fatos padrão coletados pelo módulo setup
quando é executado no host gerenciado. Eles permitem que o host
gerenciado forneça variáveis ao Ansible, o que pode ser usado para
ajustar o comportamento das ações.
Fatos personalizados podem ser definidos em um arquivo estático, formatado como um arquivo INI ou usando JSON. Eles também podem ser scripts executáveis que geram saída JSON, assim como um script de inventário dinâmico.
Os fatos personalizados permitem aos administradores definir certos valores para hosts gerenciados que as ações podem usar para preencher arquivos de configuração ou executar tarefas condicionalmente. Os fatos personalizados dinâmicos permitem que os valores desses fatos ou, até mesmo, quais fatos são fornecidos, sejam determinados programaticamente quando a ação é executada.
Por padrão, o módulo setup
carrega fatos personalizados de arquivos e scripts no diretório /etc/ansible/facts.d
de cada host gerenciado. O nome de todos os arquivos ou scripts deve terminar em .fact
para ser usado. Os scripts de fatos personalizados dinâmicos devem
gerar como saída fatos formatados em JSON e devem ser executáveis.
Este é um exemplo de um arquivo de fatos personalizados estáticos gravados no formato INI. Um arquivo de fatos personalizados formatos em INI contém um nível superior definido por uma seção, seguido pelos pares de chave-valor dos fatos a serem definidos:
[packages] web_package = httpd db_package = mariadb-server [users] user1 = joe user2 = jane
Os mesmos fatos podem ser fornecidos no formato JSON. Os fatos JSON a seguir são equivalentes aos fatos especificados pelo formato INI no exemplo anterior. Os dados JSON podem ser armazenados em um arquivo de texto estático ou impressos em saída padrão por um script executável:
{ "packages": { "web_package": "httpd", "db_package": "mariadb-server" }, "users": { "user1": "joe", "user2": "jane" } }
Os arquivos de fatos personalizados não podem estar no formato YAML como um playbook. O formato JSON é o equivalente mais próximo.
Os fatos personalizados são armazenados pelo módulo setup
na variável ansible_facts.ansible_local
.
Os fatos são organizados com base no nome do arquivo que os
definiu. Por exemplo, digamos que os fatos personalizados anteriores
produziram um arquivo salvo como /etc/ansible/facts.d/custom.fact
no host gerenciado. Nesse caso, o valor de ansible_facts.ansible_local['custom']['users']['user1']
é joe
.
Você pode inspecionar a estrutura dos seus fatos personalizados executando o módulo setup
nos hosts gerenciados com um comando ad hoc.
[user@demo ~]$
ansible demo1.example.com -m setup
demo1.example.com | SUCCESS => { "ansible_facts": { ...output omitted... "ansible_local": { "custom": { "packages": { "db_package": "mariadb-server", "web_package": "httpd" }, "users": { "user1": "joe", "user2": "jane" } } }, ...output omitted... }, "changed": false }
Os fatos personalizados podem ser usados da mesma forma que os fatos padrão no playbooks:
[user@demo ~]$
cat playbook.yml
--- - hosts: all tasks: - name: Prints various Ansible facts debug: msg: > The package to install on {{ ansible_facts['fqdn'] }} is {{ ansible_facts['ansible_local']['custom']['packages']['web_package'] }}[user@demo ~]$
ansible-playbook playbook.yml
PLAY *********************************************************************** TASK [Gathering Facts] ***************************************************** ok: [demo1.example.com] TASK [Prints various Ansible facts] **************************************** ok: [demo1.example.com] => { "msg": "The package to install on demo1.example.com is httpd" } PLAY RECAP ***************************************************************** demo1.example.com : ok=2 changed=0 unreachable=0 failed=0
Algumas variáveis não são fatos ou configuradas pelo módulo setup
, mas também são definidas automaticamente pelo Ansible. Essas magic variables também podem ser úteis para obter informações específicas de um host gerenciado específico.
Quatro das mais úteis são:
hostvars
Contém as variáveis para hosts gerenciados e pode ser usada para obter os valores de outras variáveis de host gerenciado. Ela não inclui os fatos do host gerenciado se eles ainda não foram coletados para o host.
group_names
Lista todos os grupos nos quais o host gerenciado atual está.
groups
Lista todos os grupos e hosts no inventário.
inventory_hostname
Contém o nome do host gerenciado atual como configurado no inventário. Ele pode ser diferente do nome do host relatado por fatos por vários motivos.
Há também outras magic variables. Para obter mais informações, consulte https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable. Uma forma de obter informações sobre esses valores é usar o módulo debug
para gerar relatórios sobre os conteúdos da variável hostvars
de um host específico:
[user@demo ~]$
ansible localhost -m debug -a 'var=hostvars["localhost"]'
localhost | SUCCESS => { "hostvars[\"localhost\"]": { "ansible_check_mode": false, "ansible_connection": "local", "ansible_diff_mode": false, "ansible_facts": {}, "ansible_forks": 5, "ansible_inventory_sources": [ "/home/student/demo/inventory" ], "ansible_playbook_python": "/usr/bin/python2", "ansible_python_interpreter": "/usr/bin/python2", "ansible_verbosity": 0, "ansible_version": { "full": "2.7.0", "major": 2, "minor": 7, "revision": 0, "string": "2.7.0" }, "group_names": [], "groups": { "all": [ "serverb.lab.example.com" ], "ungrouped": [], "webservers": [ "serverb.lab.example.com" ] }, "inventory_hostname": "localhost", "inventory_hostname_short": "localhost", "omit": "__omit_place_holder__18d132963728b2cbf7143dd49dc4bf5745fe5ec3", "playbook_dir": "/home/student/demo" } }
Neste exercício, você reunirá fatos do Ansible de um host gerenciado e os usará em ações.
Resultados
Você deverá ser capaz de:
Reunir fatos de um host.
Criar tarefas que usem os fatos reunidos.
Faça login na workstation como student usando a senha student.
Na workstation, execute o comando lab data-facts start. Este script cria o diretório de trabalho, data-facts, e o preenche com um arquivo de configuração e um inventário de host do Ansible.
[student@workstation ~]$ lab data-facts start
Em workstation, com o usuário student, acesse o diretório /home/student/data-facts.
[student@workstation ~]$ cd ~/data-facts
[student@workstation data-facts]$
O módulo setup do Ansible recupera fatos de sistemas. Execute um comando ad hoc para recuperar os fatos para todos os servidores no grupo webserver. A saída exibe todos os fatos reunidos para servera.lab.example.com em formato JSON. Revise algumas das variáveis exibidas.
[student@workstation data-facts]$ ansible webserver -m setup
...output omitted...
servera.lab.example.com | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"172.25.250.10"
],
"ansible_all_ipv6_addresses": [
"fe80::2937:3aa3:ea8d:d3b1"
],
...output omitted...
Em workstation, crie um arquivo de fato chamado de /home/student/data-facts/custom.fact. O arquivo de fato define o pacote a ser instalado e o serviço a ser iniciado em servera. O arquivo deverá exibir o seguinte texto:
[general]
package = httpd
service = httpd
state = started
enabled = true
Crie o playbook setup_facts.yml para fazer o diretório remoto /etc/ansible/facts.d e para salvar o arquivo custom.fact nesse diretório.
---
- name: Install remote facts
hosts: webserver
vars:
remote_dir: /etc/ansible/facts.d
facts_file: custom.fact
tasks:
- name: Create the remote directory
file:
state: directory
recurse: yes
path: "{{ remote_dir }}"
- name: Install the new facts
copy:
src: "{{ facts_file }}"
dest: "{{ remote_dir }}"
Execute um comando ad hoc com o módulo de setup. Procure a seção ansible_local na saída. Não deve haver nenhum fato personalizado nesse momento.
[student@workstation data-facts]$ ansible webserver -m setup
servera.lab.example.com | SUCCESS => {
"ansible_facts": {
...output omitted...
"ansible_local": {}
...output omitted...
},
"changed": false
}
Antes de executar o playbook, verifique se a sintaxe está correta executando ansible-playbook --syntax-check. Se ela relatar algum erro, corrija-o antes de ir para a próxima etapa. Você verá uma saída semelhante à seguinte:
[student@workstation data-facts]$ ansible-playbook --syntax-check setup_facts.yml
playbook: setup_facts.yml
Execute o playbook setup_facts.yml.
[student@workstation data-facts]$ ansible-playbook setup_facts.yml
PLAY [Install remote facts] ****************************************************
TASK [Gathering Facts] *********************************************************
ok: [servera.lab.example.com]
TASK [Create the remote directory] *********************************************
changed: [servera.lab.example.com]
TASK [Install the new facts] ***************************************************
changed: [servera.lab.example.com]
PLAY RECAP *********************************************************************
servera.lab.example.com : ok=3 changed=2 unreachable=0 failed=0
Agora é possível criar o playbook principal que usa tanto fatos padrão como do usuário para configurar o servera. Ao longo de várias etapas a seguir, você acrescentará ao arquivo do playbook. Crie o playbook playbook.yml com o seguinte:
---
- name: Install Apache and starts the service
hosts: webserver
Continue a editar o arquivo playbook.yml criando a primeira tarefa que instala o pacote httpd. Use o fato do usuário para o nome do pacote.
tasks:
- name: Install the required package
yum:
name: "{{ ansible_facts['ansible_local']['custom']['general']['package'] }}"
state: latest
Crie outra tarefa que usa o fato personalizado para iniciar o serviço httpd.
- name: Start the service
service:
name: "{{ ansible_facts['ansible_local']['custom']['general']['service'] }}"
state: "{{ ansible_facts['ansible_local']['custom']['general']['state'] }}"
enabled: "{{ ansible_facts['ansible_local']['custom']['general']['enabled'] }}"
Quando todas as tarefas forem concluídas, o playbook completo deverá ter a aparência a seguir. Revise o playbook e certifique-se de que todas as tarefas estejam definidas.
---
- name: Install Apache and starts the service
hosts: webserver
tasks:
- name: Install the required package
yum:
name: "{{ ansible_facts['ansible_local']['custom']['general']['package'] }}"
state: latest
- name: Start the service
service:
name: "{{ ansible_facts['ansible_local']['custom']['general']['service'] }}"
state: "{{ ansible_facts['ansible_local']['custom']['general']['state'] }}"
enabled: "{{ ansible_facts['ansible_local']['custom']['general']['enabled'] }}"
Antes de executar o playbook, use um comando ad hoc para verificar se o serviço httpd não está em execução no momento em servera.
[student@workstation data-facts]$ ansible servera.lab.example.com -m command \
> -a 'systemctl status httpd'
servera.lab.example.com | FAILED | rc=4 >>
Unit httpd.service could not be found.non-zero return code
Verifique a sintaxe do playbook usando o comando ansible-playbook --syntax-check. Se ela relatar algum erro, corrija-o antes de ir para a próxima etapa. Você verá uma saída semelhante à seguinte:
[student@workstation data-facts]$ ansible-playbook --syntax-check playbook.yml
playbook: playbook.yml
Execute o playbook usando o comando ansible-playbook. Observe a saída enquanto o Ansible instala o pacote e, em seguida, ativa o serviço.
[student@workstation data-facts]$ ansible-playbook playbook.yml
PLAY [Install Apache and start the service] ************************************
TASK [Gathering Facts] *********************************************************
ok: [servera.lab.example.com]
TASK [Install the required package] ********************************************
changed: [servera.lab.example.com]
TASK [Start the service] *******************************************************
changed: [servera.lab.example.com]
PLAY RECAP *********************************************************************
servera.lab.example.com : ok=3 changed=2 unreachable=0 failed=0
Use um comando ad hoc para executar systemctl e determinar se o serviço httpd está agora em execução em servera.
[student@workstation data-facts]$ ansible servera.lab.example.com -m command \
> -a 'systemctl status httpd'
servera.lab.example.com | CHANGED | rc=0 >>
● httpd.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled)
Active: active (running) since Mon 2019-05-27 07:50:55 EDT; 50s ago
Docs: man:httpd.service(8)
Main PID: 11603 (httpd)
Status: "Running, listening on: port 80"
Tasks: 213 (limit: 4956)
Memory: 24.1M
CGroup: /system.slice/httpd.service
...output omitted...
Encerramento
Na workstation, execute o script lab data-facts finish para limpar este exercício.
[student@workstation ~]$ lab data-facts finish
Isso conclui o exercício orientado.
Lista de verificação de desempenho
Neste laboratório, você escreverá e executará um playbook do Ansible que usa variáveis, segredos e fatos.
Resultados
Você deverá ser capaz de definir variáveis e usar fatos em um playbook, bem como usar as variáveis definidas em um arquivo criptografado.
Faça login na workstation
como student
usando a senha student
.
Na workstation
, execute o comando lab data-review start. O script cria o diretório de trabalho /home/student/data-review
e o preenche com um arquivo de configuração e um inventário de host do Ansible. O host gerenciado serverb.lab.example.com
está definido nesse inventário como membro do grupo de hosts webserver
.
Um desenvolvedor solicitou que você escrevesse um playbook do
Ansible para automatizar a configuração de um ambiente de servidor web
em serverb.lab.example.com
, que controla o acesso dos usuários ao site usando autenticação básica.
O subdiretório files
contém:
Um arquivo de configuração httpd.conf
para o serviço web Apache para autenticação básica.
Um arquivo .htaccess
, usado para controlar o acesso ao diretório raiz de documentos do servidor web.
Um arquivo htpasswd
contendo credenciais para usuários permitidos.
[student@workstation ~]$
lab data-review start
No diretório de trabalho, crie o playbook playbook.yml
e adicione o grupo de hosts webserver
como host gerenciado. Defina as seguintes variáveis de ação:
Mude para o diretório de trabalho /home/student/data-review
.
[student@workstation ~]$
cd ~/data-review
[student@workstation data-review]$
Crie o arquivo do playbook playbook.yml
e altere-o em um editor de texto. O início do arquivo deve aparecer da seguinte maneira:
--- - name: install and configure webserver with basic auth hosts: webserver vars: firewall_pkg: firewalld firewall_svc: firewalld web_pkg: httpd web_svc: httpd ssl_pkg: mod_ssl httpdconf_src: files/httpd.conf httpdconf_dest: /etc/httpd/conf/httpd.conf htaccess_src: files/.htaccess secrets_dir: /etc/httpd/secrets secrets_src: files/htpasswd secrets_dest: "{{ secrets_dir }}/htpasswd" web_root: /var/www/html
Adicione a seção tasks
à ação. Escreva uma tarefa que garanta que a versão mais
recente dos pacotes necessários está instalada. Esses pacotes
são definidos pelas variáveis firewall_pkg
, web_pkg
e ssl_pkg
.
Defina o início da seção tasks
adicionando a seguinte linha ao playbook:
tasks:
Adicione as linhas a seguir ao playbook para definir uma tarefa que use o módulo yum
para instalar os pacotes necessários.
- name: latest version of necessary packages installed yum: name: - "{{ firewall_pkg }}" - "{{ web_pkg }}" - "{{ ssl_pkg }}" state: latest
Adicione uma segunda tarefa ao playbook que garanta que o arquivo especificado pela variável httpdconf_src
seja copiado (com o módulo copy
) para o local determinado pela variável httpdconf_dest
no host gerenciado. O arquivo deve ser de propriedade do usuário root
e do grupo root
. Também defina as permissões do arquivo como 0644.
Adicione as linhas a seguir ao playbook para definir uma tarefa que use o módulo copy
para copiar o conteúdo do arquivo definido pela variável httpdconf_src
para o local especificado pela variável httpdconf_dest
.
- name: configure web service copy: src: "{{ httpdconf_src }}" dest: "{{ httpdconf_dest }}" owner: root group: root mode: 0644
Adicione uma terceira tarefa que use o módulo file
para criar o diretório especificado pela variável secrets_dir
no host gerenciado. Esse diretório contém os arquivos de
senha usados para a autenticação básica dos serviços web. O
arquivo deve ser de propriedade do usuário apache
e do grupo apache
. Defina as permissões do arquivo como 0500.
Adicione as linhas a seguir ao playbook para definir uma tarefa que use o módulo file
para criar o diretório definido pela variável secrets_dir
.
- name: secrets directory exists file: path: "{{ secrets_dir }}" state: directory owner: apache group: apache mode: 0500
Adicione uma quarta tarefa que use o módulo copy
para posicionar um arquivo htpasswd, usado para a autenticação básica
dos usuários web. A fonte deve ser definida pela variável secrets_src
. O destino deve ser definido pela variável secrets_dest
. O arquivo deve ser de propriedade do usuário e grupo apache
. Set 0400
as the file permissions.
- name: htpasswd file exists copy: src: "{{ secrets_src }}" dest: "{{ secrets_dest }}" owner: apache group: apache mode: 0400
Adicione uma quinta tarefa que use o módulo copy
para criar o arquivo .htaccess
no diretório raiz do documento do servidor web. Copie o arquivo especificado pela variável htaccess_src
para {{ web_root}}/.htaccess
. O arquivo deve ser de propriedade do usuário apache
e do grupo apache
. Defina as permissões do arquivo como 0400.
Adicione as linhas a seguir ao playbook para definir uma tarefa que use o módulo copy
para criar o arquivo .htaccess
usando o arquivo definido pela variável htaccess_src
.
- name: .htaccess file installed in docroot copy: src: "{{ htaccess_src }}" dest: "{{ web_root }}/.htaccess" owner: apache group: apache mode: 0400
Adicione uma sexta tarefa que use o módulo copy
para criar o arquivo de conteúdo da web index.html
no diretório especificado pela variável web_root
. O arquivo deve conter a mensagem HOSTNAME
(IPADDRESS
) has been customized by Ansible., onde HOSTNAME
é o nome totalmente qualificado do host gerenciado e IPADDRESS
é seu endereço IPv4. Use a opção content
para o módulo copy
para especificar o conteúdo do arquivo e fatos Ansible para especificar o nome do host e o endereço IP.
Adicione as linhas a seguir ao playbook para definir uma tarefa que use o módulo copy
para criar o arquivo index.html
no diretório definido pela variável web_root
. Preencha o arquivo com o conteúdo especificado usando os fatos do Ansible ansible_facts['fqdn']
e ansible_facts['default_ipv4']['address']
recuperados do host gerenciado.
- name: create index.html copy: content: "{{ ansible_facts['fqdn'] }} ({{ ansible_facts['default_ipv4']['address'] }}) has been customized by Ansible.\n" dest: "{{ web_root }}/index.html"
Adicione uma sétima tarefa que use o módulo service
para ativar e iniciar o serviço de firewall no host gerenciado.
Adicione as linhas a seguir ao playbook para definir uma tarefa que use o módulo service
para ativar e iniciar o serviço de firewall.
- name: firewall service enabled and started service: name: "{{ firewall_svc }}" state: started enabled: true
Adicione uma oitava tarefa que use o módulo firewalld
para permitir o serviço https
necessário para que os usuários acessem os serviços web no host
gerenciado. Essa alteração de firewall deve ser permanente e
deve ocorrer imediatamente.
Adicione as linhas a seguir ao playbook para definir uma tarefa que use o módulo firewalld
para abrir a porta HTTPS para o serviço web.
- name: open the port for the web server firewalld: service: https state: enabled immediate: true permanent: true
Adicione uma tarefa final que use o módulo service
para ativar e iniciar o serviço web no host gerenciado para que todas
as alterações de configuração entrem em vigor. O nome do
serviço web é definido pela variável web_svc
.
- name: web service enabled and started service: name: "{{ web_svc }}" state: started enabled: true
Defina uma segunda ação direcionada ao localhost
que testará a autenticação para o servidor web. Ela não
precisa de privilégios de escalonamento. Defina uma variável
chamada web_user
com o valor guest
.
Adicione a linha a seguir para definir o início de uma segunda ação. Observe que não há recuo.
- name: test web server with basic auth
Adicione a linha a seguir para indicar que a ação se aplica ao host gerenciado localhost
.
hosts: localhost
Adicione a linha a seguir para desabilitar o escalonamento de privilégios.
become: no
Adicione as linhas a seguir para definir uma lista de variáveis e a variável web_user
.
vars: web_user: guest
Adicione uma diretiva à ação que acrescenta outras variáveis de um arquivo de variáveis chamado de vars/secret.yml
.
Esse arquivo contém uma variável que determina a senha do
usuário web. Você criará esse arquivo posteriormente no
laboratório.
Defina o início da lista de tarefas.
Usando a palavra-chave vars_files
, adicione as linhas a seguir ao playbook para instruir o Ansible a usar as variáveis encontradas no arquivo vars/secret.yml
.
vars_files: - vars/secret.yml
Adicione a linha a seguir para definir o início da lista tasks
.
tasks:
Adicione duas tarefas à segunda ação.
O primeiro usa o módulo uri
para solicitar conteúdo de https://serverb.lab.example.com
usando autenticação básica. Observe que o certificado apresentado por serverb
não será confiável, portanto, será necessário evitar a validação do
certificado. A tarefa deve verificar um código de status HTTP
de retorno 200
. Configure a tarefa
para colocar o conteúdo retornado na variável de resultados da tarefa.
Registre o resultado da tarefa em uma variável.
A segunda tarefa usa o módulo debug
para imprimir o conteúdo retornado do servidor web.
Adicione as linhas a seguir para criar a tarefa de modo a verificar o serviço web a partir do nó de controle. Certifique-se de fazer o recuo da primeira linha com quatro espaços.
- name: connect to web server with basic auth uri: url: https://serverb.lab.example.com validate_certs: no force_basic_auth: yes user: "{{ web_user }}" password: "{{ web_pass }}" return_content: yes status_code: 200 register: auth_test
Crie a segunda tarefa usando o módulo de depuração. O
conteúdo retornado do servidor web é adicionado à variável registrada
como a chave content
.
- debug: var: auth_test.content
O playbook completo deverá ficar desta forma:
--- - name: install and configure webserver with basic auth hosts: webserver vars: firewall_pkg: firewalld firewall_svc: firewalld web_pkg: httpd web_svc: httpd ssl_pkg: mod_ssl httpdconf_src: files/httpd.conf httpdconf_dest: /etc/httpd/conf/httpd.conf htaccess_src: files/.htaccess secrets_dir: /etc/httpd/secrets secrets_src: files/htpasswd secrets_dest: "{{ secrets_dir }}/htpasswd" web_root: /var/www/html tasks: - name: latest version of necessary packages installed yum: name: - "{{ firewall_pkg }}" - "{{ web_pkg }}" - "{{ ssl_pkg }}" state: latest - name: configure web service copy: src: "{{ httpdconf_src }}" dest: "{{ httpdconf_dest }}" owner: root group: root mode: 0644 - name: secrets directory exists file: path: "{{ secrets_dir }}" state: directory owner: apache group: apache mode: 0500 - name: htpasswd file exists copy: src: "{{ secrets_src }}" dest: "{{ secrets_dest }}" owner: apache group: apache mode: 0400 - name: .htaccess file installed in docroot copy: src: "{{ htaccess_src }}" dest: "{{ web_root }}/.htaccess" owner: apache group: apache mode: 0400 - name: create index.html copy: content: "{{ ansible_facts['fqdn'] }} ({{ ansible_facts['default_ipv4']['address'] }}) has been customized by Ansible.\n" dest: "{{ web_root }}/index.html" - name: firewall service enable and started service: name: "{{ firewall_svc }}" state: started enabled: true - name: open the port for the web server firewalld: service: https state: enabled immediate: true permanent: true - name: web service enabled and started service: name: "{{ web_svc }}" state: started enabled: true - name: test web server with basic auth hosts: localhost become: no vars: - web_user: guest vars_files: - vars/secret.yml tasks: - name: connect to web server with basic auth uri: url: https://serverb.lab.example.com validate_certs: no force_basic_auth: yes user: "{{ web_user }}" password: "{{ web_pass }}" return_content: yes status_code: 200 register: auth_test - debug: var: auth_test.content
Save and close the playbook.yml
file.
Crie um arquivo criptografado com o Ansible Vault, chamado vars/secret.yml
. Ele deve definir a variável web_pass
como redhat
, que será a senha do usuário web.
Crie um subdiretório chamado vars
no diretório de trabalho.
[student@workstation data-review]$
mkdir vars
Crie o arquivo variável criptografado, vars/secret.yml
, usando o Ansible Vault. Defina a senha do arquivo criptografado para redhat
.
[student@workstation data-review]$
ansible-vault create vars/secret.yml
New Vault password:
redhat
Confirm New Vault password:
redhat
Adicione a definição de variável a seguir ao arquivo.
web_pass: redhat
Salve e feche o arquivo.
Execute o playbook playbook.yml
.
Verifique se o conteúdo foi retornado com êxito do servidor
web e se corresponde ao que foi configurado em uma tarefa anterior.
Antes de executar o playbook, verifique se a sintaxe está correta executando ansible-playbook --syntax-check. Use a opção --ask-vault-pass
que será solicitada para a senha do vault. Digite redhat
quando a senha for solicitada. Se ela relatar algum erro,
corrija-o antes de ir para a próxima etapa. Você verá uma
saída semelhante à seguinte:
[student@workstation data-review]$
ansible-playbook --syntax-check \
>
--ask-vault-pass playbook.yml
Vault password:
redhat
playbook: playbook.yml
Usando o comando ansible-playbook, execute o playbook com a opção --ask-vault-pass
. Digite redhat
quando a senha for solicitada.
[student@workstation data-review]$
ansible-playbook playbook.yml --ask-vault-pass
Vault password:
redhat
PLAY [Install and configure webserver with basic auth] ********************* ...output omitted... TASK [connect to web server with basic auth] *********************************** ok: [localhost] TASK [debug] ******************************************************************* ok: [localhost] => { "auth_test.content": "serverb.lab.example.com (172.25.250.11) has been customized by Ansible.\n" } PLAY RECAP ********************************************************************* localhost : ok=3 changed=0 unreachable=0 failed=0 serverb.lab.example.com : ok=10 changed=8 unreachable=0 failed=0
Isso conclui o laboratório.
Neste capítulo, você aprendeu que:
As variáveis do Ansible permitem que os administradores reutilizem valores em todos os arquivos de um projeto do Ansible.
As variáveis podem ser definidas para hosts e grupos de hosts no arquivo de inventário.
As variáveis podem ser definidas para playbooks usando fatos e arquivos externos. Elas também podem ser definidas na linha de comando.
A palavra-chave register pode ser usada para capturar a saída de um comando em uma variável.
O Ansible Vault é uma forma de proteger dados confidenciais, como hashes de senha e chaves privadas, para implantação usando playbooks do Ansible.
Os fatos do Ansible são variáveis que são automaticamente descobertas pelo Ansible a partir de um host gerenciado.
Depois de concluir esta seção, você deverá ser capaz de usar loops para escrever tarefas eficientes e usar condições para controlar quando as tarefas são executadas.
O uso de loops poupa os administradores de ter de escrever múltiplas tarefas que usam o mesmo módulo. Por exemplo, em vez de escrever cinco tarefas para garantir que cinco usuários existem, você pode escrever uma tarefa para iterar em uma lista de cinco usuários e garantir que todos eles existem.
O Ansible dá suporte à iteração de uma tarefa em um conjunto de itens usando a palavra-chave loop
.
Você pode configurar loops para repetir uma tarefa usando cada item
em uma lista, o conteúdo de cada um dos arquivos em uma lista, uma
sequência gerada de números ou usando estruturas mais complexas.
Esta seção abrange loops simples que são iterados em uma lista de itens.
Consulte a documentação para conhecer cenários de loop mais
avançados.
Loops simples
Um loop simples itera uma tarefa em uma lista de itens. A palavra-chave loop
é adicionada à tarefa e assume como valor a lista de itens na qual a tarefa deve ser iterada. A variável de loop item
mantém o valor usado durante cada iteração.
Considere o seguinte trecho que usa o módulo service
duas vezes para garantir que os dois serviços de rede estejam em execução:
- name: Postfix is running service: name: postfix state: started - name: Dovecot is running service: name: dovecot state: started
Essas duas tarefas podem ser sobrescritas para usar um loop simples, para que somente uma tarefa seja necessária para garantir que ambos os serviços estejam em execução:
- name: Postfix and Dovecot are running service: name: "{{ item }}" state: started loop: - postfix - dovecot
A lista usada pela tarefa loop
pode ser fornecida por uma variável. No exemplo a seguir, a variável mail_services
contém a lista de serviços que precisam estar em execução.
vars: mail_services: - postfix - dovecot tasks: - name: Postfix and Dovecot are running service: name: "{{ item }}" state: started loop: "{{ mail_services }}"
Loops em uma lista de hashes ou dicionários
A lista loop
não precisa ser uma lista de valores simples. No exemplo a
seguir, cada item na lista é, na verdade, um hash ou um dicionário.
Cada hash ou dicionário no exemplo tem duas chaves, name
e groups
e o valor de cada chave na variável de loop item
atual pode ser recuperado com as variáveis item.name
e item.groups
, respectivamente.
- name: Users exist and are in the correct groups user: name: "{{ item.name }}" state: present groups: "{{ item.groups }}" loop: - name: jane groups: wheel - name: joe groups: root
O resultado da tarefa anterior é que o usuário jane
está presente e é membro do grupo wheel
e o usuário joe
está presente e é membro do grupo root
.
Palavras-chave de loop nas versões anteriores
Antes do Ansible2.5, a maioria dos playbooks utilizava uma
sintaxe diferente para loops. Eram fornecidas várias
palavras-chave de loop, com prefixos with_
,
seguido pelo nome de um plugin de pesquisa do Ansible (um recurso
avançado não explicado em detalhes neste curso). Essa sintaxe para
looping é muito comum nos playbooks existentes, mas provavelmente será
descontinuada em algum momento no futuro.
Alguns exemplos são listados na tabela abaixo:
Um exemplo de with_items
em um playbook é mostrado a seguir:
vars: data: - user0 - user1 - user2 tasks: - name: "with_items" debug: msg: "{{ item }}" with_items: "{{ data }}"
A partir do Ansible2.5, a maneira recomendada de escrever loops é usar a palavra-chave loop
.
No entanto, você ainda precisa entender a antiga sintaxe, especialmente with_items
,
porque ela é amplamente utilizada nos playbooks existentes. É
provável que você encontre playbooks e funções que continuem usando
palavras-chave with_*
para looping.
Qualquer tarefa que use a sintaxe antiga pode ser convertida para usar loop
com os filtros do Ansible. Você não precisa saber como usar os
filtros do Ansible para fazer isso. Há uma boa referência sobre como
converter os loops antigos para a nova sintaxe, bem como exemplos de
como fazer o loop em itens que não são listas simples, na documentação
do Ansible na seção Migração de with_X para loop do Guia do Usuário do Ansible.
Você provavelmente encontrará tarefas de playbooks mais antigos que contenham palavras-chave with_*
.
Técnicas de looping avançadas estão fora do escopo deste curso.
Todas as tarefas de iteração do curso podem ser implementadas com a
palavra-chave with_items
ou loop
.
Uso de variáveis de registro com loops
A palavra-chave register
também pode capturar a saída de uma tarefa que faz um loop. O
trecho a seguir mostra a estrutura da variável de registro de uma tarefa
que faz um loop:
[student@workstation loopdemo]$ cat loop_register.yml --- - name: Loop Register Test gather_facts: no hosts: localhost tasks: - name: Looping Echo Task shell: "echo This is my item: {{ item }}" loop: - one - two register: echo_results- name: Show echo_results variable debug: var: echo_results
Executar o playbook acima produz a seguinte saída:
[student@workstation loopdemo]$ ansible-playbook loop_register.yml PLAY [Loop Register Test] **************************************************** TASK [Looping Echo Task] ***************************************************** ...output omitted... TASK [Show echo_results variable] ******************************************** ok: [localhost] => { "echo_results": {"changed": true, "msg": "All items completed", "results": [
{
"_ansible_ignore_errors": null, ...output omitted... "changed": true, "cmd": "echo This is my item: one", "delta": "0:00:00.011865", "end": "2018-11-01 16:32:56.080433", "failed": false, ...output omitted... "item": "one", "rc": 0, "start": "2018-11-01 16:32:56.068568", "stderr": "", "stderr_lines": [], "stdout": "This is my item: one", "stdout_lines": [ "This is my item: one" ] }, {
"_ansible_ignore_errors": null, ...output omitted... "changed": true, "cmd": "echo This is my item: two", "delta": "0:00:00.011142", "end": "2018-11-01 16:32:56.828196", "failed": false, ...output omitted... "item": "two", "rc": 0, "start": "2018-11-01 16:32:56.817054", "stderr": "", "stderr_lines": [], "stdout": "This is my item: two", "stdout_lines": [ "This is my item: two" ] } ]
} } ...output omitted...
No exemplo acima, a chave results
contém uma lista. Abaixo, o playbook é modificado de forma que a segunda tarefa itere nessa lista:
[student@workstation loopdemo]$ cat new_loop_register.yml --- - name: Loop Register Test gather_facts: no hosts: localhost tasks: - name: Looping Echo Task shell: "echo This is my item: {{ item }}" loop: - one - two register: echo_results - name: Show stdout from the previous task. debug: msg: "STDOUT from previous task: {{ item.stdout }}" loop: "{{ echo_results['results'] }}"
Depois de executar o playbook acima, a saída será:
PLAY [Loop Register Test] **************************************************** TASK [Looping Echo Task] ***************************************************** ...output omitted... TASK [Show stdout from the previous task.] *********************************** ok: [localhost] => (item={...output omitted...}) => { "msg": "STDOUT from previous task: This is my item: one" } ok: [localhost] => (item={...output omitted...}) => { "msg": "STDOUT from previous task: This is my item: two" } ...output omitted...
O Ansible pode usar condicionais para executar tarefas ou ações quando certas condições são atendidas. Por exemplo, uma condicional pode ser usada para determinar a memória disponível em um host gerenciado antes de o Ansible instalar ou configurar um serviço.
As condicionais permitem aos administradores diferenciar entre hosts gerenciados e atribuí-los a funções práticas com base nas condições que atendem. As variáveis de playbook, as variáveis registradas e os fatos do Ansible podem ser testados com condicionais. Existem operadores disponíveis para comparar strings, dados numéricos e valores booleanos.
Os cenários a seguir ilustram o uso de condicionais no Ansible:
Um limite rígido pode ser definido em uma variável (por exemplo, min_memory
) e comparado à memória disponível em um host gerenciado.
A saída do comando pode ser capturada e avaliada pelo Ansible para determinar se uma tarefa foi concluída antes de realizar mais ações. Por exemplo, se ocorrer uma falha em um programa, um batch será ignorado.
Use os fatos do Ansible para determinar a configuração da rede de hosts gerenciados e decidir qual arquivo de template será enviado (por exemplo, bonding ou troncos de rede).
O número de CPUs pode ser avaliado para determinar como ajustar um servidor web adequadamente.
Compare uma variável registrada com uma variável predefinida para determinar se um serviço foi alterado. Por exemplo, teste a soma de verificação MD5 de um arquivo de configuração do serviço para ver se o serviço foi alterado.
Sintaxe de tarefa condicional
A instrução when
é usada para executar uma tarefa de forma condicional Ela recebe
como valor a condição a ser testada. Se a condição for atendida, a
tarefa é executada. Se a condição não for atendida, a tarefa é
ignorada.
Uma das condições mais simples que pode ser testada é se uma variável booleana é verdadeira ou falsa. A instrução when
no seguinte exemplo faz com que a tarefa seja executada apenas se run_my_task
for verdadeira:
--- - name: Simple Boolean Task Demo hosts: all vars: run_my_task: true tasks: - name: httpd package is installed yum: name: httpd when: run_my_task
O próximo exemplo é um pouco mais sofisticado e testa se a variável my_service
tem um valor. Se ela tiver, o valor de my_service
será usado como nome do pacote a ser instalado. Se a variável my_service
não estiver definida, a tarefa será ignorada sem um erro.
--- - name: Test Variable is Defined Demo hosts: all vars: my_service: httpd tasks: - name: "{{ my_service }} package is installed" yum: name: "{{ my_service }}" when: my_service is defined
A tabela a seguir mostra algumas operações que os administradores podem usar ao trabalhar com as condicionais:
A última entrada da tabela anterior pode parecer confusa à primeira vista. O exemplo a seguir ilustra como funciona.
No exemplo, a variável ansible_distribution
é um fato determinado durante a tarefa Gathering Facts
e identifica a distribuição do sistema operacional do host gerenciado. A variável supported_distros
foi criada pelo autor do playbook e contém a lista de distribuições do
sistema operacional que o playbook suporta. Se o valor ansible_distribution
estiver na lista supported_distros
, a condicional será aprovada e a tarefa, executada.
--- - name: Demonstrate the "in" keyword hosts: all gather_facts: yes vars: supported_distros: - RedHat - Fedora tasks: - name: Install httpd using yum, where supported yum: name: http state: present when: ansible_distribution in supported_distros
Observe o recuo da instrução when
. Como a instrução when
não é uma variável de módulo, ela deve ser colocada fora do módulo e recuada no nível superior da tarefa.
Uma tarefa é um hash/dicionário YAML e a instrução when
é simplesmente mais uma chave na tarefa como o nome da tarefa e o
módulo que ela usa. Uma convenção comum coloca qualquer
palavra-chave when
que possa estar presente depois do nome e módulo da tarefa (e argumentos de módulo).
Teste de condições múltiplas
Uma instrução when
pode ser usada para avaliar diversas condicionais. Para isso, as condicionais podem ser combinadas com as palavras-chave and
ou or
e agrupadas entre parênteses.
Os trechos a seguir mostram alguns exemplos de como expressar várias condições.
Se uma instrução condicional deve ser atendida quando nenhuma condição for verdadeira, use a instrução or
.
Por exemplo, a condição a seguir será atendida se a máquina
estiver executando RedHat EnterpriseLinux ou Fedora:
when: ansible_distribution == "RedHat" or ansible_distribution == "Fedora"
Com a operação and
,
as duas condições devem ser verdadeiras para que a instrução
condicional inteira seja atendida. Por exemplo, a seguinte
condição é atendida se o host remoto for o host RedHat
EnterpriseLinux7.5 e o kernel instalado for a versão especificada:
when: ansible_distribution_version == "7.5" and ansible_kernel == "3.10.0-327.el7.x86_64"
A palavra-chave when
também oferece suporte ao uso de uma lista para descrever a lista de
condições. Quando é fornecida uma lista para a palavra-chave when
palavra-chave, todas as condicionais são combinadas usando o operador and
. O exemplo abaixo demonstra outra maneira de combinar várias instruções condicionais usando o operador and
:
when: - ansible_distribution_version == "7.5" - ansible_kernel == "3.10.0-327.el7.x86_64"
Esse formato melhora a legibilidade, uma meta fundamental para gerar playbooks do Ansible bem escritos.
Instruções condicionais mais complexas podem ser expressas pelo agrupamento de condições com parênteses. Isso garante que eles sejam interpretados corretamente.
Por exemplo, a seguinte instrução condicional será atendida se a máquina estiver executando o RedHat EnterpriseLinux7 ou Fedora28: Este exemplo usa o caractere de maior que (>) para permitir a divisão da condicional longa em várias linhas no playbook, facilitando a leitura.
when: > ( ansible_distribution == "RedHat" and ansible_distribution_major_version == "7" ) or ( ansible_distribution == "Fedora" and ansible_distribution_major_version == "28" )
É possível combinar loops e condicionais.
No exemplo a seguir, o pacote mariadb-server será instalado pelo módulo yum
se houver um sistema de arquivos montado em /
com mais de 300MB livres. O fato ansible_mounts
é uma lista de dicionários, cada um representando fatos sobre o sistema
de arquivos montado. O loop itera em cada dicionário na lista e a
instrução condicional não é atendida a não ser que um dicionário seja
encontrado representando um sistema de arquivos montado no qual ambas as
condições sejam verdadeiras.
- name: install mariadb-server if enough space on root yum: name: mariadb-server state: latest loop: "{{ ansible_mounts }}" when: item.mount == "/" and item.size_available > 300000000
Quando você usar when
com loop
em uma tarefa, a instrução when
será verificada para cada item.
Veja outro exemplo que combina condicionais e variáveis de registro. O playbook anotado a seguir reinicia o serviço httpd
apenas se o serviço postfix
estiver em execução.
--- - name: Restart HTTPD if Postfix is Running hosts: all tasks: - name: Get Postfix server status command: /usr/bin/systemctl is-active postfixignore_errors: yes
register: result
- name: Restart Apache HTTPD based on Postfix status service: name: httpd state: restarted when: result.rc == 0
Depois de concluir esta seção, você deverá ser capaz de usar loops para escrever tarefas eficientes e usar condições para controlar quando as tarefas são executadas.
O uso de loops poupa os administradores de ter de escrever múltiplas tarefas que usam o mesmo módulo. Por exemplo, em vez de escrever cinco tarefas para garantir que cinco usuários existem, você pode escrever uma tarefa para iterar em uma lista de cinco usuários e garantir que todos eles existem.
O Ansible dá suporte à iteração de uma tarefa em um conjunto de itens usando a palavra-chave loop
.
Você pode configurar loops para repetir uma tarefa usando cada item
em uma lista, o conteúdo de cada um dos arquivos em uma lista, uma
sequência gerada de números ou usando estruturas mais complexas.
Esta seção abrange loops simples que são iterados em uma lista de itens.
Consulte a documentação para conhecer cenários de loop mais
avançados.
Loops simples
Um loop simples itera uma tarefa em uma lista de itens. A palavra-chave loop
é adicionada à tarefa e assume como valor a lista de itens na qual a tarefa deve ser iterada. A variável de loop item
mantém o valor usado durante cada iteração.
Considere o seguinte trecho que usa o módulo service
duas vezes para garantir que os dois serviços de rede estejam em execução:
- name: Postfix is running service: name: postfix state: started - name: Dovecot is running service: name: dovecot state: started
Essas duas tarefas podem ser sobrescritas para usar um loop simples, para que somente uma tarefa seja necessária para garantir que ambos os serviços estejam em execução:
- name: Postfix and Dovecot are running service: name: "{{ item }}" state: started loop: - postfix - dovecot
A lista usada pela tarefa loop
pode ser fornecida por uma variável. No exemplo a seguir, a variável mail_services
contém a lista de serviços que precisam estar em execução.
vars: mail_services: - postfix - dovecot tasks: - name: Postfix and Dovecot are running service: name: "{{ item }}" state: started loop: "{{ mail_services }}"
Loops em uma lista de hashes ou dicionários
A lista loop
não precisa ser uma lista de valores simples. No exemplo a
seguir, cada item na lista é, na verdade, um hash ou um dicionário.
Cada hash ou dicionário no exemplo tem duas chaves, name
e groups
e o valor de cada chave na variável de loop item
atual pode ser recuperado com as variáveis item.name
e item.groups
, respectivamente.
- name: Users exist and are in the correct groups user: name: "{{ item.name }}" state: present groups: "{{ item.groups }}" loop: - name: jane groups: wheel - name: joe groups: root
O resultado da tarefa anterior é que o usuário jane
está presente e é membro do grupo wheel
e o usuário joe
está presente e é membro do grupo root
.
Palavras-chave de loop nas versões anteriores
Antes do Ansible2.5, a maioria dos playbooks utilizava uma
sintaxe diferente para loops. Eram fornecidas várias
palavras-chave de loop, com prefixos with_
,
seguido pelo nome de um plugin de pesquisa do Ansible (um recurso
avançado não explicado em detalhes neste curso). Essa sintaxe para
looping é muito comum nos playbooks existentes, mas provavelmente será
descontinuada em algum momento no futuro.
Alguns exemplos são listados na tabela abaixo:
Um exemplo de with_items
em um playbook é mostrado a seguir:
vars: data: - user0 - user1 - user2 tasks: - name: "with_items" debug: msg: "{{ item }}" with_items: "{{ data }}"
A partir do Ansible2.5, a maneira recomendada de escrever loops é usar a palavra-chave loop
.
No entanto, você ainda precisa entender a antiga sintaxe, especialmente with_items
,
porque ela é amplamente utilizada nos playbooks existentes. É
provável que você encontre playbooks e funções que continuem usando
palavras-chave with_*
para looping.
Qualquer tarefa que use a sintaxe antiga pode ser convertida para usar loop
com os filtros do Ansible. Você não precisa saber como usar os
filtros do Ansible para fazer isso. Há uma boa referência sobre como
converter os loops antigos para a nova sintaxe, bem como exemplos de
como fazer o loop em itens que não são listas simples, na documentação
do Ansible na seção Migração de with_X para loop do Guia do Usuário do Ansible.
Você provavelmente encontrará tarefas de playbooks mais antigos que contenham palavras-chave with_*
.
Técnicas de looping avançadas estão fora do escopo deste curso.
Todas as tarefas de iteração do curso podem ser implementadas com a
palavra-chave with_items
ou loop
.
Uso de variáveis de registro com loops
A palavra-chave register
também pode capturar a saída de uma tarefa que faz um loop. O
trecho a seguir mostra a estrutura da variável de registro de uma tarefa
que faz um loop:
[student@workstation loopdemo]$ cat loop_register.yml --- - name: Loop Register Test gather_facts: no hosts: localhost tasks: - name: Looping Echo Task shell: "echo This is my item: {{ item }}" loop: - one - two register: echo_results- name: Show echo_results variable debug: var: echo_results
Executar o playbook acima produz a seguinte saída:
[student@workstation loopdemo]$ ansible-playbook loop_register.yml PLAY [Loop Register Test] **************************************************** TASK [Looping Echo Task] ***************************************************** ...output omitted... TASK [Show echo_results variable] ******************************************** ok: [localhost] => { "echo_results": {"changed": true, "msg": "All items completed", "results": [
{
"_ansible_ignore_errors": null, ...output omitted... "changed": true, "cmd": "echo This is my item: one", "delta": "0:00:00.011865", "end": "2018-11-01 16:32:56.080433", "failed": false, ...output omitted... "item": "one", "rc": 0, "start": "2018-11-01 16:32:56.068568", "stderr": "", "stderr_lines": [], "stdout": "This is my item: one", "stdout_lines": [ "This is my item: one" ] }, {
"_ansible_ignore_errors": null, ...output omitted... "changed": true, "cmd": "echo This is my item: two", "delta": "0:00:00.011142", "end": "2018-11-01 16:32:56.828196", "failed": false, ...output omitted... "item": "two", "rc": 0, "start": "2018-11-01 16:32:56.817054", "stderr": "", "stderr_lines": [], "stdout": "This is my item: two", "stdout_lines": [ "This is my item: two" ] } ]
} } ...output omitted...
No exemplo acima, a chave results
contém uma lista. Abaixo, o playbook é modificado de forma que a segunda tarefa itere nessa lista:
[student@workstation loopdemo]$ cat new_loop_register.yml --- - name: Loop Register Test gather_facts: no hosts: localhost tasks: - name: Looping Echo Task shell: "echo This is my item: {{ item }}" loop: - one - two register: echo_results - name: Show stdout from the previous task. debug: msg: "STDOUT from previous task: {{ item.stdout }}" loop: "{{ echo_results['results'] }}"
Depois de executar o playbook acima, a saída será:
PLAY [Loop Register Test] **************************************************** TASK [Looping Echo Task] ***************************************************** ...output omitted... TASK [Show stdout from the previous task.] *********************************** ok: [localhost] => (item={...output omitted...}) => { "msg": "STDOUT from previous task: This is my item: one" } ok: [localhost] => (item={...output omitted...}) => { "msg": "STDOUT from previous task: This is my item: two" } ...output omitted...
O Ansible pode usar condicionais para executar tarefas ou ações quando certas condições são atendidas. Por exemplo, uma condicional pode ser usada para determinar a memória disponível em um host gerenciado antes de o Ansible instalar ou configurar um serviço.
As condicionais permitem aos administradores diferenciar entre hosts gerenciados e atribuí-los a funções práticas com base nas condições que atendem. As variáveis de playbook, as variáveis registradas e os fatos do Ansible podem ser testados com condicionais. Existem operadores disponíveis para comparar strings, dados numéricos e valores booleanos.
Os cenários a seguir ilustram o uso de condicionais no Ansible:
Um limite rígido pode ser definido em uma variável (por exemplo, min_memory
) e comparado à memória disponível em um host gerenciado.
A saída do comando pode ser capturada e avaliada pelo Ansible para determinar se uma tarefa foi concluída antes de realizar mais ações. Por exemplo, se ocorrer uma falha em um programa, um batch será ignorado.
Use os fatos do Ansible para determinar a configuração da rede de hosts gerenciados e decidir qual arquivo de template será enviado (por exemplo, bonding ou troncos de rede).
O número de CPUs pode ser avaliado para determinar como ajustar um servidor web adequadamente.
Compare uma variável registrada com uma variável predefinida para determinar se um serviço foi alterado. Por exemplo, teste a soma de verificação MD5 de um arquivo de configuração do serviço para ver se o serviço foi alterado.
Sintaxe de tarefa condicional
A instrução when
é usada para executar uma tarefa de forma condicional Ela recebe
como valor a condição a ser testada. Se a condição for atendida, a
tarefa é executada. Se a condição não for atendida, a tarefa é
ignorada.
Uma das condições mais simples que pode ser testada é se uma variável booleana é verdadeira ou falsa. A instrução when
no seguinte exemplo faz com que a tarefa seja executada apenas se run_my_task
for verdadeira:
--- - name: Simple Boolean Task Demo hosts: all vars: run_my_task: true tasks: - name: httpd package is installed yum: name: httpd when: run_my_task
O próximo exemplo é um pouco mais sofisticado e testa se a variável my_service
tem um valor. Se ela tiver, o valor de my_service
será usado como nome do pacote a ser instalado. Se a variável my_service
não estiver definida, a tarefa será ignorada sem um erro.
--- - name: Test Variable is Defined Demo hosts: all vars: my_service: httpd tasks: - name: "{{ my_service }} package is installed" yum: name: "{{ my_service }}" when: my_service is defined
A tabela a seguir mostra algumas operações que os administradores podem usar ao trabalhar com as condicionais:
A última entrada da tabela anterior pode parecer confusa à primeira vista. O exemplo a seguir ilustra como funciona.
No exemplo, a variável ansible_distribution
é um fato determinado durante a tarefa Gathering Facts
e identifica a distribuição do sistema operacional do host gerenciado. A variável supported_distros
foi criada pelo autor do playbook e contém a lista de distribuições do
sistema operacional que o playbook suporta. Se o valor ansible_distribution
estiver na lista supported_distros
, a condicional será aprovada e a tarefa, executada.
--- - name: Demonstrate the "in" keyword hosts: all gather_facts: yes vars: supported_distros: - RedHat - Fedora tasks: - name: Install httpd using yum, where supported yum: name: http state: present when: ansible_distribution in supported_distros
Observe o recuo da instrução when
. Como a instrução when
não é uma variável de módulo, ela deve ser colocada fora do módulo e recuada no nível superior da tarefa.
Uma tarefa é um hash/dicionário YAML e a instrução when
é simplesmente mais uma chave na tarefa como o nome da tarefa e o
módulo que ela usa. Uma convenção comum coloca qualquer
palavra-chave when
que possa estar presente depois do nome e módulo da tarefa (e argumentos de módulo).
Teste de condições múltiplas
Uma instrução when
pode ser usada para avaliar diversas condicionais. Para isso, as condicionais podem ser combinadas com as palavras-chave and
ou or
e agrupadas entre parênteses.
Os trechos a seguir mostram alguns exemplos de como expressar várias condições.
Se uma instrução condicional deve ser atendida quando nenhuma condição for verdadeira, use a instrução or
.
Por exemplo, a condição a seguir será atendida se a máquina
estiver executando RedHat EnterpriseLinux ou Fedora:
when: ansible_distribution == "RedHat" or ansible_distribution == "Fedora"
Com a operação and
,
as duas condições devem ser verdadeiras para que a instrução
condicional inteira seja atendida. Por exemplo, a seguinte
condição é atendida se o host remoto for o host RedHat
EnterpriseLinux7.5 e o kernel instalado for a versão especificada:
when: ansible_distribution_version == "7.5" and ansible_kernel == "3.10.0-327.el7.x86_64"
A palavra-chave when
também oferece suporte ao uso de uma lista para descrever a lista de
condições. Quando é fornecida uma lista para a palavra-chave when
palavra-chave, todas as condicionais são combinadas usando o operador and
. O exemplo abaixo demonstra outra maneira de combinar várias instruções condicionais usando o operador and
:
when: - ansible_distribution_version == "7.5" - ansible_kernel == "3.10.0-327.el7.x86_64"
Esse formato melhora a legibilidade, uma meta fundamental para gerar playbooks do Ansible bem escritos.
Instruções condicionais mais complexas podem ser expressas pelo agrupamento de condições com parênteses. Isso garante que eles sejam interpretados corretamente.
Por exemplo, a seguinte instrução condicional será atendida se a máquina estiver executando o RedHat EnterpriseLinux7 ou Fedora28: Este exemplo usa o caractere de maior que (>) para permitir a divisão da condicional longa em várias linhas no playbook, facilitando a leitura.
when: > ( ansible_distribution == "RedHat" and ansible_distribution_major_version == "7" ) or ( ansible_distribution == "Fedora" and ansible_distribution_major_version == "28" )
É possível combinar loops e condicionais.
No exemplo a seguir, o pacote mariadb-server será instalado pelo módulo yum
se houver um sistema de arquivos montado em /
com mais de 300MB livres. O fato ansible_mounts
é uma lista de dicionários, cada um representando fatos sobre o sistema
de arquivos montado. O loop itera em cada dicionário na lista e a
instrução condicional não é atendida a não ser que um dicionário seja
encontrado representando um sistema de arquivos montado no qual ambas as
condições sejam verdadeiras.
- name: install mariadb-server if enough space on root yum: name: mariadb-server state: latest loop: "{{ ansible_mounts }}" when: item.mount == "/" and item.size_available > 300000000
Quando você usar when
com loop
em uma tarefa, a instrução when
será verificada para cada item.
Veja outro exemplo que combina condicionais e variáveis de registro. O playbook anotado a seguir reinicia o serviço httpd
apenas se o serviço postfix
estiver em execução.
--- - name: Restart HTTPD if Postfix is Running hosts: all tasks: - name: Get Postfix server status command: /usr/bin/systemctl is-active postfixignore_errors: yes
register: result
- name: Restart Apache HTTPD based on Postfix status service: name: httpd state: restarted when: result.rc == 0
Depois de concluir esta seção, você deverá ser capaz de implementar uma tarefa que seja executada somente quando outra tarefa alterar o host gerenciado.
Os módulos do Ansible são projetados para ser idempotentes. Isso significa que, em um playbook escrito adequadamente, o playbook e suas tarefas podem ser executados diversas vezes sem alterar o host gerenciado, a menos que seja necessário alterar para que o host gerenciado entre no estado desejado.
Entretanto, quando uma tarefa altera o sistema, pode ser necessário executar outra tarefa. Por exemplo, uma alteração em um arquivo de configuração do serviço pode exigir que o serviço seja recarregado para que a configuração alterada tenha efeito.
Manipuladores são tarefas que respondem a uma notificação acionada por outras tarefas. As tarefas só notificam seus manipuladores quando a tarefa provoca alguma mudança em um host gerenciado. Cada manipulador tem um nome globalmente exclusivo e é acionado no fim de um bloco de tarefas em um playbook. Se nenhuma tarefa notificar o manipulador pelo nome, ele não será executado. Se uma ou mais tarefas notificarem o manipulador, ele será executado exatamente uma vez depois que todas as outras tarefas na ação forem concluídas. Como os manipuladores são tarefas, os administradores podem usar os mesmos módulos nos manipuladores que usariam em qualquer outra tarefa. Normalmente, os manipuladores são usados para reinicializar hosts e reiniciar serviços.
Os manipuladores podem ser considerados tarefas inativas, que são acionadas apenas quando explicitamente invocadas usando uma instrução notify
. O trecho abaixo mostra como o servidor Apache é restaurado apenas pelo manipulador restart apache
quando um arquivo de configuração é atualizado e o notifica:
tasks: - name: copy demo.example.conf configuration templatetemplate: src: /var/lib/templates/demo.example.conf.template dest: /etc/httpd/conf.d/demo.example.conf notify:
- restart apache
handlers:
- name: restart apache
service:
name: httpd state: restarted
No exemplo anterior, o manipulador restart apache
será acionado quando receber uma notificação da tarefa template
de que ocorreu uma alteração. Uma tarefa pode chamar mais de um manipulador em sua seção notify
. O Ansible trata a instrução notify
como uma matriz e itera nos nomes de manipulador:
tasks: - name: copy demo.example.conf configuration template template: src: /var/lib/templates/demo.example.conf.template dest: /etc/httpd/conf.d/demo.example.conf notify: - restart mysql - restart apache handlers: - name: restart mysql service: name: mariadb state: restarted - name: restart apache service: name: httpd state: restarted
Como discutido na documentação do Ansible, há alguns pontos importantes a serem lembrados ao usar os manipuladores:
Os manipuladores sempre são executados na ordem especificada pela seção handlers
da ação. Eles não são executados na ordem em que são listados pelas instruções notify
em uma tarefa, nem na ordem em que as tarefas os notificam.
Normalmente, os manipuladores são executados após todas as
outras tarefas na ação serem concluídas. Um manipulador chamado
por uma tarefa na parte tasks
do playbook não será executado até que todas as tarefas em tasks
sejam processadas. (Existem algumas pequenas exceções em relação a isso.)
Os nomes de manipulador ficam em um namespace por ação. Caso dois manipuladores tenham o mesmo nome incorretamente, apenas um será executado.
Mesmo que mais de uma tarefa notifique um manipulador, ele é executado apenas uma vez. Se nenhuma tarefa notificar um manipulador, ele não será executado.
Se uma tarefa que inclui uma instrução notify
não relatar um resultado changed
(por exemplo, já existe um pacote instalado e a tarefa relata ok
),
o manipulador não será notificado. O manipulador será ignorado a
não ser que outra tarefa o notifique. O Ansible notifica os
manipuladores apenas se a tarefa relata o status changed
.
Os manipuladores devem executar uma ação adicional quando uma tarefa faz uma alteração em um host gerenciado. Eles não devem ser usados como substitutos para tarefas normais.
Neste exercício, você implantará manipuladores em playbooks
Resultados
Você deverá ser capaz de definir manipuladores em playbooks e notificá-los de alterações de configuração.
Em workstation
, execute lab control-handlers start para configurar o ambiente para o exercício. Este script cria o diretório do projeto /home/student/control-handlers
e faz o download dos arquivos de configuração e inventário de host do
Ansible necessários para o exercício. O diretório do projeto
também contém um playbook parcialmente concluído, configure_db.yml
.
[student@workstation ~]$
lab control-handlers start
Em workstation.lab.example.com
, abra um novo terminal e vá para o diretório de projetos /home/student/control-handlers
.
[student@workstation ~]$
cd ~/control-handlers
[student@workstation control-handlers]$
Nesse diretório, use um editor de textos para editar o arquivo do playbook configure_db.yml
.
Esse playbook instala e configura um servidor de banco de
dados. Quando a configuração do servidor de banco de dados é
alterada, o playbook aciona a reinicialização do serviço de banco de
dados e configura a senha administrativa do banco de dados.
Usando um editor de texto, revise o playbook configure_db.yml
. Ele começa com a inicialização de algumas variáveis:
--- - name: MariaDB server is installed hosts: databases vars: db_packages:- mariadb-server - python3-PyMySQL db_service: mariadb
resources_url: http://materials.example.com/labs/control-handlers
config_file_url: "{{ resources_url }}/my.cnf.standard"
config_file_dst: /etc/my.cnf
tasks:
No arquivo configure_db.yml
defina uma tarefa que usa o módulo yum
para instalar os pacotes de banco de dados necessários como definido pela variável db_packages
. Se a tarefa alterar o sistema, o banco de dados não será instalado e você precisará notificar o manipulador set db password
para definir seu usuário inicial e senha do banco de dados.
Lembre-se de que a tarefa do manipulador, se ele for notificado, não
será executada até que todas as tarefas da seção tasks
sejam executadas.
A tarefa deve ser a seguinte:
tasks: - name: "{{ db_packages }} packages are installed" yum: name: "{{ db_packages }}" state: present notify: - set db password
Adicione uma tarefa para iniciar e ativar o serviço de banco de dados. A tarefa deve ser a seguinte:
- name: Make sure the database service is running service: name: "{{ db_service }}" state: started enabled: true
Adicione uma tarefa para baixar de my.cnf.standard
para /etc/my.cnf
no host gerenciado usando o módulo get_url
. Adicione uma condição que notifique o manipulador restart db service
para reiniciar o serviço de banco de dados após uma alteração no
arquivo de configuração. A tarefa deve ser a seguinte:
- name: The {{ config_file_dst }} file has been installed get_url: url: "{{ config_file_url }}" dest: "{{ config_file_dst }}" owner: mysql group: mysql force: yes notify: - restart db service
Adicione a palavra-chave handlers
para determinar o início da lista de tarefas do manipulador. Defina o primeiro manipulador restart db service
, que reinicia o serviço mariadb
. O resultado deve ser este:
handlers: - name: restart db service service: name: "{{ db_service }}" state: restarted
Defina o segundo manipulador set db password
, que define a senha administrativa do serviço de banco de dados. O manipulador usa o módulo mysql_user
para executar o comando. O manipulador deverá ficar assim:
- name: set db password mysql_user: name: root password: redhat
Quando concluído, o playbook deverá ficar desta forma:
--- - name: MariaDB server is installed hosts: databases vars: db_packages: - mariadb-server - python3-PyMySQL db_service: mariadb resources_url: http://materials.example.com/labs/control-handlers config_file_url: "{{ resources_url }}/my.cnf.standard" config_file_dst: /etc/my.cnf tasks: - name: "{{ db_packages }} packages are installed" yum: name: "{{ db_packages }}" state: present notify: - set db password - name: Make sure the database service is running service: name: "{{ db_service }}" state: started enabled: true - name: The {{ config_file_dst }} file has been installed get_url: url: "{{ config_file_url }}" dest: "{{ config_file_dst }}" owner: mysql group: mysql force: yes notify: - restart db service handlers: - name: restart db service service: name: "{{ db_service }}" state: restarted - name: set db password mysql_user: name: root password: redhat
Antes de executar o playbook, verifique se a sintaxe está correta executando ansible-playbook com a opção --syntax-check
.
Se ela relatar algum erro, corrija-o antes de ir para a
próxima etapa. Você verá uma saída semelhante à seguinte:
[student@workstation control-handlers]$
ansible-playbook configure_db.yml \
>
--syntax-check
playbook: configure_db.yml
Execute o playbook configure_db.yml
. A saída mostra que os manipuladores estão sendo executados.
[student@workstation control-handlers]$
ansible-playbook configure_db.yml
PLAY [Installing MariaDB server] ********************************************* TASK [Gathering Facts] ******************************************************* ok: [servera.lab.example.com] TASK [['mariadb-server', 'python3-PyMySQL'] packages are installed] ********** changed: [servera.lab.example.com] TASK [Make sure the database service is running] ***************************** changed: [servera.lab.example.com] TASK [The /etc/my.cnf file has been installed] ******************************* changed: [servera.lab.example.com] RUNNING HANDLER [restart db service] ***************************************** changed: [servera.lab.example.com] RUNNING HANDLER [set db password] ******************************************** changed: [servera.lab.example.com] PLAY RECAP ******************************************************************* servera.lab.example.com : ok=6 changed=5 unreachable=0 failed=0
Execute o playbook novamente.
[student@workstation control-handlers]$
ansible-playbook configure_db.yml
PLAY [Installing MariaDB server] ********************************************* TASK [Gathering Facts] ******************************************************* ok: [servera.lab.example.com] TASK [['mariadb-server', 'python3-PyMySQL'] packages are installed] ********** ok: [servera.lab.example.com] TASK [Make sure the database service is running] ***************************** ok: [servera.lab.example.com] TASK [The /etc/my.cnf file has been installed] ******************************* ok: [servera.lab.example.com] PLAY RECAP ******************************************************************* servera.lab.example.com : ok=4 changed=0 unreachable=0 failed=0
Nesse momento, os manipuladores são ignorados. Caso
o arquivo de configuração remoto seja alterado no futuro, a execução do
playbook acionará o manipulador restart db service
, mas não o manipulador set db password
.
Isso conclui o exercício orientado.
Objetivos
Depois de concluir esta seção, você deverá ser capaz de controlar o que acontece quando há falha em uma tarefa e quais condições fazem com que uma tarefa falhe. Gerenciamento de erros de tarefas em ações
O Ansible avalia o código de retorno de cada tarefa para determinar se a tarefa foi bem-sucedida ou apresentou erro. Normalmente, quando ocorre uma falha em uma tarefa, o Ansible imediatamente aborta a ação nesse host, ignorando todas as tarefas subsequentes.
Entretanto, algumas vezes, você quer que a ação de um playbook prossiga mesmo que ocorra uma falha na tarefa. Por exemplo, você pode esperar que uma tarefa em particular poderia falhar e pode desejar recuperá-la executando alguma outra tarefa de forma condicional. Há diversos recursos do Ansible que podem ser usados para gerenciar os erros em tarefas.
Ignorar falhas em tarefas
Por padrão, se ocorrer uma falha em uma tarefa, a ação é abortada. Entretanto, esse comportamento pode ser substituído por ignorar as tarefas que falharem. Você pode usar a palavra-chave ignore_errors em uma tarefa para conseguir isso.
O trecho a seguir mostra como usar ignore_errors em uma tarefa para prosseguir a execução do playbook no host mesmo que ocorra uma falha na tarefa. Por exemplo, se o pacote notapkg não existir, ocorre uma falha no módulo yum. Porém, definir ignore_errors como yes permite que a execução continue.
Execução forçada de manipuladores após a falha da tarefa
Normalmente, quando uma tarefa falha e a ação é abortada no host, qualquer manipulador que tiver sido notificado por tarefas anteriores no host não será executado. Se você configurar a palavra-chave force_handlers: yes na ação, os manipuladores notificados são chamados, mesmo que a ação tenha sido abortada devido a uma falha de uma tarefa posterior.
O trecho abaixo mostra como usar a palavra-chave force_handlers em uma ação para forçar a execução do manipulador mesmo que ocorra uma falha em uma tarefa:
hosts: all force_handlers: yes tasks:
name: a task which always notifies its handler command: /bin/true notify: restart the database
name: a task which fails because the package doesn't exist yum: name: notapkg state: latest
handlers:
Lembre-se de que os manipuladores são notificados quando uma tarefa relata um resultado changed, mas não são notificados quando ela relata um resultado ok ou failed.
Especificação de condições de falha de tarefas
Você pode usar a palavra-chave failed_when em uma tarefa para especificar quais condições indicam que ocorreu uma falha na tarefa. Isso é frequentemente usado com módulos de comando que podem executar com êxito um comando, mas a saída do comando indica uma falha.
Por exemplo, você pode executar um script que tenha como saída uma mensagem de erro e usar essa mensagem para definir o estado de falha da tarefa. O seguinte trecho mostra como a palavra-chave failed_when pode ser usada em uma tarefa:
tasks:
O módulo fail também pode ser usado para forçar a falha de uma tarefa. Como alternativa, o cenário acima pode ser escrito como duas tarefas:
tasks:
name: Run user creation script shell: /usr/local/bin/create_users.sh register: command_result ignore_errors: yes
name: Report script failure fail: msg: "The password is missing in the output" when: "'Password missing' in command_result.stdout"
Você pode usar o módulo fail para fornecer uma mensagem de falha clara para a tarefa. Essa abordagem também permite atrasar a falha, possibilitando a execução de tarefas intermediárias para concluir ou reverter outras alterações.
Especificação de quando uma tarefa relata resultados changed
Quando uma tarefa faz uma alteração em um host gerenciado, ela relata o estado changed e notifica os manipuladores. Quando uma tarefa não precisa fazer uma alteração, ela relata ok e não notifica os manipuladores.
A palavra-chave changed_when pode ser usada para controlar quando uma tarefa relata que ela foi alterada. Por exemplo, o módulo shell no próximo exemplo está sendo usado para obter credenciais do Kerberos que serão usadas por tarefas subsequentes. Normalmente, ela sempre relataria changed quando fosse executada. Para suprimir essa alteração, a opção changed_when: false é configurada para que relate apenas ok ou failed.
O seguinte exemplo usa o módulo shell para relatar changed com base na saída do módulo que é coletado por uma variável registrada:
tasks:
handlers:
Os blocos e o tratamento de erros do Ansible
Nos playbooks, blocos são cláusulas que agrupam tarefas de forma lógica e podem ser usadas para controlar como as tarefas são executadas. Por exemplo, um bloco de tarefas pode ter a palavra-chave when para aplicar uma condicional a múltiplas tarefas:
Os blocos também permitem o tratamento de erros em combinação com as instruções rescue e always. Se ocorrer uma falha em qualquer tarefa em um bloco, as tarefas em seu bloco rescue são executadas para fazer a recuperação. Depois que as tarefas na cláusula block forem executadas, assim como as tarefas na cláusula rescue se houver uma falha, as tarefas na cláusula always serão executadas. Para resumir:
block: define as principais tarefas a serem executadas.
rescue: define as tarefas que serão executadas se ocorrer uma falha naquelas definidas na cláusula block.
always: define as tarefas que serão sempre executadas, independentemente do êxito ou da falha das tarefas definidas nas cláusulas block e rescue.
O exemplo a seguir mostra como implementar um bloco em um playbook. Mesmo que ocorra uma falha nas tarefas definidas na cláusula block, as tarefas definidas nas cláusulas rescue e always são executadas.
tasks:
A condição when em uma cláusula block também se aplica às cláusulas rescue e always, se existentes.
Error Handling in Playbooks — Ansible Documentation
Error Handling — Blocks — Ansible Documentation
Neste exercício, você explorará diferentes maneiras de controlar falhas de tarefas em um playbook do Ansible.
Resultados
Você deverá ser capaz de:
Ignorar comandos com falha durante a execução dos playbooks.
Forçar a execução dos manipuladores.
Substituir o que constituir uma falha nas tarefas.
Substituir o estado changed das tarefas.
Implementar block, rescue e always em playbooks.
Em workstation, execute o script de início do laboratório para confirmar se o ambiente está pronto para que o laboratório comece. Esse script cria o diretório de trabalho, /home/student/control-errors.
[student@workstation ~]$ lab control-errors start
Em workstation.lab.example.com, altere para o diretório de projetos /home/student/control-errors.
[student@workstation ~]$ cd ~/control-errors
[student@workstation control-errors]$
O script do laboratório criou um arquivo de configuração do Ansible e um arquivo de inventário que contém o servidor servera.lab.example.com no grupo databases. Revise o arquivo antes de continuar.
Crie o playbook playbook.yml que contém uma ação com duas tarefas. Escreva a primeira tarefa com um erro deliberado que cause falha.
Abra o playbook em um editor de texto. Defina três variáveis: web_package com o valor http, db_package com o valor mariadb-server e db_service com o valor mariadb. Essas variáveis serão usadas para instalar os pacotes necessários e iniciar o servidor.
O valor http é um erro intencional no nome do pacote. O arquivo deverá exibir o seguinte texto:
---
- name: Task Failure Exercise
hosts: databases
vars:
web_package: http
db_package: mariadb-server
db_service: mariadb
Defina duas tarefas que usam o mesmo módulo yum e as duas variáveis, web_package e db_package. As tarefas instalarão os pacotes necessários. As tarefas devem ser as seguintes:
tasks:
- name: Install {{ web_package }} package
yum:
name: "{{ web_package }}"
state: present
- name: Install {{ db_package }} package
yum:
name: "{{ db_package }}"
state: present
Execute o playbook e observe a saída da ação.
[student@workstation control-errors]$ ansible-playbook playbook.yml
PLAY [Task Failure Exercise] ***************************************************
TASK [Gathering Facts] *********************************************************
ok: [servera.lab.example.com]
TASK [Install http package] ****************************************************
fatal: [servera.lab.example.com]: FAILED! => {"changed": false, "failures": ["No package http available."], "msg": "Failed to install some of the specified packages", "rc": 1, "results": []}
PLAY RECAP *********************************************************************
servera.lab.example.com : ok=1 changed=0 unreachable=0 failed=1
Ocorreu uma falha na tarefa porque não há um pacote existente chamado http. Como ocorreu uma falha na primeira tarefa, a segunda tarefa não foi executada.
Atualize a primeira tarefa para que ignore erros adicionando a palavra-chave ignore_errors. As tarefas devem ser as seguintes:
tasks:
- name: Install {{ web_package }} package
yum:
name: "{{ web_package }}"
state: present
ignore_errors: yes
- name: Install {{ db_package }} package
yum:
name: "{{ db_package }}"
state: present
Execute o playbook novamente e observe a saída da ação.
[student@workstation control-errors]$ ansible-playbook playbook.yml
PLAY [Task Failure Exercise] ***************************************************
TASK [Gathering Facts] *********************************************************
ok: [servera.lab.example.com]
TASK [Install http package] ****************************************************
fatal: [servera.lab.example.com]: FAILED! => {"changed": false, "failures": ["No package http available."], "msg": "Failed to install some of the specified packages", "rc": 1, "results": []}
...ignoring
TASK [Install mariadb-server package] ******************************************
changed: [servera.lab.example.com]
PLAY RECAP *********************************************************************
servera.lab.example.com : ok=3 changed=1 unreachable=0 failed=0
Apesar de ter ocorrido uma falha na primeira tarefa, o Ansible executou a segunda.
Nesta etapa, configuraremos uma palavra-chave block para que você teste como ela funciona.
Atualize o playbook aninhando a primeira tarefa em uma cláusula block. Remova a linha que define ignore_errors: yes. O bloco deve ficar assim:
- name: Attempt to set up a webserver
block:
- name: Install {{ web_package }} package
yum:
name: "{{ web_package }}"
state: present
Aninhe a tarefa que instala o pacote mariadb-server em uma cláusula rescue. A tarefa será executada se ocorrer uma falha na tarefa listada na cláusula block. A cláusula block deve ficar assim:
rescue:
- name: Install {{ db_package }} package
yum:
name: "{{ db_package }}"
state: present
Por fim, adicione uma cláusula always para iniciar o servidor de banco de dados após a instalação usando o módulo service. A cláusula deve ser a seguinte:
always:
- name: Start {{ db_service }} service
service:
name: "{{ db_service }}"
state: started
A seção da tarefa concluída deve ficar assim:
tasks:
- name: Attempt to set up a webserver
block:
- name: Install {{ web_package }} package
yum:
name: "{{ web_package }}"
state: present
rescue:
- name: Install {{ db_package }} package
yum:
name: "{{ db_package }}"
state: present
always:
- name: Start {{ db_service }} service
service:
name: "{{ db_service }}"
state: started
Agora, execute o playbook novamente e observe a saída.
Execute o playbook. Ocorrerá uma falha na tarefa no bloco que garante que web_package está instalada, o que fará com que a tarefa no bloco rescue seja executada. Então, a tarefa no bloco always é executada.
[student@workstation control-errors]$ ansible-playbook playbook.yml
PLAY [Task Failure Exercise] ***************************************************
TASK [Gathering Facts] *********************************************************
ok: [servera.lab.example.com]
TASK [Install http package] ****************************************************
fatal: [servera.lab.example.com]: FAILED! => {"changed": false, "failures": ["No package http available."], "msg": "Failed to install some of the specified packages", "rc": 1, "results": []}
TASK [Install mariadb-server package] ******************************************
ok: [servera.lab.example.com]
TASK [Start mariadb service] ***************************************************
changed: [servera.lab.example.com]
PLAY RECAP *********************************************************************
servera.lab.example.com : ok=3 changed=1 unreachable=0 failed=1
Edite o playbook, corrigindo o valor da variável web_package para que leia httpd. Isso fará com que a tarefa no bloco seja bem-sucedida da próxima vez que você executar o playbook.
vars:
web_package: httpd
db_package: mariadb-server
db_service: mariadb
Execute o playbook novamente. Desta vez, não ocorrerá falha na tarefa do bloco. Isso faz com que a tarefa na seção rescue seja ignorada. A tarefa em always ainda será executada.
[student@workstation control-errors]$ ansible-playbook playbook.yml
PLAY [Task Failure Exercise] ***************************************************
TASK [Gathering Facts] *********************************************************
ok: [servera.lab.example.com]
TASK [Install httpd package] ***************************************************
changed: [servera.lab.example.com]
TASK [Start mariadb service] ***************************************************
ok: [servera.lab.example.com]
PLAY RECAP *********************************************************************
servera.lab.example.com : ok=3 changed=1 unreachable=0 failed=0
Esta etapa explora como controlar a condição que faz com que uma tarefa seja relatada como changed para o host gerenciado.
Edite o playbook para que adicione duas tarefas no início da ação, precedendo o block. A primeira tarefa usa o módulo command para executar o comando date e registrar o resultado na variável command_result. A segunda tarefa usa o módulo debug para imprimir a saída padrão do comando da primeira tarefa.
tasks:
- name: Check local time
command: date
register: command_result
- name: Print local time
debug:
var: command_result.stdout
Execute o playbook. Você verá que a primeira tarefa que executa o módulo command relatará changed, mesmo se ela não tiver alterado o sistema remoto. Ela só coletou informações sobre o tempo. Isso se deve ao fato de o módulo command não saber distinguir entre um comando que coleta dados e um comando que altera o estado.
[student@workstation control-errors]$ ansible-playbook playbook.yml
PLAY [Task Failure Exercise] ***************************************************
TASK [Gathering Facts] *********************************************************
ok: [servera.lab.example.com]
TASK [Check local time] ********************************************************
changed: [servera.lab.example.com]
TASK [Print local time] ********************************************************
ok: [servera.lab.example.com] => {
"command_result.stdout": "mié mar 27 08:07:08 EDT 2019"
}
TASK [Install httpd package] ***************************************************
ok: [servera.lab.example.com]
TASK [Start mariadb service] ***************************************************
ok: [servera.lab.example.com]
PLAY RECAP *********************************************************************
servera.lab.example.com : ok=5 changed=1 unreachable=0 failed=0
Se você executar o playbook novamente, a tarefa Check local time relatará changed novamente.
Essa tarefa command não deve relatar changed toda vez que for executada porque ela não está alterando o host gerenciado. Como você sabe que a tarefa nunca alterará o host gerenciado, adicione a linha changed_when: false à tarefa para suprimir a alteração.
tasks:
- name: Check local time
command: date
register: command_result
changed_when: false
- name: Print local time
debug:
var: command_result.stdout
Execute o playbook mais uma vez e observe se a tarefa agora relata ok, mas ainda está sendo executada e salvando o tempo na variável.
[student@workstation control-errors]$ ansible-playbook playbook.yml
PLAY [Task Failure Exercise] ***************************************************
TASK [Gathering Facts] *********************************************************
ok: [servera.lab.example.com]
TASK [Check local time] ********************************************************
ok: [servera.lab.example.com]
TASK [Print local time] ********************************************************
ok: [servera.lab.example.com] => {
"command_result.stdout": "mié mar 27 08:08:36 EDT 2019"
}
TASK [Install httpd package] ***************************************************
ok: [servera.lab.example.com]
TASK [Start mariadb service] ***************************************************
ok: [servera.lab.example.com]
PLAY RECAP *********************************************************************
servera.lab.example.com : ok=5 changed=0 unreachable=0 failed=0
Como exercício final, edite o playbook para explorar como a palavra-chave failed_when interage com as tarefas.
Edite a tarefa Install {{ web_package }} package para que ela relate como se tivesse ocorrido uma falha quando web_package tiver o valor httpd. Já que este é o caso, a tarefa relatará uma falha quando você executar a ação.
Tenha cuidado com seu recuo para garantir que a palavra-chave esteja corretamente configurada na tarefa.
- block:
- name: Install {{ web_package }} package
yum:
name: "{{ web_package }}"
state: present
failed_when: web_package == "httpd"
Execute o playbook.
[student@workstation control-errors]$ ansible-playbook playbook.yml
PLAY [Task Failure Exercise] ***************************************************
TASK [Gathering Facts] *********************************************************
ok: [servera.lab.example.com]
TASK [Check local time] ********************************************************
ok: [servera.lab.example.com]
TASK [Print local time] ********************************************************
ok: [servera.lab.example.com] => {
"command_result.stdout": "mié mar 27 08:09:35 EDT 2019"
}
TASK [Install httpd package] ***************************************************
fatal: [servera.lab.example.com]: FAILED! => {"changed": false, "failed_when_result": true, "msg": "Nothing to do", "rc": 0, "results": ["Installed: httpd"]}
TASK [Install mariadb-server package] ******************************************
ok: [servera.lab.example.com]
TASK [Start mariadb service] ***************************************************
ok: [servera.lab.example.com]
PLAY RECAP *********************************************************************
servera.lab.example.com : ok=5 changed=0 unreachable=0 failed=1
Analise cuidadosamente a saída. A tarefa Install httpd package relata que ocorreu uma falha, mas ela, na verdade, foi executada e garantiu que o pacote fosse instalado primeiro. A palavra-chave failed_when altera o status que a tarefa relatar depois que a tarefa for executada, mas não altera o comportamento da tarefa em si.
Contudo, a falha relatada pode alterar o comportamento do resto da ação. Já que a tarefa estava em um bloco e relatou que ocorreu uma falha, a tarefa Install mariadb-server package na seção rescue do bloco foi executada.
Encerramento
Em workstation, execute o script lab control-errors finish para limpar os recursos criados neste exercício.
[student@workstation ~]$ lab control-errors finish
Isso conclui o exercício orientado.
Lista de verificação de desempenho
Neste laboratório, você instalará o servidor web Apache e o protegerá usando mod_ssl. Você usará condições, manipuladores e controle de falhas de tarefas no seu playbook para implantar o ambiente.
Resultados
Você deverá ser capaz de definir condicionais nos playbooks do Ansible, configurar loops que iteram em elementos, definir manipuladores em playbooks e controlar erros de tarefas.
Faça login como o usuário student na workstation e execute lab control-review start. Esse script garante que o host gerenciado, serverb, seja acessível na rede. Isso também garante que o arquivo de configuração Ansible e o inventário corretos estejam instalados no nó de controle.
[student@workstation ~]$ lab control-review start
Na workstation, mude para o diretório do projeto /home/student/control-review.
[student@workstation ~]$ cd ~/control-review
[student@workstation control-review]$
O diretório de projeto contém um playbook parcialmente concluído, playbook.yml. Usando um editor de texto, adicione uma tarefa que use o módulo failed no comentário #Fail Fast Message. Forneça um nome apropriado para a tarefa. Ela deverá ser executada somente quando o sistema remoto não atender aos requisitos mínimos.
Os requisitos mínimos para o host remoto estão listados abaixo:
Tem pelo menos a quantidade de RAM especificada pela variável min_ram_mb. A variável min_ram_mb é definida no arquivo vars.yml e tem o valor 256.
Está executando o RedHat EnterpriseLinux.
A tarefa concluída corresponde a:
tasks:
- name: Show Failed System Requirements Message
fail:
msg: "The {{ inventory_hostname }} did not meet minimum reqs."
when: >
ansible_memtotal_mb < min_ram_mb or
ansible_distribution != "RedHat"
Adicione uma única tarefa ao playbook no comentário #Install all Packages para instalar a versão mais recente dos pacotes ausentes. Os pacotes obrigatórios são especificados pela variável packages, que é definida no arquivo vars.yml.
O nome da tarefa deve ser Ensure required packages are present.
A tarefa concluída corresponde a:
#Install all Packages
- name: Ensure required packages are present
yum:
name: "{{ packages }}"
state: latest
Adicione uma única tarefa ao playbook no comentário #Enable and start services para iniciar todos os serviços. Todos os serviços especificados pela variável services, que é definida no arquivo vars.yml, devem ser iniciados e ativados. Forneça um nome apropriado para a tarefa.
A tarefa concluída corresponde a:
#Enable and start services
- name: Ensure services are started and enabled
service:
name: "{{ item }}"
state: started
enabled: yes
loop: "{{ services }}"
Adicione um bloco de tarefas ao playbook no comentário #Block of config tasks. Esse bloco contém duas tarefas:
Uma tarefa para garantir que o diretório especificado pela variável ssl_cert_dir existe no host remoto. Esse diretório armazena os certificados do servidor web.
Uma tarefa para copiar todos os arquivos especificados pela variável web_config_files para o host remoto. Examine a estrutura da variável web_config_files no arquivo vars.yml. Configure a tarefa para copiar cada arquivo para o destino correto no host remoto.
Esta tarefa deverá acionar o manipulador restart web service se algum desses arquivos for alterado no servidor remoto.
Além disso, uma tarefa de depuração será executada se uma das duas tarefas acima falhar. Nesse caso, a tarefa imprimirá a mensagem: One or more of the configuration changes failed, but the web service is still active.
Forneça um nome apropriado para todas as tarefas.
O bloco de tarefas concluído corresponde a:
#Block of config tasks
- name: Setting up the SSL cert directory and config files
block:
- name: Create SSL cert directory
file:
path: "{{ ssl_cert_dir }}"
state: directory
- name: Copy Config Files
copy:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
loop: "{{ web_config_files }}"
notify: restart web service
rescue:
- name: Configuration Error Message
debug:
msg: >
One or more of the configuration
changes failed, but the web service
is still active.
O playbook configura o host remoto para ouvir solicitações HTTPS padrão. Adicione uma única tarefa ao playbook no comentário #Configure the firewall para configurar firewalld.
Essa tarefa deve garantir que o host remoto permita conexões HTTP e HTTPS padrão. As alterações de configuração devem entrar em vigor imediatamente e persistir após uma reinicialização do sistema. Forneça um nome apropriado para a tarefa.
A tarefa concluída corresponde a:
#Configure the firewall
- name: ensure web server ports are open
firewalld:
service: "{{ item }}"
immediate: true
permanent: true
state: enabled
loop:
- http
- https
Defina o manipulador restart web service.
Quando for acionada, essa tarefa deve reiniciar o serviço web definido pela variável web_service, definida no arquivo vars.yml.
A seção handlers é adicionada ao final do playbook.
handlers:
O playbook completo contém:
name: Playbook Control Lab hosts: webservers vars_files: vars.yml tasks:
name: Setting up the SSL cert directory and config files block:
name: Create SSL cert directory file: path: "{{ ssl_cert_dir }}" state: directory
name: Copy Config Files copy: src: "{{ item.src }}" dest: "{{ item.dest }}" loop: "{{ web_config_files }}" notify: restart web service
rescue:
handlers:
No diretório do projeto, ~/control-review, execute o playbook playbook.yml. O playbook deve ser executado sem erros e acionar a execução da tarefa do manipulador.
[student@workstation control-review]$ ansible-playbook playbook.yml
PLAY [Playbook Control Lab] **
TASK [Gathering Facts] *** ok: [serverb.lab.example.com]
TASK [Show Failed System Requirements Message] *** skipping: [serverb.lab.example.com]
TASK [Ensure required packages are present] ** changed: [serverb.lab.example.com]
TASK [Ensure services are started and enabled] *** changed: [serverb.lab.example.com] => (item=httpd) ok: [serverb.lab.example.com] => (item=firewalld)
TASK [Create SSL cert directory] ***** changed: [serverb.lab.example.com]
TASK [Copy Config Files] ***** changed: [serverb.lab.example.com] => (item={'src': 'server.key', 'dest': '/etc/httpd/conf.d/ssl'}) changed: [serverb.lab.example.com] => (item={'src': 'server.crt', 'dest': '/etc/httpd/conf.d/ssl'}) changed: [serverb.lab.example.com] => (item={'src': 'ssl.conf', 'dest': '/etc/httpd/conf.d'}) changed: [serverb.lab.example.com] => (item={'src': 'index.html', 'dest': '/var/www/html'})
TASK [ensure web server ports are open] ** changed: [serverb.lab.example.com] => (item=http) changed: [serverb.lab.example.com] => (item=https)
RUNNING HANDLER [restart web service] **** changed: [serverb.lab.example.com]
PLAY RECAP *** serverb.lab.example.com : ok=7 changed=6 unreachable=0 failed=0
Verifique se o servidor web agora responde a solicitações HTTPS, usando o certificado personalizado autoassinado para criptografar a conexão. A resposta do servidor web deve corresponder à string Configured for both HTTP and HTTPS.
[student@workstation control-review]$ curl -k -vvv https://serverb.lab.example.com
Avaliação
Execute o comando lab control-review grade em workstation para confirmar o êxito deste exercício. Corrija todas as falhas relatadas e execute novamente o script até que ele seja concluído com êxito.
[student@workstation ~]$ lab control-review grade
Encerramento
Execute o comando lab control-review finish para fazer uma limpeza após o laboratório.
[student@workstation ~]$ lab control-review finish
Isso conclui o laboratório.
Neste capítulo, você aprendeu que:
Os loops são usados para iterar em um conjunto de valores, por exemplo, uma lista simples de strings ou uma lista de hashes ou dicionários.
As condicionais são usadas para executar tarefas ou ações apenas quando certas condições forem atendidas.
Manipuladores são tarefas especiais e são executados ao final da ação se notificados por outras tarefas.
Os manipuladores são notificados apenas quando uma tarefa informa que alterou algo em um host gerenciado.
As tarefas são configuradas para controlar condições de erro ignorando uma falha de tarefa, forçando a chamada de manipuladores mesmo que uma tarefa apresente falha, marcando uma tarefa como com falha quando ela teve êxito ou substituindo o comportamento que faz com que uma tarefa seja marcada como alterada.
Os blocos são usados para agrupar tarefas como uma unidade e executar outras tarefas dependendo do êxito ou falha das tarefas no bloco.
Objetivos
Depois de concluir esta seção, você deverá ser capaz de escrever um playbook básico do Ansible e executá-lo usando o comando ansible-playbook.
Playbooks do Ansible e comandos ad hoc
Os comandos ad hoc podem ser executados como uma tarefa simples e única em um conjunto de hosts direcionados como um comando avulso. O verdadeiro poder do Ansible, contudo, está em aprender como usar playbooks para executar múltiplas tarefas complexas em um conjunto de hosts direcionados de uma maneira facilmente reproduzível.
Uma ação é um conjunto ordenado de tarefas executadas em relação a hosts selecionados no inventário. Um playbook é um arquivo de texto contendo uma lista de uma ou mais ações a serem executadas em uma ordem específica.
Os playbooks permitem que você altere um conjunto longo e complexo de tarefas manuais em uma rotina facilmente reproduzível com resultados previsíveis e com êxito. Em um playbook, você pode salvar a sequência de tarefas em uma ação em um formato legível e imediatamente executável. As tarefas em si, devido à maneira na qual elas foram escritas, documentam as etapas necessárias para implantar seu aplicativo ou infraestrutura.
Formato de um playbook do Ansible
Para ajudar você a entender o formato de um playbook, revise este comando ad hoc de um capítulo anterior:
Ele pode ser reescrito como uma ação de tarefa única e salvo em um playbook. O playbook resultante terá o seguinte conteúdo:
Um playbook é um arquivo de texto escrito em formato YAML e é normalmente salvo com a extensão
yml
. O playbook usa recuo com caracteres de espaço para indicar a estrutura de seus dados. O YAML não estabelece requisitos rigorosos em relação à quantidade de espaços usada para o recuo, mas há duas regras básicas.Os elementos de dados no mesmo nível da hierarquia (como itens na mesma lista) devem ter o mesmo recuo.
Itens que são filhos de um outro item devem ter mais recuo que os seus pais.
Você também pode adicionar linhas em branco por questões de legibilidade.
Apenas o caractere de espaço pode ser usado como recuo; caracteres de tabulação não são permitidos.
Se você usar o editor de texto vi, poderá aplicar algumas configurações que podem facilitar a edição dos seus playbooks. Por exemplo, você pode adicionar a linha a seguir ao seu arquivo
$HOME/.vimrc
e, quando vi detectar que você está editando um arquivo YAML, ele fará um recuo com dois espaços quando a tecla Tab for pressionada. As linhas seguintes serão automaticamente recuadas.Um playbook começa com uma linha composta por três traços (
---
) como início do marcador de documento. Ele pode terminar em três pontos (...
) como final de marcador de documento, embora, na prática, isso seja frequentemente omitido.Entre esses marcadores, o playbook é definido como uma lista de ações. Um item em uma lista YAML começa com um único traço seguido por um espaço. Por exemplo, uma lista YAML poderá aparecer com o seguinte conteúdo:
Em , a linha depois de
---
começa com um traço e inicia a primeira ação (e somente ela) na lista de ações.A própria ação é uma coleção de pares de chave-valor. As chaves em uma mesma ação devem ter o mesmo recuo. O exemplo a seguir mostra um trecho YAML com três chaves. As duas primeiras chaves têm valores simples. A terceira tem uma lista de três itens como um valor.
A ação de exemplo original tem três chaves,
name
,hosts
etasks
, porque todas essas chaves têm o mesmo recuo.A primeira linha da ação de exemplo começa com um traço e um espaço (indicando que a ação é o primeiro item de uma lista) e, então, a primeira chave, o atributo
name
. A chavename
associa com a ação uma string arbitrária de rótulo. Isso identifica para que serve a ação. A chavename
é opcional, mas é recomendada, já que ela ajuda a documentar seu playbook. Isso é particularmente útil quando um playbook contém várias ações.A segunda chave na ação é um atributo
hosts
, que especifica os hosts nos quais as tarefas da ação são executadas. Assim como o argumento para o comando ansible, o atributohosts
toma um padrão de host como um valor, como os nomes de hosts gerenciados ou grupos no inventário.Finalmente, a última chave na ação é o atributo
tasks
, cujo valor especifica uma lista de tarefas a serem executadas para esta ação. Este exemplo tem uma única tarefa, que executa o módulouser
com argumentos específicos (para garantir que o usuárionewbie
existe e que tem uma UID 4000).O atributo
tasks
é a parte da ação que realmente lista, em ordem, as tarefas a serem executadas nos hosts gerenciados. Cada tarefa na lista é, em si, uma coleção de pares de chave-valor.Neste exemplo, a única tarefa na ação tem duas chaves:
name
é um rótulo opcional que documenta o propósito da tarefa. É recomendável nomear todas as suas tarefas para ajudar a documentar o propósito de cada etapa do processo de automação.user
é o módulo a ser executado para esta tarefa. Seus argumentos são passados como uma coleção de pares de chave-valor, que são filhos do módulo (name
,uid
estate
).Abaixo, é apresentado outro exemplo de um atributo
tasks
com múltiplas tarefas, usando o móduloservice
para garantir que vários serviços de rede estejam habilitados para iniciar durante o boot:A ordem na qual as ações e tarefas são listadas em um playbook é importante porque o Ansible as executa na mesma ordem.
Os playbooks que você viu até agora são exemplos básicos, e você verá exemplos mais sofisticados do que pode fazer com ações e tarefas à medida que este curso prosseguir.
Execução de playbooks
O comando ansible-playbook é usado para executar playbooks. O comando é executado no nó de controle e o nome do playbook a ser executado é passado como um argumento:
Quando você executa o playbook, a saída é gerada para mostrar a ação e as tarefas sendo executadas. A saída também relata os resultados de cada tarefa executada.
O exemplo a seguir mostra o conteúdo de um playbook simples e o resultado de sua execução.
Observe que o valor da chave
name
de cada ação e tarefa é exibido quando o playbook é executado. (A tarefaGathering Facts
é uma tarefa especial que o módulosetup
normalmente executa de forma automática no início de uma ação. Isso é abordado posteriormente no curso). Para playbooks com múltiplas ações e tarefas, a definição dos atributosname
torna fácil monitorar o progresso da execução do playbook.Você também deverá ver que a tarefa
latest httpd version installed
éalterada
paraservera.lab.example.com
. Isso significa que a tarefa alterou algo nesse host para garantir que sua especificação foi atendida. Neste caso, significa que o pacote httpd provavelmente não foi instalado ou não era a versão mais recente.Em geral, as tarefas nos playbooks do Ansible são idempotentes e é seguro executar o playbook várias vezes. Se os hosts gerenciados direcionados já estiverem no estado correto, nenhuma mudança deve ser feita. Por exemplo, suponha que o playbook do exemplo anterior é executado novamente:
Dessa vez, todas as tarefas foram passadas com o status
ok
e nenhuma alteração foi relatada.Aumento do detalhamento de saída
A saída padrão fornecida pelo comando ansible-playbook não fornece informações detalhadas de execução de tarefas. O comando ansible-playbook -v oferece informações adicionais, com um total de quatro níveis.
Verificação de sintaxe
Antes de executar um playbook, é recomendável realizar uma verificação para garantir que a sintaxe do conteúdo esteja correta. O comando ansible-playbook oferece uma opção
--syntax-check
que pode ser usada para verificar a sintaxe de um playbook. O exemplo a seguir mostra verificação com êxito da sintaxe de um playbook.Quando uma verificação de sintaxe falha, um erro de sintaxe é reportado. A saída também inclui o local aproximado do problema de sintaxe no playbook. O exemplo a seguir mostra uma verificação de sintaxe de um playbook que falhou, na qual o separador de espaço está ausente após o atributo
name
da ação.Execução de uma simulação
Você pode usar a opção
-C
para executar uma simulação da execução do playbook. Isso faz com que o Ansible relate que mudanças teriam ocorrido se o playbook fosse executado, mas não faz nenhuma mudança real nos hosts gerenciados.O exemplo a seguir mostra a simulação de um playbook contendo uma tarefa única para garantir que a versão mais recente do pacote httpd esteja instalada em um host gerenciado. Observe que a simulação informa que a tarefa realizaria uma mudança no host gerenciado.
Página do man
ansible-playbook
(1)Intro to Playbooks — Ansible Documentation
Playbooks — Ansible Documentation
Check Mode ("Dry Run") — Ansible Documentation