Aus Linux-Magazin 09/2014

Testen im Devops-Umfeld

© reddz, 123RF

Führt ein Unternehmen eine Devops-Kultur ein, baut es meist kurzentschlossen Teams um, verschiebt Verantwortlichkeiten und vergibt Adminrechte neu. Doch kalkuliert es auch die Risiken und Nebenwirkungen mit ein? Ein Bericht aus der Praxis.

Ein typischer Effekt beim Wechsel hin zur Devops-Kultur: Auf den Servern im Rechenzentrum wächst der Nutzerkreis mit administrativen Berechtigungen. Nach der anfänglichen Euphorie stellen sich bei Ausfällen im Alltag jedoch wieder die altbekannten Fragen: Wer war es, wie konnte das passieren und warum haben wir es nicht bemerkt?

Zwar ändert sich der Weg zum Ziel in einer agilen Welt dramatisch, doch kann auch diese kaum auf einen stabilen Betrieb und Nachhaltigkeit verzichten. War es früher nur wenigen Admins erlaubt, Änderungen auf Servern auszurollen, darf nun dank Devops fast jeder Kollege ran. Die IT soll nicht mehr von Spezialwissen und einem jahrelang antrainierten Bauchgefühl abhängen, an ihre Stelle tritt mehr und mehr die Testautomation. Von Spezialisten entwickelt, setzt sie dann jedermann ein.

Testautomation ist nicht neu und kommt in der Software-Entwicklung schon lange zum Einsatz. Dass Admins dieses Vorgehen auf den Betrieb von Servern und Rechenzentren übertragen, ist allerdings eine recht junge Entwicklung.

Die im Artikel vorgestellten Methoden eignen sich womöglich weniger für kleinere IT-Umgebung. Doch Firmen mit vielen Mitarbeitern sowie solche, die Nachhaltigkeit anstreben, dürften davon profitieren. Sie zielen vor allem darauf ab, die Kosten für das Beseitigen von Fehlern zu senken. Im Entwicklungsprozess, der aus Planen, Entwickeln, Testen und Betreiben besteht, wird die Lösung meist umso teurer, je später der Fehler auffällt (Abbildung 1). In der Planungsphase kann ein Entwickler einen Designfehler günstig und einfach beseitigen. Geht ein Softwareprodukt erst in Produktion, lässt sich der gleiche Fehler nur mit größtem Aufwand entfernen.

Abbildung 1: Fehlerfolgekosten während des Produkt-Lebenszyklus.

Abbildung 1: Fehlerfolgekosten während des Produkt-Lebenszyklus.

An genau diesem Punkt gehen Entwickler und Admins sehr unterschiedlich vor. Die Software-Entwickler investieren typischerweise viel mehr Zeit in die Planung, um dann relativ zügig zu entwickeln, zu testen und die Software in Betrieb zu nehmen. Im Operations-Bereich (auf Deutsch “Betrieb”) entstehen dagegen viele Ideen spontan oder werden in kurzen Workshops erarbeitet.

Eine entwickelte Software schicken die Admins vor dem Produktiveinsatz in eine längere Testphase. Das fertige Programm testen sie dann meist nur über Ausprobieren und stellen so fest, ob “etwas Schlimmes passiert”. Abbildung 2 vergleicht symbolhaft die beiden Welten, die Devops vereinen möchte.

Abbildung 2: Die Testphase verläuft bei Entwicklern (Devs) und Admins (Ops) nicht selten unterschiedlich.

Abbildung 2: Die Testphase verläuft bei Entwicklern (Devs) und Admins (Ops) nicht selten unterschiedlich.

Planen Betriebler umfangreiche Änderungen, treffen sie im Operations-Bereich nicht selten auf großen Widerstand. Dahinter steht meist die Angst, eine funktionierende Infrastruktur zu zerstören. In der Entwicklung passiert es hingegen häufiger, dass eine gut getestete Software auch größere Umbauten mit überschaubarem Risiko übersteht.

Aus Fehlern lernen

In der Praxis braucht Devops vor allem zwei Dinge: gegenseitigen Respekt und die Bereitschaft, voneinander zu lernen. Viele Devops-Projekte beginnen damit, dass die Entwickler das Rechenzentrum und den Serverbetrieb besser kennenlernen und die Herausforderungen im Operations-Bereich in ihrer Planung berücksichtigen. Entwickler lernen nervige Probleme zu vermeiden, indem sie dafür sorgen, dass Logfiles nicht die Festplatten überschwemmen, keine störrischen und trägen Dienste laufen, Monitoring und Alarming funktionieren und sie keine Systeme mit völlig überzogenen Ressourcen-Anforderungen bestellen.

Der Betrieb lernt von den Entwicklern meist noch mehr:

  • Kleine Schritte gehen (Incremental Improvement).
  • Infrastruktur in Code umsetzen.
  • Konsequent Versionskontrollsysteme benutzen.
  • Denken wie ein Entwickler: Funktionen in Bibliotheken auslagern, richtige Abstraktion und so weiter.
  • Anstelle von Spaghetticode mit langen Kommentaren lesbaren und wartbaren Code produzieren.
  • Schnittstellen definieren.
  • Unittests und Integrationstest umsetzen.
  • Testautomation.

Dabei zeigt sich: Viele praktische Aspekte in der modernen Software-Entwicklung ergeben auch im administrativen Bereich Sinn. So teilen Entwicklung und Betrieb eines der wichtigsten Prinzipien: die testgetriebene Entwicklung [1].

Untested = broken

Die Grundidee besagt, dass alle nicht getesteten Funktionen entweder kaputt oder nicht implementiert sind. Während Entwickler viele verschiedene Arten von Tests kennen, reichen im Operations-Bereich meist zwei Testtypen aus: Unittests und Systemtests. Unittests überprüfen eine kleine Komponente, die Unit, in einer künstlichen Umgebung. Systemtests bringen die gesamte Anwendung in einer möglichst praxisnahen Form mit anderen Anwendungen oder deren Abhängigkeiten zusammen. Unittests durchleuchten also eher einzelne Codeteile, während Systemtests überprüfen, wie sich die Anwendung als Ganzes verhält.

Die beiden gegensätzlichen Testverfahren ergeben in ihrer Kombination eine gesunde Mischung aus Detailansicht und einem Blick aufs Ganze. Je nach Testsubjekt wählt der entwickelnde Admin den einen oder anderen Weg: Für Funktionen und Bibliotheken sowie Syntaxchecks greift er zu Unittests, die er auch problemlos auf dem Entwicklerdesktop ausführt.

Wenn möglich kann der Tester das Programm oder Tool in einem Unittest bereits mit Testdaten füttern, hier würde ein Entwickler schon von einem Integrationstest sprechen.

Unittests im Buildprozess

Damit Tests Vertrauen schaffen, müssen sie regelmäßig laufen, am besten automatisch und als Teil des Buildprozesses. Praktischerweise bietet Linux mit der Paketverwaltung bereits ein einfaches Buildsystem an, Tests lassen sich aber in alle Buildsysteme [2] integrieren. Die folgenden Beispiele für Unittests basieren auf RPM-Paketen und kommen aus der Infrastruktur-Entwicklung bei Immobilienscout24. Alle im Artikel vorgestellten Beispiele helfen beim automatischen Erstellen von Paketen.

Ein RPM-Paket besteht aus einer »SPEC« -Datei und den Quelldateien, einen guten Einstieg in den Eigenbau liefert [3]. Erstere dient als Rezept, um aus den Quellen ein Paket zu schnüren. Als Teil des Bauprozesses kann ein Entwickler dabei beliebigen Code ausführen. Eine typische »SPEC« -Datei folgt in etwa dem Schema aus Listing 1, das alle notwendigen Schritte enthält, um aus Softwarequellen ein Paket zu bauen und es zu installieren. In die Buildphase für das RPM (»%build« ) kann ein Admin die Unittests integrieren. Ein sehr deutliches Beispiel für deren Nützlichkeit ist der automatische Syntaxcheck für »sudo« -Regeln in Listing 2.

Listing 2

sudo-Regeln prüfen

01 [...]
02 BuildRequires: sudo
03
04 %build
05 set -e
06 visudo -c -f sudoers
07
08 %install
09 install -m 0644 sudoers -D %{buildroot}/etc/sudoers.d/%{name}

Listing 1

SPEC-Datei

01 Name: Beispiel
02 Version: 99
03 Requires: foo
04 Source: %{name}-%{version}.tar.gz
05
06 %prep
07 %setup -q
08
09 %build
10 %configure
11 make
12
13 %install
14 install Beispiel %{buildroot}/Beispiel
15 install Beispiel %{buildroot}/Beispiel
16
17 %files
18 %defattr(-, root, root, -)
19 /*

Dieser Test ist wichtig, weil »sudo« bei Syntaxfehlern generell die Mitarbeit verweigert. Ein Admin mit einer defekten »sudoers« -Datei hat sich den Ast abgesägt, auf dem er sitzt, weil er die Zugriffsrechte verliert, um das Problem zu beheben. Steigt das Skript in der Buildphase mit einem Fehler aus, wofür »set -e« sorgt, bricht »rpmbuild« automatisch den Bauprozess ab.

Weitere einfache, aber hilfreiche Tests überprüfen die Syntax von Bash-, Perl- und Python-Skripten, jedoch auch die von Konfigurationsdateien im XML- und Yaml-Format (Listing 3). Jeder einzelne Test erzeugt einen Fehlercode, wodurch das Testskript erfährt, ob er fehlgeschlagen ist. Weitere Beispiele sind automatisierte Tests von Konfigurationen für Apache HTTPD, ISC DHCPD, ISC Bind oder Postfix. All diese Anwendungen bringen bereits Funktionen mit, um eigene Settings zu testen. Selbst aufwändigere Szenarien lassen sich in einem Unittest abbilden, zum Beispiel automatisierte Tests für SSH-Konfigurationen [4].

Listing 3

Syntaxchecks

01 set -e
02 perl -c Skript.pl
03 bash -n Skript.sh
04 pylint -E Skript.py
05 xmllint Datei.xml
06 python -c "import yamlyaml.safe_load (open('Datei.yaml'))"

Diese Automation hat den großen Vorteil, dass sie Quellcode, Buildrezepte und Tests verknüpft. Schlagen die Tests fehl, baut das Buildskript ein Paket gar nicht erst. Die Tests sind stets dabei und das Buildskript führt sie immer mit aus. Arbeitet der Entwickler am Quellcode, fällt es ihm leicht, die Tests gleich mit anzupassen, sodass neue Funktionen sie gleich im Gepäck haben.

Systemtests

Da es bei Systemtests um das große Ganze geht, dauern sie meist etwas länger, beginnen sie doch mit der Installation des Programms auf einem Testsystem. Dort feuert die Testautomation dann verschiedene Checks von außen ab. Fehlerzustände stellt der Admin künstlich her, indem er das Testsystem vor dem Test gezielt manipuliert. Als Falschspieler bieten sich die IPtables-Regeln an, wenn sie alle oder gemeinerweise nur 50 Prozent der Netzwerkpakete verwerfen und so ein Netzwerkproblem simulieren. Eine robuste Anwendung sollte nicht kommentarlos abstürzen, sondern eine Wartungsseite anzeigen, bis die Datenbankverbindung wieder steht.

Ein Systemtest muss auch unbedingt die typischen operativen Aufgaben einbeziehen: einen Dienst starten, seinen Status überprüfen und den Dienst wieder beenden. Ein Reboot zeigt, ob nach dem Neustart wieder alle relevanten Dienste laufen und das System benutzbar ist. Wer sich diesen Test spart, sitzt auf einer tickenden Zeitbombe, die beim nächsten größeren Ausfall im Rechenzentrum den schnellen Wiederanlauf verhindert.

Ist ein Ausfall plötzlich akut, benötigt der Admin eine Möglichkeit, auf dem Testsystem an der bestehenden Automation vorbei beliebige Befehle auszuführen. Dafür empfehlen sich entweder SSH [5] oder sogar RSH [6], falls der Admin den SSH-Schlüsseln blind vertraut.

Von der Software-Entwicklung eingeführt, sind Versionskontrollsysteme wie SVN und Git inzwischen auch im Operations-Bereich fest etabliert. Zusammen mit einem Continuous-Integration-Server (CI) wie etwa Jenkins [7] und einem einfachen Buildskript wie zum Beispiel »svn2rpm« [8] lässt sich eine automatische Pipeline zwischen den Paketquellen, die in einem SVN liegen, und einem Yum-Repository aufbauen.

Der CI-Server steuert dabei alle notwendigen Schritte: Eine Änderung an den Quellen stößt einen Buildjob an, der auf einem dazu bestimmten Rechner, dem Buildagent, läuft. Der Buildjob erstellt die Software direkt im Versionskontrollsystem aus dem Quellcode, wobei auch die Unittests laufen. Dann schiebt er das fertige RPM-Paket zum Testen in ein Yum-Repository.

Schließt der Buildjob seine Aufgabe erfolgreich ab, stößt er einen Testjob für den Systemtest an. Dieser installiert das RPM-Paket auf einem Testserver und lässt die Systemtests dagegen laufen. Sind die erfolgreich, verschiebt der Testjob das RPM-Paket aus dem Test-Repository in ein Produktions-Repository und löst auf dem Produktivserver ein Update mit der neu erstellten und getesteten Software aus. Im Ergebnis entsteht eine Lieferkette (Abbildung 3), die automatisiert die Änderungen oder Aktualisierungen aus den Softwarequellen auf die produktiven Systeme bringt.

Abbildung 3: Eine automatisierte Lieferkette testet die Änderungen, die Admins an den Quelldateien vornehmen, und transportiert die Software dann auf die Produktivsysteme.

Abbildung 3: Eine automatisierte Lieferkette testet die Änderungen, die Admins an den Quelldateien vornehmen, und transportiert die Software dann auf die Produktivsysteme.

Der entscheidende Teil dieser Lieferkette sind die Testphasen. Der Buildprozess führt die Unittests mit aus, die Systemtests sind ein eigener Schritt im automatisierten Prozess.

Mocking

Der Umfang eines Systemtests läuft schnell aus dem Ruder, wenn er versucht, auch noch alle Subsysteme einer Anwendung abzudecken. Der Admin muss dann jeden fehlgeschlagenen Einzeltest aufwändig untersuchen, um herauszufinden, ob der Fehler in einem Subsystem steckt oder in einer der letzten Änderungen.

Mocking ist ein gängiges Verfahren, um einen Systemtest präziser auf die eigene Anwendung zuzuschneiden. Es ersetzt alle für den Test irrelevanten Bestandteile der Anwendung durch sich ähnlich verhaltende, aber funktional nutzlose Komponenten. Diese laufen meist lokal auf demselben Server mit, um die externen Abhängigkeiten möglichst zu reduzieren. Gutes Mocking ersetzt im Test die externe Datenbank durch eine lokale, die exakt definierte Inhalte verwaltet. Oder es wechselt einen externen Anmeldedienst gegen eine lokale Version aus, die immer nur mit “Ja” antwortet.

Schon in der Designphase eines Softwareprojekts sollte sich der Entwickler Gedanken über dessen Eignung für Tests machen. Indem er die unterschiedlichen funktionalen Einheiten geschickt voneinander trennt, kann er sie später besser testen und einfacher geeignete Mocks erstellen. Dahinter steckt die Idee, alle Probleme möglichst klein zu schneiden und sie damit überschaubar zu machen. Denkt der Entwickler erst ans Testen, wenn er den Code bereits umsetzt, macht er es sich wesentlich schwerer. Zwei Beispiele zeigen den Umgang mit Mocking in der Praxis.

Beispiel 1: SAN-Mountservice

Der SAN-Mountservice verwaltet dauerhaften Speicher (persistent Storage), den die bei Immobilienscout24 installierten Systeme verwenden. In der Praxis sieht es so aus, dass alle eingesetzten Linux-Systeme einen Systembereich und bei Bedarf einen Datenbereich erhalten. Da der Installationsdienst den Systembereich automatisch löscht, sobald er ein neues Linux aufsetzt, muss eine Anwendung den persistenten Datenbereich verwenden, um ihre Daten langfristig zu sichern. Letzterer steht dem System entweder als zweite lokale Festplatte oder auch als SAN LUN zur Verfügung.

Technisch ist die Lösung relativ einfach: Der SAN-Mountservice hängt alle Dateisysteme mit einem Label nach dem Schema »persist:/Pfad« in den entsprechenden »/Pfad« ein. Da dieser Dienst sehr früh beim Booten startet, verlassen sich die Anwendungen auf den Persistenzspeicher.

Auch der SAN-Mountservice liegt in Form von RPM-Paketen vor, die – dem zuvor gezeigten Entwicklungsmodell folgend – Unittests und einen Systemtest durchlaufen. Diese Absicherung erlaubt es, das Paket automatisch bauen zu lassen und es nach erfolgreichen Tests direkt in die Produktion zu schieben. Das damit verbundene Risiko ist durchaus hoch, genügt doch bereits ein einfacher Tippfehler, um in der gesamten Produktion die Datenfestplatten zu deaktivieren.

Mocking im Blockdevice

Der SAN-Mountservice selbst ist ein Bash-Skript, das nur ungefähr 200 Zeilen umfasst. Der Unittest besteht in diesem Fall lediglich aus einem einfachen Syntaxcheck mit »bash -n« . Der entscheidende Test ist der Systemtest, der das neu gebaute RPM-Paket auf einem Testsystem installiert. Das Testskript führt über RSH ungefähr die Schritte aus Tabelle 1 aus. Zusätzlich spielt der Test noch einige typische Fehlerszenarien durch.

Tabelle 1

RPM-Testschritte für den SAN-Mountservice

Schritte

Aufgaben

Schritt 1

Eine Datei mit einem Ext-4-Image und dem Label »persist:/testsan« anlegen.

Schritt 2

Die Datei via Loopdevice im System als Blockdevice bereitstellen.

Schritt 3

Den SAN-Mountservice starten: »service san-mount start« .

Schritt 4

Kontrollieren, ob unter »/testsan« ein Dateisystem eingebunden ist.

Schritt 5

Unter »/testsan« eine Datei ablegen.

Schritt 6

Den SAN-Mountservice wieder anhalten: »service san-mount stop« .

Schritt 7

Kontrollieren, ob die eben angelegte Datei tatsächlich vorhanden ist.

Schritt 8

Den SAN-Mountservice erneut starten.

Schritt 9

Kontrollieren, ob die eben angelegte Datei wieder vorhanden ist.

Schritt 10

Den SAN-Mountservice beenden.

Schritt 11:

Das Loopdevice deaktivieren und die Imagedatei löschen.

Das Mocking findet in diesem Beispiel auf der Ebene des Blockdevice statt. Für den SAN-Mountservice kommt es lediglich darauf an, dass der abgesetzte »blkid« -Befehl [9] ein Blockdevice mit einem passend benannten Dateisystem anzeigt. Wie das Blockdevice im Linux-System hängt, interessiert ihn nicht.

Daher eignet sich dieser Test mit dem lokal per Loopdevice bereitgestellten Blockdevice auch für Produktivumgebungen mit einer zweiten Festplatte (als »/dev/sdb« ) oder gar mit SAN LUNs (als »/dev/mapper/Alias-Name« ). Gleichzeitig stellt der Test keine Anforderungen an das Testsystem, sodass hierfür eine gewöhnliche Wegwerf-VM ohne besondere Konfiguration genügt.

Beispiel 2: Proxy-Konfiguration

Auch die Konfiguration für die Squid-Proxys pflegt das Unternehmen des Autors als Softwarepaket, das sich bestens in die Testautomation der Lieferkette einfügt. Jede der etwa 200 Funktionsgruppen, die zur Plattform gehören, möchte in irgendeiner Form mit externen Servern reden. Wer früher das Regelwerk des Proxys ändern wollte, musste ein Ticket an die Produktion schicken, wo die Ops-Kollegen die Konfiguration manuell anpassten. Dieser Prozess erwies sich als fehlerträchtig und langsam. Heute hinterlegt jeder Entwickler selbst die Regeln für seine Anwendung im Konfigurations-RPM und muss sich nur um eine Codereview seiner Änderungen bemühen.

Zusammen mit exemplarischen Testfällen pflegt das Konfigurations-RPM die Regeln für den Proxy. Die Tests decken die benötigten Proxy-Freigaben für jede Funktionsgruppe ab und stellen dadurch sicher, dass eine Änderung an der Konfiguration die Funktion der anderen Systeme nicht beeinträchtigt. Ein Fehler an dieser Stelle würde sofort die ganze Unternehmensplattform beeinträchtigen, denn viele Teile der Webseite binden externe Dienste ein.

Der Systemtest (Abbildung 4) läuft auf einem Testsystem ohne Internetzugang, weil die API-Aufrufe andernfalls bei den Partnern Verwirrung stiften könnten. Das neu gebaute Konfigurations-RPM wandert dann auf das Testsystem, auf dem nun Squid startet.

Abbildung 4: Der Systemtest für die Proxy-Konfiguration reagiert zum Beispiel auf die Fehlermeldung »ERR_ACCESS_DENIED« und bewertet »Bad Gateway« als positives Signal.

Abbildung 4: Der Systemtest für die Proxy-Konfiguration reagiert zum Beispiel auf die Fehlermeldung »ERR_ACCESS_DENIED« und bewertet »Bad Gateway« als positives Signal.

Mit Hilfe des »X-Forwarded-For« -Headers setzt das Testskript für jeden HTTP-Aufruf eine IP-Adresse aus dem Bereich der zuständigen Server ein. Erlaubt die Proxy-Konfiguration den Aufruf, antwortet der Proxy mit einer »502 Bad Gateway« -Meldung, weil das Testsystem nicht im Internet ist. Diese Fehlermeldung zählt hier als Erfolg: Wäre der Aufruf nicht erlaubt, würde der Proxy mit einer »403 Forbidden« -Meldung reagieren. Daher zählt die »ERR_ACCESS_DENIED« -Fehlermeldung in Abbildung 4 als Fehler, alles andere als Erfolg.

Das Mocking findet hier also rund um den Proxyserver statt. Der Test schaltet den »X-Forwarded-For« -Header frei, der normalerweise nur für Load Balancer von Bedeutung ist. Da der Test lediglich das Regelwerk im Proxy überprüft, kommt es nicht darauf an, ob der Proxy die aufgerufene Seite überhaupt lädt. Die Trennung vom Internet beschleunigt den Test sogar, weil der Proxy keine Inhalte laden muss.

Continuous Live Deployment

Wer diese Arbeitsweise konsequent für alle Anwendungen umsetzt, egal ob sie in der Entwicklung oder im Operations-Bereich laufen, kann trotz permanenter Änderungen hohe Stabilität im Betrieb erreichen. Admins müssen die Entwicklungszyklen der unterschiedlichen Pakete dabei nicht synchronisieren, schließlich bringt jede Anwendung ihre eigenen Tests mit (Abbildung 5). Hat eine Anwendung diese bestanden, landet sie in der Produktion. Dort laufen regelmäßig Aktualisierungswellen und bringen die Server auf den neuesten Stand.

Abbildung 5: Im Continuous Live Deployment lassen sich Anwendungen unabhängig voneinander ausliefern.

Abbildung 5: Im Continuous Live Deployment lassen sich Anwendungen unabhängig voneinander ausliefern.

Überträgt ein Unternehmen diese Arbeitsweise auf seine gesamte Plattform, spricht man von Continuous Live Deployment [10]. Diese Methode hilft den Teams ihre Änderungen möglichst schnell in die Produktion zu bringen.

Getreu dem Motto “Never change a running System” galten Software-Deployments früher als gefährliche Aktion. Heute verlässt man sich eher auf Tests. Die reduzieren das Risiko sowohl beim Entwickeln als auch bei der Inbetriebnahme und schaffen im Devops-Bereich eine Basis gegenseitigen Vertrauens zwischen Entwicklern und Admins.

Infos

  1. Testgetriebene Entwicklung: http://de.wikipedia.org/wiki/Testgetriebene_Entwicklung
  2. Bernhard Bablok, Markus Feilner, “Baumeister”: Linux-Magazin 01/11 , S. 48, https://www.linux-magazin.de/Ausgaben/2011/01/Buildsysteme
  3. Maximum RPM: http://www.rpm.org/max-rpm-snapshot/
  4. Automatisierte Konfigurationstests für Open SSH: http://blog.schlomo.schapiro.org/2014/04/automated-openssh-configuration-tests.html
  5. Marco Woitschitzky, Schlomo Schapiro, “Schließanlage”: Linux-Magazin 08/13, S. 38, https://www.linux-magazin.de/Ausgaben/2013/08/SSH-Key-Management
  6. RSH in Skripten einsetzen: http://blog.schlomo.schapiro.org/2013/12/rsh-pitfall-remote-exit-code.html
  7. Continuous-Integration-Server Jenkins : http://jenkins-ci.org
  8. Svn2rpm: https://github.com/ImmobilienScout24/svn2rpm
  9. Handbuch zu »blkid« : http://linux.die.net/man/8/blkid
  10. Continuous Live Deployment: http://guide.agilealliance.org/guide/cd.html
  11. Konferenzvortrag und Video zum Artikel:http://www.slideshare.net/schlomo/osdc-2014-test-driven-infrastructure, auch auf der DELUG-DVD
  12. Markus Feilner, “Endlose Stufen”: Linux-Magazin 08/2012, S. 42, https://www.linux-magazin.de/Ausgaben/2012/08/Rollout-Testing
DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 5 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Nach oben