Fondamentaux du registre d'artefacts¶
Spécification OCI¶
L'Open Container Initiative (OCI) définit trois standards qui gouvernent le stockage et la distribution d'artefacts conteneur :
| Standard | Rôle |
|---|---|
| OCI Image Spec | Format de l'image : layers, config, manifest |
| OCI Distribution Spec | API HTTP pour push/pull entre client et registre |
| OCI Runtime Spec | Comment exécuter un conteneur (hors scope registre) |
| OCI Artifacts | Extension pour stocker des artefacts non-image (Helm, SBOM) |
Un registre conforme OCI accepte tout artefact qui respecte le format manifest + blobs, pas uniquement des images conteneur.
Anatomie d'une image OCI¶
Layers¶
Une image est composee de couches (layers) empilees. Chaque instruction du Dockerfile qui modifie le système de fichiers produit un nouveau layer.
graph TD
L4["Layer 4 : COPY app.jar<br/>Votre application"] --- L3
L3["Layer 3 : RUN apt install<br/>Dependances"] --- L2
L2["Layer 2 : ENV JAVA_HOME<br/>Config (metadata)"] --- L1
L1["Layer 1 : Base image<br/>debian:bookworm-slim"] Chaque layer est identifié par son hash SHA-256. Deux images partageant la même base reutilisent les mêmes layers (deduplication côté registre).
Manifest¶
Le manifest est un document JSON qui référence les layers et la configuration de l'image :
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:abc123...",
"size": 7023
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:def456...",
"size": 32654
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:789ghi...",
"size": 16724
}
]
}
Index (manifest list)¶
Un index OCI regroupe plusieurs manifests pour différentes architectures sous une même référence :
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.index.v1+json",
"manifests": [
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:aaa...",
"platform": { "architecture": "amd64", "os": "linux" }
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:bbb...",
"platform": { "architecture": "arm64", "os": "linux" }
}
]
}
Tags vs Digests¶
Tags¶
Un tag est un alias mutable qui pointe vers un digest :
# Pousser une image avec un tag
podman push registry.example.com/myapp:v1.2.3
# Le tag v1.2.3 pointe vers sha256:abc123...
# Si on repousse avec le meme tag, le digest change !
podman push registry.example.com/myapp:v1.2.3 # → sha256:def456...
Ne jamais déployer via tag mutable en production
Les tags latest, stable, main changent de contenu sans prevenir. Un déploiement reproductible exige un digest ou un tag immutable.
Digests¶
Un digest est l'identifiant immuable d'un manifest, calcule par hash SHA-256 :
# Tirer une image par digest — toujours le meme contenu
podman pull registry.example.com/myapp@sha256:abc123def456...
# Obtenir le digest d'une image locale
podman inspect --format='{{.Digest}}' myapp:v1.2.3
Stratégie de tagging recommandee¶
| Tag | Usage | Mutable | Production |
|---|---|---|---|
v1.2.3 | Version semantique | Non* | Oui |
sha-a1b2c3d | Hash du commit Git | Non | Oui |
latest | Dernière version | Oui | Non |
main | Branche principale | Oui | Non |
*Immutabilite forcee par le registre (Harbor tag immutability rule).
Images multi-architecture¶
Construire des images multi-arch¶
# Avec Podman (ou Docker buildx)
podman manifest create registry.example.com/myapp:v1.2.3
podman build --platform linux/amd64 -t myapp:v1.2.3-amd64 .
podman build --platform linux/arm64 -t myapp:v1.2.3-arm64 .
podman manifest add registry.example.com/myapp:v1.2.3 \
myapp:v1.2.3-amd64
podman manifest add registry.example.com/myapp:v1.2.3 \
myapp:v1.2.3-arm64
podman manifest push registry.example.com/myapp:v1.2.3
Le registre stocke un index OCI qui pointe vers les deux manifests. Le client tire automatiquement la bonne architecture.
Helm charts comme artefacts OCI¶
Depuis Helm 3.8, les charts peuvent etre stockes dans un registre OCI au lieu d'un repository HTTP classique :
# Empaqueter le chart
helm package mychart/
# Se connecter au registre
helm registry login registry.example.com -u robot-ci
# Pousser le chart
helm push mychart-1.0.0.tgz oci://registry.example.com/charts
# Tirer le chart
helm pull oci://registry.example.com/charts/mychart --version 1.0.0
# Installer directement
helm install myrelease oci://registry.example.com/charts/mychart \
--version 1.0.0
Un seul registre pour tout
Stocker images conteneur et charts Helm dans le même registre OCI simplifie la gestion des credentials, la réplication et le RBAC.
Attachements : SBOM et attestations¶
SBOM (Software Bill of Materials)¶
Un SBOM liste toutes les dependances d'une image. Il est attache a l'image dans le registre via les artefacts OCI.
# Generer un SBOM avec Syft
syft registry.example.com/myapp:v1.2.3 -o spdx-json > sbom.json
# Attacher le SBOM a l'image via ORAS
oras attach registry.example.com/myapp:v1.2.3 \
--artifact-type application/spdx+json \
sbom.json
# Ou via Cosign
cosign attach sbom --sbom sbom.json \
registry.example.com/myapp:v1.2.3
Attestations de build (SLSA)¶
Les attestations SLSA (Supply-chain Levels for Software Artifacts) prouvent comment une image a ete construite :
# Attacher une attestation de provenance
cosign attest --predicate provenance.json \
--type slsaprovenance \
--key cosign.key \
registry.example.com/myapp:v1.2.3
Signature d'images¶
Cosign (Sigstore)¶
Cosign est l'outil de référence pour signer les images conteneur. Il stocke les signatures directement dans le registre OCI.
# Generer une paire de cles
cosign generate-key-pair
# Signer une image
cosign sign --key cosign.key registry.example.com/myapp:v1.2.3
# Verifier une signature
cosign verify --key cosign.pub registry.example.com/myapp:v1.2.3
Signature keyless (Fulcio + Rekor)¶
En mode keyless, Cosign utilise un certificat ephemere via Fulcio et enregistre la signature dans un log de transparence (Rekor) :
# Signature keyless (OIDC)
COSIGN_EXPERIMENTAL=1 cosign sign registry.example.com/myapp:v1.2.3
# Verification keyless
cosign verify \
--certificate-identity fabien@example.com \
--certificate-oidc-issuer https://keycloak.example.com/realms/org \
registry.example.com/myapp:v1.2.3
Notary v2 (Notation)
Notation (anciennement Notary v2) est une alternative a Cosign, portee par le projet CNCF Notary. Harbor supporte les deux.
Garbage collection¶
Les registres accumulent des layers orphelins (blobs non références par aucun manifest). Le garbage collection libere cet espace.
graph LR
subgraph Avant["Avant GC"]
M1a["manifest:v1.0"] --> LA["layer-A"]
M1a --> LBa["layer-B"]
M2a["manifest:v1.1"] --> LBa
M2a --> LCa["layer-C"]
Orphelin["(orphelin)"] -.-> LD["layer-D<br/>n'est plus reference"]
end
subgraph Apres["Apres GC"]
M1b["manifest:v1.0"] --> LA2["layer-A"]
M1b --> LBb["layer-B"]
M2b["manifest:v1.1"] --> LBb
M2b --> LCb["layer-C"]
end GC et disponibilité
Sur Docker Distribution, le GC necessite un arrêt du registre (mode lecture seule). Harbor effectue le GC en ligne sans interruption de service.
Réplication¶
La réplication synchronise les artefacts entre deux registres distants.
Push vs Pull¶
| Mode | Déclencheur | Cas d'usage |
|---|---|---|
| Push | Le registre source pousse vers la cible | Réplication vers site DR, edge |
| Pull | Le registre cible tire depuis la source | Import depuis registre externe |
Filtres de réplication¶
| Filtre | Exemple | Effet |
|---|---|---|
| Nom | library/** | Replique uniquement le projet library |
| Tag | v* | Replique uniquement les tags de version |
| Label | production=true | Replique uniquement les images labellisees |
| Ressource | image, chart, artifact | Replique uniquement un type d'artefact |
Topologies¶
graph TD
Principal["Harbor<br/>(actif)<br/>Site principal"] -->|push| DR["Harbor<br/>(passif)<br/>Site DR"]
Principal -->|push| Edge1["Harbor<br/>(edge-1)"]
Principal -->|push| Edge2["Harbor<br/>(edge-2)"] La réplication permet aussi d'importer des images depuis des registres publics (Docker Hub, Quay, GCR) vers le registre interne, creant ainsi un miroir contrôle.