Aller au contenu

Retriever de zone et cascade

Une zone est pilotée par un seul document : son Retriever. Il liste, par nom et par contrainte de version, les recipes que la zone doit détenir, et nomme le cookbook où les résoudre. L’instance le relit à chaque synchronisation ; changer ce que contient une zone, c’est changer ce document — ou publier une nouvelle version de recipe qu’une contrainte existante couvre déjà. Le format du document est normatif sur le site recipe-spec : voir la spécification du Retriever (le kind Retriever, recipe.tobby.dev/v1alpha1). Un exemple complet et commenté est livré dans le dépôt, sous examples/retriever.yaml.

retriever.source accepte trois formes (FR-010) :

Forme Exemple Quand
Fichier local /etc/tobby/retriever.yaml Le document est géré avec la configuration de l’instance.
URL HTTP(S) https://git.example.com/platform/retriever.yaml Le document vit dans un dépôt Git ou sur n’importe quel serveur web — le montage GitOps habituel.
Référence OCI oci://registry.example.com/config/retriever:v1 Le document voyage comme le contenu qu’il décrit — y compris d’une zone à l’autre, porté par Tobby lui-même.

La source configurée est affichée telle quelle sur l’écran d’administration Retriever (/admin/retriever, rôle admin) et sur son miroir d’API GET /api/v1/retriever — aux côtés des périmètres de confiance relâchés déclarés et de l’intervalle de synchronisation effectif. La surcharge d’intervalle vit sur le même écran (PUT /api/v1/retriever/interval, et DELETE pour revenir à la valeur configurée) ; elle persiste dans le répertoire d’état, survit aux redémarrages, l’emporte sur sync.interval, et est auditée comme un changement de configuration sensible (FR-094).

L’écran d’administration Retriever : source configurée, intervalle et sa surcharge à chaud

À chaque cycle, l’instance résout depuis le cookbook chaque recipe listée, vérifie sa signature contre les trust roots configurées (FR-033), puis réconcilie. Les contraintes de version sont résolues à chaque passe ; si aucune version publiée ne satisfait une contrainte, cette entrée échoue et le dit — les autres entrées poursuivent. Une seule recipe non résolvable ne bloque jamais la zone.

La cascade : connectée → restreinte → plus restreinte

Section intitulée « La cascade : connectée → restreinte → plus restreinte »
Zone A — amont Zone B — aval Zone C — plus en aval Tobby re-vérifie à l'entrée Tobby re-vérifie à l'entrée Tobby re-vérifie à l'entrée registre de zone …/docker.io/bitnami/wordpress registre de zone …/docker.io/bitnami/wordpress registre de zone …/docker.io/bitnami/wordpress recipes recipes Les mêmes recipes signées descendent sans modification — chaque instance re-vérifie contre ses propres trust roots Les chemins relocalisés sont invariants : le même chemin …/docker.io/… dans chaque zone, quel que soit le nombre de sauts

Les topologies réelles enchaînent les zones. La zone amont promeut dans son registre ; le Tobby de la zone aval récupère depuis ce registre, alors même que les recipes — immuables, signées, au bit près — continuent de nommer les hôtes d’origine (docker.io/...). Le pont, c’est la substitution de source (FR-036) :

# Instance aval
registries:
substitutions:
docker.io: registry.upstream.example
ghcr.io: registry.upstream.example
retriever:
source: oci://registry.upstream.example/cookbook/retriever:v1

La substitution change uniquement le point de terminaison réseau contacté — jamais le chemin de destination calculé (FR-035). C’est cette invariance qui rend la cascade composable : docker.io/bitnami/wordpress se relocalise en <registry>/docker.io/bitnami/wordpress dans chaque zone, quel que soit le nombre de sauts franchis, et ne dégénère jamais en reg.zone2/reg.zone1/docker.io/.... Le registre de chaque zone détient les mêmes chemins relocalisés sous son propre hôte, et le cookbook de chaque zone — alimenté par la propagation des recipes (FR-034) — est ce que désigne le Retriever de la zone suivante. La règle et sa justification sont dans ADR-0013 ; la grammaire normative (forme canonique des hôtes, encodage des ports, sémantique de substitution) est dans RECIPE-SPEC §11.5.

Une substitution associe un hôte d’origine à une base. L’instance y ajoute ensuite le chemin canonique complet, hôte d’origine compris :

effectif = <base>/<hôte-canonique>[_<port>]/<chemin-du-dépôt>
substitutions:
docker.io: registry.upstream.example
docker.io/library/nginx → registry.upstream.example/docker.io/library/nginx
ghcr.io/dexidp/dex → ghcr.io/dexidp/dex (non substitué : tel quel)

L’hôte d’origine reste dans le chemin à dessein : c’est là qu’un Tobby amont range le contenu, et l’instance aval l’y retrouve sans aucune traduction. La valeur est donc un hôte, suivi au plus du chemin de base sous lequel l’amont a promu. Elle ne répète jamais l’hôte d’origine.

Ce qu’on écrit dépend de ce qui alimente le registre amont :

Registre amont Alimenté par Valeur de la substitution Ce que fait l’exploitant
Le registre embarqué (/v2/) d’un Tobby amont Les synchronisations de cette instance <hôte amont>, ou <hôte amont>/<storage.basePrefix> s’il y en a un Rien : il porte déjà la disposition canonique.
Un registre de zone dans lequel un Tobby amont promeut destination.registry <hôte du registre>, ou <hôte du registre>/<destination.basePath> s’il y en a un Rien de plus que pour la promotion elle-même : Tobby pousse docker.io/library/nginx en <registre>/docker.io/library/nginx. Sur Harbor, le projet de premier niveau qui porte le nom de l’hôte d’origine doit exister (ADR-0013).
Un proxy de tirage : projet proxy-cache Harbor, dépôt distant Artifactory ou Nexus Le proxy lui-même, à la demande, depuis l’origine <hôte du proxy> Créer un proxy par hôte d’origine, nommé exactement comme l’hôte canonique (ci-dessous).

Les trois cas convergent vers la même disposition. Un registre de zone alimenté par un Tobby amont l’obtient gratuitement : Tobby source depuis l’origine, sans substitution, et pousse sous le préfixe d’origine. Un proxy l’obtient par son nom.

Proxys de tirage : un projet par hôte d’origine

Section intitulée « Proxys de tirage : un projet par hôte d’origine »

Un proxy de tirage réécrit dans l’autre sens : le nom de son projet remplace l’hôte d’origine. Un projet proxy-cache Harbor nommé dockerhub sert harbor.example/dockerhub/library/nginx depuis le library/nginx de Docker Hub. Une substitution pointée sur ce projet demanderait à Docker Hub docker.io/library/nginx, qui n’existe pas.

La correction revient au mainteneur du proxy, et c’est une simple question de nom. Il suffit de nommer le projet d’après l’hôte d’origine canonique : le chemin que demande Tobby est alors exactement celui qu’attend le proxy.

Tobby demande harbor.example/docker.io/library/nginx
Harbor lit projet « docker.io », dépôt « library/nginx »
Harbor récupère library/nginx sur Docker Hub

L’hôte canonique est à la fois la clé de substitution et le nom du projet, et le chemin demandé au proxy est toujours <proxy>/ suivi du ref de la recipe (un port s’écrivant _<port>) :

Registre d’origine Clé de substitution / projet du proxy ref dans la recipe
Docker Hub docker.io docker.io/library/nginx
GitHub Container Registry ghcr.io ghcr.io/dexidp/dex
Quay quay.io quay.io/jetstack/cert-manager-controller
Kubernetes registry.k8s.io registry.k8s.io/kube-state-metrics/kube-state-metrics
Google Container Registry gcr.io gcr.io/distroless/static
Google Artifact Registry europe-docker.pkg.dev europe-docker.pkg.dev/acme/apps/api
Microsoft Artifact Registry mcr.microsoft.com mcr.microsoft.com/dotnet/runtime
Amazon ECR Public public.ecr.aws public.ecr.aws/docker/library/alpine
Azure Container Registry acme.azurecr.io acme.azurecr.io/team/app
GitLab.com registry.gitlab.com registry.gitlab.com/acme/app/api
Forge auto-hébergée sur un port dédié clé forge.example.com:5050, projet forge.example.com_5050 forge.example.com:5050/acme/app
  • Docker Hub est le seul cas particulier, et seulement côté clients. docker pull nginx veut dire docker.io/library/nginx. Une recipe écrit toujours l’hôte, et library/ pour les images officielles. index.docker.io et registry-1.docker.io se replient sur docker.io (liste fermée, ADR-0013) : le seul projet docker.io couvre les trois graphies.
  • Un port dédié. C’est courant sur les forges auto-hébergées comme GitLab ou Froggit, dont le registre écoute souvent sur 5050. La clé garde le : (forge.example.com:5050). Le chemin l’encode en _ (forge.example.com_5050), car un nom de dépôt ne peut pas contenir :. Le projet du proxy s’appelle donc forge.example.com_5050.
  • Un hôte, un projet. C’est vrai même chez un même éditeur : gcr.io et us.gcr.io, ou deux régions d’Artifact Registry, font deux projets.
  • Vérifier les noms. Ils contiennent des points, et un tiret bas quand il y a un port. Vérifiez que votre proxy les accepte avant de bâtir sur cette disposition.
  • La confiance ne se déplace pas. Le proxy s’authentifie auprès de l’origine avec ses propres identifiants. Tobby s’authentifie auprès du proxy : le fichier de credentials et la liste blanche nomment l’hôte du proxy. Le proxy n’est pas non plus une ancre de confiance : signatures et digests épinglés sont vérifiés exactement comme si les octets venaient de l’origine.

Deux politiques lisent délibérément des références différentes :

  • La liste blanche de registries (FR-030) et la recherche de credentials portent sur l’hôte effectif réellement contacté — le substitut. C’est de là que viennent les octets, c’est donc ce que la politique réseau doit nommer.
  • Les périmètres des trust roots (FR-033) s’appliquent au ref nominal écrit dans la recipe. Une provenance signée ne change pas parce que le contenu a été récupéré depuis une copie plus proche.

Les journaux enregistrent la correspondance nominal→effectif à chaque récupération substituée : un audit peut donc toujours répondre aux deux questions — « quel était ce contenu » et « d’où viennent ces octets ».

Une instance aval s’authentifie auprès de son amont comme n’importe quel client de registre. Créez sur l’instance amont un compte (ou un jeton) dédié — voir Authentification et RBAC — dont le seul besoin est la lecture, et transmettez-le à l’instance aval par son fichier de credentials (FR-004) :

registries:
credentialsFile: /etc/tobby-credentials/config.json

Le fichier est une charge dockerconfigjson standard ; les entrées sont recherchées par hôte effectif, l’entrée nomme donc le registre amont — registry.upstream.example — et non docker.io. Il doit vivre en dehors du store (les secrets ne voyagent jamais sur un média transportable), et sur Kubernetes c’est un Secret monté que le chart câble pour vous. Les credentials en écriture suivent le même chemin : le push vers le registre de destination de la zone utilise le même fichier de credentials, indexé par l’hôte de destination.

Le versant destination de la même instance a sa propre section de configuration — destination.registry, plus destination.basePath et destination.cookbook — délibérément séparée des substitutions : l’une répond à « où est-ce que je lis », l’autre à « où est-ce que je promeus », et appliquer une réécriture côté lecture à une écriture publierait dans un registre que personne n’a nommé.

Ensuite : vos clusters et vos hôtes consomment ce qui est arrivé — Brancher vos clients.