12 Januar 2016

Puppet: Rollen und Profile

Kategorien: HowTos
Tags: Puppet

Wenn es darum geht, mit Puppet eine große Anzahl Systeme in komplexen Umgebungen zu managen, führt früher oder später kein Weg daran vorbei, sich Gedanken über die Organisation der eigenen, oft gewachsenen Puppet-Codebase zu machen. Ein Entwurfsmuster hierfür ist das „Role-/Profile-Pattern“: ein Ansatz der darauf abzielt eine schnelle Orientierung zu ermöglichen und Konfigurationen leichter nachzuvollziehen. Dieses soll im Folgenden näher erläutert werden.

Rollen

In der Regel lassen sich Systeme mit einem Begriff beschreiben, der oft auch für Nicht-Techniker Rückschlüsse auf den Zweck eines Systems zulässt.

So handelt es sich bei einem Mailserver, Webserver oder Applikationsserver um eine Rolle die ein System erfüllt – all diesen Begriffen gemeinsam ist, dass kein direkter Bezug auf die verwendeten Komponenten oder gar detaillierte Konfiguration genommen werden muss, um die Funktion zu verdeutlichen.

Einen guten Hinweis auf Role-Bezeichnungen können in vielen Fällen Logische Netzwerkdiagramme liefern, und für das Verständnis ist es sicherlich hilfreich, bereits im Team bekannte Begriffe wiederzuverwenden.

Profile

Bei einem Profil geht es um die konkreten Technologie, die für eine Rolle benötigt wird, und dessen Organisationsspezifische Konfiguration. An dem Wort „Organisationsspezifisch“ erkennt man bereits, das hier kein Fokus auf der organisationsübergreifenden Wiederverwendbarkeit wie bei Komponentenmodulen gelegt werden soll.

Wie könnte beispielsweise eine Rolle Webserver für eine konkrete PHP-Applikation aussehen? Wenn man das für einen anderen Techniker beschreiben müsste, liefert das gute Hinweise auf Profile. So könnte eine Aufzählung möglicherweise lauten:

  • apache2-Webserver
  • Verschiedene PHP-Komponenten
  • Die Applikation selbst (sofern diese nicht mit einem spezialisierten Tool deployed wird)

Zudem ergeben sich bei einer solchen Aufzählung auch „Konfigurationen“, die für alle oder zumindest mehrere Systeme gelten:

  • Software-Quellen
  • Systemübergreifende Netzwerkkonfiguration (DNS- und NTP-Server)
  • SSH-Server und Einstellungen für diesen
  • Benutzer und SSH-Keys der Administratoren

Oft können solche Komponenten nicht direkt einer spezifischen Komponente zugeordnet werden oder benötigen zusätzliche individuelle Konfiguration: etwa könnten für eine unternehmenseigene Webapplikation zusätzliche Verzeichnisse benötigt werden oder eigene Health-Checker für einen Loadbalancer.

Im „Role-/Profile-Pattern“ stellen Profile die Brücke zwischen den funktionalen Rollen und einzelnen Komponentenmodulen dar. Was das genau bedeutet, wird im Nachfolgenden noch erläutert.

Komponentenmodule

Auf der nächst tieferen Ebene finden sich Puppet-Module, um einzelne Komponenten (apache, postgresql, dovecot, tomcat, etc.) zu konfigurieren oder Unterstützung für zusätzliche Ressourcen bereitzustellen, die Puppet nicht von Haus aus unterstützt.

Da diese für viele unterschiedliche Puppet-Nutzer nützlich sein können, sind diese Module im Idealfall so generisch, dass sie auch von anderen Organisationen mit möglichst wenig oder keinen Anpassungen verwendet werden können. Das schließt beispielsweise in den Klassen definierte Abhängigkeiten auf andere Komponentenmodule (beispielsweise Konfiguration der Datenbank für eine Webapplikation) weitestgehend aus, weil ein anderer Nutzer vielleicht ein anderes DBMS verwenden will.

Vor allem lohnt sich der Blick auf PuppetForge, wo eine Vielzahl bereits fertiger Module angeboten werden.

Wie wird das Pattern technisch umgesetzt?

Auf Ebene des tatsächlichen Puppetcode werden sowohl Rollen und Profile als Klassen umgesetzt, die wiederum in Modulen gebündelt werden.

Dabei gelten grob die folgenden Regeln:

  • Je System gibt es eine einzige Role
  • Eine Role beinhaltet Includes auf mehrere Profiles, die den eigentlichen Servertyp definieren
  • Ein Profile greift auf verschiedene (Komponenten-)module und nach Bedarf ergänzenden Ressourcen zurück, um einen logischen Technologiestack zu definieren

Das Pattern sieht außerdem vor, die Namespace-Features der Puppet-DSL zu nutzen, um Rollen und Profile einerseits voneinander und andererseits von Komponentenmodulen abzugrenzen.

Ob dabei tatsächlich jeweils ein eigenes Module für die Rollen und eines für die Profile verwendet wird, ist vor allem eine Frage der eigenen Anforderungen:

  • Sollen Rollen und Profile unabhängig voneinander wiederverwendet werden?
  • Ist ein Prefix gewünscht, um Rollen und Profile des einen Setups von einem anderen unterscheiden zu können?

Wie Komponentenmodule am besten geschrieben werden bzw. wie man Komponentenmodule von Dritten auswählt, damit sie sich gut in das Muster einfügen, ist ein eigenes Thema, das sich für einen weiteren Artikel eignet.

Die Faustregel lautet: Komponentenmodule verwenden Ressourcen und kümmern sich nur um Aspekte, die die Komponente betreffen, für die sie geschrieben wurden.

Praxisbeispiel

Für ein Setup, bestehend aus Loadbalancern und Webservern, könnte die (stark vereinfachte) Puppet-Konfiguration wie folgt aussehen (natürlich aufgeteilt auf verschiedene Dateien gemäß Puppet Autoload Layout).

Rollen

Die Rollen erlauben einen schnellen Überblick, was auf einem Loadbalancer oder einem Webserver läuft, ohne all zu sehr in die Details zu gehen.

# role/manifests/loadbalancer.pp
class role::loadbalancer {
  include profile::base
  include profile::loadbalancer
}
 
# role/manifests/webserver.pp
class role::webserver {
  include profile::base
  include profile::webserver
  include profile::webapplication
}

Profile

In den Profilen dagegen lässt sich die tatsächliche Konfiguration ablesen, und wie man sieht, ist ein Profil nicht darauf beschränkt, eine einzelne Komponente zu konfigurieren. Man kann, muss aber nicht zwingend für Basiskomponenten wie NTP, SSH und dergleichen eigene Profile definieren. Hier empfiehlt sich einfach eine Abwägung zwischen dem Umfang, was konfiguriert werden muss und wie wahrscheinlich es für diese Konfiguration ist, dass man sie in manchen Rollen benötigt, in manchen aber nicht. Auch Abhängigkeiten zwischen den Profilen können in die Überlegungen mit einfließen.

# profile/manifests/base.pp
class profile::base {
   # Paketquellen, User etc.  
  class { '::openssh':
    permit_root_login => 'no'
  }
  class { '::ntp':
    servers => ['ntp1.credativ.lan', 'ntp2.credativ.lan']
  }
}

Im Gegenteil: Hier wird getan, was nötig ist, damit eine Funktionalität lauffähig ist – und wenn es das Setzen von sysctl-Parametern einschließt.

# profile/manifests/loadbalancer.pp
class profile::loadbalancer {
  class { '::keepalived':
    virtual_ip_address => '1.2.3.4',
    ...
  }
  sysctl::value { 'ip_forward': value => 1 }
}
 
# profile/manifests/webserver.pp
class profile::webserver {
  file { '/var/www':
    ensure => directory
  }
  class { '::apache':
    ...
  }
}
 
# profile/manifests/webapplication.pp
class profile::webapplication {
  require profile::webserver
  # Install a custom webapplication
  #  ...
}

Daten für Profile

Zuletzt bleibt noch das Problem von veränderlichen Daten, beispielsweise um andere Paketquellen in Test und Produktion oder andere NTP-Server für unterschiedliche Standorte zu verwenden.

Für diesen Zweck hat sich im Puppet-Ökosystem das Tool Hiera etabliert, das hier jedoch nicht näher erläutert werden soll. Im Bezug auf das Role-/Profile-Pattern bleibt nur die Frage, wo die Lookups idealerweise erfolgen sollen.

Wie eingangs bereits erwähnt, stellen Profile die Brücke zwischen Rollen und Komponenten dar. Daher erfolgen Hiera-Lookups idealerweise in Profilen. Was man an Daten aus Hiera bezieht und wie man die Daten idealerweise speichert, ist eine Philosophie für sich. Erfahrungsgemäß ist es aber sinnvoll möglichst nur Daten aus Hiera zu beziehen, die in mehreren Profilen benutzt werden oder die sich zwischen unterschiedlichen Hierarchieebenen unterscheiden. Ansonsten wird es schnell unübersichtlich. Weiterhin sollte vermieden werden, Werte im Hiera doppelt zu hinterlegen. So macht es beispielsweise wenig Sinn, einen Datenbankbenutzer der sowohl im Datenbank-Profil als auch im Anwendungs-Profil benötigt wird in Hiera unter zwei verschiedenen Schlüsselnamen abzulegen.

Weiterführende Informationen

Weiterführende Informationen finden sich auf den folgenden Seiten (die teilweise auch als Quelle für diesen Artikel gedient haben):

 

Dieser Artikel wurde ursprünglich geschrieben von Patrick Schönfeld.

Kategorien: HowTos
Tags: Puppet

SH

über den Autor

Sascha Heuer


Beitrag teilen: