Puppet: ein NTP-Modul mit zwei Neuerungen

Nach und nach alle Dienste mit puppet abzudecken und dabei in der Komplexitaet immer weiter steigern. Das mache ich gerade. Heute ein Modul mit zwei Neuerungen:

  1. Sicherstellen, dass der Service auch laeuft
  2. Im Template ein Array durchlaufen

Man kann mit puppet sehr einfach sicherstellen, dass Dienste auch laufen. Dafuer reichen die drei Zeilen:

  service { 'ntp':
    ensure => running,
  }

Weiter gibt es Sinn mehrere NTP Server in der ntp.conf zu definieren. Aus diesem Grund konnte ich nicht mit der gleichen Syntax arbeiten wie bei dem apt Modul. Der entscheidende neue Abschnitt im Template ist:

<% @servers.each do |server| -%>
server <%= server %>
<% end -%>

Siehe dazu auch die Sektion Iteration in der Using Puppet Templates Dokumentation. Komplett sieht das ganze so aus:

  • Ordnerstruktur:
    ├── manifests
    │   └── init.pp
    └── templates
        └── ntp.conf.erb
  • init.pp:
    # make sure ntp package is installed and service
    # is running
     
    class ntp ( $servers = [
      '0.debian.pool.ntp.org',
      '1.debian.pool.ntp.org',
      '2.debian.pool.ntp.org',
      '3.debian.pool.ntp.org',
      ] ) {
     
      package { 'ntp':
        ensure => installed
      }
     
      service { 'ntp':
        ensure => running,
      }
     
      file { '/etc/ntp.conf':
        ensure  => 'present',
        owner   => 'root',
        group   => 'root',
        mode    => '0644',
        content => template('ntp/ntp.conf.erb'),
        }
    }
  • ntp.conf.erb:
    #############################################################################
    #                                                                           #
    # !!! This file is managed by puppet, all manual changes will be lost !!!   #
    #                                                                           #
    #############################################################################
     
     
    # Keep ntpd from panicking in the event of a large clock skew
    # when a VM guest is suspended and resumed.
    tinker panic 0
     
    # Local users may interrogate the ntp server more closely.
    restrict 127.0.0.1
    restrict ::1
     
    # You do need to talk to an NTP server or two (or three).
    <% @servers.each do |server| -%>
    server <%= server %>
    <% end -%>
     
    # Undisciplined Local Clock. This is a fake driver intended for backup
    # and when no outside source of synchronized time is available. 
    server  127.127.1.0
    fudge   127.127.1.0 stratum 10
    restrict 127.127.1.0
     
    # By default, exchange time with everybody, but don't allow configuration.
    restrict -4 default kod notrap nomodify nopeer noquery
    restrict -6 default kod notrap nomodify nopeer noquery
     
    # Driftfile.
    driftfile /var/lib/ntp/drift
  • Definition des nodes:
    node 'node1.example.org' inherits default {
      class { 'ntp':
        servers => [ 'ntp1.example.org', 'ntp2.example.org', 'ntp3.example.org' ],
      }
    }

Puppet: Konfiguration in GIT Repository verwalten / push-to-deploy

Es ergibt Sinn die puppet Konfiguration in einem GIT Repository zu verwalten. So kann man immer auf alte Versionen zurueckgehen sowie automatische Pruefungen zwischenschalten bevor man Aenderungen Live schaltet. Sexy wird das ganze mit push-to-deploy. So wird es gemacht:

Vorbereitungen auf dem puppet server

mkdir -p /srv/git/puppet
cd /srv/git/puppet
git init --bare
addgroup puppet-push
chgrp -R puppet-push /srv/git/puppet
find /srv/git/puppet -type d -exec chmod 0775 {} ;
find /srv/git/puppet -type d -exec chmod +s {} ;
chgrp -R puppet-push /etc/puppet
chmod 775 /etc/puppet

Damit mein Benutzername auch in das GIT Repository schreiben darf muss dieser Mitglied in der Gruppe puppet-push sein:

adduser USERNAME puppet-push

Nun benoetigen wir zwei Hooks in dem Repository. Einen update Hook der die Syntax ueberprueft, und ein post-receive Hook, der bei Bedarf die gepushten Dateien gleich an die richtige Stelle kopiert:

cd /srv/git/puppet/hooks/
wget http://git.pregos.info/scripts.git/blob_plain/HEAD:/push-to-deploy/update
chmod +x update
http://git.pregos.info/scripts.git/blob_plain/HEAD:/push-to-deploy/post-receive
chmod +x post-receive

Anschliessend sind auf Server Seite keine weiteren Aenderungen mehr notwendig.

Vorbereitungen auf dem lokalen Rechner

Bei sich lokal auf dem Rechner muss man nun ein Verzeichnis erzeugen, die aktuellen Dateien vom puppetmaster.example.org:/etc/puppet dorthin kopieren, GIT Repository initialisieren und committen:

mkdir ~/puppet
cd ~/puppet
scp user@puppetmaster.example.org:/etc/puppet .
git init
git add .
git commit -m "initial commit"

Nun noch das remote Repository hinzufuegen und pushen:

git remote add live user@puppetmaster.example.org:/srv/git/puppet
git push live master

Wenn alles richtig ist, dann ist die Ausgabe in etwa wie folgt:

user@client:~/git/puppet$ git push live master
Counting objects: 22, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (16/16), done.
Writing objects: 100% (22/22), 4.91 KiB | 0 bytes/s, done.
Total 22 (delta 2), reused 0 (delta 0)
remote: fatal: bad object 0000000000000000000000000000000000000000
remote: 
remote: diff-tree:
remote: Already on 'master'
remote: DEPLOY: master(bd01768f198a2513ba82f5f8765b4b222908ce4b) copied to '/etc/puppet'
To user@puppetmaster.example.org:/srv/git/puppet
 * [new branch]      master -> master

Syntaxfehler fuehren dazu, dass die Aenderungen nicht uebernommen werden:

user@client:~/git/puppet$ git push live master
Counting objects: 7, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (4/4), 328 bytes | 0 bytes/s, done.
Total 4 (delta 2), reused 0 (delta 0)
remote: 
remote: diff-tree:
remote: :100644 100644 c31414e094fbbf3af3792965f2f706ede6a70d24 f47cd2be9c7b3a9d4e23b885b2252ac0579319d2 M	manifests/site.pp
remote: 
remote: err: Could not parse for environment production: Syntax error at end of file; expected '}' at manifests/site.pp:6
remote: err: Try 'puppet help parser validate' for usage
remote: For more details run this:  git diff c31414e094fbbf3af3792965f2f706ede6a70d24 f47cd2be9c7b3a9d4e23b885b2252ac0579319d2 
remote: 
remote: error: hook declined to update refs/heads/master
To user@puppetmaster.example.org:/srv/git/puppet
 ! [remote rejected] master -> master (hook declined)
error: Fehler beim Versenden einiger Referenzen nach 'user@puppetmaster.example.org:/srv/git/puppet'

Die Informationen sind zusammengetragen von:

Puppet: alte Reports automatisch aufraeumen

Puppet legt in dem Verzeichnis /var/lib/puppet/reports/ fuer jeden node fuer jede Abfrage eine .yaml Datei als Report ab. Diese werden nicht automatisch aufgeraeumt. Damit die Festplatte nicht voll laeuft ergibt es Sinn dieses automatisch zu tun. Am einfachsten geht das mit einem kleinen Skript das per Cron aufgerufen wird. In meinem Fall die folgende Datei:

  • /etc/cron.d/puppet-cleanreports
PATH=/usr/bin:/bin:/usr/sbin/
#
# Cron job to clean puppet reports older than 31 days
#
 
 
0 23  * * *     root    find /var/lib/puppet/reports/ -type f -name "*.yaml" -mtime +31 -exec rm -f {} ;

Puppet: ein Modul mit Template, Variablen und Bedingung (/etc/apt/sources.list)

Vor kurzem erst hatte ich darueber geschrieben, wie man in puppet ein eigenes Modul definiert.
Heute moechte ich zeigen, wie in einem Modul mit Templates, Variablen und if-Bedingung gearbeitet werden kann. Ich habe mir dafuer die Datei /etc/apt/sources.list ausgesucht in die ich – je nach Host – unterschiedliche Mirror eintragen moechte. Dafuer habe ich das Verzeichnis /etc/puppet/modules/apt/ mit der folgenden Ordnerstruktur angelegt:

├── manifests
│   └── init.pp
└── templates
    └── sources.list.erb

Mein Template fuer die sources.list sieht wie folgt aus:

#############################################################################
#                                                                           #
# !!! This file is managed by puppet, all manual changes will be lost !!!   #
#                                                                           #
#############################################################################
 
 
 
deb     <%= mirror %>  <%= release %>  main
<% if source -%>
deb-src <%= mirror %>  <%= release %>  main
<% end -%>
 
deb     http://security.debian.org/  <%= release %>/updates  main
<% if source  -%>
deb-src http://security.debian.org/  <%= release %>/updates  main
<% end -%>

In dem Template kann man zwei Variablen erkennen. Diese sollen bei Bedarf ueberschrieben werden koennen <%= mirror -%> und <%= release %>. Ausserdem gibt es eine Bedingung, wenn die Variable source Wahr ist, wird auch die deb-src Zeile eingefuegt.

Die apt/manifests/init.pp fuer das Modul sieht wie folgt aus:

class apt (
  $mirror = 'http://ftp.de.debian.org',
  $release = 'wheezy',
  $source = false ) {
 
  file { '/etc/apt/sources.list':
    ensure  => 'present',
    owner   => 'root',
    group   => 'root',
    mode    => '0644',
    content => template('apt/sources.list.erb'),
    }
}

Neu ist, dass in der Klassendefinition die Variablen mit entsprechenden Standardwerten definiert wurden. Das Bild wird komplett, wenn nun ein neuer Node in der sites.pp definiert wird:

node default {
  class { 'motd': }
}
 
node 'client02.example.org' inherits default {
  class { 'apt':
    mirror => 'http://debianmirror.example.org/pub/linux/debian/debian/',
    source => true
  }
}

Hier ist neu, dass der Node „client02.example.org“ explizit angegeben wurde. Er erbt die Einstellungen des default Node. Erweiternd ist die Klasse apt definiert, bei der die Variablen mirror und source einen anderen Wert annehmen als der Standartwert.

Ich bin mir dessen bewusst, dass es ein apt Modul auf puppetforge gibt, zum Lernen finde ich das hier gerade aber einfacher.

Informationen habe ich auch aus dem Blogeintrag „Puppet templates and default values for variables„.