Vous voulez héberger vos images Docker en interne sans dépendre de Docker Hub ou Quay.io ? Distribution (anciennement Docker Registry v2) est le projet CNCF officiel pour créer une registry privée on-premise. Léger, stateless et compatible OCI, il se déploie en quelques minutes avec Docker Compose.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »Ce guide couvre le déploiement complet d’une registry privée, de l’installation basique jusqu’à la configuration production avec TLS et authentification.
- Déployer une registry avec Docker ou Docker Compose
- Configurer TLS avec Let’s Encrypt ou certificats auto-signés
- Ajouter l’authentification htpasswd ou token
- Configurer le stockage : filesystem, S3, Azure, GCS
- Exécuter le garbage collection pour libérer l’espace disque
- Intégrer avec Kubernetes et vos pipelines CI/CD
Prérequis
Section intitulée « Prérequis »Qu’est-ce que Distribution ?
Section intitulée « Qu’est-ce que Distribution ? »Distribution est le projet open source officiel de la CNCF (Cloud Native Computing Foundation) pour implémenter une registry de conteneurs. C’est le même code qui propulse Docker Hub, GitHub Container Registry et de nombreuses registries cloud.
Un peu d’histoire
Section intitulée « Un peu d’histoire »Le projet a connu plusieurs noms au fil du temps, ce qui peut prêter à confusion :
| Période | Nom | Notes |
|---|---|---|
| 2013-2015 | Docker Registry v1 | Format propriétaire, abandonné |
| 2015-2020 | Docker Registry v2 | Nouveau format, base du standard OCI |
| 2020-présent | CNCF Distribution | Projet transféré à la CNCF, image registry:2 |
| 2024-présent | Distribution 3.0 | Version majeure avec support OCI natif amélioré |
Distribution vs alternatives
Section intitulée « Distribution vs alternatives »Quand utiliser Distribution plutôt qu’une solution plus complète comme Harbor ?
| Critère | Distribution | Harbor | Quay (self-hosted) |
|---|---|---|---|
| Complexité | Très simple | Moyenne | Élevée |
| Ressources | ~50 MB RAM | ~2 GB RAM | ~4 GB RAM |
| Interface web | ❌ Non | ✅ Oui | ✅ Oui |
| Scan de sécurité | ❌ Non | ✅ Trivy/Clair | ✅ Clair |
| RBAC avancé | ❌ Non | ✅ Oui | ✅ Oui |
| Réplication | ❌ Non | ✅ Oui | ✅ Oui |
| Cas d’usage | Dev, CI/CD, petit cluster | Entreprise | Entreprise Red Hat |
En résumé : Distribution est idéal pour les environnements où vous avez besoin d’une registry simple, rapide à déployer, sans interface graphique ni fonctionnalités avancées. Pour la production enterprise, préférez Harbor.
Déployer une registry basique
Section intitulée « Déployer une registry basique »Commençons par le déploiement le plus simple : une registry locale sans authentification ni TLS. Parfait pour le développement ou les tests.
Avec Docker (une commande)
Section intitulée « Avec Docker (une commande) »# Lancer une registry sur le port 5000docker run -d \ --name registry \ --restart always \ -p 5000:5000 \ -v registry-data:/var/lib/registry \ registry:2
# Vérifier que la registry fonctionnecurl http://localhost:5000/v2/# Résultat attendu : {}Tester avec une image
Section intitulée « Tester avec une image »# Taguer une image existante pour la registry localedocker pull alpine:3.19docker tag alpine:3.19 localhost:5000/alpine:3.19
# Pousser vers la registrydocker push localhost:5000/alpine:3.19
# Vérifier la présencecurl http://localhost:5000/v2/_catalog# {"repositories":["alpine"]}
# Supprimer l'image locale et la retirerdocker rmi localhost:5000/alpine:3.19docker pull localhost:5000/alpine:3.19Déployer avec Docker Compose (production)
Section intitulée « Déployer avec Docker Compose (production) »Pour un déploiement plus robuste avec TLS et authentification, utilisez Docker Compose avec un fichier de configuration dédié.
Structure des fichiers
Section intitulée « Structure des fichiers »registry/├── docker-compose.yml├── config.yml├── auth/│ └── htpasswd└── certs/ ├── domain.crt └── domain.keyConfiguration de la registry
Section intitulée « Configuration de la registry »Créez config.yml avec les paramètres de votre registry :
version: 0.1log: level: info formatter: text fields: service: registry
storage: filesystem: rootdirectory: /var/lib/registry delete: enabled: true cache: blobdescriptor: inmemory
http: addr: :5000 headers: X-Content-Type-Options: [nosniff] tls: certificate: /certs/domain.crt key: /certs/domain.key
auth: htpasswd: realm: Registry Realm path: /auth/htpasswd
health: storagedriver: enabled: true interval: 10s threshold: 3Docker Compose
Section intitulée « Docker Compose »Créez docker-compose.yml :
services: registry: image: registry:2.8 container_name: registry restart: always ports: - "5000:5000" environment: REGISTRY_STORAGE_DELETE_ENABLED: "true" volumes: - ./config.yml:/etc/docker/registry/config.yml:ro - ./auth:/auth:ro - ./certs:/certs:ro - registry-data:/var/lib/registry
volumes: registry-data:Configurer TLS
Section intitulée « Configurer TLS »Une registry de production doit être protégée par TLS. Docker refuse par défaut de communiquer avec une registry HTTP non sécurisée (sauf localhost).
Option 1 : Let’s Encrypt avec Traefik
Section intitulée « Option 1 : Let’s Encrypt avec Traefik »La solution la plus simple pour obtenir un certificat valide automatiquement. Traefik gère le renouvellement et le reverse proxy.
services: traefik: image: traefik:v3.0 command: - "--api.insecure=true" - "--providers.docker=true" - "--entrypoints.websecure.address=:443" - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true" - "--certificatesresolvers.letsencrypt.acme.email=admin@example.com" - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json" ports: - "443:443" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - letsencrypt:/letsencrypt
registry: image: registry:2.8 labels: - "traefik.enable=true" - "traefik.http.routers.registry.rule=Host(`registry.example.com`)" - "traefik.http.routers.registry.entrypoints=websecure" - "traefik.http.routers.registry.tls.certresolver=letsencrypt" volumes: - registry-data:/var/lib/registry
volumes: registry-data: letsencrypt:Option 2 : Certificat auto-signé
Section intitulée « Option 2 : Certificat auto-signé »Pour les environnements internes sans accès Internet, générez un certificat auto-signé. Les clients devront faire confiance à ce certificat.
-
Générer le certificat
Fenêtre de terminal # Créer le répertoire des certificatsmkdir -p certs# Générer une clé privée et un certificat (valide 365 jours)openssl req -x509 -nodes -days 365 -newkey rsa:4096 \-keyout certs/domain.key \-out certs/domain.crt \-subj "/CN=registry.local" \-addext "subjectAltName=DNS:registry.local,DNS:localhost,IP:192.168.1.100" -
Configurer Docker pour faire confiance au certificat
Sur chaque machine cliente :
Fenêtre de terminal # Créer le répertoire de certificats pour la registrysudo mkdir -p /etc/docker/certs.d/registry.local:5000# Copier le certificatsudo cp certs/domain.crt /etc/docker/certs.d/registry.local:5000/ca.crt# Redémarrer Dockersudo systemctl restart docker -
Tester la connexion
Fenêtre de terminal docker login registry.local:5000# Username: admin# Password: ********# Login Succeeded
Configurer l’authentification
Section intitulée « Configurer l’authentification »Distribution supporte plusieurs méthodes d’authentification. La plus simple est htpasswd, suffisante pour la plupart des cas d’usage.
Authentification htpasswd
Section intitulée « Authentification htpasswd »L’authentification htpasswd utilise un fichier de mots de passe hashés, comme Apache. Simple à mettre en place, mais sans gestion fine des permissions (tout ou rien).
-
Créer le fichier htpasswd
Fenêtre de terminal # Créer le répertoire authmkdir -p auth# Créer le premier utilisateur (bcrypt)docker run --rm --entrypoint htpasswd \httpd:2-alpine -Bbn admin motdepasse_securise > auth/htpasswd# Ajouter d'autres utilisateursdocker run --rm --entrypoint htpasswd \httpd:2-alpine -Bbn ci_user autre_motdepasse >> auth/htpasswd -
Configurer la registry
Dans
config.yml:auth:htpasswd:realm: Registry Realmpath: /auth/htpasswd -
Tester l’authentification
Fenêtre de terminal # Sans auth : erreur 401curl https://registry.local:5000/v2/# {"errors":[{"code":"UNAUTHORIZED",...}]}# Avec auth : succèscurl -u admin:motdepasse_securise https://registry.local:5000/v2/# {}
Authentification par token (avancé)
Section intitulée « Authentification par token (avancé) »Pour une gestion plus fine des permissions (lecture seule, écriture, par repository), utilisez un serveur d’authentification externe compatible avec le protocole Docker Registry Token.
Des projets comme Cesanta Docker Auth ou Portus implémentent ce protocole et permettent de définir des ACL granulaires.
Configurer le stockage
Section intitulée « Configurer le stockage »Par défaut, Distribution stocke les images sur le filesystem local. Pour la production, vous pouvez utiliser un stockage objet (S3, GCS, Azure Blob).
Stockage filesystem (défaut)
Section intitulée « Stockage filesystem (défaut) »Configuration simple pour un serveur unique. Le stockage est dans un volume Docker ou un répertoire local.
storage: filesystem: rootdirectory: /var/lib/registry delete: enabled: trueStockage S3 (AWS ou compatible)
Section intitulée « Stockage S3 (AWS ou compatible) »Pour la haute disponibilité et la scalabilité, utilisez un stockage objet S3. Compatible avec MinIO, Ceph, Wasabi et tout service S3-compatible.
storage: s3: accesskey: AKIAIOSFODNN7EXAMPLE secretkey: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY region: eu-west-1 bucket: my-registry-bucket rootdirectory: /registry encrypt: true secure: true v4auth: true delete: enabled: true cache: blobdescriptor: inmemoryStockage Azure Blob
Section intitulée « Stockage Azure Blob »storage: azure: accountname: myaccount accountkey: ${AZURE_STORAGE_KEY} container: registry realm: core.windows.netStockage Google Cloud Storage
Section intitulée « Stockage Google Cloud Storage »storage: gcs: bucket: my-registry-bucket keyfile: /path/to/keyfile.json rootdirectory: /registryGarbage Collection
Section intitulée « Garbage Collection »Les images Docker sont composées de layers (blobs) partagés entre images. Quand vous supprimez un tag, les blobs ne sont pas automatiquement supprimés — ils peuvent encore être référencés par d’autres images.
Le garbage collection (GC) identifie et supprime les blobs orphelins pour récupérer l’espace disque.
Supprimer un tag
Section intitulée « Supprimer un tag »Avant le GC, vous devez supprimer les tags que vous ne voulez plus conserver.
# Lister les tags d'une imagecurl -u admin:password https://registry.local:5000/v2/myapp/tags/list
# Récupérer le digest d'un tagDIGEST=$(curl -s -u admin:password \ -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \ https://registry.local:5000/v2/myapp/manifests/v1.0.0 \ -I | grep -i docker-content-digest | awk '{print $2}' | tr -d '\r')
# Supprimer le manifest (soft delete)curl -u admin:password -X DELETE \ https://registry.local:5000/v2/myapp/manifests/$DIGESTExécuter le garbage collection
Section intitulée « Exécuter le garbage collection »Le GC doit être exécuté registry arrêtée ou en mode read-only pour éviter les corruptions.
# Arrêter la registrydocker stop registry
# Dry-run : voir ce qui serait supprimédocker run --rm \ -v registry-data:/var/lib/registry \ -v $(pwd)/config.yml:/etc/docker/registry/config.yml \ registry:2 garbage-collect --dry-run /etc/docker/registry/config.yml
# Exécution réelledocker run --rm \ -v registry-data:/var/lib/registry \ -v $(pwd)/config.yml:/etc/docker/registry/config.yml \ registry:2 garbage-collect /etc/docker/registry/config.yml
# Redémarrer la registrydocker start registryIntégration Kubernetes
Section intitulée « Intégration Kubernetes »Pour utiliser votre registry privée avec Kubernetes, créez un Secret de type
docker-registry et référencez-le dans vos Pods.
Créer le Secret
Section intitulée « Créer le Secret »kubectl create secret docker-registry registry-credentials \ --docker-server=registry.local:5000 \ --docker-username=admin \ --docker-password=motdepasse_securise \ --docker-email=admin@example.com \ -n defaultUtiliser dans un Pod
Section intitulée « Utiliser dans un Pod »apiVersion: v1kind: Podmetadata: name: myappspec: containers: - name: app image: registry.local:5000/myapp:v1.0.0 imagePullSecrets: - name: registry-credentialsConfigurer containerd pour TLS
Section intitulée « Configurer containerd pour TLS »Si vous utilisez un certificat auto-signé, configurez containerd sur chaque nœud Kubernetes pour faire confiance à votre registry.
# Créer le répertoire de configurationsudo mkdir -p /etc/containerd/certs.d/registry.local:5000
# Créer hosts.tomlcat << 'EOF' | sudo tee /etc/containerd/certs.d/registry.local:5000/hosts.tomlserver = "https://registry.local:5000"
[host."https://registry.local:5000"] capabilities = ["pull", "resolve", "push"] ca = "/etc/containerd/certs.d/registry.local:5000/ca.crt"EOF
# Copier le certificat CAsudo cp domain.crt /etc/containerd/certs.d/registry.local:5000/ca.crt
# Redémarrer containerdsudo systemctl restart containerdAutomatiser avec GitHub Actions
Section intitulée « Automatiser avec GitHub Actions »Voici un workflow pour pousser vos images vers votre registry privée depuis GitHub Actions.
Configurer les secrets GitHub
Section intitulée « Configurer les secrets GitHub »Créez des secrets pour l’authentification à votre registry privée :
| Secret | Valeur |
|---|---|
REGISTRY_URL | registry.example.com:5000 |
REGISTRY_USERNAME | ci_user |
REGISTRY_PASSWORD | Mot de passe htpasswd |
Workflow GitHub Actions
Section intitulée « Workflow GitHub Actions »name: Build and Push to Private Registry
on: push: branches: [main] tags: ['v*']
env: IMAGE_NAME: myapp
jobs: build-and-push: runs-on: ubuntu-latest
steps: - name: Checkout code uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Docker Buildx uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1
- name: Login to Private Registry uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: registry: ${{ secrets.REGISTRY_URL }} username: ${{ secrets.REGISTRY_USERNAME }} password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Extract metadata id: meta uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1 with: images: ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }} tags: | type=ref,event=branch type=semver,pattern={{version}} type=sha,prefix=
- name: Build and push uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 with: context: . push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }}API REST de la registry
Section intitulée « API REST de la registry »Distribution expose une API REST standard (OCI Distribution Spec) pour interagir programmatiquement avec la registry.
Endpoints principaux
Section intitulée « Endpoints principaux »| Endpoint | Méthode | Description |
|---|---|---|
/v2/ | GET | Vérifier que la registry fonctionne |
/v2/_catalog | GET | Lister tous les repositories |
/v2/{name}/tags/list | GET | Lister les tags d’une image |
/v2/{name}/manifests/{ref} | GET | Récupérer un manifest |
/v2/{name}/manifests/{ref} | DELETE | Supprimer un manifest |
/v2/{name}/blobs/{digest} | GET | Télécharger un blob |
Exemples d’utilisation
Section intitulée « Exemples d’utilisation »# Lister tous les repositoriescurl -u admin:password https://registry.local:5000/v2/_catalog# {"repositories":["alpine","myapp","nginx"]}
# Lister les tags d'une imagecurl -u admin:password https://registry.local:5000/v2/myapp/tags/list# {"name":"myapp","tags":["v1.0.0","v1.1.0","latest"]}
# Récupérer le manifest d'un tagcurl -u admin:password \ -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \ https://registry.local:5000/v2/myapp/manifests/v1.0.0Dépannage
Section intitulée « Dépannage »Voici les erreurs les plus fréquentes lors du déploiement d’une registry Distribution et leurs solutions.
| Symptôme | Cause probable | Solution |
|---|---|---|
http: server gave HTTP response to HTTPS client | Registry non TLS | Configurer TLS ou ajouter à insecure-registries |
x509: certificate signed by unknown authority | Certificat auto-signé non reconnu | Copier le CA dans /etc/docker/certs.d/ |
unauthorized: authentication required | Credentials incorrects | Vérifier htpasswd et docker login |
manifest unknown | Tag inexistant | Vérifier le nom exact avec l’API /tags/list |
denied: requested access is denied | Permissions insuffisantes | Vérifier la config auth dans config.yml |
| Espace disque plein | Blobs orphelins | Exécuter le garbage collection |
À retenir
Section intitulée « À retenir »-
Distribution est la registry officielle CNCF, le même code qui propulse Docker Hub. Simple, léger (~50 MB RAM), idéal pour le dev et les petits clusters.
-
TLS est obligatoire pour un accès réseau. Utilisez Let’s Encrypt avec Traefik ou des certificats auto-signés avec configuration des clients.
-
L’authentification htpasswd suffit pour la plupart des cas. Pour du RBAC avancé, passez à Harbor ou utilisez un serveur de tokens externe.
-
Le garbage collection libère l’espace des blobs orphelins. Planifiez-le régulièrement, registry arrêtée ou en read-only.
-
Pour la production enterprise, préférez Harbor : interface web, scan de sécurité, réplication, RBAC. Distribution reste excellent comme registry cache ou pour le dev.