Deployment von Webanwendungen (in meinem Fall Django-Projekte) kann anstrengend und nervtötend sein - insbesondere dann, wenn stets dieselbe Routine notwendig ist, um die Webanwendung auf dem Server zu aktualisieren und ggf. zu warten.
Gestern habe ich fabric, ein von der Idee her triviales Framework, kennen gelernt, mit dem sich alle nun folgenden Schritte (und sicher noch viele mehr) automatisieren lassen. Es erleichtert einem die Arbeit enorm - ich weiß schon gar nicht mehr, wie ich bislang ohne konnte. 
Die gängigsten Schritte beim Deployment sind m.M.n.
Etwas seltener, jedoch auch durchaus noch stets nach Schema F, kümmert man sich um:
Fabric ist einfach gestrickt: fabfile.py angelegt, einfache Funktionen geschrieben, die jeweils eine Aufgabe übernehmen. Das Framework bietet unter anderem Funktionen zur lokalen und entfernten Befehlsausführung (local(), run(), sudo(), normales Bash-Script z. B.) und zum Dateitransport (put()). Ist das fabfile angelegt (idealerweise im Project-Root), kann über die Fabric-Anwendung fab auf die einzelnen Funktionen zugegriffen werden. Dabei liest fab das fabfile ein und führt entweder, sofern der Funktionsname als Argument 1 übergeben wurde, die Funktion aus oder präsentiert (ohne Übergabe von Argumenten) alle möglichen Funktionen inklusive ihrer Docstrings:
florian-schlachters-imac:home flosch$ fab
Fabric v. 0.1.1.
No commands given.
Available commands are:
about : Display Fabric version, warranty and license information
access_log : Tail the servers access logfile.
deploy : Syncs all repositories and restarts the whole server
error_log : Tail the servers error logfile.
fullhg : Synchronizes and updates the reps
help : Display Fabric usage help, or help for a given command.
hgsync : Pushes and pulls to/from remote
hgupdate : Updates the reps
let : Set a Fabric variable.
list : Display a list of commands with descriptions.
restart : Restarts django fcgi process
shell : Start an interactive shell connection to the specified hosts.
start : Starts django fcgi daemon
stop : Stops the django daemons
upgrade : Upgrades all used Django apps.
Done.
Ausführen der Funktion stop(), die in meinem Fall auf dem Deploy-Server den Django-Prozess stoppt:
florian-schlachters-imac:home flosch$ fab stop
Fabric v. 0.1.1.
Running stop...
Logging into the following hosts as root:
project.tld
[project.tld] run: ...
Done.
florian-schlachters-imac:pb2 flosch$
Die Kommunikation findet via SSH statt. Dazu stellt fabric nach dem Starten per SSH eine Verbindung mit dem Remote-Host her. Findet keine Anmeldung via Public-Key statt, erfragt es das Passwort des Nutzers (mehr dazu weiter unten).
fabfile.pyViel zu Konfigurieren gibt es bei fabric nicht: Mit dem Projektnamen und dem Remote-Host ist es in der Grundkonfiguration eigentlich schon getan:
config.project = 'projectname'
config.fab_hosts = ['project.tld']
Auf Wunsch kann der Hostname mit einem SSH-Benutzernamen versehen werden, gemäß gewöhnlicher Syntax: user@host.tld.
Ferner können Konstanten für die spätere Befehlsausführung festgelegt werden. So kann beispielsweise definiert werden, wo Logfiles liegen, wenn der Pfad später in einigen Befehlen öfter benötigt wird:
config.log_dir = '/var/log/lighttpd/
Der Name der Konstante (hier log_dir) kann frei gewählt werden. Genutzt werden kann der Platzhalter im späteren Verlauf innerhalb eines Befehls via gewöhnlichem String-Formatting. Beispiel:
def access_log():
"Tail the servers access logfile."
run('tail -f %(log_dir)saccess.log')
Die Funktion acesss_log() wird weiter unten noch näher erläutert (siehe Nachkontrolle und Debugging).
Zum Synchronisieren aller Änderungen zwischen Dev-Rechner und Server lohnt die Verwendung einer Versionsverwaltung (ich empfehle Mercurial). Dabei habe ich mir eine Fabric-Funktion gestrickt, die alle Änderungen auf dem Deploy-Server committed (falls ich wirklich mal direkt am Server mit vi Kleinigkeiten korrigieren sollte) und daraufhin alle Änderungen vom Server abholt, meine Änderungen an den Server schickt und letzen Endes am Server die Codebase auf den aktuellsten Stand bringt.
Konkret schauen die drei wichtigsten Funktionen folgendermaßen aus:
def hgsync():
"Pushes and pulls to/from remote"
run("cd /srv/project ; hg commit -m 'remote changes'") # remote Befehlsausführung
local('hg push') # lokale Befehlsausführung
local('hg pull')
def hgupdate():
"Updates the reps"
run('cd /srv/project ; hg update')
local('hg update')
def fullhg():
"Synchronizes and updates the reps"
hgsync()
hgupdate()
Aufrufen kann ich sie via fab hgsync, fab hgupdate oder fab fullhg. fullhg() hat keine eigene Funktionalität sondern erspart mir lediglich, hgsync() und hgupdate() manuell hintereinander ausführen zu müssen.
Die Leute, die Django in Kombination mit WSGI und Apache nutzen, haben eindeutig die elegantere Möglichkeit, die Webanwendung bei Änderung der Codebase neuzustarten. Hier reicht ein touch des entsprechendens wsgi-Files. Ich nutze jedoch die runfcgi-Methode der manage.py im Daemon- und TCP-Mode. Dabei startet Django FCGI-Prozesse im Hintergrund und akzeptiert Anfragen via TCP-Protokoll auf einem von mir festgelegtem Port. Ich kann die Anwendung nur dann neustarten, wenn ich den alten Prozess terminiere und ihn neu aufrufe.
Update: steph hat mich darauf aufmerksam gemacht, dass der manage.py-Command runfcgi den Parameter pidfile akzeptiert, über den ein Filename angegeben werden kann, in dem die Prozess-ID des Daemon abgelegt wird. Die Variante ist natürlich viel schöner als mein (voriges) herumgegreppe (hast du es hier nicht gelesen, ignoriere es einfach
). Ich habe meine start()- und stop()-Funktionen entsprechend angepasst:
def stop():
"Stops the django daemons"
run("if [ -f $(pidfile) ]; then kill `cat $(pidfile)` && rm $(pidfile); fi")
def start():
"Starts django fcgi daemon"
run("cd /srv/pb2 ; python ./manage.py runfcgi protocol=fcgi host=127.0.0.1 port=3033 pidfile=$(pidfile)")
start() sollte soweit selbsterklärend sein; stop() prüft, ob das pidfile existiert. Ist dem so, wird der Django-Prozess terminiert und das pidfile gelöscht. Beide Funktionen greifen auf die Konstante config.pidfile zu, in der ich den pidfile-Dateipfad des Projekts (z. B. /myproject/myproject.pid) festgelegt habe.
Das bisherige manuelle anmelden am Server + tailen von Logfiles kann auch per fabric automatisiert werden. Per fab access_log loggt es sich ein und zeigt sofort den fortlaufenden Tail des Access- oder Errorlogs. Die Idee habe ich dem django-de-fabric-File entnommen. Danke dafür 
def access_log():
"Tail the servers access logfile."
run('tail -f %(log_dir)saccess.log')
def error_log():
"Tail the servers error logfile."
run('tail -f %(log_dir)serror.log')
Es gibt sicher noch viel mehr Anwendungsgebiete für's Debugging, die per fabric automatisiert werden können. Ich bin an neuen Ideen interessiert!
Wer ein Verzeichnis mit allen Libraries besitzt (und diese per VCS ausgecheckt hat), möchte möglicherweise mehr oder weniger regelmäßig alle Bibliotheken auf dem Laufenden halten. Hierzu dient ein einfaches Shell-Script, dass ich per run() remote auf dem Deploy-Server ausführe.
def upgrade():
"Upgrades all used Django apps."
# Git
run("for app in /srv/libs/*; do cd $app && git pull && git gc; done", fail='ignore')
# Svn
run("for app in /srv/libs/*; do cd $app && svn up; done", fail='ignore')
Das Argument fail='ignore' im run() dient hier übrigens dazu, dass fabric nicht meine Funktion an der aktuellen Stelle ohne weitere Ausführung beendet, sollte ein run() oder sudo() fehlschlagen (z. B. durch einen entsprechenden Returncode). Das tut es nämlich regulär und ist hier nicht gewünscht (einige pulls/updates werden nämlich fehlschlagen, da es sich beiden Verzeichnissen möglicherweise nicht um ein entsprechendes Repository handelt).
Hat man sich seine einzelnen Funktionen für die jeweiligen Aufgaben zusammengeschrieben, können diese noch zu einem großen Deploy-Befehl zusammengefasst werden. Meiner schaut zum Beispiel folgendermaßen aus:
def deploy():
"Syncs all repositories and restarts the whole server"
fullhg()
restart()
Der Sourcecode wird synchronisiert und die Djangoprozesse neugestartet. restart() führt dabei übrigens einfach stop()und start() hintereinander aus.
Möchte ich meine lokalen Änderungen auf meinen Production-Server anwenden, reicht somit ab sofort nur noch ein einfaches fab deploy - und alles geht wie von Zauberhand. 
Damit Fabric nicht bei jedem Aufruf nach dem SSH-Passwort fragt (was auch ziemlich nervig sind kann
), ist die Einrichtung einer SSH Public-Key-Authentication sinnvoll. Mehr Informationen zur Einrichtung finden sich zum Beispiel hier.
sudo()?Ich habe mich zu Testzwecken stets als Benutzer root beim Fremdsystem angemeldet - eigentlich ein Unding, das ich hier auch niemandem empfehlen möchte. Zu diesem Zweck gibt es das Unix-Tool sudo (super user do), das als normaler User eine Befehlsausführung als root ermöglicht. Wann immer also die Ausführung als Administrator notwendig ist, kann auch in fabric sudo() genutzt werden (identisch in der Nutzung wie run()).
Beitrag veröffentlicht 30.04.2009 03:22:55 Uhr
0 Kommentar(e)
Quelle oder weiterführende URL
http://www.nongnu.org/fab/
Labor: Blog & Bookmarks
Deutscher Django-Verein e. V. gegründet (08.12.2009)
Kurz notiert: PIL unter Mac OS X (01.09.2009)
Kurz notiert: psycopg2 unter Mac OS X (01.09.2009)
Unsere Referenzen (Auszug)
psd-tutorials.de vertraut als eines der führenden deutschen Grafikportale auf unsere Server-Infrastruktur und nutzt diese für seine regelmäßigen Backups.
pizzabus.de ist die erste deutsche Online-Vermittlungsplattform für Lieferdienste, die Online-Zahlung akzeptiert. pizzabus.de wird von uns in Eigenregie betrieben.
Die Firma Rumsoft ist in der Softwareentwicklung tätig und hat ihr Dienstleistungsportfolio durch Wiederverkäufer-Dienstleistungen aus unserem Hause erweitert. In ihrem Namen bietet sie eigene Domain- und Hostingleistungen an.
Für die TeleskopManufaktur haben wir einen umfangreichen und branchenspezifischen Online-Shop entworfen. Umfangreiche Community-Funktionen wie Foren oder Benutzerprofile runden die Internetpräsenz ab.
Die Carl-Zeiss-Oberschule erhielt ein frisches, neues Design und nimmt unser maßgeschneidertes Webhosting in Anspruch. Über von uns bereitgestellte Mailinglisten hat sich die Berliner Schule ein individuelles Kommunikationsnetzwerk aufgebaut.
Unter dem Projektnamen OpenSeek betreiben wir bereits seit über vier Jahren Forschungen auf den Gebieten der Suchmaschinentechnik und Semantic Search.
Florian SchlachterWir nutzen Icons vom Tango Desktop Project.
Alle Preise sind Endpreise und inklusive der derzeitig gültigen Umsatzsteuer in Höhe von 19%.