Salta el contingut

Puppet: Gestió d'Infraestructures a Escala

4. Puppet: Gestió d'Infraestructures a Escala

Filosofia i Visió

Puppet va ser creat el 2005 per Luke Kanies amb una visió diferent a la d'Ansible. Mentre Ansible se centra en la simplicitat i l'accessibilitat, Puppet va ser dissenyat des del principi per a entorns empresarials massius, on podries estar gestionant desenes de milers de nodes amb configuracions extremadament complexes.

La filosofia de Puppet gira al voltant del model declaratiu pur. En lloc de dir-li a Puppet què fer pas per pas, li dius com vols que estiguin les coses, i Puppet s'encarrega de trobar la manera d'arribar-hi. Aquesta aproximació és extremadament poderosa en entorns complexos on les dependencies entre components poden ser intrincades.

Puppet també posa un èmfasi especial en la conformitat (compliance) i l'auditoria. És comú en grans empreses o organitzacions governamentals que necessitin demostrar que els seus sistemes compleixen amb estàndards específics com PCI-DSS, HIPAA, o SOC 2. Puppet facilita mantenir i reportar aquesta conformitat.

Arquitectura de Puppet

Puppet utilitza una arquitectura client-servidor (també anomenada agent-master o més recentment agent-server). Aquesta és una diferència fonamental amb Ansible. En l'arquitectura de Puppet, tens un "Puppet Server" central que emmagatzema totes les configuracions, i "Puppet Agents" instal·lats en cada node gestionat.

El flux de treball típic és així: cada 30 minuts (per defecte, configurable), cada agent Puppet es connecta al Puppet Server, descarrega el seu catàleg (la configuració compilada específica per a aquest node), aplica els canvis necessaris per convergir cap a l'estat desitjat, i envia un informe detallat dels canvis realitzats al servidor. Aquest model "pull" contrasta amb el model "push" d'Ansible.

Hi ha avantatges i desavantatges en aquesta aproximació. Els avantatges inclouen que els nodes es mantenen constantment en l'estat desitjat sense intervenció manual, el servidor pot monitoritzar l'estat de tots els nodes en temps real, i és més fàcil escalar a milers de nodes perquè no necessites mantenir una connexió oberta des d'un control node. Els desavantatges són la complexitat addicional d'haver d'instal·lar i mantenir els agents, i la necessitat de mantenir un Puppet Server altament disponible.

Nota de versió: A l'hora d'escriure aquest document (gener 2025), la versió més recent de Puppet és Puppet 8.x. Puppet 7.x encara està en suport però es recomana utilitzar Puppet 8 per a noves instal·lacions. La documentació oficial es pot trobar a https://www.puppet.com/docs/puppet/8/puppet_index.html

Components Clau de Puppet

Per entendre com funciona Puppet, necessitem conèixer els seus components principals.

Puppet Server: És el component central que emmagatzema les configuracions (manifests), compila catàlegs per als nodes, i recull informes. El Puppet Server està escrit en Clojure i s'executa sobre la JVM (Java Virtual Machine), la qual cosa el fa robust però també requereix recursos significatius.

Puppet Agent: És el servei que s'executa en cada node gestionat. L'agent s'encarrega de comunicar-se amb el servidor, aplicar els canvis, i enviar informes. L'agent també recopila "facts" sobre el node (sistema operatiu, maquinari, xarxa, etc.) que es poden utilitzar en les configuracions.

Manifests: Els manifests són fitxers amb extensió .pp (Puppet Program) escrits en el llenguatge DSL (Domain-Specific Language) de Puppet. Aquests fitxers descriuen l'estat desitjat dels recursos del sistema. Un manifest defineix recursos com paquets, serveis, fitxers, usuaris, etc.

Un manifest simple podria ser:

# Assegurar que nginx està instal·lat i executant-se
package { 'nginx':
  ensure => installed,
}

service { 'nginx':
  ensure  => running,
  enable  => true,
  require => Package['nginx'],
}

file { '/var/www/html/index.html':
  ensure  => file,
  content => '<h1>Benvinguts al meu servidor web</h1>',
  owner   => 'www-data',
  group   => 'www-data',
  mode    => '0644',
  require => Package['nginx'],
}

Catàleg: El catàleg és una compilació del manifest específic per a un node particular. Quan un agent contacta el servidor, el servidor compila tots els manifests aplicables a aquest node (tenint en compte el seu sistema operatiu, ubicació, rol, etc.) en un catàleg, que és essencialment una llista de recursos i el seu estat desitjat. L'agent després aplica aquest catàleg.

Mòduls: Similar a Ansible, Puppet organitza el codi en mòduls. Un mòdul de Puppet és un directori amb una estructura específica que conté manifests, fitxers, plantilles, i altres recursos relacionats amb una funcionalitat particular. Per exemple, podries tenir un mòdul "apache" que conté tot el necessari per configurar Apache.

La comunitat de Puppet ha creat milers de mòduls disponibles a Puppet Forge (https://forge.puppet.com/), l'equivalent al Ansible Galaxy. Aquests mòduls estan molt polits i testejats, sent un dels punts forts de Puppet.

Hiera: Hiera és el sistema de gestió de dades de Puppet. Permet separar les dades (com noms de domini, credencials, versions de software) de la lògica del codi. Això fa que sigui fàcil reutilitzar el mateix codi en diferents entorns simplement canviant les dades. Hiera utilitza una jerarquia per decidir quines dades aplicar a cada node.

Facter: Facter és l'eina que recopila informació sobre cada node (facts). Aquesta informació inclou coses com el sistema operatiu, la versió del kernel, la quantitat de memòria, les interfícies de xarxa, etc. Pots utilitzar aquests facts en els teus manifests per prendre decisions condicionals.

PuppetDB: És una base de dades que emmagatzema informació sobre tots els nodes gestionats, incloent els seus facts, catàlegs, i informes. PuppetDB permet fer consultes avançades sobre la teva infraestructura, com "mostra'm tots els nodes amb més de 8GB de RAM que tinguin Ubuntu 22.04 instal·lat".

Llenguatge Declaratiu de Puppet

El llenguatge de Puppet és declaratiu i està basat en recursos. Un recurs en Puppet és una unitat que volem gestionar: un paquet, un fitxer, un servei, un usuari, etc. Definim recursos especificant el tipus de recurs, un títol únic, i atributs que descriuen l'estat desitjat.

La sintaxi bàsica d'un recurs és:

tipus_recurs { 'títol':
  atribut => valor,
  altre_atribut => altre_valor,
}

Per exemple, per gestionar un usuari:

user { 'joan':
  ensure     => present,
  uid        => '1001',
  gid        => 'developers',
  shell      => '/bin/bash',
  home       => '/home/joan',
  managehome => true,
}

Aquest codi declara que volem que existeixi un usuari anomenat "joan" amb les característiques especificades. Si l'usuari no existeix, Puppet el crearà. Si existeix però els atributs són diferents, Puppet els corregirà. Si existeix i ja té tots els atributs correctes, Puppet no farà res.

Una característica poderosa de Puppet és el sistema de dependencies. Pots especificar que un recurs depèn d'un altre utilitzant els meta-paràmetres require, before, notify, i subscribe. Això permet a Puppet entendre l'ordre en què ha d'aplicar els canvis, fins i tot en configuracions molt complexes.

Exemple Pràctic de Puppet: Configuració d'un Servidor Web

Vegem com seria configurar un servidor web complet amb Puppet. Primer, crearem un mòdul anomenat "webserver". L'estructura del mòdul seria:

webserver/
├── manifests/
│   ├── init.pp
│   ├── install.pp
│   ├── config.pp
│   └── service.pp
├── templates/
│   └── nginx.conf.erb
├── files/
│   └── default_site.conf
└── metadata.json

El fitxer manifests/init.pp és el punt d'entrada del mòdul:

class webserver (
  String $server_name = 'localhost',
  Integer $port = 80,
  String $document_root = '/var/www/html',
  Boolean $enable_ssl = false,
) {
  # Declarar que aquesta classe conté altres classes
  contain webserver::install
  contain webserver::config
  contain webserver::service

  # Establir l'ordre d'execució
  Class['webserver::install']
  -> Class['webserver::config']
  ~> Class['webserver::service']
}

Aquest codi defineix una classe "webserver" amb paràmetres configurables. La notació de fletxes (-> i ~>) estableix l'ordre: primer s'executa install, després config, i finalment service. La fletxa amb onada (~>) significa que si config fa canvis, service serà notificat i es reiniciarà.

El fitxer manifests/install.pp gestiona la instal·lació dels paquets:

class webserver::install {
  # Assegurar que el sistema està actualitzat
  exec { 'apt-update':
    command => '/usr/bin/apt-get update',
    unless  => "/usr/bin/test -f /var/cache/apt/pkgcache.bin && 
                /usr/bin/find /var/cache/apt/pkgcache.bin -mmin -60 | grep -q .",
  }

  # Instal·lar nginx
  package { 'nginx':
    ensure  => installed,
    require => Exec['apt-update'],
  }

  # Instal·lar eines addicionals
  package { ['curl', 'vim', 'git']:
    ensure  => installed,
    require => Exec['apt-update'],
  }
}

El fitxer manifests/config.pp gestiona la configuració:

class webserver::config {
  # Obtenir paràmetres de la classe principal
  $server_name = $webserver::server_name
  $port = $webserver::port
  $document_root = $webserver::document_root

  # Crear directori del document root
  file { $document_root:
    ensure => directory,
    owner  => 'www-data',
    group  => 'www-data',
    mode   => '0755',
  }

  # Crear pàgina d'índex per defecte
  file { "${document_root}/index.html":
    ensure  => file,
    content => template('webserver/index.html.erb'),
    owner   => 'www-data',
    group   => 'www-data',
    mode    => '0644',
    require => File[$document_root],
  }

  # Configurar nginx utilitzant un template
  file { '/etc/nginx/sites-available/default':
    ensure  => file,
    content => template('webserver/nginx.conf.erb'),
    owner   => 'root',
    group   => 'root',
    mode    => '0644',
    require => Package['nginx'],
    notify  => Service['nginx'],
  }

  # Assegurar que el site està activat
  file { '/etc/nginx/sites-enabled/default':
    ensure  => link,
    target  => '/etc/nginx/sites-available/default',
    require => File['/etc/nginx/sites-available/default'],
    notify  => Service['nginx'],
  }
}

El fitxer manifests/service.pp gestiona el servei:

class webserver::service {
  service { 'nginx':
    ensure     => running,
    enable     => true,
    hasstatus  => true,
    hasrestart => true,
  }
}

Finalment, el template templates/nginx.conf.erb utilitza el llenguatge de plantilles ERB (Embedded Ruby):

server {
    listen <%= @port %>;
    server_name <%= @server_name %>;
    root <%= @document_root %>;

    index index.html index.htm;

    location / {
        try_files $uri $uri/ =404;
    }

    <% if @enable_ssl -%>
    listen 443 ssl;
    ssl_certificate /etc/nginx/ssl/cert.pem;
    ssl_certificate_key /etc/nginx/ssl/key.pem;
    <% end -%>
}

Per utilitzar aquest mòdul, simplement l'inclouries en el manifest d'un node:

node 'web01.example.com' {
  class { 'webserver':
    server_name   => 'www.example.com',
    port          => 80,
    document_root => '/var/www/mysite',
    enable_ssl    => false,
  }
}

Hiera: Separant Dades i Codi

Una de les característiques més potents de Puppet és Hiera, que permet separar completament les dades de la lògica. Imagina que tens diferents entorns (desenvolupament, staging, producció) i cada un necessita configuracions diferents. Amb Hiera, pots mantenir el mateix codi i només canviar les dades.

Un fitxer de configuració de Hiera (hiera.yaml) podria ser:

---
version: 5
defaults:
  datadir: data
  data_hash: yaml_data

hierarchy:
  - name: "Per node"
    path: "nodes/%{facts.networking.hostname}.yaml"

  - name: "Per entorn"
    path: "environments/%{facts.environment}.yaml"

  - name: "Per sistema operatiu"
    path: "os/%{facts.os.family}.yaml"

  - name: "Comú"
    path: "common.yaml"

Aquesta jerarquia diu a Puppet on buscar dades, en ordre. Primer busca un fitxer específic per al node, després per l'entorn, després pel sistema operatiu, i finalment en un fitxer comú. El primer lloc on trobi una dada determinada és el que s'utilitza.

Els fitxers de dades podrien ser:

data/environments/production.yaml:

---
webserver::server_name: 'www.production.com'
webserver::enable_ssl: true
webserver::port: 443

data/environments/development.yaml:

---
webserver::server_name: 'localhost'
webserver::enable_ssl: false
webserver::port: 8080

Amb Hiera, el mateix mòdul webserver funcionaria diferent segons l'entorn, sense necessitat de canviar cap codi.

Avantatges de Puppet

Puppet brilla en entorns empresarials grans. La seva arquitectura agent-server fa que escalar a desenes de milers de nodes sigui manejable. El model pull significa que els nodes es mantenen constantment en l'estat desitjat sense intervenció. El sistema de reportatge és extremadament detallat, perfecte per auditoria i compliance. La separació entre codi i dades amb Hiera és molt elegant. I la comunitat ha creat mòduls extremadament madurs i testejats per pràcticament qualsevol software comú.

Referència oficial: La documentació completa de Puppet està disponible a https://www.puppet.com/docs/. Per començar, es recomana el "Puppet Overview": https://www.puppet.com/docs/puppet/8/puppet_overview.html i el "Learning Puppet": https://www.puppet.com/docs/puppet/8/learning_puppet.html