Betrieb einer privaten Docker-Registry
Docker-Images können in eine zentrale Registry hochgeladen und von dort aus von sämtlichen Nutzern heruntergeladen werden. Die größte öffentliche Image-Registry ist Docker Hub. Jeder kann eine solche Registry aber auch in einem privaten Netzwerk betreiben, um Images nur Netzwerk-intern verfügbar zu machen.
Warum?
Die Motivationen hinter einer privaten Registry sind sicherlich vielfältig. Ein Grund wäre beispielsweise, dass man nicht unbedingt jedes Image auf Docker Hub aller Welt zur Verfügung stellen möchte, zumal für kostenlose Accounts aktuell nur ein privates Repository möglich ist.
Ansonsten ist eine private Registry auch für Firmen und Selfhosting-Freunde interessant. Firmen müssen abwägen, ob die einfachere Standard-Registry ausreicht - oder ob sie sich mit Docker EE die Docker Trusted Registry samt Image-Signing und automatischen Sicherheitsscans leisten möchten.
Eine lokale Registry starten
Die besagte Registry wird von Docker selbst mit dem Image
Der gezeigte Befehl startet eine Registry-Instanz mit dem Namen
Images hoch- und herunterladen
Für die Befehle
Im folgenden Beispiel lade ich ein Alpine-Image von Docker Hub herunter, erstelle einen neuen Tag mit dem genannten Prefix und lade es in meine private Registry hoch.
Das Alpine-Image mit dem neu erstellten Tag ist ab sofort in der lokalen Registry verfügbar. Lokal kann ich das Image also löschen.
Nun kann Alpine-Linux aus der privaten Registry heruntergeladen werden. Um
Den Storage auslagern
Standardmäßig wird das Verzeichnis
Registry im Netzwerk verfügbar machen
Bisher handelt es sich bei unserer privaten Registry um eine lokale Registry. Um sie öffentlich im Netzwerk zugänglich zu machen, verlangt Docker eine Absicherung per TLS, womit also ein TLS-Zertifikat erforderlich ist. Für dieses Beispiel habe ich ein Verzeichnis
Mit einem Bind Mount kann das Verzeichnis nun in den Container eingehängt werden:
Die
Authentifizierung für die Registry
Die einfachste Authentifizierungsmethode für die private Registry ist
Zuerst lege ich ein lokales Verzeichnis
Beim Start des Containers lege erstelle ich nun einen Bind Mount, mit dem ich das
Der mittlere Teil stellt die Änderung im Vergleich zum vorherigen Befehl dar. Nun ist ein Login mit
Erweiterte Konfiguration
Alle konfigurierbaren Eigenschaften der Registry befinden sich in der Datei
Einzelne Werte überschreiben: Jeder Konfigurationswert kann mit einer Umgebungsvariable als
Dabei wird immer der gesamte "Pfad" zum Key angegeben. Die Eigenschaft
Gesamte Konfiguration überschreiben: Wem das zu viele einzelne Umgebungsvariablen sind, kann auch die gesamte Standard-Konfiguration überschreiben, indem er einfach eine eigene YAML-Datei mountet. Zur besseren Übersichtlichkeit lasse hier die oben aufgeführten Optionen weg:
Welche Konfigurationswerte verfügbar sind, ist in der entsprechenden Referenz aufgeführt.
Docker-Images können in eine zentrale Registry hochgeladen und von dort aus von sämtlichen Nutzern heruntergeladen werden. Die größte öffentliche Image-Registry ist Docker Hub. Jeder kann eine solche Registry aber auch in einem privaten Netzwerk betreiben, um Images nur Netzwerk-intern verfügbar zu machen.
Warum?
Die Motivationen hinter einer privaten Registry sind sicherlich vielfältig. Ein Grund wäre beispielsweise, dass man nicht unbedingt jedes Image auf Docker Hub aller Welt zur Verfügung stellen möchte, zumal für kostenlose Accounts aktuell nur ein privates Repository möglich ist.
Ansonsten ist eine private Registry auch für Firmen und Selfhosting-Freunde interessant. Firmen müssen abwägen, ob die einfachere Standard-Registry ausreicht - oder ob sie sich mit Docker EE die Docker Trusted Registry samt Image-Signing und automatischen Sicherheitsscans leisten möchten.
Eine lokale Registry starten
Die besagte Registry wird von Docker selbst mit dem Image
registry
zur Verfügung gestellt, d. h. die Registry ist selbst ein Docker-Container. Inzwischen sollte Version 2 verwendet werden. Starten kann man den Registry-Container dann folgendermaßen:
Bash:
$ docker container run -d -p 5000:5000 --restart always --name private-registry registry:2
Der gezeigte Befehl startet eine Registry-Instanz mit dem Namen
private-registry
, die unter localhost:5000
auf dem Host erreichbar ist. Für erste Tests reicht dieses Setup aus.Images hoch- und herunterladen
Für die Befehle
docker image push
zum Hochladen und docker image pull
zum Herunterladen eines Images gibt es eine Konvention: Startet ein Image-Tag mit <Host>:<Port>/
, handelt es sich dabei um die Adresse einer Registry als Ziel bzw. Quelle. Für unsere Registry bedeutet das, dass wir jedes Image mit localhost:5000/
als Prefix taggen müssen.Im folgenden Beispiel lade ich ein Alpine-Image von Docker Hub herunter, erstelle einen neuen Tag mit dem genannten Prefix und lade es in meine private Registry hoch.
Bash:
$ docker image pull alpine:3.9
$ docker image tag alpine:3.9 localhost:5000/alpine:3.9
$ docker image push localhost:5000/alpine:3.9
Das Alpine-Image mit dem neu erstellten Tag ist ab sofort in der lokalen Registry verfügbar. Lokal kann ich das Image also löschen.
Bash:
$ docker image rm alpine:3.9
$ docker image rm localhost:5000/alpine:3.9
Nun kann Alpine-Linux aus der privaten Registry heruntergeladen werden. Um
docker image pull
mitzuteilen, dass die Quelle von Docker Hub abweicht, verwende ich wieder die Adresse meiner Registry als Prefix.
Bash:
$ docker image pull localhost:5000/alpine:3.9
Den Storage auslagern
Standardmäßig wird das Verzeichnis
/var/lib/registry
, in dem der Registry-Storage liegt, automatisch als anonymes Volume eingebunden. Um die Storage-Verwaltung zu erleichtern, bietet es sich an, einen Bind Mount oder ein Named Volume dafür zu erstellen.
Bash:
$ docker volume create registry-data
$ docker container run -d \
-p 5000:5000
--restart=always
--name private-registry
--mount type=volume,source=registry-data,destination=/var/lib/registry
registry:2
Registry im Netzwerk verfügbar machen
Bisher handelt es sich bei unserer privaten Registry um eine lokale Registry. Um sie öffentlich im Netzwerk zugänglich zu machen, verlangt Docker eine Absicherung per TLS, womit also ein TLS-Zertifikat erforderlich ist. Für dieses Beispiel habe ich ein Verzeichnis
certs
erstellt, in welchem sich das TLS-Zertifikat domain.crt
und der TLS-Schlüssel domain.key
befinden.Mit einem Bind Mount kann das Verzeichnis nun in den Container eingehängt werden:
Bash:
$ docker container run -d \
--restart=always \
--name private-registry \
--mount type=bind,source=/pfad/zu/certs,destination=/certs \
-e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
-p 443:443
registry:2
Die
-e
-Optionen legen die Umgebungsvariablen fest, die von der Registry zur TLS-Konfiguration herangezogen werden. Außerdem ändern sich die Ports nun zu 443
.Authentifizierung für die Registry
Die einfachste Authentifizierungsmethode für die private Registry ist
htpasswd
, die ich hier demonstrieren werde. Für ernsthafte Anwendungsfälle sollte aber auch nginx als Authentifizierungs-Proxy in Erwägung gezogen werden.Zuerst lege ich ein lokales Verzeichnis
auth
an und erstelle eine htpasswd
-Datei mit Zugangsdaten.
Bash:
$ mkdir auth
$ echo "dominik passwort123" >> auth/htpasswd
Beim Start des Containers lege erstelle ich nun einen Bind Mount, mit dem ich das
auth
-Verzeichnis an der Stelle /auth
im Container einhänge. Außerdem setze ich zwei Umgebungsvariablen, welche ebenfalls von der Registry verwendet werden.
Bash:
$ docker container run -d \
--restart=always \
--name private-registry \
\
--mount type=bind,source=/pfad/zu/auth,destination=/auth \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
\
--mount type=bind,source=/pfad/zu/certs,destination=/certs \
-e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
-p 443:443
registry:2
Der mittlere Teil stellt die Änderung im Vergleich zum vorherigen Befehl dar. Nun ist ein Login mit
docker login localhost:443
erforderlich, um Images hoch- und herunterladen zu können.Erweiterte Konfiguration
Alle konfigurierbaren Eigenschaften der Registry befinden sich in der Datei
/etc/docker/registry/config.yml
. Zwar sind diese Eigenschaften mit sinnvollen Standardwerten versehen, wer seine Registry aber im produktiven Einsatz betreiben möchte, sollte diese Werte unbedingt überprüfen. Bei Bedarf gibt es zwei Möglichkeiten, die Konfigurationswerte anzupassen.Einzelne Werte überschreiben: Jeder Konfigurationswert kann mit einer Umgebungsvariable als
-e
-Option beim Start des Containers überschrieben werden. Der Name der Umgebungsvariable hat immer die Form REGISTRY_<EIGENSCHAFT>
, wobei <EIGENSCHAFT>
der Name des YAML-Keys ist.
YAML:
storage:
filesystem:
rootdirectory: /var/lib/registry
Dabei wird immer der gesamte "Pfad" zum Key angegeben. Die Eigenschaft
rootdirectory
ließe sich beispielsweise mit folgender Option überschreiben:
Bash:
-e REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY=/mein/pfad
Gesamte Konfiguration überschreiben: Wem das zu viele einzelne Umgebungsvariablen sind, kann auch die gesamte Standard-Konfiguration überschreiben, indem er einfach eine eigene YAML-Datei mountet. Zur besseren Übersichtlichkeit lasse hier die oben aufgeführten Optionen weg:
Bash:
$ docker container run -d \
-p 5000:5000 \
--restart=always \
--name private-registry \
--mount type=bind,source=/pfad/zur/eigenen/config.yml,destination=/etc/docker/registry/config.yml \
registry:2
Welche Konfigurationswerte verfügbar sind, ist in der entsprechenden Referenz aufgeführt.