Das Szenario
– Nginx als SSL Reverse Proxy innerhalb eines Docker Containers
– Externe Domäne www.domain.tld
– Lokaler Webserver auf Port 8080/TCP/HTTP > http://localhost:8080
– Alle Anfragen auf Port 80/TCP/HTTP nach 443/TCP/HTTPS innerhalb des Containers.
Alle Befehle werde ich als Benutzer root ausführen.
Haltet euch immer vorrangig an die offiziellen Dokumentation Nginx‘ und Dockers‘. :-)
Installation Dockers
Debian Wheezy
Installation des Kernels aus dem Backport Repository mit anschließendem Neustart.
Dies ist notwendig, da erst ein Kernel ab Version 3.10 empfohlen wird.
echo deb http://ftp.us.debian.org/debian wheezy-backports main > /etc/apt/sources.list.d/wheezy-backports.list apt-get update -y apt-get -t wheezy-backports install linux-image-amd64 -y # Für ein amd64-basierendes System reboot
Der OVH Kernel ist leider etwas beschnitten und bietet unter anderem keine cgroups.
Nun Docker installieren (lassen). Vorab „curl“ installieren:
apt-get install curl curl -sSL https://get.docker.io/ | bash
Ubuntu 14.04
Ich empfehle den Docker Installer zu verwenden. Es wird das offizielle Repository installiert. Auch hier im Vorfeld „curl“ installieren:
apt-get install curl curl -sSL https://get.docker.io/ubuntu/ | bash
Konfiguration
Ab hier erst einmal einige Informationen zur Auffrischung der Docker-Kenntnisse:
- Docker Images werden in der Regel aus dem Repository Dockers‘ gezogen („pull“).
- Images (Ubuntu, Debian, Centos etc., Debian + Apache, Centos + Nginx und viele mehr) können entweder direkt verwendet- oder durch Dockerfiles manipuliert werden.
- Docker Images sind für Container immer read-only! Images werden bei Ausführung nie verändert!
- Ein Container benutzt eine Kopie des Images als Basis. 5 Container mit selben Image, benutzen jeweils eigene Kopien des Images.
Als Grundlage des Reverse Proxys dient das aktuellste (> „latest“) Ubuntu Image „Phusion“, das dem offiziellen Ubuntu Image Dockers‘ einige Optimierungen voraus hat.
Ein Dockerfile erweitert die Basis-Installation um einen Nginx Server aus dem Nginx Repository.
Ein Blick in diese Datei schadet nicht
- Die Zeile „FROM dockerfile/ubuntu“ ersetzt „sed“ später durch das Phusion Image („phusion/baseimage:latest). Dies ist die Basis.
- Durch die „RUN“ Befehle wird das Image um Nginx aus dem Nginx PPA erweitert, diesem die Daemon-Funktionalität entzogen (notwendig für Docker) und noch einige weitere Kleinigkeiten angepasst.
- „VOLUME“ Anweisungen erstellen „mount points“, auf die von außen zugegriffen werden kann.
- „WORKDIR“ beschreibt das „working directory“ (no shit, Sherlock ;-) ). So werden etwa alle „RUN“ Anweisungen in diesem Verzeichnis ausgeführt.
- „CMD“ hält die Anweisung bereit, Nginx zu starten.
- „EXPOSE“ beschreibt die Ports, die im Container während der Laufzeit aktiv sein können.
Alle Daten werde ich im Verzeichnis „/opt“ ablegen. Der letzte Befehl „docker build“ wird das Dockerfile abarbeiten und das veränderte Phusion Image mit der Bezeichnung „phusion/nginx“ ablegen.
mkdir /opt/docker_nginx/ ; cd /opt/docker_nginx wget -q -O - https://raw.githubusercontent.com/dockerfile/nginx/master/Dockerfile | sed "s/dockerfile\/ubuntu/phusion\/baseimage:latest/g" > Dockerfile docker build -t="phusion/nginx" .
Hat alles geklappt, schließt die Einrichtung mit „Successfully built XYZ“ ab. Wobei XYZ einer eindeutigen ID entspricht.
Nun besitzt der Host zwei Images, zu prüfen mit „docker images“:
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE phusion/nginx latest 19c681f50628 36 minutes ago 309.1 MB phusion/baseimage latest cf39b476aeec 8 days ago 289.4 MB
Das Phusion „baseimage“ ist ebenso im lokalen Speicher vorhanden, da es als Basis für das um Nginx erweiterte Image diente.
Dieses könnte durch ein „docker rmi phusion/baseimage“ gelöscht werden, ohne das Nginx Image zu zerstören.
Die Verzeichnisse „/etc/nginx/sites-enabled“, „/etc/nginx/certs“ sowie „/var/logs/nginx“, möchte ich vom Host in den Container mounten, um die Verwaltung zu erleichtern.
Auf dem Host erstelle ich drei Ausgangspunkte für diese Verzeichnisse:
mkdir /opt/docker_nginx/{sites,logs,certs}
Eine einfache Site-Konfiguration für einen Reverse Proxy lege ich unter „sites“ ab:
nano /opt/docker_nginx/sites/reverse_proxy
Inhalt:
server { listen 80; return 301 https://www.domain.tld/$request_uri; } server { listen 443; server_name www.domain.tld domain.tld; ssl on; ssl_certificate /etc/nginx/certs/www.crt; ssl_certificate_key /etc/nginx/certs/www.key; ssl_ciphers 'EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH:+CAMELLIA256:+AES256:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!ECDSA:CAMELLIA256-SHA:AES256-SHA:CAMELLIA128-SHA:AES128-SHA'; ssl_prefer_server_ciphers on; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; add_header Strict-Transport-Security max-age=15768000; location / { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://localhost:8080; proxy_redirect off; } }
Das Zertifikat und der zugehörige Schlüssel liegen im Verzeichnis „/opt/docker_nginx/certs“. Die Namen (www.*) müssen in der Site entsprechend angepasst werden. Der Pfad bleibt bestehen!
Nun darf der Container mit folgendem Parameter erstmalig gestartet werden:
docker run -p 443:443 -p 80:80 -d -v /opt/docker_nginx/sites:/etc/nginx/sites-enabled -v /opt/docker_nginx/certs:/etc/nginx/certs -v /opt/docker_nginx/logs:/var/log/nginx phusion/nginx
Durch diese Anweisung werden Port 80/TCP sowie 443/TCP vom Host in den Container weitergeleitet, die drei besagten Verzeichnisse eingebunden („-v HOST_VERZ:CONTAINER_VERZ“) und der Container „detached“ gestartet (> nicht-interaktiv, im Hintergrund).
Ein Zugriff auf www.domain.tld leitet ab diesem Punkt auf die lokale Installation auf Port 8080 weiter.
Zur Verwaltung des Containers:
docker ps # Listet gestartete Container docker ps -a # Listet gestartete und gestoppte Container docker images # Listet Images docker stop CONTAINER_ID # Stop einen vorhandenen Container docker start CONTAINER_ID # Startet einen vorhandenen, gestoppten Container docker run XYZ # Erstellt (!) einen neuen Container. Nicht mit "start" zu verwechseln!
Pingback: Wordpress Testumgebungen in Docker - debinux Admin-Blog