<h2>The Problem</h2>

<p>When one of our locally hosted bare-metal k8s (1.18) nodes is powered-on, pods are scheduled, but struggle to reach 'Ready' status - almost entirely due to a land-rush of disk IO from 30-40 pods being scheduled simultaneously on the node. </p>

<p>This often results in a cascade of Deployment failures:</p>

<ul>
<li>IO requests on the node stack up in the IOWait state as pods deploy.</li>
<li>Pod startup times skyrocket from (normal) 10-20 seconds to minutes.</li>
<li>livenessProbes fail.</li>
<li>Pods are re-scheduled, compounding the problem as more IO
requests stack up.</li>
<li>Repeat.</li>
</ul>

<p>FWIW Memory and CPU are vastly over-provisioned on the nodes, even in the power-on state (&lt;10% usage).</p>

<p>Although we do have application NFS volume mounts (that would normally be suspect WRT IO issues), the disk activity and restriction at pod startup is almost entirely in the local docker container filesystem.</p>

<h2>Attempted Solutions</h2>

<p>As <a href="https://github.com/kubernetes/kubernetes/issues/54923" rel="nofollow noreferrer">disk IO is not a limitable resource</a>, we are struggling to find a solution for this. We have tuned our docker images to write to disk as little as possible at startup, and this has helped some.</p>

<p>One basic solution involves lowering the number of pods scheduled per node by increasing the number of nodes in the cluster. This isn't ideal for us, as they are physical machines, and once the nodes DO start up, the cluster is significantly over-resourced.</p>

<p>As we are bare-metal/local we do not have an automated method to auto-provision nodes in startup situations and lower them as the cluster stabilizes.</p>

<p>Applying priorityClasses at first glance seemed to be a solution. We have created priorityClasses and applied them accordingly, however, as listed in the <a href="https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/" rel="nofollow noreferrer">documentation</a>:</p>

<blockquote>
  <p>Pods can have priority. Priority indicates the importance of a Pod
  relative to other Pods. If a Pod cannot be scheduled, the scheduler
  tries to preempt (evict) lower priority Pods to make scheduling of the
  pending Pod possible.</p>
</blockquote>

<p>tldr: Pods will still all be "scheduleable" simultaneously at power-on, as no configurable resource limits are being exceeded.</p>

<h2>Question(s)</h2>

<ul>
<li>Is there a method to limit scheduing pods on a node based on its current number of non-Ready pods? This would allow priority classes to evict non-priority pods and schedule the higher priority first.</li>
<li>Aside from increasing the number of
cluster nodes, is there a method we have not thought of to manage this disk IO landrush otherwise?</li>
</ul>

<p>While I am also interested to see <em>smart</em> people answer the question, here is my probably "just OK" idea:</p>

<ol>
<li>Configure the new node with a Taint that will prevent your "normal" pods from being scheduled to it.</li>
<li>Create a deployment of do-nothing pods with:

<ul>
<li>A "reasonably large" memory request, eg: 1GB.</li>
<li>A number of replicas high enough to "fill" the node.</li>
<li>A toleration for the above Taint.</li>
</ul></li>
<li>Remove the Taint from the now-"full" node.</li>
<li>Scale down the do-nothing deployment at whatever rate you feel is appropriate as to avoid the "land rush".</li>
</ol>

<p>Here's a Dockerfile for the do-nothing "noop" image I use for testing/troubleshooting:</p>

<pre><code>FROM alpine:3.9

CMD sh -c 'while true; do sleep 5; done'
</code></pre>
