Teil 1 Teil 2 Teil 3 Teil 4

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 Images
  • docker pull image : Holt das genannte Image
  • docker 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 Terminal
  • docker ps -a : Listet alle Container
  • docker rm container : löscht einen Container
  • docker rmi image : löscht ein image
  • docker 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.