Auch Docker gehörte zu den Tools, die ich schon lange in meiner “Todo” bzw. “ToLookAt” Schublade hatte. Jetzt hatte ich endlich Zeit und Lust dazu.
Installation
Wer einen Mac mit homebrew hat, ist fein raus:
brew install boot2docker
boot2docker init
boot2docker up
fertig.
Für andere Betriebsysteme stellt Docker Installationsanleitungen bereit. Bottomline: Man braucht ein halbwegs aktuelles 64-Bit Betriebssystem.
Kleiner Crashkurs
docker images
: Listet alle geladenen Imagesdocker pull image
: Holt das genannte Imagedocker run image
: Erstellt und startet einen Container vom genannten Image (Lädt das Image ggf. zuerst herunter, falls lokal noch nicht vorhanden.docker run -t -i image sh
: Erstellt einen Container und startet ihn im interaktiven Modus mit Terminaldocker ps -a
: Listet alle Containerdocker rm container
: löscht einen Containerdocker rmi image
: löscht ein imagedocker start -t container
: Einen bestehenden Container starten
Achtung Möglicher Denkfehler mit hohem Zeitverlustrisiko: Wenn Sie ein image mit docker run
starten, darin eine Menge ändern,
und es dann verlassen, sind Ihre
Änderungen keinesegs im Image gespeichert, sondern nur im Container. Den können Sie erneut starten mit docker start
. Wenn
Sie stattdessen erneut docker run
eingeben, erhalten Sie einen neuen Container, der nichts von Ihren Änderungen enthält.
Sie können Änderungen in ein neues Image persistieren, indem Sie docker commit
verwenden. Bottomline: Es lohnt sich,
über den Unterschied zwischen Image und Container nachzudenken: Ein Image ist ein read-only Abbild eines lauffähigen Systems.
Ein Container ist ein “zum Leben erweckter” Abzug eines Image. Dieser verändert das Image niemals, egal was man damit anstellt.
Beispiel: Einen git-server starten
Ziel: Wir wollen lokal vorhandene Git-Repositories übers Netz erreichbar machen. Wir möchten aber nicht mit SSH-Schlüsseln hantieren, da wir ohnehin alle kennen, die Zugriff auf unser Netzwerk haben. Unsere git-Repositories seien auf /srv/develop/git.
Ausführung:
1. `docker run -d -p 8080:80 -v /srv/develop/git:/opt/git rgwch/git-server`
Das war’s. Es gibt kein 2. und kein 3. Man kann z.B. das webelexis-Repository jetzt erreichen mit: git clone http:<ip-adresse des docker-hosts>:8080/git/webelexis
Ein kleines Problem Wir werden Änderungen so nicht dorthin pushen können, denn push lässt git nur bei “bare” repositories zu. Wir müssten also zunächst
von unseren Arbeitsrepositories “bare”-repositories auf den Server erstellen: git clone --bare <original> <bare-repo>
Wenn wir dem Git-Server dann diese bare-Repositories zur Verfügungs stellen, klappt sowohl pull also auch push wie erwartet. (Beim Push muss man wenn man
nichts ändert, als Name und passwort jeweils “git” eingeben)
Git Server container selber bauen
Den Quellcode, mit dem dieser git-server erstellt werden kann, finden Sie hier: https://github.com/rgwch/docker-git. Wenn sie Docker schon installiert haben, können Sie Ihren eigenen git server so bauen:
git clone https://github.com/rgwch/docker-git.git gitserver
cd gitserver
sudo docker build -t ihrname/git-server:tag .
Das ist alles. Für “ihrname/git-server:tag” können Sie sich was Eigenes einfallen lassen.
Werfen wir einen kurzen Blick auf “Dockerfile”, die Steuerugsdatei des Ganzen:
FROM debian:latest
MAINTAINER weirich@elexis.ch
COPY runcontainer /usr/sbin/runcontainer
RUN apt-get -y update && apt-get -y install nano gitweb lighttpd
ENV GIT_HTTP_EXPORT_ALL ""
RUN mkdir /opt/git
COPY config/gitweb.conf /etc/gitweb.conf
COPY addgituser.pl /usr/sbin/addgituser.pl
COPY config/lighttpd/ /etc/lighttpd
RUN chmod +x /usr/sbin/runcontainer && chmod +x /usr/sbin/addgituser.pl
WORKDIR /etc/lighttpd
CMD ["/usr/sbin/runcontainer"]
Wir gehen also von einem existierenden Image namens ‘debian:latest’ aus. Dort installieren wir mit apt-get alles, was wir benötigen, kopieren ein paar Steuerfiles in den Container und weisen den Container am Ende mit CMD an, das mitgelieferte “runcontainer”-Script zu starten, wenn der Anwender das Image mit “Run” benutzt.
Runcontainer sieht so aus:
TARGET_GID=$(stat -c "%g" /opt/git)
TARGET_UID=$(stat -c "%u" /opt/git)
groupadd -g $TARGET_GID -o gitgroup
useradd -u $TARGET_UID -g $TARGET_GID -r gitserver
git config --global user.email "info@gitserver.invalid"
git config --global user.name "admin git server"
/usr/sbin/addgituser.pl git git >/etc/lighttpd/.passwd
/usr/sbin/lighttpd -D -f /etc/lighttpd/lighttpd.conf
Zuerst versucht es (meist erfolgreich),herauszufinden, unter welcher UID und GID das Verzeichnis /opt/git läuft. Das ist ja das Verzeichnis, das wir dem container mit “sudo docker run -v /my/home/dir:/opt/git…. “ mitgegeben haben. Zum Zeitpunkt des Erstellens des Images wussten wir noch nicht, auf welches Verzeichnis /opt/git beim Start gemappt sein wird, deswegen können wir erst hier darauf reagieren: Wir erstellen einen user ‘gitserver’ und eine Gruppe ‘gitgroup’, die die herausgefundene UID bzw. GID haben.Da wir zu diesem Zeitpunkt root sind,dürfen wir das alles. Und nicht ganz zufällig weisen wir den lighttpd Server in seiner lighttpd.conf an, unter diesem Usernamen und dieser Gruppe zu laufen.
Der Effekt dieses kleine Hacks: lighttpd darf auf das eigentlich ausserhalb des Containers liegende Verzeichnis mit den git-Repositories frei zugreifen, weil der Linux server “meint”, es gehöre ihm.
Dann macht das Script noch pseudo-Userangaben, um git glücklich zu machen und erstellt einen user namens git mit Schreibrechten. Und in der letzten Zeile wird der lighttpd Server gestartet. Er wird nicht als daemon, sondern im Vordergrund gestartet, weil der Docker-Container sich sonst umgehend wieder beenden würde.