PHP-Deployment mit Capistrano und Git
Am Beispiel Drupal, Github und Uberspace.
Deployment, das ist der Prozess des »auf dem Server einrichtens«. Wäre es nicht schön, wenn man statt des aufwändigen manuellen Vorgangs einfach mit einer Zeile im Terminal die Computer für sich arbeiten zu lassen? Wäre es nicht noch schöner, wenn dabei gleichzeitig die Zeit in der die Website nicht erreichbar ist, minimiert wird? Und wäre es nicht das Sahnehäubchen, wenn wir bei Problemen binnen Sekunden wieder zum alten Stand zurückspringen können?
Wer den Titel gelesen hat, oder sich in Ruby/Rails-Gefilden auskennt, weiss,
dass die Antwort Capistrano ist: cap deploy in die
Kommandozeile, fertig.
TL;DR
Capistrano ist ein Kommandozeilen-Werkzeug für Deployments und ist so flexibel, dass es für fast jede Umgebung geeignet ist. Unsere Umgebung hier: Drupal, Github, Uberspace.
- Wir benötigen Ruby und RubyGems.
- Capistrano via RubyGems installieren:
$ gem install capistrano. - Im Projekt-Ordner:
$ capify .. - Inhalt der
config/deploy.rbmit dem fertigen Rezept austauschen, Pfade, Zugangsdaten usw. anpassen. - Deployment auf dem Server vorbereiten:
$ cap deploy:setup. - Server ggf. Konfigurieren. Das Verzeichnis
currentbeinhaltet die aktuelle Version der Seite. - Weiterarbeiten und deployen:
$ cap deploy.
Was ist Capistrano?
„Capistrano is a developer tool for deploying web applications.“
Und ein sehr flexibles noch dazu. Seinen Ursprung hat es in der Rails/Ruby-Gemeinde und es ist so flexibel, dass es ohne Probleme in fast jeder Umgebung eingesetzt werden kann. In unserem konkreten Fall ist das Drupal 6, Github und ein Live-Server bei Uberspace.
Vorraussetzungen und Einrichtung
Für unsere Vorgehensweise gehen wir davon aus, dass wir SSH-Zugriff auf den Server haben und uns mit SSH-Keys dort einloggen.
Capistrano selbst ist am einfachsten als RubyGem zu installieren:
$ gem install capistrano
Anmerkung: Zeilen, die mit einem $ beginnen sind für die Kommando-Zeile
bestimmt.
Natürlich muss dafür Ruby und auch RubyGems auf dem System installiert sein. Ruby ist glücklicherweise auf vielen UNIXoiden Systemen (bei uns MacOS X) bereits installiert, und RubyGems sind fix installiert. Das installiert dann auch gleich alles Andere mit, was von Capistrano benötigt wird. Auf dem Server ist weder RubyGems noch Ruby erforderlich.
Allerdings werde ich nicht groß auf irgendwelche syntaktischen Eigenarten von Ruby eingehen, bei fragen, schreibt mir einfach auf Twitter (@nerdismus). Zum Glück ist ruby eine sehr leserliche Sprache und Vieles ist selbsterklärend.
Falls noch nicht geschehen, wechseln wir nun in das Verzeichnis, in dem unsere Webseite liegt und initialisieren Capistrano:
$ cd ~/sites/beispielSeite
$ capify .
[add] writing './Capfile'
[add] making directory './config'
[add] writing './config/deploy.rb'
[done] capified!
Capistrano legt also eine Datei Capfile im aktuellen Verzeichnis und eine
Datei deploy.rb im Unterordner config an. Die deploy.rb ist die Datei,
für die wir uns jetzt besonders interessieren. Die löschen wir nämlich erstmal.
Capistrano Rezept
Denn das Meiste aus der deploy.rb brauchen wir nicht. Also legen wir eine
neue an und konfigurieren Capistrano:
set :domain , "vortrieb.net"
set :user , "vortrieb"
set :repository , "git@github.com:Vortrieb/Vortrieb.net.git"
set :scm , :git # git statt svn benutzen
set :branch , "master" # der git-branch, der deployt werden soll
set :use_sudo , false # auf uberspace dürfen wir natürlich kein
# sudo benutzen.
Das dürfte ziemlich selbsterklärend sein. Zu beachten ist allerdings, dass
:domain und :user zum Verbindungsaufbau per SSH benutzt werden.
Da wir uns mit unseren SSH-Keys einloggen wollen, um nicht bei jedem Deployment ein Passwort eingeben zu müssen, sagen wir Capistrano, dass es die Keys benutzen soll:
ssh_options[:forward_agent] = true
Und natürlich muss Capistrano noch wissen wo es die Dateien hinschieben soll.
set :deploy_to, "/var/www/virtual/#{user}/"
role :website, domain, :primary => true
Die zweite Zeile lässt bereits vermuten wie mächtig Capistrano ist. Es ist
nämlich überhaupt keine großes Sache Capistrano in komlexeren Umgebungen mit
verschiedenen Servern und/oder Datenbanken einzusetzen. Da wir es hier aber mit
nur einem Server zu tun haben, reicht es eine Rolle :website zu definieren
und als Standard festzulegen.
Als nächstes bereiten wir das Deployment auf dem Server vor.
Deployment vorbereiten
Capistrano arbeitet mit 3 Ordnern auf dem Server: current, shared und
releases. Naja nicht so ganz. current ist ein Symlink auf den aktuellsten
bzw. aktiven Release im releases-Ordner. Das hat den unglaublichen Vorteil,
dass beim deployment die Ausfall-Zeit der Seite praktisch auf 0 reduziert wird.
Das eigentliche Deployment erfolgt in den releases-Ordner. Für jede
veröffentliche Version wird darin ein weiterer Ordner mit dem aktuellen Datum
als Namen angelegt. Erst wenn alle Daten vollständig hochgeladen wurden, wird
der Symlink auf das neue Release geändert.
Ein weiterer Vorteil durch diese Arbeitsweise, der wirklich Gold wert ist, sind
Rollbacks. Gibt es irgendwelche Probleme mit der neuen Version, kann man
schnell zum vorherigen Release zurückspringen. Wieviele Releases gespeichert
werden sollen, könnten wir mit set :keep_releases, 5 festlegen. 5 ist der
Default-Wert und für uns völlig ausreichend.
Der shared-Ordner ist für gemeinsame Daten unter den Releases gedacht. Also
zum Beispiel die User-Uploads, die bei Drupal in sites/default/files liegen
und vom Deployment unangestatstet bleiben sollen.
Darum kümmern wir uns aber erst später.
$ cap deploy:setup
* executing `deploy:setup'
* executing "mkdir -p /var/www/virtual/vortrieb/test /var/www/virtual/vortrieb/test/releases /var/www/virtual/vortrieb/test/shared /var/www/virtual/vortrieb/test/shared/system /var/www/virtual/vortrieb/test/shared/log /var/www/virtual/vortrieb/test/shared/pids"
servers: ["vortrieb.net"]
[vortrieb.net] executing command
command finished in 80ms
* executing "chmod g+w /var/www/virtual/vortrieb/test /var/www/virtual/vortrieb/test/releases /var/www/virtual/vortrieb/test/shared /var/www/virtual/vortrieb/test/shared/system /var/www/virtual/vortrieb/test/shared/log /var/www/virtual/vortrieb/test/shared/pids"
servers: ["vortrieb.net"]
[vortrieb.net] executing command
command finished in 88ms
Mit diesem Befehl lassen wir uns von Capistrano die notwendigen Ordner anlegen
und prüfen gleichzeitig, ob wir bisher alles richtig gemacht haben. Auf dem
Server müssten nun die Ordner shared und releases existieren.
Ist dies der Fall, könnten wir eigentlich schon fröhlich unsere Seite mit $
cap deploy auf den Server schieben. Das sollte auch funktionieren und wer mag,
kann das auch mal ausprobieren.
Aber wie bereits angemerkt, müssen wir uns noch um die gemeinsamen Dateien kümmern – und ein wenig vom Standard-Verhalten eliminieren.
Capistrano Tasks
Das Herzstück von Capistrano sind die Tasks, mit denen wir das Deployment an unsere Umgebung anpassen können. Ein Deployment lässt sich in kleine Schritte aufteilen: "Neues Release herunterladen", "Zugriffsrechte einstellen", "Datenbank migrieren", "Symlinks aktualisieren", "Server Neustarten", "Cache leeren" und was nicht noch alles.
Capistrano hat einige Standard-Tasks, die vor allem für Rails gedacht sind. Das ist eigentlich kein Problem, erzeugt für uns aber ein paar unschöne Fehlermeldungen. Wir überschreiben diese Tasks also:
namespace :deploy do
task :set_permissions, :except => { :no_release => true } do
# noop
end
task :restart do
# noop
end
task :start do
# noop
end
end
Wer nicht so vertraut mit Ruby ist, wird das hier vermutlich etwas ungewöhnlich
finden. Die do … end-Teile liessen sich auch { … } schreiben. Das sind sind
sogenannte Blöcke, ein mächtiges Werkzeug aus der funktionalen Programmierung,
das in Ruby quasi allgegenwärtig ist (Stichwort: Closures).
Ich erkläre hier jetzt mal nicht genauer, wie die Blöcke funktionieren, wir wollen ja heute auch noch fertig werden. Was wir hier machen ist dagegen ziemlich simpel: wir überschreiben die Default-Tasks, die zu Fehlern führten und lassen sie einfach gar nichts machen.
Unsere speziellen Tasks für Drupal fassen wir in einem eigenen Namespace zusammen:
namespace :drupal do
end
Zunächst fügen wir einen Task ein, der uns einen Unterordner files im
shared-Ordner anlegt.
namespace :drupal do
task :setup do
run "mkdir -p #{shared_path}/files"
end
end
shared_path enthält den Pfad zum shared-Ordner. Diesen Befehl können wir
nun ausführen:
$ cap drupal:setup
* executing `drupal:setup'
* executing "mkdir -p /var/www/virtual/vortrieb/test/shared/files"
servers: ["vortrieb.net"]
[vortrieb.net] executing command
command finished in 80ms
Wir können uns übrigens die verfügbaren Befehle mit $ cap -T anzeigen lassen.
Jetzt kümmern wir uns noch um den sites/default/files-Ordner.
Wir müssen nach jedem Deployment einen Symlink in sites/default des aktuellen
Releases anlegen, der auf den shared/files-Ordner zeigt. Das klingt zum Glück
komplizierter als es ist:
task :symlink do
run "ln -s #{shared_path}/files #{latest_release}/sites/default/files"
end
Natürlich wollen wir das nicht jedesmal von Hand eintippen, sondern das soll
automatisch bei jedem Deployment erfolgen. Dazu fügen wir ausserhalb von
namespace :drupal do … end folgende Zeilen ein:
after 'deploy:symlink' , 'drupal:symlink'
after 'deploy:setup' , 'drupal:setup'
Nach jedem deploy:symlink, soll also auch ein drupal:symlink gemacht
werden. Man kann es sich fast denken, before gibt es natürlich auch.
Fertig.
Bonus Level: Drupal Cache Leeren
Es liegt auf der Hand, dass man mit Capistrano auch andere Aufgaben, die nicht
zwingend zum Deployment gehören, automatisieren lassen kann. Wir haben gesehen,
dass wir mit run "command" ganz einfach Befehle auf dem Server ausführen
können – warum nicht einen Schritt weiter gehen und zum Beispiel mit
Drush den Cache auf dem Server leeren?
Achtung: wenn Du noch nie von $PATH gehört hast und nicht weisst, was es
damit auf sich hat, dann könnte der folgende Teil etwas schwer zu verstehen
sein. Oder gehe direkt zur Zusammenfassung
Wenn Drush auf dem Server eingerichtet ist, geht das ganz einfach:
task :clear_cache do
run "drush cc all"
end
$ cap drupal:clear_cache
* executing \`drupal:clear_cache'
* executing "drush cc all"
servers: ["vortrieb.net"]
[vortrieb.net] executing command
*** [err :: vortrieb.net] sh: drush: command not found
command finished in 82ms
failed: "sh -c 'drush cc all'" on vortrieb.net
Hoppla. drush wurde nicht gefunden. Ganz so einfach ist es also doch nicht?
Das Deployment soll natürlich möglichst schnell gehen, deswegen wird eine
.bashrc oder .zshrc nicht geladen, sodass unser $PATH eventuell ziemlich
leer ist und drush folglich nicht gefunden werden kann.
Aber zum Glück haben die Capistrano-Leute auch daran gedacht.
set :default_environment, {
'PATH' => "$HOME/bin/:/package/host/localhost/php-5/bin/:$PATH:"
}
Die Drush-bin liegt bei uns in ~/bin/ und wir brauchen für Drush mindestens
PHP 5.2.0. Wir fügen also zusätzlich noch den Pfad zur aktuellen PHP-Version
hinzu, die bei Überspace in /package/host/localhost/php-5/bin/ liegt.
Das reicht leider noch immer nicht. drush muss im Root-Ordner einer
Drupal-Instanz ausgeführt werden und das Working-Directory ist das, was wir
unter :deploy_to angegeben haben. Also müssen wir noch ins Verzeichnis des
aktuellen Releases wechseln.
Also noch einmal:
task :clear_cache do
run "cd #{latest_release} && drush cc all"
end
$ cap drupal:clear_cache
* executing `drupal:clear_cache'
* executing "ls -x /var/www/virtual/vortrieb/test/releases"
servers: ["vortrieb.net"]
[vortrieb.net] executing command
command finished in 80ms
* executing "cd /var/www/virtual/vortrieb/test/releases/20111208131850 && drush cc all"
servers: ["vortrieb.net"]
[vortrieb.net] executing command
** [out :: vortrieb.net]
*** [err :: vortrieb.net] 'all' cache was cleared [success]
Und um das ganze abzuschliessen, leeren wir den Cache nun nach jedem Deployment.
after 'deploy', 'drupal:clear_cache'
Zusammenfassung
Wir können nun ganz normal mit unserer Versionsverwaltung weiterarbeiten und
mit $ cap deploy die aktuelle Version unserer Drupal-Seite aus dem
Master-Branch auf unseren Server schieben lassen.
Bei jeder neuen Drupal-Seite müssen wir nun lediglich ein paar Werte in unserer
deploy.rb anpassen und schon können wir Capistrano auch dort benutzen. Und
wir haben gesehen, dass es überhaupt kein Problem ist das Deployment so
anzupassen, dass wir Capistrano praktisch mit jedem CMS, Framework oder
dergleichen einsetzen können.
Die komplette deploy.rb mit erklärenden Kommentaren gibt es auf
gist.github.
Weiterführende Links
- Capistrano auf Github
- Das fertige Recipe
- Railsless Capistrano: entfernt alle Railsisms von Capistrano.-
- Default Behaviour visualisiert.
- Capistrano Dokumentation
- Ruby
- RubyGems
- Drush: Command-Line shell für Drupal

Hallo Nils,
ein sehr schöner Artikel. Super.
Kommentar hinzufügen