add report

This commit is contained in:
Jörg Thalheim 2014-06-10 14:56:14 +02:00
parent 3181cd16dd
commit 2b9f5e380f
15 changed files with 3459 additions and 1469 deletions

1
.envrc
View File

@ -1,3 +1,2 @@
layout ruby layout ruby
PATH_add $(pwd)/presentation/node_modules/.bin PATH_add $(pwd)/presentation/node_modules/.bin
PATH_add $(pwd)/presentation/node_modules/.bin

View File

@ -17,7 +17,8 @@ Für Chef
Ruby-Abhängigkeiten installieren: Ruby-Abhängigkeiten installieren:
$ cd chef-lctp && bundle $ cd chef-lctp
$ bundle
Es werden ein paar andere Cookbooks verwendet, diese werden mit diesem Befehl Es werden ein paar andere Cookbooks verwendet, diese werden mit diesem Befehl
heruntergeladen heruntergeladen

6
Vagrantfile vendored
View File

@ -10,10 +10,10 @@ def load_json(name)
end end
boxes = [ boxes = [
#{ name: "puppet0.lctp", provision: :puppet, mac: "5CA1AB1E0F01"}, # uncomment to enable puppet { name: "puppet0.lctp", provision: :puppet, mac: "5CA1AB1E0F01"}, # uncomment to enable puppet
{ name: "node0.lctp", provision: :chef, role: :head_node, mac: "5CA1AB1E0001", json: load_json("node0.json") }, { name: "node0.lctp", provision: :chef, role: :head_node, mac: "5CA1AB1E0001", json: load_json("node0.json") },
{ name: "node1.lctp", provision: :chef, role: :compute_node, mac: "5CA1AB1E0002", json: load_json("node1.json") }, { name: "node1.lctp", provision: :chef, role: :compute_node, mac: "5CA1AB1E0002", json: load_json("node1.json") },
#{ name: "node2.lctp", provision: :chef, role: :compute_node, mac: "5CA1AB1E0003", json: load_json("node2.json") } { name: "node2.lctp", provision: :chef, role: :compute_node, mac: "5CA1AB1E0003", json: load_json("node2.json") }
] ]
["vbguest", "berkshelf"].each do |plugin| ["vbguest", "berkshelf"].each do |plugin|
@ -27,11 +27,11 @@ boxes = [
end end
end end
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vbguest.auto_update = true config.vbguest.auto_update = true
config.vbguest.auto_reboot = true config.vbguest.auto_reboot = true
config.vm.provision(:shell){ |shell| shell.path = "script/fix_stdin_error.sh" }
boxes.each do |box| boxes.each do |box|
config.vm.define box[:name] do |node| config.vm.define box[:name] do |node|
node.vm.provider :virtualbox do |vb| node.vm.provider :virtualbox do |vb|

File diff suppressed because it is too large Load Diff

6
report/appandix.tex Normal file
View File

@ -0,0 +1,6 @@
\appendix
\subsection{Initsystem}
\label{sec:initsysteme}
Prozess, der in einem Betriebsystem alle nachfolgenden Prozesse verwaltet und startet.

499
report/comparison.tex Normal file
View File

@ -0,0 +1,499 @@
\subsection{Funktionsweise von Chef}
\label{ssub:funktionsweise_von_chef}
\href{http://www.getchef.com/chef/}{Chef} ist ein Framework, welches eine
automatisierte Serverkonfiguration und -verwaltung ermöglicht. Chef übernimmt
dabei Aufgaben der Provisionierung (Installation der grundlegenden Dienste,
Ressourcenverwaltung, Einrichtung und Konfiguration von Middleware) bis hin zum
Deployment (Verteilung der eigentlichen Business-Anwendung).
Der Endanwender beschreibt hierbei die Systemressourcen und ihre Zustände in der
Programmiersprache \href{https://www.ruby-lang.org/}{Ruby}. Diese Definitionen
werden von dem Programm \texttt{Chef-Client} eingelesen und in notwendige Aktionen
übersetzt, welche ausgeführt werden müssen, um den beschriebenen Zustand
umzusetzen.
Die Gesamtheit aller Definitionen/Einstellungen nennt man das \texttt{Chef-Repo}.
Ein solches untergliedert sich in mehrere \texttt{Cookbooks}\label{cookbook}. Ein
Cookbook ist die Grundverwaltungseinheit in Chef. Es erfüllt einen bestimmten
Teilaspekt des Systems (z.B. die Einrichtung eines Webservers
\href{https://github.com/opscode-cookbooks/apache2}{Apache}). Cookbooks können
versioniert werden. Es können Abhängigkeiten zwischen mehreren Cookbooks
angegeben werden.
Physikalische oder virtuelle Maschinen werden als \texttt{Nodes} bezeichnet.
Der Node werden \texttt{Attribute}, \texttt{Rollen} und Cookbooks zugewiesen.
Rollen und Cookbooks werden dazu in die sogenannte \texttt{Run-List} eingefügt.
Diese gibt die Reihenfolge an, in welcher Rollen und Cookbooks angewendet
werden. Rollen bieten eine Möglichkeit, Nodes zu gruppieren, welche die gleichen
Aufgaben in einer Organisation erfüllen (z.B. Webserver).
Es gibt mehrere Möglichkeiten \texttt{Chef} zu betreiben:
\begin{description}
\item[Chef-Solo] Chef-Solo ist die einfachste Ausführungsform. Alle nötigen
Daten werden aus einem lokalen Verzeichnis geladen. Im Gegensatz zu
\texttt{Chef-Server} und \texttt{Enterprise-Chef} wird bei Chef-Solo das
Programm \texttt{chef-solo} an Stelle von \texttt{chef-client} ausgeführt. Diese
Form wurde für die Umsetzung der Aufgabenstellung in
Abschnitt~\ref{sub:einrichtung-der-netzwerkdienste} gewählt.
\item[Chef-Server] Hierbei authentifiziert sich \texttt{Chef-Client} über eine
\texttt{REST-Api} zu einem \texttt{Chef-Server} mittels eines privaten
RSA-Keys. Der Server verwaltet zentral das Chef-Repo. Der Chef-Client
bekommt von diesem alle nötigen Informationen für die zu provisionierende
\texttt{Node}. Chef-Server bietet eine webbasierte GUI für die
Administration an. Die Attribute aller Nodes sind über die eingebaute
Suchmaschine \href{https://lucene.apache.org/solr/}{\texttt{Solr}}
durchsuchbar.
\item[Enterprise-Chef/Hosted-Enterprise-Chef] Enterprise-Chef bietet
zusätzlich zu den Funktionen der Opensource-Version Chef-Server eine
rollenbasierte Benutzerverwaltung, bessere Überwachung, eine verbesserte
Weboberfläche sowie Push-Deployment an. Während bei Hosted-Enterprise-Chef
die Firma Chef die Infrastruktur des Chef-Server betreibt und die Skalierung
des Dienstes übernimmt, befindet sich im Falle von Enterprise-Chef der
Server in der eigenen Organisation~\cite{chefenterprise}.
\end{description}
\subsubsection{Aufbau eines Cookbooks}
\label{aufbau_eines_cookbook}
\begin{figure}[h]
\centering
\caption{Ordnerstruktur eines Cookbooks am Beispiel des \href{https://github.com/opscode-cookbooks/apt}{apt}-Cookbooks dargestellt.}
\begin{tikzpicture}
\treeroot{apt-2.3.4}
\altentry{attributes}{1}
\altentry{default.rb}{2}
\altentry{files}{1}
\altentry{default}{2}
\altentry{apt-proxy-v2.conf}{3}
\altentry{libraries}{1}
\altentry{helpers.rb}{2}
\altentry{network.rb}{2}
\altentry{providers}{1}
\altentry{preference.rb}{2}
\altentry{repository.rb}{2}
\altentry{recipes}{1}
\altentry{cacher-client.rb}{2}
\altentry{cacher-ng.rb}{2}
\altentry{default.rb}{2}
\altentry{resources}{1}
\altentry{preference.rb}{2}
\altentry{repository.rb}{2}
\altentry{templates}{1}
\altentry{debian-6.0}{2}
\altentry{default}{2}
\altentry{01proxy.erb}{3}
\altentry{acng.conf.erb}{3}
\altentry{ubuntu-10.04}{2}
\altentry{acng.conf.erb}{3}
\altentry{CHANGELOG}{1}
\altentry{metadata.json}{1}
\altentry{metadata.rb}{1}
\altentry{README.md}{1}
\end{tikzpicture}
\end{figure}
Die Verzeichnisnamen und die Datei \texttt{metadata.rb} sind fest vorgeben.
Jedes Verzeichnis hat seine eigene Funktion. Dies hat den Vorteil, das man sich
schnell in neuen Cookbooks zurecht findet.
\begin{description}
\item[attributes] Attribute sind einfache Schlüssel-Wert-Beziehungen und
setzen Standardwerte für das Cookbook. Die Schlüssel sind hierarchisch
organisiert. In der Regel ist die höchste Ebene der Name des Cookbooks (z.B.
\texttt{normal.mysql.client.packages}). Werte können Strings, Zahlen oder
Arrays sein. Die gesetzten Attribute können in Rollen, Nodes oder von
anderen Cookbooks überschrieben werden. Hierfür werden die Attribute mit den
verschiedenen Prioritäten \texttt{default}, \texttt{force\_default},
\texttt{normal} und \texttt{override} (aufsteigender Wertigkeit) gesetzt.
Attribute mit einer höheren Priorität überschreiben den Wert von Attributen
mit einer niedrigeren Priorität.
\item[files] In diesem Verzeichnis können statische Dateien eingefügt werden,
welche auf dem Zielsystem in das entsprechende Verzeichnis kopiert werden
können.
\item[libraries] In diesem Pfad können Hilfsfunktionen und
Spracherweiterungen definiert werden.
\item[resources] Ressourcen beschreiben die Bestandteile eines Systems. Eine
Ressource kann z.B. eine Datei, ein Prozess oder ein Paket sein. Man
beschreibt, welchen Zustand (Action in Chef genannt) diese Ressource haben
soll und Chef versucht, diesen Zustand herzustellen. Chef liefert bereits
viele wichtige Ressourcen mit. In Cookbooks können darüber hinaus eigene
Ressourcen definiert werden.
\item[providers] Während Ressourcen nur die Schnittstelle mit allen Attribute
beschreiben, die gesetzt werden können, ist der Provider eine konkrete
Implementierung. Deswegen muss für jede Ressource mindestens ein Provider
existieren. Es kann mehrere Provider für eine Ressource geben, um zum
Beispiel mehrere Plattformenvarianten oder Betriebssysteme abdecken zu
können (z.B. bei Paketmanagern oder Initsystemen - \ref{sec:initsysteme}). In
eigenen Cookbooks erstellte Ressourcen/Provider nennt man LWRP
(englische Abkürzung für \texttt{Lightweight Resources and Providers}).
\item[recipes] In Recipes werden Ressourcen instantiiert, welche nötig sind,
um das gewünschte Ziel zu erreichen. Dabei können Abhängigkeiten zwischen
Recipes angegeben werden.
\item[definitions] Ressourcen, welche häufiger in verschiedenen Recipes in
ähnlicher Form benötigt werden, können in eine \texttt{Definition}
ausgelagert werden. Ein Beispiel ist das Generieren eines SSH-Schlüssels
für verschiedene Nutzer auf dem System. Für komplexere Konstrukte sollten
jedoch LWRPs (siehe oben) bevorzugt werden.
\item[templates] Häufig werden dynamisch generierte Dateien benötigt, um zum
Beispiel Konfigurationsdateien zu erzeugen. In Chef wird für diesen Zweck
die Templatesprache eRuby (Embedded Ruby) verwendet. In ERB-Templates wird
Rubyquellcode ausgeführt, der sich zwischen den Tags \texttt{<\%} und \texttt{\%>}
befindet. Dies erlaubt es einerseits den Inhalt von Variablen oder den
Rückgabewert von Methoden einzufügen, andererseits können in Templates
Kontrollstrukturen wie If-Statements und Schleifen verwendet werden.
\item[metadata.rb] In der Datei \texttt{metadata.rb} kann der Name des Cookbook,
die eigene Version, eine Beschreibung sowie Abhängigkeiten zu anderen
Cookbooks angegeben werden.
\end{description}
\begin{lstlisting}[caption={Beispiel ERB-Template:},label={lst:erb-templates}]
Diese Zeile wird beim Rendern ohne Aenderung uebernommen
<%# Ein Kommentar%>
Diese Node heisst: <%= @node.name %>
<% if node[:platform] == "ubuntu" -%> <%# Bedingte Anweisung %>
Diese Zeile erscheint auf Ubuntu-basierten Nodes.
<% else %>
Diese Zeile erscheint auf nicht Ubuntu-basierten Nodes.
<% end -%>
<%# Listet in einer Schleife alle Blockdevices des Node auf %>
<% @node.block_device.each do |block_device, attributes| %>
<%= block_device %>: <%= attributes.join(", ") %>
<% end %>
\end{lstlisting}
\subsubsection{Ablauf einer Provisionierung}
\label{ablauf_einer_provisionierung}
Der genaue Ablauf wurde der Onlinedokumentation (\cite{chefrun}) von Chef
entnommen. Wie schon zu Beginn erwähnt, wird die Provisionierung von einem
Programm namens \texttt{Chef-Client} durchgeführt.
Je nach gewählter Umgebung kann dieser unterschiedlich gestartet werden:
\begin{itemize}
\item periodisch vom Scheduler \texttt{Cron}
\item permanent als Systemdienst (z.B. bei Enterprise Chef)
\item manuell (z.B. bei Vagrant - siehe~\ref{sub:einrichtung-der-netzwerkdienste})
\end{itemize}
Als erstes lädt dieser Prozess seine Konfiguration aus der Datei
\texttt{client.rb}. In dieser stehen beispielsweise die URL des Chef-Server und
der Name des Node. Letzteres ist wichtig, um die Node in Chef einordnen zu
können und die richtigen Einstellungen zuzuweisen. Alternativ kann der Name auch
von der Bibliothek \href{http://docs.opscode.com/ohai.html}{Ohai} gesetzt
werden, in dem auf den Hostnamen oder der FQDN (Fully Qualified Domain Name)
zurückgegriffen wird. Ohai sammelt systemrelevante Daten wie Details über
Hardwarekomponenten (Anzahl der CPUs, Größe und Art des RAMs, Netzwerkanbindung,
Festplatten/SSDs, \dots), Informationen über die Plattform (Art des
Betriebssystems und sowie dessen Version, installierte Anwendungssoftware) und
die laufenden Prozesse. Diese Informationen sind durch eigene Ohai-Plugins
erweiterbar und können im Provisionierungsprozess genutzt werden, um weitere
Entscheidungen zu treffen. Sie sind darüber hinaus auch auf dem Server
gespeichert und für andere Clients abrufbar.
Nach dem alle Einstellungen eingelesen sind, verbindet sich der Chef-Client mit
dem Chef-Server. Die Authentifizierung erfolgt über dem vorher auf dem Node
abgelegten RSA-Schlüssel. Für Administratoren gibt es einen Validator-Key. Mit
diesem kann ein Node auf dem Server registriert werden und so ein
Client-Schlüssel generiert werden.
Anschließend werden zuvor gesetzte Attribute und die Run-List vom Server
übertragen. Im 1. Durchlauf oder bei Verwendung von Chef-Solo sind diese Daten
nicht vorhanden. Stattdessen kann eine Datei im JSON-Format angegeben werden,
um die Attribute und die Run-List für die Node zu spezifizieren. Außerdem ist
es möglich eine Run-List auf dem Chef-Server einzustellen, welche ausgeführt
wird, wenn die Node keine eigene Run-List besitzt.
Durch Auswertung der eingebunden Rollen und Recipes werden die benötigen
Cookbooks ermittelt. Der Client fordert eine Liste aller darin enthaltenen
Dateien und deren Checksumme an. Alle geänderten oder neuen Dateien werden
heruntergeladen und lokal gespeichert.
Nun werden die Attribute zurückgesetzt und aus den Cookbooks, Rollen und dem
Node neu generiert und entsprechend ihrer Priorität gesetzt. Die Ressourcen aus
den Cookbooks werden geladen und in der Ressource-Collection zusammengefasst.
Nachdem alle Definitionen und Bibliotheken geladen wurden, werden schließlich die
Recipes verarbeitet. Die darin erstellten Ressourcen beschreiben das System. Für
jede Ressource wird der Zustand festgelegt.
Im nächsten Schritt folgt das sogenannte \texttt{Converging} (englisch für
Angleichen). Es werden alle Ressourcen Schritt für Schritt abgearbeitet. Dabei
wird für jede Ressource der für die Plattform zugehörige Provider ausgewählt.
Dieser überprüft den aktuellen Zustand der Ressource und verändert falls
notwendig das System, um den Sollzustand zu erreichen. Zum Schluss überträgt
Chef-Client die aktualisierten Attribute auf den Server, von welchem sie in
\texttt{Solr} indexiert werden.
Es besteht die Möglichkeit, Handler vor oder nach der Provisionierung auszuführen.
Diese können im Fehlerfall Benachrichtigungen an das Monitoringssystem oder per
Email verschicken. In letzten Abschnitt (\ref{minitest_handler}) wird dieser
Mechanismus genutzt, um Tests auszuführen.
\subsubsection{Vergleich mit puppet}
\label{vergleich_mit_puppet}
\paragraph{Historischer Kontext}
Ein ebenfalls weit verbreitetes Konfigurationsmanagementsystem ist Puppet. Es ist
das Ältere der beiden Projekte. Während das erste Puppet-Release bereits im
Jahre 2005 von den Puppet Labs veröffentlicht wurde, erschien Chef erst 4 Jahre später
im Jahr 2009. Chef wurde stark von Puppet beeinflusst. Der Erfinder von Chef,
Adam Jacob, war selbst langjähriger Puppetnutzer, bevor er Chef schrieb. Seine
damalige Firma betreute als Unternehmensberater mehrere Firmen bei der
Provisionierung der Infrastruktur bis hin zum Deployment der Anwendung. Dabei
kam Puppet zum Einsatz. Mit steigender Anzahl der Kunden, wuchs nach Aussagen
von Adam Jacob der Aufwand bei der Verwaltung der Puppet-Konfiguration. Diese
mussten häufig für jeden Kunden stark angepasst oder neu geschrieben werden. Aus
diesem Grund begann er an ein neues Deploymentsystem zu schreiben. Damals trug
es noch den Namen \texttt{Marionette}. Dabei verwendete er, wie schon bei
Puppet, die Programmiersprache Ruby zur Implementierung des Clients. Ein
wichtiges Designziel seines neuen System war es, bessere
Abstraktionsmöglichkeiten zu schaffen, um damit die Wiederverwendbarkeit zu
erhöhen (Quelle: \cite{chefhistory}). Anzumerken ist, dass seit der damals
veröffentlichten Puppetversion
(\href{https://github.com/puppetlabs/puppet/commit/ce964ecb6d6a38cb7fb9f0b13a7e6b2eb4c381c3}{0.24.5})
neue Funktionen und Spracherweiterungen zu Puppet hinzugefügt wurden, um dieses
Problem zu adressieren. (\cite{puppetlanguagechangelog})
\paragraph{Sprache}
Während bei Chef die Konfiguration in Ruby geschrieben wird, besitzt Puppet eine
eigene Konfigurationssprache. Puppet's Sprache ist im Gegensatz zu allgemeinen
verwendeten Sprachen (engl. General-Purpose-Languages, kurz GPL) wie Ruby, Java
oder C/C++ eine domänspezifische Sprache (engl. Domain-Specific-Language - DSL).
Eine DSL ist eine speziell für den Anwendungszweck geschriebene und optimierte
Sprache. Sie enthält häufig Elemente und Ausdrücke, welche es erlauben, Probleme
der Anwendungsdomäne effizient zu lösen. Es wird häufig auf umfangreiche
Standardbibliotheken und Sprachkonstrukte verzichtet, die in GPLs üblich sind.
Puppet's Sprache wurde an das Konfigurationsformat der Überwachungssoftware
Nagios angelehnt (\cite{puppetlanguage}). Sie ist deklarativ gehalten und soll
möglichst einfach erlernbar sein (auch für Administratoren ohne programmiertechnischen
Hintergrund). Der Schwerpunkt liegt auf der Beschreibung von
\texttt{Ressourcen}. Die Sprache besitzt Kontrollstrukturen wie Case- und
If-Statements. Es gibt Datentypen wie \texttt{Strings}, \texttt{Booleans},
\texttt{Arrays}, \texttt{Reguläre Ausdrücke} und \texttt{Hashes}. Diese können
in Variablen gespeichert werden. Die
\href{https://forge.puppetlabs.com/puppetlabs/stdlib}{Standardbibliothek} von
Puppet stellt Funktionen bereit, um auf diesen Datentypen einfache Operationen
auszuführen. Allerdings ist es nicht möglich, Schleifen auszuführen. (Diese
\href{http://docs.puppetlabs.com/puppet/latest/reference/experiments_lambdas.html}{Funktion}
ist momentan als \texttt{experimentell} markiert). Funktionen können nicht
direkt in Puppet's Sprache definiert werden. Stattdessen werden diese als
Erweiterung des Parsers in Ruby implementiert, was wiederum den Nachteil hat,
dass dafür eine weitere Sprachen erlernt werden muss. Manche Unternehmen und
Organisationen greifen bevorzugt auf Puppet zurück, weil es einfacher ist, neue
Mitarbeiter ohne Rubykenntnisse in diesem Framework zu schulen. Andere wiederum
bevorzugen die Flexibilität von Ruby. Facebook gab dies als einen der Grund an
für einen Umstieg im Jahre 2013 von \texttt{CFEngine2} auf \texttt{Chef 11}
\cite{facebooklikeschef}.
\paragraph{Communities}
Das strukturelle Gegenstück zu \texttt{Cookbooks} in Chef ist das
\texttt{Modul} in Puppet. Diese werden in der Nutzergemeinschaft entwickelt. Da
Puppet älter ist, ist anzunehmen, dass hierfür mehr Module zur Verfügung
stehen, als Cookbooks für Chef. Die primäre Distributionsquelle ist
\href{https://forge.puppetlabs.com/}{Puppet-Forge}, in dem \textbf{2206}
\href{https://forge.puppetlabs.com/modules?supported=yes}{Modul} zur Verfügung
stehen (Stand: 31.03.2014). Für Chef gibt es eine ähnliche
\href{http://community.opscode.com/}{Community-Seite} mit \textbf{1368} Modulen,
(Stand: 31.03.2014 - ermittelt über die
\href{https://cookbooks.opscode.com/api/v1/cookbooks?start}{API}). Zu einer
weiteren wichtigen Quelle hat sich die Plattform
\href{https://github.com}{Github} für beide Projekte entwickelt. Für einen
Vergleich wurde die Anzahl der Suchtreffer für Projekte, die die Begriffe
``Chef'' und ``Puppet'' in der Suchmaschine auf Github herangezogen. Github
filtert Forks (Abspaltungen) von Projekten aus den Suchergebnissen heraus und
schlüsselt die Ergebnisse nach Programmiersprache auf. Es wurden alle Sprachen
in beiden Projekte mit weniger als 100 Suchtreffer aus Übersichtlichkeitsgründen
nicht in das Diagramm übernommen (siehe Tabelle~\ref{tab:rohdaten}). Eine
Stichproben der Ergebnisse, dass die Suchtreffer sich überwiegend mit den
eigentlichen Projekten Chef und Puppet beschäftigen. Anzumerken ist, dass
Zielgruppe von Puppet überwiegend Systemadminstratoren aus besteht, während Chef
auch von vielen Entwicklern genutzt wird. Letztere verwenden bevorzugt Github.
\begin{figure}[h]
\pgfplotstableread{
Puppet Ruby Shell Python Javascript Perl Andere
0 9902 321 148 124 42 260
7315 3108 751 207 157 137 211
}\dataset
\definecolor{bblue}{HTML}{4F81BD}
\definecolor{rred}{HTML}{C0504D}
\definecolor{ggreen}{HTML}{9BBB59}
\definecolor{ppurple}{HTML}{9F4C7C}
\begin{tikzpicture}
\begin{axis}[ybar stacked,
width=12cm,
enlarge x limits=0.5,
height=10cm,
ymin=0,
ymax=13500,
scaled x ticks = false,
scaled y ticks = false,
ylabel={Anzahl der Suchtreffer},
xtick=data,
xticklabels = {
\strut Chef (Gesamt: 10797),
\strut Puppet (Gesamt: 11749)
},
major x tick style = {opacity=0},
every node near coord/.append style={
anchor=west,
rotate=90
},
legend entries={Puppet, Ruby, Shell, Python, Javascript, Perl, Andere},
legend columns=13,
]
\addplot[draw=black,fill=rred] table[x index=0,y index=0] \dataset;
\addplot[draw=black,fill=ggreen] table[x index=0,y index=1] \dataset;
\addplot[draw=black,fill=bblue] table[x index=0,y index=2] \dataset;
\addplot[draw=black,fill=ppurple] table[x index=0,y index=3] \dataset;
\addplot[draw=black,fill=magenta] table[x index=0,y index=4] \dataset;
\addplot[draw=black,fill=yellow] table[x index=0,y index=5] \dataset;
\addplot[draw=black,fill=black] table[x index=0,y index=6] \dataset;
\end{axis}
\end{tikzpicture}
\caption{Anzahl der Suchtreffer auf Github aufgeschlüsselt nach
Programmiersprache für die Begriffe ``Chef'' und ``Puppet''.}
\label{tab:rohdaten}
\end{figure}
\begin{table}[h]
\caption{Ausgangsdaten für das Diagramm}
\begin{tabular}{l|c|c|c|c|c|c|c|c|c|c|c|c}
Sprache & \textbf{Ruby} & \textbf{Puppet} & \textbf{Shell} & \textbf{Python} & \textbf{Javascript} & \textbf{Perl} & PHP & Java & VimL & CSS & C & C++ \\\hline
\textbf{Chef} & 9,902& - & 321 & 148 & 124 & 42 & 56 & 88 & - & 31 & 48& 37 \\\hline
\textbf{Puppet} & 3,108& 7,315 & 751 & 207 & 157 & 137 & 82 & 42 & 64 & 23 & - & - \\
\end{tabular}
\end{table}
\vspace{0.5cm}
Eine weitere wichtige Statistik für Opensource-Projekte ist die Anzahl der
Abonnenten auf den jeweiligen Mailinglisten. Engagierte und aktive
Nutzer/Entwickler abonnieren häufig diese, wodurch sich die Größe der Community
qualitativ vergleichen lassen.
\begin{description}
\item[chef@lists.opscode.com] Community-Mailingliste, 1620 Abonnenten, Quelle:~\cite{chefcommunitylist}, Stand: 31.03.2014
\item[chef-dev@lists.opscode.com] Entwickler-Mailingliste, 652 Abonnenten, Quelle:~\cite{chefdeveloperlist}, Stand: 31.03.2014
\item[puppet-users@googlegroups.com] Community-Mailingliste, \textasciitilde{}7000 Abonnenten, Quelle:~\cite{puppetcommunitylist}, Stand: 01.04.2014
\end{description}
Die Anzahl der verfügbaren Module, veröffentlichte Githubprojekte und der
Abonnenten auf den Mailinglisten weisen darauf hin, dass Puppet nach wie vor
eine größere Community hat.
\paragraph{Funktionsweise}
Anstelle von Recipes werden in Puppet Manifeste geschrieben. Das sind Dateien,
die auf den Suffix .pp enden und sich in dem Ordner \texttt{manifests} im Modul
befinden. Jedes Manifest definiert eine Klasse, eingeleitet durch das
Schlüsselwort \texttt{class}. Der Namen dieser Klasse wird aus dem Modulnamen
und dem Manifest-Namen gebildet. Wenn das Modul \texttt{foo} das Manifest
\texttt{bar} enthält, ist der Name der Klasse \texttt{foo::bar}. Eine Ausnahme
bildet das Manifest \texttt{init.pp}, bei dem die Klasse nur \texttt{bar} lauten
würde. Diese Benennungskonvention wurde in Chef übernommen, um Recipes in
Cookbooks zu referenzieren. Allerdings werden in Recipes keine separaten Objekt
definiert und der ganze Inhalt der Datei bildet das Recipe.
Eine Klasse in Puppet kann über Parameter konfiguriert werden. Parameter werden
im Kopfteil der Klasse deklariert und können Standardwerte besitzen. Chef
besitzt mit \texttt{Attributen} ein vergleichbares Konzept. Allerdings werden
Attribute getrennt von den Recipes definiert und sie werden dem Node-Objekte
zugewiesen. Die Attribute stehen somit allen Recipes zu Verfügung und können an
verschiedenen Stellen überschrieben werden. In Puppet 3 wurde diese Trennung
von Code und organsationsspezifischen Daten durch die Erweiterung \texttt{Hiera}
ebenfalls eingeführt. Klassenparameter werden automatisch in der
\texttt{HieraDB} gesucht und gegebenenfalls überschrieben. In älteren Versionen
von \texttt{Puppet} wurden Einstellungen für die Nodes in der zentralen
\texttt{site.pp}-Manifest verwaltet. Hiera ersetzt die \texttt{site.pp} weitest
gehend. Durch die Funktion \texttt{hiera\_include} können Klassen im
Hiera-Backend gesetzt werden (ähnlich der Run-List in Chef).
Ressourcen heißen in Puppet \texttt{Types}. Puppet liefert wie Chef bereits eine Reihe
von Types mit. Diese werden Core-Types genannt. Wie auch in Chef können Types in
Puppet mehrere plattformspezifische Provider besitzen. Es ist möglich, eigene
Types zu definieren, auch Custom-Types genannt (Ähnlich LRWP in Chef). Die
Implementierung der Types/Provider erfolgt in Ruby im Verzeichnis
\texttt{lib/puppet}. Die Zustände einer Ressource können in Puppet über das Setzen
des Parameters \texttt{ensure} festgelegt werden (vergleichbar mit \texttt{action}
in Chef).
Ein weiteres häufig genutztes Entwurfsmuster, um Ressourcen zu gruppieren, ist
der \texttt{Defined-Type}. Dieser ist das Äquivalent zur aus Chef bekannten
\texttt{Definition}. Ein Defined-Type kann im Gegensatz zum Custom-Type auch
direkt in der Puppet-Sprache mit dem Schlüsselwort \texttt{define} erstellt
werden.
Vor der eigentlichen Provisionierung werden Informationen über das System zu
gesammelt. Dabei wird auf die Bibliothek
\href{http://puppetlabs.com/facter}{Facter} zurückgegriffen. In frühen Versionen
von Chef wurde die gleiche Bibliothek verwendet, bevor später
\href{http://docs.opscode.com/ohai.html}{Ohai} integriert wurde.
Puppet nutzt die gleiche Template-Syntax wie Chef, welche in
Quellcodelisting~\ref{lst:erb-templates} vorgestellt wurde, um Dateien auf dem System
zu generieren. Der einzige Unterschied bei Chef ist die Funktion für
verschiedene Plattformen und -versionen verschiedene Template-Varianten der
gleichen Datei im Cookbook vor halten zu können. Die Varianten werden durch Unterordner
im Verzeichnis \texttt{templates/} unterschieden (z.B.
\texttt{templates/windows} oder \texttt{templates/ubuntu-12.04}). Falls kein der
Plattform entsprechende Ordner existiert sucht Chef im Verzeichnis
\texttt{templates/default}.
Ein wesentlicher Unterschied zwischen Puppet und Chef ist die Reihenfolge der
Ausführung von Ressourcen. Chef überprüft die Ressourcen in der Reihenfolge, in
der sie in der Run-List und in den Recipes geladen werden. Puppet sortiert
Ressourcen derartig um, dass möglichst wenig Veränderungen am System vorgenommen
werden müssen um den gewünschten Zustand zu erreichen. Zum Beispiel, wenn an
mehreren Stellen eine Konfiguration für einen Dienst verändert wird, sollte
dieser nur einmal neu gestartet werden müssen. Bei Puppet spricht man von
modellbasiertem Konfigurationsmanagement, während Chef ein
\href{http://www.getchef.com/solutions/configuration-management/}{codebasiertes
Konfigurationsmanagement} ist. Da manche Ressourcen voneinander abhängen, kann
durch die Angabe der Parameter \texttt{before} und \texttt{require} die
Reihenfolge festgelegt werden. Über die Parameter \texttt{notify} und
\texttt{subscribe} können darüber hinaus Ressourcen aktualisiert werden, wenn
sich eine Abhängigkeit geändert hat (z.B. kann ein Dienst neu gestartet werden,
wenn sich die dazu gehörige Konfiguration verändert hat). In Chef kann Letzteres
über die Parameter \texttt{notifies} und \texttt{subscribes} angegeben werden.
\paragraph{Architektur} Wie auch Chef bietet Puppet verschiedene Betriebsmodi.
Im einfachsten Fall wird mit dem Befehl \texttt{puppet apply} ein lokales
Manifest geladen werden (vergleichbar mit Chef-Solo). Das Äquivalent zum
Chef-Server in Chef ist bei Puppet der Puppet-Master, zu welchem sich der Client
\texttt{Puppetd} verbindet und mittels SSL-Zertifikaten authentifiziert. In der
Standarteinstellung setzt Puppetmaster auf den verhältnismäßig einfachen
Webserver \texttt{WEBrick} Dieser skaliert allerdings nicht auf mehrere
CPU-Kerne, da nur ein Prozess und Thread gestartet wird. Für Installationen mit
mehr als 10 Knoten werden Passenger oder Mongrel als Applikationsserver
empfohlen, wobei Nginx als Load-Balancer fungiert. Ein beliebter Ansatz zum
Skalieren größerer Cluster ist das Verwalten der Manifeste in einem
Git-Repository, wobei ein Cron-Job periodisch die Manifeste vom Git-Server lädt
und Puppet ausführt. Während Chef-Server bis zur Version 10 wie Puppetmaster in
Ruby geschrieben war, wurde der API-Teil von Chef-Server in Version 11 in der
Programmiersprache Erlang neu geschrieben. Die Zahl der Nodes, die von einem
Server bedient werden, soll sich dabei vervierfacht haben und kann somit bis zu
10.000 Nodes bedienen (Quelle: \cite{chefscale}). Für Puppet wurden keine
Statistiken gefunden, die eine Aussage darüber treffen, wie viele Nodes pro
Server betreut werden können. Allerdings ist anzunehmen, dass die Anzahl der
Server, bedingt durch die genutzte Architektur, kleiner ist als bei Chef.
Zu den, von offiziell von Chef unterstützten, Plattformen gehören Windows, Mac OS X,
verschiedene Linuxderivate (Debian, Ubuntu, Redhat, \ldots) und Solaris. Puppet
bietet breiteren Support und unterstützt zusätzlich Free- und OpenBSD sowie
HP-UX und AIX.
\paragraph{Résumé}
Zusammenfassend lässt sich feststellen, dass Chef und Puppet den gleichen
Funktionsumfang bieten. Die Grundkonzepte sind ähnlich, so ein Anwender des
einen Systems mit wenig Aufwand auch das andere System lernen kann. Die beiden
Firmen, Puppet Labs und Chef, entwickeln beide ihr Produkt stetig weiter und
bieten kommerziellen Support. Während Puppet auf den klassischen
Systemadministrator abzielt, Chef spricht den Trend der
\href{http://www.getchef.com/solutions/devops/}{DevOps}-Kultur an, bei welcher
Administration und Entwicklung stärker ineinander über gehen.
% vim: set spell spelllang=de_de

22
report/mymacros.sty Normal file
View File

@ -0,0 +1,22 @@
\newcommand{\treeroot}[1]{% Title
\node[above] at (0,0) {#1};%
\setcounter{treeline}{0}
}
\newcommand{\treeentry}[2]{% Title, Level
\draw[->] (#2-1,-\value{treeline}/2) -- (#2-1,-\value{treeline}/2-0.5) --
(#2+0.5,-\value{treeline}/2-0.5) node[right] {#1};
\stepcounter{treeline}
}
\newcommand{\altentry}[2]{% Title, Level
\draw[->] (#2-1,-\value{treeline}/2) -- (#2-1,-\value{treeline}/2-0.5) --
(#2+0.5,-\value{treeline}/2-0.5) node[right] {#1};
\foreach \x in {1,...,#2}
{ \draw (\x-1,-\value{treeline}/2) -- (\x-1,-\value{treeline}/2-0.5);
}
\stepcounter{treeline}
}
\newcommand{\shellcmd}[1]{\nopagebreak\hspace{1cm}\texttt{\footnotesize\$ #1}}

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,13 @@
\usepackage{upquote} \usepackage{upquote}
\usepackage{courier} \usepackage{courier}
\usepackage{microtype} \usepackage{microtype}
\usepackage{url}
\usepackage{tikz}
\usepackage{pgfplotstable}
\newcounter{treeline}
\RequirePackage{mymacros}
\lstset{basicstyle=\footnotesize\ttfamily,breaklines=true,xleftmargin=1cm} \lstset{basicstyle=\footnotesize\ttfamily,breaklines=true,xleftmargin=1cm}
@ -26,17 +33,18 @@
\title{LCTP Abschlussaufgabe Chef \\ Wintersemester 2013 / 2014} \title{LCTP Abschlussaufgabe Chef \\ Wintersemester 2013 / 2014}
\begin{document} \begin{document}
\section{Aufgabenstellung} \section{Chef - Konfigurationsmanagementsystem (Jörg Thalheim)}
\label{sec:aufgabenstellung}
\begin{enumerate} \input{task}
\item Analysieren Sie die Funktionsweise von Chef und gehen Sie auf \input{comparison}
Unterschiede zwischen Chef und Puppet ein \input{services}
\item Wählens Sie zwei Netzwerkdienste aus die während der Lehrveranstaltung \input{tests}
besprochen wurden und erstellen Sie Provisionierungsvorlagen für diese \input{resume}
\item Beschreiben Sie wie die Verifikatioin von Provisionierungsvorlagen
bei der Bereitstellung von HPC-Software verwendet werden kann \bibliography{sources}{}
\end{enumerate} \bibliographystyle{plain}
\input{appandix}
% section aufgabenstellung (end)
\end{document} \end{document}
% vim: set spell spelllang=de_de

8
report/resume.tex Normal file
View File

@ -0,0 +1,8 @@
\subsection{Zusammenfassung}
\label{sub:Zusammenfassung}
%TODO Resume und Ausblick
% welche Erkenntnisse wurden gewonnen
% Ansible, Salt.
Chef

141
report/services.tex Normal file
View File

@ -0,0 +1,141 @@
\subsection{Einrichtung der Netzwerkdienste}
\label{sub:einrichtung-der-netzwerkdienste}
Für die Provisionierung der Netzwerkdienste wurde
\href{http://vagrantup.com}{Vagrant} verwendet. Dies ist ein Programm, um
schnell und reproduzierbar virtuelle Maschinen für Virtualbox und andere
Virtualsierungslösungen zu erstellen und zu starten. Die Einstellungen hierfür
werden in der Datei \texttt{Vagrantfile} hinterlegt, welche Vagrant beim Start
einliest. Vagrant kann Chef beim Erstellen von virtuellen Maschinen integrieren.
Zum Einsatz kam das Betriebssystem Ubuntu in der Version 12.04. Das Basisimage
hierfür wurde von \texttt{Chef}, der gleichnamigen Firma, bereitgestellt. Für
die Kommunikation mit Vagrant wurde die virtuelle Netzwerkkarte \texttt{eth0}
konfiguriert. Ein weitere Karte (\texttt{eth1}) wird für das interne virtuelle
Netzwerk zwischen den VMs zum Betreiben der Netzwerkdienste benötigt.
Vagrant bietet keine Optionen, ein virtuelles Netzwerk zu erstellen, ohne das
jeder VM eine IP-Adresse fest oder DHCP unmittelbar nach dem Start zugewiesen
wird. In dem genannten Netzwerk sollte allerdings DHCP von dem Head-Node bereit
gestellt werden. Deswegen waren zusätzliche Kommandozeilenargumente an den
Befehl \texttt{VBoxManage} im Vagrantfile nötig, welches von Vagrant genutzt
wird um Virtualbox zu verwalten. Dies schränkt die Nutzung allerdings auf den
Hypervisor Virtualbox ein.
Des weiteren wird Ruby auf dem Host benötigt, um beispielsweise die Tests
ausführen zu können. Auf Unix-Ähnlichen Systemen kann man diese
Programmiersprache mit dem Befehl:
\shellcmd{curl -sSL https://get.rvm.io | bash -s stable}
installiert werden. Auf dem Betriebssystem Windows kann auf den
\href{http://rubyinstaller.org/}{RubyInstaller} zurückgegriffen werden. Um die
benötigten Ruby-Bibliotheken zu installieren, müssen folgende 2 Befehle im
Projektverzeichnis ausgeführt werden:
\shellcmd{gem install bundler}
\shellcmd{bundle install}
Zur Verwaltung der externen und selbst geschriebenen Cookbooks wurde die
Abhängigkeitsverwaltung \href{http://berkshelf.com}{Berkshelf} verwendet. Bei
diesem werden die zu ladenden Cookbooks und die gewünschte Version in einer
Datei namens Berksfile angegeben (vergleichbar mit
\href{http://bundler.io}{Bundler} und Gemfiles in Ruby). Berkshelf unterstützt
dabei verschiedene Quellen (per API von der Communityseite von Chef, Git, lokal)
und kann Abhängigkeiten zu anderen Cookbooks auflösen. Um die Cookbooks initial
zu laden, muss der Befehl:
\shellcmd{berks install}
im Projektverzeichnis ausgeführt werden.
Für das Zusammenspiel mit Vagrant gibt es das Plugin
\href{https://github.com/berkshelf/vagrant-berkshelf}{vagrant-berkshelf}, so
dass die von Berkshelf verwalteten Cookbooks auch in Vagrant zur Verfügung
stehen.
Für bestimmte Funktionen, wie Gemeinsame Ordner (shared folders) zwischen VM und
Host, müssen die \texttt{virtualbox\--client\--modules} in der VM installiert
sein. Diese sind in vielen Images bereits vorhanden, die es für Vagrant gibt.
Allerdings muss die Virtualbox-Version des Host mit der Version in der VM
übereinstimmen. Abhilfe schafft das Vagrantplugin
\href{https://github.com/dotless-de/vagrant-vbguest}{vagrant-vbguest}. Beim
Start der VM installiert das Plugin die gleiche Version des Modul in der VM. Wenn
Virtualbox mit Linux als Host-System ausgeführt wird, muss das Kernelmodule
\texttt{vboxdrv} geladen sein. Manche Linux-Distributionen installieren dieses
Module bereits während der Installation von Virtualbox. Auf Mac OS X und
Windows sind keine weiteren Schritte notwendig.
Beide Plugins werden mit den Befehlen:
\shellcmd{vagrant plugin install vagrant-vbguest}
\shellcmd{vagrant plugin install vagrant-berkshelf}
installiert.
Gestartet wird die VM mit dem Befehl:
\shellcmd{vagrant up}
Während des ersten Starts wird die VM mit Chef provisioniert. Später kann Chef
erneut mit Befehl:
\shellcmd{vagrant provision}
gestartet werden.
Die Netzwerkdienste sollen die Protokolle DHCP, DNS und NTP bereitstellen. Es
wird wie im Praktikum zwischen \texttt{Head-Nodes} und \texttt{Compute-Nodes}
unterschieden. Die Head-Node bietet die genannten Dienste an. Die Compute-Nodes
fordern auf dem internen Netzwerk per DHCP eine IP-Adresse an und nutzen den
DNS- und NTP-Dienst der ihr zugewiesenen Head-Node.
Die Attribute für die Rollen und den Nodes wurden in JSON-Dateien in den
Verzeichnissen \texttt{roles/} und \texttt{nodes/} abgelegt. Es gibt je eine
Rollen-Datei für Compute-Nodes und Head-Nodes. In der aktuellen Konfiguration
erzeugt Vagrant eine Head-Node mit der FQDN \texttt{node0.lctp} und zwei
Compute-Nodes (\texttt{node1.lctp} und \texttt{node2.lctp}).
Es wurden fünf Cookbooks geschrieben:
\begin{description}
\item[bind]
Für Bereitstellung des DNS-Dienstes wird Named aus dem BIND-Packet
installiert. Das Cookbook richtet diesen Dienst ein und fügt die in den
Attributen konfigurierten DNS-Einträge zu den entsprechenden Zonen hinzu.
\item[dhcp]
Dieses Cookbook richtet den \href{https://www.isc.org/downloads/dhcp/}{ISC-DHCP-Server} ein. Neben der
Zuordnung von festen IP-Adressen zu Nodes, kann ein DNS-Server und ein
NTP-Server
festgelegt werden.
\item[lctp-network]
Dieses Cookbook ist ein Wrapper um das
\href{https://github.com/redguide/network_interfaces}{network\_interfaces}
Cookbook. Wrapper-Cookbooks werden häufig dazu benutzt um bestehende
Cookbooks aus anderen Quellen um Funktionalität zu erweitern. Für
Compute-Nodes aktiviert das Cookbook für die DHCP in dem
virtuellen Netzwerk. Im Falle eines Head-Nodes wird eine statische
IP-Adresse gesetzt, der DNS-Server auf localhost festgelegt und
IP-Forwarding sowie Masquerading via iptables für den Router-Betrieb
aktiviert.
\item[ntp]
Dieses Cookbook richtet den NTP-Deamon ein, welcher die Zeit zwischen den
einzelnen Nodes synchronisiert.
\item[main]
Dieses Cookbook fasst alle oben genannten Cookbooks für Compute- und
Head-Node zusammen. Man könnte dies prinzipiell auch in den jeweiligen
Rollen erledigen. Rollen haben allerdings den Nachteil, dass diese
im Gegensatz zu Cookbooks nicht versionierbar sind und (bei Chef-Server) über
alle Umgebungen identisch sind. Somit ist eine Trennung zwischen Test- und
Produktivumgebung schwierig.
\end{description}
Es wurden folgende externen Cookbooks verwendet:
\begin{description}
\item[apt] aktualisert die lokalen Paketlisten und den Paketcache.
\item[network\_interfaces] verwaltet Debian's Netzkonfiguration
\item[minitest-handler] Sammelt alle Tests in den Cookbooks und führt diese
nach der Provisionierung aus (siehe~\ref{minitest_handler}).
\end{description}
% vim: set spell spelllang=de_dihr zugewiesenen e

79
report/sources.bib Normal file
View File

@ -0,0 +1,79 @@
@misc{chefenterprise,
author = {Chef},
title = {Enterprise-class features and support},
url = {http://www.getchef.com/enterprise-chef/#features-and-support},
year = {2014},
month = {März}
}
@misc{chefrun,
author = {Chef},
title = {About the chef-client Run},
url = {http://docs.opscode.com/essentials_nodes_chef_run.html},
year = {2014},
month = {März}
}
@misc{chefhistory,
author = {Chef},
title = {History of Chef: What's in a Name?},
url = {http://www.youtube.com/watch?v=Ia2ItmjRsw8&feature=plcp},
year = {2014},
month = {März}
}
@misc{puppetlanguage,
author = {Puppet-Labs},
title = {Docs: Language: Basics},
url = {http://docs.puppetlabs.com/puppet/latest/reference/lang_summary.html#compilation-and-catalogs},
year = {2014},
month = {März}
}
@misc{puppetlanguagechangelog,
author = {Puppet-Labs},
title = {Docs: History of Puppet Language Features},
url = {http://docs.puppetlabs.com/guides/language_history.html#puppet-language-features-by-release},
year = {2014},
month = {März}
}
@misc{chefscale,
author = {Chef},
title = {Opscode Unleashes New Generation of Chef},
url = {http://www.getchef.com/press-releases/opscode-unleashes-new-generation-of-chef/},
year = {2013},
month = {Februar}
}
@misc{facebooklikeschef,
author = {Chef},
title = {Scaling systems configuration at Facebook - Phil Dibowitz},
url = {http://www.youtube.com/watch?v=SYZ2GzYAw_Q},
year = {2013},
month = {April}
}
@misc{chefcommunitylist,
author = {Chef},
title = {Opscode Mailing Lists},
url = {http://lists.opscode.com/sympa/info/chef},
year = {2014},
month = {März}
}
@misc{chefdeveloperlist,
author = {Chef},
title = {Opscode Mailing Lists},
url = {http://lists.opscode.com/sympa/info/chef-dev},
year = {2014},
month = {März}
}
@misc{puppetcommunitylist,
author = {Puppet-Labs},
title = {Anfrage auf Twitter},
url = {https://twitter.com/puppetlabs/status/450760644329881600},
year = {2014},
month = {März}
}

13
report/task.tex Normal file
View File

@ -0,0 +1,13 @@
\subsection{Aufgabenstellung}
\label{ssub:aufgabenstellung}
\begin{itemize}
\item Analysieren Sie die Funktionsweise von Chef und gehen Sie auf
Unterschiede zwischen Chef und Puppet ein.
\item Wählen Sie zwei Netzwerkdienste aus, die während der Lehrveranstaltung
besprochen wurden und erstellen Sie Provisionierungsvorlagen für diese.
\item Beschreiben Sie, wie die Verifikation von Provisionierungsvorlagen
bei der Bereitstellung von HPC-Software verwendet werden kann.
\end{itemize}
% vim: set spell spelllang=de_de

132
report/tests.tex Normal file
View File

@ -0,0 +1,132 @@
\subsection{Verifikation}
\label{ssub:verifikation}
Wie auch in der Softwareentwicklung müssen Konfigurationssysteme getestet
werden. Dies gestaltet sich oft als schwierig, weil nicht immer eine exakte
Kopie des aktuellen Produktionssystems zur Verfügung steht. Mit steigender
Komplexität steigt der Aufwand, Cookbooks manuell zu testen. Im Folgenden werden
verschiedene Möglichkeiten aufgeführt, wie dies automatisiert werden kann.
Die erste und einfachste Methode ist der Befehl:
\shellcmd{knife cookbook test [COOKBOOKS...]}
Das Kommandozeilenprogramm \texttt{knife} ist ein Teil von Chef. Es ist das
primäre Verwaltungsprogramm für Chef-Administratoren. Der Unterbefehl
\texttt{cookbook test} überprüft den Ruby-Quellcode und die Templates des
Cookbooks auf Syntaxfehler. Allerdings treten viele Fehler erst zur Laufzeit
auf, im Besonderen da Ruby dynamisch typisiert ist und der Compiler
beispielsweise Tippfehler in Methoden und Variablennamen nicht erkennen kann.
Ein anderes Programm ist foodcritic. Es führt eine statische Codeanalyse ähnlich
\href{http://www.jslint.com/}{JSlint} oder
\href{http://perl-critic.stacka.to/}{Perl::Critic} auf der eigenen Codebasis
durch. Dabei wird der Ruby-Quellcode gegen einen Regelsatz getestet, um so
häufige Programmierfehler zu erkennen oder um Code-Konventionen innerhalb eines
Projekts einzuhalten. Dieser Regelsatz kann durch eigene Regeln erweitern
werden.
\subsubsection{Chefspec}
\label{chefspec}
Chefspec baut auf das in Ruby verbreitete Testframework
\href{http://rspec.info/}{RSpec} auf. Chefspec erweitert dabei RSpec um die
Funktion, Cookbooks zu laden und stellt spezielle Matcher (RSpec-Terminologie
für Assertions) bereit, um diese zu testen. Wie bereits in
Abschnitt~\ref{ablauf_einer_provisionierung} erwähnt, gibt es zwei Phasen bei
der Ausführung von Chef. Bei Chefspec wird der Provisionierungsprozess nur bis
zur Convergingphase durchlaufen. Die eigenen Tests überprüfen nur die erzeugten
\texttt{Ressourcen}. Dies hat den Vorteil, das Tests sehr schnell durchlaufen
werden, da keine Änderungen an einem System vorgenommen werden müssen. Dies hat
Vorteile beim Entwickeln, weil man auf diese Weise schnell Feedback bekommt. Das
Zusammenspiel mehrerer Cookbooks lässt sich dadurch gut testen. Außerdem
ermöglicht es, verschiedene Konfigurationen/Betriebssysteme zu testen, ohne das
diese (zeit)aufwendig aufgesetzt werden müssen. Da Chefspec allerdings zu keinen
Zeitpunkt Code auf dem System ausführt, sind weitere Integrationstest
unerlässlich.
Der folgende Test wurde aus dem selbst geschriebenen NTP-Cookbook
(\ref{sub:einrichtung-der-netzwerkdienste}) entnommen.
\begin{lstlisting}[language=Ruby,caption={Chefspec-Test für das NTP-Cookbook}]
require_relative '../spec_helper'
describe 'ntp::default' do
let(:chef_run) do
ChefSpec::Runner.new do |node|
node.set["ntp"]["subnets"] = ["::1", "127.0.0.1", "172.28.128.0 mask 255.255.255.0 nomodify notrap nopeer"]
end.converge(described_recipe)
end
it "should setup ntp" do
chef_run.should install_package("ntp")
chef_run.should render_file("/etc/ntp.conf").with_content("172.28.128.0")
end
end
\end{lstlisting}
Im \texttt{chef\_run}-Block wird dem fiktiven Node Attribute zugewiesen und das zu
testende Cookbook ausgeführt. Das Ergebnis wird in diesem Beispiel in dem Objekt
\texttt{chef\_run} gespeichert. Gegen dieses Objekt wird getestet, ob bestimmte
\texttt{Ressourcen} korrekt initialisiert wurden. In diesem Fall wird überprüft, ob
das Paket \texttt{ntp} installiert werden soll und ob das Subnetz in dem Template
in der Konfigurationsdatei \texttt{/etc/ntp.conf} richtig gesetzt wird.
Die Tests werden mit dem Befehl \texttt{rspec} ausgeführt. Wenn keine weiteren Argumente
angegeben sind, führt dieses Programm alle Dateien unterhalb des Ordners \texttt{spec}
aus, dessen Dateinamen auf \texttt{\_spec.rb} enden.
Um alle drei oben genannten Testmethoden gleichzeitig ausführen zu lassen, wurde
ein Rakefile geschrieben. \href{http://rake.rubyforge.org/}{Rake} ist das in
Ruby geschriebene Äquivalent zu Make, welches ein verbreitetes Buildprogramm
auf UNIX-ähnlichen Plattformen ist. Die Tests werden durch den Befehl:
\shellcmd{rake test}
ausgeführt.
Dieser muss innerhalb Projektverzeichnisses aufgerufen werden.
\subsubsection{Minitest-Handler}
\label{minitest_handler}
\href{https://github.com/btm/minitest-handler}{Minitest-Handler} hingegen wird
nach jedem Provisionierungsdurchgang ausgeführt. Im Gegensatz zu Chefspec nutzt
es das Minitest-Framework, welches schon mit Ruby mitgeliefert wird. Um
Minitest-Handler zu nutzen, muss das Recipe aus
\texttt{Minitest-Handler-Cookbook} als erstes Recipe in der Node geladen werden.
Minitest-Handler durchsucht beim Durchlauf in jedem anderen Cookbook, in den
Unterordnern in \texttt{files/} nach dem Verzeichnis \texttt{test} und lädt alle
Tests aus diesem Verzeichnis. Über die Zeile:
\begin{lstlisting}[language=Ruby]
describe_recipe "ntp::default" do #
#...
end
\end{lstlisting}
wird angeben, zu welchem Test das Recipe gehört (In diesem Fall das
Recipe aus dem NTP-Cookbook). Wenn das entsprechende Recipe von dem Node
ausgeführt wird, wird der dazugehörige Test nach dem Provisionierungsdurchlauf
ebenfalls ausgeführt. Minitest-Handler erweitert RSpec um nützliche Methoden, um
den Status des Systems zu überprüfen. Nachfolgend ist ein Beispiel aus dem Bind-Cookbook,
welches in Abschnitt~\ref{sub:einrichtung-der-netzwerkdienste} erwähnt wurde:
\begin{lstlisting}[language=Ruby, caption={\texttt{Minitest}-Test für das Bind-Cookbook}]
describe_recipe 'bind::default' do
it "starts the named daemon" do
service("bind9").must_be_running
end
it "should resolve dns" do
assert_sh("dig localhost @localhost")
end
end
\end{lstlisting}
Die Methode \texttt{assert\_sh} überprüft den Exit-Code eines Befehls und schlägt
fehl, wenn dieser ungleich der Zahl Null ist, während die \texttt{service}-Methode den
Status eines Systemdienst überprüft. Weitere Testmethoden sind zum Beispiel
das Überprüfen von Verzeichnissen, Inhalte von Dateien oder Mountpoints. Viele
Fehler werden in der Regel schon von den Provider erkannt und festgestellt.
Minitest-Handler kann dies Erweitern um protokollspezifische Tests durchzuführen
oder das Testen von Funktionalität bestimmter Dienste.
% vim: set spell spelllang=de_de

View File

@ -1,10 +0,0 @@
# If last line is `mesg n`, replace with conditional.
if [ "`tail -1 /root/.profile`" = "mesg n" ]; then
echo 'Patching basebox to prevent future `stdin: is not a tty` errors...'
sed -i '$d' /root/.profile
cat << 'EOH' >> /root/.profile
if `tty -s`; then
mesg n
fi
EOH
fi