Skip to content

bradfitz/image-warmer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 

Repository files navigation

image-warmer

Keep every container image referenced by any pod in your Kubernetes cluster pre-pulled on every node, without ever running those images.

Why

Kubelet pulls images on demand when a pod starts. If a pod is rescheduled to a node that doesn't have the image cached (e.g. a worker that just rejoined after a reboot) and the upstream registry is unreachable at that moment (e.g. your home internet is down after a power outage), the pod stays Pending.

Pre-pulling every "important" image on every node fixes that, but the obvious patterns are awkward:

  • One DaemonSet per image with the image as the container: fine for busybox-style images, broken for distroless ones (no sleep to keep the container alive, so you'd need a different no-op command per image).
  • A privileged DaemonSet that calls crictl pull: works, but needs hostPath to the container runtime socket and a privileged container, and the pulled image can still be evicted by kubelet's image GC if no Pod spec references it.

How

This operator uses Kubernetes [ImageVolume] (https://kubernetes.io/docs/concepts/storage/volumes/#image) sources. Each image to be kept warm becomes a volumes[].image.reference entry on the warmer DaemonSet's pod template:

volumes:
  - name: warm-deadbeef
    image:
      reference: rancher/mirrored-coredns-coredns:1.14.2
      pullPolicy: IfNotPresent

The kubelet pulls the image to mount it as a read-only volume, and treats the volume reference as "image in use", so the image stays cached even under disk pressure. The pod's only container is a tiny holder image (you bring your own) that just runs sleep infinity. None of the warmed images are ever executed.

ImageVolume is alpha in Kubernetes v1.31, beta on by default in v1.33, and GA in v1.34. This operator assumes that path is available on the cluster it runs against.

What it does

Watches all Pods cluster-wide, collects the unique set of container image references (both containers[] and initContainers[]), filters out references the operator's flags say to skip, and reconciles a single DaemonSet whose pod template lists every remaining image. New workloads' images get added on the next reconcile (debounced after each pod event, with a configurable periodic resync as a safety net).

Usage

Build:

go build .

Required RBAC: cluster-wide list/watch on Pods, plus get/create/update/patch/delete on DaemonSets in the namespace it's configured to manage.

Flags:

--namespace string         Namespace in which to manage the warmer DaemonSet. (default "image-warmer")
--daemonset-name string    Name of the warmer DaemonSet. (default "image-warmer")
--holder-image string      Image reference for the holder container.
                           Must already be cached on every node and
                           include a tag or digest. Required.
--exclude-prefixes string  Comma-separated list of image-ref prefixes
                           to skip. Images starting with any of these
                           are never added to the warmer.
--resync duration          Resync interval. (default 5m0s)
--kubeconfig string        Path to kubeconfig (only for development).

Typical pattern: choose a holder image that you already control (e.g. your own tiny alpine-based binary you ship to every node anyway) and an --exclude-prefixes that skips your own registry (images already coming from a registry you control don't usually need warming).

image-warmer-operator \
  --holder-image=registry.example.com/me/holder:abc123 \
  --exclude-prefixes=registry.example.com/

Status

Early. Used in a personal home cluster. No formal release. Open to issues/PRs if you find it useful.

License

Apache 2.0

About

kubernetes operator to keep images pulled on all nodes

Resources

Stars

8 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages