Gerenciamento de Configuração 101: Escrevendo Playbooks Ansible

Introdução

Em poucas palavras, o gerenciamento de configuração de servidor (também conhecido como IT Automation) é uma solução para transformar a sua administração de infraestrutura em uma base de código, descrevendo todos os processos necessários para fazer o deploy de um servidor em um conjunto de scripts de provisionamento que podem ser versionados e facilmente reutilizados. Isso pode melhorar muito a integridade de qualquer infraestrutura de servidor ao longo do tempo.

Em um guia anterior, falamos sobre os principais benefícios da implementação de uma estratégia de gerenciamento de configuração para a infraestrutura de servidor, como as ferramentas de gerenciamento de configuração funcionam e o que essas ferramentas têm em comum normalmente.

Esta parte da série o guiará pelo processo de automatização do provisionamento de servidores usando o Ansible, uma ferramenta de gerenciamento de configuração que fornece uma framework completo de automação e recursos de orquestração, enquanto mantém um objetivo final de simplicidade e minimalismo. Vamos nos concentrar na terminologia da linguagem, na sintaxe e nos recursos necessários para criar um exemplo simplificado para automatizar completamente o deployment de um servidor web Ubuntu 18.04 usando o Apache.

A lista a seguir contém todas os passos que precisamos automatizar para alcançar nosso objetivo:

  1. Atualizar o cache do apt
  2. Instalar o Apache
  3. Criar um diretório raiz de documentos personalizado
  4. Colocar um arquivo index.html no diretório raiz de documentos personalizado
  5. Aplicar um template para configurar nosso virtual host personalizado
  6. Reiniciar o Apache

Começaremos examinando a terminologia usada pelo Ansible, seguida de uma visão geral dos principais recursos da linguagem que podem ser usados para escrever playbooks. No final do guia, você encontrará o conteúdo de um exemplo completo de provisionamento para automatizar os passos descritos para configurar o Apache no Ubuntu 18.04.

Nota: este guia tem como objetivo apresentar a você a linguagem do Ansible e como escrever playbooks para automatizar o provisionamento do seu servidor. Para uma visão mais introdutória do Ansible, incluindo as etapas necessárias para a instalação e sobre como inciar o uso dessa ferramenta, bem como a execução de comandos e playbooks do Ansible, consulte nosso guia Como instalar e configurar o Ansible no Ubuntu 18.04

Iniciando

Antes de podermos avançar para uma visão mais prática do Ansible, é importante nos familiarizarmos com importantes terminologias e conceitos introduzidos por essa ferramenta.

Terminologia

A lista a seguir contém uma rápida visão geral dos termos mais relevantes usados pelo Ansible:

  • Control Node: a máquina em que o Ansible está instalado, responsável pela execução do provisionamento nos servidores que você está gerenciando.
  • Inventory: um arquivo INI que contém informações sobre os servidores que você está gerenciando.
  • Playbook: um arquivo YAML contendo uma série de procedimentos que devem ser automatizados.
  • Task: um bloco que define um único procedimento a ser executado, por exemplo: instalar um pacote.
  • Module: um módulo normalmente abstrai uma tarefa do sistema, como lidar com pacotes ou criar e alterar arquivos. O Ansible possui diversos módulos integrados, mas você também pode criar módulos personalizados.
  • Role: um conjunto de playbooks, modelos e outros arquivos relacionados, organizados de maneira predefinida para facilitar a reutilização e o compartilhamento.
  • Play: um provisionamento executado do início ao fim é chamado de play.
  • Facts: variáveis globais que contêm informações sobre o sistema, como interfaces de rede ou sistema operacional.
  • Handlers: usado para acionar alterações no status do serviço, como reiniciar ou recarregar um serviço.

Formato da Task

Uma task ou tarefa define uma única etapa automatizada que deve ser executada pelo Ansible. Geralmente envolve o uso de um módulo ou a execução de um comando raw. É assim que uma tarefa se parece:

- name: Esta é uma task   apt: name=vim state=latest 

A parte do name é opcional na verdade, mas recomendada, pois aparece na saída do provisionamento quando a tarefa é executada. A parte do apt é um módulo Ansible embutido que abstrai o gerenciamento de pacotes em distribuições baseadas no Debian. Este exemplo de tarefa informa ao Ansible que o pacote vim deve ter seu estado alterado para latest, o que fará com que o gerenciador de pacotes instale este pacote, caso ainda não esteja instalado.

Formato do Playbook

Playbooks são arquivos YAML que contêm uma série de diretivas para automatizar o provisionamento de um servidor. O exemplo a seguir é um playbook simples que executa duas tasks: atualiza o cache do apt e instala o vim posteriormente:

--- - hosts: all   become: true   tasks:      - name: Update apt-cache         apt: update_cache=yes       - name: Install Vim        apt: name=vim state=latest 

O YAML depende da identação para serializar estruturas de dados. Por esse motivo, ao escrever playbooks e, especialmente, ao copiar exemplos, você precisa ter cuidado extra para manter a identação correta.

Antes do final deste guia, veremos um exemplo mais real de um playbook, explicado em detalhes. A próxima seção fornecerá uma visão geral dos elementos e recursos mais importantes que podem ser usados para escrever playbooks do Ansible.

Escrevendo Playbooks

Agora que você conhece a terminologia básica e o formato geral dos playbooks e tasks no Ansible, aprenderemos sobre alguns recursos do playbook que podem nos ajudar a criar automações mais versáteis.

Trabalhando com Variables

Existem diferentes maneiras pelas quais você pode definir variáveis no Ansible. A maneira mais simples é usar a seção vars de um playbook. O exemplo abaixo define uma variável package que mais tarde é usada dentro de uma task:

--- - hosts: all   become: true   vars:      package: vim   tasks:      - name: Install Package        apt: name={{ package }} state=latest 

A variável package possui um escopo global, o que significa que pode ser acessada a partir de qualquer ponto do provisionamento, mesmo a partir dos arquivos e modelos incluídos.

Usando Loops

Os loops são normalmente usados para repetir uma task usando diferentes valores de entrada. Por exemplo, em vez de criar 10 tasks para instalar 10 pacotes diferentes, você pode criar uma única task e usar um loop para repetir a tarefa com todos os pacotes diferentes que deseja instalar.

Para criar um loop dentro de uma task, inclua a opção with_items com uma matriz de valores. O conteúdo pode ser acessado através da variável de loop item, conforme mostrado no exemplo abaixo:

- name: Install Packages   apt: name={{ item }} state=latest   with_items:      - vim      - git      - curl   

Você também pode usar uma variável matriz para definir seus itens:

--- - hosts: all   become: true   vars:      packages: [ 'vim', 'git', 'curl' ]   tasks:      - name: Install Package        apt: name={{ item }} state=latest        with_items: "{{ packages }}" 

Usando Condicionais

Condicionais podem ser usados para decidir dinamicamente se uma tarefa deve ou não ser executada, baseado em uma variável ou em uma saída de um comando, por exemplo.

O exemplo a seguir vai encerrar apenas os sistemas baseados em Debian:

- name: Shutdown Debian Based Systems   command: /sbin/shutdown -t now   when: ansible_os_family == "Debian" 

O condicional when recebe como argumento uma expressão a ser avaliada. A task é executada apenas no caso de a expressão ser avaliada como true. Em nosso exemplo, testamos um fact para verificar se o sistema operacional é da família Debian.

Um caso de uso comum para condicionais na automação de TI é quando a execução de uma task depende da saída de um comando. Com o Ansible, a maneira como implementamos isso é registrando uma variável para armazenar os resultados de uma execução de comando e testando essa variável em uma tarefa subseqüente. Podemos testar o status de saída do comando (se houve falha ou êxito). Também podemos verificar um conteúdo específico dentro da saída, embora isso possa exigir o uso de expressões regulares (regex) e comandos de parsing de string.

O próximo exemplo mostra duas tarefas condicionais baseadas na saída de um comando php -v. Testaremos o status de saída do comando, pois sabemos que ele falhará na execução caso o PHP não esteja instalado neste servidor. A parte ignore_errors da task é importante para garantir que o provisionamento continue mesmo quando o comando falha na execução.

- name: Check if PHP is installed   register: php_installed   command: php -v   ignore_errors: true  - name: This task is only executed if PHP is installed   debug: var=php_install   when: php_installed|success  - name: This task is only executed if PHP is NOT installed   debug: msg='PHP is NOT installed'   when: php_installed|failed 

O módulo debug usado aqui é um módulo útil para mostrar o conteúdo de variáveis ou mensagens de depuração. Ele pode imprimir uma string (ao usar o argumento msg) ou imprimir o conteúdo de uma variável (ao usar o argumentovar).

Trabalhando com Templates

Os templates são usados geralmente para configurar arquivos de configuração, permitindo o uso de variáveis e outros recursos destinados a tornar esses arquivos mais versáteis e reutilizáveis. O Ansible usa o mecanismo de template Jinja2.

O exemplo a seguir é um template para configurar um virtual host no Apache, usando uma variável para configurar a raiz de documentos para este host:

<VirtualHost *:80>     ServerAdmin [email protected]     DocumentRoot {{ doc_root }}      <Directory {{ doc_root }}>         AllowOverride All         Require all granted     </Directory> </VirtualHost> 

O módulo interno template é usado para aplicar o template a partir de uma tarefa. Se você nomeou o arquivo de template acima de vhost.tpl e o colocou no mesmo diretório que seu playbook, é assim que você aplicaria o template para substituir o virtual host padrão do Apache:

- name: Change default Apache virtual host   template:      src: vhost.tpl     dest: /etc/apache2/sites-available/000-default.conf 

Definindo e Acionando Handlers

Handlers são usados para acionar uma alteração de estado em um serviço, como um restart ou um stop. Mesmo que possam parecer bastante semelhantes às tasks regulares, os handlers são executados apenas quando acionados anteriormente a partir de uma diretiva notify em uma task. Eles geralmente são definidos como uma matriz em uma seção handlers do playbook, mas também podem estar em arquivos separados.

Vamos levar em consideração o nosso exemplo anterior de uso de templates, onde configuramos um virtual host no Apache. Se você deseja garantir que o Apache seja reiniciado após uma alteração no virtual host, primeiro você precisa criar um handler para o serviço Apache. É assim que os handlers são definidos dentro de um playbook:

handlers:     - name: restart apache       service: name=apache2 state=restarted      - name: other handler       service: name=other state=restarted 

A diretiva name aqui é importante porque ela será o identificador exclusivo desse handler. Para acionar esse handler a partir de uma tarefa, você deve usar a opção notify:

- name: Change default Apache virtual host   template:      src: vhost.tpl     dest: /etc/apache2/sites-available/000-default.conf   notify: restart apache 

Vimos alguns dos recursos mais importantes que você pode usar para começar a escrever playbooks Ansible. Na próxima seção, veremos um exemplo mais real de um playbook que automatizará a instalação e configuração do Apache no Ubuntu.

Playbook de Exemplo

Agora, vamos dar uma olhada em um playbook que automatizará a instalação de um servidor web Apache dentro de um sistema Ubuntu 18.04, conforme discutido na introdução deste guia.

O exemplo completo, incluindo o arquivo de template para configurar o Apache e um arquivo HTML para ser servido pelo servidor web, pode ser encontrado no Github. A pasta também contém um arquivo Vagrant que permite testar o playbook em uma configuração simplificada, usando uma máquina virtual gerenciada pelo Vagrant.

Conteúdo do Playbook

O conteúdo completo do playbook está disponível aqui para sua conveniência:

playbook.yml

  • ---
  • - hosts: all
  • become: true
  • vars:
  • doc_root: /var/www/example
  • tasks:
  • - name: Update apt
  • apt: update_cache=yes
  • - name: Install Apache
  • apt: name=apache2 state=latest
  • - name: Create custom document root
  • file: path={{ doc_root }} state=directory owner=www-data group=www-data
  • - name: Set up HTML file
  • copy: src=index.html dest={{ doc_root }}/index.html owner=www-data group=www-data mode=0644
  • - name: Set up Apache virtual host file
  • template: src=vhost.tpl dest=/etc/apache2/sites-available/000-default.conf
  • notify: restart apache
  • handlers:
  • - name: restart apache
  • service: name=apache2 state=restarted

Vamos examinar cada parte deste playbook em mais detalhes:

hosts: all
O playbook começa afirmando que deve ser aplicado a todos os hosts em seu inventário (hosts: all). É possível restringir a execução do playbook a um host específico ou a um grupo de hosts. Esta opção pode ser substituída no momento da execução.

become: true
A parte become: true diz ao Ansible para usar a escalação de privilégios (sudo) para executar todas as tarefas neste playbook. Esta opção pode ser substituída em uma base de task por task.

vars
Define uma variável, doc_root, que é usada posteriormente em uma task. Esta seção pode conter várias variáveis.

tasks
A seção em que as tasks reais são definidas. A primeira task atualiza o cache do apt e a segunda task instala o pacote apache2.

A terceira task usa o módulo interno file para criar um diretório para servir como a raiz de documentos. Este módulo pode ser usado para gerenciar arquivos e diretórios.

A quarta task usa o módulo copy para copiar um arquivo local para o servidor remoto. Estamos copiando um arquivo HTML simples para servir como nosso site hospedado pelo Apache.

handlers
Finalmente, temos a seção handlers, onde os serviços são declarados. Definimos o handler restart apache que é notificado a partir da quarta tarefa, onde o template Apache é aplicado.

Executando um Playbook

Depois de baixar o conteúdo deste playbook para o control node Ansible, você pode usar o ansible-playbook para executá-lo em um ou mais nodes do seu inventário. O comando a seguir executará o playbook em todos os hosts do seu arquivo de inventário padrão, usando a autenticação de par de chaves SSH para conectar-se como o usuário atual do sistema:

  • ansible-playbook playbook.yml

Você também pode usar -l para limitar a execução a um único host ou a um grupo de hosts do seu inventário:

  • ansible-playbook -l host_ou_grupo playbook.yml

Se você precisar especificar um usuário SSH diferente para se conectar ao servidor remoto, poderá incluir o argumento -u user nesse comando:

  • ansible-playbook -l host_ou_grupo playbook.yml -u usuário_remoto

Para obter mais informações sobre como executar comandos Ansible e playbooks, consulte o nosso guia Como instalar e configurar o Ansible no Ubuntu 18.04.

Conclusão

O Ansible é uma ferramenta minimalista de automação de TI que possui uma baixa curva de aprendizado, usando o YAML para seus scripts de provisionamento. Ele possui um grande número de módulos internos que podem ser usados para abstrair tasks, como instalar pacotes e trabalhar com templates. Seus requisitos simplificados de infraestrutura e linguagem simples podem ser uma boa opção para quem está iniciando o gerenciamento de configurações. No entanto, pode haver alguns recursos avançados que você pode encontrar em ferramentas mais complexas, como Puppet e Chef.

Na próxima parte desta série, veremos uma visão geral prática do Puppet, uma ferramenta de gerenciamento de configuração popular e bem estabelecida que usa uma DSL personalizada expressiva e poderosa baseada no Ruby para escrever scripts de provisionamento.