Aller au contenu
Conteneurs & Orchestration medium

kube-image-keeper : cache et haute disponibilité des images Kubernetes

22 min de lecture

logo kube-image-keeper

Ce guide vous montre comment installer kube-image-keeper (kuik) pour garantir l’accès à vos images conteneur même quand Docker Hub ou votre registry est indisponible. Vous apprendrez à configurer le routage d’images vers des miroirs et la réplication entre registries. Prérequis : un cluster Kubernetes fonctionnel et Helm installé.

  • Installer kuik et ses prérequis (cert-manager)
  • Comprendre l’architecture et les CRDs de kuik v2
  • Configurer les alternatives d’images avec ClusterImageSetMirror
  • Déclarer des upstreams multiples avec ClusterReplicatedImageSet
  • Valider vos filtres sur un cluster de test avec les logs DEBUG
  • Diagnostiquer les problèmes courants

kube-image-keeper (kuik, prononcé “quick”) est un système de routage et de mise en cache d’images conteneur pour Kubernetes, développé par Enix. Il résout un problème que tout administrateur Kubernetes a rencontré : l’impossibilité de démarrer des pods parce qu’une image ne peut pas être téléchargée.

Les causes sont multiples :

  • Registry indisponible : Docker Hub en maintenance, panne réseau
  • Rate limiting : quotas Docker Hub atteints (100 pulls/6h pour les anonymes)
  • Image supprimée : politique de rétention, suppression accidentelle
  • Réseau instable : clusters edge avec connexion intermittente

kuik intercepte les créations de pods via un mutating webhook et peut :

  1. Router les images vers un miroir local quand le registry source est indisponible
  2. Répliquer les images entre plusieurs registries pour créer une haute disponibilité virtuelle

kuik v2 s’articule autour de deux concepts principaux :

Architecture de kube-image-keeper : webhook mutating, CRDs de routage et vérification de disponibilité

kuik v2 introduit quatre Custom Resource Definitions pour configurer le comportement du système. Les versions “Cluster” s’appliquent à tout le cluster, tandis que les versions sans préfixe sont limitées à un namespace spécifique.

CRDPortéeUsage
ClusterImageSetMirrorClusterDéfinit des miroirs pour router les images
ImageSetMirrorNamespaceIdem, mais limité à un namespace
ClusterReplicatedImageSetClusterConfigure la réplication entre registries
ReplicatedImageSetNamespaceIdem, mais limité à un namespace

La différence entre ImageSetMirror et ReplicatedImageSet est importante :

  • ImageSetMirror : quand une image correspond au filtre, kuik la rend disponible via le ou les miroirs déclarés, comme alternatives si l’upstream est indisponible. Ce CRD ne redirige pas systématiquement les pulls — il enrichit la liste des sources possibles.
  • ReplicatedImageSet : permet de déclarer plusieurs upstreams pour une même image. Utile quand un projet publie ses images sur plusieurs registries à la fois — kuik peut proposer l’un ou l’autre en alternative.

La réécriture d’image est décidée au démarrage du pod : kuik vérifie si l’upstream est accessible et, s’il ne l’est pas, utilise les alternatives définies par un CISM ou un CRIS. Depuis la version 2.1, il est possible de forcer la réécriture via les priorités, même quand l’upstream est disponible.

kuik utilise un mutating webhook qui intercepte les créations de pods. Ce webhook doit communiquer avec l’API server de façon sécurisée via TLS. Pour gérer automatiquement les certificats, kuik s’appuie sur cert-manager.

Si cert-manager n’est pas déjà installé sur votre cluster :

Fenêtre de terminal
# Installation de cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.17.1/cert-manager.yaml
# Attendre que cert-manager soit prêt
kubectl -n cert-manager rollout status deployment cert-manager --timeout=120s
kubectl -n cert-manager rollout status deployment cert-manager-webhook --timeout=120s
  1. Créer le namespace

    Fenêtre de terminal
    kubectl create namespace kuik-system
  2. Installer kuik

    Fenêtre de terminal
    helm upgrade --install \
    --namespace kuik-system \
    kube-image-keeper \
    oci://quay.io/enix/charts/kube-image-keeper \
    --version 2.2.1 \
    --wait
  3. Vérifier l’installation

    Fenêtre de terminal
    kubectl -n kuik-system get pods

    Résultat attendu :

    NAME READY STATUS RESTARTS AGE
    kube-image-keeper-manager-5c5dff9d7b-w6xcg 1/1 Running 0 31s

Après l’installation, vérifiez que les Custom Resource Definitions ont été créées. Ces CRDs permettent de configurer kuik de façon déclarative :

Fenêtre de terminal
kubectl api-resources | grep kuik

Résultat :

clusterimagesetmirrors cism kuik.enix.io/v1alpha1 false ClusterImageSetMirror
clusterreplicatedimagesets cris kuik.enix.io/v1alpha1 false ClusterReplicatedImageSet
imagesetmirrors ism kuik.enix.io/v1alpha1 true ImageSetMirror
replicatedimagesets ris kuik.enix.io/v1alpha1 true ReplicatedImageSet

Les raccourcis (cism, cris, ism, ris) permettent d’interagir plus rapidement avec kubectl : kubectl get cism au lieu de kubectl get clusterimagesetmirrors.

Configuration des miroirs d’images (ClusterImageSetMirror)

Section intitulée « Configuration des miroirs d’images (ClusterImageSetMirror) »

Le ClusterImageSetMirror est le cas d’usage le plus simple de kuik. Il déclare des alternatives pour un ensemble d’images filtrées. Si l’upstream est indisponible au démarrage d’un pod, kuik propose ces alternatives à la place.

Cas d’usage typiques :

  • Vous avez un Harbor configuré en proxy-cache de Docker Hub
  • Vous utilisez un registry interne qui synchronise les images critiques
  • Vous êtes sur un cluster edge avec un registry local

Supposons que vous avez un registry miroir Harbor à harbor.example.com:5000. Ce registry est configuré pour mettre en cache les images Docker Hub :

apiVersion: kuik.enix.io/v1alpha1
kind: ClusterImageSetMirror
metadata:
name: dockerhub-mirror
spec:
imageFilter:
include:
- ".+/nginx:.*" # Toutes les versions de nginx (registry normalisée incluse)
- ".+/redis:[0-9]+\\..*" # redis avec version numérique
exclude:
- ".*:latest" # Exclure les tags latest
mirrors:
- registry: "harbor.example.com:5000"
path: "docker-hub-cache"
Fenêtre de terminal
kubectl apply -f dockerhub-mirror.yaml
# Vérifier
kubectl get clusterimagesetmirrors

Quand un pod est créé avec image: nginx:1.25 :

  1. Le webhook intercepte la création du pod

    {"msg":"defaulting for Pod","namespace":"default","name":"test-nginx"}
  2. kuik normalise l’image et recense les alternatives

    nginx:1.25docker.io/library/nginx:1.25, puis kuik évalue tous les CISM et CRIS déclarés.

    {"msg":"reviewing alternatives","clusterImageSetMirrors":1,"clusterReplicatedImageSet":1}
    {"msg":"found alternatives","container":"nginx",
    "references":["docker.io/library/nginx:1.25","harbor.example.com:5000/docker-hub-cache/library/nginx:1.25"]}
  3. Il teste la disponibilité de chaque alternative

    Si le miroir est inaccessible :

    {"msg":"image is not available","reference":"harbor.example.com:5000/...",
    "error":"registry unreachable: dial tcp: lookup harbor.example.com: no such host"}
  4. Il choisit la meilleure source disponible

    Si l’upstream original est accessible, kuik le conserve (failsafe) :

    {"msg":"original image is available, using it","originalImage":"nginx:1.25"}

    Si l’upstream est indisponible et qu’une alternative est accessible, kuik réécrit l’image vers cette alternative.

Fenêtre de terminal
# Voir l'image finale dans le pod
kubectl get pod mon-pod -o jsonpath='{.spec.containers[0].image}'

Configuration des upstreams multiples (ClusterReplicatedImageSet)

Section intitulée « Configuration des upstreams multiples (ClusterReplicatedImageSet) »

Le ClusterReplicatedImageSet répond à un besoin différent : certains projets publient leurs images sur plusieurs registries simultanément. Ce CRD permet de déclarer ces upstreams multiples pour qu’ils soient tous proposés comme alternatives lors du démarrage d’un pod.

Différences avec ClusterImageSetMirror :

AspectClusterImageSetMirrorClusterReplicatedImageSet
Usage principalDéclarer un miroir existant comme alternativeDéclarer plusieurs upstreams d’un même projet
Qui gère les images ?Le miroir externe (Harbor, etc.)Les registries upstream déclarés
Cas d’usage typiqueHarbor proxy-cache, registry interneProjet publié sur docker.io ET ghcr.io

Prenons le cas de Thanos, publié à la fois sur Docker Hub et Quay.io. Si Docker Hub est indisponible, kuik peut automatiquement router vers Quay.io :

apiVersion: kuik.enix.io/v1alpha1
kind: ClusterReplicatedImageSet
metadata:
name: thanos-multi-registry
spec:
upstreams:
- registry: docker.io
path: /thanosio/thanos
imageFilter:
include:
- "/thanosio/thanos:.+" # matche après suppression du préfixe "docker.io"
- registry: quay.io
path: /thanos/thanos
imageFilter:
include:
- "/thanos/thanos:.+" # matche après suppression du préfixe "quay.io"

Quand un pod utilisant thanosio/thanos:v0.36.1 est créé, kuik vérifie la disponibilité de chacun des upstreams déclarés. Si docker.io est indisponible, kuik peut proposer quay.io comme alternative et réécrire l’image en conséquence.

Pour un upstream nécessitant une authentification (registry privée) :

apiVersion: kuik.enix.io/v1alpha1
kind: ClusterReplicatedImageSet
metadata:
name: private-images
spec:
upstreams:
- registry: ghcr.io
path: /myorg/myapp
credentialSecret:
name: ghcr-credentials
namespace: kuik-system
imageFilter:
include:
- "/myorg/myapp:.+" # après strip de "ghcr.io"
- registry: registry.example.com
path: /myorg/myapp
credentialSecret:
name: private-registry-credentials
namespace: kuik-system
imageFilter:
include:
- "/myorg/myapp:.+" # après strip de "registry.example.com"

Créez d’abord les secrets :

Fenêtre de terminal
kubectl -n kuik-system create secret docker-registry ghcr-credentials \
--docker-server=ghcr.io \
--docker-username=myuser \
--docker-password=ghp_xxxx
kubectl -n kuik-system create secret docker-registry private-registry-credentials \
--docker-server=registry.example.com \
--docker-username=myuser \
--docker-password=mypassword

kuik offre plusieurs options pour adapter son comportement à votre environnement. Les principales personnalisations concernent la haute disponibilité du manager, le comportement du routage et la gestion du cycle de vie des images.

Créez un fichier de values pour personnaliser l’installation. Voici les options les plus utiles :

values-kuik.yaml
manager:
replicas: 2 # HA pour le manager
verbosity: DEBUG # Plus de logs
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
configuration:
routing:
activeCheck:
enabled: true # Vérifier la disponibilité des registries
timeout: "2s" # Timeout de la vérification
unusedImageTTL: 48 # Heures avant suppression d'une image non utilisée
Fenêtre de terminal
helm upgrade --install \
--namespace kuik-system \
kube-image-keeper \
oci://quay.io/enix/charts/kube-image-keeper \
--version 2.2.1 \
-f values-kuik.yaml

Pour éviter que kuik n’intercepte certains namespaces système :

apiVersion: kuik.enix.io/v1alpha1
kind: ClusterImageSetMirror
metadata:
name: global-mirror
spec:
imageFilter:
include:
- ".*"
exclude:
- "quay.io/enix/kube-image-keeper:.*" # Éviter les boucles

kuik étant un composant critique (il intercepte toutes les créations de pods), il est important de savoir le surveiller et diagnostiquer les problèmes.

Le manager kuik produit des logs structurés (JSON) qui permettent de comprendre son comportement. Suivez les logs en temps réel :

Fenêtre de terminal
kubectl -n kuik-system logs deployment/kube-image-keeper-manager -f

Les messages clés à surveiller, triés par niveaux de logs :

Niveau INFO (toujours visible) :

MessageSignification
mirroring imagekuik tente de copier une image vers un miroir
could not mirror imageÉchec de la copie (registry inaccessible)
image is not used anymoreImage sans pod associé, marquée pour nettoyage

Niveau DEBUG (nécessite verbosity: DEBUG) :

MessageSignification
defaulting for PodWebhook interceptant une création de pod
reviewing alternativesÉvaluation des CISM/CRIS pour ce pod
found alternativesListe des alternatives trouvées (avec references)
image is availableUne alternative est accessible
image is not availableUne alternative est inaccessible
original image is available, using itImage originale conservée (failsafe)

Le status d’un ClusterImageSetMirror liste les images qui ont matché les filtres et l’état de chaque miroir :

Fenêtre de terminal
kubectl get cism dockerhub-mirror -o jsonpath='{.status}' | python3 -m json.tool
{
"matchingImages": [
{
"image": "docker.io/library/nginx:1.25",
"mirrors": [
{
"image": "harbor.example.com:5000/docker-hub-cache/library/nginx:1.25",
"mirroredAt": "2026-04-07T09:30:40Z"
}
]
},
{
"image": "docker.io/library/redis:7.2.4",
"mirrors": [
{
"image": "harbor.example.com:5000/docker-hub-cache/library/redis:7.2.4"
}
]
}
]
}

Si une image que vous attendiez est absente de matchingImages, votre filtre ne matche pas. Vérifiez la syntaxe regex et la normalisation de l’image.

Fenêtre de terminal
# Détail d'un ClusterImageSetMirror
kubectl get cism dockerhub-mirror -o yaml
# Détail d'un ClusterReplicatedImageSet
kubectl get cris thanos-multi-registry -o yaml
Fenêtre de terminal
# Documentation intégrée
kubectl explain clusterimagesetmirror.spec
kubectl explain clusterimagesetmirror.spec.imageFilter
kubectl explain clusterreplicatedimageset.spec.upstreams

Les problèmes avec kuik se manifestent généralement de deux façons : les pods ne démarrent pas (images introuvables) ou le routage ne s’applique pas (images non réécrites). Voici les causes les plus fréquentes :

SymptômeCause probableSolution
Webhook non déclenchécert-manager non prêtVérifier kubectl -n cert-manager get pods
invalid nested repetition operatorRegex invalide (** au lieu de .*)Corriger la syntaxe regex
Image non réécriteMiroir inaccessibleVérifier la connectivité au registry miroir
no alternative image availableAucune règle ne matcheVérifier les filtres include/exclude
Pods kuik en CrashLoopCertificats invalidesRecréer le namespace kuik-system

Si kuik indique qu’aucun miroir n’est disponible, vérifiez que les pods peuvent réellement atteindre votre registry miroir. Cette commande crée un pod temporaire pour tester la connectivité :

Fenêtre de terminal
# Depuis un pod de debug
kubectl run test-registry --rm -it --image=curlimages/curl -- \
curl -s https://harbor.example.com:5000/v2/

Une réponse {} ou {"repositories":[]} indique que le registry est accessible. Une erreur de connexion ou un timeout signifie un problème réseau (firewall, DNS, etc.).

Avant de déployer kuik en production, validez le comportement de vos filtres sur un cluster local. Cette section utilise k3d, mais tout cluster éphémère convient (kind, minikube).

Fenêtre de terminal
k3d cluster create kuik-test --agents 1 --wait

Installez cert-manager et kuik en mode DEBUG pour voir les décisions du webhook :

Fenêtre de terminal
# cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.17.1/cert-manager.yaml
kubectl -n cert-manager rollout status deployment cert-manager-webhook --timeout=120s
# kuik avec logs DEBUG
kubectl create namespace kuik-system
helm upgrade --install --namespace kuik-system kube-image-keeper \
oci://quay.io/enix/charts/kube-image-keeper \
--version 2.2.1 --set manager.verbosity=DEBUG --wait

Appliquez vos CISM/CRIS, puis créez des pods pour déclencher le webhook :

Fenêtre de terminal
# Appliquer les règles
kubectl apply -f dockerhub-mirror.yaml
kubectl apply -f thanos-multi-registry.yaml
# Pods de test : un par scénario
kubectl run test-nginx --image=nginx:1.25 --command -- sleep 3600
kubectl run test-latest --image=nginx:latest --command -- sleep 3600
kubectl run test-redis --image=redis:7.2.4 --command -- sleep 3600
kubectl run test-thanos --image=thanosio/thanos:v0.36.1 --command -- sleep 3600
kubectl run test-no-match --image=busybox:1.36 --command -- sleep 3600

Examinez les logs pour voir les décisions de kuik sur chaque pod :

Fenêtre de terminal
kubectl -n kuik-system logs deployment/kube-image-keeper-manager --since=30s \
| grep '"found alternatives"'

Résultats attendus :

PodImage normaliséeAlternatives trouvéesExplication
test-nginxdocker.io/library/nginx:1.25originale + miroir Harbor.+/nginx:.* matche, pas exclu
test-latestdocker.io/library/nginx:latestoriginale uniquement.+/nginx:.* matche mais .*:latest l’exclut
test-redisdocker.io/library/redis:7.2.4originale + miroir Harbor.+/redis:[0-9]+\\..* matche
test-thanosdocker.io/thanosio/thanos:v0.36.1docker.io + quay.ioCRIS : les deux upstreams matchent
test-no-matchdocker.io/library/busybox:1.36originale uniquementAucune règle ne matche

Vérifiez aussi le status du CISM pour confirmer les images matchées :

Fenêtre de terminal
kubectl get cism dockerhub-mirror -o jsonpath='{.status.matchingImages[*].image}'
# docker.io/library/nginx:1.25 docker.io/library/redis:7.2.4

nginx:latest et busybox:1.36 doivent être absents de cette liste.

Fenêtre de terminal
k3d cluster delete kuik-test

Pour désinstaller proprement :

Fenêtre de terminal
# Supprimer kuik
helm uninstall kube-image-keeper -n kuik-system
# Supprimer les CRDs (attention : supprime toutes les règles)
kubectl delete crd clusterimagesetmirrors.kuik.enix.io
kubectl delete crd clusterreplicatedimagesets.kuik.enix.io
kubectl delete crd imagesetmirrors.kuik.enix.io
kubectl delete crd replicatedimagesets.kuik.enix.io
# Supprimer le namespace
kubectl delete namespace kuik-system
  • kuik résout les problèmes de disponibilité des images (registry down, rate limits, images supprimées)
  • kuik v2 utilise deux CRDs principaux : ClusterImageSetMirror (alternatives via miroir) et ClusterReplicatedImageSet (upstreams multiples pour un même projet)
  • Les filtres utilisent des regex Go en full match, pas des globs
  • Les filtres CISM matchent sur l’image complète normalisée (ex : docker.io/library/nginx:1.25), tandis que les filtres CRIS strippent le registry avant comparaison (ex : /library/nginx:1.25)
  • kuik est failsafe : si aucun miroir n’est disponible, l’image originale est conservée
  • cert-manager est un prérequis obligatoire
  • Évitez d’inclure l’image kuik dans vos règles pour prévenir les boucles

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn