Aller au contenu
Conteneurs & Orchestration medium

Gérer vos images conteneurs avec Crane

21 min de lecture

Crane vous permet de copier, inspecter et optimiser vos images de conteneurs sans avoir besoin du démon Docker. Développé par Google dans le cadre du projet go-containerregistry, cet outil en ligne de commande communique directement avec les registries via l’API OCI. Vous pouvez l’utiliser pour copier une image entre Docker Hub et votre registry privée en une seule commande, inspecter les layers d’une image, ou aplatir une image multi-layers pour réduire sa taille. Particulièrement utile en CI/CD où Docker n’est pas toujours disponible, Crane s’installe en quelques secondes et fonctionne sur Linux, macOS et Windows.

  • Installer Crane sur Linux ou macOS avec asdf ou Homebrew
  • Inspecter les images d’une registry (manifest, config, layers)
  • Copier des images entre registries sans Docker
  • Optimiser les images en aplatissant les layers
  • Automatiser la gestion des tags et versions en CI/CD

Crane est un outil développé par Google qui permet de manipuler les images de conteneurs sans avoir besoin du démon Docker. C’est particulièrement utile dans les environnements CI/CD où Docker n’est pas toujours disponible, ou quand vous voulez simplement inspecter ou copier des images rapidement.

Crane : opérations sur les registries - copie, manifest, config, flatten

Cas d’usage typiques :

BesoinAvec DockerAvec Crane
Copier une image entre registriesdocker pull + docker tag + docker pushcrane copy source dest
Voir les layers d’une imagedocker history (limité)crane manifest (détaillé)
Obtenir le digest SHA256docker inspectcrane digest
Exporter une image en tardocker savecrane export
Aplatir les layersImpossible nativementcrane flatten

Plusieurs méthodes d’installation sont disponibles selon votre système d’exploitation et vos préférences. Choisissez celle qui correspond le mieux à votre environnement de travail.

asdf-vm est un gestionnaire de versions polyvalent. C’est ma méthode recommandée car elle permet de fixer une version précise par projet grâce au fichier .tool-versions.

Fenêtre de terminal
asdf plugin add crane
asdf install crane 0.20.7
asdf set --home crane 0.20.7

L’avantage d’asdf est de pouvoir utiliser différentes versions de Crane selon les projets, ce qui est utile quand vous travaillez sur des codebases avec des contraintes de compatibilité différentes.

Vérification de l’installation :

Fenêtre de terminal
crane version
# Résultat attendu : 0.20.7

Pour les exemples qui suivent, je vais utiliser une registry locale. Cela permet de tester sans risque et sans créer de compte sur Docker Hub.

  1. Démarrer une registry locale

    Fenêtre de terminal
    docker run -d -p 5000:5000 --name registry registry:2.8

    La registry sera accessible sur localhost:5000.

  2. Créer un Dockerfile de test

    FROM alpine:3.21
    RUN apk --no-cache add nginx
    RUN mkdir -p /var/www/html
    COPY index.html /var/www/html/
    WORKDIR /var/www/html
    EXPOSE 80
  3. Créer le fichier index.html

    <!DOCTYPE html>
    <html>
    <body><h1>Test Crane</h1></body>
    </html>
  4. Construire et pousser l’image

    Fenêtre de terminal
    docker build . -t localhost:5000/monserveurweb:0.1
    docker push localhost:5000/monserveurweb:0.1

Crane propose une vingtaine de commandes pour interagir avec les registries. Dans cette section, nous allons voir les plus utilisées au quotidien. Toutes ces commandes communiquent directement avec la registry via HTTP/HTTPS, sans passer par le démon Docker.

La commande catalog interroge l’API de la registry pour récupérer la liste de toutes les images (appelées “repositories” dans la terminologie OCI). C’est le point de départ pour explorer le contenu d’une registry.

Fenêtre de terminal
crane catalog localhost:5000
# monserveurweb

Sur une registry publique comme Docker Hub, cette commande peut retourner des milliers de résultats. Utilisez-la plutôt sur vos registries privées.

Une fois que vous connaissez le nom d’une image, ls affiche tous les tags disponibles. Un tag est un pointeur vers une version spécifique de l’image.

Fenêtre de terminal
crane ls localhost:5000/monserveurweb
# 0.1

Cette commande est utile pour vérifier quelles versions sont disponibles avant de copier ou de déployer une image.

Le manifest contient les métadonnées de l’image : ses layers, leur taille, leur digest SHA256. C’est l’équivalent d’une “table des matières” de l’image.

Fenêtre de terminal
crane manifest localhost:5000/monserveurweb:0.1 | jq
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"config": {
"mediaType": "application/vnd.docker.container.image.v1+json",
"size": 1476,
"digest": "sha256:28865db66a2e..."
},
"layers": [
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2829647,
"digest": "sha256:f7dab3ab2d6e..."
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 617439,
"digest": "sha256:607cfc754b09..."
}
]
}

La commande config affiche la configuration complète de l’image : historique des layers, variables d’environnement, commande par défaut, labels…

Fenêtre de terminal
crane config localhost:5000/monserveurweb:0.1 | jq
{
"architecture": "amd64",
"os": "linux",
"config": {
"Cmd": ["/bin/sh"],
"Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],
"WorkingDir": "/var/www/html",
"ExposedPorts": { "80/tcp": {} }
},
"history": [
{ "created_by": "/bin/sh -c #(nop) ADD file:..." },
{ "created_by": "RUN /bin/sh -c apk --no-cache add nginx" },
{ "created_by": "COPY index.html /var/www/html/" }
]
}

Le digest est l’identifiant unique et immuable d’une image. Contrairement au tag qui peut être modifié, le digest garantit que vous utilisez exactement la même image.

Fenêtre de terminal
crane digest localhost:5000/monserveurweb:0.1
# sha256:beb9fd8064525aa1787d60cfd6eba89c4580125f1e32a42e0ab006912cf48038

C’est la fonctionnalité phare de Crane. En une seule commande, vous copiez une image d’une registry à une autre, y compris les images multi-architectures. Contrairement à la méthode traditionnelle (docker pull + docker tag + docker push), Crane ne télécharge pas l’image sur votre machine : il orchestre un transfert direct entre les deux registries.

Fenêtre de terminal
crane copy alpine:3.21 localhost:5000/alpine:3.21 --platform linux/amd64
# 2026/01/23 09:13:02 Copying from alpine:3.21 to localhost:5000/alpine:3.21
# 2026/01/23 09:13:03 existing blob: sha256:39477eca89b9...
# 2026/01/23 09:13:03 existing blob: sha256:f637881d1138...
# 2026/01/23 09:13:03 localhost:5000/alpine:3.21: digest: sha256:41c81533... size: 1022

Cette commande copie l’image alpine:3.21 depuis Docker Hub vers votre registry locale. L’option --platform limite la copie à une architecture spécifique. Sans cette option, toutes les architectures sont copiées.

Ce que Crane fait en coulisses :

  1. Récupère le manifest de l’image source (la “table des matières”)
  2. Pour chaque layer, vérifie s’il existe déjà dans la destination
  3. Copie uniquement les layers manquants (économie de bande passante)
  4. Pousse le manifest final pour rendre l’image disponible

Vérification :

Fenêtre de terminal
crane catalog localhost:5000
# alpine
# nginx
crane ls localhost:5000/alpine
# 3.21

Une image Docker est composée de plusieurs layers (couches), chacune représentant une modification du système de fichiers. Par exemple, l’image nginx:1.27.3-alpine contient 8 layers : l’image de base Alpine, l’installation de nginx, les fichiers de configuration, etc.

La commande flatten fusionne tous ces layers en un seul. L’image résultante contient exactement les mêmes fichiers, mais dans une structure simplifiée.

Fenêtre de terminal
# Copier nginx vers la registry locale
crane copy nginx:1.27.3-alpine localhost:5000/nginx:1.27.3-alpine --platform linux/amd64
# Aplatir l'image
crane flatten localhost:5000/nginx:1.27.3-alpine \
-t localhost:5000/nginx:1.27.3-alpine-flat

L’option -t (ou --tag) spécifie le nom et le tag de l’image aplatie. Ici, nous créons une nouvelle image plutôt que de modifier l’originale.

Comparaison avant/après :

Fenêtre de terminal
# Avant : 8 layers
crane manifest localhost:5000/nginx:1.27.3-alpine | jq '.layers | length'
# 8
# Après : 1 seul layer
crane manifest localhost:5000/nginx:1.27.3-alpine-flat | jq '.layers | length'
# 1

Vérification de la taille :

Fenêtre de terminal
docker pull localhost:5000/nginx:1.27.3-alpine
docker pull localhost:5000/nginx:1.27.3-alpine-flat
docker images | grep nginx
# localhost:5000/nginx 1.27.3-alpine 20.5MB
# localhost:5000/nginx 1.27.3-alpine-flat 20.2MB

Les tags sont des pointeurs vers des versions spécifiques d’une image. Un même digest (identifiant unique basé sur le contenu) peut avoir plusieurs tags. Par exemple, une image peut être taguée 1.0.0, 1.0 et latest simultanément.

La commande tag crée un nouveau pointeur vers une image existante. C’est une opération légère qui ne duplique pas les données : les deux tags pointent vers le même contenu.

Fenêtre de terminal
crane tag localhost:5000/alpine:3.21 latest

Cette commande ajoute le tag latest à l’image qui porte déjà le tag 3.21. Les deux tags coexistent et pointent vers la même version.

Vérification :

Fenêtre de terminal
crane ls localhost:5000/alpine
# 3.21
# latest

La commande validate vérifie que l’image est conforme aux spécifications OCI (Open Container Initiative). Elle contrôle que le manifest est bien formé et que tous les layers référencés existent.

Fenêtre de terminal
crane validate --remote localhost:5000/monserveurweb:0.1
# PASS: localhost:5000/monserveurweb:0.1

Utilisez cette commande après une copie ou une modification pour vous assurer que l’image est utilisable.

Les registries privées (Docker Hub pour les images privées, GCR, ECR, ACR, Harbor, GitLab Registry…) nécessitent une authentification avant de pouvoir lire ou écrire des images.

Crane stocke les credentials dans le même fichier que Docker (~/.docker/config.json), ce qui permet de réutiliser les authentifications existantes.

Fenêtre de terminal
crane auth login registry.example.com -u username -p "password"
# logged in via /home/user/.docker/config.json

Sur les providers cloud, vous pouvez utiliser des tokens temporaires. Par exemple, pour GCR avec gcloud :

Fenêtre de terminal
crane auth login gcr.io -u oauth2accesstoken -p "$(gcloud auth print-access-token)"

Ces commandes sont moins utilisées au quotidien mais peuvent s’avérer très utiles dans des cas spécifiques : audit de sécurité, débogage, migration d’images, ou automatisation avancée.

Les labels sont des métadonnées attachées à une image (version, mainteneur, licence, etc.). La commande mutate permet de les modifier sans reconstruire l’image, ce qui est utile pour ajouter des informations après coup.

Fenêtre de terminal
crane mutate localhost:5000/alpine:3.21 \
--label "maintainer=team@example.com" \
--label "version=3.21" \
-t localhost:5000/alpine:3.21-labeled

Vérification des labels ajoutés :

Fenêtre de terminal
crane config localhost:5000/alpine:3.21-labeled | jq '.config.Labels'
# {
# "maintainer": "team@example.com",
# "version": "3.21"
# }

L’option -t (ou --tag) crée une nouvelle référence plutôt que de modifier l’originale.

Depuis la version 0.19.2, mutate permet de modifier la plateforme d’une image avec l’option --set-platform. C’est utile quand une image a été buildée avec une plateforme incorrecte ou manquante.

Fenêtre de terminal
crane mutate localhost:5000/myapp:1.0 \
--set-platform linux/amd64 \
-t localhost:5000/myapp:1.0-amd64

La commande export télécharge tous les layers d’une image et les assemble dans une archive tar. C’est utile pour l’archivage, le transfert vers un environnement déconnecté (air-gapped), ou l’analyse du contenu.

Fenêtre de terminal
crane export localhost:5000/monserveurweb:0.1 monserveurweb.tar

L’archive contient le système de fichiers complet de l’image. Vous pouvez l’explorer avec tar -tf monserveurweb.tar ou l’extraire pour inspection.

Pour du débogage approfondi, vous pouvez extraire un layer particulier. Chaque layer correspond à une instruction du Dockerfile (RUN, COPY, ADD). Cela permet de voir exactement ce que chaque étape a ajouté ou modifié.

Fenêtre de terminal
# Récupérer les digests des layers
crane manifest localhost:5000/monserveurweb:0.1 | jq '.layers[].digest'
# Extraire un layer spécifique (remplacer le digest)
crane blob localhost:5000/monserveurweb:0.1@sha256:607cfc754b09... > layer.tar.gz
# Explorer le contenu du layer
tar -tzf layer.tar.gz | head -20

rebase permet de reconstruire une image avec une nouvelle image de base, sans Dockerfile :

Fenêtre de terminal
crane rebase localhost:5000/monserveurweb:0.1 \
--old_base alpine:3.20 \
--new_base alpine:3.21 \
--tag localhost:5000/monserveurweb:rebased

Crane est particulièrement adapté aux pipelines CI/CD pour plusieurs raisons :

  • Pas besoin de Docker : fonctionne sans démon, sans privilèges root
  • Installation rapide : un seul binaire statique de ~15 Mo
  • Efficace : copie directe entre registries, pas de stockage intermédiaire
  • Scriptable : commandes simples, sorties prévisibles, codes retour standards

Les exemples ci-dessous montrent comment intégrer Crane dans vos workflows pour copier automatiquement les images validées vers un registry de production.

name: Copy image to production registry
on:
push:
tags:
- 'v*'
jobs:
copy:
runs-on: ubuntu-latest
steps:
- name: Install Crane
run: |
VERSION=0.20.7
curl -sL "https://github.com/google/go-containerregistry/releases/download/v${VERSION}/go-containerregistry_Linux_x86_64.tar.gz" | tar xzf - crane
sudo mv crane /usr/local/bin/
- name: Login to registries
run: |
crane auth login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
crane auth login registry.example.com -u ${{ secrets.PROD_USER }} -p ${{ secrets.PROD_PASSWORD }}
- name: Copy image
run: |
crane copy ghcr.io/${{ github.repository }}:${{ github.ref_name }} \
registry.example.com/myapp:${{ github.ref_name }}
copy-to-prod:
image: gcr.io/go-containerregistry/crane:v0.20.7
script:
- crane auth login $CI_REGISTRY -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD
- crane auth login $PROD_REGISTRY -u $PROD_USER -p $PROD_PASSWORD
- crane copy $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG $PROD_REGISTRY/myapp:$CI_COMMIT_TAG
SymptômeCause probableSolution
unauthorizedToken expiré ou invalidecrane auth login à nouveau
manifest unknownTag inexistantVérifier avec crane ls
UNSUPPORTED sur deleteRegistry ne supporte pas la suppressionNormal pour registry locale par défaut
connection refusedRegistry non accessibleVérifier l’URL et le réseau
certificate signed by unknown authorityCertificat auto-signéAjouter --insecure (dev uniquement)
Fenêtre de terminal
# Vérifier la connectivité à une registry
crane auth login registry.example.com -u test -p test
# Lister les images disponibles
crane catalog registry.example.com
# Vérifier qu'un tag existe
crane digest registry.example.com/myimage:mytag
# Voir les détails d'une erreur
crane manifest registry.example.com/myimage:mytag 2>&1
  • Ne jamais utiliser --insecure en production
  • Stocker les credentials dans des secrets CI/CD, jamais dans le code
  • Utiliser les digests plutôt que les tags pour les déploiements critiques
  • Préférer crane copy à docker pull/push pour les transferts entre registries
  • Utiliser flatten avant de pousser des images finales en production
  • Paralléliser les copies dans vos pipelines si vous avez plusieurs images
  • Versionner les images avec des tags sémantiques (1.2.3, pas latest)
  • Documenter les digests des images déployées en production
  • Nettoyer régulièrement les anciennes images de vos registries
  1. Crane fonctionne sans Docker : idéal pour les environnements CI/CD restreints
  2. crane copy est plus efficace que pull/tag/push : il ne copie que les layers manquants
  3. Le manifest contient toutes les métadonnées d’une image (layers, taille, digest)
  4. flatten optimise les images en fusionnant les layers, mais supprime le cache
  5. Les digests sont immuables : préférez-les aux tags pour la reproductibilité
  6. Authentification : utilisez toujours des variables d’environnement pour les secrets
  7. Projet Google : Crane fait partie de go-containerregistry, maintenu activement