chef: work in progress

This commit is contained in:
Jörg Thalheim 2014-03-12 16:49:23 +01:00
parent fe77f6a65d
commit dc3fc48f0f
9 changed files with 365 additions and 1 deletions

View File

@ -1,3 +1,4 @@
\section{Abschlussaufgaben}
\input{nftables/nftables}
\input{chef/chef}

View File

@ -20,6 +20,9 @@
\usepackage{pgfplotstable}
\usepackage{csquotes}
\usepackage{float}
\usepackage[utf8]{inputenc}
\usepackage{ucs}
\RequirePackage{mymacros}
@ -62,4 +65,6 @@
\input{anhang}
\bibliography{sources}{}
\bibliographystyle{plain}
\end{document}

View File

@ -42,6 +42,6 @@ dem {\tt eth0}-Interface setzen (siehe \emph{aufgabe2.3/iptables.rules}) und mit
\input{bs/bs-git}
\input{bs/bs-pdsh}
\input{bs/bs-pdsh}
\pagebreak

View File

@ -0,0 +1,127 @@
\subsubsection{Funktionsweise von Chef}
\label{ssub:funktionsweise_von_chef}
\href{http://www.getchef.com/chef/}{Chef} ist ein Framework, welches es
ermöglicht automatisiert Server zu konfigurieren und zu verwalten. Der
Endanwender beschreibt hierbei die Systemresourcen und ihre Zustände in der
Programmiersprache \href{https://www.ruby-lang.org/}{Ruby}. Diese Definitionen
werden von \emph{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 \emph{Chef-repo}.
Diese untergliedert sich in mehrere \emph{Cookbooks}\label{cookbook}, welche die
Grundverwaltungseinheit darstellt. Jedes dieser Cookbooks, erfüllt einen
bestimmten Teilaspekt des Systems, (z.B. die Einrichtung eines Webservers
\href{https://github.com/opscode-cookbooks/apache2}{Apache}). Coobooks
können versioniert werden. Es können Abhängigkeiten zwischen mehreren
Cookbooks angegeben werden.
Eine physikalische oder virtuelle Maschine wird als \emph{node} bezeichnet.
Einer Node können \emph{Attributes}, \emph{Roles} und Cookbooks zugewiesen
werden. Roles bieten eine Möglichkeit Nodes, welche die gleiche
Aufgaben in einer Organisation besitzen, zu gruppieren (z.B. webserver).
Es gibt mehrere Möglichkeiten \emph{Chef} zu betreiben:
\begin{description}
\item[chef-solo] Dies ist die einfachste Ausführungsform. Hierbei wird lädt
\emph{Chef-client} alle nötigen Daten aus einem lokalen Verzeichnis. Diese
Form wurde für die Umsetzung der Aufgabenstellung.
\item[chef-server] Hierbei authentifiziert sich \emph{Chef-client} über eine
\emph{REST-Api} zu einem \emph{chef-server} mittels Zertifikaten. Auf diesem
wird das Chef-repo zentral verwaltet. Der Chef-client bekommt von diesem
alle nötigen für die zu provisionierende \emph{node}. Chef-server bietet
eine Weboberfläche für die Administrierung an. Die Attribute aller Nodes sind
über die eingebaute Suchemaschine \emph{Solr} durchsuchbar.
\item[Enterprise Chef/Hosted Enterprise Chef] Ähnlich wie Chef-server aber
bietet bessere Skalierbarkeit, rolenbasierte Benutzerverwaltung, bessere Überwachung,
Push-Deployment und eine verbesserte Weboberfläche~\cite{chefenterprise}
\end{description}
\textbf{Aufbau eines Cookbook}
\label{aufbau_eines_cookbook}
Hier ist die Ordnerstruktur des Cookbook apt (TODO Link) dargestellt:
\begin{lstlisting}
> apt-2.3.4/
> attributes/
default.rb
> files/
> default/
apt-proxy-v2.conf
> libraries/
helpers.rb
network.rb
> providers/
preference.rb
repository.rb
> recipes/
cacher-client.rb
cacher-ng.rb
default.rb
> resources/
preference.rb
repository.rb
> templates/
> debian-6.0/
> default/
01proxy.erb
acng.conf.erb
> ubuntu-10.04/
acng.conf.erb
CHANGELOG.md
metadata.json
metadata.rb
README.md
\end{lstlisting}
Die Verzeichnisnamen sind fest vorgeben. Jedes Verzeichnis hat seine eigene
Funktion. Dies hat den Vorteil, das man sich schnell in neuen Cookbooks zurecht
findet. Hier nochmal die einzelnen Verzeichnisse im Überblick:
\begin{description}
\item[attributes] setzt Standardwerte (Attribute) für das Cookbook. Dies
können Strings, Zahlen oder Arrays sein. Die gesetzten Attribute können in
Roles, Nodes oder anderen Cookbooks überschrieben werden. Hierfür gibt es
die Prioritäten default, force\_default, normal und override um das
überschreiben deterministisch zu machen. Attributes sind hierarchisch
organisiert. In der Regel ist die höchste Ebene der Name des Cookbooks.
(z.B. normal.mysql.client.packages)
\item[files] Hier können statische Dateien eingefügt werden, welche dann auf
dem Zielsystem in das entsprechende Verzeichnis kopiert werden können.
\item[libraries] In diesem Verzeichnis können Hilfsfunktionen definiert werden.
\item[resources] Ressourcen beschreiben, die Bestandteile eines Systems. Eine
Ressource kann z.B. eine Datei, ein Prozess oder ein Packet sein. Man
beschreibt, welchen Zustand (action in Chef genannt) diese Ressource haben
soll und Chef versucht diesen Zustand herzustellen. Chef liefert von Haus
viele wichtige Ressourcen mit. In Cookbooks kann man darüber hinaus eigene
Ressourcen definieren.
\item[providers] Während Ressourcen nur die Schnittstelle beschreiben, mit
allen Attributen, die gesetzt werden können, ist der Provider eine konkrete
Implementierung. Deswegen muss jede Ressource mindestens einen Provider
besitzen. Es kann mehrere Provider für eine Ressource geben, um zum Beispiel
um mehrere Plattformen/Betriebssysteme abzudecken (z.B. bei Packetmanagern
oder Initsystemen).
\item[recipes] In Recipes werden Ressourcen instantiiert, welche nötig sind
um die gewünschte Aufgabe zu erreichen. Dabei können Abhängigkeiten zwischen
diesen angegeben werden.
\item[templates] Häufig werden dynamisch generierte Dateien benötigt, um zum
Beispiel Konfigurationsdateien zu erzeugen. Chef bindet hierfür die
Templatesprache eRuby (Embedded Ruby) ein. Diese führt in den Templates
Rubycode, der sich zwischen den Tags \emph{<\%} und \emph{\%>} befindet, aus.
Dies erlaubt es Variablen zu interpolieren, sowie If-Statements und
Schleifen.
\item[metadata.rb] In dieser Datei kann der Name des Cookbook, die Version,
eine Beschreibung sowie Abhängigkeiten zu anderen Cookbooks angeben werden.
\end{description}
\textbf{Durchlauf eines Recipes}
\label{durchlauf_eines_recipes}
TODO Runlist
\textbf{Vergleich mit puppet}
\label{vergleich_mit_puppet}
% vim: set spell spelllang=de_de

View File

@ -0,0 +1,90 @@
\subsubsection{Einrichtung der Netzwerkdienste}
\label{ssub:einrichtung-der-netzwerkdienste}
Für die Provisionierung der Netzwerkdienste wurde
\href{http://vagrantup.com}{\emph{vagrant}} verwendet. Dies ein Programm um auf
der Basis von Virtualbox und anderen Virtualisierungslösungen schnell und
reproduzierbar virtuelle Maschinen zu starten. Die Einstellungen hierfür werden
in der datei \emph{Vagrantfile} geschrieben, welches vagrant beim Start
einliest. Vagrant bietet eine gute Integration für Chef. Es kann direkt
eingestellt werden, mit welchen Einstellungen neue virtuelle Maschinen
provisioniert werden sollen. Zum Einsatz kam das Betriebssystem Ubuntu 12.04
LTS. Das Basisimage hierfür wurde von \emph{opscode}, der Firma hinter Chef,
bereitgestellt. Es wurde ein Netzwerkinterface konfiguriert für die
Kommunikation mit vagrant und ein weiteres Internes für ein virtuelles Netzwerk
zwischen den VMs zum Betreiben der Netzwerkdienste.
Gestartet wird die VM mit dem Befehl:
\shellcmd{vagrant up}
Beim 1. Start wird die VM mit Chef provisioniert. Spätere kann Chef erneut mit
folgenden Befehl gestartet werden:
\shellcmd{vagrant provision}
Für bestimmte Funktionen wie geteilte Ordner zwischen VM und Host müssen die
\emph{virtualbox-client-modules} in der VM installiert sein. Diese sind zwar in
vielen Images vorhanden, die es für vagrant gibt. Allerdings muss die
Virtualboxversion des Host mit dem der VM übereinstimmen. Abhilfe schafft das
Vagrantplugin \emph{vagrant-vbguest} (TODO link). Dieses überprüft beim
Start die Versionen und installiert gegebenfalls eine Andere in der VM.
Als Netzwerkdienste wurden die Protokolle DHCP, DNS und NTP gewählt. Die
VMs wurden in 2 Gruppen geteilt, \emph{Headnodes}, die die genannten Dienste anbieten
und \emph{Computenodes}. Die Computenodes fordern auf dem internen Netzwerk per
DHCP eine IP-Adresse an und nutzen den DNS- und NTP-Dienst des jeweiligen
Headnode.
Für das Deployment wurden fünf Cookbooks geschrieben:
\begin{description}
\item[bind]
Als DNS-Server wurde bind gewählt. Dieses Cookbook diesen Dienst ein und
fügt die in den Attributen konfigurierten DNS-Einträge hinzu zu den
entsprechenden Zonen hinzu.
\item[dhcp]
Dieses Cookbook richtet den 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 Wrappercoobook um das network\_interfaces
(TODO link) Cookbook. Wrappercookbook werden häufig dazu benutzt um bestehende
Cookbooks aus anderen Quellen um Funktionalität zu erweitern. In diesem Fall
aktiviert das Cookbook für die Computenodes dhcp auf dem interen
Netzwerkinterface. Auf den Headnodes wird eine statische IPadresse gesetzt,
der DNS-Server auf localhost festgelegt und Ipforwarding sowie
Masquerading via iptables für den Routerbetrieb aktiviert.
\item[ntp]
Dieses Cookbook richtet den NTP-Deamon ein, welcher die Zeit zwischen den
einzelnen Knoten synchron halten soll.
\item[main]
Dieses Cookbook fast alle oben genannten Cookbooks für Compute- und
Headnode zusammen. Man könnte dies prinzipiell auch in den jeweiligen
Rollen erledigen. Rollen in Chef haben allerdings den Nachteil, dass diese
nicht versionierbar sind und (bei Chef-server) über alle Umgebungen gleich
sind. Somit ist eine Trennung zwischen Test- und Produktivumgebung
schwierig.
\end{description}
Es wurden folgende externen Cookbooks verwendet:
\begin{description}
\item[apt] bringt die lokalen Packetquellen auf den aktuellen Stand und
aktualisiert den Packetcache.
\item[network\_interfaces] verwaltet Debians Netzkonfiguration
/etc/network/interfaces
\item[minitest-handler] Sammelt alle Tests in den Cookbooks führt diese
nach der Provisionierung aus (siehe~\ref{chef_minitest_handler})
\end{description}
Zur Verwaltung der externen und internen Cookbooks wurde
\href{http://berkshelf.com}{Berkshelf} verwendet. Bei diesem gibt man die
Abhängigkeiten und die gewünschte Version in einer Datei namens Berkssfile an
(vergleichbar mit Gemfile in Ruby).
Berkshelf lädt diese Abhängigkeiten aus verschiedenen Quellen herunter. (per Api
von der Communityseite von Opscode, git, lokal) Für das Zusammenspiel mit
vagrant gibt es das Plugin vagrant-berkshelf (TODO link), so dass die von Berkshelf
verwalten Cookbooks auch in vagrant zur Verfügung stehen.
TODO Roles und Nodes json
% vim: set spell spelllang=de_de

View File

@ -0,0 +1,13 @@
\subsubsection{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ählenSie 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

115
bericht/chef/chef-tests.tex Normal file
View File

@ -0,0 +1,115 @@
\subsubsection{Verifikation}
\label{ssub:verifikation}
Wie auch Software müssen auch Provisionierungsskripte getestet werden.
Dies gestaltet sich oft als schwierig, weil nicht immer eine Kopie des
aktuellen Produktionssystem zur Verfügung steht. Mit steigender Komplexität
steigt der Aufwand geschriebene 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...]}
Dieser überprüft den Rubyquellcode und die Templates des Cookbooks auf Syntaxfehler.
Allerdings treten viele Fehler erst zur Laufzeit auf, insbesonderen da Ruby
eine dynamische Programmiersprache. Ein anderes Programm ist foodcritic. Dies
ist eine statische Codeanalyse ähnlich jslint oder perlcritics. Dabei wird der
Rubycode gegen Regelsatz getestet, um so schlechten Stil zu erkennen oder
um Codeingstandards innerhalb eines Projekts einzuhalten. Diesen Regelsatz kann
man durch eigene Regeln erweitern.
\textbf{Chefspec}
\label{chefspec}
Chefspec baut auf das in Ruby verbreitete Testframework Rspec auf. Dies ist ein
Vertreter des BDD (TODO mehr Infos zu BDD, Integration von Chefspec). Wie
bereits in Abschnitt ~\ref{durchlauf_eines_recipes} erwähnt, gibt es 2 Phasen
bei der Ausführung von Chef. Bei Chefspec wird die 1. Phase, die (TODO
\$Phase)-phase, durchlaufen und danach die eigenen Tests ausgeführt. 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 durchzutesten, ohne das diese aufwendig
aufgesetzt werden müssen. Da Chefspec allerdings nicht wirklich Code auf dem
System ausführt, sind Integrationstest unerlässlich.
Der folgende Test wurde aus dem NTP-Cookbook
(~\ref{ssub:einrichtung-der-netzwerkdienste}) entnommen.
\begin{lstlisting}
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 \emph{chef\_run}-Block wird der fiktiven Node Attribute zugewiesen und das zu
testende Cookbook ausgeführt. Das Ergebnis wird in diesem Beispiel in dem Objekt
\emph{chef\_run} gespeichert. Gegen dieses Objekt wird getestet, ob bestimmte
Resourcen korrekt initialisiert wurden. In diesem Fall wird überprüft, ob
TODO Ausführung
TODO rake test
\textbf{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. Allerdings kann man durch einbinden, der Zeile:
\begin{lstlisting}
require "minitest/spec"
\end{lstlisting}
eine Rspec sehr ähnliche Syntax benutzen. Um Minitest Handler zu nutzen, muss
das Recipe aus minitest-handler-cookbook als erstes Recipe in der node geladen
werden. Minitest Handler durchsucht beim Durchlauf in jedem anderen cookbook, in
den Unterordnern von \emph{files/} nach dem Verzeichnis \emph{test} und lädt
alle Tests aus diesem Verzeichnis. Über die Beschreibungszeile:
\begin{lstlisting}
describe_recipe "ntp::default" do #
#...
end
\end{lstlisting}
wird angeben für, welches Recipe der Test gedacht ist (In diesem Fall das
Defaultrecipe aus dem NTP-Cookbook). Wenn das entsprechende Recipe von der Node
ausgeführt wird, wird nach der entsprechende Test nach dem
Provisionierunsdurchlauf ebenfalls ausgeführt. Minitest Handler erweitert Rspec
um nützliche Methoden um den Status des Systems zu überprüfen. Hier ein Beispiel
aus dem bind cookbook, welches in
Abschnitt~\ref{ssub:einrichtung-der-netzwerkdienste} erwähnt wurde:
\begin{lstlisting}
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 \emph{assert\_sh} überprüft den Exitstatus eines Befehls und schlägt
fehl, wenn dieser ungleich null ist, während der \emph{service} den Status eines
Systemdienst sicherstellt. Es gibt noch weitere Testmethoden, wie das Überprüfen
von Verzeichnissen, Inhalte von Dateien oder Mountpoints.
% vim: set spell spelllang=de_de

5
bericht/chef/chef.tex Normal file
View File

@ -0,0 +1,5 @@
\subsection{Chef - Provisionierungssystem}
\input{chef/chef-task}
\input{chef/chef-comparison}
\input{chef/chef-services}
\input{chef/chef-tests}

8
bericht/sources.bib Normal file
View File

@ -0,0 +1,8 @@
@online{chefenterprise,
author = {chef},
title = {{Enterprise-class features and support }},
howpublished =
"\url{http://www.getchef.com/enterprise-chef/#features-and-support}",
year = {2014},
note = "[Online; 06.03.2014]"
}