Skip to content

Projets

Documentation Puppet pour utilisateur Ansible

Introduction

Puppet est un outil de gestion de configuration qui, comme Ansible, permet d'automatiser la configuration et la gestion des systèmes. Contrairement à Ansible qui est agentless et utilise SSH, Puppet fonctionne avec un modèle agent-serveur.

Concepts fondamentaux - Comparaison Ansible vs Puppet

Concept Ansible Puppet
Architecture Agentless (SSH) Agent-Serveur
Langage YAML (Playbooks) DSL Puppet (Manifests)
Idempotence Modules Resources
Inventaire Inventory files Node definitions
Exécution Push (à la demande) Pull (agent check régulier)

Architecture Puppet

┌─────────────────┐    ┌─────────────────┐
│   Puppet Server │◄───┤  Puppet Agent   │
│   (Puppet Master)│    │   (Node)        │
└─────────────────┘    └─────────────────┘
         │                       │
         │                       │
    ┌─────────┐              ┌─────────┐
    │  Forge  │              │ Catalog │
    │ Modules │              │ Apply   │
    └─────────┘              └─────────┘

Installation

Puppet Server (Master)

# Ubuntu/Debian
wget https://apt.puppet.com/puppet7-release-focal.deb
sudo dpkg -i puppet7-release-focal.deb
sudo apt update
sudo apt install puppetserver

# Fedora/RHEL
sudo rpm -Uvh https://yum.puppet.com/puppet7-release-el-8.noarch.rpm
sudo dnf install puppetserver

# Démarrage du service
sudo systemctl enable puppetserver
sudo systemctl start puppetserver

Puppet Agent

# Ubuntu/Debian
sudo apt install puppet-agent

# Fedora/RHEL
sudo dnf install puppet-agent

# Configuration de l'agent
sudo systemctl enable puppet
sudo systemctl start puppet

Structure des fichiers

Équivalence avec Ansible

Ansible Puppet Description
playbook.yml manifest.pp Fichier principal de configuration
roles/ modules/ Composants réutilisables
inventory site.pp Définition des nodes
group_vars/ hiera/ Variables et données

Arborescence Puppet

/etc/puppetlabs/code/environments/production/
├── manifests/
│   └── site.pp                 # Point d'entrée (comme site.yml)
├── modules/                    # Modules personnalisés
│   └── mymodule/
│       ├── manifests/
│       ├── files/
│       ├── templates/
│       └── metadata.json
├── hieradata/                  # Données (comme group_vars)
│   ├── common.yaml
│   └── nodes/
└── Puppetfile                 # Dépendances modules

Syntaxe de base

Resources (équivalent des modules Ansible)

# Package (équivalent au module package d'Ansible)
package { 'nginx':
  ensure => installed,
}

# Service (équivalent au module service d'Ansible)
service { 'nginx':
  ensure => running,
  enable => true,
  require => Package['nginx'],
}

# File (équivalent au module copy/template d'Ansible)
file { '/etc/nginx/nginx.conf':
  ensure  => file,
  content => template('nginx/nginx.conf.erb'),
  notify  => Service['nginx'],
}

Comparaison syntaxe

Ansible Playbook:

- name: Install and configure nginx
  hosts: webservers
  tasks:
    - name: Install nginx
      package:
        name: nginx
        state: present

    - name: Start nginx service
      service:
        name: nginx
        state: started
        enabled: yes

Puppet Manifest:

node 'webserver.example.com' {
  package { 'nginx':
    ensure => installed,
  }

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

Variables et Hiera (équivalent group_vars)

Fichier hiera.yaml

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

hierarchy:
  - name: "Per-node data"
    path: "nodes/%{::trusted.certname}.yaml"
  - name: "Per-OS data"
    path: "os/%{facts.os.family}.yaml"
  - name: "Common data"
    path: "common.yaml"

Utilisation dans les manifests

# Récupération de données Hiera
$web_port = lookup('web_port', Integer, 'first', 80)
$db_password = lookup('mysql::root_password')

class { 'apache':
  port => $web_port,
}

Modules et Classes

Structure d'un module

# modules/apache/manifests/init.pp
class apache (
  String $package_name = 'apache2',
  String $service_name = 'apache2',
  Integer $port = 80,
) {
  package { $package_name:
    ensure => installed,
  }

  service { $service_name:
    ensure => running,
    enable => true,
    require => Package[$package_name],
  }

  file { '/etc/apache2/ports.conf':
    content => template('apache/ports.conf.erb'),
    notify  => Service[$service_name],
  }
}

Utilisation du module

# manifests/site.pp
node 'webserver.example.com' {
  class { 'apache':
    port => 8080,
  }
}

Templates (équivalent Jinja2)

Template ERB

# modules/apache/templates/ports.conf.erb
Listen <%= @port %>

<% if @ssl_enabled -%>
Listen 443 ssl
<% end -%>

ServerName <%= scope['::fqdn'] %>

Conditionnels et boucles

Conditionnels

# Conditionnel basé sur les facts
case $facts['os']['family'] {
  'RedHat': {
    $package_name = 'httpd'
    $service_name = 'httpd'
  }
  'Debian': {
    $package_name = 'apache2'
    $service_name = 'apache2'
  }
  default: {
    fail("OS ${facts['os']['family']} not supported")
  }
}

Boucles (équivalent with_items)

# Création de plusieurs utilisateurs
$users = ['alice', 'bob', 'charlie']

$users.each |String $username| {
  user { $username:
    ensure => present,
    home   => "/home/${username}",
    shell  => '/bin/bash',
  }
}

Facts (équivalent gather_facts)

# Utilisation des facts système
notify { "Operating System: ${facts['os']['name']}" }
notify { "IP Address: ${facts['networking']['ip']}" }
notify { "Memory: ${facts['memory']['system']['total']}" }

# Facts personnalisés
if $facts['custom_role'] == 'webserver' {
  include apache
}

Gestion des erreurs et dépendances

Relations entre resources

# Ordering avec require/before
package { 'mysql-server':
  ensure => installed,
}

service { 'mysql':
  ensure  => running,
  require => Package['mysql-server'],  # Après l'installation
}

file { '/etc/mysql/my.cnf':
  content => template('mysql/my.cnf.erb'),
  notify  => Service['mysql'],         # Redémarre le service si changement
}

# Chaînage avec ->
Package['mysql-server'] -> File['/etc/mysql/my.cnf'] -> Service['mysql']

Commandes utiles

# Test de syntaxe (équivalent ansible-playbook --syntax-check)
puppet parser validate manifest.pp

# Simulation (équivalent --check mode)
puppet agent --test --noop

# Application manuelle
puppet agent --test

# Voir les facts
puppet facts

# Compilation d'un catalogue
puppet catalog compile nodename

# Validation d'un module
puppet module validate /path/to/module

Exemple complet : Stack LAMP

Site.pp

# manifests/site.pp
node 'lampserver.example.com' {
  include lamp_stack
}

node default {
  notify { 'No configuration defined for this node': }
}

Module LAMP

# modules/lamp_stack/manifests/init.pp
class lamp_stack {
  include lamp_stack::apache
  include lamp_stack::mysql
  include lamp_stack::php

  Class['lamp_stack::mysql'] -> Class['lamp_stack::php'] -> Class['lamp_stack::apache']
}

# modules/lamp_stack/manifests/apache.pp
class lamp_stack::apache {
  $web_port = lookup('lamp_stack::web_port', Integer, 'first', 80)

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

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

  file { '/etc/apache2/ports.conf':
    content => template('lamp_stack/ports.conf.erb'),
    notify  => Service['apache2'],
  }
}

# modules/lamp_stack/manifests/mysql.pp
class lamp_stack::mysql {
  $root_password = lookup('lamp_stack::mysql_root_password')

  package { 'mysql-server':
    ensure => installed,
  }

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

# modules/lamp_stack/manifests/php.pp
class lamp_stack::php {
  $php_packages = ['php', 'php-mysql', 'libapache2-mod-php']

  package { $php_packages:
    ensure => installed,
  }
}

Données Hiera

# hieradata/common.yaml
lamp_stack::web_port: 80
lamp_stack::mysql_root_password: 'SecurePassword123!'

Différences clés à retenir

  1. Philosophie : Puppet est déclaratif (état désiré) vs Ansible procédural
  2. Exécution : Puppet pull vs Ansible push
  3. Agent : Puppet nécessite un agent vs Ansible agentless
  4. Langage : DSL Puppet vs YAML Ansible
  5. Gestion d'état : Puppet maintient l'état en continu vs Ansible à la demande

Cette documentation devrait vous donner une base solide pour commencer avec Puppet en vous appuyant sur votre expérience Ansible. Avez-vous des questions spécifiques sur certains aspects ?