I was surprised to learn that Kubernetes doesn't include a built-in mechanism to pre-pull images before initiating a deployment rollout. In my mind, it makes perfect sense to pull the image before any other action.</p>
Of course, this choice aligns with the Kubernetes philosophy of managing stateless, ephemeral workloads where multiple replicas and nodes are expected to mitigate the impact of pod startup times.</p>
However, in real-world scenarios, conditions are often less than ideal. I'm sure that this won't be the last time that I encounter a single-replica application with frequent updates that requires the fastest possible pod startup time.</p>
common solutions</h2>
I've seen people run Here are some examples:</p>
I want to demonstrate now how to implement an image pre-puller using a I'll also track the startup time of the pods to see how effective this approach can be.</p>
Our initial deployment consists of 3 replicas. At startup, each replica
generates a file at Let's apply the deployment and check the startup time.</p>
To check the startup time I'll use the following bash script (DaemonSets</code> to pre-pull the images, use cache registries
or even deploy operators that manage multiple-image caches.</p>
naive implementation using a DaemonSet</h2>
DaemonSet</code>,
mainly because it’s a fun solution to try out!</p>
test deployment</h3>
/tmp/start.txt</code> containing the current timestamp to track
the startup time. We'll change the image later on.</p>
a</span>piVersion</span>:</span> a</span>pps/v1</span></span>
k</span>ind</span>:</span> D</span>eployment</span></span>
m</span>etadata</span>:</span></span>
l</span>abels</span>:</span></span>
a</span>pp</span>:</span> m</span>y-deployment</span></span>
n</span>ame</span>:</span> m</span>y-deployment</span></span>
n</span>amespace</span>:</span> d</span>efault</span></span>
s</span>pec</span>:</span></span>
r</span>eplicas</span>:</span> 3</span></span>
s</span>elector</span>:</span></span>
m</span>atchLabels</span>:</span></span>
a</span>pp</span>:</span> m</span>y-deployment</span></span>
t</span>emplate</span>:</span></span>
m</span>etadata</span>:</span></span>
l</span>abels</span>:</span></span>
a</span>pp</span>:</span> m</span>y-deployment</span></span>
s</span>pec</span>:</span></span>
c</span>ontainers</span>:</span></span>
-</span> n</span>ame</span>:</span> a</span>pp</span></span>
i</span>mage</span>:</span> b</span>usybox:latest</span></span>
c</span>ommand</span>:</span></span>
-</span> /</span>usr/bin/env</span></span>
a</span>rgs</span>:</span></span>
-</span> s</span>h</span></span>
-</span> -c</span></span>
-</span> d</span>ate +%s > /tmp/start.txt; while true; do sleep 10; done</span></span></code></pre>
$</span> kubectl</span> apply</span> -</span>f</span> deployment.yaml</span></span></code></pre>
check-startup-time.sh</code>):</p>
#!</span>/usr/bin/env bash</span></span>
set</span> -</span>eu</span> -</span>o</span> pipefail</span></span>
</span>
namespace</span>=</span>"</span>default</span>"</span></span>
pod_selector</span>=</span>"</span>app=my-deployment</span>"</span></span>
</span>
kubectl</span> get</span> pods</span> \</span></span>
-</span>-namespace</span> "</span>$</span>namespace</span>"</span> \</span></span>
-</span>-selector</span> "</span>$</span>pod_selector</span>"</span> \</span></span>
-</span>o</span> jsonpath=</span>'</span>{range .items[*]}{.metadata.name} {.spec.containers[0].image} {.spec.nodeName} {.metadata.creationTimestamp}{"\n"}{end}</span>'</span> |</span></span>
while</span> r</span>e</span>a</span>d</span> -</span>r</span> p</span>o</span>d</span> i</span>m</span>g</span> n</span>o</span>d</span>e</span> t</span>s</span>;</span> do</span></span>
startTimestamp</span>=</span>"</span>$(</span>kubectl</span> exec</span> "</span>$</span>pod</span>"</span> -</span>-</span> head</span> -</span>1</span> /tmp/start.txt</span>)</span>"</span></span>
creationTimestamp</span>=</span>"</span>$(</span>date</span> -</span>-date=</span>
