Aller au contenu
English
Cloud medium

Container Registry Scaleway : pousser une image, et savoir qui peut la tirer

Mesuré live le ·fr-par·scw 2.62.0

60 min de lecture

logo Scaleway

Créer un namespace de registre prend moins d'une seconde, et pousser une image de 3 Mo en prend trois. Cette leçon publie une image sur Container Registry, puis répond à la seule question qui compte vraiment : qui peut la tirer ? Vous découvrirez que la visibilité se règle par image et surclasse celle du namespace, qu'un cluster Kapsule n'a pas besoin du secret de tirage que la documentation officielle présente comme obligatoire, et que le champ size de l'API ne mesure pas ce que vous croyez. Chaque droit d'accès est prouvé, jamais supposé.

  • Publier une image et mesurer ce que coûte chaque envoi suivant.
  • Distinguer la visibilité du namespace de celle de chaque image.
  • Prouver un droit d'accès au lieu de le déduire d'un code de retour.
  • Décider quand un imagePullSecret est nécessaire, et quand il ne l'est pas.
  • Un compte Scaleway avec un projet dédié et un profil scw isolé.
  • docker ou podman en local, et jq pour lire les sorties JSON.
  • Pour la dernière partie, un cluster Kapsule : voir créer un cluster Kapsule.

Comment créer un namespace et y pousser une image ?

Section intitulée « Comment créer un namespace et y pousser une image ? »

Un namespace est le conteneur de vos images, et son nom est unique. C'est la première contrainte à connaître, parce qu'elle se découvre par un refus.

  1. Créer le namespace, en privé, qui est le réglage à enseigner.

    Fenêtre de terminal
    NS_ID=$(scw registry namespace create \
    name=demo-registre-$(date +%s) \
    region=fr-par \
    is-public=false \
    -o json | jq -r '.id')
    ENDPOINT=$(scw registry namespace get "${NS_ID}" region=fr-par -o json | jq -r '.endpoint')
    echo "endpoint : ${ENDPOINT}"

    La sortie doit afficher une adresse en rg.fr-par.scw.cloud/. Mesuré le 2026-09-12 : moins d'une seconde, le namespace est ready immédiatement.

  2. Se connecter au registre. La commande pose les identifiants dans votre client local.

    Fenêtre de terminal
    scw registry login region=fr-par

    La sortie doit afficher Login Succeeded. L'argument program accepte docker ou podman.

  3. Construire une image et la pousser.

    Fenêtre de terminal
    cat > Dockerfile <<'DOCKERFILE'
    FROM alpine:3.22@sha256:14358309a308569c32bdc37e2e0e9694be33a9d99e68afb0f5ff33cc1f695dce
    RUN echo "demo scaleway" > /etc/demo-marker
    CMD ["cat", "/etc/demo-marker"]
    DOCKERFILE
    docker build -t "${ENDPOINT}/demo:1.0.0" .
    docker push "${ENDPOINT}/demo:1.0.0"

    La sortie doit finir par une ligne 1.0.0: digest: sha256:… size: …. Mesuré : 3 secondes pour une image Alpine de 3 Mo.

  4. Vérifier côté API que l'image est bien référencée.

    Fenêtre de terminal
    scw registry image list namespace-id="${NS_ID}" region=fr-par -o json \
    | jq -r '.[] | "image \(.name) : visibilité \(.visibility), \(.tags | length) tag(s)"'

    La sortie doit afficher visibilité inherit. Retenez ce mot, la section suivante en dépend.

Que coûte réellement un second envoi de la même image ?

Section intitulée « Que coûte réellement un second envoi de la même image ? »

Presque rien, parce que le registre déduplique par couche. C'est mesurable en trois commandes, et cela change la façon de construire vos images.

Fenêtre de terminal
docker tag "${ENDPOINT}/demo:1.0.0" "${ENDPOINT}/demo:1.0.1"
docker push "${ENDPOINT}/demo:1.0.1"

La sortie doit afficher Layer already exists sur chaque couche. Mesuré le 2026-09-12 : 1 seconde, contre 3 pour l'envoi initial, et aucune couche ne remonte.

La conséquence pratique dépasse le registre. Si vos couches sont stables, republier coûte une seconde et zéro octet de stockage supplémentaire, puisque deux tags qui pointent le même contenu partagent le même digest. À l'inverse, une instruction qui invalide le cache tôt dans le Dockerfile, comme un COPY . . placé avant l'installation des dépendances, fait remonter toutes les couches suivantes à chaque build. Le registre ne fait que révéler le défaut : il est dans l'ordre de vos instructions.

La visibilité se règle par image, et elle surclasse celle du namespace. C'est le point le plus important de cette leçon, parce qu'une seule image mal réglée expose son contenu à Internet entier sans que le namespace change d'apparence.

ValeurEffetQuand la choisir
inheritsuit le namespacedéfaut d'une image poussée, sain tant que le namespace est privé
privateauthentification exigéeimages applicatives, toujours
publictirage anonyme autoriséimages destinées à être distribuées, et elles seules

Ne déduisez pas ce réglage d'un essai naïf. Un docker pull sur une image encore présente en cache local répond Status: Image is up to date sans rien télécharger, et ne démontre donc aucun droit d'accès. Il faut forcer un vrai transfert :

Fenêtre de terminal
IMAGE_ID=$(scw registry image list namespace-id="${NS_ID}" region=fr-par -o json | jq -r '.[0].id')
essayer_tirage() {
docker rmi -f "${ENDPOINT}/demo:1.0.0" > /dev/null 2>&1 || true # forcer le téléchargement
docker logout "${ENDPOINT%%/*}" > /dev/null 2>&1 || true
docker pull "${ENDPOINT}/demo:1.0.0" 2>&1 | tail -1
}
scw registry image update "${IMAGE_ID}" region=fr-par visibility=private -o json | jq -r '.visibility'
essayer_tirage

La sortie doit afficher le refus exact :

Error response from daemon: pull access denied for rg.fr-par.scw.cloud/…/demo,
repository does not exist or may require 'docker login'

Basculez maintenant une seule image en public, dans un namespace qui reste privé :

Fenêtre de terminal
scw registry image update "${IMAGE_ID}" region=fr-par visibility=public -o json | jq -r '.visibility'
sleep 5
essayer_tirage

La sortie doit afficher une ligne Pull complete, preuve d'un téléchargement réel par un client non authentifié. Remettez ensuite l'image en private.

Un cluster Kapsule a-t-il besoin d'un secret de tirage ?

Section intitulée « Un cluster Kapsule a-t-il besoin d'un secret de tirage ? »

Non, pas pour un registre du même projet, et c'est contraire à ce que la documentation officielle laisse entendre. Le guide Scaleway de déploiement depuis Container Registry présente la création d'un Image Pull Secret comme une étape nécessaire. Mesure faite le 2026-09-12 sur un cluster Kapsule 1.37.0 :

Fenêtre de terminal
kubectl run essai-prive --image="${ENDPOINT}/demo:1.0.0" --restart=Never \
--command -- cat /etc/demo-marker
kubectl get events --field-selector involvedObject.name=essai-prive \
-o custom-columns=RAISON:.reason,MESSAGE:.message --no-headers

La sortie doit afficher un tirage réussi :

Pulled Successfully pulled image "rg.fr-par.scw.cloud/…/demo:1.0.0" in 2.488s
(16.593s including waiting). Image size: 3790331 bytes.

Trois conditions ont été vérifiées avant d'en tirer une conclusion : le namespace était is_public: false, l'image visibility: private, et le cluster ne contenait aucun secret de registre, pas même sur le ServiceAccount par défaut.

Ce que cette mesure prouve exactement, et il faut s'y tenir : l'authentification au registre est assurée au niveau du nœud, en dehors des objets Kubernetes observables, et aucun imagePullSecret n'est nécessaire dans ce cas. Le mécanisme précis n'a pas été inspecté : identifiants statiques déposés à la création du nœud, fournisseur d'identifiants dynamique, ou autre. Conclure « des identifiants sont posés sur le nœud » irait au-delà de ce qui a été observé.

Fenêtre de terminal
kubectl get serviceaccount default -o jsonpath='{.imagePullSecrets}'
kubectl get secrets --all-namespaces | grep -i 'registry\|docker'

Les deux sorties doivent être vides.

La portée de cette facilité mérite d'être comprise, parce qu'elle décide de ce qui marchera ailleurs. Ce que le lab établit, c'est que le nœud Kapsule sait s'authentifier auprès du Container Registry de son propre projet sans qu'aucun objet Kubernetes ne porte d'identifiant : ni Secret, ni imagePullSecrets sur le ServiceAccount. Le mécanisme vit donc au niveau du nœud ou de son runtime, et non dans le cluster. Sa forme exacte reste à inspecter, et ce guide ne la suppose pas. Deux conséquences en découlent, et elles vont dans des sens opposés. D'un côté, le déploiement d'images internes se simplifie : pas de secret à créer, à faire tourner, ni à répliquer dans chaque namespace. De l'autre, cette authentification est invisible dans vos manifestes, donc absente de toute revue de code, et elle disparaît silencieusement dès que l'image vient d'ailleurs. Un manifeste qui fonctionne sur votre cluster peut échouer sur celui d'une autre équipe, sans qu'aucune ligne n'ait changé.

La documentation officielle n'est pas fausse, elle est inconditionnelle. Le secret reste indispensable dès que vous sortez du cas courant, et ce tableau dit lequel :

Origine de l'imageBesoin d'un imagePullSecretPourquoi
Registre Scaleway, même projetnonles identifiants sont déjà sur le nœud
Registre Scaleway, autre projetouile nœud n'a pas de droit sur ce projet
Registre Scaleway, autre organisationouiidem, et la clé d'API diffère
Registre tiers, Docker Hub privé ou autreouiScaleway ne pose aucun identifiant pour eux

Ces trois lignes ne sont pas déduites, elles sont mesurées. Le 2026-09-12, un cluster du projet A a tenté de tirer une image privée d'un projet B de la même organisation, sans secret :

Failed to pull image "rg.fr-par.scw.cloud/…/demo:1.0.0": failed to resolve image:
pull access denied, repository does not exist or may require authorization:
server message: insufficient_scope: authorization failed

insufficient_scope est le mot qui compte : le nœud possède bien des identifiants, mais leur portée s'arrête au projet du cluster. Le pod reste en ImagePullBackOff indéfiniment.

Dans les trois derniers cas, le secret se crée ainsi, la clé d'API n'étant jamais écrite dans un fichier versionné :

Fenêtre de terminal
kubectl create secret docker-registry registre-externe \
--docker-server=rg.fr-par.scw.cloud \
--docker-username=nologin \
--docker-password="${SCW_SECRET_KEY}"

La sortie doit afficher secret/registre-externe created. Le secret doit vivre dans le même namespace que les pods qui l'utilisent, et se référence par imagePullSecrets dans la spécification du pod.

Dans la même région, sinon chaque tirage devient du trafic facturé. C'est la seule décision d'architecture de cette leçon, et elle se prend à la création du namespace.

Images stockéesSortie inter-régionaleSortie intra-régionaleEntrée
Privées0,027 €/Go/mois0,033 €/Gogratuitegratuite
Publiquesgratuites jusqu'à 75 Gogratuitegratuitegratuite

Un cluster en fr-par qui tire depuis un namespace fr-par ne paie aucun transfert, seulement le stockage. Le même cluster tirant depuis nl-ams paie 0,033 €/Go à chaque tirage, ce qui, sur une flotte qui redémarre souvent, cesse vite d'être anecdotique.

Supprimer le namespace emporte toutes ses images. C'est la seule commande de nettoyage nécessaire ici, et elle est asynchrone.

Fenêtre de terminal
scw registry namespace delete "${NS_ID}" region=fr-par -o json | jq -r '.status'
until ! scw registry namespace get "${NS_ID}" region=fr-par > /dev/null 2>&1; do
printf '.'; sleep 10
done
echo " namespace disparu"

La première sortie doit afficher deleting, la seconde finir par namespace disparu. N'arrêtez pas à la première : tant que le namespace reste listé, le stockage reste facturé. « J'ai demandé la suppression » n'est pas « c'est supprimé ».

Vérifiez enfin qu'il ne reste rien :

Fenêtre de terminal
scw registry namespace list region=fr-par -o json | jq -r 'length'

La sortie doit afficher 0.

Les plafonds de nombre sont hors d'atteinte ; ce sont les prix et l'unicité qui contraignent.

PlafondValeurSource
Namespaces par Organisation99 999page des quotas
Images par Organisation99 999page des quotas
Versions d'une même imageillimitéFAQ Container Registry
Stockage d'images privées0,027 €/Go/moisFAQ Container Registry
Images publiques gratuitesjusqu'à 75 GoFAQ Container Registry
Sortie inter-régionale0,033 €/GoFAQ Container Registry
Unicité du nom de namespaceglobale, pas par compterefus de l'API, 2026-09-12

Les deux premiers symptômes portent le message exact renvoyé par l'API ou par le client.

SymptômeCauseSolution
{"message":"Namespace already exist"}le nom est pris, au-delà de votre organisationpréfixer avec le nom du projet ou de l'équipe
pull access denied … may require 'docker login'image private et client non authentifiéscw registry login, ou passer l'image en public si c'est voulu
docker pull répond Image is up to date sans rien téléchargerl'image est en cache local, aucun droit n'est exercédocker rmi avant le test, sinon la mesure ne prouve rien
insufficient_scope: authorization failed sur une image d'un autre projetles identifiants du nœud ont pour portée le projet du clustercréer un imagePullSecret et le référencer dans le pod

Ces quatre erreurs ont une racine commune : elles confondent le réglage affiché avec le droit réellement accordé.

AntipatternConséquenceDiscipline
Auditer la visibilité du namespaceune image publique passe inaperçue dans un namespace privéauditer visibility image par image
Conclure d'un docker pull réussi qu'on a le droitl'image venait du cache local, rien n'a été exercésupprimer l'image locale avant tout test d'accès
Estimer sa facture avec le champ size de l'APItrois ordres de grandeur d'écart, c'est le manifestelire la console de facturation
Placer le registre dans une autre région que le clusterchaque tirage devient du trafic facturémême région pour le registre et le cluster

Qu'est-ce qu'un anonyme peut tirer de votre registre aujourd'hui ? La question ne se répond pas en regardant le namespace, puisque chaque image peut contredire son réglage.

Discipline : lister périodiquement les images public, prouver chaque droit d'accès par un tirage sans authentification et sans cache local, et ne jamais écrire une clé d'API dans un fichier versionné, y compris dans un manifeste de secret.

Qui paie le trafic de vos tirages ? Personne, tant que le registre et le cluster partagent la même région ; 0,033 €/Go dès qu'ils divergent.

Discipline : créer le namespace dans la région du cluster, garder des couches stables pour que la déduplication joue, et supprimer les namespaces de test, dont le stockage se facture indéfiniment sans jamais attirer l'attention.

Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

  • Créer un namespace prend moins d'une seconde et il est ready immédiatement.
  • Le nom d'un namespace est unique au-delà de votre organisation : l'API répond Namespace already exist.
  • Une image poussée est en inherit, ni publique ni privée : elle suit le namespace.
  • La visibilité par image surclasse celle du namespace : une seule image public est tirable par n'importe qui.
  • Un docker pull sur une image en cache ne prouve aucun droit : docker rmi d'abord, sinon la mesure est vide.
  • Le champ size de l'API est celui du manifeste : 1 235 octets annoncés pour 3 790 331 octets réels.
  • Un pod Kapsule tire une image privée du même projet sans imagePullSecret : l'authentification se fait au niveau du nœud, hors des objets Kubernetes.
  • Le secret redevient nécessaire dès l'autre projet : mesuré, le tirage échoue en insufficient_scope: authorization failed.
  • Republier un contenu identique prend 1 seconde et ne remonte aucune couche.
  • Registre et cluster dans la même région : sinon chaque tirage coûte 0,033 €/Go.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn