Externer Server für statische Dateien einer Django-App
Eigentlich ist das Auslagern von statischen Files mit Django ja relativ simpel, aber schwierig wird es, wenn vom Benutzer Dateien hochgeladen werden können. Diese müssen auf dem static-Server zeitgleich verfügbar sein.
Entweder es kümmert sich gleich Django darum, die Datei weiterzureichen, was aber zusätzlichen Code erfordert. Oder es wird ein anderer Weg gewählt: über
Dann muss nichts "per Hand" synchronisiert werden und die Übertragung läuft verschlüsselt zwischen den Servern.
Damit das klappt, sind folgende Schritte notwendig:
Das wars. Die Django-App müsste nun alle Dateien ganz normal im media-Verzeichnis unter
Remount, wenn die Verbindung weg ist
Eine große Schwachstelle hat dieses System aber: Wenn der static-Server neu gestartet wird oder ein Netzwerkproblem hat oder ähnliches, ist der Mount weg und das Verzeichnis auf dem Django-Server leer. Somit klappen Uploads der Django-App nicht mehr und es kommt zu Fehlern. Eine Möglichkeit, dies etwas abzuschwächen, ist, ein Skript laufen zu lassen (z.B. jede Minute per Cron), das prüft, ob ein Mount vorliegt und ggf. neu mountet:
Somit fällt die Verbindung für max. 1 Minute aus, zumindest wenn der static-Server nur ein kurzes, temporäres Problem hat. Lässt man sich mit MAILTO in der crontab per Mail benachrichtigen, weiß man zumindest, das ein Problem vorliegt.
Nachtrag (26.05.): Dieses Skript ist nun nicht mehr optional, sondern elementar wichtig. Siehe http://www.bheil.net/blog/2010/05/26/sshfs-fstab-mounten/!
admin-media
Für die Django-admin-Files ist es die beste Lösung, diese weiterhin vom Django-Server ausliefern zu lassen. Dann müssen die nicht gespiegelt werden, vor allem wenn mit trunk gearbeitet wird. Also einfach ein virtual_host, der dieses Verzeichnis ausliefert und das dann in der settings.py angeben.
Entweder es kümmert sich gleich Django darum, die Datei weiterzureichen, was aber zusätzlichen Code erfordert. Oder es wird ein anderer Weg gewählt: über
sshfs. Die Django-Installation behandelt alles so, als wäre es lokal. Das media-Verzeichnis allerdings liegt auf dem static-Server und wird per sshfs eingehängt.Dann muss nichts "per Hand" synchronisiert werden und die Übertragung läuft verschlüsselt zwischen den Servern.
Damit das klappt, sind folgende Schritte notwendig:
- Auf dem Django-Server muss das Paket
sshfsinstalliert werden. - Da gleich beim Booten des Servers das Einhängen erfolgen soll, muss ein SSH-Login ohne Passwort vom Django-Server auf den static-Server möglich sein. Dazu macht man folgendes
- Auf dem Django-Server als
rootanmelden und ein Keypair erzeugen mitssh-keygen -t rsa. Dabei keine Passphrase vergeben. Der Schlüssel liegt dann unter/root/.ssh/id_dsa - Auf dem static-Server einen Nutzer erzeugen, der sich per SSH einloggen kann
- Den Inhalt der Datei
/root/.ssh/id_dsa.pubvom Django-Server in die Datei/home/NUTZER/.ssh/authorized_keysanhängen. Notfalls das Verzeichnis/home/NUTZER/.ssherzeugen (und Rechte setzen). - Testen, ob der Login klappt vom Django-Server aus. Ein einfaches
ssh NUTZER@static-serversollte klappen - Wenn gewünscht, kann auf dem static-Server der Login von NUTZER noch beschränkt werden durch
AllowUsers NUTZER@IPin der Datei/etc/ssh/sshd_config. (Dabei aber natürlich andere Nutzer nicht vergessen, sonst sperrt man sich selbst aus!)
- Auf dem Django-Server als
- Nun auf dem static-Server alles vorbereiten:
- Verzeichnis erzeugen, indem die Dateien liegen werden, bsp.
/var/www/django1-static - Dieses Verzeichnis muss dem Nutzer NUTZER aus vorherigem Schritt gehören
- Verzeichnis erzeugen, indem die Dateien liegen werden, bsp.
- Auf dem Django-Server die notwendigen Schritte durchführen:
- Editieren der Datei
/etc/fstabund einfügen folgender Zeile:sshfs#NUTZER@static-server:/var/www/django1-static /opt/django-app/media fuse uid=33,gid=33,umask=0,allow_other,port=222,noauto 0 0
Damit wird das Verzeichnis/var/www/django1-staticauf dem static-server ins media-Verzeichnis der Django-App unter/opt/django-app/gemountet. Wichtig sind die Parameter:uidundgidsollten die ID des Nutzers haben, unter dem die Django-App läuft (33 =www-data, kann in der Datei/etc/passwdherausgefunden werden).- Wenn SSH auf einem anderen Port läuft, dann ist die Port-Angabe wichtig.
- Der Nutzer, der mit
uidangegeben ist, muss in der Gruppefusesein:sudo adduser www-data fuse - Damit die SSH-Verbindung auch offen bleibt, noch folgende Zeile ans Ende der Datei
/etc/ssh/ssh_configanhängen:ServerAliveInterval 15
Das sendet alle 15 Sekunden ein Paket, um die Verbindung aktiv zu halten. - Mit
mount -atesten. Es sollten unter/opt/django-app/mediaalle Dateien sichtbar sein, die auf dem static-server liegen. - Unmounten geht übrigens mit
sudo fusermount -u /opt/django/media
- Editieren der Datei
Das wars. Die Django-App müsste nun alle Dateien ganz normal im media-Verzeichnis unter
/opt/django-app/media speichern können. Diese Dateien werden sofort auf den static-server übertragen und dort gespeichert - völlig transparent für die Django-App.Remount, wenn die Verbindung weg ist
Eine große Schwachstelle hat dieses System aber: Wenn der static-Server neu gestartet wird oder ein Netzwerkproblem hat oder ähnliches, ist der Mount weg und das Verzeichnis auf dem Django-Server leer. Somit klappen Uploads der Django-App nicht mehr und es kommt zu Fehlern. Eine Möglichkeit, dies etwas abzuschwächen, ist, ein Skript laufen zu lassen (z.B. jede Minute per Cron), das prüft, ob ein Mount vorliegt und ggf. neu mountet:
#!/bin/bash
if ! mount | grep "on /opt/django-app/media type fuse.sshfs" > /dev/null; then
echo "SSHFS-Mount /opt/django-app/media ist weg. Versuche neu zu mounten..."
mount /opt/django-app/media
fi
Somit fällt die Verbindung für max. 1 Minute aus, zumindest wenn der static-Server nur ein kurzes, temporäres Problem hat. Lässt man sich mit MAILTO in der crontab per Mail benachrichtigen, weiß man zumindest, das ein Problem vorliegt.
Nachtrag (26.05.): Dieses Skript ist nun nicht mehr optional, sondern elementar wichtig. Siehe http://www.bheil.net/blog/2010/05/26/sshfs-fstab-mounten/!
admin-media
Für die Django-admin-Files ist es die beste Lösung, diese weiterhin vom Django-Server ausliefern zu lassen. Dann müssen die nicht gespiegelt werden, vor allem wenn mit trunk gearbeitet wird. Also einfach ein virtual_host, der dieses Verzeichnis ausliefert und das dann in der settings.py angeben.