Tutorial

Gerenciamento de Configuração 101: Escrevendo Receitas do Chef

Published on March 12, 2020
Português
Gerenciamento de Configuração 101: Escrevendo Receitas do Chef

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 Chef, uma poderosa ferramenta de gerenciamento de configuração que aproveita a linguagem de programação Ruby para automatizar a administração e o provisionamento da infraestrutura. Vamos nos concentrar na terminologia da linguagem, sintaxe e recursos necessários para criar um exemplo simplificado para automatizar completamente o deployment de um servidor web Ubuntu 18.04 usando o Apache.

Esta é a lista de 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 (document root)
  4. Colocar um arquivo index.html no document root personalizado
  5. Aplicar um template para configurar nosso virtual host personalizado
  6. Reiniciar o Apache

Começaremos examinando a terminologia usada pelo Chef, seguida de uma visão geral dos principais recursos da linguagem que podem ser usados para escrever recipes ou receitas. No final deste guia, compartilharemos o exemplo completo para que você possa experimentar sozinho.

Nota: este guia pretende apresentar a linguagem do Chef e como escrever receitas para automatizar o provisionamento de servidor. Para uma visão mais introdutória do Chef, incluindo as etapas necessárias para instalar e começar com esta ferramenta, consulte a documentação oficial do Chef.

Começando

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

Termos do Chef

  • Chef Server: um servidor central que armazena informações e gerencia o provisionamento dos nodes
  • Chef Node: um servidor individual que é gerenciado por um Chef Server
  • Chef Workstation: uma máquina controladora na qual os provisionamentos são criados e carregados para o Chef Server
  • Recipe (receita): um arquivo que contém um conjunto de instruções (recursos) a serem executados. Uma receita deve estar contida dentro de um Cookbook
  • Resource (recurso): uma parte do código que declara um elemento do sistema e que ação deve ser executada. Por exemplo, para instalar um pacote, declaramos um recurso package com a ação install
  • Cookbook (livro de receitas): uma coleção de receitas e outros arquivos relacionados organizados de forma predefinida para facilitar o compartilhamento e a reutilização de partes de um provisionamento
  • Attributes (atributos): detalhes sobre um node específico. Os Attributes (atributos) podem ser automáticos (veja a próxima definição) e também podem ser definidos nas receitas
  • Automatic Attributes: variáveis globais que contêm informações sobre o sistema, como interfaces de rede e sistema operacional (conhecidos como facts em outras ferramentas). Esses atributos automáticos são coletados por uma ferramenta chamada Ohai
  • Services: usado para acionar alterações de status do serviço, como reiniciar ou parar um serviço

Formato da Receita

As receitas do Chef são escritas usando Ruby. Uma receita é basicamente uma coleção de definições de recursos que criarão um conjunto passo a passo de instruções a serem executadas pelos nodes. Essas definições de recursos podem ser combinadas com o código Ruby para maior flexibilidade e modularidade.

Abaixo você pode encontrar um exemplo simples de uma receita que irá executar o apt-get update e instalar o vim posteriormente:

execute "apt-get update" do
 command "apt-get update"
end

apt_package "vim" do
 action :install
end

Escrevendo Receitas

Trabalhando com Variáveis

As variáveis locais podem ser definidas nas receitas como variáveis locais regulares do Ruby. O exemplo abaixo mostra como criar uma variável local que é usada posteriormente dentro de uma definição de recurso:

package  = "vim"

apt_package package do
 action :install
end

Essas variáveis, no entanto, têm um escopo limitado, sendo válidas apenas dentro do arquivo em que foram definidas. Se você deseja criar uma variável e disponibilizá-la globalmente, para poder usá-la em qualquer um dos seus cookbooks ou receitas, você precisa definir um atributo personalizado.

Usando Atributos

Os atributos representam detalhes sobre um node. O Chef possui atributos automáticos, que são os atributos coletados por uma ferramenta chamada Ohai e contêm informações sobre o sistema (como plataforma, nome do host e endereço IP padrão), mas também permitem definir seus próprios atributos personalizados.

Os atributos têm níveis de precedência diferentes, definidos pelo tipo de atributo que você cria. Os atributos default são a escolha mais comum, pois ainda podem ser sobrescritos por outros tipos de atributos quando desejado.

O exemplo a seguir mostra como o exemplo anterior seria com um atributo de node default em vez de uma variável local:

node.default['main']['package'] = "vim"

apt_package node['main']['package'] do
 action :install
end

Há dois detalhes a serem observados neste exemplo:

A prática recomendada ao definir variáveis de node é organizá-las como hashes usando o cookbook atual em uso como chave. Nesse caso, usamos main, porque temos um cookbook com o mesmo nome. Isso evita confusão se você estiver trabalhando com vários cookbooks que podem ter atributos com nomes semelhantes. Observe que usamos node.default ao definir o atributo, mas ao acessar seu valor posteriormente, usamos node diretamente. O uso do node.default define que estamos criando um atributo do tipo default. Esse atributo pode ter seu valor sobrescrito por outro tipo com precedência mais alta, como atributos normal ou override.

A precedência dos atributos pode ser um pouco confusa no início, mas você se acostumará com isso depois de alguma prática. Para ilustrar o comportamento, considere o seguinte exemplo:

node.normal['main']['package']  = "vim"

node.override['main']['package'] = "git"

node.default['main']['package'] = "curl"

apt_package node['main']['package'] do
 action :install
end

Você sabe qual pacote será instalado neste caso? Se você achou que é o git, adivinhou corretamente. Independentemente da ordem em que os atributos foram definidos, a maior precedência do tipo override fará com que o node['main']['package'] seja avaliado como git.

Usando Loops

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

O Chef suporta todas as estruturas de loop Ruby para criar loops dentro de receitas. Para uso simples, each é uma escolha comum:

['vim', 'git', 'curl'].each do |package|
 apt_package package do
   action :install
 end
end

Em vez de usar um array inline, você também pode criar uma variável ou atributo para definir os parâmetros que deseja usar dentro do loop. Isso manterá as coisas mais organizadas e fáceis de ler. Abaixo, o mesmo exemplo agora usando uma variável local para definir os pacotes que devem ser instalados:

packages = ['vim', 'git', 'curl']

packages.each do |package|
 apt_package package do
   action :install
 end
end

Usando Condicionais

Condicionais podem ser usadas para decidir dinamicamente se um bloco de código deve ou não ser executado, com base em uma variável ou em uma saída de um comando, por exemplo.

O Chef suporta todos as condicionais do Ruby para criar instruções condicionais nas receitas. Além disso, todos os tipos de recursos suportam duas propriedades especiais que avaliarão uma expressão antes de decidir se a tarefa deve ser executada ou não: if_only e not_if.

O exemplo abaixo verificará a existência do php antes de tentar instalar a extensão php-pear. Ele usará o comando which para verificar se existe um executável php atualmente instalado neste sistema. Se o comando which php retornar falso, esta tarefa não será executada:

apt_package "php-pear" do
 action :install
 only_if "which php"
end

Se quisermos fazer o oposto, executando um comando o tempo todo exceto quando uma condição é avaliada como verdadeira, usamos not_if. Este exemplo instalará o php5, a menos que o sistema seja o CentOS:

apt_package "php5" do
 action :install
 not_if { node['platform'] == 'centos' }
end

Para realizar avaliações mais complexas, ou se você desejar executar várias tarefas sob uma condição específica, você pode usar qualquer uma das condicionais padrão do Ruby. O exemplo a seguir só executará apt-get update quando o sistema for Debian ou Ubuntu:

if node['platform'] == 'debian' || node['platform'] == 'ubuntu'
 execute "apt-get update" do
   command "apt-get update"
 end
end

O atributo node['platform'] é um atributo automático do Chef. O último exemplo foi apenas para demonstrar uma construção condicional mais complexa, no entanto, pode ser substituída por um teste simples usando o atributo automático node['platform_family'], que retornaria “debian” para os sistemas Debian e Ubuntu.

Trabalhando com Templates

Os templates geralmente são usados para definir 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 Chef usa templates Embedded Ruby (ERB), que é o mesmo formato usado pelo Puppet. Eles suportam condicionais, loops e outros recursos do Ruby.

Abaixo está um exemplo de um modelo de ERB para configurar um virtual host no Apache, usando uma variável para definir o document root para esse host:

<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot <%= @doc_root %>

    <Directory <%= @doc_root %>>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

Para aplicar o template, precisamos criar um recurso template. É assim que você aplicaria esse template para substituir o virtual host padrão do Apache:

template "/etc/apache2/sites-available/000-default.conf" do
 source "vhost.erb"
 variables({ :doc_root => node['main']['doc_root'] })
 action :create
end 

O Chef faz algumas suposições ao lidar com arquivos locais, a fim de reforçar a organização e a modularidade. Nesse caso, o Chef procuraria um arquivo de template vhost.erb dentro de uma pasta templates que deveria estar no mesmo cookbook em que esta receita está localizada.

Ao contrário das outras ferramentas de gerenciamento de configuração que vimos até agora, o Chef tem um escopo mais estrito para variáveis. Isso significa que você precisará fornecer explicitamente todas as variáveis que planeja usar dentro de um template, ao definir o recurso template. Neste exemplo, usamos o método variables para passar adiante o atributo doc_root que precisamos no modelo de virtual host.

Definindo e Acionando Serviços

Os recursos de serviço são usados para garantir que os serviços sejam inicializados e ativados. Eles também são usados para acionar a reinicialização do serviço.

No Chef, os recursos de serviço precisam ser declarados antes de você tentar notificá-los, caso contrário, você receberá um erro.

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 mudança no virtual host, primeiro crie um recurso service para o serviço Apache. É assim que esse recurso é definido no Chef:

service "apache2" do
  action [ :enable, :start ]
end

Agora, ao definir o recurso template, você precisa incluir uma opção notify para acionar uma reinicialização:

template "/etc/apache2/sites-available/000-default.conf" do
 source "vhost.erb"
 variables({ :doc_root => node['main']['doc_root'] })
 action :create
 notifies :restart, resources(:service => "apache2")
end

Exemplo de Receita

Agora, vamos dar uma olhada em uma receita que automatizará a instalação de um servidor Web Apache em um sistema Ubuntu 14.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 a receita em uma configuração simplificada, usando uma máquina virtual gerenciada pelo Vagrant.

Abaixo você pode encontrar a receita completa:

  1. node.default['main']['doc_root'] = "/vagrant/web"
  2. execute "apt-get update" do
  3. command "apt-get update"
  4. end
  5. apt_package "apache2" do
  6. action :install
  7. end
  8. service "apache2" do
  9. action [ :enable, :start ]
  10. end
  11. directory node['main']['doc_root'] do
  12. owner 'www-data'
  13. group 'www-data'
  14. mode '0644'
  15. action :create
  16. end
  17. cookbook_file "#{node['main']['doc_root']}/index.html" do
  18. source 'index.html'
  19. owner 'www-data'
  20. group 'www-data'
  21. action :create
  22. end
  23. template "/etc/apache2/sites-available/000-default.conf" do
  24. source "vhost.erb"
  25. variables({ :doc_root => node['main']['doc_root'] })
  26. action :create
  27. notifies :restart, resources(:service => "apache2")
  28. end

Receita Explicada

linha 1

A receita começa com uma definição de atributo, node['main']['doc_root']. Poderíamos ter usado uma variável local simples aqui, no entanto, na maioria dos cenários de casos de uso, as receitas precisam definir variáveis globais que serão usadas nas receitas incluídas ou em outros arquivos. Para essas situações, é necessário criar um atributo em vez de uma variável local, pois a última possui um escopo limitado.

linhas 3-5

Esse recurso execute executa um apt-get update.

linhas 7-10

Este recurso apt_package instala o pacote apache2.

linhas 12-15

Este recurso service ativa e inicia o serviço apache2. Posteriormente, precisaremos notificar esse recurso para reiniciar o serviço. É importante que a definição de serviço venha antes de qualquer recurso que tente notificar um serviço, caso contrário, você receberá um erro.

linhas 17-22

Este recurso directory usa o valor definido pelo atributo personalizado node['main']['doc_root'] para criar um diretório que servirá como nosso document root

linhas 24-29

Um recurso cookbook_file é usado para copiar um arquivo local em um servidor remoto. Este recurso copiará nosso arquivo index.html e o colocará dentro do document root que criamos em uma tarefa anterior.

linhas 31-36

Por fim, esse recurso template aplica nosso template de virtual host do Apache e notifica o serviço apache2 para uma reinicialização.

Conclusão

O Chef é uma poderosa ferramenta de gerenciamento de configuração que utiliza a linguagem Ruby para automatizar o provisionamento e o deployment de servidores. Ele lhe oferece liberdade para usar os recursos de linguagem padrão para obter o máximo de flexibilidade, além de oferecer DSLs personalizadas para alguns recursos.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors
Default avatar

Developer Advocate

Dev/Ops passionate about open source, PHP, and Linux.



Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Become a contributor for community

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

DigitalOcean Documentation

Full documentation for every DigitalOcean product.

Resources for startups and SMBs

The Wave has everything you need to know about building a business, from raising funding to marketing your product.

Get our newsletter

Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

New accounts only. By submitting your email you agree to our Privacy Policy

The developer cloud

Scale up as you grow — whether you're running one virtual machine or ten thousand.

Get started for free

Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

*This promotional offer applies to new accounts only.