Deploying WordPress mit Git und Capistrano

Deploying WordPress mit Git und Capistrano 1

Bereits 2011 hatte Mark Jaquith auf dem WordCamp in San Francisco in seinem Vortrag Capistrano erwähnt. Dabei stellte er fest, dass das Publizieren von Web-Anwendungen mit Hilfe von Capistrano auf unterschiedlichen Servern zu Test- und Entwicklungszwecken einfach und zugleich flexibel erweiterbar abgewickelt werden kann.

Nicht selten kommt es bei Großprojekten vor, dass die Serverlandschaft ausgebaut wird, um den aktuellen Ansturm an Traffic zu bewältigen. Selbst bei kleinen bis mittelgroßen Projekten arbeiten wir bei Inpsyde mit folgender Konstellation:

  • Lokale Entwicklungsumgebung
  • DEV-Server (gemeinsamer Entwicklungsserver)
  • Stage/Test-Server (sollte jeder haben! Kundenfreigabe)
  • Live-Server {n}

Was ist Capistrano?

Capistrano ist ein Kommandozeilen-Programm für die Bereitstellung von Web-Anwendungen auf einem oder mehreren Servern. Es wurde in erster Linie für Anwendungen mit Ruby on Rails entwickelt, funktioniert aber für alle Arten von Web-Anwendungen, darunter natürlich auch WordPress.

Eine typische Anwendung wie eine WordPress-Seite wird normal in einem Versionierungsprogramm (GIT, SVN, CVS …) verwaltet. Nachdem lokal alles entwickelt und getestet worden ist, loggt man sich per SSH sich auf dem Server ein und spielt die Änderungen aus dem Repository des Versionierungsprogramms ein.

An diesem Punkt der Entwicklung kommt Capistrano ins Spiel. Nach einer kurzen Konfiguration wickelt das Programm mit einem einzigen Befehl folgenden Ablauf standardmäßig ab:

  1. Einloggen per SSH auf dem Server
  2. In das richtige Verzeichnis wechseln
  3. Änderungen aus dem Repository einspielen.

Zusätzlich findet automatisch eine Versionierung der verschiedenen Deploys im Dateiverzeichnis statt. Somit können wir jederzeit auf eine ältere Version zurückspringen. Auch kann der Ablauf um weitere Automatismen erweitert werden.

Installation Capistrano

Da Capistrano in Ruby geschrieben ist, muss Ruby auf der lokalen Arbeitsumgebung installiert sein. Die Scripte und Konfigurationsdateien sind ebenfalls in Ruby zu schreiben. Aber schon mit geringen Programmierkenntnissen ist das recht einfach.

Mit dem Befehl ruby --version kann über die Konsole herausgefunden werden, ob Ruby bereits installiert ist. In einigen Linux-Distributionen ist Ruby bereits vorinstalliert, ansonsten muss dies per Paket-Manager über den Befehl apt-get install ruby (in Debian) noch nachgeholt werden. Für Windows und Mac OS X I’ll können die folgenden Installer verwendet werden:

Ruby kommt mit einem eigenen Paket-Manager namens gem. So kann Capistrano anschließend mit folgenden Befehlen lokal über die Konsole installiert werden:

gem install capistrano
gem install railsless-deploy
gem install capistrano-ext

Das erste Paket ist das Hauptprogramm Capistrano, mit dem railsless-deploy-Paket kann man auch Nicht-Rails-Anwendungen per Capistrano deployen. Das dritte Paket liefert noch einige nützliche Erweiterungen zu Capistrano um z.B. “Staging” zu ermöglichen.

Anschließend sollte per

cap -V 

überprüft werden, ob die Installation erfolgreich war. Folgende Meldung müsste dann erscheinen:

Capistrano v2.15.4

Projektstruktur in Git

Für unsere Kunden-Projekte haben wir nur den wp-content-Ordner im Git-Repository. Der uploads-Ordner wird dabei nicht mit ins Git eingecheckt, denn für dynamische Daten haben wir in Capistrano-Projekten einen extra Ordner. Unser Git-Repository sieht also folgendermaßen aus:

the_user / the_repo_name:
	languages
	plugins
	mu-plugins
	themes
	  the_theme
	...
	index.php

Projektstruktur auf dem Server

Als Projektstruktur auf dem Server bietet sich folgende an:

your-project
deployments
	current
	releases
		shared
			uploads
	wordpress
		wp-admin
		wp-content
		wp-includes

Das wordpress-Verzeichnis ist das Root-Verzeichnis für unsere Webseite. Die Ordner wp-admin und wp-includes sowie die PHP-Dateien im Root-Verzeichnis müssen auf den Server. Der wp-content-Ordner ist leer und bekommt einen Symlink auf your-project/deployments/current. Auf das uploads-Verzeichnis im shared-Ordner gehe ich später noch ein.

WordPress

Damit WordPress die Symlinks korrekt auflöst, muss das WP_CONTENT_DIR neu gesetzt werden. Hierzu kann die PHP-Funktion realpath verwendet werden. Diese löst den Symlink auf das wp-content-Verzeichnis auf und liefert den korrekten Pfad aus.

define( 'WP_CONTENT_DIR', realpath( __DIR__ . '/wp-content' ) );

Capify WordPress

Nachdem alle Vorbereitungen getroffen sind, kommen wir nun zum wichtigsten Teil: WordPress für Capistrano bereitstellen. Dazu navigiere in deiner Konsole in dein wp-content-Verzeichnis des Projektes und gib folgenden Befehl ein:

capify .

Dieser Befehl erstellt eine spezielle Datei namens Capfile im wp-content-Ordner und generiert einen Ordner config. Dieser Ordner enthält die wichtigsten Konfigurationsdateien für das Deployen des Projektes. Die Capfile hilft Capistrano beim Laden deiner Konfigurationen und Scripte.

Widmen wir uns erst der Capfile. Dazu öffne die Datei ersetze alle generierten Standardwerte durch folgendes:

require 'railsless-deploy'

# Multistage
set :stages, [ 'development', 'staging', 'production' ]
set :default_stage, 'development'
require 'capistrano/ext/multistage'

# load the deploy.rb in config-Folder
load 'config/deploy'

Die erste Zeile lädt das railsless-deploy-Modul, das benötigt wird, da wir WordPress, eine nicht-Ruby-Anwendung, mit Capistrano deployen möchten. Der Multistage-Block definiert, welche Stages wir später haben. In default_stage wird einer dieser Stages eingetragen. Zu den gewählten Stages werden wir später gleichnamige Konfigurationsdateien mit den benötigten Serverdaten anlegen.

Zum Schluss laden wir noch die deploy.rb-Datei aus dem config-Verzeichnis. Öffne diese Datei und ersetze alles durch folgende Werte:

set :application, "your-application-name"

set :scm, :git
set :repository, "git@github.com:you/your-project.git"
set :branch, 'your-branch'
set :deploy_via, :remote_cache

set :copy_exclude, ['.git', '.DS_Store', '.gitignore', '.gitmodules']
set :keep_releases, 5
# Server side config
set :use_sudo, false

Zuerst geben wir unserer Applikation einen Namen. Der nächste Block leitet mit dem SCM (Source Control Management) ein. Hier weisen wir :git zu (weitere SCM hier). In den nachfolgenden Zeilen definieren wir unser Repository und den Branch, welchen wir auf unserem Server haben möchten.
Die Variable deploy_via setzen wir auf :remote_cache. Diese Variante hält auf dem Server eine Kopie des Git-Repositories, führt nur ein “fetch” anstelle eines kompletten “clone” aus und beschleunigt somit dem Deployment-Ablauf. Weitere Deploy-Strategrien findest hier.

Im letzten Block definieren wir in der Variable :copy_exlude nun noch einen Array mit Dateien, die wir nicht auf unserem Server haben wollen, aber in unseren Git-Repo sind. :keep_releases definiert, wie viele Versionen zurück wir aufheben möchten und :use_sudo setzen wir auf false, da wir nicht möchten, dass Capistrano auf dem Server alles via sudo-Befehl ausführt.

Weitere Einstellungen sind in der offiziellen Dokumentation zu finden.

Zu guter letzt legen wir noch im config-Ordner einen Ordner mit der Bezeichnung deploy an. Hier kommen die oben erwähnten 3 Konfigurationsdateien zu den gleichnamigen Stages zum Einsatz:

deployment.rb
staging.rb
production.rb

In die Dateien trägst du dann wie folgt deine Serverzugangsdaten ein:

set :deploy_to, '/path/to/deployments/'
set :user, 'the_ssh_user'
server 'the_host.tld', :app

Der Wert in deploy_to ist das Verzeichnis auf dem Server, wo die Anwendung bereitgestellt wird (siehe Projektstruktur auf dem Server) und der user beinhaltet den SSH-User fürs Deployen. In der letzten Zeile wird der Host für die Rolle :app gesetzt. Diese spielt an diesem Punkt noch keine wichtige Rolle, sollte aber im Hinterkopf behalten werden. Zum Abschluss führen wir noch folgenden Befehl aus:

cap development deploy:setup

Dieser Befehl bereitet auf dem Server die benötigten Verzeichnisse vor. Um zu überprüfen, ob alles in Ordnung ist, kann folgender Befehl ausgeführt werden:

cap development deploy:check

Hierbei wird die lokale Umgebung auf dem angegebenen Server überprüft und es wird nach möglichen Problemen gesucht. Sollten hier keine Fehlermeldungen erscheinen und alles korrekt konfiguriert sein, erscheint folgende Meldung:

command finished in {n}ms
You appear to have all necessary dependencies installed

Unser erster Deploy

Nachdem wir nun alle Konfigurationen erfolgreich abgeschlossen und getestet haben, kann auch endlich deployt werden. Dazu gehe per Konsole in dein Projektverzeichnis wo die Capfile liegt und gebe folgendes ein:

cap development deploy

Dieser Befehl wird auf deinem angegeben development-Server the_host.tld einloggen und in das Verzeichnis /path/to/deployments/ navigieren. Anschließend wird er in das Verzeichnis releases den master-Branch von Git klonen. Nun sollte in deiner Konsole eine Menge an Ausgaben zu sehen sein, welche im Grunde sagen, in welcher Phase sie was tun. Am Ende, wenn alles erfolgreich war, sollte nun folgende Meldung stehen:

command finished in {n}ms
***transaction: commit

Um nicht bei jedem Deploy über Capistrano erneut die Zugangsdaten einzugeben, kann ein RSA-Schlüssel generiert werden. Dieser muss auch auf dem Zielserver hinterlegt werden. Einige weiterführende Informationen hierzu findet Ihr hier:

Nun haben wir erfolgreich unseren ersten cap deploy ausgeführt. Jetzt wollen wir doch mal sehen, was passiert ist. Dazu verbinden wir uns via SSH auf unseren Server und navigieren zu unseren deploy_to-Pfad. Dort wirst du nun folgende zwei neue Verzeichnisse sehen:

releases
current

Das current-Verzeichnis ist dabei ein Symlink auf das neueste Unterverzeichnis in releases.
Nach jedem Deploy wird im releases-Verzeichnis ein neuer Ordner mit dem kompletten Verzeichnis angelegt. Der Symlink auf dem current-Verzeichnis wird dabei immer automatisch aktualisiert.

Rollback

Nicht, dass wir es brauchen, aber was ist wenn mal etwas schief geht und die Seite nach dem deploy nicht mehr erreichbar ist? Die Lösung ist einfach:

cap deploy:rollback

Dieser Befehl setzt den aktuellen Symlink auf unseren current-Ordner auf den vorherigen Release. Per

cap deploy

kann man ganz einfach wieder auf die aktuelle Version switchen.

Releases aufräumen

Da wir bei jedem cap deploy einen neuen Release unserer Anwendung erstellen, kann es vorkommen, dass unser release-Ordner mit der Zeit ziemlich voll wird. In unserer deploy.rb haben wir bereits mit set :keep_releases, 5 definiert, dass wir nur die letzten fünf Releases behalten wollen. Allerdings müssen wir Capistrano auch sagen “Räum’ endlich auf!”. Folgender Befehl bringt hier Abhilfe:

cap deploy:cleanup

Damit man nicht nach jedem Deploy diesen Befehl eingeben muss, können wir ganz bequem dieses Kommando wie folgt ans Ende unserer deploy.rb setzen:

after 'deploy:update', 'deploy:cleanup'

Somit wird nach jedem Deploy-Vorgang der Hook “cleanup” ausgeführt und überflüssige Releases gelöscht.

Capistrano Shared Files

Wir wissen nun, wie man Deploys und Rollbacks ausführt. Jetzt lernen wir, wie wir unsere freigegebenen Dateien im shared-Ordner verlinken. Da sich ja bei jedem Deploy/Rollback unser wp-content-Verzeichnis verschiebt (neuer Ordner in releases), müssen wir den Symlink für unser uploads-Verzeichnis automatisiert aktualisieren. Was bietet sich da nicht besser an, als selbst eine kleine Aufgabe zu definieren die wir bei jedem Deploy mit ausführen lassen?

Folgende 3 Schritte sind dafür nötig:

1. Erstelle im config-Ordner die Datei task.rb und trage folgende neue Aufgabe ein:

namespace :shared do
   task :make_symlink, :roles => :app do
      run "ln -nfs #{shared_path}/uploads #{release_path}/uploads"
   end
end

Zunächst definieren wir einen Namespace, unter welchem der Aufruf später erfolgt. Dieser entspricht dem Schema: cap {namepsace}:{task}. Der nächste Block definiert die eigentliche Aufgabe mit der Rollenzuweisung :app. Das heißt, dass unser Task nur für den definierten Server mit der Rolle :app ausgeführt wird. In der 4. Zeile definieren wir den eigentlichen Befehl. Hier wird das Kommando ln -nfs ausgeführt und der Symlink erzeugt. Die Variablen shared_path und release_path sind vordefinierte Variablen von Capistrano.

2. Füge vor load 'config/deploy' in der Capfile folgendes hinzu:

load 'config/task'

Somit laden wir vor jedem Deploy unsere definierten Tasks und stellen diese zur Verfügung.

3. Öffne deine deploy.rb-Datei und trage deinen neuen Task ans Ende der Datei ein:

after 'deploy:update_code', 'shared:make_symlink'

Hiermit definieren wir, dass nach dem Abschluss des Deploys die neu definierte Ausgabe ausgeführt wird. Diese aktualisiert den Symlink unseres aktuellen Releases:

development/releases/{current_release}/uploads

auf unserem shared-Ordner

development/shared/uploads

Zusammenfassung

Ich hoffe, Ihr konntet in meinem Beitrag ein paar Einblicke in den Arbeitsablauf bekommen und auch etwas dazulernen. Viele Prozesse lassen sich heutzutage einfach und schnell optimieren. Natürlich gehört auch bei Capistrano etwas Einarbeitungszeit und eine Erst-Konfiguration dazu, aber gerade bei großen Projekten mit vielen Servern spart man enorm Zeit, löst viele Abhängigkeiten durch Verwendung von gemeinsamen Schnittstellen und kann verschiedenste Automatismen in den Deploy-Ablauf mit einbinden.

Anbei noch die ganzen Dateien zum Anschauen und Downloaden:

Newsletter abonnieren

Das könnte dich auch interessieren

20% Rabatt - Da kommt Frühlingsstimmung auf!

Der Frühling ist da, die Laune steigt und die Motivation ist hoch. Darum erhältst du in unserem Online-Shop vom 1.4.2024 bis zum 8.4.2024 einen Rabatt vo ...

Mehr erfahren

Grüner E-Commerce: Mit WooCommerce profitieren

Kunden achten immer mehr auf Nachhaltigkeit und umweltbewusstes Einkaufen. Dieser Trend macht auch vor dem Online-Handel nicht halt. Grüner E-Commerce ...

Mehr erfahren

SEO-Quick-Wins für bessere Rankings

Die Liste an möglichen Maßnahmen zur Suchmaschinenoptimierung ist lang und viele Seitenbetreiber wissen oftmals nicht, wo sie anfangen sollen. In der Pra ...

Mehr erfahren

Kommentare

6 Kommentare

  1. Hallo Christan

    Danke für die ausführliche Beschreibung 🙂

    1. Gern! Ich hoffe es hat alles geklappt?! 🙂

  2. Ehrlich gesagt habe ich das Tutorial erst im Kopf durchgespielt. Werde es aber diese Woche noch für ein grösseres Projekt in unserer Agentur ausprobieren 🙂

  3. Hi Chris,

    vielen Dank für das super Tutorial!
    Ganz am Ende hakt es bei mir. Zum Einen muss man (wahrscheinlich mittlerweile) am Ende der deploy.rb den Task wie folgt aufrufen: after “deploy”, “shared:make_symlink”.
    Also an erster Stelle immer noch den Namen des vorherigen Tasks mit angeben. Ansonsten gibts nen Syntax-Fehler.
    Zum anderen legt er bei mir momentan die Symlinks nicht an. Da bin ich grad noch am suchen.

    Viele Grüße
    Christian

    1. Hallo Chris,

      vielen Dank für das Lob! 🙂 Hast du schon einmal die Ordnerrechte überprüft? Dadurch das wir set :use_sudo, false setzen, wird das Ganze nicht mit “Superuser-Rechten” ausgeführt.

      Bzgl. deines Hinweises, vielen Dank! Ich habe die Config entsprechend angepasst.

  4. Hi,

    ich nochmal. Ich weiß nicht, woran es lag. Aber so hat es funktioniert:

    In der deploy.rb habe ich zunächst den Task ausgeführt und ganz zum Schluss die alten Deploys aufgeräumt:
    after “deploy:update_code”, “shared:make_symlinks”
    after “deploy”, “deploy:cleanup”

    Und mein Task sieht wie folgt aus:
    namespace :shared do
    desc “Create symlink for uploads”
    task :make_symlinks, :roles => :app do
    run “ln -nfs #{shared_path}/uploads #{release_path}/uploads”
    end
    end

    An der Reihenfolge der Tasks liegt es nicht. Aber so läuft nun alles durch bei mir.

    Dank dir nochmal!

    Viele Grüße
    Christian

Schreibe eine Antwort an Christian Schätzle Antwort abbrechen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Du kannst folgende HTML Tags verwenden: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>