Quando il puppet client si collega al PuppetMaster, quest'ultimo verifica se l'hostname del client esiste fra i nodi che lui conosce e compila e restituisce un catalog con l'elenco delle risorse che sono gestite sul client e che il client stesso provvederà ad applicare.
L'elenco dei nodi gestiti dal PuppetMaster può essere definita in manifest Puppet (file di testo, scritto nel linguaggio Puppet) o su un sistema esterno ("External node classifier" , ad esempio un database ldap) a cui il PuppetMaster può essere configurato per collegarsi. Quando si usano frontend web come Puppet Dashboard o The Foreman questi POSSONO essere usati come external node classifier.
Se invece si definiscono i nodi in un manifest Puppet, questi avranno una sintassi tipo:
node 'fileserver.example42.com' {
# Risorse Puppet, dichiarazione di variabili o inclusione di classi
}
In entrambi i casi per un nodo si possono definire delle variabili custom e includere classi o definire direttemente risorse ("resource types", come package, file, service, user ecc).
Normalmente nella definizione di un nodo si includono direttamente delle classi, che tipicamente sono di due tipi:
- Classi legate ad un modulo, e quindi ad una applicazione specifica, in cui sono definite le risorse da gestire relative a quella applicazione (es: include apache)
- Classi "di raggruppamento" che a loro volta includono altre classi legate a specifiche applicazioni. Queste ultime, ad esempio possono essere classi generali che hanno gli elementi comuni a tutti i nodi della propria infrastruttura e/o classi legate a ruoli specifici (ad esempio server web, mail server ecc.)
Vediamo un esempio reale di un nodo che include due classi definite nei rispettivi moduli (cacti e mysql) e una classe generale:
node 'cacti.example42.com' inherits basenode {
$cacti_mysqluser = "cactiuser"
$cacti_mysqlpassword = "example42"
$cacti_mysqlhost = "localhost"
$cacti_mysqldbname = "cacti"
$mysql_passwd = "example42"
include general
include cacti
include mysql
}
Notare che la gestione delle variabili in Puppet e in particolare il modo con cui vengono valorizzate non segue le stess logiche di un linguaggio procedurale e può creare confusione e problemi di "varible scoping". Una regola di massima è quella di definire PRIMA le variabili e poi includere le classi che le usano. Se si definisce una struttura basata sulla nodes inheritance come nell'esempio di sopra (un nodo reale che eredita un basenode) è bene definire ed eventualmente ridefinire le variabili a difersi livello dell'albero di ereditarietà, ma includere le classi solo alla fine, nella definizione del nodo fisico.
In riferimento all'esempio di cui sopra, la classe generale potrà essere qualcosa tipo:
class general {
include puppet
include hosts
include users
include rsyslog
include ntp
include munin
include nrpe
[...]
}
Una classe che invece contiene effettivamente risorse gestite da Puppet può essere qualcosa di simile:
class mysql {
package { "mysql":
name => $operatingsystem ? {
ubuntu => "mysql-client",
debian => "mysql-client",
default => "mysql",
},
ensure => present,
}
package { "mysql-server":
name => "mysql-server",
ensure => present,
}
service { "mysql":
name => $operatingsystem ? {
redhat => "mysqld",
centos => "mysqld",
default => "mysql",
},
ensure => running,
enable => true,
require => Package["mysql-server"],
subscribe => File["my.cnf"],
}
file { "my.cnf":
mode => 644, owner => root, group => root,
require => Package["mysql-server"],
ensure => present,
path => $operatingsystem ?{
default => "/etc/my.cnf",
},
}
}
Questo esempio è una classe chiamata mysql che gestisce 4 risorse:
- Il pacchetto "mysql" che ha nome diverso a seconda del sistema operativo e per il quale ci si assicura che sia "presente".
- Il pacchetto "mysql-server", per il quale si definisce un nome unico.
- Il servizio "mysql", con nome diverso a seconda del sistema operativo, per il quale Puppet si assicura che sia "running", lo abilita al boot, si assicura che venga gestito DOPO che il pacchetto "mysql-server" sia installato, e si "abbona" al File "my.cnf", nel senso che viene riavviato ogni volta che questo file viene modificato.
- Il File "my.cnf", per il quale si definiscono modo, owner, group, il fatto che richiede prima l'installazione del pacchetto "mysql-server", il fatto che esista e il suo path. notare che in questo caso non viene definito COME questo file deve essere, quale contenuto deve avere.
Alcune considerazione generali sul codice Puppet sopra riportato:
"mysql", "mysql-server", "mysql", "my.cnf" sono i nomi delle risorse, e sono arbitrari (potrebbero chiamarsi Pippo) ma rappresenta il modo con cui risorse vengono referenziare (es: Package["mysql"] , il nome effettivo che queste risorse hanno sul sistema gestito sono definiti con l'argomento name => (per servizi e pacchetti) o path => (nel caso dei file). Se questi argomenti non sono definiti allora di default assumono il nome della risorsa stessa.
Notare che possono esistere due diverse risorse con lo stesso nome (es: Package["mysql"] e Service["mysql"]) ma che la stessa risorsa con lo stesso nome può essere definita una volta sola per lo stesso nodo (non si possono definire due Package["mysql"] con caratteristiche diverse nello stesso nodo).
Abbiamo quindi parlato di classi, che possono includere altre classi o direttamente delle risorse. Abbiamo accennato alle risorse che Puppet gestisce, dette anche resource Types. Queste possono essere native (Package, Service, File, User, Group, Mount, Exec ecc.) o create direttamente dall'utente. Ogni risorsa ha un nome e degli argomenti è importante usare la giusta sintassi:
package { "mysql-server": # Il nome è meglio metterlo fra doppio apice con i punti alla fine, Può anche stare su una nuova riga
name => "mysql-server", # Ogni argomento è seguito dalla => e deve avere alla fine una virgola
ensure => present, # Il valore può essere con o senza apici. In genere si mettono fra apici stringhe alfanumeriche custom
}
Notare che il valore fornito ad un parametro può essere una variabile:
name => "$mysql::params::packagename",
che fa riferimento diretto alla variabile $packagename definita nella classe apache::params o può avere valori diversi a seconda del valore di altre variabili:
name => $operatingsystem ? {
ubuntu => "mysql-client",
debian => "mysql-client",
default => "mysql",
}, # Ricordarsi la virgola dopo la graffa di chiusura
in questo caso il parametro name assume diversi valori a seconda del contenuto della variabile $operatingsystem e quindi, di fatto, si adatta al supporto per diversi OS.
Puppet è un potente strumento di system automation che permette la gestione centralizzata di una infrastruttura di sistemi Unix.