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.
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.
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:
Zudem ergeben sich bei einer solchen Aufzählung auch „Konfigurationen“, die für alle oder zumindest mehrere Systeme gelten:
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.
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.
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:
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:
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.
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).
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 }
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 # ... }
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 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 |