Wer im Plattformteam arbeitet, kennt solche Gespräche:
„Security sagt: Wir müssen gehärtete Base-Images verwenden."
„Prima – wo trage ich jetzt die Credentials für noch eine weitere Registry ein?"
„Und wie stellen wir sicher, dass alle sie auch wirklich nutzen?"
Oder diese hier:
„Warum sind unsere Builds so langsam?"
„Wir pullen dasselbe 500-MB-Image in jedem einzelnen Job neu von Docker Hub."
„Kann man die nicht irgendwo cachen?"
Ich arbeite bei GitLab an der Container Virtual Registry – einem Pull-Through-Cache, der vor den vorgelagerten Registries sitzt: Docker Hub, dhi.io (Docker Hardened Images), MCR und Quay. Teams erhalten einen einzigen Endpunkt zum Pullen. Images werden beim ersten Abruf gecacht; alle nachfolgenden Pulls kommen aus dem Cache. Das Entwicklungsteam muss nicht wissen, aus welchem Upstream ein bestimmtes Image stammt.
Dieser Artikel zeigt die Einrichtung der Container Virtual Registry – mit Docker Hardened Images als konkretem Anwendungsfall, da diese Kombination für Teams mit Sicherheitsanforderungen besonders naheliegt.
Das Problem: Registry-Wildwuchs im Plattformteam
Die Plattformteams, mit denen ich spreche, verwalten Container-Images über drei bis fünf Registries:
- Docker Hub für die meisten Base-Images
- dhi.io für Docker Hardened Images (sicherheitskritische Workloads)
- MCR für .NET- und Azure-Tooling
- Quay.io für das Red-Hat-Ökosystem
- Interne Registries für proprietäre Images
Jede davon hat eigene Authentifizierungsmechanismen, unterschiedliche Netzwerklatenz und eine eigene Pfadstruktur für Images.
CI/CD-Konfigurationen füllen sich mit registry-spezifischer Logik. Credential-Management wird zum eigenständigen Projekt. Und jeder Pipeline-Job lädt dieselben Base-Images erneut über das Netz – obwohl sie sich seit Wochen nicht geändert haben.
Container Virtual Registry konsolidiert das: eine Registry-URL, ein Authentifizierungsfluss über GitLab, gecachte Images aus GitLab-Infrastruktur statt wiederholter Internet-Traversierung.
Funktionsweise
Das Modell ist geradlinig:
Pipeline ruft ab:
gitlab.com/virtual_registries/container/1000016/python:3.13
Virtual Registry prüft:
1. Im Cache vorhanden? → Direkt zurückgeben
2. Nein? → Vom Upstream laden, cachen, zurückgeben
Upstreams werden in Prioritätsreihenfolge konfiguriert. Bei einem eingehenden Pull-Request durchsucht die Virtual Registry die Upstreams der Reihe nach, bis das Image gefunden wird. Das Ergebnis wird für einen konfigurierbaren Zeitraum gecacht – standardmäßig 24 Stunden.
┌─────────────────────────────────────────────────────────┐ │ CI/CD Pipeline │ │ │ │ │ ▼ │ │ gitlab.com/virtual_registries/container/<id>/image │ └─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐ │ Container Virtual Registry │ │ │ │ Upstream 1: Docker Hub ────────────────┐ │ │ Upstream 2: dhi.io (Hardened) ────────┐│ │ │ Upstream 3: MCR ─────────────────────┐││ │ │ Upstream 4: Quay.io ────────────────┐│││ │ │ ││││ │ │ ┌─────────────────┴┴┴┴──┐ │ │ │ Cache │ │ │ │ (manifests + layers) │ │ │ └───────────────────────┘ │ └─────────────────────────────────────────────────────────┘
Was das konkret bringt – besonders mit Docker Hardened Images
Docker Hardened Images zeichnen sich durch minimale Angriffsfläche, nahezu keine bekannten CVEs, vollständige Software Bills of Materials (SBOMs) und SLSA-Provenance aus. Für Teams, die Base-Images für sicherheitskritische Workloads evaluieren, gehören sie auf die Shortlist.
Der Wechsel zu dhi.io erzeugt jedoch dieselbe operative Reibung wie jede neue Registry:
- Credential-Verteilung: Docker-Credentials müssen auf alle Systeme verteilt werden, die Images von dhi.io abrufen.
- CI/CD-Anpassungen: Jede Pipeline muss für die Authentifizierung mit dhi.io aktualisiert werden.
- Akzeptanzproblem: Ohne zentrale Steuerung greifen Teams weiterhin auf reguläre Images zurück.
- Fehlende Transparenz: Ob Teams tatsächlich die gehärteten Varianten nutzen, ist kaum nachvollziehbar.
Die Virtual Registry löst jeden dieser Punkte:
Einzelne Credential: Teams authentifizieren sich bei GitLab. Die Virtual Registry übernimmt die Upstream-Authentifizierung. Docker-Credentials werden einmalig auf Registry-Ebene konfiguriert und gelten für alle Pulls.
Keine per-Team-CI/CD-Änderungen: Pipelines auf die Virtual Registry zeigen lassen – fertig. Die Upstream-Konfiguration ist zentralisiert.
Schrittweise Einführung: Da Images mit ihrem vollständigen Pfad gecacht werden, ist im Cache sichtbar, was tatsächlich abgerufen wird. Wird library/python:3.11 statt der gehärteten Variante gepullt, ist das erkennbar.
Audit-Trail: Der Cache zeigt exakt, welche Images aktiv genutzt werden – nachvollziehbar für Compliance-Zwecke und als Grundlage für das Verständnis der tatsächlichen Infrastruktur-Abhängigkeiten.
Wer das Konzept verstanden hat und die Einrichtung zu einem späteren Zeitpunkt in Angriff nimmt: Die wesentlichen Konzepte sind damit abgedeckt. Die technische Konfiguration folgt im nächsten Abschnitt.
Einrichtung
Die folgende Einrichtung nutzt den Python-Client aus dem Demo-Projekt.
Virtual Registry erstellen
from virtual_registry_client import VirtualRegistryClient
client = VirtualRegistryClient()
registry = client.create_virtual_registry(
group_id="785414", # ID der obersten Gruppe
name="platform-images",
description="Cached container images for platform teams"
)
print(f"Registry ID: {registry['id']}") # Diese ID wird für die Pull-URL benötigt
Docker Hub als Upstream hinzufügen
Für offizielle Images wie Alpine, Python usw.:
docker_upstream = client.create_upstream(
registry_id=registry['id'],
url="https://registry-1.docker.io",
name="Docker Hub",
cache_validity_hours=24
)
Docker Hardened Images (dhi.io) hinzufügen
Docker Hardened Images werden auf dhi.io gehostet – einer separaten Registry mit Authentifizierungspflicht:
dhi_upstream = client.create_upstream(
registry_id=registry['id'],
url="https://dhi.io",
name="Docker Hardened Images",
username="your-docker-username",
password="your-docker-access-token",
cache_validity_hours=24
)
Weitere Upstreams hinzufügen
# MCR für .NET-Teams client.create_upstream(
registry_id=registry['id'],
url="https://mcr.microsoft.com",
name="Microsoft Container Registry",
cache_validity_hours=48
)
# Quay für das Red-Hat-Ökosystem client.create_upstream(
registry_id=registry['id'],
url="https://quay.io",
name="Quay.io",
cache_validity_hours=24
)
CI/CD aktualisieren
Eine .gitlab-ci.yml, die über die Virtual Registry pullt:
variables:
VIRTUAL_REGISTRY_ID: <your_virtual_registry_ID>
build:
image: docker:24
services:
- docker:24-dind
before_script:
# Authentifizierung bei GitLab – Upstream-Auth wird übernommen
- echo "${CI_JOB_TOKEN}" | docker login -u gitlab-ci-token --password-stdin gitlab.com
script:
# Alle Pulls laufen über die zentrale Virtual Registry
# Offizielle Docker Hub Images (library/-Präfix erforderlich)
- docker pull gitlab.com/virtual_registries/container/${VIRTUAL_REGISTRY_ID}/library/alpine:latest
# Docker Hardened Images von dhi.io (kein Präfix nötig)
- docker pull gitlab.com/virtual_registries/container/${VIRTUAL_REGISTRY_ID}/python:3.13
# .NET von MCR
- docker pull gitlab.com/virtual_registries/container/${VIRTUAL_REGISTRY_ID}/dotnet/sdk:8.0
Image-Pfadformate
Verschiedene Registries verwenden unterschiedliche Pfadkonventionen:
| Registry | Beispiel-Pull-URL |
|---|---|
| Docker Hub (offiziell) | .../library/python:3.11-slim |
| Docker Hardened Images (dhi.io) | .../python:3.13 |
| MCR | .../dotnet/sdk:8.0 |
| Quay.io | .../prometheus/prometheus:latest |
Funktionsprüfung
Nach einigen Pulls lässt sich der Cache überprüfen:
upstreams = client.list_registry_upstreams(registry['id']) for upstream in upstreams:
entries = client.list_cache_entries(upstream['id'])
print(f"{upstream['name']}: {len(entries)} cached entries")
Messergebnisse
Testergebnisse beim Pullen über die Virtual Registry:
| Messgröße | Ohne Cache | Mit warmem Cache |
|---|---|---|
| Pull-Zeit (Alpine) | 10,3 s | 4,2 s |
| Pull-Zeit (Python 3.13 DHI) | 11,6 s | ~4 s |
| Netzwerk-Roundtrips zum Upstream | Jeder Pull | Nur Cache-Misses |
Der erste Pull hat dieselbe Dauer – das Image muss vom Upstream geladen werden. Jeder weitere Pull innerhalb der Cache-Gültigkeitsdauer kommt direkt aus GitLab-Storage: kein Netzwerk-Hop zu Docker Hub, dhi.io, MCR oder einer anderen Registry.
Bei Teams mit vielen Pipeline-Jobs pro Tag summiert sich das zu einem messbaren Gewinn bei den Build-Laufzeiten.
Praktische Hinweise
Cache-Gültigkeit
Der Standard sind 24 Stunden. Für sicherheitskritische Images, bei denen Patches schnell verfügbar sein sollen, empfiehlt sich ein kürzeres Intervall:
client.create_upstream(
registry_id=registry['id'],
url="https://dhi.io",
name="Docker Hardened Images",
username="your-username",
password="your-token",
cache_validity_hours=12
)
Für stabile Images mit fixen Versions-Tags ist ein längeres Intervall problemlos.
Upstream-Priorität
Upstreams werden der Reihe nach geprüft. Bei gleichnamigen Images in verschiedenen Registries gewinnt der erste passende Upstream.
Limits
- Maximal 20 Virtual Registries pro Gruppe
- Maximal 20 Upstreams pro Virtual Registry
Konfiguration über die Oberfläche
Virtual Registries und Upstreams lassen sich auch direkt in der GitLab-Oberfläche einrichten – ohne API-Aufrufe. Unter Einstellungen > Pakete und Registries > Virtual Registry der jeweiligen Gruppe stehen folgende Optionen zur Verfügung:
- Virtual Registries erstellen und verwalten
- Upstreams hinzufügen, bearbeiten und neu anordnen
- Cache anzeigen und verwalten
- Überblick, welche Images abgerufen werden
Ausblick
In Entwicklung:
- Allow/Deny-Listen: Regex-basierte Steuerung, welche Images aus welchen Upstreams abgerufen werden dürfen.
Container Virtual Registry befindet sich in der Beta-Phase. Die Funktion wird produktiv eingesetzt und wird weiterentwickelt – Feedback fließt direkt in die Priorisierung ein.
Feedback
Wer als Plattformteam mit Registry-Wildwuchs zu kämpfen hat: Ich möchte verstehen, wie die aktuelle Situation aussieht.
- Wie viele Upstream-Registries werden verwaltet?
- Wo liegt der größte Schmerzpunkt?
- Würde ein solcher Ansatz helfen – und falls nicht: Was fehlt?
Erfahrungen und Rückmeldungen gerne im Container Virtual Registry Feedback-Issue teilen.
Weiterführende Ressourcen
- Neue GitLab-Metriken und Registry-Funktionen zur Optimierung von CI/CD-Pipelines
- Container Virtual Registry – Dokumentation
- Container Virtual Registry – API
Für deutsche Unternehmen könnte dies folgende Themen betreffen
Teams, die sicherheitsgehärtete Base-Images mit vollständigen SBOMs und SLSA-Provenance einsetzen, haben möglicherweise auch Compliance-Überlegungen – beispielsweise in Bereichen wie Sicherheit der Software-Lieferkette, Nachvollziehbarkeit von Image-Abhängigkeiten und zentralem Audit-Trail.
Regulatorische Frameworks wie NIS2 und der Cyber Resilience Act adressieren ähnliche Themen rund um Software-Lieferketten und SBOM-Transparenz. Für konkrete Compliance-Anforderungen empfiehlt sich Rücksprache mit entsprechender Fachberatung.




