Table of Contents

objetivo

Nesta wiki vou abordar o funcionamento do puppet localmente, sem integrá-lo a um servidor puppetmaster, este passo é importante para entendermos como o puppet funciona.

Vamos estudar e entender sua sintaxe e seus principais recursos.

Podemos chamar o puppet que roda localmente de autônomo ou serverless.

Após entendermos o puppet autônomo, vamos prosseguir estudando o puppet funcionando em modo cliente/servidor - se preferir pode chamar de modo agente/mestre.

sobre o puppet

O Puppet essencialmente é um framework opensource e um conjunto de ferramentas e soluções para gerência de configurações.

O puppet faz parte de uma nova geração de sistemas para automatização de servidores e serviços, ele nos permite tratar infraestrutura como código.

Ele é composto por uma linguagem declarativa que nos permite expressar as configurações que os nossos servidores, sistemas e serviços devem ter, fazendo isto através de uma sintaxe simples e prática, sendo natural para sysadmins.

Ele nos oferece duas formas de funcionamento, a primeira podemos chamar de autônoma, neste modo nós criamos e executamos nossas configurações localmente. Além do modo autônomo, o puppet também funciona no modelo cliente (agente) e servidor (master) para distribuir estas configurações para todo o parque de servidores através da rede.

Aqui vamos estudar e compreender o modo cliente/servidor.

colaboradores e agradecimentos

Agradeço o apoio dos bons amigos Douglas Andrade, Daniel Sobral, Ulysses Almeida e José Pissin que foram a grande motivação para escrever esta primeira wiki sobre o Puppet.

Agradeço também ao @coredump por ter apresentado ao puppet para a equipe de sysadmins no projeto EBC.

Agradeço também aos leitores do blog/wiki – Bruno Anjos e Junior Alvin – por mandarem sugestões e correções durante a elaboração desta página.

premissas e dependências

Inicio essa wiki partindo da premissa de que você já estudou os seguintes posts

O entendimento pleno desta wiki se apóia e depende destes, portanto leia com calma e tranquilidade antes de prosseguir.

instalando puppet

Para começar precisamos instalar o Puppet em nosso sistema, aqui vou abordar a instalação em Debian e Centos.

centos

requisitos

Habilite o repositorio Puppetlabs criando o arquivo abaixo

/etc/yum/yum.repo.d/puppet.repo
[puppetlabs]
name=Puppet Labs Packages
baseurl=http://yum.puppetlabs.com/el/6/products/x86_64/
enabled=1
gpgcheck=0
 
[puppetlabs2]
name=Puppet Labs Packages Deps
baseurl=http://yum.puppetlabs.com/el/6/dependencies/x86_64/
enabled=1
gpgcheck=0

Habilite o repositório Epel criando o arquivo abaixo

/etc/yum/yum.repo.d/epel.repo
[epel]
name=Extra Packages for Enterprise Linux 6 - $basearch
#baseurl=http://download.fedoraproject.org/pub/epel/6/$basearch
mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-6&arch=$basearch
failovermethod=priority
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6

Veja a versão do pacote puppet

[root@galaxy yum.repos.d]# yum info puppet

Acompanhe a saída

  Loaded plugins: fastestmirror
  Loading mirror speeds from cached hostfile
  Installed Packages
  Name        : puppet
  Arch        : noarch
  Version     : 2.7.13
  Release     : 1.el6
  Size        : 3.0 M
  Repo        : installed
  From repo   : puppetlabs
  Summary     : A network tool for managing many disparate systems
  URL         : http://puppetlabs.com
  License     : ASL 2.0
  Description : Puppet lets you centrally manage every important aspect of your system using a
            : cross-platform specification language that manages all the separate elements
            : normally aggregated in different files, like users, cron jobs, and hosts,
            : along with obviously discrete elements like packages, services, and files.

instalando puppet

yum install puppet

debian

requisitos

Habilite o repositório squeeze-backports adicionando a linha abaixo ao seu sources.list

deb http://ftp.debian.org/debian squeeze main contrib non-free
deb http://apt.puppetlabs.com squeeze main
deb http://security.debian.org/ squeeze/updates main contrib non-free

Puxando chave puppetlabs

gpg --recv-key 4BD6EC30
gpg -a --export 4BD6EC30 | sudo apt-key add -

Atualize os índices

aptitude update

Veja se a versão do pacote é 2.7.x

puppetagent:~# aptitude show puppet
  
Pacote: puppet                               
Estado: instalado
Automaticamente instalado: não
Versão: 2.7.14-1puppetlabs1
Prioridade: opcional
Seção: admin
Mantenedor: Puppet Labs <info@puppetlabs.com>
Tamanho Descompactado: 410 k
Depende de: puppet-common (= 2.7.14-1puppetlabs1), ruby1.8
Recomenda: rdoc
Sugere: puppet-el, vim-puppet
Descrição: Centralized configuration management - agent startup and compatibility scripts
 This package contains the startup script and compatbility scripts for the puppet agent, which is the process responsible for configuring the local node. 
 
 Puppet lets you centrally manage every important aspect of your system using a cross-platform specification language that manages all the separate elements
 normally aggregated in different files, like users, cron jobs, and hosts, along with obviously discrete elements like packages, services, and files. 
 
 Puppet's simple declarative specification language provides powerful classing abilities for drawing out the similarities between hosts while allowing them to
 be as specific as necessary, and it handles dependency and prerequisite relationships between objects clearly and explicitly.
Página web: http://projects.puppetlabs.com/projects/puppet

instalando puppet

aptitude install puppet

início

Estou me baseando na documentação do Puppet para apresentar suas funcionalidades, nas referências vocês vão encontrar os links que permitiram que essa documentação fosse criada.

RAL

Podemos dizer que o puppet é composto por um conjunto de recursos (resources), são através destes recursos que construiremos nossas configurações.

Um usuário pode ser considerado um recurso, um arquivo pode ser considerado um recurso, um pacote pode ser considerado um recurso, um serviço que está rodando pode ser considerado um recurso, uma rotina do cron pode ser considerada um recurso, um alias de correio pode ser considerado um recurso, e até mesmo a execução de um comando pode ser considerado um recurso para o puppet.

Cada recurso em sua essência é similar a uma classe com seus atributos, por exemplo, um arquivo tem um caminho no filesystem (path), um dono (owner), um grupo dono (group) e permissões (mode), já um usuário tem nome, id de usuário (uid), id de grupo (gid), acesso shell e diretório home.

camada de abstração

Veja que temos recursos com contextos similares, portanto pela lógica poderíamos agrupá-los por tipos distintos de recursos, indo além, podemos observar que os atributos de alguns tipos de recursos são conceitualmente idênticos em alguns sistemas operacionais, tais como UID ou PATH, e mesmo em implementações diferentes seu funcionamento e objetivo não vão mudar, com isto, podemos entender que a descrição de um recurso no Puppet sofre uma abstração quanto a sua forma de implementação.
Essas duas características (agrupar e abstrair) formam no Puppet o que podemos chamar de camada de abstração de recursos (Resource Abstraction Layer) ou RAL.

tipos e provedores

O RAL divide recursos em resource types (tipos) - cada tipo tem seus parâmetros e meta-parâmetros - e providers (provedores), com isto o puppet nos permite descrever nossas configurações de uma forma abrangente, possibilitando que tais configurações sejam aplicadas em praticamente qualquer sistema operacional.

modificando/sincronizando

O Puppet usa o RAL tanto para ler quanto para modificar o estado de recursos em um sistema.

No caso da sincronização de um recurso, o puppet usa o RAL da seguinte forma:

  • verifica o estado atual de um recurso;
  • compara os dados coletados (estado atual) com o estado declarado para o recurso;
  • aplica mudanças necessárias no ambiente para que seu estado do recurso reflita o que foi declarado para ele;

    os recursos

No puppet, cada recurso é uma instância de algum tipo, e cada tipo de recurso tem um título e seus atributos, os atributos podem ser parâmetros, meta-parâmetros, funções e provedores.

exemplo de resource type

tipo { 'título':
  param1     => 'valor',
  param2     => 'valor',
  param3     => 'valor',
  metaparam1 => 'valor',
  metaparam2 => 'valor',
  function1  => 'valor',
  function2  => 'valor',
}


exemplo: resource type user

O recurso user pode ser representando desta forma.

user { 'gutocarvalho':
  ensure           => 'present',
  gid              => '500',
  home             => '/home/gutocarvalho',
  password         => '$6$BE6a/5SJ$7mSGRgRZBPVRv5p7OkClaTUwx.hwTLzrpVNEbqvyjFy5yFUdGZeL41b76VC6DXV/eFUG.zFS.wkjTpsO.KfCy0',
  password_max_age => '99999',
  password_min_age => '0',
  shell            => '/bin/bash',
  uid              => '500',
  }


Veja que o tipo de recurso é user título é gutocarvalho e os atributos são os campos mais a esquerda:

ensure, gid, home, password, password_max_age, password_min_age, shell, uid

Os campos depois da seta (⇒) são os valores destes atributos.

exemplo: resource type file

O recurso file é representando desta forma

file { '/etc/resolv.conf':
  ensure   => 'file',
  content  => '{md5}24ebe9c85187eb484686b5e835eb24ae',
  ctime    => 'Tue May 22 13:32:17 -0300 2012',
  group    => '0',
  mode     => '644',
  mtime    => 'Tue May 22 13:32:17 -0300 2012',
  owner    => '0',
  selrange => 's0',
  selrole  => 'object_r',
  seltype  => 'net_conf_t',
  seluser  => 'system_u',
  type     => 'file',
}

Veja o tipo

file

O título

/etc/resolv.conf

E os principais atributos:

owner, group, mode

puppet resource

verificando estados

O Puppet essencialmente é utilizado em linha de comando, portanto ele nos oferece algumas ferramentas para checarmos e modificarmos recursos, veja o exemplo:

[root@galaxy ~]# puppet resource user gutocarvalho

Acompanhe a saída

user { 'gutocarvalho':
  ensure           => 'present',
  comment          => 'Jose Augusto da Costa Carvalho,,,',
  gid              => '50000',
  groups           => ['infra'],
  home             => '/home/gutocarvalho',
  password         => '$6$UE/yPfbi$eq3kd1WPZxv8assXjxc6ZSiNc/YF40dmVknbPxT8LUFn5gKqhCGMbtWV9cobF8xWai1Kk7xSu.aHjAubzQuHA0',
  password_max_age => '99999',
  password_min_age => '0',
  shell            => '/bin/bash',
  uid              => '50000',
}

Outro exemplo

[root@galaxy ~]# puppet resource user gutocarvalho

Acompanhe a saída

user { 'gutocarvalho':
  ensure           => 'present',
  comment          => 'Jose Augusto da Costa Carvalho,,,',
  gid              => '50000',
  groups           => ['infra'],
  home             => '/home/gutocarvalho',
  password         => '$6$UE/yPfbi$eq3kd1WPZxv8assXjxc6ZSiNc/YF40dmVknbPxT8LUFn5gKqhCGMbtWV9cobF8xWai1Kk7xSu.aHjAubzQuHA0',
  password_max_age => '99999',
  password_min_age => '0',
  shell            => '/bin/bash',
  uid              => '50000',
}

Outro exemplo

[root@galaxy ~]# puppet resource user lucas
user { 'lucas':
  ensure => 'absent',
}

Veja que ele retornou absent, significa que o usuário não existe no sistema.

modificando estado de usuario

Podemos usar o ralsh para modificar o estado de um recurso

criando usuario

Vamos falar para o puppet que o usuário lucas deve estar presente

[root@galaxy ~]# puppet resource user lucas ensure=present

Acompanhe a saída

notice: /User[lucas]/ensure: created
user { 'lucas':
  ensure => 'present',
}

Veja que o usuário não existia no sistema, logo o puppet criou o lucas, veja as informações do usuário criado

[root@galaxy ~]# puppet resource user lucas

Acompanhe a saída

user { 'lucas':
  ensure           => 'present',
  gid              => '50006',
  home             => '/home/lucas',
  password         => '!!',
  password_max_age => '99999',
  password_min_age => '0',
  shell            => '/bin/bash',
  uid              => '50006',
}

removendo usuario

Agora vamos falar para o puppet que o usuário lucas não deve existir no sistema

[root@galaxy ~]# puppet resource user lucas ensure=absent

Acompanhe a saída

notice: /User[lucas]/ensure: removed
user { 'lucas':
  ensure => 'absent',
}

Pronto, o puppet removeu o usuário pois ele existia no sistema, se checarmos o usuário lucas veremos que ele não existe no sistema

[root@galaxy ~]# puppet resource user lucas

Acompanhe a saída

user { 'lucas':
  ensure => 'absent',
}

modificando estado de pacote

instalando pacote

Vamos falar para o puppet que o pacote htop deve estar presente no sistema

[root@galaxy ~]# puppet resource package htop ensure=present

Acompanhe a saída

notice: /Package[htop]/ensure: created
package { 'htop':
  ensure => '0.8.3-2.el6',
}

Bacana, o pacote htop não estava presente e foi instalado, se rodarmos novamente o comando ele vai nos dizer que o htop está presente e também vai nos mostrar sua versão

[root@galaxy ~]# puppet resource package htop ensure=present

Acompanhe a saída

package { 'htop':
  ensure => '0.8.3-2.el6',
}

removendo pacote

Vamos falar para o puppet que o pacote htop não deve estar presente

[root@galaxy ~]# puppet resource package htop ensure=absent

Acompanhe a saída

notice: /Package[htop]/ensure: removed
package { 'htop':
  ensure => 'absent',
}

O pacote foi removido, se rodarmos novamente, ele vai nos dizer que o htop não existe no sistema

[root@galaxy ~]# puppet resource package htop ensure=absent

Acompanhe a saída

package { 'htop':
  ensure => 'absent',
}

versões

Podemos falar para o puppet que o pacote htop tem que estar sempre na última versão disponível

[root@galaxy ~]# puppet resource package htop ensure=latest

Acompanhe a saída

package { 'htop':
  ensure => '0.8.3-2.el6',
}

Desta forma, se houver alguma versão mais recente no repositório de pacotes, esta será instalada pelo puppet.

editando atributos

Podemos usar a flag –edit para abrir um editor e alterar os atributos manualmente

Alterando atributos de usuário

ralsh user lucas --edit

Alterando atributos de um pacote

ralsh package htop --edit

entendendo a abstração

Com o Puppet nós declaramos o que precisamos através de sua sintaxe declarativa, e veremos que isto é uma tarefa muito simples.

Uma vez declarada a configuração, o puppet se encarrega de verificar os recursos envolvidos e aplica as mudanças necessárias.

O mais legal de tudo isto é que não precisamos dizer se ele tem que usar USERADD, ADDUSER, DELUSER ou USERMOD para criar, modificar ou remover usuários, não precisamos dizer se ele tem que usar o APT, APTITUDE ou YUM para instalar ou remover os pacotes, ele simplesmente recebeu o pedido e tratou de acordo com os recursos que estavam disponíveis.

Não se preocupar com os comandos e ferramentas para executar alguma coisa é uma forma de abstração em pelo menos um nível de nosso sistema, o Puppet nos dá condições de trabalhar em um nível mais alto, mais próximo do entendimento humano, assim podemos declarar configurações sem especificar comandos com diversos parâmetros.

principais recursos

Como já vimos, os recursos no puppet são agrupados em TIPOS, os principais TIPOS (core resource types) nativos do Puppet são em minha opinião:

  • User
  • Exec
  • Cron
  • File
  • Package
  • Service
  • Notify
  • Host
  • Mailalias

Vamos abordá-los em nossos testes a seguir.

Na documentação do Puppet temos 48 TIPOS de recursos nativos, embutidos no Puppet, disponíveis para o uso imediato.

Outros tipos de recursos podem ser carregados externamente a partir dos módulos.

O pessoal do Puppet fez um guia de referência rápida (Cheat Sheet) para facilitar nossa vida, este documento é composto por 2 folhas A4 com informações sobre os recursos e tipos nativos do puppet, lá vamos encontrar inclusive exemplos de uso destes, recomendo imprimir e colocar em uma parede próxima.

usando o describe

O puppet é tão bacana que ele já traz as informações dos tipos e atributos de uma forma fácil de acessar via CLI (command line interface).

puppetagent:~# puppet describe

Acompanhe a saída

puppet-describe(8) -- Display help about resource types
========

SYNOPSIS
--------
Prints help about Puppet resource types, providers, and metaparameters.


USAGE
-----
puppet describe [-h|--help] [-s|--short] [-p|--providers] [-l|--list] [-m|--meta]


OPTIONS
-------
* --help:
  Print this help text

* --providers:
  Describe providers in detail for each type

* --list:
  List all types

* --meta:
  List all metaparameters

* --short:
  List only parameters without detail


EXAMPLE
-------
    $ puppet describe --list
    $ puppet describe file --providers
    $ puppet describe user -s -m


AUTHOR
------
David Lutterkort


COPYRIGHT
---------
Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License

Podemos listar todos os tipos disponíveis

puppetagent:~# puppet describe --list

Acompanhe a saída

These are the types known to puppet:
augeas          -  Apply a change or an array of changes to the ...
computer        - Computer object management using DirectorySer ...
cron            -  Installs and manages cron jobs
exec            - Executes external commands
file            - Manages files, including their content, owner ...
filebucket      - A repository for backing up files
group           - Manage groups
host            - Installs and manages host entries
interface       - This represents a router or switch interface
k5login         - Manage the `
macauthorization - Manage the Mac OS X authorization database
mailalias       - Creates an email alias in the local alias dat ...
maillist        - Manage email lists
mcx             - MCX object management using DirectoryService  ...
mount           - Manages mounted filesystems, including puttin ...
nagios_command  - The Nagios type command
nagios_contact  - The Nagios type contact
nagios_contactgroup - The Nagios type contactgroup
nagios_host     - The Nagios type host
nagios_hostdependency - The Nagios type hostdependency
nagios_hostescalation - The Nagios type hostescalation
nagios_hostextinfo - The Nagios type hostextinfo
nagios_hostgroup - The Nagios type hostgroup
nagios_service  - The Nagios type service
nagios_servicedependency - The Nagios type servicedependency
nagios_serviceescalation - The Nagios type serviceescalation
nagios_serviceextinfo - The Nagios type serviceextinfo
nagios_servicegroup - The Nagios type servicegroup
nagios_timeperiod - The Nagios type timeperiod
notify          - Sends an arbitrary message to the agent run-t ...
package         - Manage packages
resources       - This is a metatype that can manage other reso ...
router          - Manages connected router
schedule        -  Define schedules for Puppet
scheduled_task  - Installs and manages Windows Scheduled Tasks
selboolean      - Manages SELinux booleans on systems with SELi ...
selmodule       - Manages loading and unloading of SELinux poli ...
service         - Manage running services
ssh_authorized_key - Manages SSH authorized keys
sshkey          - Installs and manages ssh host keys
stage           - A resource type for specifying run stages
tidy            - Remove unwanted files based on specific crite ...
user            - Manage users
vlan            - Manages a VLAN on a router or switch
whit            - Whits are internal artifacts of Puppet's curr ...
yumrepo         - The client-side description of a yum reposito ...
zfs             - Manage zfs
zone            - Manages Solaris zones
zpool           - Manage zpools

describe package

Vamos pedir para o puppet descrever os parâmetros e provedores do recurso package, omitindo os detalhes (-s).

puppetagent:~# puppet describe -s package

Acompanhe a saída

package
=======
Manage packages.  There is a basic dichotomy in package
support right now:  Some package types (e.g., yum and apt) can
retrieve their own package files, while others (e.g., rpm and sun)
cannot.  For those package formats that cannot retrieve their own files,
you can use the `source` parameter to point to the correct file.

Puppet will automatically guess the packaging format that you are
using based on the platform you are on, but you can override it
using the `provider` parameter; each provider defines what it
requires in order to function, and you must meet those requirements
to use a given provider.

**Autorequires:** If Puppet is managing the files specified as a
package's `adminfile`, `responsefile`, or `source`, the package
resource will autorequire those files.


Parameters
----------
    adminfile, allowcdrom, category, configfiles, description, ensure,
    flavor, install_options, instance, name, platform, responsefile, root,
    source, status, type, vendor

Providers
---------
    aix, appdmg, apple, apt, aptitude, aptrpm, blastwave, dpkg, fink,
    freebsd, gem, hpux, macports, msi, nim, openbsd, pacman, pip, pkg,
    pkgdmg, pkgutil, portage, ports, portupgrade, rpm, rug, sun,
    sunfreeware, up2date, urpmi, yum, zypper
    

Veja a quantidade de provedores (gerenciadores de pacotes) que são suportados pelo Puppet, esse é um grande diferencial.

describe user

Vamos pedir para o puppet descrever os parâmetros do tipo USER com seus meta-parâmetros.

puppetagent:~# puppet describe -s -m user

Veja saída

user
====
Manage users.  This type is mostly built to manage system
users, so it is lacking some features useful for managing normal
users.

This resource type uses the prescribed native tools for creating
groups and generally uses POSIX APIs for retrieving information
about them.  It does not directly modify `/etc/passwd` or anything.

**Autorequires:** If Puppet is managing the user's primary group (as
provided in the `gid` attribute), the user resource will autorequire
that group. If Puppet is managing any role accounts corresponding to the
user's roles, the user resource will autorequire those role accounts.


Parameters
----------
    allowdupe, attribute_membership, attributes, auth_membership, auths,
    comment, ensure, expiry, gid, groups, home, ia_load_module,
    key_membership, keys, managehome, membership, name, password,
    password_max_age, password_min_age, profile_membership, profiles,
    project, role_membership, roles, shell, system, uid

Meta Parameters
---------------
    alias, audit, before, check, loglevel, noop, notify, require, schedule,
    stage, subscribe, tag

Providers
---------
    aix, directoryservice, hpuxuseradd, ldap, pw, user_role_add, useradd,
    windows_adsi

Se não colocarmos o -s (short) ele trará ainda mais informações, veja baixo a descrição de um parâmetro

  **home**
  The home directory of the user.  The directory must be created
  separately and is not currently checked for existence.

E aqui um exemplo de uso de metaparâmetros (alias) e parâmetros (owner,group).

  file { "/etc/ssh/sshd_config":
          owner => root,
          group => root,
          alias => 'sshdconfig'
        }

describe service

Vamos pedir para o puppet descrever os parâmetros e metaparâmetros do TIPO service.

puppetagent:~# puppet describe -m -s service

Acompanhe a saída

service
=======
Manage running services.  Service support unfortunately varies
widely by platform --- some platforms have very little if any concept of a
running service, and some have a very codified and powerful concept.
Puppet's service support is usually capable of doing the right thing, but
the more information you can provide, the better behaviour you will get.

Puppet 2.7 and newer expect init scripts to have a working status command.
If this isn't the case for any of your services' init scripts, you will
need to set `hasstatus` to false and possibly specify a custom status
command in the `status` attribute.

Note that if a `service` receives an event from another resource,
the service will get restarted. The actual command to restart the
service depends on the platform. You can provide an explicit command for
restarting with the `restart` attribute, or you can set `hasrestart` to
true to use the init script's restart command; if you do neither, the
service's stop and start commands will be used.


Parameters
----------
    binary, control, enable, ensure, hasrestart, hasstatus, manifest, name,
    path, pattern, restart, start, status, stop

Meta Parameters
---------------
    alias, audit, before, check, loglevel, noop, notify, require, schedule,
    stage, subscribe, tag

Providers
---------
    base, bsd, daemontools, debian, freebsd, gentoo, init, launchd, redhat,
    runit, smf, src, systemd, upstart, windows

uso do describe

Esse recurso nos ajuda muito no decorrer da construção das configurações, ele serve de referência rápida, mas é claro que há ocasiões em que o acesso a documentação online será mais confortável e produtiva, principalmente por causa da busca.

fatos e facter

Os fatos, vamos falar e vamos usar em muitos pontos da documentação o que chamamos de fatos, fatos são informações do sistema, estas informações são extraídas através da ferramenta FACTER, veja um exemplo

root@puppetagent:~/puppet# facter

Acompanhe a saída

architecture => i386
augeasversion => 0.7.2
domain => hacklab
facterversion => 1.6.8
fqdn => puppetagent.hacklab
hardwareisa => unknown
hardwaremodel => i686
hostname => puppetagent
id => root
interfaces => eth0,eth1,lo
ipaddress => 10.0.2.15
ipaddress_eth0 => 10.0.2.15
ipaddress_eth1 => 192.168.56.101
ipaddress_lo => 127.0.0.1
is_virtual => true
kernel => Linux
kernelmajversion => 2.6
kernelrelease => 2.6.32-5-686
kernelversion => 2.6.32
lsbdistcodename => squeeze
lsbdistdescription => Debian GNU/Linux 6.0.5 (squeeze)
lsbdistid => Debian
lsbdistrelease => 6.0.5
lsbmajdistrelease => 6
macaddress => 08:00:27:5d:70:59
macaddress_eth0 => 08:00:27:5d:70:59
macaddress_eth1 => 08:00:27:2f:69:3d
manufacturer => innotek GmbH
memoryfree => 76.75 MB
memorysize => 121.91 MB
memorytotal => 121.91 MB
netmask => 255.255.255.0
netmask_eth0 => 255.255.255.0
netmask_eth1 => 255.255.255.0
netmask_lo => 255.0.0.0
network_eth0 => 10.0.2.0
network_eth1 => 192.168.56.0
network_lo => 127.0.0.0
operatingsystem => Debian
operatingsystemrelease => 6.0.5
osfamily => Debian
path => /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
physicalprocessorcount => 1
processor0 => Intel(R) Core(TM)2 Duo CPU     P8600  @ 2.40GHz
processorcount => 1
productname => VirtualBox
ps => ps -ef
puppetversion => 2.7.14
rubysitedir => /usr/local/lib/site_ruby/1.8
rubyversion => 1.8.7
selinux => false
serialnumber => 0
sshdsakey => AAAAB3NzaC1kc3MAAACBAMOZ3UbH8J96JvS0mL+b4+1RIz7OFF95pGbC27n2p/+pTKP8LQgcEUg9UcN5y+/12+WHaa49vd/jRK6OU30EWtzDX9D0szqqqjta+m0xvgkW6rq17kyAo1b9PXq6t/G93P4ay0OyLu9V59KuV2idsRvldHSKEMS/76M0BFR4ja0HAAAAFQCavJkL+hjy1SSmqGl3tXahjnVWNwAAAIEAhicZtItkahH6wnANnnCVNdQkKOPfmv5ai6VF2Zw+wFC73nkCNVQ3XdXTNiFNTw+qmpPsrEsfpujVaskxfpNI7GgvRIIa0PtgF0TFOFtapnKWjyteYXfkOE3OMKIbeCI3ZTBGcMQwNRl+qFWF58XVvQP5ZpOfYfBDpyvi4wmqYz0AAACACHlfgnaJ/vJhAt3mcgtwIP8EQd4auEJxKXxlYfuCEJss3M8jdudCQa+mgdL6F7989XoraXj7XsGDVwHu3IyR87ifxMAYjqn1q6zJjBctkdZoLpA12BHy9J7/m9CV+mFIX+57i1wDDlfl+HTwZbjmVh9qdr9qhRF65muehqPXEfc=
sshrsakey => AAAAB3NzaC1yc2EAAAADAQABAAABAQDamZ2JYEoRcnX9LKllswm1xWqYhGdffdBSyA7drPqKQZACYW8A3Ca6PhHUQSrjQLR8rQkUEQ49dxUpObAZTBU1LzFxNckfhTQ/bVhZGm09pFLN4WI+2enhmmftvNcM5gSUYjacIJ3IcSBoIS1kMMnNSAp/WHavQk01FK2J4XOH/UK7mHnKs759StBI7sg8Lam1OQkZzChBecoCTGaOLOe5xiwwWm71y9G+iOfuHeU8SEBmRPentxfs6taBLY/WSN5Em1ZurXEcFEXSiYXqfOCPx/obIThkady2nOop3lWwTiHSkX25fFxCIQwYSrSymvpFx0Pbqpic1nk8X+GlYkKf
swapfree => 211.99 MB
swapsize => 211.99 MB
timezone => BRT
uniqueid => 000a0f02
uptime => 4:41 hours
uptime_days => 0
uptime_hours => 4
uptime_seconds => 16862
virtual => virtualbox

Podemos usar esses fatos em diversas configurações, veremos mais sobre isto na parte de variáveis e condicionais.

fqdn

Uma coisa muito importante que resolvi embutir antes dos manifests é a configuração correta de fqdn (fully qualified domain name).

Algumas configurações do puppet dependem essencialmente do FQDN, então, antes de mais nada, se estiver usando debian confira as entradas fqdn no hosts e hostname.

Se não sabe o que é FQDN confira aqui http://en.wikipedia.org/wiki/Fully_qualified_domain_name

hosts

Edite o arquivo /etc/hosts e tenha certeza de que o nome do node está devidamente declarado com fqdn e alias, veja o exemplo abaixo

ip fqdn alias

Agora veja um exemplo real

192.168.56.101 puppet.dominio puppet puppetmaster

hostname

No debian existe o arquivo hostname que contem o fqdn do node, confira e ajuste se necessário.

puppet.dominio

Após ajustar, faça um logout e um logon no terminal para que o login leia o hostname e corrija o fqdn da sessão.

termos e significados

Antes de começarmos, entenda alguns termos que serão utilizados no decorrer da wiki.

termosignificado
puppet, puppetmaster ou master servidor do puppet que distribui configurações para os nodes
node ou nó servidor gerenciado pelo puppet
puppet agent, agent ou agente agente do puppet que roda em um node
resource type tipo de recurso - agrupamento de recursos de um mesmo contexto
objeto quando utilizamos um tipo de recurso e damos definimos um título, este passa a ser considerado um objeto
describe recurso que descreve parâmetros e providers de outros recursos.
provider provedor de pacotes
parameter parâmetro de um recurso
metaparameter metaparâmetro que pode ser usado em qualquer resource type
function funções do puppet
variables variáveis
fact fato
selection seletor condicionais
case caso condicional
definitions definições para configurações - requer uso de variáveis
classe cojunto de configurações parametrizadas - ajuda no reaproveitamento de código
módulo pacotes de configurações que possuem ambiente própio

manifests

Fazendo uma rápida recapitulação, nós já vimos boa parte do que o Puppet pode fazer, já entendemos resumidamente como ele funciona, já entendemos o que é RAL, já vimos diferentes tipos de recursos, já entendemos o que são parâmetros e provedores, agora vamos a diante.

O Puppet ainda tem muito mais, ele vai muito além destes recursos soltos que testamos, ele foi feito para gerenciar grandes parques de servidores com muitos serviços e sistemas rodando, e os manifests podem ser considerados as 'receitas de bolo', as mudanças são o reflexo destes manifestos.

o que é?

Os manifests em sua essência são os programas que escrevemos usando a linguagem do Puppet, são através destes manifests que aplicamos configurações em um node (servidor que roda o puppet agent).

Podemos ainda dizer que os manifests são configurações bastante elaborados e detalhadas na linguagem declarariva do Puppet, nos manifests nós vamos combinar os tipos de recursos que já vimos para alcançar nossos objetivos.

É agora que a brincadeira realmente começa, vamos escrever nossos primeiros códigos na linguagem declarativa do Puppet.

como funciona?

Manifests podem ser criados e aplicados no modelo cliente-server (agent/master) ou no modo autônomo, mas antes de falarmos do modo cliente/servidor, precisamos antes aprender a criar um manifest e aplicá-lo localmente.

Os programas escritos com linguagem do puppet usam a extensão .pp

praticando

Para aplicar um manifest basta usar o seguinte comando

puppet apply meu_primeiro_manifest.pp

Você nem precisa usar o APPLY, usa-se apenas para deixar bem claro o que estamos fazendo, se tiver dúvidas

man puppet-apply

catálogo local

A aplicação de um ou vários manifests segue os seguinte passos:

  • Puppet verifica os Manifests;
  • Os Manifests são compilados em um catálogo;
  • O Puppet processa e aplica o catálogo compilado;
  • O Puppet verifica o estado atual do sistema após aplicar processar e catálogo - se for necessário ele executa novas modificações;
  • O sistema passa a ter um novo estado - refletindo o manifest;

Veja uma representação gráfica destes passos:

declarando recursos

Aqui vamos aprender como declarar alguns recursos e como devemos aplicá-los.

Todas as informações acerca de declaração estão disponíveis no site docs-puppetlabs na parte type reference

Crie um diretório /root/puppet para deixar os arquivos que vamos criar, testar e modificar.

resource type: file

Podemos começar pelo resource type file, ele pode ser utilizado para criar arquivos, diretórios ou links.

Agora vamos aprender como declarar um resource type file, crie um arquivo chamado arquivo.pp e insira o conteúdo abaixo

    file {'arquivoteste':
      path    => '/tmp/arquivoteste',
      ensure  => present,
      mode    => 0640,
      content => "Eu sou um arquivo de teste",
    }

Para aplicar esse manifest rode:

puppetagent:~# puppet apply arquivo.pp 

Acompanhe a saída

notice: /Stage[main]//File[arquivoteste]/ensure: created
notice: Finished catalog run in 0.13 seconds

Conferindo se funcionou

puppetagent:~# ls /tmp/
arquivoteste

Arquivo criado, agora vamos olhar seu conteúdo

puppetagent:~# cat /tmp/arquivoteste 
Eu sou um arquivo de teste

Beleza, criado certinho, agora vamos adicionar mais alguns parâmetros

file {'arquivoteste':
      path    => '/tmp/arquivoteste',
      ensure  => present,
      owner   => gutocarvalho,
      group   => gutocarvalho,
      mode    => 0600,
      content => "Eu sou um arquivo de teste",
}

Aplique a configuração e vamos usar o –verbose

puppetagent:~# puppet apply arquivo.pp --verbose

Acompanhe a saída

info: Applying configuration version '1337889615'
notice: /Stage[main]//File[arquivoteste]/owner: owner changed 'root' to 'gutocarvalho'
notice: /Stage[main]//File[arquivoteste]/group: group changed 'root' to 'gutocarvalho'
notice: /Stage[main]//File[arquivoteste]/mode: mode changed '0640' to '0600'
notice: Finished catalog run in 0.15 seconds

Bacana, ele alterou o owner, grupo e a permissão do arquivo, agora vamos fazer um teste, mude a permissão manualmente e aplique novamente a configuração

puppetagent:~# chmod 777 /tmp/arquivoteste

Aplicando o manifest

puppetagent:~# puppet apply arquivo.pp --verbose

Acompanhe a saída

info: Applying configuration version '1337889774'
notice: /Stage[main]//File[arquivoteste]/mode: mode changed '0777' to '0600'
notice: Finished catalog run in 0.10 seconds

Veja que mesmo que alguém mude a permissão, owner ou group, o manifest vai sempre modificar o arquivo para que ele reflita o que declaramos.

diretório

Para criar um diretório podemos fazer assim

file {'/tmp/subdiretorio':
      ensure => directory,
      mode   => 0755,
}

Para criar um link podemos fazer assim

file {'linkteste':
      path   => '/tmp/linkteste',
      ensure => link,
      target => '/tmp/arquivoteste',
}
 

juntando tudo

Adicione o conteúdo arquivo no arquivo.pp e vamos aplicar as configurações

file {'arquivoteste':
      path    => '/tmp/arquivoteste',
      ensure  => present,
      owner   => gutocarvalho,
      group   => gutocarvalho,
      mode    => 0600,
      content => "Eu sou um arquivo de teste",
}
 
file {'/tmp/subdiretorio':
      ensure => directory,
      mode   => 0755,
}
 
file {'linkteste':
      path   => '/tmp/linkteste',
      ensure => link,
      target => '/tmp/arquivoteste',
}

Aplicando

puppetagent:~# puppet apply arquivo.pp

Acompanhe a saída

notice: /Stage[main]//File[/tmp/subdiretorio]/ensure: created
notice: /Stage[main]//File[linkteste]/ensure: created
notice: Finished catalog run in 0.17 seconds

Vamos checar

puppetagent:~# ls -l /tmp/
total 8
-rw------- 1 gutocarvalho gutocarvalho   26 May 24 16:56 arquivoteste
lrwxrwxrwx 1 root         root           17 May 24 17:16 linkteste -> /tmp/arquivoteste
drwxr-xr-x 2 root         root         4096 May 24 17:16 subdiretorio

Bacana, tudo funcionando.

dicas file

No resource type file podemos usar os seguintes valores para o parâmetro ensure:

  • present
  • absent
  • file
  • directory
  • link
  • recurse

Os parâmetros mais utilizados são:

  • owner
  • group
  • mode
  • source
  • recurse
criando aquivo e declarando origem

No exemplo abaixo declaramos que o arquivo main.cf deve existir no diretório /etc/postfix, e adicionamos a origem, ou seja, o arquivo deve existir no /etc/postfix e ser igual a origem (source) que declaramos.

file { "/etc/postfix/main.cf":
      ensure => present, 
      owner => "postfix",
      group => "postfix",
      mode => 0644,
      source => "/root/puppet/files/postfix/main.cf",
  }
criando um diretório e arquivos recursivamente

Aqui veremos um exemplo de criação de diretório e todos seus arquivos 'filho' de forma recursiva.

file { "/etc/zabbix/conf.d":
      ensure => directory, 
      recurse => true, 
      owner => "zabbix",
      group => "zabbix",
      mode => 0644,
      source => "/root/zabbix-agent/conf.d",
  }

Com isso o puppet vai criar o diretorio /etc/zabbix/conf.d e colocar lá dentro todos os arquivos que existirem na origem, de forma recursiva, graças ao parâmetro recurse.

limpando arquivos de um diretório

Vamos supor que você deseja limpar o conteúdo do diretório antes de criar arquivos lá dentro, vou dar um exmeplo

Primeiro crie o diretório e dois arquivos

mkdir /tmp/ola
touch /tmp/ola/a1
tpuch /tmp/ola/a2

Agora vamos declarar uma configuração no arquivo.pp

file { "/tmp/ola":
        ensure  => present,
        recurse => true,
        purge   => true,
 
}

Aplique a configuração

root@puppetagent:/tmp# puppet apply limpaola.pp
notice: /File[/tmp/ola/a2]/ensure: removed
notice: /File[/tmp/ola/a1]/ensure: removed
notice: Finished catalog run in 0.20 seconds

Veja que o puppet limpou o diretório usando os parâmetros recurse e purge.

Isso já me foi útil quando precisava criar arquivos no centos dentro do /etc/yum.repos.d/ , eu queria criar um arquivo lá mas antes queria garantir que o diretório fosse limpo, assim eu evitaria que algum outro arquivo pré-existente pudesse conflitar com os repositórios que o puppet fosse configurar :)

Se desejar que os subdiretórios sejam apagados use o parâmetro force

file { "/tmp/ola":
        ensure  => present,
        recurse => true,
        purge   => true,
        force   => true,
}

Mais informações em http://docs.puppetlabs.com/references/stable/type.html#file

padronização nos arquivos do filesystem

Manter os arquivos com o owner, grupo e modo correto pode não parecer muito importante, porém dependendo da aplicação isto pode causar um grande incidente.

Ao declarar suas configurações de arquivos, diretórios e links via puppet, você estará mantendo o sistema padronizado e integro, e caso algum sysadmin desavisado altere as configurações de um aquivo/diretório/link, o puppet vai detectar a mudança e corrigir, mantendo o sistema funcionando da forma como foi declarado. Esse é um passo importante para alcançar maior disponibilidade e maior padronização em seu ambiente.

resource type: package

O resource type package pode ser utilizado para instalar e remover pacotes em um sistema

Agora vamos aprender como declarar um pacote, crie um arquivo chamado pacotes.pp dentro de /root/puppet seguindo o exemplo abaixo:

package { 'htop':
      ensure  => installed,
}

Aplique o manifest

puppetagent:~# puppet apply pacotes.pp

Acompanhe a saída

notice: /Stage[main]//Package[htop]/ensure: ensure changed 'purged' to 'present'
notice: Finished catalog run in 8.31 seconds  

Podemo entender que o pacote não existia, logo, como estava declarado, foi instalado pelo puppet.

latest

Além de instalar, podemos declarar que desejamos que o pacote instalado esteja sempre em sua última versão, para isto basta trocar o valor do parâmetro ensure para latest veja o exemplo abaixo:

package { 'facter':
      ensure  => latest,
}

Avaliando os pacotes instalados no sistema, pude verificar que o pacote facter - instalado - tem uma atualização disponível que pode ser aplicada, veja o método que eu usei para checar.

puppetagent:~# aptitude versions facter

p A 1.5.7-3               stable                                                           500 
i A 1.6.7-1~bpo60+1                                                                        100 
p A 1.6.8-1~bpo60+1       squeeze-backports                                                100

Como alterarmos o valor do parâmetro ensure para latest, o puppet vai forçar essa atualização.

Vou testar em meu ambiente, para isto basta insirir o trecho de código de exemplo no arquivo pacotes.pp e rodar o apply

puppetagent:~# puppet apply pacotes.pp

Acompanhe a saída

notice: /Stage[main]//Package[facter]/ensure: ensure changed '1.6.7-1~bpo60+1' to '1.6.8-1~bpo60+1'
notice: Finished catalog run in 4.66 seconds

Veja que o pacote facter foi atualizado, perfeito, mas o que acontece se rodarmos novamente?

puppetagent:~# puppet apply pacotes.pp --verbose

Acompanhe a saída

notice: Finished catalog run in 0.13 seconds
puppetagent:~# puppet apply pacotes.pp --verbose
info: Applying configuration version '1337891550'
notice: Finished catalog run in 0.31 seconds

Veremos que nada foi executado ou modificado, a razão disto é que o estado atual do sistema reflete extamente as configurações declaradas no manifest, simples assim.

dicas package

O parâmetro ensure pode ter os seguinte valores no resource type package:

  • present|installed
  • absent
  • purged
  • held
  • latest

Outros parâmetros interessantes

  • provider (define o gerenciador de pacotes a ser utilizado)
  • source (define a origem de um pacote, pode estar em um diretório ou url)

Mais informações em http://docs.puppetlabs.com/references/stable/type.html#package

padronização de pacotes

a padronização de pacotes é sempre muito importante, manter um grupo de ferramentas padrão instaladas em cada node facilita a administração pela equipe de sysadmins, padroniza os nodes, permite que o sysadmin trabalhe com tranquilidade independente do node em que estiver logado, as ferramentas do dia-a-dia sempre estarão prontas para labuta, evitando aqueles cenários em que algumas máquinas tem certos pacotes e outras não.

resource type: service

O resource type service é utilizado para gerenciar um serviço/dameon em um sistema

Agora vamos aprender a declarar um serviço, para isto crie um arquivo chamado service.pp em /root/puppet com o seguinte conteúdo

service { "sshd":
    ensure     => running,
    enable     => true,
}

Após criar o arquivo, aplique a configuração como no exemplo abaixo

root@puppetagent:~/puppet# puppet apply service.pp
notice: Finished catalog run in 0.31 seconds

Veja que ele rodou mas não fez nada, se checarmos com o ralsh veremos o estado do ssh é 'running', logo ele está rodando

root@puppetagent:~/puppet# ralsh service ssh

Acompanhe a saída

  service { 'ssh':
    ensure => 'running',
    enable => 'true', 
  }

Agora vamos simular uma parada

root@puppetagent:~/puppet# invoke-rc.d ssh stop
Stopping OpenBSD Secure Shell server: sshd.

E vamos aplicar novamente a configuração

root@puppetagent:~/puppet# puppet apply service.pp --verbose
info: Applying configuration version '1338215679'
notice: /Stage[main]//Service[ssh]/ensure: ensure changed 'stopped' to 'running'
notice: Finished catalog run in 0.37 seconds

Veja que o puppet recebeu a declaração de que o serviço ssh deve ter estado 'running', mas ao verificar o serviço o estado que retornou foi 'stopped', portanto ele modificou esse mesmo estado para 'running'.

dicas service

O resource type service ainda tem muitos outros parâmetros, porém eles funcionam melhor quando o combinados com meta-parâmetros como o subscribe e o notify, veremos isto mais a frente na seção meta-parâmetros.

O parâmetro ensure no no resource type service podem ter os seguintes estados:

  • running|true
  • stopped|false

Outros parâmetros interessantes

  • enable (diz se o serviço tem que subir na inicialização)
  • hasrestart (diz ao puppet que o script init do serviço tem restart)
  • hasstatus (diz ao puppet que o script init do serviço tem status)

Veja o exemplo:

service { "sshd":
    ensure     => running,
    enable     => true,
    hasrestart => true,
    hasstatus  => true,
}

Na versão 2.7 os parâmetros hasrestart e hasstatus já são tem valor default true, porém, caso seu init não tenha suporte a um deles, declare como false.

Cada provider em cada tipo de sistema operacional tem particularidades e parâmetros própios, veja no site de documentação do Puppet os parâmetros do provider (ex: aptitude) que estará usando, isso pode facilitar sua vida e te ajudar a refinar sua configuração.

Mais informações em http://docs.puppetlabs.com/references/stable/type.html#service

padronizando configurações de serviços

A padronização dos serviços também é algo importante, com o puppet podemos declarar qual o estado desejado de um ou N serviços, com isso ele vai mantê-lo(s) funcionando de acordo com o que foi declarado. Esse também é um passo importante no caminho da padronização de seus nodes, serviços como SSH, NTPD e MTA devem estar sempre rodando para que seja possível fazer manutenções (SSH), auditorias precisas em eventos através do acompanhamento de logs (NTPD) além de possibilitarem o recebimento de informações dos nodes (MTA). Com o puppet podemos declarar e distribuir essas configurações para todos os nossos nodes.

resource type: user

O resource type user é utilizado para criar, modificar ou remover um usuário

Agora vamos aprender a declarar um usuário, crie dentro de /root/puppet um arquivo chamado usuario.pp com o conteúdo abaixo:

user { 'lucas':
  ensure           => 'present',
  shell            => '/bin/ksh',
}

Após criar o arquivo, aplique a configuração

root@puppetagent:~/puppet# puppet apply usuario.pp 

notice: /Stage[main]//User[lucas]/ensure: created
notice: Finished catalog run in 0.23 seconds

Podemos ver que o puppet criou o usuário lucas e agora vamos conferir se o lucas está com o shell que declaramos (ksh).

root@puppetagent:~/puppet# cat /etc/passwd|grep lucas
lucas:x:1003:1003::/home/lucas:/bin/ksh

Bacana, o usuário está presente exatamente da forma que o declaramos, agora vamos removê-lo, basta alterar o ensure para absent

user { 'lucas':
  ensure           => 'absent',
  shell            => '/bin/ksh',
}

Aplique a configuração

root@puppetagent:~/puppet# puppet apply usuario.pp 
notice: /Stage[main]//User[lucas]/ensure: removed
notice: Finished catalog run in 0.19 seconds

Veja que ao declarar que o usuário não deve existir, o puppet verificou, encontrou e removeu o usuário do sistema

dicas user

O parâmetro ensure pode ter os seguintes valores neste resource type

  • ensure
  • absent
  • role
    Outros parâmeteos interessantes
    • uid
    • gid
    • expires
    • groups
    • home
    • name
    • password
    • passwordmaxage
    • passwordminage
    • comment

    Veja o exemplo do resource type user com os parâmetros default

user { 'lucas':
  ensure           => 'present',
  gid              => '1003',
  home             => '/home/lucas',
  password         => '!',
  password_max_age => '99999',
  password_min_age => '0',
  shell            => '/bin/ksh',
  uid              => '1003',
}

Mais informações em http://docs.puppetlabs.com/references/stable/type.html#user

padronizando usuários

A padronização de usuários é algo que causa muita dor de cabeça no setor de TI, principalmente quando precisamos criar o usuário de um novo colaborador em dezenas de máquinas, ou mesmo remover usuários de colaboradores que sairam da equipe, com o puppet você pode criar configurações e aplicá-las nos nodes de forma simplificada, esse é certamente é mais um passo importante no caminho da padronização.

resource type: exec

O resource type exec é utilizado para executar comandos em nosso sistema.

Agora vamos aprender a declarar o resource type exec, para isto crie um arquivo em /root/puppet chamado exec.pp com o conteúdo abaixo:

exec { "update-alternatives --set editor /usr/bin/vim.basic":
                path   => "/usr/bin:/usr/sbin:/bin",
}

Aplique a configuração

root@puppetagent:~/puppet# puppet apply exec.pp 
notice: /Stage[main]//Exec[update-alternatives --set editor /usr/bin/vim.basic]/returns: executed successfully
notice: Finished catalog run in 0.26 seconds

Bacana, o puppet executou o comando que passamos com sucesso.

unless

O exec é um resource type bacana e tem um parâmetro muito interessante, ele chama-se unless, é um condicional, com ele podemos tratar algumas questões antes de executar o comando declarado.

Para testar modifique o conteúdo do exec.pp de acordo com o exemplo abaixo:

exec { "update-alternatives --set editor /usr/bin/vim.basic":
                path   => "/usr/bin:/usr/sbin:/bin",
                unless => "test /etc/alternatives/editor -ef /usr/bin/vim.basic",

Aplique a configuração

root@puppetagent:~/puppet# puppet apply exec.pp 
notice: Finished catalog run in 0.28 seconds

Bacana, executado, mas nada aconteceu pois fizemos uma checagem antes do exec que obteve retorno true.

Ao usar o parâmetro unless, estamos declarado execute isto a não ser que , e caso a condição seja positiva (true) ele não executa

A checagem que usei de exemplo verifica se o vim.basic já é o editor padrão, como ele é, nada foi executado.

refreshonly

O resource type exec tem características simples, mas nós precisamos entendê-las de forma clara. E importante saber que cada vez que o puppet fizer a leitura de um arquivo manifest com um resource type exec, este será executado, até ai nenhum problemo, porém isto pode ser incômodo em alguns casos, por exemplo, quando o arquivo alias é modificado precisamos rodar o newalias ou o postalias - no caso do postfix, mas ele só precisa ser executado no caso de mudança do arquivo e não toda a vez que o manifest é lido.

Para resolver este problema, existe o parâmetro refreshonly, este parâmetro, quando declarado, só executa o refresh se houver mudança em uma dependência, esta dependência pode ser declarada através dos meta-parâmetros subscribe ou notify e apenas por eles

Essa dependência é um tipo de de gatilho que aciona a execução do comando.

Veja o exemplo abaixo:

# declarando o arquivo aliases
file { "/etc/aliases":
  source => "/root/puppet/aliases"
}
 
# declarando comando para reconstrução da base aliases caso o conteúdo do arquivo mude
exec { "postalias /etc/aliases":
  path        => ["/usr/bin", "/usr/sbin"],
  subscribe   => File["/etc/aliases"],
  refreshonly => true
}

O comando exec faz um subscribe para a configuração /etc/aliases que também está declarada no arquivo, o subscribe roda internamente aquela configuração, logo se o arquivo for o mesmo nada será feito, agora se o arquivo de origem for alterado, gerando uma mudança no destino, esse será o gatilho para a execução do comando.

Abaixo o conteúdo do /root/puppet/aliases, em nosso teste ele é idêntico ao /etc/aliases

/root/puppet/aliases
# HEADER: This file was autogenerated at Fri May 25 14:33:50 -0300 2012
# HEADER: by puppet.  While it can still be managed manually, it
# HEADER: is definitely not recommended.
# /etc/aliases
mailer-daemon: postmaster
postmaster: root
nobody: root
hostmaster: root
usenet: root
news: root
webmaster: root
www: root
ftp: root
abuse: root
noc: root
security: root
root: root

Vamos aplicar para ver o que acontece

root@puppetagent:~/puppet# puppet apply exec.pp
notice: Finished catalog run in 0.12 seconds

Veja que nada foi executado, isso ocorreu pois o exec não recebeu nenhum evento a partir da depêndencia File, com isso o gatilho para o exec não foi executado. Agora vamos modificar o alias do root no arquivo de origem /root/puppet/aliases conforme o exemplo abaixo

root: gutocarvalho@gmail.com

Vamos aplicar a configuração novamente

root@puppetagent:~/puppet# puppet apply exec.pp 
notice: /Stage[main]//File[/etc/aliases]/content: content changed '{md5}f45ef1a7007147600003a49ce1032fc1' to '{md5}f0ab37052b7616ade1e73fab8dadf4eb'
notice: /Stage[main]//Exec[postalias /etc/aliases]: Triggered 'refresh' from 1 events
notice: Finished catalog run in 0.46 seconds

Vacana, houve alteração, ao rodar o subscribe File, o puppet rodou o File e viu que o arquivo /etc/aliases do destino era diferente da origem /root/puppet/aliases, portando, ele modificou o arquivo, com isso houve um evento, evento este que foi detectado pelo exec, isso acionou o gatilho e o exec foi executado.

Veja que o refreshonly é um parâmetro muito útil em muitos contextos

dicas exec

Alguns parâmetros interessantes do resource type exec

  • onlyif (só executa se)
  • user (usuário que deve rodar o comando)
  • group (grupo que deve rodar o comando)
  • cwd (local onde o comando deve ser executado)

Mais informações em http://docs.puppetlabs.com/references/stable/type.html#exec

padronizando execução de comandos

O uso do resource type exec é muito prático, com ele acabamos de vez com aquela sensação de termos esquecido de rodar o newalias ou postalias após uma atualização no arquivo aliases, e podemos expandir isto para outros sistemas e serviços, garantindo que comandos essenciais rodem a partir de mudanças ou dependências. Isto nos ajuda a manter a integridade dos nossos sistemas, evita problemas e incidentes, bastando traduzir as necessidades em configurações puppet para aplicá-las nos nodes de seu parque.

resource type: cron

O resource type cron é utilizado parar criar tarefas agendadas usando o cron do sistema.

Agora vamos aprender a utilizar o resource type cron, para isto crie um arquivo em /root/puppet com o nome cron.pp com o conteúdo abaixo

cron { "cron-updatedb":
    ensure  => present,
    user    => root,
    command => "/usr/bin/updatedb",
    minute  => 00,
}

Aplique a configuração

root@puppetagent:~/puppet# puppet apply cron.pp 
notice: /Stage[main]//Cron[cron-updatedb]/ensure: created 
notice: Finished catalog run in 0.14 seconds

Vamos verificar se o comando declarado está no spool de rotinas do cron do usuário root

root@puppetagent:~/puppet# crontab -l

Acompanhe a saída

# HEADER: This file was autogenerated at Tue May 29 19:58:17 -0300 2012 by puppet.
# HEADER: While it can still be managed manually, it is definitely not recommended.
# HEADER: Note particularly that the comments starting with 'Puppet Name' should
# HEADER: not be deleted, as doing so could cause duplicate cron jobs.
# Puppet Name: cron-updatedb
0 * * * * /usr/bin/updatedb

A declaração de rotina cron foi criada para o usuário root, veja que esse recurso é simples e direto, e mesmo que você adicione novo comando o puppet não vai sobrescrever o arquivo.

Vamos adicionar um novo comando no cron, só para ter certeza de que ele não vai substituir e apagar a referência ao comando.

# HEADER: This file was autogenerated at Tue May 29 20:03:12 -0300 2012 by puppet.
# HEADER: While it can still be managed manually, it is definitely not recommended.
# HEADER: Note particularly that the comments starting with 'Puppet Name' should
# HEADER: not be deleted, as doing so could cause duplicate cron jobs.
# Puppet Name: cron-updatedb
0 * * * * updatedb
0 * * * * novocomando

Aplique a configuração

root@puppetagent:~/puppet# puppet apply cron.pp 
notice: Finished catalog run in 0.11 seconds

Veja que nada foi feito, afinal o comando updatedb está presente, agora se eu remover a linha do updatedb

# HEADER: This file was autogenerated at Tue May 29 20:03:12 -0300 2012 by puppet.
# HEADER: While it can still be managed manually, it is definitely not recommended.
# HEADER: Note particularly that the comments starting with 'Puppet Name' should
# HEADER: not be deleted, as doing so could cause duplicate cron jobs.
# Puppet Name: cron-updatedb
0 * * * * novocomando

Aplicando novamente a configuração

root@puppetagent:~/puppet# puppet apply cron.pp 
notice: /Stage[main]//Cron[cron-updatedb]/command: command changed 'novocomando' to 'updatedb'
notice: Finished catalog run in 0.14 seconds

Ele vai colocar a linha do updatedb novamente, prático e eficiente.

root@puppetagent:~/puppet# crontab -l

Acompanhe a saída

# HEADER: This file was autogenerated at Tue May 29 20:03:12 -0300 2012 by puppet.
# HEADER: While it can still be managed manually, it is definitely not recommended.
# HEADER: Note particularly that the comments starting with 'Puppet Name' should
# HEADER: not be deleted, as doing so could cause duplicate cron jobs.
# Puppet Name: cron-updatedb
0 * * * * updatedb
0 * * * * novocomando

Apesar da edição manual funcionar, recomenda-se sempre usar as rotinas no puppet já que o puppet está gerenciando a máquina.

dicas cron

Valores possíveis para o parâmetro ensure no resource type cron

  • present
  • absent
    Outros parâmetros interessantes para cron
  • hour
  • minute
  • month
  • monthday
  • weekday

Mais informações em http://docs.puppetlabs.com/references/stable/type.html#cron

padronizando rotinas cron

Agora imagine um cenário hipotético onde você cria configurações para as suas principais rotinas, aquelas comuns para todas as máquina, e imagine que estas configurações foram distribuídas para todos os seus nodes, agora pense que isso não precisa ser um cenário hipotético, isso pode ser real e totalmente possível com o puppet, e fique tranquilo pois veremos mais detalhes na parte puppet cliente/servidor.

A cada recurso, mais um passo para padronização!

resource type: notify

O resource type notify é utilizado para imprimir notificações durante a execução de um manifest.

Agora vamos aprender a utilizar este resource type notify, crie um arquivo no /root/puppet chamado notify.pp e adicione o seguinte conteúdo

notify { "Estamos aprendendo a utilizar o tipo de recurso notify": }
 
notify { "o sistema operacional é $operatingsystem": }
 
notify { "o hostname do none é $fqdn": }
 
notify { "o uptime do ambiente é $uptime": }
 
notify { "o ip da eth0 neste node é $network_eth0": }
 
notify { "esta máquina roda o kernel versão $kernelversion": }

Aplicando a configuração

root@puppetagent:~/puppet# puppet apply notify.pp

Acompanhe a saída

notice: esta máquina roda o kernel versão 2.6.32
notice: /Stage[main]//Notify[esta máquina roda o kernel versão 2.6.32]/message: defined 'message' as 'está máquina roda o kernel versão 2.6.32'
notice: Estamos aprendendo a utilizar o tipo de recurso notify
notice: /Stage[main]//Notify[Estamos aprendendo a utilizar o tipo de recurso notify]/message: defined 'message' as 'Estamos aprendendo a utilizar o tipo de recurso notify'
notice: o hostname do none é puppetagent.hacklab
notice: /Stage[main]//Notify[o hostname do none é puppetagent.hacklab]/message: defined 'message' as 'o hostname do none é puppetagent.hacklab'
notice: o sistema operacional é Debian
notice: /Stage[main]//Notify[o sistema operacional é Debian]/message: defined 'message' as 'o sistema operacional é Debian'
notice: o ip da eth0 neste node é 10.0.2.0
notice: /Stage[main]//Notify[o ip da eth0 neste node é 10.0.2.0]/message: defined 'message' as 'o ip da eth0 neste node é 10.0.2.0'
notice: o uptime do ambiente é 0:25 hours
notice: /Stage[main]//Notify[o uptime do ambiente é 0:25 hours]/message: defined 'message' as 'o uptime do ambiente é 0:25 hours'
notice: Finished catalog run in 0.12 seconds

Abaixo os mesmos notices filtrados para melhor leitura (filtrei manualmente).

notice: /Stage[main]//Notify[esta máquina roda o kernel versão 2.6.32]/message: defined 'message' as 'está máquina roda o kernel versão 2.6.32'
notice: /Stage[main]//Notify[Estamos aprendendo a utilizar o tipo de recurso notify]/message: defined 'message' as 'Estamos aprendendo a utilizar o tipo de recurso notify'
notice: /Stage[main]//Notify[o hostname do none é puppetagent.hacklab]/message: defined 'message' as 'o hostname do none é puppetagent.hacklab'
notice: /Stage[main]//Notify[o sistema operacional é Debian]/message: defined 'message' as 'o sistema operacional é Debian'
notice: /Stage[main]//Notify[o ip da eth0 neste node é 10.0.2.0]/message: defined 'message' as 'o ip da eth0 neste node é 10.0.2.0'
notice: /Stage[main]//Notify[o uptime do ambiente é 0:25 hours]/message: defined 'message' as 'o uptime do ambiente é 0:25 hours'
notice: Finished catalog run in 0.12 seconds

O uso do notify vai se encaixar em diferentes tipos de contextos, normalmente uso para depurar algum problema usando variáveis do facter.

resource type: host

O resource type host é utilizado para criar entradas no arquivo /etc/hosts

Agora vamos aprender a usar o resource type host, crie um arquivo em /root/puppet chamado hosts.pp com o conteúdo abaixo

host { 'jedi':
    ensure  => present,
    ip      => '192.168.0.1',
}

Antes de aplicar vou dar um cat no meu /etc/hosts só para conhecer seu conteúdo

root@puppetagent:~/puppet# cat /etc/hosts

Veja a saída

127.0.0.1 localhost

Agora vamos aplicar a configuração

root@puppetagent:~/puppet# puppet apply hosts.pp 
notice: /Stage[main]//Host[jedi]/ensure: created
notice: Finished catalog run in 0.09 seconds

Verificar se a linha foi adicionado ao hosts

root@puppetagent:~/puppet# cat /etc/hosts

Veja a saída

127.0.0.1 localhost
192.168.0.1 jedi

hosts e facter

você pode ainda trabalhar com variáveis do facter

fqdn

Podemos usar a variável fqdn, ipaddress e hostname para criar as entradas com o nome da máquina, isso é fundamental para o funcionamento de alguns programas, adicione o trecho abaixo ao final do hosts.pp

host { "$fqdn":
    ensure       => present,
    ip           => "$ipaddress",
    host_aliases => "$hostname",
}

Aplique a configuração

root@puppetagent:~/puppet# puppet apply hosts.pp
notice: /Stage[main]//Host[puppetagent.hacklab]/ensure: created
notice: Finished catalog run in 0.09 seconds

Confira o arquivo

root@puppetagent:~/puppet# cat /etc/hosts

Veja o conteúdo

127.0.0.1 localhost
192.168.0.1 jedi
10.0.2.15 puppetagent.hacklab puppetagent

Bacana ele criou uma entrada com o fqdn do node e adicionou um apelido com o hostname.

padronizando hosts

Agora imagine usar o recurso hosts em todos os seus nodes, declarando algumas maquinas fundamentais como zabbix, puppet, ntp e de quebra adicionar o fqdn e hostname do node, esse certamente é mais um passo no caminho da padronização.

resource type: mailalias

O resource type mailalias pode ser utilizado para inserir um alias dentro do arquivo /etc/alias ou modificar um alias.

Agora vamos aprender como declarar um alias para o usuário root, para isto crie um arquivo chamado mailalias.pp dentro de /root/puppet com o conteúdo abaixo:

mailalias { 'root':
    ensure    => 'present',
    recipient => 'monitora@dominio.gov.br',
}

Aplique a configuração

root@puppetagent:~/puppet# puppet apply mailalias.pp 

Acompanhe a saída

notice: /Stage[main]//Mailalias[root]/recipient: recipient changed 'root' to 'monitora@dominio.gov.br'
notice: Finished catalog run in 0.12 seconds

Bacana, o puppet então modificou a root presente no arquivo aliases, colocando o valor monitora@dominio.gov.br como destino.

Eu normalmente crio uma configuração que instala o postfix em cada node e ajusta o aliases, desta forma as rotinas no cron que tem problemas são enviadas por e-mail para a conta monitora, assim posso descobrir quais entradas cron precisam ser corrigidas - tendo a saída padrão redirecionadas para uma arquivo de log - e quais máquina efetivamente rodam rotinas de cron, é uma estratégia interessante para descobrir algumas coisas - perdidas - em seus nodes.

combinando mailalias com exec e refreshonly

Podemos combinar os seguintes resource types mailalias com o exec e refreshonly, assim ao criar o alias, caso ele não exista, o puppet vai rodar o exec, dentro do exec está o comando newaliases, responsável por atualizar o banco de dados do aliases.

mailalias { 'root':
    ensure    => 'present',
    recipient => 'monitora@ebc.com.br',
    notify    => Exec['/usr/bin/newaliases'],
    }
 
exec { '/usr/bin/newaliases':
    refreshonly => true,
}

dicas mailalias

O parâmetro ensure pode ter os seguintes valores

  • present
  • absent

Outro parâmetros interessantes

  • name
  • provider
  • recipient
  • target

padronização do aliases

Se você tiver um MTA rodando em seus nodes, padronizar o aliases é muito importante para enviar notificações para destinos relevantes, parece simplório receber um e-mail com um alerta, mas lembre-se a integridade e disponibilidade pode ser garantida com um simples e-mail de alerta recebido e devidamente tratato, a máquina avisa, nós só temos que aprender a escutar.

meta-parâmetros

Metaparâmetros são basicamente parâmetros coringas que podem ser utilizados em qualquer resource type, eles fazem parte do framework do puppet e podem ser utilizados em qualquer instância do seu manifest.

Os meta-parêmetros do puppet são:

  • alias
  • audit
  • before
  • check
  • loglevel
  • noop
  • notify
  • require
  • schedule
  • stage
  • subscribe
  • tag

Vou abordar aqueles que considero os principais para os novos entusiastas.

metaparameter: before

O meta-parâmetro before é muito útil para você especificar uma ordem para a execução de seu manifest.

Primeiro é importante entender que a ordem dos resource types e objetos criados em um manifest não é necessariamente a ordem em que o puppet vai processá-los e executá-los, o RAL vai executar da forma que ele achar mais eficiente usando os recursos disponíveis no sistema. No entanto, podemos determinar algumas regras, dependências e condições para que as configurações sejam aplicadas. O before é um meta-parâmetro que nos ajuda bastante.

Para entender melhor o uso do before, vamos declarar a instalação do pacote postfix, e nesta configuração vamos usar o before para especificar que antes de instalar o pacote, primeiro o puppet deve incluir entradas referentes ao FQDN nos arquivos hostname, mailname e hosts.

Parece complicado atender a esses requisitos, mas observe abaixo uma das soluções possíveis:

package { 'postfix':
    ensure  => present,
}
 
host { "$fqdn":
    ensure       => present,
    ip           => "$ipaddress",
    host_aliases => "$hostname",
    before       => Package['postfix'],
}
 
file {'hostname':
      path    => '/etc/hostname',
      ensure  => present,
      owner   => root,
      group   => root,
      mode    => 0644,
      content => "$fqdn",
      before  => Package['postfix'],
}
 
file {'mailname':
      path    => '/etc/mailname',
      ensure  => present,
      owner   => root,
      group   => root,
      mode    => 0644,
      content => "$fqdn",
      before  => Package['postfix'],
}

Veja que na declaração dos arquivos eu coloco before package, ou seja faça isto antes de processar o objeto postfix do recurso package.

No objeto package postfix por sua vez eu digo que ele requer/exige que os arquivos mailname e hostname estejam presentes e devidamente configurados.

Vamos agora fazer um teste, crie dentro de /root/puppet um arquivo chamado meta-before.pp, insira o conteúdo do exemplo e aplique a configuração

root@puppetagent:~/puppetlabs# puppet apply meta-before.pp

Acompanhe a saída

notice: /Stage[main]//File[mailname]/content: content changed '{md5}e66f20a70a5cc73dfce299c7acd7ff5b' to '{md5}3711f24bc523b25a7188d2cdef919f0c'
notice: /Stage[main]//Host[puppetagent.hacklab]/ensure: created
notice: /Stage[main]//File[hostname]/content: content changed '{md5}e66f20a70a5cc73dfce299c7acd7ff5b' to '{md5}3711f24bc523b25a7188d2cdef919f0c'
notice: /Stage[main]//Package[postfix]/ensure: ensure changed 'purged' to 'present'
notice: Finished catalog run in 18.50 seconds

Bacana, rodou seguindo quase exatamente a ordem, o package postfix foi a última coisa que ele executou.

Vamos fazer de outro jeito, definindo que um objeto deve rodar antes do outro, forçando uma ordem em cadeia, para isto substitua o conteúdo do arquivo pelo conteúdo abaixo:

package { 'postfix':
    ensure  => present,
}
 
host { "$fqdn":
    ensure       => present,
    ip           => "$ipaddress",
    host_aliases => "$hostname",
    before       => Package['postfix'],
}
 
file {'hostname':
      path    => '/etc/hostname',
      ensure  => present,
      owner   => root,
      group   => root,
      mode    => 0644,
      content => "$fqdn",
      before  => Host["$fqdn"],
}
 
file {'mailname':
      path    => '/etc/mailname',
      ensure  => present,
      owner   => root,
      group   => root,
      mode    => 0644,
      content => "$fqdn",
      before  => File['hostname'],
}

Desfaça as alterações em hostname, mailname, hosts e remova o pacote do puppet, após aplique a configuração.

root@puppetagent:~/puppet# puppet apply meta-before.pp

Acompanhe a saída

notice: /Stage[main]//File[mailname]/content: content changed '{md5}d41d8cd98f00b204e9800998ecf8427e' to '{md5}3711f24bc523b25a7188d2cdef919f0c' 
notice: /Stage[main]//File[hostname]/content: content changed '{md5}d41d8cd98f00b204e9800998ecf8427e' to '{md5}3711f24bc523b25a7188d2cdef919f0c'
notice: /Stage[main]//Host[puppetagent.hacklab]/ensure: created
notice: /Stage[main]//Package[postfix]/ensure: ensure changed 'purged' to 'present'
notice: Finished catalog run in 21.40 seconds

Muito bem, veja que o objeto mailname rodou antes do objeto hostname, que rodou antes do objeto host, que rodou antes do objeto postfix.

dicas before

O meta-parâmetro before é um grande aliado para criação de configurações, através dele podemos estabelecer dependências, defindo a ordem em que os objetos devem ser executados.

metaparameter: subscribe

O meta-parâmetro subscribe faz uma referência a um objeto no qual ele depende, e caso ocorra uma mudança na dependência, isto vai gerar um evento dde log, este evento será detectado e então a configuração do objeto principal em que ele foi declarado será executada.

Agora vamos aprender a utilizar este meta-parâmetro, crie um arquivo em /root/puppet chamado meta-subscribe.pp com o conteúdo abaixo

file { 'zabbixconf':
    path   => "/etc/zabbix/zabbix_agentd.conf",
    source => "/root/puppet/zabbix_agentd.conf",
}
service { 'zabbix-agent':
    ensure    => running,
    subscribe => File['zabbixconf']
  }

Crie o arquivo de exemplo zabbix_agentd.conf no /root/puppet com o seguinte conteúdo:

Server=192.168.20.250,192.168.20.134
Hostname=puppetagent.dominio
StartAgents=5
DebugLevel=3
PidFile=/var/run/zabbix/zabbix_agentd.pid
LogFile=/var/log/zabbix/zabbix_agentd.log
Timeout=10
Include=/etc/zabbix/conf.d/
DisableActive=0
DisablePassive=0

Aqui estou usando o debian para esta demonstração, já tenho o pacote zabbix-agent instalado, esse pacote é uma premissa para nosso exemplo.

Vamos aplicar a configuração que criamos

root@puppetagent:~/puppetlabs# puppet apply meta-subscribe.pp 

Acompanhe a saída

notice: /Stage[main]//File[zabbixconf]/content: content changed '{md5}ec5e96ffbffed08231250be0f2ab60aa' to '{md5}f788751ddaa59011fbcdd468e66803ac'
notice: /Stage[main]//Service[zabbix-agent]: Triggered 'refresh' from 1 events
notice: Finished catalog run in 0.75 seconds

Observe que o arquivo /etc/zabbix/zabbix_agentd.conf era diferente da nossa origem, logo o puppet substituiu o arquivo, esta ação acionou o gatilho do service zabbix-agent e com isso o serviço zabbix sofreu um refresh (foi reiniciado).

Se aplicarmos novamente a configuaração

root@puppetagent:~/puppet# puppet apply meta-subscribe.pp
notice: Finished catalog run in 0.13 seconds

Nada será feito visto que os arquivos são idênticos, logo o serviço também não será reiniciado mesmo lendo tendo um exec declarado no manifest.

dicas subscribe

O subscribe é um ótimo aliado para trabalhar com dependência direta, funciona muito bem aliado com o resource type service, apesar disto, não é meu meta-parêmetro preferido para tratar dependências, gosto muito do notify, vamos conhecê-lo agora.

metaparameter: notify

Este meta-parâmetro trabalha criando um relacionamento de dependência indireta, ele funciona assim, caso ocorra mudança no objeto principal a dependência será acionada, veja que a mudança no objeto principal será o gatilho para o acionamento, este é o inverso do metaparâmetro subscribe.

Agora vamos aprender como funciona o meta-parâmetro notify, crie um arquivo em /root/puppet com o nome meta-notify.pp e insira o conteúdo abaixo:

<codec > file { '/etc/apt/sources.list':

  ensure  => present,
  owner   => 'root',
  group   => 'root',
  mode    => 644,
  source  => 'puppet:///files/apt/sources.list.debian.squeeze',
  notify  => Exec['aptitude update'],

}

exec { 'aptitude update':

   refreshonly => true,

} </code>

Neste exemplo, declaramos um arquivo de configuração de repositórios APT para o DEBIAN, na declaração dissemos que em caso de aplicação da configuração - motivada por uma mudança no arquivo de origem, o notify vai enviar um notificação para o recurso Exec com título 'aptitude update', e este comando será executado.

Esta é outra forma de executar um comando a partir de uma dependência, já tínhamos visto um exemplo nas dicas do resource type exec usando o meta-parâmetro subscribe, e como vimos, podemos fazer o mesmo tratamento de dependências - só que aqui de forma inversa - usando o meta-parâmetro notify.

dicas notify

Eu particularmente uso o notify regularmente para notificar serviços quando um arquivo de configuração deste foi alterado, vou dar um exemplo:

file { 'cyrus.conf':
    path   => "/etc/cyrus.conf",
    source => "/root/puppet/cyrus.conf",
    notify => Service['imapd']
}
service { 'imapd':
    ensure    => running,
  }

Nesse caso, a dependência que aciona o gatilho notify está no resource type file, com isto, caso o arquivo mude, ele notifica o serviço imapd, este por usa vez sofre um refresh (restart). Eu acho esta a forma mais elegante de executar um refresh em um serviço.

metaparameter: alias

Esse meta-parâmetro permite que criemos um nome simbólico (apelido) para um objeto, caso não tenha percebido nós já usamos esse recurso no decorrer desta wiki e agora vamos entender melhor como ele funciona.

exemplo 1: declarando alias de forma explícita

Manifest sem alias no serviço.

file { '/etc/postfix/main.cf':
    source    => "/root/puppet/main.cf",
    notify => Service['postfix']
}
 
service { "postfix":
        ensure     => running,
        enable     => true,
        hasstatus  => true,
        hasrestart => true,
}

Manifest usando alias no serviço.

file { '/etc/postfix/main.cf':
    source    => "/root/puppet/main.cf",
    notify => Service['pfx']
}
 
service { "postfix":
        alias      => 'pfx',
        ensure     => running,
        enable     => true,
        hasstatus  => true,
        hasrestart => true,
}

exemplo 2: declarando alias de forma implícita

A declaração sem alias implícito (caminhos absolutos).

file { '/etc/apache2/httpd.conf':
    source => "/root/puppet/httpd.conf",
}
 
service { 'httpd':
    ensure    => running,
    subscribe => File['/etc/apache2/httpd.conf']
}

Declarando alias de forma implícita usando o title como alias.

file { 'apacheconf':
    path   => "/etc/apache2/httpd.conf",
    source => "/root/puppet/httpd.conf",
}
 
service { 'httpd':
    ensure    => running,
    subscribe => File['apacheconf']
}

testando

sem usar o alias

crie um arquivo em /root/puppet chamado meta-alias.pp e insira o seguinte conteúdo

file { '/etc/zabbix/zabbix_agentd.conf':
    source => "/root/puppet/zabbix_agentd.conf",
}
 
service { 'zabbix-agent':
    ensure    => running,
    subscribe => File['/etc/zabbix/zabbix_agentd.conf']
}

modifique o arquivo zabbix_agentd.conf, você pode inserir uma linha com comentário, esta mudança tem a finalidade de acionar o gatilho de refresh no serviço, após modificar aplique a configuração

root@puppetagent:~/puppet# puppet apply meta-alias.pp
notice: /Stage[main]//File[/etc/zabbix/zabbix_agentd.conf]/content: content changed '{md5}de27fc879839c0f8d544476fc9172f98' to   '{md5}174640c93599238636955c986d200ac1' 
notice: /Stage[main]//Service[zabbix-agent]: Triggered 'refresh' from 1 events
notice: Finished catalog run in 0.38 seconds

bacana, arquivo modificado e refresh executado, agora vamos usar alias

usando alias
file { 'zabbixconf':
    path      => "/etc/zabbix/zabbix_agentd.conf",
    source    => "/root/puppet/zabbix_agentd.conf",
    notify => Service['zbx'],
}
 
package { 'zabbix-agent':
    ensure    => 'present',
    require   => File['zabbixconf'],
}
 
service { "zabbix-agent":
        alias      => 'zbx',
        ensure     => running,
        enable     => true,
        hasstatus  => true,
        hasrestart => true,
}

veja que na declaração acima eu estou usando alias explícito e implícito

adicione um novo comentário no arquivo do zabbix e rode novamente o puppet

root@puppetagent:~/puppet# puppet apply meta-alias.pp 
notice: /Stage[main]//File[zabbixconf]/content: content changed '{md5}10aa4fbd48c8e19392eae801844b4295' to '{md5}39ff620af8b3889e4bf5b4198feb0893'
notice: /Stage[main]//Service[zabbix-agent]: Triggered 'refresh' from 1 events
notice: Finished catalog run in 0.94 seconds 

funcionou, veja que ele encontrou o arquivo zabbixconf e o serviço zbx, sem erros.

dicas alias

usando o alias você escreve menos e deixa seu código mais elegante e legível, evita os erros de digitação e a repetição de paths longos, portanto eu recomendo que use e abuse deste recurso.

metaparameter: require

Esse meta-parâmetro permite que seja definida uma dependência para que um objeto seja processado.

Vamos fazer um exemplo, declare o arquivo olamundo.sh em /opt/scripts, para isto crie um arquivo chamado meta-require.pp em /root/puppet com o conteúdo abaixo:

file {'/opt/scripts/olamundo.sh':
      ensure  => present,
      mode    => 755,
      require => File["/opt/scripts"]
      content => "Eu sou um arquivo de teste",
}

Execute

root@puppetagent:~/puppet# puppet apply meta-require.pp 
err: /Stage[main]//File[/opt/scripts/olamundo.sh]/ensure: change from absent to present failed: Could not set 'present on ensure: No such file or     directory - /opt/scripts/olamundo.sh.puppettmp_9852 at /root/puppet/meta-require.pp:10
notice: Finished catalog run in 0.13 seconds

Veja o erro, ele não criou pois o diretório não existe, podemos até declarar o diretório e usar o before para definir a ordem, mas aqui será melhor se usarmos o parâmetro require, veja o exemplo abaixo:

file { "/opt/scripts":
  ensure => directory
}
 
file {'/opt/scripts/olamundo.sh':
      ensure  => present,
      mode    => 755,
      require => File["/opt/scripts"]
      content => "echo OlaMundo!",
}

Atualize o arquivo meta-require.pp com o conteúdo acima e aplique a configuração novamente

root@puppetagent:~/puppet# puppet apply meta-require.pp 

Acompanhe a saída

notice: /Stage[main]//File[/opt/scripts]/ensure: created
notice: /Stage[main]//File[/opt/scripts/olamundo.sh]/ensure: created
notice: Finished catalog run in 0.15 seconds

Vamos conferir

root@puppetagent:~/puppet# ls /opt/
scripts
root@puppetagent:~/puppet# ls /opt/scripts/
olamundo.sh

Veja que o objeto olamundo.sh foi declarando dizendo que ele requer o objeto diretório, e desta forma a configuração foi executada com sucesso.

dicas require

o require é um meta-parâmetro que eu uso em 99% das configurações que eu crio no puppet, é muito útil e prático.

Podemos ter várias dependências declaradas no require, veja o exemplo abaixo:

 require => [ File["/etc/hostname"], File["/etc/mailname"] ]

configuração em trio

Bom, agora que já colocamos a mão na massa e aprendemos de forma teórica e prática o que é RAL, o que são RESOURCE TYPES, o que são PARAMETERS e o que são METAPARAMETERS, vamos em frente, ainda tem muita coisa para vermos.

Vamos combinar tudo o que aprendemos para criar uma configurações mais elaborada, algo que reflita as necessidades do dia-a-dia, vou chamar isso de configuração em trio.

Para criar essa configuração vamos utilizar 3 RESOURCE TYPES (Trio) usar 2 METAPAREMETERS.

Resource Types

  • package
  • service
  • file

MetaParameters

  • require
  • notify

exemplo conceitual trio

Na configuração em trio vamos atender as seguintes necessidades:

  • Um pacote deve ser declarado como presente/instalado
  • O serviço relacionado ao pacote deve estar habilitado
    • O serviço relacionado ao pacote de estar rodando
    • O serviço relacionado ao pacote aceita comando restart em seu init script
    • O serviço relacionado ao pacote aceita comando status em seu init script
    • O serviço depende do objeto pacote
  • Um arquivo de configuração do serviço deve ser declarado como presente
    • O arquivo de configuração depende do objeto pacote
    • O arquivo deve pertencer ao usuário root
    • O arquivo deve pertecer ao grupo root
    • O arquivo deve ter permissão 644
    • O arquivo de configuração deve notificar o serviço caso ocorra mudança na origem

A tradução e construção destes requisitos acima em código puppet pode ser solucionada desta forma:

    package { 'pacote':
        ensure => present,
    }
 
    service { 'servico':
        ensure     => running,
        enable     => true,
        hasrestart => true,
        hasstatus  => true,
        require    => Package['pacote'],
    }
 
    file { 'main.cf':
        path    => "/etc/pacote/pacote.conf",
        source  => "/root/puppet/pacote.conf",
        owner   => 'root',
        group   => 'root',
        mode    => 644,
        require => Package['pacote'],
        notify  => Service['servico'],
    }

Veja que a tradução é visualmente mais objetiva que a declaração das necessidades/requisitos, o código é limpo e entendível.

exemplo postfix

Crie um arquivo em /root/puppet chamado postfix.pp com o conteúdo abaixo

    package { 'postfix':
        ensure => present,
    }
 
    service { 'postfix':
        ensure     => running,
        enable     => true,
        hasrestart => true,
        hasstatus  => true,
        require    => Package['postfix'],
    }
 
    file { 'main.cf':
        path    => "/etc/postfix/main.cf",
        source  => '/root/puppet/main.cf',
        owner   => 'root',
        group   => 'root',
        mode    => 644,
        require => Package['postfix'],
        notify  => Service['postfix'],
    }

Crie um arquivo em /root/puppet com o nome main.cf com o conteúdo abaixo

inet_interfaces = localhost
inet_protocols = all
 
myhostname = puppetagent.dominio
mydomain = dominio
mydestination = $myhostname, localhost.$mydomain, localhost
 
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases

bacana agora vamos aplicar a configuração

root@puppetagent:~/puppet# puppet apply postfix.pp

Acompanhe a saída

notice: /Stage[main]//Package[postfix]/ensure: ensure changed 'purged' to 'present'
notice: /Stage[main]//File[main.cf]/content: content changed '{md5}a9b852b498950544c2ea8f63aa9cd3bd' to '{md5}fbffdbd6f85cdf5fd998c0c7c4a06ec6'
notice: /Stage[main]//Service[postfix]: Triggered 'refresh' from 1 events
notice: Finished catalog run in 24.77 seconds

Agora vamos conferir o arquivo de configuração

root@puppetagent:~/puppet# cat /etc/postfix/main.cf

Saída

inet_interfaces = localhost
inet_protocols = all
 
myhostname = puppetagent.dominio
mydomain = dominio
mydestination = $myhostname, localhost.$mydomain, localhost
 
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases

Vamos conferir se o serviço está rodando

root@puppetagent:~/puppet# /etc/init.d/postfix status

Saída

postfix is running.

Vamos conferir se o pacote foi instalado

root@puppetagent:~/puppet# dpkg --list|grep postfix

Saída

ii  postfix                            2.7.1-1+squeeze1             High-performance mail transport agent

Tudo conforme declaramos.

Veja que esta configuração trio será a estrutura básica em muitos códigos puppet que você vai escrever.

variáveis, fatos, condições e casos

Partindo do pressuposto que você compreende o que é uma variável e o que é uma estrutura condicional, vamos abordar então o uso de variáveis e condionais nas configurações do puppet.

variables

Nos exemplos do notify em RESOURCE TYPES nós já utilizamos algumas variáveis do facter, vamos fazer um novo exemplo apenas para aquecimento, antes vamos conhecer algumas regrinhas básicas

  • $variaveis sempre começam com cifrão. Use = para associar um valor a uma variável.
  • Podemos associar caracteres, números, valores especiais (true,false,undef), arrays e hashes em variáveis do puppet.

Crie um arquivo chamado variables.pp com o conteúdo abaixo

$alerta = "Mensagem de alerta - Aprendendo a utilizar variáveis."
notify { "$alerta": }

Aplique a configuração

root@puppetagent:~/puppet# puppet apply variables.pp 
notice: /Stage[main]//Notify[Mensagem de alerta (Aprendendo a utilizar variáveis.)]/message: defined 'message' as 'Mensagem de alerta (Aprendendo a   utilizar variáveis.)'
notice: Finished catalog run in 0.17 seconds

bacana, exemplo simples, porém direto!

facts

Lá no início da wiki falei de fatos e facter, vamos detalhar um pouco mais.

O facter é uma biblioteca ruby multiplataforma criada e mantida pelo puppetlabs, esta biblioteca coleta informações do sistema operacional e do hardware, informações como tipo sistema operacional, versão do sistema operacional, kernel, ip, mac, hostname, fqdn, sshkey, dentre outras coisas.

Após a coleta ele transforma estes dados no que vamos chamar de fatos, os fatos coletados em essência são associados a variáveis que podem ser utilizadas nas configurações do puppet.

O facter funciona nos mesmos sistemas operacionais e nas mesmas arquiteturas que o puppet, ele na verdade é uma dependência para o puppet funcionar.

Além dos fatos DEFAULT que vem no puppet, podemos estender esses fatos, ou seja, podemos criar novos fatos de acordo com nossas necessidades

Se quiser saber mais sobre o facter acesse http://www.puppetlabs.com/puppet/related-projects/facter/

Já usamos fatos nos exemplos do tipo de recurso host para declarar entradas no /etc/hosts, vamos ver novamente

host { "$fqdn":
    ensure       => present,
    ip           => "$ipaddress",
    host_aliases => "$hostname",
}

Seu uso é simples assim.

conditions

podemos trabalhar condicionais no puppet, são eles IF, ELSIF e ELSE.

if condição {
  bloco de código
}
elsif condição {
  bloco de código
}
else {
  bloco de código
}

Aqui um exemplo retirado do site do puppet, está bacana para um primeiro entendimento, veja que ele verifica um fato $is_virtual, caso seja verdadeiro ele desliga o serviço ntpd, caso seja falso ele declara o serviço ntpd como ativo.

if $is_virtual == 'true' {
      service {'ntpd':
        ensure => stopped,
        enable => false,
      }
    }
    else {
      service { 'ntpd':
        name       => 'ntpd',
        ensure     => running,
        enable     => true,
        hasrestart => true,
        require => Package['ntp'],
      }
    }

Aqui outro exemplo, verifico se o valor de um fato é Redhat ou Debian, defino um valor para uma outra variável e imprimo essa informação.

if $operatingsystem == 'RedHat' {
   $vimeditor = "vim"
   notify { "o nome do pacote vim para $operatingsystem é $vimeditor": }
}
elsif $operatingsystem == "Debian" {
    $vimeditor = "vim-enhanced"
    notify { "o nome do pacote vim para $operatingsystem é $vimeditor": }
}
else {
   notify { "O sistema $operatingsystem não casou com nenhuma regra": }
}

O uso do E (and) lógico pode ser feito da seguinte forma

<codec> if ( $operatingsystem == 'Debian' ) and ( $lsbdistcodename == 'squeeze' ) {

  
  bloco de código

}
</code>

O uso do OU (or) lógico pode ser feito da seguinte forma

<codec> if ( $operatingsystem == 'Debian' ) or ( $operatingsystem == 'Ubuntu' ) {

  
  bloco de código

}
</code>

valores

Verdadeiro

if var = true
if var = 1

Falso

if var = false
if var = 0

Fatos

if var = $fact
if var = $operatingsystem
if var = $kernel

Não definido

if var = undef

Outrovalor

if var = outrovalor

operadores

Os operadores possíveis de utilizar em condicionais são:

comparando
  • Igual ==
  • Diferente !=
  • Menor <
  • Maior >
  • Maior Igual ⇐
  • Menor Igual >=
regex

case

Esse é um recurso que uso muito, principalmente para verificar qual é o sistema operacional, com isto posso definir nomes de pacotes, arquivos específicos para aquele sistema operacional e muito mais.

O uso do case é bastante simples, veja o conceito

caso condição faça

Veja a sintaxe

case $var {
      valor1: { código }
      valor2: { código }
      default: { codigo }
    }

Veja o exemplo de case para verificar o sistema operacional, com isso ele declara o nome do pacote de acordo com o sistema operacional

 case $operatingsystem {
      centos: { $apache = "httpd" }
      debian: { $apache = "apache2" }
    }
    package {'apache':
      name   => $apache,
      ensure => latest,
    }

Esse tipo de configuração é importante pois no debian o pacote chama-se vim-enhanced e no redhat chama-se apenas vim, esse tipo de coisa deve ser tratada com muito cuidado, e o case no ajuda nestas situações.

Agora vamos deixar a configuração um pouco mais elaborada, acompanhe abaixo

case $operatingsystem {
  CentOS,RedHat: { 
    $package_name = 'ntp'
    $service_name = 'ntpd'
    $conf_file    = 'ntp.conf.el'
  }
  Debian,Ubuntu: { 
    $package_name = 'ntp'
    $service_name = 'ntp'
    $conf_file    = 'ntp.conf.debian'
  }
}
 
# declarando pacote com uso de variável do case
 
package { "$package_name":
  ensure => present,
}
 
# declarando serviço com uso de variável do case
 
service { "$service_name":
  ensure     => running,
  enable     => true,
  hasrestart => true,
  hasstatus  => true,
  require    => Package['ntp'],
} 
 
# declarando arquivo de configuração com uso de variável do case
 
file { '/etc/ntp.conf':
  ensure  => present,
  source  => "/root/puppet/${conf_file}",
  notify  => Service["$service_name"],
  require => Package["$package_name"],
}

Veja que desta vez uso o case para verificar o tipo de sistema operacional, com isto declaro 3 variáveis contendo nome do pacote, nome do serviço e arquivo de configuração, com isso posso declarar pacote, servico e arquivo usando variáveis, evitando repetição e checagens condicionais desnecessárias.

dicas case

Seguem algumas dicas bacanas para o dia a dia.

function fail

Se usarmos a função fail no case, caso ele não case com nada vai parar com um erro.

case $operatingsystem {
      centos, redhat: { $apache = "httpd" }
      debian, ubuntu: { $apache = "apache2" }
      default: { fail("Sistema operacional não reconhecido") }
    }

vamos falar sobre funções em outro momento

match

Você pode utilizar o case para verificar se o valor de um fato 'casa' com as opções que você declarou, veja os exemplos abaixo:

case $operatingsystem {
                /Debian|Ubuntu/: { }
}

No exemplo acima quero comparar e ver se o valor da variável $operatingsystem casa com Debian ou Ubuntu, no exemplo abaixo quero saber se o nome da máquina começa com alguma das opções que estou declarando.

case $hostname {
        /^(mta|smtp|zimbra|mail|laboratorio04|otrs|ocomon|boletins|radioagencia01)/: { }
}

Mais informações em http://docs.puppetlabs.com/learning/variables.html#case

seletores

Os seletores são mais incomuns, porem muito funcionais quando desejamos declarar algumas variáveis de acordo com um fato, veja o exemplo:

  $apache = $operatingsystem ? {
      centos                => 'httpd',
      debian                => 'apache2', 
      default               => undef,
    }

Aqui dizemos algo como:

  • a variável apache tem o valor httpd se o valor de $operatingsystem for centos
  • a variável apache tem o valor apache2 se o valor de $operatingsystem for debian
  • por padrão a variável apache não está definida com nenhum valor

Outro exemplo:

$vim = $operatingsystem ? {
        'RedHat' => 'vim-enhanced',
        'CentOS' => 'vim-enhanced',
        'Fedora' => 'vim-enhanced',
        default  => 'vim',
    }
 
    package { "$vim":
        ensure => present,
        alias  => 'vim',
    }
 
    file { "vimrc":
        ensure  => present,
        path    => $operatingsystem ? {
            'Debian' => '/etc/vim/vimrc',
            'Ubuntu' => '/etc/vim/vimrc',
            default  => '/etc/vimrc',
        },
        owner   => "root",
        group   => "root",
        mode    => "644",
        source => "/root/puppet/vimrc"
}

Observe que neste exemplo eu uso seletores em dois momentos, primeiro para definir o nome do pacote, depois para definir o path do arquivo vimrc.

Ao usar selectores você escreve um pouco menos que o case e na minha opinião o código fica mais elegante e legível.

Mais info em http://docs.puppetlabs.com/learning/variables.html#selectors

classes

No ponto em que estamos já sabemos usar muitos resource types do puppet, parâmetros, meta-parâmetros e condicionais, o que aprendemos é ótimo para a criação de configurações simples e genéricas. Isso já nos permite aplicadar configurações em todos os nossos nodes com o objetivo de padronizá-los.

Mas qual seria a forma mais eficiente de aplicar tais configurações?

Para aplicar essas configurações podemos usar duas funcionalidades do Puppet, são elas CLASSES e MÓDULOS, essas funcionalidades permitem que façamos reaproveitamento de código, e isso é importante e agiliza a vida do administrador de sistemas.

Além do reaproveitamento de código e configurações, existem alguns tipos de configurações que se aplicam a um node específico ou a grupos de nodes dentro de um mesmo contexto, logo elas precisam ser acionadas apenas em certos casos.

O uso de classes e módulos nos permite segmentar as configurações que criamos, isso evita que sejam escritos aqueles longos arquivos com centenas de milhares de linhas. Sabemos que manter um arquivo de configuração com milhares de linhas de código, linhas que fazem centenas de ações diferentes não é uma boa prática, cada alteração aumenta a complexidade do arquivo, dificultando novas mudanças, manutenções, aumentando o risco de um incidente em seu ambiente, e para isto basta a falta de uma vírgula no final de uma linha.

Separar as configurações por contextos, significa essencialmente que vamos organizá-las de alguma forma que nos permita executá-las apenas quando necessário, declarando isto de forma clara e precisa.

Agora vamos entender como funcionam as CLASSES e depois os MÓDULOS.

funcionamento de uma classe

No puppet as classes são coleções de recursos que podem ser aplicadas a um node.

Uma classe contém blocos de códigos - configurações puppet - que podem ser ligados ou desligadas conforme demanda.

Pense que uma classe pode agrupar diversos recursos parametrizados para executar uma configuração. Podemos criar uma classe chamada postfix que vai agrupar recursos para instalar o pacote postfix, empurrar os arquivos de configuração main.cf e master.cf e definir aspectos e funcionamento do daemon do postfix (master), e com a classe pronta, podemos aplicá-la a um node.

Uma das grandes vantagens do uso de classes é o reaproveitamento de código.

A classe do puppet não tem relação com classe de orientação a objetos, uma classe do puppet está mais 
para um conjunto de papéis (roles) ou aspectos de uma configuração.

reaproveitamento de código

Imagine que você tenha duas máquinas que rodam aplicações web, em ambas as máquinas você precisa instalar o Apache2 e empurrar alguns arquivos de configuração, com o que já aprendemos poderíamos criar um arquivo puppet para cada máquina e executar a configuração.

Mas veja que neste caso, em ambos arquivos seria necessário declarar recursos para a instalação do Apache2, com isto, teríamos que repetir algumas linhas de código, e caso você queira melhorar a configuração do apache2, teríamos de ajustar nos dois arquivos sempre.

Eu entendo que isso não é uma boa prática e que isto ainda nos dará mais trabalho quando desejarmos evoluir um código, pois esse código terá de ser replicado em vários arquivos de configuração de várias máquinas.

Esse problema pode ser resolvido com o uso de classes, afinal elas vão nos permitir construir configurações genéricas para a instalação do Apache2, configurações estas que podem ser chamadas ou incluídas na configuração de cada máquina, reaproveitando o código, diminuindo a complexidade e facilitando a evolução da configuração do Apache2 que está centralizada e segura dentro de uma única classe.

Resumindo, nos arquivos de cada máquina teríamos então o include da classe e além disto as configurações e as particulares de cada ambiente.

declarando uma classe

Agora vamos aprender como declarar uma classe

estrutura de uma classe

Toda a classe deve ter um nome - esse nome deve ser escrito em letras minusculas, e um bloco de código, veja o exemplo abaixo

class nome { 
  bloco de código
}

exemplo de classe ntp

Já temos o exemplo da configuração ntp, vamos transformá-la em classe.

class ntp {
 
  # tratando distribuicoes
 
    case $operatingsystem {
          CentOS,RedHat: { 
              $package_name = 'ntp'
              $service_name = 'ntpd'
              $conf_file    = 'ntp.conf.el'
    }
    Debian,Ubuntu: { 
      $package_name = 'ntp'
      $service_name = 'ntp'
      $conf_file    = 'ntp.conf.debian'
    }
  }
 
  # declarando pacote
 
  package { "$package_name":
    ensure => present,
  }
 
  # declarando servico
 
  service { "$service_name":
    ensure     => running,
    enable     => true,
    hasrestart => true,
    hasstatus  => true,
    require    => Package['ntp'],
  }
 
        # declarando arquivo de configuração
 
  file { '/etc/ntp.conf':
    ensure  => present,
    source  => "/root/puppet/ntp/${conf_file}",
    notify  => Service["$service_name"],
    require => Package["$package_name"],
  }
}

Agora vamos aplicar essa classe

root@puppet:/etc/puppet/manifests/classes# puppet apply ntp.pp 
notice: Finished catalog run in 0.04 seconds

Veja que nada foi feito, isso ocorre pois declaramos a classe, porém não dissemos que ela deve ser executada. Para resolver isto, edite o arquivo e insira o trecho abaixo na última linha.

class {'ntp': }

Agora rode novamente

root@puppet:/etc/puppet/manifests/classes# puppet apply ntp.pp 

Acompanhe a saída

notice: /Stage[main]/Ntp/Package[ntp]/ensure: ensure changed 'purged' to 'present'
notice: /Stage[main]/Ntp/File[/etc/ntp.conf]/content: content changed '{md5}3e250ecaf470e1d3a2b68edd5de46bfd' to '{md5}c9f420ed46fdcba5d7e3edbfe3d6c0a0'
notice: /Stage[main]/Ntp/Service[ntp]: Triggered 'refresh' from 1 events
notice: Finished catalog run in 12.20 seconds

Bacana, declaramos a classe e declaramos que ela deve ser executada, a configuração foi aplicada.

Veja que podemos declarar dezenas de classes em um arquivo, porém se não declararmos quais classes devem ser executadas, nada acontecerá.

include

Existe uma outra forma de declarar a execução de nossas classes, podemos fazer isto através da função include.

include ntp
include zabbix
include apache

No entanto se usarmos o include, ainda assim o puppet não saberá onde estão as classes que estamos declarando.

Para podermos usá-lo corretamente, primeiro precisamos conhecer o module autoloader, será através dele que o puppet vai encontrar e carregar nossas classes.

modulepath

Primeiro vamos verificar o path atual para o diretório de módulos (instalação default do puppet em debian).

root@puppet:/etc/puppet/manifests/classes# puppet apply --configprint modulepath
/etc/puppet/modules:/usr/share/puppet/modules

Bacana, o puppet vai buscar os módulos e classes em /etc/puppet/modules, agora vamos entender o que é um módulo.

Falando nisso, anote ai esse parâmetro (--configprint) com ele você pode obter diversas informações acerca de configurações do puppet.

Continue acompanhando na seção módulos.

módulos

entendendo a estrutura

Para entender a estrutura de um módulos veja o diagrama abaixo

CorSignificado
Azul Diretório Principal
Verde Sub-diretórios Default do Puppet
Roxo Sub-diretório sugestão
Vermelho Manifest init.pp arquivo obrigatório em todos os módulos

directory: module

Você vai substituir o nome module pelo nome do seu módulo, por exemplo apache.

directory: manifests

Contém todos os manifests do módulo e o arquivo init.pp, obrigatório.

file: init.pp

É o arquivo em que declaramos nossa classe principal, ela deve ter o mesmo nome do módulo. Esse é o manifest default de carregamento do seu módulo.

directory: files

Aqui colocamos os arquivos que serão usados e referenciados pelas classes e definições

directory: templates

Aqui colocamos os templates que serão usados pelas classes e definições

directory: lib

Este diretório conterá plugins, podem ser fatos (facts) ou tipos de recursos (resource tyes) criados por você.

criando estrutura

Vou transformar a classe NTP em um módulo, vamos lá.

mão na massa

Acesse do diretório do puppet

cd /etc/puppet

Acesse o diretório de módulos

cd modules

Crie o diretório para seu módulo

mkdir ntp

Acesse o diretório do módulo

cd ntp

Crie o diretório manifests

mkdir manifests

Crie o diretório manifests

mkdir templates

Crie o diretório manifests

mkdir lib

Crie o diretório manifests

mkdir files

Acesse o diretório manifests

cd manifests

Abra o arquivo init.pp (ele ainda não existe)

vim init.pp

Coloque o conteúdo da classe ntp que havíamos criado.

class ntp {
 
  # tratando distribuicoes
 
    case $operatingsystem {
        CentOS,RedHat: { 
            $package_name = 'ntpd'
            $service_name = 'ntpd'
            $conf_file    = 'ntp.conf.el'
        }
        Debian,Ubuntu: { 
            $package_name = 'ntp'
            $service_name = 'ntp'
            $conf_file    = 'ntp.conf.debian'
        }
  }
 
  # declarando pacote
 
  package { "$package_name":
    ensure => present,
  }
 
  # declarando servico
 
  service { "$service_name":
    ensure     => running,
    enable     => true,
    hasrestart => true,
    hasstatus  => true,
    require    => Package['ntp'],
  }
 
  # declarando arquivo de configuração
 
  file { '/etc/ntp.conf':
    ensure  => present,
    source  => "/root/puppet/ntp/${conf_file}",
    notify  => Service["$service_name"],
    require => Package["$package_name"],
  }
}

Veja que eu não declarei no final do arquivo a execução da classe, e vai ficar assim mesmo.

executando módulo

Agora vamos aprender a executar o módulo.

diretamente

Execute

puppet apply -e "include ntp"

Veja a saída

notice: /Stage[main]/Ntp/Package[ntp]/ensure: ensure changed 'purged' to 'present'
notice: /Stage[main]/Ntp/File[/etc/ntp.conf]/content: content changed '{md5}3e250ecaf470e1d3a2b68edd5de46bfd' to '{md5}c9f420ed46fdcba5d7e3edbfe3d6c0a0'
notice: /Stage[main]/Ntp/File[/etc/ntp.conf]/owner: owner changed 'root' to 'gutocarvalho'
notice: /Stage[main]/Ntp/File[/etc/ntp.conf]/group: group changed 'root' to 'gutocarvalho'
notice: /Stage[main]/Ntp/Service[ntp]: Triggered 'refresh' from 3 events
notice: Finished catalog run in 11.71 seconds
usando modulepath

Você pode desenvolver seu módulo abaixo de um diretório como /root/puppet/modules por exemplo, vai funcionar sem nenhum problema desde que você não se esqueça de definir o parâmetro modulepath na execução, veja o exemplo abaixo:

puppet apply -e "include ntp" --modulepath=/root/puppet/modules

fazendo include

Crie um arquivo chamado configura-ntp.pp

vim configura-ntp.pp

Insira o seguinte conteúdo

include ntp

Aplique a configuração

puppet apply configura-ntp.pp 

Veja a saída

notice: /Stage[main]/Ntp/Package[ntp]/ensure: ensure changed 'purged' to 'present'
notice: /Stage[main]/Ntp/File[/etc/ntp.conf]/content: content changed '{md5}3e250ecaf470e1d3a2b68edd5de46bfd' to '{md5}c9f420ed46fdcba5d7e3edbfe3d6c0a0'
notice: /Stage[main]/Ntp/File[/etc/ntp.conf]/owner: owner changed 'root' to 'gutocarvalho'
notice: /Stage[main]/Ntp/File[/etc/ntp.conf]/group: group changed 'root' to 'gutocarvalho'
notice: /Stage[main]/Ntp/Service[ntp]: Triggered 'refresh' from 3 events
notice: Finished catalog run in 10.40 seconds  

Com isso vimos duas formas diferentes de fazer o include.

exemplos de referenciamento

veremos agora alguns exemplos de referenciamento para chamarmos classes em um módulo, e além disto, indicar o caminho para arquivos e templates.

classes

É importante entender como devemos referenciar as classes dentro de um módulo.

  • módulo — esse é o diretório principal do módulo
    • manifests/ — contém os manifests do módulo
      • init.pp — contém um classe, essa classe deve ter o mesmo nome do módulo
    • outraclasse.pp — Contém uma classe (modulo::outraclasse)
    • definicao.pp — Contém uma definição (modulo::definicao)
    • implementa/ — diretório que contém outras classes
      • configuracao.pp — classe configura (modulo::implementa::configuracao)
      • arquivamento.pp — classe arquivamento (modulo::implementa:arquivamento)
        No caso da classe principal, presente dentro de init.pp referenciamos como

        include modulo
        No caso da classe chamada outraclasse

        include modulo::outraclasse
        No caso da classe chamada definição

        include modulo::definicao
        No caso da classe configura dentro do diretório implementa

        include modulo::implementa::configuracao
        No caso da classe configura dentro do diretório implementa

        include modulo::implementa::arquivamento

A classe no arquivo precisa ter o mesmo nome do include, logo o último exemplo teria uma classe assim:

class modulo::implementa::arquivamento {
  block code
}

A puppetlabs recomenda que caa arquivo/manifest tenha apenas uma classe.

Quando entrarmos na parte de cliente servidor, veremos que podemos trabalhar com o referenciamento de classes de outra forma.

files

Ao usar módulos podemos fazer alguns ajustes no referenciamento de arquivos e dos templates (veremos templates em breve).

Veja que chamamos um arquivo com path absoluto do nosso filesystem.

file { '/etc/ntp.conf':
    ensure  => present,
    source  => "/root/puppet/ntp/${conf_file}",
    notify  => Service["$service_name"],
    require => Package["$package_name"],
}

Porém podemos chamá-lo de outra forma, veja o exemplo abaixo:

source  => "puppet:///modules/ntp/${conf_file}",

Ficaria assim

file { '/etc/ntp.conf':
    ensure  => present,
    source  => "puppet:///modules/ntp/${conf_file}",
    notify  => Service["$service_name"],
    require => Package["$package_name"],
}

Assim não importa onde esteja o módulo, o module autoloader vai encontrá-lo, consequentemente vai encontrar o diretório file e o arquivo referenciado.

templates

Caso seu módulo chame-se apache, qualquer arquivo colocado no diretório template deve ser referenciado da seguinte forma em sua classe

file {'/etc/apache2/sites-enabled/intranet.conf':
    ensure => file,
    content => template('apache/intranet.conf'),
}

Mais a frente vamos entender os templates

repositórios com módulos

Você pode encontrar módulos na Puppet Forge (http://forge.puppetlabs.com/).

No Blog do puppet você poderá até encontrar a coluna módulo da semana (http://puppetlabs.com/category/blog/module-of-the-week-blog/).

No github se procurar por puppet também vai encontrar diversos módulos, acho inclusive uma fonte mais interessante que o puppet-forge.

puppet-forge

O puppet tem um recurso nativo que nos permite a instalar módulos do puppet-forge, veja o exemplo abaixo

puppet module install puppetlabs-mysql

Listando módulos do puppet-forge instalados

puppet module list

Outras informações sobre instalações de módulos (http://docs.puppetlabs.com/puppet/2.7/reference/modules_installing.html).

Por hora o que vimos é suficente para construir módulos e trabalhar com eles localmente, agora vamos adiante, precisamos aprender a usar os templates, isso vai facilitar a nossa vida.

templates

O uso de templates ao invés de arquivos estáticos nos permite criar arquivos de forma dinâmica.

As variáveis locais, globais e fatos do facter estão disponíveis para uso nos templates.

Vamos para um exemplo mais elaborado de configuração com uso de templates.

class postfix {
 
       package { 'postfix':
                ensure  => present,
        }
}
 
class postfix::backend inherits postfix {
 
        package { 'postfix-ldap':
                ensure  => present,
        }
 
        file {  '/etc/postfix/main.cf':
                ensure    => present,
                owner     => 'root',
                group     => 'root',
                mode      => 644,
                content   => template('expressolivre/backend.erb'),
                notify    => Service['postfix'],
        }
 
        file {  '/etc/postfix/master.cf':
                ensure   => present,
                owner    => 'root',
                group    => 'root',
                mode     => 644,
                source   => "puppet:///expressolivre/master.cf.debian",
                notify   => Service['postfix'],
        }
 
        service { "postfix":
                ensure     => running,
                enable     => true,
                hasrestart => true,
                hasstatus  => true,
                require => [
                        File["/etc/postfix/main.cf"],
                        File["/etc/postfix/master.cf"],
                        Package["postfix"],
                        Package["postfix-ldap"],
                ]
        }
}

Primeiro vamos interpretar a configuração acima:

  • Tenho uma classe chamada postfix
  • A classe postfix instala o pacote postfix
  • Tenho uma classe chamada postfix::backend
  • A classe postfix::backend herda as configurações da postfix (inherits)
  • A classe postfix::backend declara um arquivo main.cf e aponta a origem para um template
  • A classe postfix::backend declara um arquivo master.cf como file (arquivo estático)
  • A classe postfix::backend declara um serviço postfix com múltiplas dependências.

Legal, mas qual a diferença entre um arquivo estático e um template?

O Arquivo estático não muda não na execução do objeto, já o template é gerado de forma dinâmica.

Veja o arquivo estático do main.cf

# /etc/postfix/main.cf
 
# HEADER: This file was autogenerated by puppet.
# HEADER: While it can still be managed manually, it
# HEADER: is definitely not recommended.
 
smtpd_banner = $myhostname ESMTP $mail_name (detran)
 
inet_interfaces = all
inet_protocols = ipv4
 
relayhost =
recipient_delimiter = +
 
myorigin = host
mydestination = $myhostname, localhost.$mydomain, localhost, host.dominio
mynetworks = 127.0.0.0/8 10.138.26.0/24
 
unknown_local_recipient_reject_code = 550
 
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases

Agora veja um exemplo de template do mesmo arquivo, chamado aqui de main.erb

# /etc/postfix/main.cf
 
# HEADER: This file was autogenerated by puppet.
# HEADER: While it can still be managed manually, it
# HEADER: is definitely not recommended.
 
smtpd_banner = $myhostname ESMTP $mail_name (detran)
 
inet_interfaces = all
inet_protocols = ipv4
 
relayhost =
recipient_delimiter = +
 
myorigin = <%= hostname %>
mydestination = $myhostname, localhost.$mydomain, localhost, <%= fqdn %>
mynetworks = 127.0.0.0/8 10.138.26.0/24
 
unknown_local_recipient_reject_code = 550
 
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases

Observe as linhas do arquivo que o caracterizam como template

myorigin = <%= hostname %>
mydestination = $myhostname, localhost.$mydomain, localhost, <%= fqdn %>

Neste exemplo estou usando a variável fqdn e a variável hostname ambas do facter, com isto, quando o template for chamado essas informações serão substituidas pelo valor de fqdn e hostname no node em que o template estiver sendo processado, na máquina que estou testando ficaria assim:

myorigin = puppetserverless
mydestination = $myhostname, localhost.$mydomain, localhost, puppetserverless.hacklab

extensao

Todo o template deve ter a extensão .erb obrigatóriamente

taggeando

Quando formos referenciar alguma variável, vamos usar as seguintes tags

<%= %>

Exemplo prático

<%= variable %>

comentários

Se quiser fazer um comentário no código use o exemplo abaixo

<%# This comment will be ignored. %>

lookupvar

Quando queremos utilizar variáveis de alguma classe externa, devemos fazer um lookup, veja o exemplo abaixo:

<%= scope.lookupvar('apache::user') %>

concatenando

Você pode fazer referências a vários templates de uma só vez

template('my_module/template1.erb','my_module/template2.erb')

uso de arrays

Podemos criar iterações com arrays, abaixo defino um array em um manifest que referencia um template

$namservers = ["8.8.8.8", "8.8.4.4", "208.67.222.222", "208.67.220.220"]

No template eu posso entrar com o seguinte código

<% nameservers.each do |val| -%>
nameserver <%= val %>
<% end -%>

Com isso a saída seria a seguinte

nameserver 8.8.8.8
nameserver 8.8.4.4
nameserver 208.67.222.222
nameserver 208.67.220.220

São muitas as possibilidades que temos com esse suporte a arrays e impressão por linha.

condicionais

Podemos usar condicionais nos templates, veja o exemplo colocado em um template chamado vimrc.erb

<% if operatingsystem == "Debian" -%> runtime! debian.vim <% end %>

Caso o sistema operacional seja Debian, ele vai colocar a linha abaixo no arquivo

runtime! debian.vim

Simples e prático.

testando templates

Se desejar testa a sintaxe de seus templates, pode fazê-lo usando o comando abaixo

erb -P -x -T '-' mytemplate.erb | ruby -c

diretório do template

serverless/masterless

Os templates devem ser colocados no diretório templates do módulo.

puppetmaster

Normalmente no caso de uso de master/agent ou server/cliente você poderá colocar o arquivo em

/etc/puppet/templates

Caso ele seja referencia por alguma classe em /etc/puppet/manifests, porém prefira sempre usar dentro de módulos.

referenciando templates

Já vimos como referenciar templates nos exemplos das classes, aqui vai mais um exemplo:

    file {'/etc/foo.conf':
      ensure  => file,
      require => Package['foo'],
      content => template('foo/foo.conf.erb'),
    }

Isso representaria algo como

...modulo/templates/foo.conf.erb

ou

/etc/puppet/tempates/foo/foo.conf.erb

Depende do seu ambiente

dicas templates

Use e abuse dos templates, eles farão você reaproveitar código diminuir o número de arquivos estáticos e lhe dará agilidade para configurar seus serviços.

Mais informações http://docs.puppetlabs.com/guides/templating.html

definitions

Até agora já vimos o ral, manifests, classes e módulos, portanto já podemos dar uma olhada na parte de definitions.

A maioria das classes não atende a todas as questões em diferentes em sistemas, e com isto, mesmo utilizando os recursos dinâmicos para ajustar o comportamento de classes, ainda assim, com que já aprendemos é impossível construir módulos que se ajustem perfeitamente a diferentes cenários. Isto nos força a criar novas classes, muito similares, repetindo código e aumentando a complexidade de nossa administração.

As definições nos ajudam a resolver isto, podemos construir classes genéricas que receberão atributos no momento em que a classe for declarada para sua execução.

entenda e reflita

Imagine por exemplo uma classe chamada PROXY que instala e configura o SQUID em seu sistema.

Sabemos que algumas configurações do SQUID mudam de máquina para máquina, principalmente o tamanho de CACHE, diretório de CACHE, quantidade de uso de memória, quantidade de uso de disco, tipos de algoritmos para controle do CACHE, dentre outras coisas.

Algumas coisas podemos prever com uso de templates como o servername e ips, porém outras configurações como porta e nameservers não poderiam.

Com isto, precisaríamos de vários templates, praticamente iguais, atendendo a algumas poucas máquinas, e isto seria ruim para administrar e manter.

Agora imagine que ao declarar uma classe, nós já aproveitamos e definimos alguns atributos que poderão ser utilizados pelo template, diretamente no manifest. Assim poderíamos ter uma estrutura única de configuração de SQUID que seria alimentada por estes atributos de forma dinâmica.

É isso que as definicões nos permitem fazer.

estrutura

Entendam a estrutura de uma definição.

definição

define nome($var1, $var2) {
    código
}

declaração

Para executar essa definição devemos declarar da seguinte forma em um manifest

nome { 'titulo' :
        var1                       => 'valor1',
        var2                       => 'valor2',
}

casos reais

proxy

Agora vamos pegar um exemplo real de proxy

definition

define proxy::squid($http_port, $squid_mode, $squid_hostname, $cache_mem, $maximum_object_size_in_memory, $maximum_object_size, $memory_replacement_policy, $cache_replacement_policy, $cache_dir, $cache_mgr, $cache_effective_user, $cache_effective_group, $dns_nameservers, $ips_squid) {
 
    ### pacotes ####################################
 
    package { 'squid':
        ensure => present,
    }
 
    ### servicos  ##########################
 
    service { 'squid':
       ensure     => running,
       enable     => true,
       hasrestart => true,
       hasstatus  => true,
       require    => Package['squid'],
    }
 
    ### arquivos ############################
 
    # configuracao do servico squid
 
    file { '/etc/squid/squid.conf':
        ensure  => present,
        content => template('proxy/squid.conf.erb'),
        owner   => 'root',
        group   => 'root',
        mode    => 644,
        require => Package['squid'],
        notify  => Exec['squid-reconfigure'],
    }
 
    ### execs ##########################
 
    exec { 'squid -k reconfigure':
        alias       => 'squid-reconfigure',
        path        => '/usr/bin:/usr/sbin:/bin',
        refreshonly => true,
    }
 
}

Veja que uma definição é muito parecida com uma classe, porém declaramos os atributos que ela vai receber no início.

Essa definição faz o seguinte:

  • Declara o pacote squid
  • Declara a configurações para o serviço squid
  • Declara um arquivo squid.conf com referencia para um template squid.erb
  • Declara um comando do squid

E os atributos que ele vai receber são:

$http_port, 
$squid_mode, 
$squid_hostname, 
$cache_mem, 
$maximum_object_size_in_memory, 
$maximum_object_size, 
$memory_replacement_policy, 
$cache_replacement_policy, 
$cache_dir, 
$cache_mgr, 
$cache_effective_user, 
$cache_effective_group, 
$dns_nameservers, 
$ips_squid

Entendido, agora veremos como declarar a execução dessa definição.

declarando a definição

No manifest do seu nome, você vai declarar a definição, definir um título e especificar o valor de cada atributo, como o exemplo abaixo:

proxy::squid { 'proxyFilial' :
        http_port                       => '3128',
        squid_mode                      => 'transparent',
        squid_hostname                  => 'proxy.ebc',
        cache_mem                       => '2 GB',
        maximum_object_size_in_memory   => '6 MB',
        maximum_object_size             => '128 MB',
        memory_replacement_policy       => 'heap GDSF',
        cache_replacement_policy        => 'heap LFUDA',
        cache_dir                       => 'aufs /var/spool/squid 1024 16 256',
        cache_mgr                       => 'monitora@ebc.com.br',
        cache_effective_user            => 'proxy',
        cache_effective_group           => 'proxy',
        dns_nameservers                 => '127.0.0.1 10.61.12.2 172.16.1.1',
        ips_squid                       => '127.0.0.1 192.168.12.3',
    }

Aqui outro exemplo para o proxy de uma matriz.

proxy::squid { 'proxyMatriz' :
        http_port                       => '3128',
        squid_mode                      => 'transparent',
        squid_hostname                  => 'proxy.ebc',
        cache_mem                       => '4 GB',
        maximum_object_size_in_memory   => '6 MB',
        maximum_object_size             => '128 MB',
        memory_replacement_policy       => 'heap GDSF',
        cache_replacement_policy        => 'heap LFUDA',
        cache_dir                       => 'aufs /var/spool/squid 4098 16 256',
        cache_mgr                       => 'monitora@ebc.com.br',
        cache_effective_user            => 'proxy',
        cache_effective_group           => 'proxy',
        dns_nameservers                 => '127.0.0.1 10.61.12.2 172.16.1.1',
        ips_squid                       => '127.0.0.1 192.168.12.3',
    }

Veja como é prático declarar os atributos e usar uma definição para diferentes cenários.

Usar definições desse jeito fará mais sentido no momento em que estivermos estudando o puppet em modo client/servidor.

Mas o importante é que você já entendeu como funcionam definições.

template squid.erb

Agora vejam um trecho do arquivo de template.

...
http_port <%= http_port %> <%= squid_mode %>
visible_hostname <%= squid_hostname %>
dns_nameservers <%= dns_nameservers %>
...
cache_mem <%= cache_mem %>
cache_replacement_policy <%= cache_replacement_policy %>
cache_dir <%= cache_dir %>
cache_mgr <%= cache_mgr %>
cache_effective_user <%= cache_effective_user %>
cache_effective_group <%= cache_effective_group %>
...
acl ips_squid src <%= ips_squid %>
...

dicas definition

As definições são fundamentais para o reaproveitamento de código, o funcionamento não é diferente das classes, um arquivo para cada classe, um arquivo para cada definição, arquivos dentro do diretório manifests, e a parte de namespace dos subdiretórios é a mesma coisa das classes, o esquema de referencia não muda, com exceção da declaração dos atributos, característica que é única das definições.

Mais informações em: http://docs.puppetlabs.com/learning/modules2.html

testando sem aplicar (noop)

Antes de aplicar uma configuração, podemos fazer um teste mais seguro, simulando as ações que o puppet vai executar durante a aplicação das configurações do catálogo.

Para fazer o teste usamos o parâmetro –noop, peguei o exemplo de trio postfix que já criamos para ilustrar o cenário.

puppetserverless:~/puppetlabs# puppet apply postfix.pp --noop

Veja a saída

notice: /Stage[main]//Package[postfix]/ensure: current_value purged, should be present (noop)
notice: /Stage[main]//File[main.cf]/ensure: current_value absent, should be file (noop)
err: /Stage[main]//Service[postfix]: Could not evaluate: Could not find init script for 'postfix'
notice: /Stage[main]//Service[postfix]: Would have triggered 'refresh' from 1 events
notice: Class[Main]: Would have triggered 'refresh' from 3 events
notice: Stage[main]: Would have triggered 'refresh' from 1 events
notice: Finished catalog run in 0.73 seconds

Observe que ele simula a execução mas não aplica nenhuma configuração. Esse parâmetro é essencialmente útil para avaliar por exemplo se algum pacote será removido devido a uma dependência ou se alguma modificação além do que foi declarado será feita como consequência da mudança. Na dúvida use o –noop antes de aplicar de fato.

Aplicando a configuração sem o noop

puppetserverless:~/puppetlabs# puppet apply postfix.pp

Saída

notice: /Stage[main]//Package[postfix]/ensure: ensure changed 'purged' to 'present'
notice: /Stage[main]//File[main.cf]/content: content changed '{md5}6c2b81273db245fb47d3f5aacbc71c8e' to '{md5}fbffdbd6f85cdf5fd998c0c7c4a06ec6'
notice: /Stage[main]//Service[postfix]: Triggered 'refresh' from 1 events
notice: Finished catalog run in 22.25 seconds

No modo cliente/servidor você também poderá fazer testes antes de aplicar configurações, bastará usar o seguinte comando

puppet agent --test --noop

Mas isto, veremos na wiki puppet cliente/servidor.

debug/trace/log

Se está ocorrendo algum problema, podemos utilizar alguns parâmetros que vão nos ajudar depurar o problema

debug

Se quiser ativar o debug completo durante a execução do manifest.

Em modo autônomo

puppet apply manifest.pp --debug

Em modo cliente/servidor

puppet agent --test --debug

trace

Se quiser debugar e imprimir stacl traces , primeiro habilite trace = true no puppet.conf e depois execute o comando abaixo:

Em modo autônomo

puppet apply manifest.pp --debug --trace

Em modo cliente/servidor

puppet agent --test --debug --trace

Mais info em http://docs.puppetlabs.com/references/stable/configuration.html#trace

redirecionando logs

Se quiser direcionar a saída para um arquivo de log use os comandos abaixo:

Em modo autônomo

puppet apply -l /tmp/manifest.log manifest.pp

Em modo cliente/servidor

puppet agent --test -l /tmp/agent.log

filebucket (backup/restore)

O filebucket é um recurso que nos permite salvar, recuperar e visualizar arquivos que foram modificados pelo puppet. Ele é um recurso essencial, é muito importante aprender a usá-lo corretamente.

restore

eu tenho um arquivo main.cf com o seguinte conteúdo

# /etc/postfix/main.cf

# HEADER: This file was autogenerated by puppet.
# HEADER: While it can still be managed manually, it
# HEADER: is definitely not recommended.

smtpd_banner = $myhostname ESMTP $mail_name (detran)

inet_interfaces = all
inet_protocols = ipv4

relayhost =
recipient_delimiter = +

myorigin = agent-debian.hacklab
mydestination = $myhostname, localhost.$mydomain, localhost, agent-debian.hacklab
mynetworks = 127.0.0.0/8 10.138.26.0/24

unknown_local_recipient_reject_code = 550

alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases

na ultima linha eu vou adicionar um comentário

# teste de recuperacao do filebucket

e vou rodar o puppet

root@agent-debian:/tmp# puppet agent --testinfo: Retrieving plugin

saída

info: Loading facts in /var/lib/puppet/lib/facter/etcgroup.rb
info: Loading facts in /var/lib/puppet/lib/facter/etcpasswd.rb
No LSB modules are available.
info: Caching catalog for agent-debian.hacklab
info: Applying configuration version '1340384371'
notice: /Stage[main]/Localmta/File[/etc/postfix/main.cf]/content: 
--- /etc/postfix/main.cf  2012-06-22 16:12:39.000000000 -0300
+++ /tmp/puppet-file20120622-5649-1e4kghw-0 2012-06-22 16:13:36.000000000 -0300
@@ -20,5 +20,3 @@
 
 alias_maps = hash:/etc/aliases
 alias_database = hash:/etc/aliases
-
-# teste de recuperacao do filebucket

info: /Stage[main]/Localmta/File[/etc/postfix/main.cf]: Filebucketed /etc/postfix/main.cf to server with sum d59100e183899a5d11f435656c4d8f5b
notice: /Stage[main]/Localmta/File[/etc/postfix/main.cf]/content: content changed '{md5}d59100e183899a5d11f435656c4d8f5b' to '{md5}c9bcb67fdf5c367dd40691aad07fa3b9'
info: /Stage[main]/Localmta/File[/etc/postfix/main.cf]: Scheduling refresh of Service[postfix]
notice: /Stage[main]/Localmta/Service[postfix]: Triggered 'refresh' from 1 events
notice: Finished catalog run in 24.85 seconds

veja que o puppet modificou o arquivo pois ele estava diferente do que foi declarado, mas não se aflija, caso precise recuperá-la basta usar o comando abaixo

root@agent-debian:/tmp# puppet filebucket restore /etc/postfix/main.cf d59100e183899a5d11f435656c4d8f5b

observe que estamos usando o hash da modificação, este pode ser obtido no arquivo /var/log/puppet/puppet.log ou durante a execução do puppet como pode ve no log acima do comando, agora vamos conferir o arquivo realmente foi recuperado

root@agent-debian:/tmp# cat /etc/postfix/main.cf 

saída

# /etc/postfix/main.cf

# HEADER: This file was autogenerated by puppet.
# HEADER: While it can still be managed manually, it
# HEADER: is definitely not recommended.

smtpd_banner = $myhostname ESMTP $mail_name (detran)

inet_interfaces = all
inet_protocols = ipv4

relayhost =
recipient_delimiter = +

myorigin = agent-debian.hacklab
mydestination = $myhostname, localhost.$mydomain, localhost, agent-debian.hacklab
mynetworks = 127.0.0.0/8 10.138.26.0/24

unknown_local_recipient_reject_code = 550

alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases

# teste de recuperacao do filebucket

veja que confere, é o arquivo que adicionamos o comentário

get

se você quiser apenas observar o arquivo que foi salvo, pode usar o parâmetro get

root@agent-debian:/tmp# puppet filebucket get d59100e183899a5d11f435656c4d8f5b

saída

# /etc/postfix/main.cf

# HEADER: This file was autogenerated by puppet.
# HEADER: While it can still be managed manually, it
# HEADER: is definitely not recommended.

smtpd_banner = $myhostname ESMTP $mail_name (detran)

inet_interfaces = all
inet_protocols = ipv4

relayhost =
recipient_delimiter = +

myorigin = agent-debian.hacklab
mydestination = $myhostname, localhost.$mydomain, localhost, agent-debian.hacklab
mynetworks = 127.0.0.0/8 10.138.26.0/24

unknown_local_recipient_reject_code = 550

alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases

# teste de recuperacao do filebucket

backup

agora vamos aprender a fazer backup de um arquivo, vou adicionar mais uma linha ao final do arquivo, apenas para ter um novo hash

# novo teste, agora de backup

e vamos fazer o backup

root@agent-debian:/tmp# puppet filebucket backup /etc/postfix/main.cf 

saída

/etc/postfix/main.cf: 2de085d00496a249b5eaba083809ad08

veja que ele me deu um novo hash, com ele eu posso fazer um restore ou visualizar o conteúdo com o get, para testar vamos rodar o get

root@agent-debian:/tmp# puppet filebucket get 2de085d00496a249b5eaba083809ad08

saída

# /etc/postfix/main.cf

# HEADER: This file was autogenerated by puppet.
# HEADER: While it can still be managed manually, it
# HEADER: is definitely not recommended.

smtpd_banner = $myhostname ESMTP $mail_name (detran)

inet_interfaces = all
inet_protocols = ipv4

relayhost =
recipient_delimiter = +

myorigin = agent-debian.hacklab
mydestination = $myhostname, localhost.$mydomain, localhost, agent-debian.hacklab
mynetworks = 127.0.0.0/8 10.138.26.0/24

unknown_local_recipient_reject_code = 550

alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases

# teste de recuperacao do filebucket
# novo teste, agora de backup

bacana, o filebucket é bastante seguro e bastante prático, é essencial saber usá-lo.

uso de puppet autônomo

depois de ver tudo isso pode ser que ainda fique a dúvida, quais usos teria para o puppet em modo autônomo/serverless?

em seu desktop

pode usar isso para por exemplo configurar seu ambiente de usuário em seu Desktop Linux ou Unix, fazendo isto em segundos após a instalação do OS.

puppet apply meudesktop.pp

para instalar o puppet

podes criar um manifest puppetmaster-passenger.pp para instalar e configurar o puppet em seu ambiente, fazendo isto em segundos.

puppet apply puppetmaster-passenger.pp

para instalar serviços

podes criar um manifest para instalar e configurar por exemplo um ambiente tomcat completo, fazendo isto em segundos.

puppet apply tomcat-environment.pp

puppet modo cliente/servidor

Agora que já entendemos como funciona o Puppet em modo autônomo, é hora de entender como é que funciona em modo cliente/servidor.

Acesse a Wiki …..

referências

comentários



puppet_serverless.txt · Last modified: 2013/02/13 18:35 by gutocarvalho
CC Attribution-Share Alike 4.0 International
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0