Rangierbahnhof
Größere Webprojekte will niemand Datei für Datei auf den oder die Server kopieren. Im Ruby-on-Rails-Umfeld ist Capistrano das Softwareverteilungswerkzeug der Wahl.
- Ralf Wirdemann
- Thomas Baustert
Nicht zuletzt wegen des Webframeworks Ruby on Rails, kurz Rails oder RoR, erfreut sich die Skriptsprache Ruby wachsender Beliebtheit. Auf dem Entwicklungsserver erzeugte Anwendungen sollen irgendwann auf dem externen Webserver (oder mehreren) landen. FĂĽr dieses sogenannte Deployment gibt es ein frei verfĂĽgbares Werkzeug.
Capistrano heiĂźt die Software fĂĽr das automatisierte AusfĂĽhren von Aufgaben auf einem oder mehreren entfernten Servern. Sie gilt heute als das gefragteste Deployment-Tool fĂĽr Rails-Anwendungen. Zum Zeitpunkt, da dieser Artikel erscheint, dĂĽrfte Version 2.0 vorliegen, weswegen dieser Artikel schon die neue Fassung behandelt.
Zentrales Prinzip ist die vollständige Automatisierung des Verteilungsprozesses. Dass dies Sinn ergibt, erkennt man spätestens, wenn man sich die Einzelschritte eines typischen Verteilungsvorgangs vor Augen führt:
- Auschecken der Software aus der Versionskontrolle,
- AusfĂĽhren der Unit-Tests,
- Ăśbertragung der Software auf die Ziel-Server,
- Aktualisierung der Datenbanken und
- Neustart des Webservers beziehungsweise der Applikationsprozesse.
Die manuelle Ausführung der angeführten Schritte gestaltet sich zeitintensiv, fehleranfällig und darüber hinaus langweilig. Bedenkt man außerdem, dass Deployment im Rahmen eines agilen Entwicklungsprozesses relativ häufig stattfindet, erscheint die Automatisierung des Prozesses umso sinnvoller.
Genau dies hat sich Jamis Buck von der Firma 37signals gedacht, als er 2005 die erste Version von Capistrano (damals noch unter dem Namen Switchtower) entwickelte. Er hat Capistrano zunächst für interne Zwecke zum Deployment von Basecamp entwickelt, einer Rails-basierten Projektmanagementsoftware. Allerdings wurde schnell deutlich, dass Capistrano für andere Rails-Anwendungen ebenfalls nützlich ist, sodass die Firma Capistrano als eigenständiges Deployment-Tool veröffentlicht hat.
Automatisierung auf Knopfdruck
Capistrano setzt auf die sogenannte „Commanded Automation“ (siehe das Buch von Mike Clark, [3]), eine Form der Automatisierung, die, einmal vom Benutzer angestoßen, ohne weitere Interaktion abläuft. Der Start von Capistrano erfolgt auf dem lokalen Entwicklungsrechner durch den Kommandozeilenaufruf: cap deploy. Der Befehl bewirkt, dass sich Capistrano per Secure Shell (SSH) auf die verschiedenen Rechner der Deployment-Umgebung verbindet, um dort die für den Rechner vorgesehenen Einzelschritte durchzuführen.
Angenommen, die Umgebung besteht aus einem Web-, einem Applikations- und einem Datenbankserver (siehe obige Abbildung „Deployment-Umgebung“). Web- und Applikationsserver unterscheiden sich voneinander, weil auf Ersterem der HTTP-Prozess (beispielsweise Apache) und auf den Applikationsservern die eigentliche Rails-Anwendung (etwa in Form von Fast-CGI-Prozessen) läuft. Ein typischer Deployment-Vorgang mit Capistrano läuft so ab:
- Der Entwickler checkt seine Quellcodeänderungen ins Subversion-Repository ein und startet Capistrano.
- Dieser weist den Applikationsserver an, die Quellen der Anwendung auszuchecken.
- Aktualisierung des Datenbankschema auf dem DB-Server.
- Neustart der Applikationsprozesse (beispielsweise Fast-CGI oder Mongrel) auf dem Applikationsserver.
FĂĽr jeden Einzelschritt gilt, dass das Deployment-Tool sich per SSH mit dem jeweiligen Server verbindet und ihm mitteilt, welches Kommando er durchfĂĽhren soll. Deutlich zeigt dies die Quellcodeaktualisierung: Es werden nicht die auf dem lokalen Entwicklungsrechner befindlichen Sourcen verteilt, sondern Capistrano teilt dem Applikationsserver stattdessen per SSH mit, dass er die aktuellen Sourcen direkt aus dem Subversion-Repository auschecken soll. AuĂźer Subversion unterstĂĽtzt das Werkzeug CVS und Darcs.
Selbst in Ruby entwickelt, lässt sich Capistrano leicht mit Gem, dem Packaging-System der Sprache, installieren: sudo gem install capistrano - -include-dependencies. Anschließend kann man ein Rails-Projekt für das Deployment vorbereiten: capify . <projektname> (hier zeigt sich ein Unterschied zu Capistranos Vorgängerversion, in der es cap -apply-to . projektname hieß). Der Aufruf erzeugt das Capfile im Rootverzeichnis sowie die zentrale Konfigurationsdatei deploy.rb im Verzeichnis config des jeweiligen Projekts.
Rezepte fĂĽrs Verteilen
Die Capistrano-Konfiguration eines Rails-Projekts regelt ein sogenanntes Deployment-Rezept. Dabei handelt es sich um die oben genannte Datei deploy.rb, die standardmäßig im Verzeichnis config liegt. Als Pflichtangaben erwartet Capistrano den Anwendungsnamen sowie die URL des zu verwendenden Subversion-Repositorys. Beide Angaben erfolgen durch Aufruf der Methode set, die als Parameter einen Variablennamen, gefolgt von einem Wert, erwartet:
set :application, "ontrack"
set :repository, "http://svn.b-simple.de/#{application}/trunk"
Außer diesen beiden Pflichtangaben enthält die Konfigurationsdatei eine Rollenzuordnung der Deployment-Server. Capistrano unterscheidet zwischen den Rollen „web“ für Webserver, „app“ für Applikationsserver und „db“ für Datenbankserver. Die Aufteilung ist notwendig, da sich die auf den Rechnern durchzuführenden Deployment-Aufgaben voneinander unterscheiden. Beispielsweise ist die Migration der Datenbank nur auf dem Datenbankserver sinnvoll, während die FCGI-Prozesse nur auf den Applikationsservern neu gestartet werden müssen. Die Zuordnung von Servern zu Rollen erfolgt über die Methode „role“:
role :web, "www.b-simple.de"
role :app, "ontrack.b-simple.de", "ontrack2.b-simple.de"
role :db, "db.b-simple.de"
Eine weitere wichtige Variable ist deploy_to, die das Zielverzeichnis des Deployments auf dem Applikationsservern angibt: set :deploy_to „/home/rails“.
Schaltzentrale von Capistrano ist das Kommandozeilenprogramm cap. Es läuft auf dem lokalen Entwicklungsrechner und erwartet als Parameter eine Task, die die durchzuführenden Aktionen bestimmt. Häufig verwendete Tasks sind deploy, deploy:migrations (in der früheren Version deploy_with_migrations) oder deploy:rollback. Der Aufruf cap -T liefert eine Übersicht der vorhandenen Standardaufgaben. Die Erweiterung um eigene Tasks ist einfach umzusetzen.
Einzelschritte der Verteilung
Nach erfolgter Basiskonfiguration soll das initiale Deployment erfolgen. Es beginnt mit dem Erzeugen der Verzeichnisstruktur auf den Applikationsservern durch Ausführung des Kommandos cap deploy:setup im Wurzelverzeichnis des jeweiligen Projekts. Der Aufruf erzeugt auf allen Applikationsservern (alle in der Rolle „app“) unterhalb des durch deploy_to spezifizierten Verzeichnisses (hier /home/rails) die Capistrano-typische Struktur:
meinprojekt/releases
meinprojekt/shared
Im Verzeichnis releases legt das Tool fĂĽr jedes Deployment ein Unterverzeichnis an, in das die aktuellen Sourcen der Anwendung ausgecheckt werden. Das Verzeichnis shared dient als Ablage fĂĽr Dateien und Verzeichnisse, die fĂĽr alle Deployments gleich sind, wie die Datenbankkonfiguration oder Logfiles. Das stellt unter anderem sicher, dass unterschiedliche Releases weiterhin in dasselbe Logfile schreiben.
Der zweite Schritt beinhaltet das Auschecken der aktuellen Sourcen: cap deploy:update_code. Die Task erzeugt unterhalb von releases ein Unterverzeichnis, benannt nach Datum und Uhrzeit, und checkt die aktuellen Sources dorthin aus:
meinprojekt/releases/20070303103303/app
/config
...
Die aktuelle Version der Anwendung verwaltet Capistrano ĂĽber den symbolischen Link current, den der Aufruf cap deploy:symlink erzeugt: meinprojekt/current -> meinprojekt/releases/20070303103303. Die Verwendung eines symbolischen Links stellt sicher, dass man die Webserverkonfiguration nicht nach jedem neuen Deployment aktualisieren muss. Stattdessen nimmt der Server als DocumentRoot einfach das Verzeichnis meinprojekt/current an, sodass immer die aktuelle Version der Anwendung zur AusfĂĽhrung kommt.
Abschließend muss Capistrano noch den Webserver und die zugehörigen Applikationsprozesse starten. Außerdem muss das Werkzeug sicherstellen, dass auf dem Datenbankserver die benötigte Datenbank angelegt und mit dem aktuellen Schema initialisiert ist. Auch hier bietet das Werkzeug mit der Task deploy:migrate Unterstützung.
Während die initiale Verteilung aus einer Reihe von Einzelschritten besteht (die Task deploy:cold fasst diese zu einem Schritt zusammen), erweist sich der Nutzen des Tools erst bei nachfolgenden Deployment-Schritten so richtig: cap deploy. Diese Task führt sämtliche Einzelschritte ohne weiteres Zutun durch den Benutzer aus. Hat sich das Datenbankschema seit dem letzten Deployment geändert, so ist statt deploy deploy:migrations zu verwenden. Diese Task bringt zusätzlich zu den in deploy enthaltenen Aktionen die Datenbank des Datenbankservers auf den neuen Stand.
Rollback in Sekundenschnelle
Wer kennt sie nicht, die Situation, dass jemand „mal eben“ einen Hotfix verteilt, ohne die Anwendung zuvor ausreichend getestet zu haben? Schnell stellt sich heraus, dass irgendetwas übersehen wurde und beispielsweise ein erst für die nächste Release vorgesehenes Feature in die Anwendung gerutscht ist, obwohl es noch gar nicht funktioniert. Kein Problem für Capistrano, denn dank des Rollback-Mechanismus lassen sich durchgeführte Deployments innerhalb von Sekunden zurücknehmen: cap deploy:rollback.
Das Prinzip ist einfach: Capistrano hängt den symbolischen Link current einfach auf die Vorgängerversion im releases-Verzeichnis, löscht die aktuelle Version daraus und startet die Applikationsprozesse auf den Applikationsservern durch.
Etwas aufwendiger gestaltet sich das Zurückrollen, wenn zwischen dem aktuellen und dem vorherigen Deployment eine Datenbankänderung stattgefunden hat. In diesem Fall muss Capistrano zusätzlich das Datenbankschema auf die Vorgängerversion zurücksetzen. Spätestens hier zahlt sich die Verwendung von Active Record Migrations aus. Ein einfaches rake db:migrate unter Angabe der Versionsnummer genügt, die Datenbank zurückzusetzen. Dieser Schritt erfolgt entweder direkt auf dem Datenbankserver oder per Capistrano.
Erweiterung durch eigene Tasks
Capistrano lässt sich durch die Definition eigener Tasks erweitern. Sie werden in Ruby programmiert und bestehen im Wesentlichen aus dem Aufruf der Methode task:
desc "Print the current ruby version"
task :which_ruby, :roles => [:app] do
run ,ruby -v'
end
task erwartet als Parameter einen Namen, ein Array mit Rollen sowie einen Block mit den eigentlichen Anweisungen. Der Block kann beliebigen Ruby-Code enthalten, besteht aber häufig aus einem oder mehreren Aufrufen des Task-Helpers run. Die Methode führt den übergebenen String auf den Servern der angegebenen Rollen aus. Die Ausgaben des Kommandos überträgt die Methode zurück an den Client, den Entwicklungsrechner und gibt sie auf der Shell aus.
Fazit
Mit Erscheinen dieses Artikels dürfte Capistrano in der Version 2.0 verfügbar sein. Neben Bugfixes und Refactorings enthält die neue Version eine Reihe häufig geforderter Features wie Namespaces und verschiedene Deployment-Strategien. Insbesondere Letzteres klingt interessant, da hierdurch neben SCM-basierten Deployments die Übertragung der Projektquellen via sftp möglich sein wird, was einige Firewall-Schwierigkeiten der Vergangenheit beseitigen dürfte.
Capistrano vereinfacht das Deployment von Rails-Anwendungen in hohem Maße, spart Zeit und vermeidet Fehler. Einmal korrekt aufgesetzt, reduziert sich der Vorgang auf ein einziges Kommando, unabhängig davon, wie viele Server in der Umgebung in Zukunft hinzukommen. Entwickler können lästige und manuell auszuführende Deployment-Vorschriften vergessen und haben den Kopf frei für wichtige Dinge.
Ralf Wirdemann, Thomas Baustert
sind Software-Coaches und unterstĂĽtzen Unternehmen bei der DurchfĂĽhrung von Ruby-on-Rails-Projekten.
Literatur
[1] Ralf Wirdemann, Thomas Baustert; Rapid Web Development mit Ruby on Rails; 2. Auflage; MĂĽnchen, Wien (Hanser) 2007
[2] Dave Thomas, David Heinemeier Hansson; Agile Web Development with Rails; Second Edition; Pragmatic Bookshelf 2006
[3] Mike Clark: Project Automation; Pragmatic Bookshelf 2004 (hb)