<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://samof76.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://samof76.github.io/" rel="alternate" type="text/html" /><updated>2026-05-22T07:16:37+00:00</updated><id>https://samof76.github.io/feed.xml</id><title type="html">samof76: random notes</title><subtitle>ramblings of a humid mind.
</subtitle><author><name>M Samuel Vijaykumar</name></author><entry><title type="html">Kubernetes In Anger</title><link href="https://samof76.github.io/kubernetes-in-anger.html" rel="alternate" type="text/html" title="Kubernetes In Anger" /><published>2026-05-21T00:00:00+00:00</published><updated>2026-05-21T00:00:00+00:00</updated><id>https://samof76.github.io/kubernetes-in-anger</id><content type="html" xml:base="https://samof76.github.io/kubernetes-in-anger.html"><![CDATA[<blockquote>
  <p>NOTE: Any discussions can be had on <a href="https://lobste.rs/s/iggblv/kubernetes_anger">Lobsters</a></p>
</blockquote>

<h2 id="0-quick-start-emergency-edition">0. Quick start (emergency edition)</h2>

<h3 id="is-this-the-right-guide">Is this the right guide?</h3>

<p><strong>YES, if:</strong></p>
<ul>
  <li>You’re debugging a live EKS production issue</li>
  <li>You need to upgrade/change EKS safely</li>
  <li>You want to prevent common EKS outages</li>
  <li>You’re oncall for EKS workloads</li>
</ul>

<p><strong>NO, if:</strong></p>
<ul>
  <li>You’re learning Kubernetes basics (try the official tutorials first)</li>
  <li>You need EKS setup instructions (use AWS documentation)</li>
  <li>You want comprehensive Kubernetes reference (use kubernetes.io)</li>
</ul>

<h3 id="emergency-shortcuts">Emergency shortcuts</h3>

<p><strong>Cluster is on fire right now?</strong> → Jump to <a href="#210-what-to-do-when-a-tier-0-component-is-unhealthy-eks-incident-playbook">Section 2.10 Tier-0 Incident Playbook</a></p>

<p><strong>Need to upgrade safely?</strong> → Jump to <a href="#8-upgrades-and-maintenance">Section 8 Upgrades and maintenance</a></p>

<p><strong>Investigating an incident?</strong> → Start with <a href="#12-quick-cluster-health-snapshot-30-seconds">Section 1.2 Quick Cluster Health Snapshot</a></p>

<h3 id="prerequisites">Prerequisites</h3>

<p>This guide assumes you know:</p>
<ul>
  <li>Basic kubectl commands (<code class="language-plaintext highlighter-rouge">get</code>, <code class="language-plaintext highlighter-rouge">describe</code>, <code class="language-plaintext highlighter-rouge">logs</code>)</li>
  <li>AWS CLI basics</li>
  <li>What pods, services, and deployments are</li>
  <li>How to read YAML manifests</li>
</ul>

<h3 id="what-makes-eks-different">What makes EKS different</h3>

<p>EKS is not “just Kubernetes”. Key differences that affect reliability:</p>
<ul>
  <li>Pods get real VPC IPs (AWS VPC CNI)</li>
  <li>AWS services become dependencies (NAT, NLB, EBS)</li>
  <li>Node limits are AWS EC2 limits</li>
  <li>Networking failures look like application failures</li>
  <li>Upgrades affect multiple AWS components</li>
</ul>

<hr />

<h2 id="introduction">Introduction</h2>

<h3 id="on-running-infrastructure">On running infrastructure</h3>

<p>There’s a common way of thinking about Kubernetes that goes something like this: you declare what you want, the system converges toward it, and your job is mostly done. Write the YAML, apply it, the scheduler places your pods, the controllers reconcile state, and everything just works.</p>

<p>This is roughly true until it isn’t.</p>

<p>The thing about Kubernetes — and EKS specifically — is that it doesn’t fail like a monolith fails. A monolith crashes and you know it. EKS degrades. DNS gets slow. A node hits a network limit you didn’t know existed. Pods keep running but their connections reset every 6 minutes. The dashboard is green. Customers are complaining. You’re staring at healthy pods wondering what’s wrong with your application, when the real problem is three layers down in a conntrack table or a subnet that ran out of IPs.</p>

<p>Most other platforms fail at the boundary between your code and the infrastructure. EKS fails <em>inside</em> the infrastructure, in ways that look like your code is broken. This is the fundamental debugging challenge: the symptom is always “the app is slow” or “requests are failing”, and the cause is somewhere in a stack of networking, scheduling, storage, and AWS service interactions that your application has no visibility into.</p>

<p>This matters because the instinct — “my app is returning 5xx, let me look at my app” — is wrong most of the time in EKS. The 5xx is real. But the fix is often in a probe configuration, a security group limit, a DNS resolver being overwhelmed, or a node that silently filled its conntrack table.</p>

<h3 id="the-two-jobs">The two jobs</h3>

<p>If you run EKS in production, you have two jobs:</p>

<p>The first is building workloads that survive the platform misbehaving. Probes that don’t cascade. Graceful shutdowns that actually drain. Pod distributions that tolerate losing a node or an AZ without paging anyone. This is the preventive work — the engineering equivalent of washing your hands.</p>

<p>The second is diagnosing live systems when things go wrong anyway. Connecting to a cluster that’s on fire, figuring out what’s actually broken vs what’s just symptomatic, collecting evidence before it disappears, and fixing the right thing without making the incident worse. This is the equivalent of surgery — you’re operating on a patient that’s still awake and serving traffic.</p>

<p>Both matter. Most guides only cover the first one.</p>

<p>This guide is about both. It’s a collection of patterns, failure modes, and diagnostic workflows that came from running EKS in production — the things that caused real incidents, the things that made debugging take hours instead of minutes, and the guardrails that prevented repeat occurrences.</p>

<h3 id="who-is-this-for">Who is this for?</h3>

<p>This guide is not for beginners. There’s a gap between knowing Kubernetes concepts (pods, deployments, services, kubectl) and actually being able to keep an EKS cluster healthy in production. There’s a fumbling phase where you’ve read the docs, passed the certification maybe, deployed some workloads — and then something breaks at 2am and you realize you don’t know where to look or what’s safe to touch.</p>

<p>This assumes you know the basics. It does not assume you know how to debug a cluster that’s misbehaving, how EKS-specific failure modes differ from generic Kubernetes ones, or what the safe sequence of actions is when you’re staring at a production incident.</p>

<p>What you won’t find here: how to set up EKS, what a pod is, or how to write a Deployment manifest. What you will find: what to do when pods are Pending and you don’t know why, how to tell if DNS is the problem or just a symptom, why your NLB keeps resetting connections, and how to collect evidence before the cluster auto-heals and destroys your ability to do an RCA.</p>

<h3 id="how-to-read-this-guide">How to read this guide</h3>

<p>The guide is organized by domain — networking, storage, security, observability, scaling, upgrades, and so on. Each section mixes both jobs: how to build it right, and how to debug it when it breaks. You’ll find design patterns and diagnostic runbooks side by side, because in practice you need both at the same time.</p>

<p>You can read it front-to-back if you’re setting up a new cluster or onboarding to an existing one. Or you can jump to the relevant section when something breaks — each one is self-contained enough to be useful on its own.</p>

<p>If the cluster is on fire right now, start at Section 1. It gives you a triage sequence to identify the failure domain in under 2 minutes.</p>

<hr />
<h2 id="1-how-to-dive-into-an-eks-cluster">1. How to dive into an EKS cluster</h2>

<p>When production is broken the first job is to mitigate — stop the bleeding, restore service, reduce blast radius. But you can’t mitigate effectively if you don’t know <em>what’s broken</em>. Rollback the wrong thing and you’ve wasted 10 minutes. Upsize the wrong component and nothing changes.</p>

<p>So the actual first job is: figure out where the problem is, fast enough that you can pick the right mitigation within 2 minutes. Not root cause — just failure domain:</p>

<ul>
  <li>One pod?</li>
  <li>One deployment / namespace?</li>
  <li>One node group / AZ?</li>
  <li>The entire cluster?</li>
  <li>AWS integration (CNI / LB / EBS)?</li>
  <li>An upstream dependency (RDS, Redis, external APIs)?</li>
</ul>

<p>Once you know the failure domain, you mitigate (rollback, drain, upsize, block). Root cause comes after the incident is contained.</p>

<p>What follows is a reliable entry sequence to get that signal fast, without guessing.</p>

<hr />

<h3 id="11-establish-context-dont-debug-the-wrong-cluster">1.1 Establish Context (don’t debug the wrong cluster)</h3>

<p>Before anything else, confirm you’re looking at the right cluster and identity.</p>

<p><strong>Commands</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl config current-context
kubectl cluster-info
aws sts get-caller-identity
</code></pre></div></div>

<p><strong>What you’re checking</strong></p>

<ul>
  <li>You’re in the correct kubecontext (prod vs staging mistakes happen)</li>
  <li>You have valid AWS credentials</li>
  <li>The API server is reachable at all</li>
</ul>

<p>If <code class="language-plaintext highlighter-rouge">kubectl cluster-info</code> is slow or timing out, that’s already a strong signal:</p>

<ul>
  <li>API server under load</li>
  <li>auth problems</li>
  <li>network path issues from your machine (VPN / corp DNS / proxy)</li>
</ul>

<hr />

<h3 id="12-quick-cluster-health-snapshot-30-seconds">1.2 Quick Cluster Health Snapshot (30 seconds)</h3>

<p>This is the fastest “is the cluster sick?” view.</p>

<p><strong>Commands</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get nodes <span class="nt">-o</span> wide
kubectl get pods <span class="nt">-A</span> <span class="nt">--field-selector</span><span class="o">=</span>status.phase!<span class="o">=</span>Running
kubectl get events <span class="nt">-A</span> <span class="nt">--sort-by</span><span class="o">=</span>.lastTimestamp | <span class="nb">tail</span> <span class="nt">-n</span> 50
</code></pre></div></div>

<p><strong>What you’re checking</strong></p>

<ul>
  <li>Any nodes <code class="language-plaintext highlighter-rouge">NotReady</code></li>
  <li>Any pods stuck in <code class="language-plaintext highlighter-rouge">Pending</code>, <code class="language-plaintext highlighter-rouge">CrashLoopBackOff</code>, <code class="language-plaintext highlighter-rouge">ImagePullBackOff</code></li>
  <li>Events that scream the root cause:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">FailedScheduling</code></li>
      <li><code class="language-plaintext highlighter-rouge">FailedMount</code></li>
      <li><code class="language-plaintext highlighter-rouge">Unhealthy</code> (probe failures)</li>
      <li><code class="language-plaintext highlighter-rouge">Back-off restarting failed container</code></li>
      <li><code class="language-plaintext highlighter-rouge">FailedCreatePodSandBox</code> (CNI problems)</li>
    </ul>
  </li>
</ul>

<p>This is where you decide:</p>

<ul>
  <li><strong>workload issue</strong> vs <strong>node issue</strong> vs <strong>cluster-wide issue</strong></li>
</ul>

<hr />

<h3 id="13-confirm-eks-system-components-the-platform-basics">1.3 Confirm EKS System Components (the “platform basics”)</h3>

<p>If system components are down, application debugging is mostly pointless.</p>

<p><strong>Commands</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get pods <span class="nt">-n</span> kube-system <span class="nt">-o</span> wide
kubectl get ds <span class="nt">-n</span> kube-system
kubectl get deploy <span class="nt">-n</span> kube-system
</code></pre></div></div>

<p><strong>Focus on these in EKS</strong></p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">coredns</code> (cluster DNS)</li>
  <li><code class="language-plaintext highlighter-rouge">aws-node</code> (AWS VPC CNI)</li>
  <li><code class="language-plaintext highlighter-rouge">kube-proxy</code> (unless you’re on eBPF dataplane)</li>
  <li><code class="language-plaintext highlighter-rouge">ebs-csi-node</code> / <code class="language-plaintext highlighter-rouge">ebs-csi-controller</code> (if you use EBS CSI)</li>
  <li><code class="language-plaintext highlighter-rouge">metrics-server</code> (if HPA depends on it)</li>
</ul>

<p><strong>Red flags</strong></p>

<ul>
  <li>CoreDNS pods not ready → widespread service discovery failures</li>
  <li><code class="language-plaintext highlighter-rouge">aws-node</code> not ready → pods can’t get IPs / networking breaks</li>
  <li>EBS CSI issues → StatefulSets fail to mount volumes</li>
</ul>

<hr />

<h3 id="14-decide-is-this-a-scheduling-problem">1.4 Decide: Is this a “Scheduling” Problem?</h3>

<p>If pods are Pending, do this immediately.</p>

<p><strong>Commands</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get pods <span class="nt">-A</span> | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s2">"Pending|ContainerCreating"</span>
kubectl describe pod <span class="nt">-n</span> &lt;ns&gt; &lt;pod&gt;
</code></pre></div></div>

<p><strong>What you’re looking for in <code class="language-plaintext highlighter-rouge">describe</code></strong></p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">FailedScheduling</code>
    <ul>
      <li>insufficient CPU/memory</li>
      <li>taints not tolerated</li>
      <li>affinity rules too strict</li>
      <li>topology spread constraints blocking placement</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">Insufficient pods</code> / <code class="language-plaintext highlighter-rouge">Too many pods</code>
    <ul>
      <li>node has hit max pod density (ENI/IP limits)</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">node(s) had volume node affinity conflict</code>
    <ul>
      <li>common with EBS + AZ mismatch</li>
    </ul>
  </li>
</ul>

<p>If scheduling fails, do <strong>not</strong> restart the deployment blindly. It won’t help.</p>

<hr />

<h3 id="15-decide-is-this-a-node-problem">1.5 Decide: Is this a “Node” Problem?</h3>

<p>If nodes are NotReady or workloads are failing on specific nodes, zoom in.</p>

<p><strong>Commands</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl describe node &lt;node-name&gt;
kubectl top nodes
kubectl get pods <span class="nt">-A</span> <span class="nt">-o</span> wide | <span class="nb">grep</span> &lt;node-name&gt;
</code></pre></div></div>

<p><strong>What to check on the node</strong></p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Conditions</code>: MemoryPressure / DiskPressure / PIDPressure</li>
  <li><code class="language-plaintext highlighter-rouge">Allocatable</code> vs <code class="language-plaintext highlighter-rouge">Allocated resources</code></li>
  <li>Events mentioning:
    <ul>
      <li>kubelet issues</li>
      <li>container runtime issues</li>
      <li>frequent reboots</li>
      <li>network plugin failures</li>
    </ul>
  </li>
</ul>

<p>In EKS, node failures often correlate with:</p>

<ul>
  <li>EBS issues</li>
  <li>CNI/IP exhaustion</li>
  <li>disk full (especially on small root volumes)</li>
  <li>aggressive DaemonSets consuming resources</li>
</ul>

<hr />

<h3 id="16-decide-is-this-a-network--cni-problem">1.6 Decide: Is this a “Network / CNI” Problem?</h3>

<p>EKS networking failures are often AWS VPC CNI related.</p>

<p><strong>Symptoms</strong></p>

<ul>
  <li>Pods stuck at <code class="language-plaintext highlighter-rouge">ContainerCreating</code></li>
  <li><code class="language-plaintext highlighter-rouge">FailedCreatePodSandBox</code></li>
  <li>random cross-service timeouts</li>
  <li>sudden increase in Pending pods (no IPs)</li>
</ul>

<p><strong>Commands</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> k8s-app<span class="o">=</span>aws-node <span class="nt">--tail</span><span class="o">=</span>100
kubectl <span class="nt">-n</span> kube-system describe ds aws-node
</code></pre></div></div>

<p><strong>Also check pod density</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get nodes <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{range .items[*]}{.metadata.name}{"\t"}{.status.allocatable.pods}{"\n"}{end}'</span>
</code></pre></div></div>

<p>If you’re hitting pod density/IP limits:</p>

<ul>
  <li>scaling nodes may help</li>
  <li>but the real fix is often <strong>prefix delegation</strong> / node type sizing / ENI planning</li>
</ul>

<hr />

<h3 id="17-decide-is-this-a-dns--coredns-problem">1.7 Decide: Is this a “DNS / CoreDNS” Problem?</h3>

<p>DNS issues can look like “application is broken”.</p>

<p><strong>Symptoms</strong></p>

<ul>
  <li>timeouts to internal services</li>
  <li>failures resolving service names</li>
  <li>sudden spike in retries / connection errors</li>
</ul>

<p><strong>Commands</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nt">-n</span> kube-system get pods <span class="nt">-l</span> k8s-app<span class="o">=</span>kube-dns <span class="nt">-o</span> wide
kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> k8s-app<span class="o">=</span>kube-dns <span class="nt">--tail</span><span class="o">=</span>200
</code></pre></div></div>

<p>If CoreDNS is unhealthy:</p>

<ul>
  <li>don’t waste time debugging app-level service discovery logic</li>
  <li>fix CoreDNS capacity / upstream resolver / node networking first</li>
</ul>

<hr />

<h3 id="18-decide-is-this-a-storage--ebs-csi-problem">1.8 Decide: Is this a “Storage / EBS CSI” Problem?</h3>

<p>Stateful workloads fail differently.</p>

<p><strong>Symptoms</strong></p>

<ul>
  <li>pods stuck in <code class="language-plaintext highlighter-rouge">ContainerCreating</code></li>
  <li><code class="language-plaintext highlighter-rouge">FailedMount</code></li>
  <li>volumes not attaching</li>
</ul>

<p><strong>Commands</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl describe pod <span class="nt">-n</span> &lt;ns&gt; &lt;pod&gt;
kubectl get pvc <span class="nt">-A</span>
kubectl <span class="nt">-n</span> kube-system get pods | <span class="nb">grep </span>ebs
kubectl <span class="nt">-n</span> kube-system logs deploy/ebs-csi-controller <span class="nt">--tail</span><span class="o">=</span>200
</code></pre></div></div>

<p><strong>What you’re looking for</strong></p>

<ul>
  <li>volume attachment timeouts</li>
  <li>AZ mismatch</li>
  <li>stuck PV/PVC lifecycle</li>
  <li>CSI controller/node not healthy</li>
</ul>

<hr />

<h3 id="19-find-the-blast-radius-what-is-actually-impacted">1.9 Find the “Blast Radius” (what is actually impacted)</h3>

<p>Before you attempt changes, quantify impact.</p>

<p><strong>Commands</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get pods <span class="nt">-A</span> | <span class="nb">wc</span> <span class="nt">-l</span>
kubectl get pods <span class="nt">-A</span> <span class="nt">--field-selector</span><span class="o">=</span>status.phase!<span class="o">=</span>Running | <span class="nb">head</span> <span class="nt">-n</span> 50
kubectl get nodes | <span class="nb">grep</span> <span class="nt">-v</span> Ready
</code></pre></div></div>

<p><strong>Interpretation</strong></p>

<ul>
  <li>If only one namespace is impacted → likely app or namespace-level dependency</li>
  <li>If one node group/AZ is impacted → capacity, subnet, EBS/AZ, or rollout targeting issue</li>
  <li>If kube-system is unhealthy → platform issue, stop chasing app symptoms</li>
</ul>

<hr />

<h3 id="110-evidence-collection-before-you-change-anything">1.10 Evidence Collection (before you change anything)</h3>

<p>Kubernetes evidence disappears fast (pods restart, nodes recycle, events roll over).</p>

<p>Capture minimal evidence first.</p>

<p><strong>Commands</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get events <span class="nt">-A</span> <span class="nt">--sort-by</span><span class="o">=</span>.lastTimestamp | <span class="nb">tail</span> <span class="nt">-n</span> 200 <span class="o">&gt;</span> events.txt
kubectl get nodes <span class="nt">-o</span> wide <span class="o">&gt;</span> nodes.txt
kubectl get pods <span class="nt">-A</span> <span class="nt">-o</span> wide <span class="o">&gt;</span> pods.txt
</code></pre></div></div>

<p>If it’s a single workload incident:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl describe pod <span class="nt">-n</span> &lt;ns&gt; &lt;pod&gt; <span class="o">&gt;</span> pod.describe.txt
kubectl logs <span class="nt">-n</span> &lt;ns&gt; &lt;pod&gt; <span class="nt">--previous</span> <span class="o">&gt;</span> pod.prev.log.txt
</code></pre></div></div>

<p>This makes the RCA possible later, without relying on memory and guesswork.</p>

<hr />

<h3 id="111-node-specific-failures-conntrack-sysctls-and-kubelet-settings">1.11 Node-Specific Failures (Conntrack, Sysctls, and Kubelet Settings)</h3>

<p>Some of the nastiest EKS incidents are <strong>node-local</strong>. The cluster looks “fine”, pods are “Running”, but traffic becomes unreliable, latency spikes, or connections fail randomly.</p>

<p>These issues often come from:</p>

<ul>
  <li><strong>conntrack exhaustion</strong></li>
  <li><strong>ephemeral port exhaustion</strong></li>
  <li><strong>kernel / sysctl defaults not sized for your traffic</strong></li>
  <li><strong>kubelet behaviour under pressure</strong></li>
  <li><strong>bad QoS due to incorrect requests/limits</strong></li>
</ul>

<h4 id="1111-conntrack-exhaustion-classic-random-networking-failures">1.11.1 Conntrack Exhaustion (classic “random networking failures”)</h4>

<p><strong>What it looks like</strong></p>

<ul>
  <li>intermittent timeouts to upstream services</li>
  <li>random 5xx at ingress / Envoy / Nginx</li>
  <li>“connection reset”, “i/o timeout”, “no route to host” type errors</li>
  <li>affects specific nodes more than others</li>
  <li>spikes during traffic bursts or connection-heavy workloads</li>
</ul>

<p><strong>Why it happens</strong>
Linux conntrack tracks NAT and connection state. On busy nodes (especially with L7 proxies, service meshes, high churn, short-lived connections), conntrack tables fill up and the kernel starts dropping new connections.</p>

<p><strong>Quick checks (from Kubernetes side)</strong></p>

<ol>
  <li>Identify if failures correlate to a node:</li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get pods <span class="nt">-A</span> <span class="nt">-o</span> wide | <span class="nb">grep</span> &lt;node-name&gt;
kubectl describe node &lt;node-name&gt;
</code></pre></div></div>

<ol>
  <li>Check node-level kernel counters (best effort)
If you have node access (SSM/SSH):</li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>sysctl net.netfilter.nf_conntrack_count
<span class="nb">sudo </span>sysctl net.netfilter.nf_conntrack_max
dmesg | egrep <span class="nt">-i</span> <span class="s2">"conntrack|nf_conntrack"</span>
</code></pre></div></div>

<p><strong>Strong indicators</strong></p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">nf_conntrack_count</code> close to <code class="language-plaintext highlighter-rouge">nf_conntrack_max</code></li>
  <li>kernel logs mentioning conntrack table full / dropped packets</li>
</ul>

<p><strong>Fix patterns</strong></p>

<ul>
  <li>Increase conntrack max (sysctl)</li>
  <li>Reduce connection churn (keep-alives, pooling)</li>
  <li>Spread load (more nodes / better pod distribution)</li>
  <li>Ensure nodes aren’t overloaded with too many L7-heavy pods</li>
</ul>

<hr />

<h4 id="1112-ephemeral-port-exhaustion-the-sneaky-cousin">1.11.2 Ephemeral Port Exhaustion (the sneaky cousin)</h4>

<p><strong>What it looks like</strong></p>

<ul>
  <li>outbound calls failing from a node under burst load</li>
  <li>retries make it worse</li>
  <li>symptoms disappear when traffic drops</li>
</ul>

<p><strong>Quick checks</strong>
On node:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cat</span> /proc/sys/net/ipv4/ip_local_port_range
ss <span class="nt">-s</span>
ss <span class="nt">-ant</span> state time-wait | <span class="nb">wc</span> <span class="nt">-l</span>
</code></pre></div></div>

<p><strong>Fix patterns</strong></p>

<ul>
  <li>widen ephemeral port range</li>
  <li>reduce TIME_WAIT pressure (carefully)</li>
  <li>enable connection reuse/keepalive at clients and proxies</li>
</ul>

<hr />

<h3 id="112-evidence-collection-automation">1.12 Evidence Collection Automation</h3>

<p>When production is broken, evidence disappears fast. Capture it first, debug second.</p>

<p><strong>Quick evidence collection script</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="c"># Save as: collect-evidence.sh</span>
<span class="nv">TIMESTAMP</span><span class="o">=</span><span class="si">$(</span><span class="nb">date</span> +%Y%m%d-%H%M%S<span class="si">)</span>
<span class="nv">EVIDENCE_DIR</span><span class="o">=</span><span class="s2">"evidence-</span><span class="k">${</span><span class="nv">TIMESTAMP</span><span class="k">}</span><span class="s2">"</span>
<span class="nb">mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="nv">$EVIDENCE_DIR</span><span class="s2">"</span>

<span class="nb">echo</span> <span class="s2">"Collecting evidence to </span><span class="nv">$EVIDENCE_DIR</span><span class="s2">..."</span>

<span class="c"># Cluster overview</span>
kubectl get nodes <span class="nt">-o</span> wide <span class="o">&gt;</span> <span class="s2">"</span><span class="nv">$EVIDENCE_DIR</span><span class="s2">/nodes.txt"</span>
kubectl get pods <span class="nt">-A</span> <span class="nt">-o</span> wide <span class="o">&gt;</span> <span class="s2">"</span><span class="nv">$EVIDENCE_DIR</span><span class="s2">/pods-all.txt"</span>
kubectl get events <span class="nt">-A</span> <span class="nt">--sort-by</span><span class="o">=</span>.lastTimestamp <span class="o">&gt;</span> <span class="s2">"</span><span class="nv">$EVIDENCE_DIR</span><span class="s2">/events-all.txt"</span>

<span class="c"># Tier-0 components</span>
kubectl <span class="nt">-n</span> kube-system get pods <span class="nt">-o</span> wide <span class="o">&gt;</span> <span class="s2">"</span><span class="nv">$EVIDENCE_DIR</span><span class="s2">/kube-system-pods.txt"</span>
kubectl <span class="nt">-n</span> kube-system describe pods <span class="o">&gt;</span> <span class="s2">"</span><span class="nv">$EVIDENCE_DIR</span><span class="s2">/kube-system-describe.txt"</span>

<span class="c"># Recent events (last 200)</span>
kubectl get events <span class="nt">-A</span> <span class="nt">--sort-by</span><span class="o">=</span>.lastTimestamp | <span class="nb">tail</span> <span class="nt">-n</span> 200 <span class="o">&gt;</span> <span class="s2">"</span><span class="nv">$EVIDENCE_DIR</span><span class="s2">/events-recent.txt"</span>

<span class="c"># Unhealthy pods</span>
kubectl get pods <span class="nt">-A</span> <span class="nt">--field-selector</span><span class="o">=</span>status.phase!<span class="o">=</span>Running <span class="o">&gt;</span> <span class="s2">"</span><span class="nv">$EVIDENCE_DIR</span><span class="s2">/unhealthy-pods.txt"</span>

<span class="c"># Node conditions</span>
kubectl get nodes <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{range .items[*]}{.metadata.name}{"\t"}{.status.conditions[?(@.type=="Ready")].status}{"\t"}{.status.conditions[?(@.type=="MemoryPressure")].status}{"\t"}{.status.conditions[?(@.type=="DiskPressure")].status}{"\n"}{end}'</span> <span class="o">&gt;</span> <span class="s2">"</span><span class="nv">$EVIDENCE_DIR</span><span class="s2">/node-conditions.txt"</span>

<span class="nb">echo</span> <span class="s2">"Evidence collected in </span><span class="nv">$EVIDENCE_DIR</span><span class="s2">"</span>
<span class="nb">echo</span> <span class="s2">"Attach this directory to your incident ticket"</span>
</code></pre></div></div>

<p><strong>What to capture for specific failure types</strong></p>

<p><strong>DNS/CoreDNS issues</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> k8s-app<span class="o">=</span>kube-dns <span class="nt">--tail</span><span class="o">=</span>500 <span class="o">&gt;</span> coredns-logs.txt
kubectl <span class="nt">-n</span> kube-system get pods <span class="nt">-l</span> k8s-app<span class="o">=</span>kube-dns <span class="nt">-o</span> yaml <span class="o">&gt;</span> coredns-pods.yaml
</code></pre></div></div>

<p><strong>CNI/Networking issues</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> k8s-app<span class="o">=</span>aws-node <span class="nt">--tail</span><span class="o">=</span>500 <span class="o">&gt;</span> aws-node-logs.txt
kubectl get pods <span class="nt">-A</span> <span class="nt">-o</span> wide | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s2">"ContainerCreating|Pending"</span> <span class="o">&gt;</span> stuck-pods.txt
</code></pre></div></div>

<p><strong>Node-specific issues</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Replace NODE_NAME with actual node</span>
kubectl describe node NODE_NAME <span class="o">&gt;</span> node-describe.txt
kubectl get pods <span class="nt">-A</span> <span class="nt">-o</span> wide | <span class="nb">grep </span>NODE_NAME <span class="o">&gt;</span> node-pods.txt
</code></pre></div></div>

<hr />

<h3 id="113-common-false-positives">1.13 Common False Positives</h3>

<p>These look like EKS issues but usually aren’t:</p>

<h4 id="kubernetes-is-slow-but-its-not">“Kubernetes is slow” (but it’s not)</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>kubectl commands are slow</li>
  <li>Deployments take forever</li>
  <li>“Everything is sluggish”</li>
</ul>

<p><strong>Usually actually:</strong></p>
<ul>
  <li>Your laptop’s VPN/network to cluster</li>
  <li>AWS API throttling (too many concurrent kubectl users)</li>
  <li>Your kubeconfig pointing to wrong cluster/region</li>
</ul>

<p><strong>Quick check:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">time </span>kubectl get nodes
<span class="c"># Should complete in &lt;2 seconds for healthy cluster</span>
</code></pre></div></div>

<h4 id="pods-keep-restarting-but-eks-is-fine">“Pods keep restarting” (but EKS is fine)</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>High restart count</li>
  <li>CrashLoopBackOff</li>
  <li>“Kubernetes keeps killing my app”</li>
</ul>

<p><strong>Usually actually:</strong></p>
<ul>
  <li>Application bugs (not Kubernetes bugs)</li>
  <li>Incorrect liveness probe configuration</li>
  <li>Resource limits too low (OOMKilled)</li>
  <li>Missing dependencies (DB, Redis, etc.)</li>
</ul>

<p><strong>Quick check:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl describe pod POD_NAME
<span class="c"># Look at "Last State" and "Reason"</span>
<span class="c"># OOMKilled = memory limit too low</span>
<span class="c"># Error = application crash</span>
</code></pre></div></div>

<h4 id="service-discovery-is-broken-but-dns-is-fine">“Service discovery is broken” (but DNS is fine)</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Services can’t reach each other</li>
  <li>“Connection refused” errors</li>
  <li>“Name resolution failures”</li>
</ul>

<p><strong>Usually actually:</strong></p>
<ul>
  <li>Wrong service name/namespace in application config</li>
  <li>Application listening on wrong port</li>
  <li>Readiness probe failing (pod not ready to receive traffic)</li>
  <li>Network policies blocking traffic</li>
</ul>

<p><strong>Quick check:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Test DNS resolution from inside a pod</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> POD_NAME <span class="nt">--</span> nslookup SERVICE_NAME.NAMESPACE.svc.cluster.local
<span class="c"># Test if service endpoints exist</span>
kubectl get endpoints SERVICE_NAME
</code></pre></div></div>

<hr />

<h3 id="what-you-should-know-by-now">What you should know by now</h3>

<p>After running through the above, you should be able to answer these with evidence:</p>

<ol>
  <li><strong>What is broken, and what is not?</strong>
    <ul>
      <li>One pod vs one workload vs one namespace vs one node group vs entire cluster</li>
    </ul>
  </li>
  <li><strong>Where is the failure domain?</strong>
    <ul>
      <li><strong>Workload-level</strong> (bad rollout, probe failures, config/secret issues)</li>
      <li><strong>Cluster-level</strong> (kube-system degradation, DNS, CNI, storage controller issues)</li>
      <li><strong>AWS integration layer</strong> (VPC CNI / ENI/IP limits, ALB/NLB behaviour, EBS attach/detach)</li>
      <li><strong>Node-level</strong> (resource pressure, kubelet instability, kernel/network issues)</li>
    </ul>
  </li>
  <li><strong>What category does this incident fall into?</strong>
    <ul>
      <li>Scheduling/capacity (<code class="language-plaintext highlighter-rouge">FailedScheduling</code>, Pending pods)</li>
      <li>Networking/CNI (pod sandbox failures, IP exhaustion, random timeouts)</li>
      <li>DNS/CoreDNS (service discovery failures)</li>
      <li>Storage/EBS CSI (FailedMount, volume attach issues)</li>
      <li>Control plane/API issues (timeouts, throttling, admission webhook failures)</li>
    </ul>
  </li>
  <li><strong>Is this node-specific “kernel pain” or cluster-wide?</strong>
    <ul>
      <li>Conntrack exhaustion / ephemeral port pressure</li>
      <li>Mis-sized sysctls</li>
      <li>Kubelet eviction behaviour under pressure</li>
      <li>Incorrect QoS class due to bad requests/limits</li>
    </ul>
  </li>
  <li><strong>What is the blast radius and what’s the next safe action?</strong>
    <ul>
      <li>Can you isolate by draining/cordoning nodes?</li>
      <li>Should you pause a rollout?</li>
      <li>Should you scale out node groups / reduce pressure?</li>
      <li>Or do you need to stop and fix platform components first?</li>
    </ul>
  </li>
  <li><strong>What evidence did you capture before making changes?</strong>
    <ul>
      <li>Events, node state, pod distribution, and logs that will make the RCA real (and not guesswork)</li>
    </ul>
  </li>
</ol>

<p>If you can’t answer these after Section 1, don’t start “random fixes”. You need more signal (metrics, kube-system logs, AWS-side telemetry) before touching production.</p>

<hr />
<h2 id="2-eks-tier-0-components-what-must-stay-healthy">2. EKS Tier-0 components (what must stay healthy)</h2>

<p>EKS gives you a managed control plane, but your workloads still depend on a small set of platform-critical components. If any of these degrade, the cluster looks partially alive while production is effectively down.</p>

<p>Below: what they do, how they fail, what you’ll see, and what to check first.</p>

<hr />

<h3 id="21-eks-control-plane-managed-but-not-magic">2.1 EKS Control Plane (Managed, but not magic)</h3>

<p><strong>What it includes</strong></p>

<ul>
  <li>Kubernetes API server</li>
  <li>etcd (managed)</li>
  <li>controller-manager (managed)</li>
  <li>scheduler (managed)</li>
</ul>

<p><strong>Common symptoms when control plane is unhealthy</strong></p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">kubectl</code> commands are slow / timing out</li>
  <li>deployments take forever to apply</li>
  <li>controllers lag (HPA doesn’t scale, pods don’t reschedule)</li>
  <li>random “context deadline exceeded” errors</li>
</ul>

<p><strong>First checks</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get <span class="nt">--raw</span><span class="o">=</span><span class="s1">'/readyz?verbose'</span>
kubectl get <span class="nt">--raw</span><span class="o">=</span><span class="s1">'/livez?verbose'</span>
kubectl get events <span class="nt">-A</span> <span class="nt">--sort-by</span><span class="o">=</span>.lastTimestamp | <span class="nb">tail</span> <span class="nt">-n</span> 50
</code></pre></div></div>

<p><strong>Pragmatic note</strong>
Even if the control plane is “managed”, you still need:</p>

<ul>
  <li>sane API usage (avoid thundering herds from controllers/tools)</li>
  <li>sane webhook behaviour (one broken webhook can block deployments)</li>
</ul>

<hr />

<h3 id="22-coredns-cluster-dns">2.2 CoreDNS (Cluster DNS)</h3>

<p><strong>Why it’s Tier-0</strong>
If DNS breaks, your apps don’t “partially degrade”. They fail in confusing ways:</p>

<ul>
  <li>timeouts</li>
  <li>connection errors</li>
  <li>retries that amplify load</li>
</ul>

<p><strong>Symptoms</strong></p>

<ul>
  <li>services can’t resolve (<code class="language-plaintext highlighter-rouge">*.svc.cluster.local</code>)</li>
  <li>random failures between pods</li>
  <li>sudden spike in upstream request errors</li>
</ul>

<p><strong>First checks</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nt">-n</span> kube-system get pods <span class="nt">-l</span> k8s-app<span class="o">=</span>kube-dns <span class="nt">-o</span> wide
kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> k8s-app<span class="o">=</span>kube-dns <span class="nt">--tail</span><span class="o">=</span>200
kubectl <span class="nt">-n</span> kube-system describe deploy coredns
</code></pre></div></div>

<p><strong>Common root causes</strong></p>

<ul>
  <li>CoreDNS under-provisioned (CPU/mem)</li>
  <li>upstream resolver issues</li>
  <li>network problems to kube-dns service IP</li>
  <li>node-local DNS/cache behaviour (if enabled)</li>
</ul>

<hr />

<h3 id="23-aws-vpc-cni-aws-node--networking-foundation">2.3 AWS VPC CNI (<code class="language-plaintext highlighter-rouge">aws-node</code>) — Networking Foundation</h3>

<p><strong>Why it’s Tier-0</strong>
This is what gives pods IPs and makes pod networking real.
If this is unhealthy, pods won’t start or won’t communicate reliably.</p>

<p><strong>Symptoms</strong></p>

<ul>
  <li>pods stuck in <code class="language-plaintext highlighter-rouge">ContainerCreating</code></li>
  <li><code class="language-plaintext highlighter-rouge">FailedCreatePodSandBox</code></li>
  <li>sudden surge of Pending pods due to no IPs</li>
  <li>node-specific network failures</li>
</ul>

<p><strong>First checks</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nt">-n</span> kube-system get pods <span class="nt">-l</span> k8s-app<span class="o">=</span>aws-node <span class="nt">-o</span> wide
kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> k8s-app<span class="o">=</span>aws-node <span class="nt">--tail</span><span class="o">=</span>200
kubectl <span class="nt">-n</span> kube-system describe ds aws-node
</code></pre></div></div>

<p><strong>Typical EKS causes</strong></p>

<ul>
  <li>subnet IP exhaustion</li>
  <li>ENI limits / pod density limits</li>
  <li>prefix delegation mis-sizing</li>
  <li>conntrack pressure (often shows up as “network flaky”)</li>
</ul>

<hr />

<h3 id="24-kube-proxy-service-routing">2.4 kube-proxy (Service Routing)</h3>

<p><strong>Why it’s Tier-0</strong>
Even if pods are healthy, service routing can break:</p>

<ul>
  <li>ClusterIP routing issues</li>
  <li>weird partial connectivity problems</li>
</ul>

<p><strong>First checks</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nt">-n</span> kube-system get pods <span class="nt">-l</span> k8s-app<span class="o">=</span>kube-proxy <span class="nt">-o</span> wide
kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> k8s-app<span class="o">=</span>kube-proxy <span class="nt">--tail</span><span class="o">=</span>200
</code></pre></div></div>

<p><strong>Pragmatic note</strong>
If you’re using an eBPF dataplane instead of kube-proxy, document it explicitly.
Debug steps change.</p>

<hr />

<h3 id="25-aws-load-balancer-controller-albnlb-integration">2.5 AWS Load Balancer Controller (ALB/NLB Integration)</h3>

<p><strong>Why it’s Tier-0</strong>
This is the bridge between Kubernetes ingress/service objects and AWS load balancers.
If it breaks, external traffic fails even if the cluster is healthy.</p>

<p><strong>Symptoms</strong></p>

<ul>
  <li>Ingress doesn’t provision</li>
  <li>Target groups empty / unhealthy</li>
  <li>external traffic 4xx/5xx</li>
  <li>“it works inside the cluster but not from the internet”</li>
</ul>

<p><strong>First checks</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nt">-n</span> kube-system get deploy | <span class="nb">grep</span> <span class="nt">-i</span> load-balancer
kubectl <span class="nt">-n</span> kube-system logs deploy/aws-load-balancer-controller <span class="nt">--tail</span><span class="o">=</span>200
kubectl get ingress <span class="nt">-A</span>
kubectl describe ingress <span class="nt">-n</span> &lt;ns&gt; &lt;name&gt;
</code></pre></div></div>

<p><strong>Common causes</strong></p>

<ul>
  <li>IAM permissions issues</li>
  <li>security group rules wrong</li>
  <li>subnet tagging wrong</li>
  <li>controller version mismatch during upgrades</li>
</ul>

<h4 id="251-nlb-idle-timeout--keep-alive-silent-connection-kill">2.5.1 NLB Idle Timeout + Keep-Alive (Silent Connection Kill)</h4>

<p>When you use an <strong>AWS Network Load Balancer (NLB)</strong>, remember this:</p>

<ul>
  <li>NLB is <strong>L4</strong>, not L7.</li>
  <li>It does <strong>not</strong> create a separate application-layer connection like an ALB does.</li>
  <li>It tracks TCP/UDP flows internally so it can route packets correctly.</li>
  <li>If a connection is <strong>idle for ~350 seconds</strong>, NLB will forget it.</li>
  <li>After that, if the client or server tries to send more data on that “old” connection, the NLB can respond with a <strong>TCP RST</strong>.</li>
</ul>

<p><strong>What it looks like</strong></p>

<ul>
  <li>random <code class="language-plaintext highlighter-rouge">connection reset by peer</code></li>
  <li>intermittent failures for long-lived but mostly-idle connections</li>
  <li>higher failure rates on low-traffic tenants or long polling style traffic</li>
  <li>hard-to-reproduce “only happens sometimes” reports</li>
</ul>

<p><strong>Why it happens</strong>
Your application (or client) assumes the TCP connection is still valid because it was never explicitly closed.
But the NLB has expired the idle flow state, so the next packet hits a dead path and gets reset.</p>

<p><strong>Fix</strong>
Enable <strong>TCP keep-alives</strong> so the connection never goes idle long enough to be forgotten.</p>

<p>You generally need:</p>

<ul>
  <li>keep-alives enabled on the <strong>server listener socket</strong></li>
  <li>keep-alives enabled on the <strong>client side</strong> too (if you control it)</li>
</ul>

<p><strong>Pragmatic guidance</strong></p>

<ul>
  <li>If you’re running connection-heavy services behind NLB (gRPC, streaming, long-lived HTTP/1.1 keep-alive, custom TCP protocols), treat keep-alive tuning as a <strong>production requirement</strong>, not an optimization.</li>
  <li>If you can’t control the client, you may need to:
    <ul>
      <li>reduce server-side idle timeouts</li>
      <li>implement app-level heartbeats</li>
      <li>or prefer ALB where L7 behaviour is needed</li>
    </ul>
  </li>
</ul>

<h4 id="252-nat-idle-timeout--keep-alive-egress-connection-resets">2.5.2 NAT Idle Timeout + Keep-Alive (Egress Connection Resets)</h4>

<p>When workloads in EKS talk to services on the public internet, traffic often goes through a <strong>NAT device</strong> (commonly AWS NAT Gateway). NAT devices track connection state, and they will <strong>forget idle connections</strong> after a timeout.</p>

<p>For example, <strong>AWS NAT Gateway has an idle timeout of ~350 seconds</strong>. After that, the NAT forgets the flow. If the client or server tries to send traffic on that old connection, it can result in a <strong>TCP RST</strong>.</p>

<p><strong>What it looks like</strong></p>

<ul>
  <li>random outbound <code class="language-plaintext highlighter-rouge">connection reset by peer</code></li>
  <li>flaky third-party API calls (only under low traffic / idle periods)</li>
  <li>long-lived connections that “randomly die”</li>
  <li>retries sometimes help, sometimes amplify load</li>
</ul>

<p><strong>Why it happens</strong>
Your application thinks it still has a valid TCP connection.
The NAT has expired the mapping due to idleness.
The next packet is treated as invalid state and gets reset.</p>

<p><strong>Fix: enable TCP keep-alives</strong></p>

<p>The simplest fix is to ensure connections don’t remain idle long enough to be forgotten.</p>

<p><strong>Options</strong></p>

<ol>
  <li><strong>Enable TCP keep-alives in the application / proxy</strong>
    <ul>
      <li>If your proxy supports keepalive tuning, configure it.</li>
      <li>Example: Envoy has TCP keepalive configuration support.</li>
    </ul>
  </li>
  <li><strong>If the application/proxy doesn’t support it</strong>
    <ul>
      <li>Consider enabling it transparently using <code class="language-plaintext highlighter-rouge">LD_PRELOAD</code></li>
      <li>Tools like <code class="language-plaintext highlighter-rouge">libsetsockopt</code> can be used to apply <code class="language-plaintext highlighter-rouge">setsockopt()</code> defaults without changing application code</li>
    </ul>
  </li>
</ol>

<p><strong>Pragmatic guidance</strong></p>

<ul>
  <li>Prefer fixing this at the proxy layer (Envoy / Nginx / HAProxy) where possible.</li>
  <li>If you use <code class="language-plaintext highlighter-rouge">LD_PRELOAD</code>, treat it as an engineering workaround:
    <ul>
      <li>document it</li>
      <li>test it under load</li>
      <li>make it part of your base image / runtime standard</li>
      <li>expect debugging complexity later</li>
    </ul>
  </li>
</ul>

<p><strong>Monitor it (otherwise you’ll rediscover it during incidents)</strong></p>

<p>If you use AWS NAT Gateway, monitor the metric:</p>

<ul>
  <li><strong>IdleTimeoutCount</strong></li>
</ul>

<p>A rising IdleTimeoutCount is a strong indicator of:</p>

<ul>
  <li>too many idle-but-long-lived connections</li>
  <li>missing keepalive settings</li>
  <li>workload patterns that need pooling / reuse / heartbeat</li>
</ul>

<hr />

<h3 id="26-ebs-csi-driver-stateful-workloads-depend-on-it">2.6 EBS CSI Driver (Stateful Workloads Depend on It)</h3>

<p><strong>Why it’s Tier-0</strong>
If you run StatefulSets with EBS, this is not optional.</p>

<p><strong>Symptoms</strong></p>

<ul>
  <li>pods stuck in <code class="language-plaintext highlighter-rouge">ContainerCreating</code></li>
  <li>volume attach/detach timeouts</li>
  <li><code class="language-plaintext highlighter-rouge">FailedMount</code> events</li>
  <li>reschedules fail across AZs</li>
</ul>

<p><strong>First checks</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nt">-n</span> kube-system get pods | <span class="nb">grep </span>ebs
kubectl <span class="nt">-n</span> kube-system logs deploy/ebs-csi-controller <span class="nt">--tail</span><span class="o">=</span>200
kubectl get pvc <span class="nt">-A</span>
kubectl describe pod <span class="nt">-n</span> &lt;ns&gt; &lt;pod&gt;
</code></pre></div></div>

<hr />

<h3 id="27-metrics-server-scaling--visibility">2.7 Metrics Server (Scaling + Visibility)</h3>

<p><strong>Why it matters</strong>
Not Tier-0 for <em>serving traffic</em>, but Tier-0 for <em>operating sanely</em>.
Without it:</p>

<ul>
  <li>HPA breaks</li>
  <li><code class="language-plaintext highlighter-rouge">kubectl top</code> is useless</li>
  <li>you lose quick visibility into node/pod pressure</li>
</ul>

<p><strong>First checks</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nt">-n</span> kube-system get deploy metrics-server
kubectl <span class="nt">-n</span> kube-system logs deploy/metrics-server <span class="nt">--tail</span><span class="o">=</span>200
kubectl top nodes
kubectl top pods <span class="nt">-A</span> | <span class="nb">head</span>
</code></pre></div></div>

<hr />

<h3 id="28-eks-addon-compatibility-silent-failure-generator">2.8 EKS Addon Compatibility (Silent Failure Generator)</h3>

<p>EKS upgrades are rarely just “upgrade Kubernetes”. You’re upgrading an ecosystem:</p>

<ul>
  <li>CoreDNS</li>
  <li>kube-proxy</li>
  <li>VPC CNI</li>
  <li>CSI drivers</li>
  <li>LB controller</li>
</ul>

<p><strong>Rule</strong>
If you don’t track addon versions and compatibility, you will eventually debug a failure caused by version skew.</p>

<hr />

<h3 id="29-aws-vpc--ec2-network-limits-the-invisible-ceiling">2.9 AWS VPC / EC2 Network Limits (The Invisible Ceiling)</h3>

<p>In AWS, network performance is not “infinite until it breaks”. It is governed by a set of <strong>hard limits</strong> at the VPC/EC2 layer. Some of these limits are documented, many are not. When you hit them, AWS usually does not fail loudly — it fails as <strong>latency, timeouts, dropped packets, and random connection resets</strong>.</p>

<p>In EKS, this becomes easier to trigger because Kubernetes <strong>binpacks many workloads onto one EC2 instance</strong>, which means:</p>

<ul>
  <li>one noisy pod can consume a node-level network limit</li>
  <li>every other pod on the node suffers</li>
  <li>symptoms look like “the app is slow” even though the app is fine</li>
</ul>

<p>This section exists so we stop blaming applications for AWS network ceilings.</p>

<h4 id="291-why-this-matters-more-in-eks-binpacking-amplifies-limits">2.9.1 Why this matters more in EKS (binpacking amplifies limits)</h4>

<p>In a VM-per-service world, a single service hitting a network limit impacts itself.
In Kubernetes, a single node can run:</p>

<ul>
  <li>dozens/hundreds of pods</li>
  <li>shared proxies (Envoy/Nginx)</li>
  <li>CoreDNS</li>
  <li>DaemonSets</li>
</ul>

<p>Everything shares the same node-level network limits.</p>

<p>The result is predictable:</p>

<blockquote>
  <p>A single workload can push the node over a limit and make unrelated services time out.</p>
</blockquote>

<h4 id="292-real-world-failure-mode-dns-lookups-clustering-onto-coredns-nodes">2.9.2 Real-world failure mode: DNS lookups clustering onto CoreDNS nodes</h4>

<p>By default, pods resolve DNS through CoreDNS:</p>

<p><strong>Traffic path</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>application pod
  -&gt; kube-dns service (CoreDNS pods)
    -&gt; EC2 link-local resolver
</code></pre></div></div>

<p>Only a small number of CoreDNS pods typically run in a cluster, which means:</p>

<ul>
  <li>DNS traffic concentrates onto a few nodes (where CoreDNS pods are scheduled)</li>
  <li>those EC2 instances can hit <strong>link-local limits</strong></li>
  <li>the cluster sees “DNS is flaky” even though most nodes are fine</li>
</ul>

<p>This is one of the easiest ways to create a cluster-wide incident with no obvious “broken component”.</p>

<h4 id="293-link-local-traffic-limits-169254xx--easy-to-hit-hard-to-debug">2.9.3 Link-local traffic limits (169.254.x.x) — easy to hit, hard to debug</h4>

<p>Each EC2 instance exposes local services via link-local addresses (example: <code class="language-plaintext highlighter-rouge">169.254.169.254</code>). These are used for:</p>

<ul>
  <li>instance metadata</li>
  <li>temporary IAM credentials</li>
  <li>time sync and other local services</li>
  <li>DNS resolution via the VPC resolver path</li>
</ul>

<p>These endpoints have limits. If you breach them, traffic gets rate-limited or dropped, and the failures are messy:</p>

<ul>
  <li>timeouts</li>
  <li>slow DNS</li>
  <li>slow credential refresh</li>
  <li>sporadic errors that look unrelated</li>
</ul>

<p><strong>Pragmatic rule</strong>
Treat link-local as a <strong>shared, rate-limited dependency</strong>.</p>

<h4 id="294-dns-query-limits-vpc-resolver--the-1024-packetssec-trap">2.9.4 DNS query limits (VPC resolver) — the 1024 packets/sec trap</h4>

<p>Each EC2 instance has a hard cap on DNS traffic to the VPC resolver. As of this writing, it’s effectively capped at:</p>

<ul>
  <li><strong>1024 packets per second</strong> <em>(packets, not queries)</em></li>
</ul>

<p>This distinction matters:</p>

<ul>
  <li>A “DNS query” is not always one packet.</li>
  <li>With UDP, you usually pay at least:
    <ul>
      <li>1 packet request + 1 packet response → <strong>2 packets per query</strong></li>
    </ul>
  </li>
  <li>That means you might only get <strong>~512 queries/sec</strong> in the simplest case.</li>
  <li>With larger responses, retries, TCP fallback, or DNSSEC, it gets worse.</li>
</ul>

<p>In EKS, hundreds of pods on a node share this limit. It’s trivial to breach it.</p>

<p><strong>What it looks like</strong></p>

<ul>
  <li>intermittent DNS resolution timeouts</li>
  <li>cascading app failures (everything depends on DNS)</li>
  <li>retries amplify the packet rate and make it worse</li>
</ul>

<p><strong>Mitigation</strong></p>

<ul>
  <li>cache DNS aggressively (application-level where possible)</li>
  <li>consider node-local DNS caching (NodeLocal DNSCache / dnsmasq style)</li>
  <li>keep CoreDNS well distributed across nodes/AZs</li>
</ul>

<h4 id="295-security-group-connection-tracking-limits-stateful-firewall-limits">2.9.5 Security Group connection tracking limits (stateful firewall limits)</h4>

<p>Security Groups are stateful. That means connection tracking happens, and there is a finite limit to how many concurrent connections an instance can sustain.</p>

<p>Important details:</p>

<ul>
  <li>limits vary by instance type</li>
  <li>some limits are not clearly documented</li>
  <li>when you hit them, symptoms look like:
    <ul>
      <li>new TCP connections fail</li>
      <li>connection establishment stalls</li>
      <li>timeouts and latency spikes</li>
    </ul>
  </li>
</ul>

<p><strong>Where this hurts in EKS</strong></p>

<ul>
  <li>reverse proxies handling lots of traffic</li>
  <li>long-lived connections (websocket, streaming, gRPC)</li>
  <li>high churn connection patterns (bad client behaviour, retries, load tests)</li>
</ul>

<p><strong>Pragmatic guidance</strong></p>

<ul>
  <li>long-lived connections are fine, but you must design for them:
    <ul>
      <li>keepalive tuning</li>
      <li>connection pooling</li>
      <li>horizontal scaling</li>
      <li>avoid concentrating all traffic on a single node</li>
    </ul>
  </li>
</ul>

<h4 id="296-what-to-monitor-and-alert-on">2.9.6 What to monitor (and alert on)</h4>

<p>CloudWatch is not enough for this class of failures.</p>

<p>To monitor these limits properly, you need <strong>node-level network driver metrics</strong>.</p>

<p>AWS ENA exposes useful counters on each EC2 instance that are:</p>

<ul>
  <li>not always available in CloudWatch by default</li>
  <li>best collected by scraping from the node and shipping to your monitoring system</li>
</ul>

<p><strong>Action item</strong>
Run a node-level metrics collector (DaemonSet or host agent) that scrapes ENA-related counters and publishes them to Prometheus / your metrics pipeline.</p>

<hr />

<h3 id="210-what-to-do-when-a-tier-0-component-is-unhealthy-eks-incident-playbook">2.10 What to do when a Tier-0 component is unhealthy (EKS Incident Playbook)</h3>

<p>Tier-0 failures are not limited to <code class="language-plaintext highlighter-rouge">kube-system</code>. In EKS, Tier-0 also includes AWS networking primitives that your cluster depends on:</p>

<ul>
  <li><strong>NLB / ALB</strong> (ingress and L4/L7 connectivity)</li>
  <li><strong>NAT Gateway</strong> (egress to internet / third-party dependencies)</li>
  <li><strong>Security Group connection tracking</strong> (stateful connection ceilings)</li>
</ul>

<p>When any of these degrade, application symptoms become misleading. Your job is to stabilize the platform and the AWS networking layer first.</p>

<h4 id="2101-step-0--stop-making-the-incident-worse">2.10.1 Step 0 — Stop making the incident worse</h4>

<p><strong>Do NOT</strong></p>

<ul>
  <li>restart random deployments</li>
  <li>roll out unrelated changes</li>
  <li>run scripts that spam the Kubernetes API</li>
  <li>scale CoreDNS / proxies blindly without checking node headroom</li>
</ul>

<p><strong>Do</strong></p>

<ul>
  <li>pause ongoing rollouts if they’re increasing churn</li>
  <li>capture evidence before it disappears</li>
</ul>

<p><strong>Capture evidence (minimum)</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get nodes <span class="nt">-o</span> wide <span class="o">&gt;</span> nodes.txt
kubectl get pods <span class="nt">-A</span> <span class="nt">-o</span> wide <span class="o">&gt;</span> pods.txt
kubectl get events <span class="nt">-A</span> <span class="nt">--sort-by</span><span class="o">=</span>.lastTimestamp | <span class="nb">tail</span> <span class="nt">-n</span> 200 <span class="o">&gt;</span> events.txt
</code></pre></div></div>

<h4 id="2102-step-1--confirm-blast-radius-cluster-vs-node-group-vs-az-vs-edge">2.10.2 Step 1 — Confirm blast radius (cluster vs node-group vs AZ vs edge)</h4>

<p>Tier-0 failures often appear “global” but aren’t. Quickly classify:</p>

<ul>
  <li><strong>Cluster-wide</strong>: kube-system pods unhealthy across nodes</li>
  <li><strong>Node-group/AZ-specific</strong>: only one pool or AZ shows issues</li>
  <li><strong>Edge-only</strong>: internal works, external traffic fails (NLB/ALB)</li>
  <li><strong>Egress-only</strong>: internal works, outbound calls fail (NAT)</li>
</ul>

<p><strong>Quick checks</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get nodes
kubectl get pods <span class="nt">-n</span> kube-system <span class="nt">-o</span> wide
kubectl get pods <span class="nt">-A</span> <span class="nt">-o</span> wide | <span class="nb">head</span> <span class="nt">-n</span> 50
</code></pre></div></div>

<h4 id="2103-step-2--identify-the-failing-tier-0-dependency-k8s--aws">2.10.3 Step 2 — Identify the failing Tier-0 dependency (K8s + AWS)</h4>

<p><strong>A) CoreDNS unhealthy → DNS failures everywhere</strong></p>

<p><strong>Checks</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nt">-n</span> kube-system get pods <span class="nt">-l</span> k8s-app<span class="o">=</span>kube-dns <span class="nt">-o</span> wide
kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> k8s-app<span class="o">=</span>kube-dns <span class="nt">--tail</span><span class="o">=</span>200
</code></pre></div></div>

<p><strong>Safe actions</strong></p>

<ul>
  <li>scale CoreDNS only if nodes have headroom</li>
  <li>spread CoreDNS pods (avoid hotspot nodes)</li>
  <li>reduce DNS pressure (cache / fix runaway clients)</li>
</ul>

<p><strong>B) VPC CNI (<code class="language-plaintext highlighter-rouge">aws-node</code>) unhealthy → pod networking breaks</strong></p>

<p><strong>Checks</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nt">-n</span> kube-system get pods <span class="nt">-l</span> k8s-app<span class="o">=</span>aws-node <span class="nt">-o</span> wide
kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> k8s-app<span class="o">=</span>aws-node <span class="nt">--tail</span><span class="o">=</span>200
</code></pre></div></div>

<p><strong>Safe actions</strong></p>

<ul>
  <li>cordon impacted nodes</li>
  <li>scale out node groups if out of IPs / pod density</li>
  <li>verify subnet IP capacity / prefix delegation</li>
</ul>

<p><strong>C) NAT idle timeout / keepalive mismatch → random outbound resets (egress path)</strong></p>

<p>Same 350s idle timeout as NLB, but on the egress path. See <a href="#252-nat-idle-timeout-keep-alive-egress-connection-resets">Section 2.5.2</a> for full detail.</p>

<p><strong>Safe actions</strong></p>

<ul>
  <li>enable TCP keep-alives in app/proxy</li>
  <li>monitor NAT <code class="language-plaintext highlighter-rouge">IdleTimeoutCount</code></li>
</ul>

<h4 id="2104-step-3--contain-blast-radius-prevent-spread">2.10.4 Step 3 — Contain blast radius (prevent spread)</h4>

<p>Once you know the failing Tier-0 dependency:</p>

<p><strong>Containment options</strong></p>

<ul>
  <li>cordon nodes to stop new scheduling:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl cordon &lt;node&gt;
</code></pre></div></div>

<ul>
  <li>drain nodes only when safe:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl drain &lt;node&gt; <span class="nt">--ignore-daemonsets</span> <span class="nt">--delete-emptydir-data</span>
</code></pre></div></div>

<ul>
  <li>scale out node groups if the failure is capacity-related (IP/CPU/mem/conntrack)</li>
</ul>

<p><strong>Key principle</strong>
Containment &gt; churn. Avoid actions that increase retries and reconnections.</p>

<h4 id="2105-step-4--restore-tier-0-health-stabilize-then-recover">2.10.5 Step 4 — Restore Tier-0 health (stabilize, then recover)</h4>

<p>Stabilize in this order:</p>

<ol>
  <li>Control plane responsiveness (kubectl/API sanity)</li>
  <li>kube-system essentials (CoreDNS, aws-node, kube-proxy)</li>
  <li>Storage controllers (EBS CSI if stateful workloads exist)</li>
  <li>Ingress/egress path (NLB/NAT behaviours, keepalives, target health)</li>
  <li>Workloads</li>
</ol>

<p>Only after Tier-0 is stable should you restart/roll workloads.</p>

<h4 id="2106-step-5--validate-recovery-dont-declare-victory-early">2.10.6 Step 5 — Validate recovery (don’t declare victory early)</h4>

<p>Recovery means:</p>

<ul>
  <li>DNS stable</li>
  <li>new pods schedule and start</li>
  <li>ingress traffic healthy</li>
  <li>egress stable (no NAT idle reset spikes)</li>
  <li>error rate + tail latency back to baseline</li>
</ul>

<p><strong>Quick checks</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get nodes
kubectl <span class="nt">-n</span> kube-system get pods <span class="nt">-o</span> wide
kubectl get pods <span class="nt">-A</span> <span class="nt">--field-selector</span><span class="o">=</span>status.phase!<span class="o">=</span>Running | <span class="nb">head</span>
</code></pre></div></div>

<h4 id="2107-post-incident-hardening-mandatory-follow-up">2.10.7 Post-incident hardening (mandatory follow-up)</h4>

<p>Every Tier-0 incident must produce:</p>

<ul>
  <li>alerts (CoreDNS, aws-node, EBS CSI, LB controller)</li>
  <li>AWS networking alerts (NAT <code class="language-plaintext highlighter-rouge">IdleTimeoutCount</code>, node <code class="language-plaintext highlighter-rouge">conntrack_allowance_exceeded</code>)</li>
  <li>guardrails:
    <ul>
      <li>CoreDNS spread constraints</li>
      <li>DNS caching strategy</li>
      <li>keepalive defaults for ingress/egress</li>
      <li>instance type sizing for connection-heavy services</li>
    </ul>
  </li>
</ul>

<hr />
<h2 id="3-networking">3. Networking</h2>

<p>This is where most “mysterious” production failures actually live. Unlike compute or storage failures that fail loudly, networking degrades gradually and inconsistently. A connection works 95% of the time. DNS resolves “most of the time”. Latency spikes “only under load”.</p>

<p>Below: the EKS networking stack and how to debug it systematically when things go sideways.</p>

<hr />

<h3 id="31-eks-networking-stack-what-can-break-and-where">3.1 EKS Networking Stack (What Can Break and Where)</h3>

<p>Understanding the layers helps you debug faster:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Pod A] 
  ↓ (veth pair)
[Node's root netns] 
  ↓ (AWS VPC CNI / ENI)
[AWS VPC] 
  ↓ (routing, security groups, NACLs)
[Target: Pod B / Service / Internet]
</code></pre></div></div>

<p><strong>Each layer can fail differently:</strong></p>

<ul>
  <li><strong>Pod network namespace</strong>: wrong routes, missing interfaces</li>
  <li><strong>Node networking</strong>: CNI plugin issues, IP exhaustion, conntrack</li>
  <li><strong>VPC layer</strong>: security groups, routing tables, subnet capacity</li>
  <li><strong>AWS services</strong>: NLB/ALB behavior, NAT timeouts, DNS resolver limits</li>
</ul>

<hr />

<h3 id="32-aws-vpc-cni-deep-dive-the-foundation">3.2 AWS VPC CNI Deep Dive (The Foundation)</h3>

<p>The AWS VPC CNI is what makes “pod gets a real VPC IP” work. When it breaks, symptoms are confusing because pods might start but not communicate, or communication works sometimes but not others.</p>

<h4 id="321-how-aws-vpc-cni-works-simplified">3.2.1 How AWS VPC CNI Works (Simplified)</h4>

<ol>
  <li><strong>ENI allocation</strong>: Each node gets multiple ENIs (network interfaces)</li>
  <li><strong>IP allocation</strong>: Each ENI gets either:
    <ul>
      <li>Multiple secondary IPs (legacy mode)</li>
      <li>IP prefixes (/28 blocks) when prefix delegation is enabled</li>
    </ul>
  </li>
  <li><strong>Pod assignment</strong>: Each pod gets one IP from the available pool</li>
  <li><strong>Routing</strong>: Node routes traffic between pod netns and ENI</li>
</ol>

<p>Pod density isn’t just CPU/memory limited — it’s bounded by ENI limits and either IP-per-ENI limits (legacy) or prefix allocation limits (with prefix delegation).</p>

<p><strong>Prefix delegation benefits:</strong></p>
<ul>
  <li>Dramatically increases pod density (from ~10-250 pods per node to ~110-750+ pods)</li>
  <li>Reduces ENI pressure on larger instance types</li>
  <li>More efficient IP utilization</li>
</ul>

<h4 id="322-common-cni-failure-modes">3.2.2 Common CNI Failure Modes</h4>

<p><strong>A) IP Exhaustion (Pods stuck in Pending)</strong></p>

<p><strong>Symptoms:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get pods <span class="nt">-A</span> | <span class="nb">grep </span>Pending
kubectl describe pod &lt;pod&gt; | <span class="nb">grep</span> <span class="nt">-i</span> <span class="s2">"failed to allocate"</span>
</code></pre></div></div>

<p><strong>Root causes:</strong></p>
<ul>
  <li>Subnet out of IPs</li>
  <li>Node hit max pods per instance type</li>
  <li>Prefix delegation misconfigured or not enabled</li>
  <li>ENI limits reached without prefix delegation</li>
</ul>

<p><strong>Quick diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check available IPs in subnet</span>
aws ec2 describe-subnets <span class="nt">--subnet-ids</span> &lt;subnet-id&gt;

<span class="c"># Check pod density limits</span>
kubectl get nodes <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{range .items[*]}{.metadata.name}{"\t"}{.status.allocatable.pods}{"\n"}{end}'</span>

<span class="c"># Check CNI logs</span>
kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> k8s-app<span class="o">=</span>aws-node <span class="nt">--tail</span><span class="o">=</span>100
</code></pre></div></div>

<p><strong>B) ENI Attachment Failures</strong></p>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>New nodes can’t schedule pods</li>
  <li><code class="language-plaintext highlighter-rouge">FailedCreatePodSandBox</code> errors</li>
  <li>CNI timeouts during pod creation</li>
</ul>

<p><strong>Quick diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> k8s-app<span class="o">=</span>aws-node | <span class="nb">grep</span> <span class="nt">-i</span> <span class="s2">"eni</span><span class="se">\|</span><span class="s2">attach</span><span class="se">\|</span><span class="s2">interface"</span>
</code></pre></div></div>

<p><strong>C) Cross-AZ Communication Issues</strong></p>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Pods in different AZs can’t reach each other</li>
  <li>Intermittent timeouts between services</li>
  <li>Works within AZ, fails across AZ</li>
</ul>

<p><strong>Root causes:</strong></p>
<ul>
  <li>Route table misconfigurations</li>
  <li>Security group rules</li>
  <li>NACLs blocking cross-AZ traffic</li>
</ul>

<h4 id="323-cni-configuration-tuning">3.2.3 CNI Configuration Tuning</h4>

<p><strong>Key parameters to understand:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check current CNI configuration</span>
kubectl <span class="nt">-n</span> kube-system describe daemonset aws-node
</code></pre></div></div>

<p><strong>Important environment variables:</strong></p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">ENABLE_PREFIX_DELEGATION</code>: Increases pod density</li>
  <li><code class="language-plaintext highlighter-rouge">WARM_ENI_TARGET</code>: Pre-allocates ENIs for faster pod startup</li>
  <li><code class="language-plaintext highlighter-rouge">WARM_IP_TARGET</code>: Pre-allocates IPs for faster pod startup</li>
  <li><code class="language-plaintext highlighter-rouge">MAX_ENI</code>: Limits ENI usage per node</li>
</ul>

<p><strong>Production tuning example:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">env</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">ENABLE_PREFIX_DELEGATION</span>
  <span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">true"</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">WARM_PREFIX_TARGET</span>
  <span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">1"</span>        <span class="c1"># Keep 1 prefix warm (16 IPs)</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">WARM_IP_TARGET</span>
  <span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">3"</span>        <span class="c1"># Keep 3 individual IPs warm</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">MAX_ENI</span>
  <span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">10"</span>       <span class="c1"># Limit ENI usage if needed</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">AWS_VPC_K8S_CNI_EXTERNALSNAT</span>
  <span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">true"</span>     <span class="c1"># Preserve pod IPs for cross-VPC communication</span>
</code></pre></div></div>

<p><strong>Understanding prefix vs IP mode:</strong></p>
<ul>
  <li><strong>Legacy (IP mode)</strong>: Each ENI gets ~15-50 secondary IPs depending on instance type</li>
  <li><strong>Prefix mode</strong>: Each ENI gets /28 prefixes (16 IPs each), dramatically increasing density</li>
  <li><strong>Mixed mode</strong>: Can use both prefixes and individual IPs on same ENI</li>
</ul>

<p><strong>External SNAT configuration:</strong></p>
<ul>
  <li><strong>Default (false)</strong>: Pod traffic to external destinations gets SNATed to node IP</li>
  <li><strong>External SNAT (true)</strong>: Pod retains its VPC IP when talking to external destinations</li>
  <li><strong>Critical for</strong>: Cross-VPC communication, VPC peering, Transit Gateway scenarios</li>
  <li><strong>Why it matters</strong>: Allows destination to see actual pod IP instead of node IP for logging, security groups, etc.</li>
</ul>

<hr />

<h3 id="33-dns-and-service-discovery-coredns-operational-reality">3.3 DNS and Service Discovery (CoreDNS Operational Reality)</h3>

<p>DNS failures in Kubernetes don’t just break service discovery—they cascade into timeouts, retries, and connection pool exhaustion that can take down entire applications.</p>

<h4 id="331-coredns-under-load-when-dns-becomes-the-bottleneck">3.3.1 CoreDNS Under Load (When DNS Becomes the Bottleneck)</h4>

<p><strong>Common failure pattern:</strong></p>
<ol>
  <li>Application makes many DNS queries (poor caching)</li>
  <li>CoreDNS pods hit CPU/memory limits</li>
  <li>DNS queries start timing out</li>
  <li>Applications retry aggressively</li>
  <li>DNS load increases, making timeouts worse</li>
  <li>Cascade failure across services</li>
</ol>

<p><strong>Symptoms:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># DNS timeouts in application logs</span>
kubectl logs &lt;app-pod&gt; | <span class="nb">grep</span> <span class="nt">-i</span> <span class="s2">"dns</span><span class="se">\|</span><span class="s2">resolve</span><span class="se">\|</span><span class="s2">timeout"</span>

<span class="c"># CoreDNS resource pressure</span>
kubectl <span class="nt">-n</span> kube-system top pods <span class="nt">-l</span> k8s-app<span class="o">=</span>kube-dns

<span class="c"># DNS query patterns</span>
kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> k8s-app<span class="o">=</span>kube-dns | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s2">"NXDOMAIN|timeout|error"</span>
</code></pre></div></div>

<h4 id="332-dns-query-patterns-that-kill-performance">3.3.2 DNS Query Patterns That Kill Performance</h4>

<p><strong>Bad patterns:</strong></p>
<ul>
  <li>No DNS caching in applications</li>
  <li>Querying external domains from every pod</li>
  <li>Short TTL on frequently accessed services</li>
  <li>DNS queries in tight loops</li>
</ul>

<p><strong>Example of problematic application behavior:</strong></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># BAD: DNS lookup on every request
</span><span class="k">def</span> <span class="nf">make_request</span><span class="p">():</span>
    <span class="n">host</span> <span class="o">=</span> <span class="n">socket</span><span class="p">.</span><span class="n">gethostbyname</span><span class="p">(</span><span class="s">"api.external.com"</span><span class="p">)</span>  <span class="c1"># DNS lookup every time
</span>    <span class="k">return</span> <span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="sa">f</span><span class="s">"http://</span><span class="si">{</span><span class="n">host</span><span class="si">}</span><span class="s">/api"</span><span class="p">)</span>

<span class="c1"># GOOD: Cache DNS resolution
</span><span class="n">dns_cache</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">def</span> <span class="nf">make_request</span><span class="p">():</span>
    <span class="k">if</span> <span class="s">"api.external.com"</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">dns_cache</span><span class="p">:</span>
        <span class="n">dns_cache</span><span class="p">[</span><span class="s">"api.external.com"</span><span class="p">]</span> <span class="o">=</span> <span class="n">socket</span><span class="p">.</span><span class="n">gethostbyname</span><span class="p">(</span><span class="s">"api.external.com"</span><span class="p">)</span>
    <span class="n">host</span> <span class="o">=</span> <span class="n">dns_cache</span><span class="p">[</span><span class="s">"api.external.com"</span><span class="p">]</span>
    <span class="k">return</span> <span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="sa">f</span><span class="s">"http://</span><span class="si">{</span><span class="n">host</span><span class="si">}</span><span class="s">/api"</span><span class="p">)</span>
</code></pre></div></div>

<h4 id="333-coredns-scaling-and-distribution">3.3.3 CoreDNS Scaling and Distribution</h4>

<p><strong>Horizontal scaling:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nt">-n</span> kube-system scale deployment coredns <span class="nt">--replicas</span><span class="o">=</span>5
</code></pre></div></div>

<p><strong>Anti-affinity to spread CoreDNS pods:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">coredns</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">kube-system</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">affinity</span><span class="pi">:</span>
        <span class="na">podAntiAffinity</span><span class="pi">:</span>
          <span class="na">preferredDuringSchedulingIgnoredDuringExecution</span><span class="pi">:</span>
          <span class="pi">-</span> <span class="na">weight</span><span class="pi">:</span> <span class="m">100</span>
            <span class="na">podAffinityTerm</span><span class="pi">:</span>
              <span class="na">labelSelector</span><span class="pi">:</span>
                <span class="na">matchExpressions</span><span class="pi">:</span>
                <span class="pi">-</span> <span class="na">key</span><span class="pi">:</span> <span class="s">k8s-app</span>
                  <span class="na">operator</span><span class="pi">:</span> <span class="s">In</span>
                  <span class="na">values</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">kube-dns"</span><span class="pi">]</span>
              <span class="na">topologyKey</span><span class="pi">:</span> <span class="s">kubernetes.io/hostname</span>
</code></pre></div></div>

<h4 id="334-nodelocal-dnscache-advanced-dns-optimization">3.3.4 NodeLocal DNSCache (Advanced DNS Optimization)</h4>

<p>For clusters with heavy DNS load, NodeLocal DNSCache runs a DNS cache on each node:</p>

<p><strong>Benefits:</strong></p>
<ul>
  <li>Reduces load on CoreDNS</li>
  <li>Improves DNS response times</li>
  <li>Reduces DNS-related network traffic</li>
</ul>

<p><strong>Trade-offs:</strong></p>
<ul>
  <li>Additional complexity</li>
  <li>More moving parts to debug</li>
  <li>Cache invalidation edge cases</li>
</ul>

<p><strong>When to consider:</strong></p>
<ul>
  <li>High DNS query volume (&gt;1000 QPS cluster-wide)</li>
  <li>DNS-related performance issues</li>
  <li>Applications that can’t implement proper DNS caching</li>
</ul>

<h4 id="335-dns-search-domain-optimization-ndots-configuration">3.3.5 DNS Search Domain Optimization (ndots Configuration)</h4>

<p><strong>The ndots problem:</strong> By default, Kubernetes sets <code class="language-plaintext highlighter-rouge">ndots:5</code> in <code class="language-plaintext highlighter-rouge">/etc/resolv.conf</code>, causing excessive DNS queries for external domains.</p>

<p><strong>Default behavior analysis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Inside a pod, resolving "google.com" triggers these queries:</span>
<span class="c"># 1. google.com.default.svc.cluster.local</span>
<span class="c"># 2. google.com.svc.cluster.local  </span>
<span class="c"># 3. google.com.cluster.local</span>
<span class="c"># 4. google.com.us-west-2.compute.internal</span>
<span class="c"># 5. google.com.compute.internal</span>
<span class="c"># 6. google.com (finally!)</span>
</code></pre></div></div>

<p><strong>Impact on AWS linklocal limits:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Each failed query hits 169.254.169.254 (AWS DNS resolver)</span>
<span class="c"># With ndots:5, external domains generate 6x DNS traffic</span>
<span class="c"># AWS limit: 1024 PPS per instance - easily exceeded in dense clusters</span>
</code></pre></div></div>

<p><strong>Optimized ndots configuration:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Pod</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">optimized-dns-app</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">dnsPolicy</span><span class="pi">:</span> <span class="s">ClusterFirst</span>
  <span class="na">dnsConfig</span><span class="pi">:</span>
    <span class="na">options</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">ndots</span>
      <span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">1"</span>  <span class="c1"># Reduce from default 5 to 1</span>
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">edns0</span>  <span class="c1"># Enable DNS extensions</span>
  <span class="na">containers</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">app</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">nginx</span>
</code></pre></div></div>

<p><strong>Application-specific DNS optimization:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># For apps that primarily call external services</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">external-api-client</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">dnsConfig</span><span class="pi">:</span>
        <span class="na">options</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">ndots</span>
          <span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">1"</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">app</span>
        <span class="na">env</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">EXTERNAL_API_URL</span>
          <span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">https://api.example.com."</span>  <span class="c1"># Trailing dot = absolute FQDN</span>
</code></pre></div></div>

<h3 id="336-listen-backlog-and-connection-handling">3.3.6 Listen backlog and connection handling</h3>

<p>These are not DNS topics, but connection-level tuning is commonly needed alongside DNS optimization when debugging service latency.</p>

<p><strong>Listen Backlog Configuration for High-Traffic Services</strong></p>

<p><strong>Problem:</strong> Default listen backlog (128) causes connection drops under bursty load.</p>

<p><strong>Root cause:</strong> When services receive more concurrent connection attempts than the listen backlog can queue, connections are dropped at the kernel level.</p>

<p><strong>Solution - Configure via sysctls:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">high-traffic-service</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">securityContext</span><span class="pi">:</span>
        <span class="na">sysctls</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">net.core.somaxconn</span>
          <span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">32000"</span>  <span class="c1"># Increase from default 128</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">net.ipv4.ip_local_port_range</span>
          <span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">1024</span><span class="nv"> </span><span class="s">64000"</span>  <span class="c1"># Expand ephemeral port range</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">app</span>
        <span class="c1"># Application configuration</span>
</code></pre></div></div>

<p><strong>Monitor listen backlog with sidecar pattern:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">monitored-service</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">app</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">my-app:latest</span>
        <span class="c1"># Main application container</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">node-exporter</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">prom/node-exporter:latest</span>
        <span class="na">args</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="s">--web.listen-address=0.0.0.0:9100</span>
        <span class="pi">-</span> <span class="s">--collector.disable-defaults</span>
        <span class="pi">-</span> <span class="s">--web.disable-exporter-metrics</span>
        <span class="pi">-</span> <span class="s">--collector.conntrack</span>
        <span class="pi">-</span> <span class="s">--collector.filefd</span>
        <span class="pi">-</span> <span class="s">--collector.netstat</span>
        <span class="pi">-</span> <span class="s">--collector.sockstat</span>
        <span class="na">ports</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">9100</span>
          <span class="na">name</span><span class="pi">:</span> <span class="s">metrics</span>
        <span class="na">securityContext</span><span class="pi">:</span>
          <span class="na">readOnlyRootFilesystem</span><span class="pi">:</span> <span class="no">true</span>
          <span class="na">runAsNonRoot</span><span class="pi">:</span> <span class="no">true</span>
          <span class="na">allowPrivilegeEscalation</span><span class="pi">:</span> <span class="no">false</span>
          <span class="na">capabilities</span><span class="pi">:</span>
            <span class="na">drop</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">all"</span><span class="pi">]</span>
</code></pre></div></div>

<p><strong>Prometheus alerts for listen backlog issues:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">groups</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">listen-backlog</span>
  <span class="na">rules</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">ListenDrops</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">sum by (k8s_cluster_name, pod) (rate(node_netstat_TcpExt_ListenDrops[5m]) &gt; 0) &gt; </span><span class="m">5</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">2m</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Listen</span><span class="nv"> </span><span class="s">drops</span><span class="nv"> </span><span class="s">detected</span><span class="nv"> </span><span class="s">on</span><span class="nv"> </span><span class="s">"</span>
      <span class="na">description</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Pod</span><span class="nv">  </span><span class="s">is</span><span class="nv"> </span><span class="s">dropping</span><span class="nv"> </span><span class="s">connections</span><span class="nv"> </span><span class="s">due</span><span class="nv"> </span><span class="s">to</span><span class="nv"> </span><span class="s">listen</span><span class="nv"> </span><span class="s">backlog</span><span class="nv"> </span><span class="s">overflow"</span>

  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">ListenOverflows</span>  
    <span class="na">expr</span><span class="pi">:</span> <span class="s">sum by (k8s_cluster_name, pod) (rate(node_netstat_TcpExt_ListenOverflows[5m]) &gt; 0) &gt; </span><span class="m">5</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">2m</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Listen</span><span class="nv"> </span><span class="s">overflows</span><span class="nv"> </span><span class="s">detected</span><span class="nv"> </span><span class="s">on</span><span class="nv"> </span><span class="s">"</span>
      <span class="na">description</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Pod</span><span class="nv">  </span><span class="s">has</span><span class="nv"> </span><span class="s">listen</span><span class="nv"> </span><span class="s">queue</span><span class="nv"> </span><span class="s">overflows</span><span class="nv"> </span><span class="s">-</span><span class="nv"> </span><span class="s">increase</span><span class="nv"> </span><span class="s">somaxconn"</span>
</code></pre></div></div>

<p><strong>Protection Against Slow Clients</strong></p>

<p><strong>Problem:</strong> Slow clients can exhaust thread/process pools in request-per-thread models.</p>

<p><strong>Attack vector simulation:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Simulate slow client sending 10KB slowly (1 byte per second)</span>
<span class="o">(</span><span class="nb">echo</span> <span class="nt">-e</span> <span class="nt">-n</span> <span class="s1">'POST /api HTTP/1.1\r\nHost: example.com\r\nContent-Length: 10000\r\n\r\n'</span><span class="p">;</span> 
 <span class="nv">i</span><span class="o">=</span>0<span class="p">;</span> <span class="k">while</span> <span class="o">[</span> <span class="nv">$i</span> <span class="nt">-lt</span> 10000 <span class="o">]</span><span class="p">;</span> <span class="k">do </span><span class="nb">echo</span> <span class="nt">-n</span> <span class="s2">"a"</span><span class="p">;</span> <span class="nb">sleep </span>1<span class="p">;</span> <span class="nv">i</span><span class="o">=</span><span class="k">$((</span>i+1<span class="k">))</span><span class="p">;</span> <span class="k">done</span><span class="o">)</span> <span class="se">\</span>
 | socat <span class="nt">-t</span> 10 - TCP4:service.example.com:80
</code></pre></div></div>

<p><strong>Solution - Reverse proxy with buffering:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ConfigMap</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">nginx-config</span>
<span class="na">data</span><span class="pi">:</span>
  <span class="na">nginx.conf</span><span class="pi">:</span> <span class="pi">|</span>
    <span class="s">events {</span>
        <span class="s">worker_connections 1024;</span>
    <span class="s">}</span>
    <span class="s">http {</span>
        <span class="s"># Buffer entire request before forwarding to backend</span>
        <span class="s">proxy_request_buffering on;</span>
        <span class="s">proxy_buffering on;</span>
    <span class="no">    </span>
        <span class="s"># Timeout configurations</span>
        <span class="s">client_header_timeout 10s;    # Max time to receive headers</span>
        <span class="s">client_body_timeout 30s;      # Max time to receive body</span>
        <span class="s">send_timeout 30s;             # Max time to send response</span>
        <span class="s">keepalive_timeout 65s;        # Connection idle timeout</span>
    <span class="no">    </span>
        <span class="s">upstream backend {</span>
            <span class="s">server app-service:8080;</span>
        <span class="s">}</span>
    <span class="no">    </span>
        <span class="s">server {</span>
            <span class="s">listen 80;</span>
            <span class="s">location / {</span>
                <span class="s">proxy_pass http://backend;</span>
                <span class="s">proxy_set_header Host $host;</span>
                <span class="s">proxy_set_header X-Real-IP $remote_addr;</span>
            <span class="s">}</span>
        <span class="s">}</span>
    <span class="s">}</span>
<span class="s">---</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">nginx-proxy</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">nginx</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">nginx:alpine</span>
        <span class="na">volumeMounts</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">config</span>
          <span class="na">mountPath</span><span class="pi">:</span> <span class="s">/etc/nginx/nginx.conf</span>
          <span class="na">subPath</span><span class="pi">:</span> <span class="s">nginx.conf</span>
      <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">config</span>
        <span class="na">configMap</span><span class="pi">:</span>
          <span class="na">name</span><span class="pi">:</span> <span class="s">nginx-config</span>
</code></pre></div></div>

<p><strong>Envoy configuration for slow client protection:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ConfigMap</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">envoy-config</span>
<span class="na">data</span><span class="pi">:</span>
  <span class="na">envoy.yaml</span><span class="pi">:</span> <span class="pi">|</span>
    <span class="s">static_resources:</span>
      <span class="s">listeners:</span>
      <span class="s">- name: listener_0</span>
        <span class="s">address:</span>
          <span class="s">socket_address:</span>
            <span class="s">address: 0.0.0.0</span>
            <span class="s">port_value: 8080</span>
        <span class="s">filter_chains:</span>
        <span class="s">- filters:</span>
          <span class="s">- name: envoy.filters.network.http_connection_manager</span>
            <span class="s">typed_config:</span>
              <span class="s">"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager</span>
              <span class="s">request_timeout: 30s</span>
              <span class="s">stream_idle_timeout: 300s</span>
              <span class="s">request_headers_timeout: 10s</span>
              <span class="s">http_filters:</span>
              <span class="s">- name: envoy.filters.http.buffer</span>
                <span class="s">typed_config:</span>
                  <span class="s">"@type": type.googleapis.com/envoy.extensions.filters.http.buffer.v3.Buffer</span>
                  <span class="s">max_request_bytes: 1048576  # 1MB buffer</span>
              <span class="s">- name: envoy.filters.http.router</span>
              <span class="s">route_config:</span>
                <span class="s">name: local_route</span>
                <span class="s">virtual_hosts:</span>
                <span class="s">- name: backend</span>
                  <span class="s">domains: ["*"]</span>
                  <span class="s">routes:</span>
                  <span class="s">- match:</span>
                      <span class="s">prefix: "/"</span>
                    <span class="s">route:</span>
                      <span class="s">cluster: backend_cluster</span>
      <span class="s">clusters:</span>
      <span class="s">- name: backend_cluster</span>
        <span class="s">connect_timeout: 5s</span>
        <span class="s">type: STRICT_DNS</span>
        <span class="s">lb_policy: ROUND_ROBIN</span>
        <span class="s">load_assignment:</span>
          <span class="s">cluster_name: backend_cluster</span>
          <span class="s">endpoints:</span>
          <span class="s">- lb_endpoints:</span>
            <span class="s">- endpoint:</span>
                <span class="s">address:</span>
                  <span class="s">socket_address:</span>
                    <span class="s">address: app-service</span>
                    <span class="s">port_value: 8080</span>
</code></pre></div></div>

<hr />

<h3 id="34-service-mesh-networking-when-l7-proxy-becomes-critical-path">3.4 Service Mesh Networking (When L7 Proxy Becomes Critical Path)</h3>

<p>Service meshes add another networking layer that can fail in EKS-specific ways.</p>

<h4 id="341-envoy-sidecar-resource-limits">3.4.1 Envoy Sidecar Resource Limits</h4>

<p><strong>Common failure:</strong>
Envoy sidecar hits CPU/memory limits under load, causing:</p>
<ul>
  <li>Request timeouts</li>
  <li>Connection pool exhaustion</li>
  <li>Circuit breaker activation</li>
</ul>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check sidecar resource usage</span>
kubectl top pods <span class="nt">--containers</span> | <span class="nb">grep </span>envoy

<span class="c"># Check Envoy admin interface</span>
kubectl <span class="nb">exec</span> &lt;pod&gt; <span class="nt">-c</span> istio-proxy <span class="nt">--</span> curl localhost:15000/stats | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s2">"cx_|rq_|upstream"</span>
</code></pre></div></div>

<p><strong>Tuning:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">metadata</span><span class="pi">:</span>
  <span class="na">annotations</span><span class="pi">:</span>
    <span class="na">sidecar.istio.io/proxyCPU</span><span class="pi">:</span> <span class="s2">"</span><span class="s">100m"</span>
    <span class="na">sidecar.istio.io/proxyMemory</span><span class="pi">:</span> <span class="s2">"</span><span class="s">128Mi"</span>
    <span class="na">sidecar.istio.io/proxyCPULimit</span><span class="pi">:</span> <span class="s2">"</span><span class="s">200m"</span>
    <span class="na">sidecar.istio.io/proxyMemoryLimit</span><span class="pi">:</span> <span class="s2">"</span><span class="s">256Mi"</span>
</code></pre></div></div>

<h4 id="342-mtls-certificate-rotation-issues">3.4.2 mTLS Certificate Rotation Issues</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Intermittent 503 errors between services</li>
  <li>TLS handshake failures</li>
  <li>Services work sometimes, fail other times</li>
</ul>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check certificate expiration</span>
kubectl <span class="nb">exec</span> &lt;pod&gt; <span class="nt">-c</span> istio-proxy <span class="nt">--</span> openssl s_client <span class="nt">-connect</span> &lt;service&gt;:443 <span class="nt">-servername</span> &lt;service&gt; &lt; /dev/null 2&gt;/dev/null | openssl x509 <span class="nt">-noout</span> <span class="nt">-dates</span>

<span class="c"># Check Envoy TLS stats</span>
kubectl <span class="nb">exec</span> &lt;pod&gt; <span class="nt">-c</span> istio-proxy <span class="nt">--</span> curl localhost:15000/stats | <span class="nb">grep </span>ssl
</code></pre></div></div>

<hr />

<h3 id="35-load-balancer-integration-albnlb-operational-patterns">3.5 Load Balancer Integration (ALB/NLB Operational Patterns)</h3>

<h4 id="351-alb-target-group-health-issues">3.5.1 ALB Target Group Health Issues</h4>

<p><strong>Common failure pattern:</strong></p>
<ol>
  <li>Pod starts and becomes “Ready”</li>
  <li>ALB target group shows “unhealthy”</li>
  <li>Traffic doesn’t reach the pod</li>
  <li>Application appears to be “not working”</li>
</ol>

<p><strong>Root causes:</strong></p>
<ul>
  <li>Health check path misconfigured</li>
  <li>Security group rules blocking ALB health checks</li>
  <li>Pod readiness probe vs ALB health check mismatch</li>
</ul>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check ALB target group health</span>
aws elbv2 describe-target-health <span class="nt">--target-group-arn</span> &lt;arn&gt;

<span class="c"># Check ingress configuration</span>
kubectl describe ingress &lt;ingress-name&gt;

<span class="c"># Check ALB controller logs</span>
kubectl <span class="nt">-n</span> kube-system logs deployment/aws-load-balancer-controller
</code></pre></div></div>

<p><strong>Fix patterns:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">networking.k8s.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Ingress</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">annotations</span><span class="pi">:</span>
    <span class="na">alb.ingress.kubernetes.io/healthcheck-path</span><span class="pi">:</span> <span class="s">/health</span>
    <span class="na">alb.ingress.kubernetes.io/healthcheck-interval-seconds</span><span class="pi">:</span> <span class="s1">'</span><span class="s">30'</span>
    <span class="na">alb.ingress.kubernetes.io/healthcheck-timeout-seconds</span><span class="pi">:</span> <span class="s1">'</span><span class="s">5'</span>
    <span class="na">alb.ingress.kubernetes.io/healthy-threshold-count</span><span class="pi">:</span> <span class="s1">'</span><span class="s">2'</span>
    <span class="na">alb.ingress.kubernetes.io/unhealthy-threshold-count</span><span class="pi">:</span> <span class="s1">'</span><span class="s">3'</span>
</code></pre></div></div>

<h4 id="352-nlb-connection-tracking-and-keep-alive">3.5.2 NLB Connection Tracking and Keep-Alive</h4>

<p>The NLB idle timeout problem and TCP keepalive fix are covered in detail in <a href="#251-nlb-idle-timeout-keep-alive-silent-connection-kill">Section 2.5.1</a>. This section adds the language-specific code examples.</p>

<p><strong>For HTTP clients (Python):</strong></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">requests</span>
<span class="kn">from</span> <span class="nn">requests.adapters</span> <span class="kn">import</span> <span class="n">HTTPAdapter</span>

<span class="n">session</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">Session</span><span class="p">()</span>
<span class="n">adapter</span> <span class="o">=</span> <span class="n">HTTPAdapter</span><span class="p">(</span>
    <span class="n">socket_options</span><span class="o">=</span><span class="p">[</span>
        <span class="p">(</span><span class="n">socket</span><span class="p">.</span><span class="n">SOL_SOCKET</span><span class="p">,</span> <span class="n">socket</span><span class="p">.</span><span class="n">SO_KEEPALIVE</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>
        <span class="p">(</span><span class="n">socket</span><span class="p">.</span><span class="n">IPPROTO_TCP</span><span class="p">,</span> <span class="n">socket</span><span class="p">.</span><span class="n">TCP_KEEPIDLE</span><span class="p">,</span> <span class="mi">300</span><span class="p">),</span>
        <span class="p">(</span><span class="n">socket</span><span class="p">.</span><span class="n">IPPROTO_TCP</span><span class="p">,</span> <span class="n">socket</span><span class="p">.</span><span class="n">TCP_KEEPINTVL</span><span class="p">,</span> <span class="mi">5</span><span class="p">),</span>
        <span class="p">(</span><span class="n">socket</span><span class="p">.</span><span class="n">IPPROTO_TCP</span><span class="p">,</span> <span class="n">socket</span><span class="p">.</span><span class="n">TCP_KEEPCNT</span><span class="p">,</span> <span class="mi">5</span><span class="p">),</span>
    <span class="p">]</span>
<span class="p">)</span>
<span class="n">session</span><span class="p">.</span><span class="n">mount</span><span class="p">(</span><span class="s">"http://"</span><span class="p">,</span> <span class="n">adapter</span><span class="p">)</span>
<span class="n">session</span><span class="p">.</span><span class="n">mount</span><span class="p">(</span><span class="s">"https://"</span><span class="p">,</span> <span class="n">adapter</span><span class="p">)</span>
</code></pre></div></div>

<p><strong>For gRPC (Python):</strong></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">grpc</span>

<span class="n">options</span> <span class="o">=</span> <span class="p">[</span>
    <span class="p">(</span><span class="s">'grpc.keepalive_time_ms'</span><span class="p">,</span> <span class="mi">300000</span><span class="p">),</span>
    <span class="p">(</span><span class="s">'grpc.keepalive_timeout_ms'</span><span class="p">,</span> <span class="mi">5000</span><span class="p">),</span>
    <span class="p">(</span><span class="s">'grpc.keepalive_permit_without_calls'</span><span class="p">,</span> <span class="bp">True</span><span class="p">),</span>
    <span class="p">(</span><span class="s">'grpc.http2.max_pings_without_data'</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
<span class="p">]</span>

<span class="n">channel</span> <span class="o">=</span> <span class="n">grpc</span><span class="p">.</span><span class="n">insecure_channel</span><span class="p">(</span><span class="s">'service:50051'</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="n">options</span><span class="p">)</span>
</code></pre></div></div>

<hr />

<h3 id="36-network-policies-micro-segmentation-that-actually-works">3.6 Network Policies (Micro-segmentation That Actually Works)</h3>

<p>Network policies in EKS require a CNI that supports them (like Calico). When they’re misconfigured, they create “works sometimes” failures that are hard to debug.</p>

<h4 id="361-common-network-policy-mistakes">3.6.1 Common Network Policy Mistakes</h4>

<p><strong>Mistake 1: Blocking DNS</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># BAD: This blocks DNS resolution</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">networking.k8s.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">NetworkPolicy</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">deny-all</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">podSelector</span><span class="pi">:</span> <span class="pi">{}</span>
  <span class="na">policyTypes</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s">Ingress</span>
  <span class="pi">-</span> <span class="s">Egress</span>
  <span class="c1"># No egress rules = no DNS</span>
</code></pre></div></div>

<p><strong>Fix: Always allow DNS</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">networking.k8s.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">NetworkPolicy</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">allow-dns</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">podSelector</span><span class="pi">:</span> <span class="pi">{}</span>
  <span class="na">policyTypes</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s">Egress</span>
  <span class="na">egress</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">to</span><span class="pi">:</span> <span class="pi">[]</span>
    <span class="na">ports</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">protocol</span><span class="pi">:</span> <span class="s">UDP</span>
      <span class="na">port</span><span class="pi">:</span> <span class="m">53</span>
    <span class="pi">-</span> <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
      <span class="na">port</span><span class="pi">:</span> <span class="m">53</span>
</code></pre></div></div>

<p><strong>Mistake 2: Forgetting about health checks</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Need to allow kubelet health checks</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">networking.k8s.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">NetworkPolicy</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">allow-health-checks</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">podSelector</span><span class="pi">:</span> <span class="pi">{}</span>
  <span class="na">policyTypes</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s">Ingress</span>
  <span class="na">ingress</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">from</span><span class="pi">:</span> <span class="pi">[]</span>
    <span class="na">ports</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
      <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>  <span class="c1"># Your health check port</span>
</code></pre></div></div>

<h4 id="362-debugging-network-policy-issues">3.6.2 Debugging Network Policy Issues</h4>

<p><strong>Test connectivity between pods:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># From source pod to target pod</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;source-pod&gt; <span class="nt">--</span> nc <span class="nt">-zv</span> &lt;target-pod-ip&gt; &lt;port&gt;

<span class="c"># Test DNS resolution</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod&gt; <span class="nt">--</span> nslookup kubernetes.default.svc.cluster.local

<span class="c"># Check if network policies are applied</span>
kubectl get networkpolicy <span class="nt">-A</span>
kubectl describe networkpolicy &lt;policy-name&gt;
</code></pre></div></div>

<p><strong>Calico-specific debugging:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check Calico policy status</span>
kubectl <span class="nb">exec</span> <span class="nt">-n</span> kube-system &lt;calico-node-pod&gt; <span class="nt">--</span> calicoctl get policy <span class="nt">-o</span> wide

<span class="c"># Check Calico logs</span>
kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> k8s-app<span class="o">=</span>calico-node
</code></pre></div></div>

<hr />

<h3 id="37-cross-az-networking-latency-and-cost-optimization">3.7 Cross-AZ Networking (Latency and Cost Optimization)</h3>

<h4 id="371-understanding-cross-az-traffic-patterns">3.7.1 Understanding Cross-AZ Traffic Patterns</h4>

<p><strong>Network latency between AZs:</strong></p>
<ul>
  <li>Intra-AZ: ~0.1-0.5ms</li>
  <li>Inter-AZ: ~1-2ms</li>
  <li>Cross-region: 20-100ms+</li>
</ul>

<p><strong>Cost implications:</strong></p>
<ul>
  <li>Intra-AZ traffic: Free</li>
  <li>Inter-AZ traffic: $0.01/GB (as of 2024)</li>
  <li>Cross-region: $0.02/GB+</li>
</ul>

<h4 id="372-topology-spread-constraints-for-network-optimization">3.7.2 Topology Spread Constraints for Network Optimization</h4>

<p><strong>Spread pods across AZs for availability:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">web-app</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">6</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">topologySpreadConstraints</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">maxSkew</span><span class="pi">:</span> <span class="m">1</span>
        <span class="na">topologyKey</span><span class="pi">:</span> <span class="s">topology.kubernetes.io/zone</span>
        <span class="na">whenUnsatisfiable</span><span class="pi">:</span> <span class="s">DoNotSchedule</span>
        <span class="na">labelSelector</span><span class="pi">:</span>
          <span class="na">matchLabels</span><span class="pi">:</span>
            <span class="na">app</span><span class="pi">:</span> <span class="s">web-app</span>
</code></pre></div></div>

<p><strong>Keep related services in same AZ:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">cache-service</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">affinity</span><span class="pi">:</span>
        <span class="na">podAffinity</span><span class="pi">:</span>
          <span class="na">preferredDuringSchedulingIgnoredDuringExecution</span><span class="pi">:</span>
          <span class="pi">-</span> <span class="na">weight</span><span class="pi">:</span> <span class="m">100</span>
            <span class="na">podAffinityTerm</span><span class="pi">:</span>
              <span class="na">labelSelector</span><span class="pi">:</span>
                <span class="na">matchExpressions</span><span class="pi">:</span>
                <span class="pi">-</span> <span class="na">key</span><span class="pi">:</span> <span class="s">app</span>
                  <span class="na">operator</span><span class="pi">:</span> <span class="s">In</span>
                  <span class="na">values</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">web-app"</span><span class="pi">]</span>
              <span class="na">topologyKey</span><span class="pi">:</span> <span class="s">topology.kubernetes.io/zone</span>
</code></pre></div></div>

<hr />

<h3 id="38-debugging-network-issues-systematic-approach">3.8 Debugging Network Issues (Systematic Approach)</h3>

<h4 id="381-layer-by-layer-debugging">3.8.1 Layer-by-Layer Debugging</h4>

<p><strong>Step 1: Pod-to-Pod IP connectivity</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Get pod IPs</span>
kubectl get pods <span class="nt">-o</span> wide

<span class="c"># Test basic IP connectivity</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;source-pod&gt; <span class="nt">--</span> ping &lt;target-pod-ip&gt;

<span class="c"># Test specific port</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;source-pod&gt; <span class="nt">--</span> nc <span class="nt">-zv</span> &lt;target-pod-ip&gt; &lt;port&gt;
</code></pre></div></div>

<p><strong>Step 2: Service discovery</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Test DNS resolution</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod&gt; <span class="nt">--</span> nslookup &lt;service-name&gt;.&lt;namespace&gt;.svc.cluster.local

<span class="c"># Test service connectivity</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod&gt; <span class="nt">--</span> curl &lt;service-name&gt;.&lt;namespace&gt;.svc.cluster.local:&lt;port&gt;
</code></pre></div></div>

<p><strong>Step 3: Ingress/Load balancer</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check ingress status</span>
kubectl get ingress
kubectl describe ingress &lt;ingress-name&gt;

<span class="c"># Test from outside cluster</span>
curl <span class="nt">-v</span> http://&lt;load-balancer-dns&gt;/health
</code></pre></div></div>

<h4 id="382-network-debugging-tools">3.8.2 Network Debugging Tools</h4>

<p><strong>Essential tools to have in debug pods:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Pod</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">network-debug</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">containers</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">debug</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">nicolaka/netshoot</span>
    <span class="na">command</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">/bin/bash"</span><span class="pi">]</span>
    <span class="na">args</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">-c"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">while</span><span class="nv"> </span><span class="s">true;</span><span class="nv"> </span><span class="s">do</span><span class="nv"> </span><span class="s">sleep</span><span class="nv"> </span><span class="s">30;</span><span class="nv"> </span><span class="s">done;"</span><span class="pi">]</span>
    <span class="na">securityContext</span><span class="pi">:</span>
      <span class="na">capabilities</span><span class="pi">:</span>
        <span class="na">add</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">NET_ADMIN"</span><span class="pi">]</span>
</code></pre></div></div>

<p><strong>Useful commands in debug pod:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Network interface info</span>
ip addr show
ip route show

<span class="c"># DNS debugging</span>
dig @8.8.8.8 google.com
nslookup kubernetes.default.svc.cluster.local

<span class="c"># Port scanning</span>
nmap <span class="nt">-p</span> 80,443,8080 &lt;target-ip&gt;

<span class="c"># Packet capture</span>
tcpdump <span class="nt">-i</span> any <span class="nt">-w</span> /tmp/capture.pcap host &lt;target-ip&gt;

<span class="c"># Connection testing</span>
nc <span class="nt">-zv</span> &lt;host&gt; &lt;port&gt;
telnet &lt;host&gt; &lt;port&gt;
</code></pre></div></div>

<h4 id="383-performance-testing-and-monitoring">3.8.3 Performance Testing and Monitoring</h4>

<p><strong>Network performance testing:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Bandwidth testing between pods</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod1&gt; <span class="nt">--</span> iperf3 <span class="nt">-s</span> &amp;
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod2&gt; <span class="nt">--</span> iperf3 <span class="nt">-c</span> &lt;pod1-ip&gt;

<span class="c"># Latency testing</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod1&gt; <span class="nt">--</span> ping <span class="nt">-c</span> 100 &lt;pod2-ip&gt;
</code></pre></div></div>

<p><strong>Key metrics to monitor:</strong></p>
<ul>
  <li>DNS query latency and error rate</li>
  <li>Service-to-service latency (P50, P95, P99)</li>
  <li>Network throughput and packet loss</li>
  <li>Connection pool utilization</li>
  <li>Cross-AZ traffic volume and cost</li>
</ul>

<hr />

<h3 id="39-network-security-defense-in-depth">3.9 Network Security (Defense in Depth)</h3>

<h4 id="391-security-groups-vs-network-policies">3.9.1 Security Groups vs Network Policies</h4>

<p><strong>Security Groups (AWS level):</strong></p>
<ul>
  <li>Applied at ENI level</li>
  <li>Stateful firewall rules</li>
  <li>Good for node-to-node and external access control</li>
</ul>

<p><strong>Network Policies (Kubernetes level):</strong></p>
<ul>
  <li>Applied at pod level</li>
  <li>More granular control</li>
  <li>Good for micro-segmentation within cluster</li>
</ul>

<p><strong>Best practice: Use both layers</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Network policy for pod-to-pod</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">networking.k8s.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">NetworkPolicy</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">web-app-policy</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">podSelector</span><span class="pi">:</span>
    <span class="na">matchLabels</span><span class="pi">:</span>
      <span class="na">app</span><span class="pi">:</span> <span class="s">web-app</span>
  <span class="na">policyTypes</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s">Ingress</span>
  <span class="pi">-</span> <span class="s">Egress</span>
  <span class="na">ingress</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">from</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">podSelector</span><span class="pi">:</span>
        <span class="na">matchLabels</span><span class="pi">:</span>
          <span class="na">app</span><span class="pi">:</span> <span class="s">frontend</span>
    <span class="na">ports</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
      <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
  <span class="na">egress</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">to</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">podSelector</span><span class="pi">:</span>
        <span class="na">matchLabels</span><span class="pi">:</span>
          <span class="na">app</span><span class="pi">:</span> <span class="s">database</span>
    <span class="na">ports</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
      <span class="na">port</span><span class="pi">:</span> <span class="m">5432</span>
</code></pre></div></div>

<h4 id="392-pod-security-groups-eks-specific">3.9.2 Pod Security Groups (EKS-specific)</h4>

<p>For fine-grained security group control at pod level:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Pod</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">secure-pod</span>
  <span class="na">annotations</span><span class="pi">:</span>
    <span class="na">eks.amazonaws.com/security-groups</span><span class="pi">:</span> <span class="s">sg-12345678,sg-87654321</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">containers</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">app</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">myapp:latest</span>
</code></pre></div></div>

<p><strong>When to use pod security groups:</strong></p>
<ul>
  <li>Need different security rules per workload</li>
  <li>Compliance requirements for network isolation</li>
  <li>Integration with AWS security tools</li>
</ul>

<p><strong>Trade-offs:</strong></p>
<ul>
  <li>Additional complexity</li>
  <li>Potential performance impact</li>
  <li>Limited to specific instance types and CNI versions</li>
</ul>

<hr />

<h3 id="310-network-troubleshooting-runbook">3.10 Network Troubleshooting Runbook</h3>

<h4 id="3101-service-is-unreachable-runbook">3.10.1 “Service is unreachable” Runbook</h4>

<p><strong>Symptoms:</strong> Application can’t reach another service</p>

<p><strong>Step 1: Verify service exists and has endpoints</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get svc &lt;service-name&gt;
kubectl get endpoints &lt;service-name&gt;
</code></pre></div></div>

<p><strong>Step 2: Test DNS resolution</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod&gt; <span class="nt">--</span> nslookup &lt;service-name&gt;.&lt;namespace&gt;.svc.cluster.local
</code></pre></div></div>

<p><strong>Step 3: Test direct IP connectivity</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod&gt; <span class="nt">--</span> nc <span class="nt">-zv</span> &lt;endpoint-ip&gt; &lt;port&gt;
</code></pre></div></div>

<p><strong>Step 4: Check network policies</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get networkpolicy <span class="nt">-n</span> &lt;namespace&gt;
kubectl describe networkpolicy &lt;policy-name&gt;
</code></pre></div></div>

<p><strong>Step 5: Check security groups (if using pod security groups)</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>aws ec2 describe-security-groups <span class="nt">--group-ids</span> &lt;sg-id&gt;
</code></pre></div></div>

<h4 id="3102-dns-is-slowfailing-runbook">3.10.2 “DNS is slow/failing” Runbook</h4>

<p><strong>Symptoms:</strong> DNS timeouts, slow service discovery</p>

<p><strong>Step 1: Check CoreDNS health</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nt">-n</span> kube-system get pods <span class="nt">-l</span> k8s-app<span class="o">=</span>kube-dns
kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> k8s-app<span class="o">=</span>kube-dns <span class="nt">--tail</span><span class="o">=</span>100
</code></pre></div></div>

<p><strong>Step 2: Test DNS from multiple pods</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod1&gt; <span class="nt">--</span> <span class="nb">time </span>nslookup kubernetes.default.svc.cluster.local
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod2&gt; <span class="nt">--</span> <span class="nb">time </span>nslookup kubernetes.default.svc.cluster.local
</code></pre></div></div>

<p><strong>Step 3: Check DNS query patterns</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> k8s-app<span class="o">=</span>kube-dns | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s2">"NXDOMAIN|timeout"</span> | <span class="nb">tail</span> <span class="nt">-20</span>
</code></pre></div></div>

<p><strong>Step 4: Monitor CoreDNS resource usage</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nt">-n</span> kube-system top pods <span class="nt">-l</span> k8s-app<span class="o">=</span>kube-dns
</code></pre></div></div>

<p><strong>Step 5: Scale CoreDNS if needed</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nt">-n</span> kube-system scale deployment coredns <span class="nt">--replicas</span><span class="o">=</span>&lt;new-count&gt;
</code></pre></div></div>

<h4 id="3103-load-balancer-not-working-runbook">3.10.3 “Load balancer not working” Runbook</h4>

<p><strong>Symptoms:</strong> External traffic can’t reach services</p>

<p><strong>Step 1: Check ingress/service status</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get ingress
kubectl describe ingress &lt;ingress-name&gt;
kubectl get svc &lt;service-name&gt;
</code></pre></div></div>

<p><strong>Step 2: Check AWS Load Balancer Controller</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nt">-n</span> kube-system logs deployment/aws-load-balancer-controller
</code></pre></div></div>

<p><strong>Step 3: Verify target group health</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>aws elbv2 describe-target-health <span class="nt">--target-group-arn</span> &lt;arn&gt;
</code></pre></div></div>

<p><strong>Step 4: Test internal connectivity</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;debug-pod&gt; <span class="nt">--</span> curl &lt;service-name&gt;:&lt;port&gt;/health
</code></pre></div></div>

<p><strong>Step 5: Check security group rules</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>aws ec2 describe-security-groups <span class="nt">--group-ids</span> &lt;alb-sg-id&gt;
</code></pre></div></div>

<hr />

<h2 id="4-workload-identity-and-security">4. Workload identity and security</h2>

<p>EKS security failures often look like “the application is broken” when the real issue is auth, authorization, or secrets. Misconfigured IRSA, missing encryption, bad RBAC — these create confusing incidents that send you chasing application bugs that don’t exist.</p>

<hr />

<h3 id="41-iam-roles-for-service-accounts-irsa---the-foundation">4.1 IAM Roles for Service Accounts (IRSA) - The Foundation</h3>

<p>IRSA is how pods get AWS permissions without embedding long-lived credentials. When it breaks, applications fail to access AWS services with cryptic permission errors.</p>

<h4 id="411-how-irsa-works-what-can-break">4.1.1 How IRSA Works (What Can Break)</h4>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Pod with ServiceAccount] 
  ↓ (projected token volume)
[OIDC JWT Token] 
  ↓ (AWS STS AssumeRoleWithWebIdentity)
[Temporary AWS Credentials] 
  ↓ (AWS API calls)
[AWS Services: S3, RDS, etc.]
</code></pre></div></div>

<p><strong>Each step can fail:</strong></p>
<ul>
  <li>ServiceAccount annotation missing/wrong</li>
  <li>OIDC provider not configured</li>
  <li>IAM role trust policy incorrect</li>
  <li>IAM role permissions insufficient</li>
  <li>Token projection/mounting issues</li>
</ul>

<h4 id="412-common-irsa-failure-patterns">4.1.2 Common IRSA Failure Patterns</h4>

<p><strong>A) “Access Denied” but IAM role looks correct</strong></p>

<p><strong>Symptoms:</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>AccessDenied: User: arn:aws:sts::123456789012:assumed-role/eksctl-my-cluster-nodegroup-NodeInstanceRole-XXXXX/i-1234567890abcdef0 is not authorized to perform: s3:GetObject
</code></pre></div></div>

<p><strong>Root cause:</strong> Pod is using node IAM role instead of IRSA role</p>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check if ServiceAccount has IRSA annotation</span>
kubectl describe sa &lt;service-account-name&gt;

<span class="c"># Check if pod is using the ServiceAccount</span>
kubectl describe pod &lt;pod-name&gt; | <span class="nb">grep</span> <span class="s2">"Service Account"</span>

<span class="c"># Check if OIDC provider exists</span>
aws eks describe-cluster <span class="nt">--name</span> &lt;cluster-name&gt; <span class="nt">--query</span> <span class="s2">"cluster.identity.oidc.issuer"</span>
aws iam list-open-id-connect-providers
</code></pre></div></div>

<p><strong>B) Token projection failures</strong></p>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Pod starts but AWS calls fail with authentication errors</li>
  <li>Missing <code class="language-plaintext highlighter-rouge">/var/run/secrets/eks.amazonaws.com/serviceaccount/token</code></li>
</ul>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check if token is mounted</span>
kubectl <span class="nb">exec</span> &lt;pod-name&gt; <span class="nt">--</span> <span class="nb">ls</span> <span class="nt">-la</span> /var/run/secrets/eks.amazonaws.com/serviceaccount/

<span class="c"># Check token content (should be JWT)</span>
kubectl <span class="nb">exec</span> &lt;pod-name&gt; <span class="nt">--</span> <span class="nb">cat</span> /var/run/secrets/eks.amazonaws.com/serviceaccount/token | <span class="nb">cut</span> <span class="nt">-d</span><span class="nb">.</span> <span class="nt">-f2</span> | <span class="nb">base64</span> <span class="nt">-d</span>
</code></pre></div></div>

<h4 id="413-irsa-setup-and-troubleshooting">4.1.3 IRSA Setup and Troubleshooting</h4>

<p><strong>Correct IRSA setup:</strong></p>

<ol>
  <li><strong>Create OIDC provider (one-time per cluster):</strong></li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>eksctl utils associate-iam-oidc-provider <span class="nt">--cluster</span> &lt;cluster-name&gt; <span class="nt">--approve</span>
</code></pre></div></div>

<ol>
  <li><strong>Create IAM role with trust policy:</strong></li>
</ol>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"Version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2012-10-17"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"Statement"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"Effect"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Allow"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Principal"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"Federated"</span><span class="p">:</span><span class="w"> </span><span class="s2">"arn:aws:iam::ACCOUNT-ID:oidc-provider/oidc.eks.REGION.amazonaws.com/id/OIDC-ID"</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="nl">"Action"</span><span class="p">:</span><span class="w"> </span><span class="s2">"sts:AssumeRoleWithWebIdentity"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"Condition"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
        </span><span class="nl">"StringEquals"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
          </span><span class="nl">"oidc.eks.REGION.amazonaws.com/id/OIDC-ID:sub"</span><span class="p">:</span><span class="w"> </span><span class="s2">"system:serviceaccount:NAMESPACE:SERVICE-ACCOUNT-NAME"</span><span class="p">,</span><span class="w">
          </span><span class="nl">"oidc.eks.REGION.amazonaws.com/id/OIDC-ID:aud"</span><span class="p">:</span><span class="w"> </span><span class="s2">"sts.amazonaws.com"</span><span class="w">
        </span><span class="p">}</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<ol>
  <li><strong>Annotate ServiceAccount:</strong></li>
</ol>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ServiceAccount</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">my-service-account</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">my-namespace</span>
  <span class="na">annotations</span><span class="pi">:</span>
    <span class="na">eks.amazonaws.com/role-arn</span><span class="pi">:</span> <span class="s">arn:aws:iam::ACCOUNT-ID:role/my-irsa-role</span>
</code></pre></div></div>

<ol>
  <li><strong>Use ServiceAccount in pod:</strong></li>
</ol>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Pod</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">my-pod</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">serviceAccountName</span><span class="pi">:</span> <span class="s">my-service-account</span>
  <span class="na">containers</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">my-container</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">my-app:latest</span>
</code></pre></div></div>

<p><strong>Validation script:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="c"># Test IRSA setup</span>
<span class="nv">NAMESPACE</span><span class="o">=</span><span class="s2">"my-namespace"</span>
<span class="nv">SA_NAME</span><span class="o">=</span><span class="s2">"my-service-account"</span>
<span class="nv">POD_NAME</span><span class="o">=</span><span class="s2">"test-pod"</span>

<span class="nb">echo</span> <span class="s2">"1. Checking ServiceAccount annotation..."</span>
kubectl get sa <span class="nv">$SA_NAME</span> <span class="nt">-n</span> <span class="nv">$NAMESPACE</span> <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.metadata.annotations.eks\.amazonaws\.com/role-arn}'</span>

<span class="nb">echo</span> <span class="nt">-e</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">2. Checking pod ServiceAccount..."</span>
kubectl get pod <span class="nv">$POD_NAME</span> <span class="nt">-n</span> <span class="nv">$NAMESPACE</span> <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.spec.serviceAccountName}'</span>

<span class="nb">echo</span> <span class="nt">-e</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">3. Checking token mount..."</span>
kubectl <span class="nb">exec</span> <span class="nv">$POD_NAME</span> <span class="nt">-n</span> <span class="nv">$NAMESPACE</span> <span class="nt">--</span> <span class="nb">ls</span> <span class="nt">-la</span> /var/run/secrets/eks.amazonaws.com/serviceaccount/

<span class="nb">echo</span> <span class="nt">-e</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">4. Testing AWS credentials..."</span>
kubectl <span class="nb">exec</span> <span class="nv">$POD_NAME</span> <span class="nt">-n</span> <span class="nv">$NAMESPACE</span> <span class="nt">--</span> aws sts get-caller-identity
</code></pre></div></div>

<hr />

<h3 id="42-pod-security-standards-replacing-psps">4.2 Pod Security Standards (Replacing PSPs)</h3>

<p>Pod Security Policies (PSPs) are deprecated. Pod Security Standards are the replacement, but they work differently and can create new failure modes.</p>

<h4 id="421-pod-security-standards-levels">4.2.1 Pod Security Standards Levels</h4>

<p><strong>Privileged:</strong> No restrictions (dangerous for production)
<strong>Baseline:</strong> Minimal restrictions, prevents known privilege escalations
<strong>Restricted:</strong> Heavily restricted, follows pod hardening best practices</p>

<h4 id="422-common-pod-security-failures">4.2.2 Common Pod Security Failures</h4>

<p><strong>A) Pods rejected by admission controller</strong></p>

<p><strong>Symptoms:</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Error creating: pods "my-pod" is forbidden: violates PodSecurity "restricted:latest": 
allowPrivilegeEscalation != false, unrestricted capabilities, runAsNonRoot != true
</code></pre></div></div>

<p><strong>Fix patterns:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Pod</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">secure-pod</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">securityContext</span><span class="pi">:</span>
    <span class="na">runAsNonRoot</span><span class="pi">:</span> <span class="no">true</span>
    <span class="na">runAsUser</span><span class="pi">:</span> <span class="m">1000</span>
    <span class="na">fsGroup</span><span class="pi">:</span> <span class="m">2000</span>
  <span class="na">containers</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">app</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">myapp:latest</span>
    <span class="na">securityContext</span><span class="pi">:</span>
      <span class="na">allowPrivilegeEscalation</span><span class="pi">:</span> <span class="no">false</span>
      <span class="na">readOnlyRootFilesystem</span><span class="pi">:</span> <span class="no">true</span>
      <span class="na">runAsNonRoot</span><span class="pi">:</span> <span class="no">true</span>
      <span class="na">runAsUser</span><span class="pi">:</span> <span class="m">1000</span>
      <span class="na">capabilities</span><span class="pi">:</span>
        <span class="na">drop</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="s">ALL</span>
</code></pre></div></div>

<p><strong>B) Applications fail due to security restrictions</strong></p>

<p><strong>Common issues:</strong></p>
<ul>
  <li>App tries to write to read-only filesystem</li>
  <li>App needs specific capabilities</li>
  <li>App runs as root by default</li>
</ul>

<p><strong>Debugging approach:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check pod security context</span>
kubectl describe pod &lt;pod-name&gt; | <span class="nb">grep</span> <span class="nt">-A</span> 20 <span class="s2">"Security Context"</span>

<span class="c"># Check container security context</span>
kubectl get pod &lt;pod-name&gt; <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.spec.containers[*].securityContext}'</span>

<span class="c"># Test file system permissions</span>
kubectl <span class="nb">exec</span> &lt;pod-name&gt; <span class="nt">--</span> <span class="nb">touch</span> /tmp/test-write
kubectl <span class="nb">exec</span> &lt;pod-name&gt; <span class="nt">--</span> <span class="nb">id</span>
</code></pre></div></div>

<h4 id="423-namespace-level-pod-security-configuration">4.2.3 Namespace-Level Pod Security Configuration</h4>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Namespace</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">production</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">pod-security.kubernetes.io/enforce</span><span class="pi">:</span> <span class="s">restricted</span>
    <span class="na">pod-security.kubernetes.io/audit</span><span class="pi">:</span> <span class="s">restricted</span>
    <span class="na">pod-security.kubernetes.io/warn</span><span class="pi">:</span> <span class="s">restricted</span>
</code></pre></div></div>

<p><strong>Gradual rollout strategy:</strong></p>
<ol>
  <li>Start with <code class="language-plaintext highlighter-rouge">warn</code> mode to identify violations</li>
  <li>Add <code class="language-plaintext highlighter-rouge">audit</code> mode to log violations</li>
  <li>Finally enable <code class="language-plaintext highlighter-rouge">enforce</code> mode to block violations</li>
</ol>

<hr />

<h3 id="43-secrets-management-beyond-kubernetes-secrets">4.3 Secrets Management (Beyond Kubernetes Secrets)</h3>

<p>Kubernetes Secrets are base64 encoded, not encrypted at rest by default, and visible to anyone with cluster access. For production workloads, you need better secrets management.</p>

<h4 id="431-aws-secrets-manager-integration">4.3.1 AWS Secrets Manager Integration</h4>

<p><strong>Using AWS Load Balancer Controller with Secrets Manager:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Secret</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">db-credentials</span>
  <span class="na">annotations</span><span class="pi">:</span>
    <span class="na">aws-load-balancer-controller.k8s.aws/secret-manager</span><span class="pi">:</span> <span class="s2">"</span><span class="s">arn:aws:secretsmanager:region:account:secret:prod/db/credentials"</span>
<span class="na">type</span><span class="pi">:</span> <span class="s">Opaque</span>
</code></pre></div></div>

<p><strong>Using External Secrets Operator:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">external-secrets.io/v1beta1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">SecretStore</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">aws-secrets-manager</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">provider</span><span class="pi">:</span>
    <span class="na">aws</span><span class="pi">:</span>
      <span class="na">service</span><span class="pi">:</span> <span class="s">SecretsManager</span>
      <span class="na">region</span><span class="pi">:</span> <span class="s">us-west-2</span>
      <span class="na">auth</span><span class="pi">:</span>
        <span class="na">secretRef</span><span class="pi">:</span>
          <span class="na">accessKeyID</span><span class="pi">:</span>
            <span class="na">name</span><span class="pi">:</span> <span class="s">awssm-secret</span>
            <span class="na">key</span><span class="pi">:</span> <span class="s">access-key</span>
          <span class="na">secretAccessKey</span><span class="pi">:</span>
            <span class="na">name</span><span class="pi">:</span> <span class="s">awssm-secret</span>
            <span class="na">key</span><span class="pi">:</span> <span class="s">secret-access-key</span>
<span class="nn">---</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">external-secrets.io/v1beta1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ExternalSecret</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">app-secret</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">refreshInterval</span><span class="pi">:</span> <span class="s">15s</span>
  <span class="na">secretStoreRef</span><span class="pi">:</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">aws-secrets-manager</span>
    <span class="na">kind</span><span class="pi">:</span> <span class="s">SecretStore</span>
  <span class="na">target</span><span class="pi">:</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">app-secret</span>
    <span class="na">creationPolicy</span><span class="pi">:</span> <span class="s">Owner</span>
  <span class="na">data</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">secretKey</span><span class="pi">:</span> <span class="s">password</span>
    <span class="na">remoteRef</span><span class="pi">:</span>
      <span class="na">key</span><span class="pi">:</span> <span class="s">prod/db/credentials</span>
      <span class="na">property</span><span class="pi">:</span> <span class="s">password</span>
</code></pre></div></div>

<h4 id="432-secrets-csi-driver">4.3.2 Secrets CSI Driver</h4>

<p><strong>Mount secrets as volumes:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Pod</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">app-pod</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">serviceAccountName</span><span class="pi">:</span> <span class="s">app-service-account</span>
  <span class="na">containers</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">app</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">myapp:latest</span>
    <span class="na">volumeMounts</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">secrets-store</span>
      <span class="na">mountPath</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/mnt/secrets"</span>
      <span class="na">readOnly</span><span class="pi">:</span> <span class="no">true</span>
  <span class="na">volumes</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">secrets-store</span>
    <span class="na">csi</span><span class="pi">:</span>
      <span class="na">driver</span><span class="pi">:</span> <span class="s">secrets-store.csi.k8s.io</span>
      <span class="na">readOnly</span><span class="pi">:</span> <span class="no">true</span>
      <span class="na">volumeAttributes</span><span class="pi">:</span>
        <span class="na">secretProviderClass</span><span class="pi">:</span> <span class="s2">"</span><span class="s">app-secrets"</span>
<span class="nn">---</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">secrets-store.csi.x-k8s.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">SecretProviderClass</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">app-secrets</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">provider</span><span class="pi">:</span> <span class="s">aws</span>
  <span class="na">parameters</span><span class="pi">:</span>
    <span class="na">objects</span><span class="pi">:</span> <span class="pi">|</span>
      <span class="s">- objectName: "prod/db/credentials"</span>
        <span class="s">objectType: "secretsmanager"</span>
        <span class="s">jmesPath:</span>
          <span class="s">- path: "password"</span>
            <span class="s">objectAlias: "db-password"</span>
</code></pre></div></div>

<h4 id="433-secrets-rotation-and-lifecycle">4.3.3 Secrets Rotation and Lifecycle</h4>

<p><strong>Automatic rotation with External Secrets:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">external-secrets.io/v1beta1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ExternalSecret</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">rotating-secret</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">refreshInterval</span><span class="pi">:</span> <span class="s">1h</span>  <span class="c1"># Check for updates every hour</span>
  <span class="na">secretStoreRef</span><span class="pi">:</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">aws-secrets-manager</span>
    <span class="na">kind</span><span class="pi">:</span> <span class="s">SecretStore</span>
  <span class="na">target</span><span class="pi">:</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">app-secret</span>
    <span class="na">creationPolicy</span><span class="pi">:</span> <span class="s">Owner</span>
    <span class="na">template</span><span class="pi">:</span>
      <span class="na">metadata</span><span class="pi">:</span>
        <span class="na">annotations</span><span class="pi">:</span>
          <span class="na">reloader.stakater.com/match</span><span class="pi">:</span> <span class="s2">"</span><span class="s">true"</span>  <span class="c1"># Trigger pod restart on change</span>
</code></pre></div></div>

<p><strong>Monitoring secrets rotation:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check External Secrets status</span>
kubectl get externalsecrets
kubectl describe externalsecret &lt;name&gt;

<span class="c"># Check secret age</span>
kubectl get secrets <span class="nt">-o</span> custom-columns<span class="o">=</span>NAME:.metadata.name,AGE:.metadata.creationTimestamp
</code></pre></div></div>

<hr />

<h3 id="44-network-security-security-groups-and-network-policies">4.4 Network Security (Security Groups and Network Policies)</h3>

<h4 id="441-security-groups-for-pods">4.4.1 Security Groups for Pods</h4>

<p>EKS allows assigning security groups directly to pods for fine-grained network control:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Pod</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">secure-pod</span>
  <span class="na">annotations</span><span class="pi">:</span>
    <span class="na">eks.amazonaws.com/security-groups</span><span class="pi">:</span> <span class="s">sg-12345678</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">containers</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">app</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">myapp:latest</span>
</code></pre></div></div>

<p><strong>When to use pod security groups:</strong></p>
<ul>
  <li>Need different network rules per workload</li>
  <li>Compliance requirements for network isolation</li>
  <li>Integration with AWS security services</li>
</ul>

<p><strong>Limitations:</strong></p>
<ul>
  <li>Only works with supported instance types</li>
  <li>Requires specific CNI configuration</li>
  <li>Can impact performance</li>
</ul>

<h4 id="442-network-policies-for-micro-segmentation">4.4.2 Network Policies for Micro-segmentation</h4>

<p><strong>Default deny all traffic:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">networking.k8s.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">NetworkPolicy</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">default-deny-all</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">podSelector</span><span class="pi">:</span> <span class="pi">{}</span>
  <span class="na">policyTypes</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s">Ingress</span>
  <span class="pi">-</span> <span class="s">Egress</span>
</code></pre></div></div>

<p><strong>Allow specific service communication:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">networking.k8s.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">NetworkPolicy</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">web-to-api</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">podSelector</span><span class="pi">:</span>
    <span class="na">matchLabels</span><span class="pi">:</span>
      <span class="na">app</span><span class="pi">:</span> <span class="s">api</span>
  <span class="na">policyTypes</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s">Ingress</span>
  <span class="na">ingress</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">from</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">podSelector</span><span class="pi">:</span>
        <span class="na">matchLabels</span><span class="pi">:</span>
          <span class="na">app</span><span class="pi">:</span> <span class="s">web</span>
    <span class="na">ports</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
      <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
</code></pre></div></div>

<p><strong>Always allow DNS and health checks:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">networking.k8s.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">NetworkPolicy</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">allow-dns-and-health</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">podSelector</span><span class="pi">:</span> <span class="pi">{}</span>
  <span class="na">policyTypes</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s">Egress</span>
  <span class="pi">-</span> <span class="s">Ingress</span>
  <span class="na">egress</span><span class="pi">:</span>
  <span class="c1"># Allow DNS</span>
  <span class="pi">-</span> <span class="na">to</span><span class="pi">:</span> <span class="pi">[]</span>
    <span class="na">ports</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">protocol</span><span class="pi">:</span> <span class="s">UDP</span>
      <span class="na">port</span><span class="pi">:</span> <span class="m">53</span>
    <span class="pi">-</span> <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
      <span class="na">port</span><span class="pi">:</span> <span class="m">53</span>
  <span class="na">ingress</span><span class="pi">:</span>
  <span class="c1"># Allow health checks from kubelet</span>
  <span class="pi">-</span> <span class="na">from</span><span class="pi">:</span> <span class="pi">[]</span>
    <span class="na">ports</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
      <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>  <span class="c1"># Your health check port</span>
</code></pre></div></div>

<hr />

<h3 id="45-image-security-and-supply-chain">4.5 Image Security and Supply Chain</h3>

<h4 id="451-image-scanning-and-vulnerability-management">4.5.1 Image Scanning and Vulnerability Management</h4>

<p><strong>ECR image scanning:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Enable scan on push</span>
aws ecr put-image-scanning-configuration <span class="nt">--repository-name</span> myapp <span class="nt">--image-scanning-configuration</span> <span class="nv">scanOnPush</span><span class="o">=</span><span class="nb">true</span>

<span class="c"># Manual scan</span>
aws ecr start-image-scan <span class="nt">--repository-name</span> myapp <span class="nt">--image-id</span> <span class="nv">imageTag</span><span class="o">=</span>latest

<span class="c"># Get scan results</span>
aws ecr describe-image-scan-findings <span class="nt">--repository-name</span> myapp <span class="nt">--image-id</span> <span class="nv">imageTag</span><span class="o">=</span>latest
</code></pre></div></div>

<p><strong>Admission controller for image scanning:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ValidatingAdmissionWebhook</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">image-security-webhook</span>
<span class="na">webhooks</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">image-scan-check</span>
  <span class="na">clientConfig</span><span class="pi">:</span>
    <span class="na">service</span><span class="pi">:</span>
      <span class="na">name</span><span class="pi">:</span> <span class="s">image-security-service</span>
      <span class="na">namespace</span><span class="pi">:</span> <span class="s">security-system</span>
      <span class="na">path</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/validate"</span>
  <span class="na">rules</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">operations</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">CREATE"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">UPDATE"</span><span class="pi">]</span>
    <span class="na">apiGroups</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">"</span><span class="pi">]</span>
    <span class="na">apiVersions</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">v1"</span><span class="pi">]</span>
    <span class="na">resources</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">pods"</span><span class="pi">]</span>
</code></pre></div></div>

<h4 id="452-image-signing-and-verification">4.5.2 Image Signing and Verification</h4>

<p><strong>Using Cosign for image signing:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Sign image</span>
cosign sign <span class="nt">--key</span> cosign.key myregistry/myapp:v1.0.0

<span class="c"># Verify signature</span>
cosign verify <span class="nt">--key</span> cosign.pub myregistry/myapp:v1.0.0
</code></pre></div></div>

<p><strong>Policy enforcement with Gatekeeper:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">templates.gatekeeper.sh/v1beta1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ConstraintTemplate</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">requiresignedimages</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">crd</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">names</span><span class="pi">:</span>
        <span class="na">kind</span><span class="pi">:</span> <span class="s">RequireSignedImages</span>
      <span class="na">validation</span><span class="pi">:</span>
        <span class="na">properties</span><span class="pi">:</span>
          <span class="na">trustedKeys</span><span class="pi">:</span>
            <span class="na">type</span><span class="pi">:</span> <span class="s">array</span>
            <span class="na">items</span><span class="pi">:</span>
              <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
  <span class="na">targets</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">target</span><span class="pi">:</span> <span class="s">admission.k8s.gatekeeper.sh</span>
      <span class="na">rego</span><span class="pi">:</span> <span class="pi">|</span>
        <span class="s">package requiresignedimages</span>
        
        <span class="s">violation[{"msg": msg}] {</span>
          <span class="s">container := input.review.object.spec.containers[_]</span>
          <span class="s">not is_signed(container.image)</span>
          <span class="s">msg := sprintf("Image %v is not signed", [container.image])</span>
        <span class="s">}</span>
        
        <span class="s">is_signed(image) {</span>
          <span class="s"># Implementation depends on your signing verification logic</span>
        <span class="s">}</span>
</code></pre></div></div>

<hr />

<h3 id="46-audit-logging-and-compliance">4.6 Audit Logging and Compliance</h3>

<h4 id="461-eks-audit-logging-configuration">4.6.1 EKS Audit Logging Configuration</h4>

<p><strong>Enable audit logging:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>aws eks update-cluster-config <span class="se">\</span>
  <span class="nt">--name</span> my-cluster <span class="se">\</span>
  <span class="nt">--logging</span> <span class="s1">'{"enable":["api","audit","authenticator","controllerManager","scheduler"]}'</span>
</code></pre></div></div>

<p><strong>Audit policy for security events:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">audit.k8s.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Policy</span>
<span class="na">rules</span><span class="pi">:</span>
<span class="c1"># Log secret access</span>
<span class="pi">-</span> <span class="na">level</span><span class="pi">:</span> <span class="s">Metadata</span>
  <span class="na">resources</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">group</span><span class="pi">:</span> <span class="s2">"</span><span class="s">"</span>
    <span class="na">resources</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">secrets"</span><span class="pi">]</span>
<span class="c1"># Log RBAC changes</span>
<span class="pi">-</span> <span class="na">level</span><span class="pi">:</span> <span class="s">RequestResponse</span>
  <span class="na">resources</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">group</span><span class="pi">:</span> <span class="s2">"</span><span class="s">rbac.authorization.k8s.io"</span>
    <span class="na">resources</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">*"</span><span class="pi">]</span>
<span class="c1"># Log security context changes</span>
<span class="pi">-</span> <span class="na">level</span><span class="pi">:</span> <span class="s">Request</span>
  <span class="na">resources</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">group</span><span class="pi">:</span> <span class="s2">"</span><span class="s">"</span>
    <span class="na">resources</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">pods"</span><span class="pi">]</span>
  <span class="na">namespaces</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">production"</span><span class="pi">]</span>
  <span class="na">omitStages</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s">RequestReceived</span>
</code></pre></div></div>

<h4 id="462-security-monitoring-and-alerting">4.6.2 Security Monitoring and Alerting</h4>

<p><strong>Key security metrics to monitor:</strong></p>
<ul>
  <li>Failed authentication attempts</li>
  <li>Privilege escalation attempts</li>
  <li>Unauthorized secret access</li>
  <li>Network policy violations</li>
  <li>Image pull failures from untrusted registries</li>
</ul>

<p><strong>Example Prometheus alerts:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">groups</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">kubernetes-security</span>
  <span class="na">rules</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">UnauthorizedSecretAccess</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">increase(apiserver_audit_total{verb="get",objectRef_resource="secrets",user_username!~"system:.*"}[5m]) &gt; </span><span class="m">0</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">warning</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Unauthorized</span><span class="nv"> </span><span class="s">access</span><span class="nv"> </span><span class="s">to</span><span class="nv"> </span><span class="s">secrets</span><span class="nv"> </span><span class="s">detected"</span>
      
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">PrivilegedPodCreated</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">increase(apiserver_audit_total{verb="create",objectRef_resource="pods",requestObject_spec_securityContext_privileged="true"}[5m]) &gt; </span><span class="m">0</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">critical</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Privileged</span><span class="nv"> </span><span class="s">pod</span><span class="nv"> </span><span class="s">created"</span>
</code></pre></div></div>

<hr />

<h3 id="47-security-incident-response-runbook">4.7 Security Incident Response Runbook</h3>

<h4 id="471-pod-cant-access-aws-services-runbook">4.7.1 “Pod can’t access AWS services” Runbook</h4>

<p><strong>Symptoms:</strong> AWS API calls failing with permission errors</p>

<p><strong>Step 1: Verify IRSA setup</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl describe sa &lt;service-account&gt; | <span class="nb">grep </span>eks.amazonaws.com/role-arn
kubectl describe pod &lt;pod&gt; | <span class="nb">grep</span> <span class="s2">"Service Account"</span>
</code></pre></div></div>

<p><strong>Step 2: Check token projection</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nb">exec</span> &lt;pod&gt; <span class="nt">--</span> <span class="nb">ls</span> <span class="nt">-la</span> /var/run/secrets/eks.amazonaws.com/serviceaccount/
kubectl <span class="nb">exec</span> &lt;pod&gt; <span class="nt">--</span> aws sts get-caller-identity
</code></pre></div></div>

<p><strong>Step 3: Verify IAM role and policies</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>aws iam get-role <span class="nt">--role-name</span> &lt;irsa-role-name&gt;
aws iam list-attached-role-policies <span class="nt">--role-name</span> &lt;irsa-role-name&gt;
</code></pre></div></div>

<p><strong>Step 4: Test permissions</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nb">exec</span> &lt;pod&gt; <span class="nt">--</span> aws s3 <span class="nb">ls</span>  <span class="c"># Or whatever AWS service you're trying to access</span>
</code></pre></div></div>

<h4 id="472-pods-being-rejected-by-security-policies-runbook">4.7.2 “Pods being rejected by security policies” Runbook</h4>

<p><strong>Symptoms:</strong> Pod creation fails with security policy violations</p>

<p><strong>Step 1: Check namespace security labels</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get namespace &lt;namespace&gt; <span class="nt">-o</span> yaml | <span class="nb">grep </span>pod-security
</code></pre></div></div>

<p><strong>Step 2: Identify specific violations</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl describe pod &lt;pod&gt; | <span class="nb">grep</span> <span class="nt">-A</span> 10 <span class="s2">"violates PodSecurity"</span>
</code></pre></div></div>

<p><strong>Step 3: Fix security context</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check current security context</span>
kubectl get pod &lt;pod&gt; <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.spec.securityContext}'</span>
kubectl get pod &lt;pod&gt; <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.spec.containers[*].securityContext}'</span>
</code></pre></div></div>

<p><strong>Step 4: Apply fixes and redeploy</strong></p>

<h4 id="473-secrets-not-updating-runbook">4.7.3 “Secrets not updating” Runbook</h4>

<p><strong>Symptoms:</strong> Application using old secret values</p>

<p><strong>Step 1: Check External Secrets status</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get externalsecrets
kubectl describe externalsecret &lt;name&gt;
</code></pre></div></div>

<p><strong>Step 2: Verify secret store connectivity</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get secretstore
kubectl describe secretstore &lt;name&gt;
</code></pre></div></div>

<p><strong>Step 3: Check AWS Secrets Manager</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>aws secretsmanager describe-secret <span class="nt">--secret-id</span> &lt;secret-name&gt;
aws secretsmanager get-secret-value <span class="nt">--secret-id</span> &lt;secret-name&gt;
</code></pre></div></div>

<p><strong>Step 4: Force refresh</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl annotate externalsecret &lt;name&gt; force-sync<span class="o">=</span><span class="si">$(</span><span class="nb">date</span> +%s<span class="si">)</span>
</code></pre></div></div>

<hr />

<h3 id="48-health-probes-critical-for-reliable-services">4.8 Health probes (critical for reliable services)</h3>

<blockquote>
  <p>Note: health probes aren’t a security topic, but they’re here because probe misconfiguration is one of the most common causes of cascading failures during deployments. Misplaced in this section, but important enough to keep rather than move.</p>
</blockquote>

<h4 id="481-readiness-probe-traffic-routing-control">4.8.1 Readiness Probe (Traffic Routing Control)</h4>

<p><strong>Purpose:</strong> “Is it a good idea to send traffic to this Pod right now?”</p>

<p><strong>Common misconception:</strong> Since Kubernetes manages pods, graceful draining isn’t needed.</p>

<p><strong>Reality:</strong> Without proper readiness probes:</p>
<ul>
  <li>Traffic sent to pods before they’re ready</li>
  <li>Traffic continues to terminating pods</li>
  <li>Rolling updates cause 5xx errors</li>
</ul>

<p><strong>Best practices:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">web-service</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">app</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">my-app:latest</span>
        <span class="na">readinessProbe</span><span class="pi">:</span>
          <span class="na">httpGet</span><span class="pi">:</span>
            <span class="na">path</span><span class="pi">:</span> <span class="s">/health/ready</span>
            <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
          <span class="na">initialDelaySeconds</span><span class="pi">:</span> <span class="m">10</span>    <span class="c1"># Wait for app to start</span>
          <span class="na">periodSeconds</span><span class="pi">:</span> <span class="m">5</span>           <span class="c1"># Check every 5s</span>
          <span class="na">timeoutSeconds</span><span class="pi">:</span> <span class="m">3</span>          <span class="c1"># 3s timeout per check</span>
          <span class="na">successThreshold</span><span class="pi">:</span> <span class="m">1</span>        <span class="c1"># 1 success = ready</span>
          <span class="na">failureThreshold</span><span class="pi">:</span> <span class="m">3</span>        <span class="c1"># 3 failures = not ready</span>
        <span class="na">lifecycle</span><span class="pi">:</span>
          <span class="na">preStop</span><span class="pi">:</span>
            <span class="na">exec</span><span class="pi">:</span>
              <span class="na">command</span><span class="pi">:</span>
              <span class="pi">-</span> <span class="s">/bin/sh</span>
              <span class="pi">-</span> <span class="s">-c</span>
              <span class="pi">-</span> <span class="pi">|</span>
                <span class="s"># Fail readiness probe immediately</span>
                <span class="s">touch /tmp/shutdown</span>
                <span class="s"># Wait for load balancer to update</span>
                <span class="s">sleep 15</span>
</code></pre></div></div>

<p><strong>Readiness probe endpoint implementation:</strong></p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// Go example</span>
<span class="k">func</span> <span class="n">readinessHandler</span><span class="p">(</span><span class="n">w</span> <span class="n">http</span><span class="o">.</span><span class="n">ResponseWriter</span><span class="p">,</span> <span class="n">r</span> <span class="o">*</span><span class="n">http</span><span class="o">.</span><span class="n">Request</span><span class="p">)</span> <span class="p">{</span>
    <span class="c">// Check if shutdown initiated</span>
    <span class="k">if</span> <span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">os</span><span class="o">.</span><span class="n">Stat</span><span class="p">(</span><span class="s">"/tmp/shutdown"</span><span class="p">);</span> <span class="n">err</span> <span class="o">==</span> <span class="no">nil</span> <span class="p">{</span>
        <span class="n">http</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="s">"Shutting down"</span><span class="p">,</span> <span class="n">http</span><span class="o">.</span><span class="n">StatusServiceUnavailable</span><span class="p">)</span>
        <span class="k">return</span>
    <span class="p">}</span>
    
    <span class="c">// Check application readiness (NOT dependencies)</span>
    <span class="k">if</span> <span class="o">!</span><span class="n">app</span><span class="o">.</span><span class="n">IsReady</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">http</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="s">"Not ready"</span><span class="p">,</span> <span class="n">http</span><span class="o">.</span><span class="n">StatusServiceUnavailable</span><span class="p">)</span>
        <span class="k">return</span>
    <span class="p">}</span>
    
    <span class="n">w</span><span class="o">.</span><span class="n">WriteHeader</span><span class="p">(</span><span class="n">http</span><span class="o">.</span><span class="n">StatusOK</span><span class="p">)</span>
    <span class="n">w</span><span class="o">.</span><span class="n">Write</span><span class="p">([]</span><span class="kt">byte</span><span class="p">(</span><span class="s">"OK"</span><span class="p">))</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>Critical rule:</strong> Never depend on downstream services in readiness probes. If a database restarts, removing healthy pods from load balancers makes the outage worse.</p>

<h4 id="482-liveness-probe-container-health-check">4.8.2 Liveness Probe (Container Health Check)</h4>

<p><strong>Purpose:</strong> “Is the container healthy, or should we restart it?”</p>

<p><strong>When to use:</strong> Only when your application can deadlock and needs restart to recover.</p>

<p><strong>When NOT to use:</strong> If you don’t know why you need it, don’t configure it.</p>

<p><strong>Best practices:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">containers</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">app</span>
  <span class="na">image</span><span class="pi">:</span> <span class="s">my-app:latest</span>
  <span class="na">livenessProbe</span><span class="pi">:</span>
    <span class="na">httpGet</span><span class="pi">:</span>
      <span class="na">path</span><span class="pi">:</span> <span class="s">/health/live</span>    <span class="c1"># Different from readiness!</span>
      <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
    <span class="na">initialDelaySeconds</span><span class="pi">:</span> <span class="m">60</span>  <span class="c1"># Give app time to start</span>
    <span class="na">periodSeconds</span><span class="pi">:</span> <span class="m">30</span>        <span class="c1"># Check every 30s (less frequent than readiness)</span>
    <span class="na">timeoutSeconds</span><span class="pi">:</span> <span class="m">5</span>
    <span class="na">failureThreshold</span><span class="pi">:</span> <span class="m">3</span>      <span class="c1"># 3 failures before restart</span>
</code></pre></div></div>

<p><strong>Liveness probe implementation:</strong></p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">livenessHandler</span><span class="p">(</span><span class="n">w</span> <span class="n">http</span><span class="o">.</span><span class="n">ResponseWriter</span><span class="p">,</span> <span class="n">r</span> <span class="o">*</span><span class="n">http</span><span class="o">.</span><span class="n">Request</span><span class="p">)</span> <span class="p">{</span>
    <span class="c">// Only check internal application health</span>
    <span class="c">// Never check dependencies (databases, external APIs)</span>
    
    <span class="k">if</span> <span class="n">app</span><span class="o">.</span><span class="n">IsDeadlocked</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">http</span><span class="o">.</span><span class="n">Error</span><span class="p">(</span><span class="n">w</span><span class="p">,</span> <span class="s">"Deadlocked"</span><span class="p">,</span> <span class="n">http</span><span class="o">.</span><span class="n">StatusInternalServerError</span><span class="p">)</span>
        <span class="k">return</span>
    <span class="p">}</span>
    
    <span class="n">w</span><span class="o">.</span><span class="n">WriteHeader</span><span class="p">(</span><span class="n">http</span><span class="o">.</span><span class="n">StatusOK</span><span class="p">)</span>
    <span class="n">w</span><span class="o">.</span><span class="n">Write</span><span class="p">([]</span><span class="kt">byte</span><span class="p">(</span><span class="s">"OK"</span><span class="p">))</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>Critical rules:</strong></p>
<ul>
  <li>Never use the same endpoint for liveness and readiness</li>
  <li>Never check external dependencies in liveness probes</li>
  <li>Use conservative timeouts to avoid false positives under load</li>
</ul>

<h4 id="483-startup-probe-slow-starting-applications">4.8.3 Startup Probe (Slow-Starting Applications)</h4>

<p><strong>Purpose:</strong> “Should we start running the liveness probe now?”</p>

<p><strong>Use case:</strong> Applications that take longer to start than liveness probe allows.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">containers</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">slow-app</span>
  <span class="na">image</span><span class="pi">:</span> <span class="s">java-app:latest</span>
  <span class="na">startupProbe</span><span class="pi">:</span>
    <span class="na">httpGet</span><span class="pi">:</span>
      <span class="na">path</span><span class="pi">:</span> <span class="s">/health/startup</span>
      <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
    <span class="na">initialDelaySeconds</span><span class="pi">:</span> <span class="m">30</span>
    <span class="na">periodSeconds</span><span class="pi">:</span> <span class="m">10</span>
    <span class="na">timeoutSeconds</span><span class="pi">:</span> <span class="m">5</span>
    <span class="na">failureThreshold</span><span class="pi">:</span> <span class="m">30</span>      <span class="c1"># Allow 5 minutes for startup (30 * 10s)</span>
  <span class="na">livenessProbe</span><span class="pi">:</span>
    <span class="na">httpGet</span><span class="pi">:</span>
      <span class="na">path</span><span class="pi">:</span> <span class="s">/health/live</span>
      <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
    <span class="na">periodSeconds</span><span class="pi">:</span> <span class="m">30</span>
    <span class="na">timeoutSeconds</span><span class="pi">:</span> <span class="m">5</span>
    <span class="na">failureThreshold</span><span class="pi">:</span> <span class="m">3</span>
</code></pre></div></div>

<h4 id="484-probe-failure-troubleshooting">4.8.4 Probe Failure Troubleshooting</h4>

<p><strong>Common probe failures:</strong></p>

<ol>
  <li><strong>Readiness probe failing during load:</strong></li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check probe configuration</span>
kubectl describe pod &lt;pod-name&gt;

<span class="c"># Check application logs</span>
kubectl logs &lt;pod-name&gt; <span class="nt">--previous</span>

<span class="c"># Test probe endpoint manually</span>
kubectl <span class="nb">exec</span> &lt;pod-name&gt; <span class="nt">--</span> curl <span class="nt">-f</span> http://localhost:8080/health/ready
</code></pre></div></div>

<ol>
  <li><strong>Liveness probe causing restart loops:</strong></li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check restart count</span>
kubectl get pods <span class="nt">-o</span> wide

<span class="c"># Check events</span>
kubectl describe pod &lt;pod-name&gt;

<span class="c"># Increase probe timeouts temporarily</span>
kubectl patch deployment &lt;deployment&gt; <span class="nt">-p</span> <span class="s1">'{
  "spec": {
    "template": {
      "spec": {
        "containers": [{
          "name": "&lt;container&gt;",
          "livenessProbe": {
            "timeoutSeconds": 10,
            "failureThreshold": 5
          }
        }]
      }
    }
  }
}'</span>
</code></pre></div></div>

<ol>
  <li><strong>Startup probe preventing application start:</strong></li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check startup probe status</span>
kubectl get pods <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.items[*].status.conditions[?(@.type=="Ready")].message}'</span>

<span class="c"># Extend startup probe timeout</span>
kubectl patch deployment &lt;deployment&gt; <span class="nt">-p</span> <span class="s1">'{
  "spec": {
    "template": {
      "spec": {
        "containers": [{
          "name": "&lt;container&gt;",
          "startupProbe": {
            "failureThreshold": 60
          }
        }]
      }
    }
  }
}'</span>
</code></pre></div></div>

<h2 id="5-storage-and-persistent-volumes">5. Storage and persistent volumes</h2>

<p>Storage failures are different from everything else in this guide: they can mean data loss, not just downtime. What follows is the operational reality of running stateful workloads on EKS — the failure modes that catch teams off-guard.</p>

<hr />

<h3 id="51-ebs-csi-driver-the-critical-path-for-stateful-workloads">5.1 EBS CSI Driver (The Critical Path for Stateful Workloads)</h3>

<p>The EBS CSI driver is what makes persistent volumes work in EKS. When it fails, StatefulSets can’t start, volumes can’t attach, and data becomes inaccessible.</p>

<h4 id="511-ebs-csi-architecture-and-failure-points">5.1.1 EBS CSI Architecture and Failure Points</h4>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Pod with PVC] 
  ↓ (volume mount request)
[Kubelet] 
  ↓ (CSI calls)
[EBS CSI Node Plugin] 
  ↓ (AWS API calls)
[EBS Volume Attach/Mount]
</code></pre></div></div>

<p><strong>Each layer can fail:</strong></p>
<ul>
  <li><strong>Pod level</strong>: Wrong PVC references, security context issues</li>
  <li><strong>Kubelet level</strong>: Mount failures, device path issues</li>
  <li><strong>CSI level</strong>: Controller crashes, node plugin issues, IAM permissions</li>
  <li><strong>AWS level</strong>: EBS limits, AZ constraints, volume states</li>
</ul>

<h4 id="512-common-ebs-csi-failure-modes">5.1.2 Common EBS CSI Failure Modes</h4>

<p><strong>A) Pods stuck in ContainerCreating</strong></p>

<p><strong>Symptoms:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get pods | <span class="nb">grep </span>ContainerCreating
kubectl describe pod &lt;pod-name&gt;
<span class="c"># Shows: FailedMount, timeout waiting for volume to be attached</span>
</code></pre></div></div>

<p><strong>Root causes:</strong></p>
<ul>
  <li>Volume already attached to another node</li>
  <li>AZ mismatch between pod and volume</li>
  <li>EBS CSI controller/node plugin unhealthy</li>
  <li>IAM permissions missing</li>
</ul>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check CSI components</span>
kubectl <span class="nt">-n</span> kube-system get pods | <span class="nb">grep </span>ebs-csi
kubectl <span class="nt">-n</span> kube-system logs deployment/ebs-csi-controller
kubectl <span class="nt">-n</span> kube-system logs daemonset/ebs-csi-node

<span class="c"># Check volume attachment status</span>
kubectl get volumeattachment
kubectl describe volumeattachment &lt;va-name&gt;

<span class="c"># Check AWS side</span>
aws ec2 describe-volumes <span class="nt">--volume-ids</span> &lt;volume-id&gt;
</code></pre></div></div>

<p><strong>B) Volume attachment timeouts</strong></p>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Pods fail to start after node replacement</li>
  <li>“Multi-Attach error for volume” messages</li>
  <li>Long delays in pod scheduling</li>
</ul>

<p><strong>Common scenario:</strong></p>
<ol>
  <li>Node fails/terminates unexpectedly</li>
  <li>EBS volume remains “attached” to dead node</li>
  <li>New pod can’t attach volume until detached</li>
  <li>Detachment can take 6+ minutes</li>
</ol>

<p><strong>Force detachment (emergency):</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Find the volume</span>
kubectl get pv &lt;pv-name&gt; <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.spec.csi.volumeHandle}'</span>

<span class="c"># Force detach from AWS side</span>
aws ec2 detach-volume <span class="nt">--volume-id</span> &lt;volume-id&gt; <span class="nt">--force</span>

<span class="c"># Delete stale VolumeAttachment</span>
kubectl delete volumeattachment &lt;va-name&gt;
</code></pre></div></div>

<h4 id="513-ebs-csi-configuration-and-tuning">5.1.3 EBS CSI Configuration and Tuning</h4>

<p><strong>Essential CSI controller configuration:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">ebs-csi-controller</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">kube-system</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">ebs-plugin</span>
        <span class="na">args</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="s">controller</span>
        <span class="pi">-</span> <span class="s">--endpoint=$(CSI_ENDPOINT)</span>
        <span class="pi">-</span> <span class="s">--logtostderr</span>
        <span class="pi">-</span> <span class="s">--v=2</span>
        <span class="pi">-</span> <span class="s">--timeout=60s</span>  <span class="c1"># Increase for slow EBS operations</span>
        <span class="na">env</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">AWS_REGION</span>
          <span class="na">value</span><span class="pi">:</span> <span class="s">us-west-2</span>
        <span class="na">resources</span><span class="pi">:</span>
          <span class="na">requests</span><span class="pi">:</span>
            <span class="na">cpu</span><span class="pi">:</span> <span class="s">10m</span>
            <span class="na">memory</span><span class="pi">:</span> <span class="s">40Mi</span>
          <span class="na">limits</span><span class="pi">:</span>
            <span class="na">cpu</span><span class="pi">:</span> <span class="s">100m</span>
            <span class="na">memory</span><span class="pi">:</span> <span class="s">256Mi</span>
</code></pre></div></div>

<p><strong>Node plugin tuning for high-density workloads:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">DaemonSet</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">ebs-csi-node</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">kube-system</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">ebs-plugin</span>
        <span class="na">args</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="s">node</span>
        <span class="pi">-</span> <span class="s">--endpoint=$(CSI_ENDPOINT)</span>
        <span class="pi">-</span> <span class="s">--logtostderr</span>
        <span class="pi">-</span> <span class="s">--v=2</span>
        <span class="na">resources</span><span class="pi">:</span>
          <span class="na">requests</span><span class="pi">:</span>
            <span class="na">cpu</span><span class="pi">:</span> <span class="s">10m</span>
            <span class="na">memory</span><span class="pi">:</span> <span class="s">40Mi</span>
          <span class="na">limits</span><span class="pi">:</span>
            <span class="na">cpu</span><span class="pi">:</span> <span class="s">100m</span>
            <span class="na">memory</span><span class="pi">:</span> <span class="s">256Mi</span>
        <span class="na">securityContext</span><span class="pi">:</span>
          <span class="na">privileged</span><span class="pi">:</span> <span class="no">true</span>
        <span class="na">volumeMounts</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">kubelet-dir</span>
          <span class="na">mountPath</span><span class="pi">:</span> <span class="s">/var/lib/kubelet</span>
          <span class="na">mountPropagation</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Bidirectional"</span>
</code></pre></div></div>

<hr />

<h3 id="52-storage-classes-and-dynamic-provisioning">5.2 Storage Classes and Dynamic Provisioning</h3>

<h4 id="521-production-storage-class-configuration">5.2.1 Production Storage Class Configuration</h4>

<p><strong>GP3 with proper defaults:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">storage.k8s.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">StorageClass</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">gp3-retain</span>
  <span class="na">annotations</span><span class="pi">:</span>
    <span class="na">storageclass.kubernetes.io/is-default-class</span><span class="pi">:</span> <span class="s2">"</span><span class="s">true"</span>
<span class="na">provisioner</span><span class="pi">:</span> <span class="s">ebs.csi.aws.com</span>
<span class="na">parameters</span><span class="pi">:</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">gp3</span>
  <span class="na">iops</span><span class="pi">:</span> <span class="s2">"</span><span class="s">3000"</span>        <span class="c1"># Baseline IOPS</span>
  <span class="na">throughput</span><span class="pi">:</span> <span class="s2">"</span><span class="s">125"</span>   <span class="c1"># MB/s</span>
  <span class="na">encrypted</span><span class="pi">:</span> <span class="s2">"</span><span class="s">true"</span>
  <span class="na">kmsKeyId</span><span class="pi">:</span> <span class="s2">"</span><span class="s">alias/ebs-encryption-key"</span>
<span class="na">reclaimPolicy</span><span class="pi">:</span> <span class="s">Retain</span>  <span class="c1"># Prevent accidental data loss</span>
<span class="na">allowVolumeExpansion</span><span class="pi">:</span> <span class="no">true</span>
<span class="na">volumeBindingMode</span><span class="pi">:</span> <span class="s">WaitForFirstConsumer</span>  <span class="c1"># Critical for AZ placement</span>
</code></pre></div></div>

<p><strong>High-performance storage for databases:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">storage.k8s.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">StorageClass</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">io2-high-perf</span>
<span class="na">provisioner</span><span class="pi">:</span> <span class="s">ebs.csi.aws.com</span>
<span class="na">parameters</span><span class="pi">:</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">io2</span>
  <span class="na">iops</span><span class="pi">:</span> <span class="s2">"</span><span class="s">10000"</span>
  <span class="na">encrypted</span><span class="pi">:</span> <span class="s2">"</span><span class="s">true"</span>
<span class="na">reclaimPolicy</span><span class="pi">:</span> <span class="s">Retain</span>
<span class="na">allowVolumeExpansion</span><span class="pi">:</span> <span class="no">true</span>
<span class="na">volumeBindingMode</span><span class="pi">:</span> <span class="s">WaitForFirstConsumer</span>
</code></pre></div></div>

<h4 id="522-volume-binding-mode-implications">5.2.2 Volume Binding Mode Implications</h4>

<p><strong>Immediate vs WaitForFirstConsumer:</strong></p>

<p><strong>Immediate (default):</strong></p>
<ul>
  <li>Volume created immediately when PVC is created</li>
  <li>Can cause AZ mismatch if pod scheduled to different AZ</li>
  <li>Good for pre-provisioning scenarios</li>
</ul>

<p><strong>WaitForFirstConsumer (recommended):</strong></p>
<ul>
  <li>Volume created only when pod is scheduled</li>
  <li>Ensures volume and pod are in same AZ</li>
  <li>Required for multi-AZ clusters</li>
</ul>

<p><strong>AZ mismatch failure example:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># PVC created with Immediate binding in us-west-2a</span>
kubectl get pv &lt;pv-name&gt; <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.metadata.labels.topology\.ebs\.csi\.aws\.com/zone}'</span>
<span class="c"># Output: us-west-2a</span>

<span class="c"># Pod scheduled to us-west-2b</span>
kubectl get pod &lt;pod-name&gt; <span class="nt">-o</span> wide
<span class="c"># Shows node in us-west-2b</span>

<span class="c"># Result: FailedMount due to AZ mismatch</span>
</code></pre></div></div>

<hr />

<h3 id="53-statefulsets-and-persistent-volume-lifecycle">5.3 StatefulSets and Persistent Volume Lifecycle</h3>

<h4 id="531-statefulset-volume-management">5.3.1 StatefulSet Volume Management</h4>

<p><strong>Proper StatefulSet with volume claims:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">StatefulSet</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">database</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">serviceName</span><span class="pi">:</span> <span class="s">database</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">3</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">db</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">postgres:13</span>
        <span class="na">volumeMounts</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">data</span>
          <span class="na">mountPath</span><span class="pi">:</span> <span class="s">/var/lib/postgresql/data</span>
        <span class="na">env</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">POSTGRES_DB</span>
          <span class="na">value</span><span class="pi">:</span> <span class="s">myapp</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">PGDATA</span>
          <span class="na">value</span><span class="pi">:</span> <span class="s">/var/lib/postgresql/data/pgdata</span>
  <span class="na">volumeClaimTemplates</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">name</span><span class="pi">:</span> <span class="s">data</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">accessModes</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">ReadWriteOnce"</span><span class="pi">]</span>
      <span class="na">storageClassName</span><span class="pi">:</span> <span class="s">gp3-retain</span>
      <span class="na">resources</span><span class="pi">:</span>
        <span class="na">requests</span><span class="pi">:</span>
          <span class="na">storage</span><span class="pi">:</span> <span class="s">100Gi</span>
</code></pre></div></div>

<h4 id="532-statefulset-scaling-and-volume-orphaning">5.3.2 StatefulSet Scaling and Volume Orphaning</h4>

<p><strong>The orphaned PVC problem:</strong>
When you scale down a StatefulSet, PVCs are NOT automatically deleted:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Scale down from 5 to 3 replicas</span>
kubectl scale statefulset database <span class="nt">--replicas</span><span class="o">=</span>3

<span class="c"># PVCs for database-3 and database-4 remain</span>
kubectl get pvc | <span class="nb">grep </span>database
<span class="c"># database-data-0   Bound</span>
<span class="c"># database-data-1   Bound  </span>
<span class="c"># database-data-2   Bound</span>
<span class="c"># database-data-3   Bound  # Orphaned!</span>
<span class="c"># database-data-4   Bound  # Orphaned!</span>
</code></pre></div></div>

<p><strong>Manual cleanup required:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Delete orphaned PVCs (DANGEROUS - data loss!)</span>
kubectl delete pvc database-data-3 database-data-4

<span class="c"># Or retain for potential scale-up</span>
<span class="c"># PVCs will be reused if you scale back up</span>
</code></pre></div></div>

<h4 id="533-statefulset-rolling-updates-and-volume-safety">5.3.3 StatefulSet Rolling Updates and Volume Safety</h4>

<p><strong>Safe rolling update configuration:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">StatefulSet</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">database</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">updateStrategy</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">RollingUpdate</span>
    <span class="na">rollingUpdate</span><span class="pi">:</span>
      <span class="na">maxUnavailable</span><span class="pi">:</span> <span class="m">1</span>  <span class="c1"># Never update more than 1 pod at a time</span>
  <span class="na">podManagementPolicy</span><span class="pi">:</span> <span class="s">OrderedReady</span>  <span class="c1"># Wait for each pod to be ready</span>
</code></pre></div></div>

<p><strong>Volume expansion during updates:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Expand PVC (requires allowVolumeExpansion: true)</span>
kubectl patch pvc database-data-0 <span class="nt">-p</span> <span class="s1">'{"spec":{"resources":{"requests":{"storage":"200Gi"}}}}'</span>

<span class="c"># Check expansion status</span>
kubectl describe pvc database-data-0
<span class="c"># Look for: FileSystemResizePending or FileSystemResizeSuccessful</span>

<span class="c"># May require pod restart to complete filesystem resize</span>
kubectl delete pod database-0  <span class="c"># StatefulSet will recreate it</span>
</code></pre></div></div>

<hr />

<h3 id="54-backup-and-disaster-recovery">5.4 Backup and Disaster Recovery</h3>

<h4 id="541-ebs-snapshot-based-backups">5.4.1 EBS Snapshot-Based Backups</h4>

<p><strong>Volume Snapshot Class:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">snapshot.storage.k8s.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">VolumeSnapshotClass</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">ebs-snapshot-class</span>
<span class="na">driver</span><span class="pi">:</span> <span class="s">ebs.csi.aws.com</span>
<span class="na">deletionPolicy</span><span class="pi">:</span> <span class="s">Retain</span>  <span class="c1"># Keep snapshots even if VolumeSnapshot is deleted</span>
<span class="na">parameters</span><span class="pi">:</span>
  <span class="na">tagSpecification_1</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Name=CreatedBy,Value=EKS-CSI"</span>
  <span class="na">tagSpecification_2</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Environment=Production"</span>
</code></pre></div></div>

<p><strong>Creating snapshots:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">snapshot.storage.k8s.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">VolumeSnapshot</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">database-backup-20240126</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">volumeSnapshotClassName</span><span class="pi">:</span> <span class="s">ebs-snapshot-class</span>
  <span class="na">source</span><span class="pi">:</span>
    <span class="na">persistentVolumeClaimName</span><span class="pi">:</span> <span class="s">database-data-0</span>
</code></pre></div></div>

<p><strong>Restoring from snapshot:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">PersistentVolumeClaim</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">database-restored</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">accessModes</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s">ReadWriteOnce</span>
  <span class="na">storageClassName</span><span class="pi">:</span> <span class="s">gp3-retain</span>
  <span class="na">resources</span><span class="pi">:</span>
    <span class="na">requests</span><span class="pi">:</span>
      <span class="na">storage</span><span class="pi">:</span> <span class="s">100Gi</span>
  <span class="na">dataSource</span><span class="pi">:</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">database-backup-20240126</span>
    <span class="na">kind</span><span class="pi">:</span> <span class="s">VolumeSnapshot</span>
    <span class="na">apiGroup</span><span class="pi">:</span> <span class="s">snapshot.storage.k8s.io</span>
</code></pre></div></div>

<h4 id="542-application-consistent-backups">5.4.2 Application-Consistent Backups</h4>

<p><strong>Pre/post hooks for database consistency:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Pod</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">postgres-with-backup-hooks</span>
  <span class="na">annotations</span><span class="pi">:</span>
    <span class="na">pre.hook.backup.velero.io/command</span><span class="pi">:</span> <span class="s1">'</span><span class="s">["/bin/bash",</span><span class="nv"> </span><span class="s">"-c",</span><span class="nv"> </span><span class="s">"PGPASSWORD=$POSTGRES_PASSWORD</span><span class="nv"> </span><span class="s">pg_dump</span><span class="nv"> </span><span class="s">-h</span><span class="nv"> </span><span class="s">localhost</span><span class="nv"> </span><span class="s">-U</span><span class="nv"> </span><span class="s">$POSTGRES_USER</span><span class="nv"> </span><span class="s">$POSTGRES_DB</span><span class="nv"> </span><span class="s">&gt;</span><span class="nv"> </span><span class="s">/backup/dump.sql"]'</span>
    <span class="na">post.hook.backup.velero.io/command</span><span class="pi">:</span> <span class="s1">'</span><span class="s">["/bin/bash",</span><span class="nv"> </span><span class="s">"-c",</span><span class="nv"> </span><span class="s">"rm</span><span class="nv"> </span><span class="s">-f</span><span class="nv"> </span><span class="s">/backup/dump.sql"]'</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">containers</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">postgres</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">postgres:13</span>
    <span class="na">volumeMounts</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">data</span>
      <span class="na">mountPath</span><span class="pi">:</span> <span class="s">/var/lib/postgresql/data</span>
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">backup</span>
      <span class="na">mountPath</span><span class="pi">:</span> <span class="s">/backup</span>
</code></pre></div></div>

<h4 id="543-cross-region-backup-strategy">5.4.3 Cross-Region Backup Strategy</h4>

<p><strong>Automated cross-region snapshot copying:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="c"># Copy EBS snapshots to DR region</span>
<span class="nv">SOURCE_REGION</span><span class="o">=</span><span class="s2">"us-west-2"</span>
<span class="nv">DR_REGION</span><span class="o">=</span><span class="s2">"us-east-1"</span>

<span class="c"># Get recent snapshots</span>
<span class="nv">SNAPSHOTS</span><span class="o">=</span><span class="si">$(</span>aws ec2 describe-snapshots <span class="se">\</span>
  <span class="nt">--region</span> <span class="nv">$SOURCE_REGION</span> <span class="se">\</span>
  <span class="nt">--owner-ids</span> self <span class="se">\</span>
  <span class="nt">--filters</span> <span class="s2">"Name=tag:Environment,Values=Production"</span> <span class="se">\</span>
  <span class="nt">--query</span> <span class="s1">'Snapshots[?StartTime&gt;=`2024-01-25`].SnapshotId'</span> <span class="se">\</span>
  <span class="nt">--output</span> text<span class="si">)</span>

<span class="k">for </span>snapshot <span class="k">in</span> <span class="nv">$SNAPSHOTS</span><span class="p">;</span> <span class="k">do
  </span><span class="nb">echo</span> <span class="s2">"Copying </span><span class="nv">$snapshot</span><span class="s2"> to </span><span class="nv">$DR_REGION</span><span class="s2">"</span>
  aws ec2 copy-snapshot <span class="se">\</span>
    <span class="nt">--region</span> <span class="nv">$DR_REGION</span> <span class="se">\</span>
    <span class="nt">--source-region</span> <span class="nv">$SOURCE_REGION</span> <span class="se">\</span>
    <span class="nt">--source-snapshot-id</span> <span class="nv">$snapshot</span> <span class="se">\</span>
    <span class="nt">--description</span> <span class="s2">"DR copy of </span><span class="nv">$snapshot</span><span class="s2">"</span>
<span class="k">done</span>
</code></pre></div></div>

<hr />

<h3 id="55-performance-and-monitoring">5.5 Performance and Monitoring</h3>

<h4 id="551-ebs-performance-characteristics">5.5.1 EBS Performance Characteristics</h4>

<p><strong>IOPS and throughput limits by volume type:</strong></p>

<table>
  <thead>
    <tr>
      <th>Volume Type</th>
      <th>Max IOPS</th>
      <th>Max Throughput</th>
      <th>Use Case</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>gp3</td>
      <td>16,000</td>
      <td>1,000 MB/s</td>
      <td>General purpose</td>
    </tr>
    <tr>
      <td>io1</td>
      <td>64,000</td>
      <td>1,000 MB/s</td>
      <td>High IOPS</td>
    </tr>
    <tr>
      <td>io2</td>
      <td>64,000</td>
      <td>1,000 MB/s</td>
      <td>Mission critical</td>
    </tr>
    <tr>
      <td>io2 Block Express</td>
      <td>256,000</td>
      <td>4,000 MB/s</td>
      <td>Extreme performance</td>
    </tr>
  </tbody>
</table>

<p><strong>Instance-level limits also apply:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check instance storage performance limits</span>
aws ec2 describe-instance-types <span class="se">\</span>
  <span class="nt">--instance-types</span> m5.large <span class="se">\</span>
  <span class="nt">--query</span> <span class="s1">'InstanceTypes[0].EbsInfo'</span>
</code></pre></div></div>

<h4 id="552-storage-performance-monitoring">5.5.2 Storage Performance Monitoring</h4>

<p><strong>Key metrics to monitor:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Prometheus recording rules for storage</span>
<span class="na">groups</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">storage-performance</span>
  <span class="na">rules</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">record</span><span class="pi">:</span> <span class="s">ebs:iops_utilization</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">rate(node_disk_reads_completed_total[5m]) + rate(node_disk_writes_completed_total[5m])</span>
    
  <span class="pi">-</span> <span class="na">record</span><span class="pi">:</span> <span class="s">ebs:throughput_utilization</span>  
    <span class="na">expr</span><span class="pi">:</span> <span class="s">rate(node_disk_read_bytes_total[5m]) + rate(node_disk_written_bytes_total[5m])</span>
    
  <span class="pi">-</span> <span class="na">record</span><span class="pi">:</span> <span class="s">ebs:latency_p99</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">histogram_quantile(0.99, rate(node_disk_io_time_seconds_total[5m]))</span>
</code></pre></div></div>

<p><strong>Storage alerts:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">groups</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">storage-alerts</span>
  <span class="na">rules</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">HighDiskLatency</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">ebs:latency_p99 &gt; </span><span class="m">0.1</span>  <span class="c1"># 100ms</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">5m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">warning</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">High</span><span class="nv"> </span><span class="s">disk</span><span class="nv"> </span><span class="s">latency</span><span class="nv"> </span><span class="s">detected"</span>
      
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">EBSVolumeStuck</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">increase(kubelet_volume_stats_available_bytes[10m]) == </span><span class="m">0</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">10m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">critical</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">EBS</span><span class="nv"> </span><span class="s">volume</span><span class="nv"> </span><span class="s">appears</span><span class="nv"> </span><span class="s">stuck"</span>
</code></pre></div></div>

<h4 id="553-storage-capacity-management">5.5.3 Storage Capacity Management</h4>

<p><strong>Automatic PVC expansion:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ConfigMap</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">pvc-autoresizer-config</span>
<span class="na">data</span><span class="pi">:</span>
  <span class="na">config.yaml</span><span class="pi">:</span> <span class="pi">|</span>
    <span class="s">intervals:</span>
      <span class="s">- name: "5min"</span>
        <span class="s">interval: 5m</span>
    <span class="s">rules:</span>
      <span class="s">- name: "expand-when-80-percent-full"</span>
        <span class="s">selector:</span>
          <span class="s">matchLabels:</span>
            <span class="s">app: database</span>
        <span class="s">thresholds:</span>
          <span class="s">- threshold: 80</span>
            <span class="s">increase: "20%"</span>
          <span class="s">- threshold: 90</span>
            <span class="s">increase: "50%"</span>
</code></pre></div></div>

<hr />

<h3 id="56-multi-az-and-cross-az-storage-patterns">5.6 Multi-AZ and Cross-AZ Storage Patterns</h3>

<h4 id="561-ebs-volume-az-constraints">5.6.1 EBS Volume AZ Constraints</h4>

<p><strong>The fundamental constraint:</strong>
EBS volumes are AZ-specific and cannot be attached to instances in different AZs.</p>

<p><strong>Impact on StatefulSets:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># This will fail if pods get scheduled across AZs</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">StatefulSet</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">database</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">3</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="c1"># No AZ constraints = pods can land anywhere</span>
      <span class="c1"># But PVCs are bound to specific AZs</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">db</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">postgres:13</span>
</code></pre></div></div>

<p><strong>Solution - AZ-aware scheduling:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">StatefulSet</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">database</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">3</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">topologySpreadConstraints</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">maxSkew</span><span class="pi">:</span> <span class="m">1</span>
        <span class="na">topologyKey</span><span class="pi">:</span> <span class="s">topology.kubernetes.io/zone</span>
        <span class="na">whenUnsatisfiable</span><span class="pi">:</span> <span class="s">DoNotSchedule</span>
        <span class="na">labelSelector</span><span class="pi">:</span>
          <span class="na">matchLabels</span><span class="pi">:</span>
            <span class="na">app</span><span class="pi">:</span> <span class="s">database</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">db</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">postgres:13</span>
</code></pre></div></div>

<h4 id="562-cross-az-data-replication-patterns">5.6.2 Cross-AZ Data Replication Patterns</h4>

<p><strong>For databases requiring cross-AZ replication:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Primary in us-west-2a</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">StatefulSet</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">postgres-primary</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">1</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">nodeSelector</span><span class="pi">:</span>
        <span class="na">topology.kubernetes.io/zone</span><span class="pi">:</span> <span class="s">us-west-2a</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">postgres</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">postgres:13</span>
        <span class="na">env</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">POSTGRES_REPLICATION_MODE</span>
          <span class="na">value</span><span class="pi">:</span> <span class="s">master</span>

<span class="nn">---</span>
<span class="c1"># Replica in us-west-2b  </span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">StatefulSet</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">postgres-replica</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">1</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">nodeSelector</span><span class="pi">:</span>
        <span class="na">topology.kubernetes.io/zone</span><span class="pi">:</span> <span class="s">us-west-2b</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">postgres</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">postgres:13</span>
        <span class="na">env</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">POSTGRES_REPLICATION_MODE</span>
          <span class="na">value</span><span class="pi">:</span> <span class="s">slave</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">POSTGRES_MASTER_SERVICE</span>
          <span class="na">value</span><span class="pi">:</span> <span class="s">postgres-primary</span>
</code></pre></div></div>

<hr />

<h3 id="57-storage-troubleshooting-runbooks">5.7 Storage Troubleshooting Runbooks</h3>

<h4 id="571-pod-stuck-in-containercreating-runbook">5.7.1 “Pod stuck in ContainerCreating” Runbook</h4>

<p><strong>Symptoms:</strong> Pod won’t start, stuck in ContainerCreating state</p>

<p><strong>Step 1: Check pod events</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl describe pod &lt;pod-name&gt;
<span class="c"># Look for: FailedMount, timeout, volume attachment errors</span>
</code></pre></div></div>

<p><strong>Step 2: Check PVC status</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get pvc &lt;pvc-name&gt;
kubectl describe pvc &lt;pvc-name&gt;
<span class="c"># Status should be "Bound"</span>
</code></pre></div></div>

<p><strong>Step 3: Check VolumeAttachment</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get volumeattachment
kubectl describe volumeattachment &lt;va-name&gt;
<span class="c"># Look for attachment errors</span>
</code></pre></div></div>

<p><strong>Step 4: Check CSI components</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nt">-n</span> kube-system get pods | <span class="nb">grep </span>ebs-csi
kubectl <span class="nt">-n</span> kube-system logs deployment/ebs-csi-controller
kubectl <span class="nt">-n</span> kube-system logs daemonset/ebs-csi-node <span class="nt">-c</span> ebs-plugin
</code></pre></div></div>

<p><strong>Step 5: Check AWS EBS volume</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Get volume ID from PV</span>
kubectl get pv &lt;pv-name&gt; <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.spec.csi.volumeHandle}'</span>

<span class="c"># Check volume status</span>
aws ec2 describe-volumes <span class="nt">--volume-ids</span> &lt;volume-id&gt;
<span class="c"># State should be "available" or "in-use"</span>
</code></pre></div></div>

<h4 id="572-volume-attachment-timeout-runbook">5.7.2 “Volume attachment timeout” Runbook</h4>

<p><strong>Symptoms:</strong> Long delays in pod startup, attachment timeout errors</p>

<p><strong>Step 1: Identify stuck attachment</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get volumeattachment <span class="nt">-o</span> wide
<span class="c"># Look for old attachments with "Attaching" status</span>
</code></pre></div></div>

<p><strong>Step 2: Check if volume is stuck on dead node</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>aws ec2 describe-volumes <span class="nt">--volume-ids</span> &lt;volume-id&gt; <span class="se">\</span>
  <span class="nt">--query</span> <span class="s1">'Volumes[0].Attachments'</span>
<span class="c"># Check if attached to non-existent instance</span>
</code></pre></div></div>

<p><strong>Step 3: Force detachment (if safe)</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Verify the instance is really dead</span>
aws ec2 describe-instances <span class="nt">--instance-ids</span> &lt;instance-id&gt;

<span class="c"># Force detach</span>
aws ec2 detach-volume <span class="nt">--volume-id</span> &lt;volume-id&gt; <span class="nt">--force</span>

<span class="c"># Clean up VolumeAttachment</span>
kubectl delete volumeattachment &lt;va-name&gt;
</code></pre></div></div>

<p><strong>Step 4: Verify pod can start</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get pod &lt;pod-name&gt;
<span class="c"># Should transition to Running</span>
</code></pre></div></div>

<h4 id="573-pvc-expansion-stuck-runbook">5.7.3 “PVC expansion stuck” Runbook</h4>

<p><strong>Symptoms:</strong> PVC shows larger size but pod still sees old size</p>

<p><strong>Step 1: Check PVC conditions</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl describe pvc &lt;pvc-name&gt;
<span class="c"># Look for: FileSystemResizePending, VolumeResizeSuccessful</span>
</code></pre></div></div>

<p><strong>Step 2: Check if pod restart is needed</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Some filesystems require pod restart to complete resize</span>
kubectl get pod &lt;pod-name&gt; <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.metadata.creationTimestamp}'</span>
kubectl get pvc &lt;pvc-name&gt; <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.status.conditions[?(@.type=="FileSystemResizePending")].lastTransitionTime}'</span>
</code></pre></div></div>

<p><strong>Step 3: Restart pod if needed</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl delete pod &lt;pod-name&gt;
<span class="c"># StatefulSet/Deployment will recreate it</span>
</code></pre></div></div>

<p><strong>Step 4: Verify expansion completed</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nb">exec</span> &lt;pod-name&gt; <span class="nt">--</span> <span class="nb">df</span> <span class="nt">-h</span> /data
<span class="c"># Should show new size</span>
</code></pre></div></div>

<hr />

<h2 id="6-observability-and-monitoring">6. Observability and monitoring</h2>

<p>When monitoring is broken you can’t tell the difference between “the app is slow” and “the cluster is degraded”. This is about building observability that actually helps during incidents, not dashboards that look good in screenshots.</p>

<hr />

<h3 id="61-the-eks-observability-stack-what-you-actually-need">6.1 The EKS Observability Stack (What You Actually Need)</h3>

<h4 id="611-metrics-collection-architecture">6.1.1 Metrics Collection Architecture</h4>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Application Metrics] → [Prometheus] → [Long-term Storage] → [Alerting/Dashboards]
[System Metrics] → [Node Exporter] ↗
[Kubernetes Metrics] → [kube-state-metrics] ↗
[AWS Metrics] → [CloudWatch] → [Prometheus via adapter] ↗
</code></pre></div></div>

<p>You need metrics at multiple layers because EKS failures can happen at any level:</p>
<ul>
  <li>Application layer (your code)</li>
  <li>Kubernetes layer (pods, services, ingress)</li>
  <li>Node layer (CPU, memory, disk, network)</li>
  <li>AWS layer (EBS, ENI, load balancers)</li>
</ul>

<h4 id="612-essential-metrics-components">6.1.2 Essential Metrics Components</h4>

<p><strong>Core Prometheus stack:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Prometheus server configuration</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ConfigMap</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">prometheus-config</span>
<span class="na">data</span><span class="pi">:</span>
  <span class="na">prometheus.yml</span><span class="pi">:</span> <span class="pi">|</span>
    <span class="s">global:</span>
      <span class="s">scrape_interval: 15s</span>
      <span class="s">evaluation_interval: 15s</span>
    
    <span class="s">rule_files:</span>
    <span class="s">- "/etc/prometheus/rules/*.yml"</span>
    
    <span class="s">scrape_configs:</span>
    <span class="s"># Kubernetes API server</span>
    <span class="s">- job_name: 'kubernetes-apiservers'</span>
      <span class="s">kubernetes_sd_configs:</span>
      <span class="s">- role: endpoints</span>
      <span class="s">scheme: https</span>
      <span class="s">tls_config:</span>
        <span class="s">ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt</span>
      <span class="s">bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token</span>
      <span class="s">relabel_configs:</span>
      <span class="s">- source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]</span>
        <span class="s">action: keep</span>
        <span class="s">regex: default;kubernetes;https</span>
    
    <span class="s"># Node metrics</span>
    <span class="s">- job_name: 'kubernetes-nodes'</span>
      <span class="s">kubernetes_sd_configs:</span>
      <span class="s">- role: node</span>
      <span class="s">relabel_configs:</span>
      <span class="s">- action: labelmap</span>
        <span class="s">regex: __meta_kubernetes_node_label_(.+)</span>
    
    <span class="s"># Pod metrics</span>
    <span class="s">- job_name: 'kubernetes-pods'</span>
      <span class="s">kubernetes_sd_configs:</span>
      <span class="s">- role: pod</span>
      <span class="s">relabel_configs:</span>
      <span class="s">- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]</span>
        <span class="s">action: keep</span>
        <span class="s">regex: true</span>
      <span class="s">- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]</span>
        <span class="s">action: replace</span>
        <span class="s">target_label: __metrics_path__</span>
        <span class="s">regex: (.+)</span>
</code></pre></div></div>

<p><strong>Node exporter for system metrics:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">DaemonSet</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">node-exporter</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">matchLabels</span><span class="pi">:</span>
      <span class="na">app</span><span class="pi">:</span> <span class="s">node-exporter</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">labels</span><span class="pi">:</span>
        <span class="na">app</span><span class="pi">:</span> <span class="s">node-exporter</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">hostNetwork</span><span class="pi">:</span> <span class="no">true</span>
      <span class="na">hostPID</span><span class="pi">:</span> <span class="no">true</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">node-exporter</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">prom/node-exporter:v1.6.1</span>
        <span class="na">args</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="s">--path.procfs=/host/proc</span>
        <span class="pi">-</span> <span class="s">--path.sysfs=/host/sys</span>
        <span class="pi">-</span> <span class="s">--path.rootfs=/host/root</span>
        <span class="pi">-</span> <span class="s">--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)</span>
        <span class="pi">-</span> <span class="s">--collector.netdev.device-exclude=^(veth.*|docker.*|br-.*|lo)$$</span>
        <span class="pi">-</span> <span class="s">--collector.conntrack</span>  <span class="c1"># Critical for connection tracking issues</span>
        <span class="pi">-</span> <span class="s">--collector.ethtool</span>     <span class="c1"># AWS ENA metrics</span>
        <span class="pi">-</span> <span class="s">--collector.ethtool.metrics-include=^(ena_.*|.*_exceeded)$$</span>
        <span class="na">ports</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">9100</span>
          <span class="na">hostPort</span><span class="pi">:</span> <span class="m">9100</span>
        <span class="na">volumeMounts</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">proc</span>
          <span class="na">mountPath</span><span class="pi">:</span> <span class="s">/host/proc</span>
          <span class="na">readOnly</span><span class="pi">:</span> <span class="no">true</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">sys</span>
          <span class="na">mountPath</span><span class="pi">:</span> <span class="s">/host/sys</span>
          <span class="na">readOnly</span><span class="pi">:</span> <span class="no">true</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">root</span>
          <span class="na">mountPath</span><span class="pi">:</span> <span class="s">/host/root</span>
          <span class="na">readOnly</span><span class="pi">:</span> <span class="no">true</span>
      <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">proc</span>
        <span class="na">hostPath</span><span class="pi">:</span>
          <span class="na">path</span><span class="pi">:</span> <span class="s">/proc</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">sys</span>
        <span class="na">hostPath</span><span class="pi">:</span>
          <span class="na">path</span><span class="pi">:</span> <span class="s">/sys</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">root</span>
        <span class="na">hostPath</span><span class="pi">:</span>
          <span class="na">path</span><span class="pi">:</span> <span class="s">/</span>
</code></pre></div></div>

<hr />

<h3 id="62-eks-specific-monitoring-the-metrics-that-matter">6.2 EKS-Specific Monitoring (The Metrics That Matter)</h3>

<h4 id="621-control-plane-monitoring">6.2.1 Control Plane Monitoring</h4>

<p><strong>API Server health:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Critical API server alerts</span>
<span class="na">groups</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">kubernetes-apiserver</span>
  <span class="na">rules</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">KubernetesApiServerDown</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">up{job="kubernetes-apiservers"} == </span><span class="m">0</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">5m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">critical</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Kubernetes</span><span class="nv"> </span><span class="s">API</span><span class="nv"> </span><span class="s">server</span><span class="nv"> </span><span class="s">is</span><span class="nv"> </span><span class="s">down"</span>
      
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">KubernetesApiServerLatency</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">histogram_quantile(0.99, sum(rate(apiserver_request_duration_seconds_bucket[5m])) by (le, verb)) &gt; </span><span class="m">1</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">10m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">warning</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Kubernetes</span><span class="nv"> </span><span class="s">API</span><span class="nv"> </span><span class="s">server</span><span class="nv"> </span><span class="s">high</span><span class="nv"> </span><span class="s">latency"</span>
      
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">KubernetesApiServerErrors</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">sum(rate(apiserver_request_total{code=~"5.."}[5m])) / sum(rate(apiserver_request_total[5m])) &gt; </span><span class="m">0.05</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">5m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">critical</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Kubernetes</span><span class="nv"> </span><span class="s">API</span><span class="nv"> </span><span class="s">server</span><span class="nv"> </span><span class="s">error</span><span class="nv"> </span><span class="s">rate</span><span class="nv"> </span><span class="s">&gt;</span><span class="nv"> </span><span class="s">5%"</span>
</code></pre></div></div>

<h4 id="622-node-level-monitoring">6.2.2 Node-Level Monitoring</h4>

<p><strong>AWS-specific node metrics:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># AWS ENA network limits</span>
<span class="na">groups</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">aws-node-limits</span>
  <span class="na">rules</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">AWSNetworkLimitExceeded</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">rate(node_ethtool_conntrack_allowance_exceeded[5m]) &gt; </span><span class="m">0</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">1m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">critical</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">AWS</span><span class="nv"> </span><span class="s">connection</span><span class="nv"> </span><span class="s">tracking</span><span class="nv"> </span><span class="s">limit</span><span class="nv"> </span><span class="s">exceeded</span><span class="nv"> </span><span class="s">on</span><span class="nv"> </span><span class="s">"</span>
      
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">AWSLinkLocalLimitExceeded</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">rate(node_ethtool_linklocal_allowance_exceeded[5m]) &gt; </span><span class="m">5</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">1m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">warning</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">AWS</span><span class="nv"> </span><span class="s">link-local</span><span class="nv"> </span><span class="s">rate</span><span class="nv"> </span><span class="s">limit</span><span class="nv"> </span><span class="s">exceeded</span><span class="nv"> </span><span class="s">on</span><span class="nv"> </span><span class="s">"</span>
      
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">AWSBandwidthLimitExceeded</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">rate(node_ethtool_bw_in_allowance_exceeded[5m]) &gt; 0 or rate(node_ethtool_bw_out_allowance_exceeded[5m]) &gt; </span><span class="m">0</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">1m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">warning</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">AWS</span><span class="nv"> </span><span class="s">bandwidth</span><span class="nv"> </span><span class="s">limit</span><span class="nv"> </span><span class="s">exceeded</span><span class="nv"> </span><span class="s">on</span><span class="nv"> </span><span class="s">"</span>
</code></pre></div></div>

<p><strong>Connection tracking monitoring:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Conntrack exhaustion alerts</span>
<span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">ConntrackTableFull</span>
  <span class="na">expr</span><span class="pi">:</span> <span class="s">(node_nf_conntrack_entries / node_nf_conntrack_entries_limit) &gt; </span><span class="m">0.8</span>
  <span class="na">for</span><span class="pi">:</span> <span class="s">5m</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">severity</span><span class="pi">:</span> <span class="s">warning</span>
  <span class="na">annotations</span><span class="pi">:</span>
    <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Conntrack</span><span class="nv"> </span><span class="s">table</span><span class="nv"> </span><span class="s">80%</span><span class="nv"> </span><span class="s">full</span><span class="nv"> </span><span class="s">on</span><span class="nv"> </span><span class="s">"</span>
    
<span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">ConntrackTableNearlyFull</span>
  <span class="na">expr</span><span class="pi">:</span> <span class="s">(node_nf_conntrack_entries / node_nf_conntrack_entries_limit) &gt; </span><span class="m">0.95</span>
  <span class="na">for</span><span class="pi">:</span> <span class="s">1m</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">severity</span><span class="pi">:</span> <span class="s">critical</span>
  <span class="na">annotations</span><span class="pi">:</span>
    <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Conntrack</span><span class="nv"> </span><span class="s">table</span><span class="nv"> </span><span class="s">95%</span><span class="nv"> </span><span class="s">full</span><span class="nv"> </span><span class="s">on</span><span class="nv"> </span><span class="s">"</span>
</code></pre></div></div>

<h4 id="623-pod-and-container-monitoring">6.2.3 Pod and Container Monitoring</h4>

<p><strong>Container resource monitoring:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Container resource alerts</span>
<span class="na">groups</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">container-resources</span>
  <span class="na">rules</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">ContainerHighCPUUsage</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">(sum(rate(container_cpu_usage_seconds_total{container!=""}[5m])) by (namespace, pod, container) / sum(container_spec_cpu_quota{container!=""}/container_spec_cpu_period{container!=""}) by (namespace, pod, container)) &gt; </span><span class="m">0.9</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">5m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">warning</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Container</span><span class="nv"> </span><span class="s">//</span><span class="nv"> </span><span class="s">high</span><span class="nv"> </span><span class="s">CPU</span><span class="nv"> </span><span class="s">usage"</span>
      
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">ContainerHighMemoryUsage</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">(container_memory_working_set_bytes{container!=""} / container_spec_memory_limit_bytes{container!=""}) &gt; </span><span class="m">0.9</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">5m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">warning</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Container</span><span class="nv"> </span><span class="s">//</span><span class="nv"> </span><span class="s">high</span><span class="nv"> </span><span class="s">memory</span><span class="nv"> </span><span class="s">usage"</span>
      
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">ContainerOOMKilled</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">increase(kube_pod_container_status_restarts_total[5m]) &gt; 0 and on(namespace, pod, container) kube_pod_container_status_last_terminated_reason{reason="OOMKilled"} == </span><span class="m">1</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">0m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">warning</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Container</span><span class="nv"> </span><span class="s">//</span><span class="nv"> </span><span class="s">was</span><span class="nv"> </span><span class="s">OOM</span><span class="nv"> </span><span class="s">killed"</span>
</code></pre></div></div>

<hr />

<h3 id="63-dns-and-service-discovery-monitoring">6.3 DNS and Service Discovery Monitoring</h3>

<h4 id="631-coredns-performance-monitoring">6.3.1 CoreDNS Performance Monitoring</h4>

<p><strong>CoreDNS metrics collection:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># CoreDNS monitoring</span>
<span class="pi">-</span> <span class="na">job_name</span><span class="pi">:</span> <span class="s1">'</span><span class="s">coredns'</span>
  <span class="na">kubernetes_sd_configs</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">role</span><span class="pi">:</span> <span class="s">endpoints</span>
  <span class="na">relabel_configs</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">source_labels</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">__meta_kubernetes_service_name</span><span class="pi">]</span>
    <span class="na">action</span><span class="pi">:</span> <span class="s">keep</span>
    <span class="na">regex</span><span class="pi">:</span> <span class="s">kube-dns</span>
  <span class="pi">-</span> <span class="na">source_labels</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">__meta_kubernetes_endpoint_port_name</span><span class="pi">]</span>
    <span class="na">action</span><span class="pi">:</span> <span class="s">keep</span>
    <span class="na">regex</span><span class="pi">:</span> <span class="s">metrics</span>
</code></pre></div></div>

<p><strong>CoreDNS alerts:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">groups</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">coredns</span>
  <span class="na">rules</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">CoreDNSDown</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">up{job="coredns"} == </span><span class="m">0</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">1m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">critical</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">CoreDNS</span><span class="nv"> </span><span class="s">is</span><span class="nv"> </span><span class="s">down"</span>
      
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">CoreDNSHighLatency</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">histogram_quantile(0.99, sum(rate(coredns_dns_request_duration_seconds_bucket[5m])) by (le)) &gt; </span><span class="m">0.1</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">5m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">warning</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">CoreDNS</span><span class="nv"> </span><span class="s">high</span><span class="nv"> </span><span class="s">latency</span><span class="nv"> </span><span class="s">(99th</span><span class="nv"> </span><span class="s">percentile</span><span class="nv"> </span><span class="s">&gt;</span><span class="nv"> </span><span class="s">100ms)"</span>
      
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">CoreDNSHighErrorRate</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">sum(rate(coredns_dns_responses_total{rcode!="NOERROR"}[5m])) / sum(rate(coredns_dns_responses_total[5m])) &gt; </span><span class="m">0.05</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">5m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">warning</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">CoreDNS</span><span class="nv"> </span><span class="s">error</span><span class="nv"> </span><span class="s">rate</span><span class="nv"> </span><span class="s">&gt;</span><span class="nv"> </span><span class="s">5%"</span>
</code></pre></div></div>

<h4 id="632-service-discovery-health-checks">6.3.2 Service Discovery Health Checks</h4>

<p><strong>Synthetic DNS monitoring:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Pod</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">dns-monitor</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">dns-monitor</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">containers</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">monitor</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">busybox</span>
    <span class="na">command</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">/bin/sh</span>
    <span class="pi">-</span> <span class="s">-c</span>
    <span class="pi">-</span> <span class="pi">|</span>
      <span class="s">while true; do</span>
        <span class="s"># Test internal DNS</span>
        <span class="s">if nslookup kubernetes.default.svc.cluster.local; then</span>
          <span class="s">echo "internal_dns_success 1" | nc -u -w1 prometheus-pushgateway 9091</span>
        <span class="s">else</span>
          <span class="s">echo "internal_dns_success 0" | nc -u -w1 prometheus-pushgateway 9091</span>
        <span class="s">fi</span>
      <span class="no">  </span>
        <span class="s"># Test external DNS</span>
        <span class="s">if nslookup google.com; then</span>
          <span class="s">echo "external_dns_success 1" | nc -u -w1 prometheus-pushgateway 9091</span>
        <span class="s">else</span>
          <span class="s">echo "external_dns_success 0" | nc -u -w1 prometheus-pushgateway 9091</span>
        <span class="s">fi</span>
      <span class="no">  </span>
        <span class="s">sleep 30</span>
      <span class="s">done</span>
</code></pre></div></div>

<hr />

<h3 id="64-aws-integration-monitoring">6.4 AWS Integration Monitoring</h3>

<h4 id="641-load-balancer-monitoring">6.4.1 Load Balancer Monitoring</h4>

<p><strong>ALB/NLB CloudWatch metrics:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># CloudWatch exporter configuration for ALB metrics</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ConfigMap</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">cloudwatch-exporter-config</span>
<span class="na">data</span><span class="pi">:</span>
  <span class="na">config.yml</span><span class="pi">:</span> <span class="pi">|</span>
    <span class="s">region: us-west-2</span>
    <span class="s">metrics:</span>
    <span class="s"># ALB metrics</span>
    <span class="s">- aws_namespace: AWS/ApplicationELB</span>
      <span class="s">aws_metric_name: TargetResponseTime</span>
      <span class="s">aws_dimensions: [LoadBalancer]</span>
      <span class="s">aws_statistics: [Average]</span>
    <span class="no">  </span>
    <span class="s">- aws_namespace: AWS/ApplicationELB</span>
      <span class="s">aws_metric_name: HTTPCode_Target_5XX_Count</span>
      <span class="s">aws_dimensions: [LoadBalancer]</span>
      <span class="s">aws_statistics: [Sum]</span>
    <span class="no">  </span>
    <span class="s"># NLB metrics  </span>
    <span class="s">- aws_namespace: AWS/NetworkELB</span>
      <span class="s">aws_metric_name: TCP_ELB_Reset_Count</span>
      <span class="s">aws_dimensions: [LoadBalancer]</span>
      <span class="s">aws_statistics: [Sum]</span>
    <span class="no">  </span>
    <span class="s"># NAT Gateway metrics</span>
    <span class="s">- aws_namespace: AWS/NatGateway</span>
      <span class="s">aws_metric_name: IdleTimeoutCount</span>
      <span class="s">aws_dimensions: [NatGatewayId]</span>
      <span class="s">aws_statistics: [Sum]</span>
</code></pre></div></div>

<p><strong>Load balancer alerts:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">groups</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">aws-loadbalancer</span>
  <span class="na">rules</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">ALBHighLatency</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">aws_applicationelb_target_response_time_average &gt; </span><span class="m">1</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">5m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">warning</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ALB</span><span class="nv">  </span><span class="s">high</span><span class="nv"> </span><span class="s">response</span><span class="nv"> </span><span class="s">time"</span>
      
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">ALBHighErrorRate</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">rate(aws_applicationelb_httpcode_target_5_xx_count_sum[5m]) &gt; </span><span class="m">10</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">5m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">critical</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">ALB</span><span class="nv">  </span><span class="s">high</span><span class="nv"> </span><span class="s">5xx</span><span class="nv"> </span><span class="s">error</span><span class="nv"> </span><span class="s">rate"</span>
      
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">NLBConnectionResets</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">rate(aws_networkelb_tcp_elb_reset_count_sum[5m]) &gt; </span><span class="m">5</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">5m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">warning</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">NLB</span><span class="nv">  </span><span class="s">connection</span><span class="nv"> </span><span class="s">resets</span><span class="nv"> </span><span class="s">detected"</span>
</code></pre></div></div>

<h4 id="642-ebs-and-storage-monitoring">6.4.2 EBS and Storage Monitoring</h4>

<p><strong>EBS performance metrics:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># EBS CloudWatch metrics</span>
<span class="pi">-</span> <span class="na">aws_namespace</span><span class="pi">:</span> <span class="s">AWS/EBS</span>
  <span class="na">aws_metric_name</span><span class="pi">:</span> <span class="s">VolumeReadOps</span>
  <span class="na">aws_dimensions</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">VolumeId</span><span class="pi">]</span>
  <span class="na">aws_statistics</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">Sum</span><span class="pi">]</span>
  
<span class="pi">-</span> <span class="na">aws_namespace</span><span class="pi">:</span> <span class="s">AWS/EBS</span>
  <span class="na">aws_metric_name</span><span class="pi">:</span> <span class="s">VolumeWriteOps</span>
  <span class="na">aws_dimensions</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">VolumeId</span><span class="pi">]</span>
  <span class="na">aws_statistics</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">Sum</span><span class="pi">]</span>
  
<span class="pi">-</span> <span class="na">aws_namespace</span><span class="pi">:</span> <span class="s">AWS/EBS</span>
  <span class="na">aws_metric_name</span><span class="pi">:</span> <span class="s">VolumeTotalReadTime</span>
  <span class="na">aws_dimensions</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">VolumeId</span><span class="pi">]</span>
  <span class="na">aws_statistics</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">Sum</span><span class="pi">]</span>
  
<span class="pi">-</span> <span class="na">aws_namespace</span><span class="pi">:</span> <span class="s">AWS/EBS</span>
  <span class="na">aws_metric_name</span><span class="pi">:</span> <span class="s">BurstBalance</span>
  <span class="na">aws_dimensions</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">VolumeId</span><span class="pi">]</span>
  <span class="na">aws_statistics</span><span class="pi">:</span> <span class="pi">[</span><span class="nv">Average</span><span class="pi">]</span>
</code></pre></div></div>

<p><strong>Storage alerts:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">groups</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">ebs-storage</span>
  <span class="na">rules</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">EBSBurstBalanceLow</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">aws_ebs_burst_balance_average &lt; </span><span class="m">20</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">10m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">warning</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">EBS</span><span class="nv"> </span><span class="s">volume</span><span class="nv">  </span><span class="s">burst</span><span class="nv"> </span><span class="s">balance</span><span class="nv"> </span><span class="s">low"</span>
      
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">EBSHighLatency</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">(aws_ebs_volume_total_read_time_sum + aws_ebs_volume_total_write_time_sum) / (aws_ebs_volume_read_ops_sum + aws_ebs_volume_write_ops_sum) &gt; </span><span class="m">0.1</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">5m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">warning</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">EBS</span><span class="nv"> </span><span class="s">volume</span><span class="nv">  </span><span class="s">high</span><span class="nv"> </span><span class="s">latency"</span>
</code></pre></div></div>

<hr />

<h3 id="65-application-performance-monitoring">6.5 Application Performance Monitoring</h3>

<h4 id="651-golden-signals-for-kubernetes-workloads">6.5.1 Golden Signals for Kubernetes Workloads</h4>

<p><strong>The four golden signals adapted for Kubernetes:</strong></p>

<ol>
  <li><strong>Latency</strong> - Request duration</li>
  <li><strong>Traffic</strong> - Request rate</li>
  <li><strong>Errors</strong> - Error rate</li>
  <li><strong>Saturation</strong> - Resource utilization</li>
</ol>

<p><strong>Application metrics instrumentation:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Example application with Prometheus metrics</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">web-app</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">annotations</span><span class="pi">:</span>
        <span class="na">prometheus.io/scrape</span><span class="pi">:</span> <span class="s2">"</span><span class="s">true"</span>
        <span class="na">prometheus.io/port</span><span class="pi">:</span> <span class="s2">"</span><span class="s">8080"</span>
        <span class="na">prometheus.io/path</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/metrics"</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">app</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">myapp:latest</span>
        <span class="na">ports</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">8080</span>
        <span class="na">env</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">METRICS_ENABLED</span>
          <span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">true"</span>
</code></pre></div></div>

<p><strong>Golden signals alerts:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">groups</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">golden-signals</span>
  <span class="na">rules</span><span class="pi">:</span>
  <span class="c1"># Latency</span>
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">HighRequestLatency</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service)) &gt; </span><span class="m">0.5</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">5m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">warning</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">High</span><span class="nv"> </span><span class="s">request</span><span class="nv"> </span><span class="s">latency</span><span class="nv"> </span><span class="s">for</span><span class="nv"> </span><span class="s">"</span>
      
  <span class="c1"># Traffic</span>
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">LowTrafficVolume</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">sum(rate(http_requests_total[5m])) by (service) &lt; </span><span class="m">1</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">10m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">warning</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Low</span><span class="nv"> </span><span class="s">traffic</span><span class="nv"> </span><span class="s">volume</span><span class="nv"> </span><span class="s">for</span><span class="nv"> </span><span class="s">"</span>
      
  <span class="c1"># Errors</span>
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">HighErrorRate</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">sum(rate(http_requests_total{status=~"5.."}[5m])) by (service) / sum(rate(http_requests_total[5m])) by (service) &gt; </span><span class="m">0.05</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">5m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">critical</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">High</span><span class="nv"> </span><span class="s">error</span><span class="nv"> </span><span class="s">rate</span><span class="nv"> </span><span class="s">for</span><span class="nv"> </span><span class="s">"</span>
      
  <span class="c1"># Saturation</span>
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">HighCPUSaturation</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">avg(rate(container_cpu_usage_seconds_total[5m])) by (namespace, pod) &gt; </span><span class="m">0.8</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">5m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">warning</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">High</span><span class="nv"> </span><span class="s">CPU</span><span class="nv"> </span><span class="s">saturation</span><span class="nv"> </span><span class="s">for</span><span class="nv"> </span><span class="s">/"</span>
</code></pre></div></div>

<h4 id="652-distributed-tracing-integration">6.5.2 Distributed Tracing Integration</h4>

<p><strong>Jaeger deployment for EKS:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">jaeger</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">jaeger</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">jaegertracing/all-in-one:latest</span>
        <span class="na">env</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">COLLECTOR_ZIPKIN_HTTP_PORT</span>
          <span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">9411"</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">SPAN_STORAGE_TYPE</span>
          <span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">elasticsearch"</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">ES_SERVER_URLS</span>
          <span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">http://elasticsearch:9200"</span>
        <span class="na">ports</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">16686</span>  <span class="c1"># UI</span>
        <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">14268</span>  <span class="c1"># HTTP collector</span>
        <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">6831</span>   <span class="c1"># UDP agent</span>
</code></pre></div></div>

<hr />

<h3 id="66-log-aggregation-and-analysis">6.6 Log Aggregation and Analysis</h3>

<h4 id="661-centralized-logging-architecture">6.6.1 Centralized Logging Architecture</h4>

<p><strong>Fluent Bit for log collection:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">DaemonSet</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">fluent-bit</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">fluent-bit</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">fluent/fluent-bit:2.1.10</span>
        <span class="na">volumeMounts</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">varlog</span>
          <span class="na">mountPath</span><span class="pi">:</span> <span class="s">/var/log</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">varlibdockercontainers</span>
          <span class="na">mountPath</span><span class="pi">:</span> <span class="s">/var/lib/docker/containers</span>
          <span class="na">readOnly</span><span class="pi">:</span> <span class="no">true</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">fluent-bit-config</span>
          <span class="na">mountPath</span><span class="pi">:</span> <span class="s">/fluent-bit/etc/</span>
        <span class="na">env</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">FLUENT_ELASTICSEARCH_HOST</span>
          <span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">elasticsearch"</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">FLUENT_ELASTICSEARCH_PORT</span>
          <span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">9200"</span>
      <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">varlog</span>
        <span class="na">hostPath</span><span class="pi">:</span>
          <span class="na">path</span><span class="pi">:</span> <span class="s">/var/log</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">varlibdockercontainers</span>
        <span class="na">hostPath</span><span class="pi">:</span>
          <span class="na">path</span><span class="pi">:</span> <span class="s">/var/lib/docker/containers</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">fluent-bit-config</span>
        <span class="na">configMap</span><span class="pi">:</span>
          <span class="na">name</span><span class="pi">:</span> <span class="s">fluent-bit-config</span>
</code></pre></div></div>

<p><strong>Fluent Bit configuration:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ConfigMap</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">fluent-bit-config</span>
<span class="na">data</span><span class="pi">:</span>
  <span class="na">fluent-bit.conf</span><span class="pi">:</span> <span class="pi">|</span>
    <span class="s">[SERVICE]</span>
        <span class="s">Flush         1</span>
        <span class="s">Log_Level     info</span>
        <span class="s">Daemon        off</span>
        <span class="s">Parsers_File  parsers.conf</span>
        <span class="s">HTTP_Server   On</span>
        <span class="s">HTTP_Listen   0.0.0.0</span>
        <span class="s">HTTP_Port     2020</span>

    <span class="s">[INPUT]</span>
        <span class="s">Name              tail</span>
        <span class="s">Path              /var/log/containers/*.log</span>
        <span class="s">Parser            cri</span>
        <span class="s">Tag               kube.*</span>
        <span class="s">Refresh_Interval  5</span>
        <span class="s">Mem_Buf_Limit     50MB</span>
        <span class="s">Skip_Long_Lines   On</span>

    <span class="s">[FILTER]</span>
        <span class="s">Name                kubernetes</span>
        <span class="s">Match               kube.*</span>
        <span class="s">Kube_URL            https://kubernetes.default.svc:443</span>
        <span class="s">Kube_CA_File        /var/run/secrets/kubernetes.io/serviceaccount/ca.crt</span>
        <span class="s">Kube_Token_File     /var/run/secrets/kubernetes.io/serviceaccount/token</span>
        <span class="s">Merge_Log           On</span>
        <span class="s">K8S-Logging.Parser  On</span>
        <span class="s">K8S-Logging.Exclude Off</span>

    <span class="s">[OUTPUT]</span>
        <span class="s">Name  es</span>
        <span class="s">Match *</span>
        <span class="s">Host  ${FLUENT_ELASTICSEARCH_HOST}</span>
        <span class="s">Port  ${FLUENT_ELASTICSEARCH_PORT}</span>
        <span class="s">Index fluent-bit</span>
        <span class="s">Type  _doc</span>
</code></pre></div></div>

<h4 id="662-log-based-alerting">6.6.2 Log-Based Alerting</h4>

<p><strong>Critical log patterns to monitor:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Log-based alerts using Loki/Promtail</span>
<span class="na">groups</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">log-alerts</span>
  <span class="na">rules</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">PodCrashLooping</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">sum(rate({namespace="production"} |= "CrashLoopBackOff"[5m])) &gt; </span><span class="m">0</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">2m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">warning</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Pod</span><span class="nv"> </span><span class="s">crash</span><span class="nv"> </span><span class="s">looping</span><span class="nv"> </span><span class="s">detected</span><span class="nv"> </span><span class="s">in</span><span class="nv"> </span><span class="s">production"</span>
      
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">OutOfMemoryKills</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">sum(rate({namespace="production"} |= "OOMKilled"[5m])) &gt; </span><span class="m">0</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">1m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">critical</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">OOM</span><span class="nv"> </span><span class="s">kills</span><span class="nv"> </span><span class="s">detected</span><span class="nv"> </span><span class="s">in</span><span class="nv"> </span><span class="s">production"</span>
      
  <span class="pi">-</span> <span class="na">alert</span><span class="pi">:</span> <span class="s">ImagePullErrors</span>
    <span class="na">expr</span><span class="pi">:</span> <span class="s">sum(rate({namespace="production"} |= "ImagePullBackOff"[5m])) &gt; </span><span class="m">0</span>
    <span class="na">for</span><span class="pi">:</span> <span class="s">5m</span>
    <span class="na">labels</span><span class="pi">:</span>
      <span class="na">severity</span><span class="pi">:</span> <span class="s">warning</span>
    <span class="na">annotations</span><span class="pi">:</span>
      <span class="na">summary</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Image</span><span class="nv"> </span><span class="s">pull</span><span class="nv"> </span><span class="s">errors</span><span class="nv"> </span><span class="s">in</span><span class="nv"> </span><span class="s">production"</span>
</code></pre></div></div>

<hr />

<h3 id="67-incident-response-dashboards">6.7 Incident Response Dashboards</h3>

<h4 id="671-eks-incident-response-dashboard">6.7.1 EKS Incident Response Dashboard</h4>

<p><strong>Critical metrics for incident response:</strong></p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"dashboard"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"EKS Incident Response"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"panels"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Cluster Health Overview"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"targets"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
          </span><span class="p">{</span><span class="w">
            </span><span class="nl">"expr"</span><span class="p">:</span><span class="w"> </span><span class="s2">"up{job=</span><span class="se">\"</span><span class="s2">kubernetes-apiservers</span><span class="se">\"</span><span class="s2">}"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"legendFormat"</span><span class="p">:</span><span class="w"> </span><span class="s2">"API Server"</span><span class="w">
          </span><span class="p">},</span><span class="w">
          </span><span class="p">{</span><span class="w">
            </span><span class="nl">"expr"</span><span class="p">:</span><span class="w"> </span><span class="s2">"up{job=</span><span class="se">\"</span><span class="s2">coredns</span><span class="se">\"</span><span class="s2">}"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"legendFormat"</span><span class="p">:</span><span class="w"> </span><span class="s2">"CoreDNS"</span><span class="w">
          </span><span class="p">},</span><span class="w">
          </span><span class="p">{</span><span class="w">
            </span><span class="nl">"expr"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kube_node_status_condition{condition=</span><span class="se">\"</span><span class="s2">Ready</span><span class="se">\"</span><span class="s2">,status=</span><span class="se">\"</span><span class="s2">true</span><span class="se">\"</span><span class="s2">}"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"legendFormat"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Ready Nodes"</span><span class="w">
          </span><span class="p">}</span><span class="w">
        </span><span class="p">]</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Pod Status Distribution"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"targets"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
          </span><span class="p">{</span><span class="w">
            </span><span class="nl">"expr"</span><span class="p">:</span><span class="w"> </span><span class="s2">"sum by (phase) (kube_pod_status_phase)"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"legendFormat"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="w">
          </span><span class="p">}</span><span class="w">
        </span><span class="p">]</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Resource Utilization"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"targets"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
          </span><span class="p">{</span><span class="w">
            </span><span class="nl">"expr"</span><span class="p">:</span><span class="w"> </span><span class="s2">"100 - (avg(irate(node_cpu_seconds_total{mode=</span><span class="se">\"</span><span class="s2">idle</span><span class="se">\"</span><span class="s2">}[5m])) * 100)"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"legendFormat"</span><span class="p">:</span><span class="w"> </span><span class="s2">"CPU Usage %"</span><span class="w">
          </span><span class="p">},</span><span class="w">
          </span><span class="p">{</span><span class="w">
            </span><span class="nl">"expr"</span><span class="p">:</span><span class="w"> </span><span class="s2">"(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"legendFormat"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Memory Usage %"</span><span class="w">
          </span><span class="p">}</span><span class="w">
        </span><span class="p">]</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Network Issues"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"targets"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
          </span><span class="p">{</span><span class="w">
            </span><span class="nl">"expr"</span><span class="p">:</span><span class="w"> </span><span class="s2">"rate(node_ethtool_conntrack_allowance_exceeded[5m])"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"legendFormat"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Conntrack Exceeded - "</span><span class="w">
          </span><span class="p">},</span><span class="w">
          </span><span class="p">{</span><span class="w">
            </span><span class="nl">"expr"</span><span class="p">:</span><span class="w"> </span><span class="s2">"rate(node_ethtool_linklocal_allowance_exceeded[5m])"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"legendFormat"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Link-local Exceeded - "</span><span class="w">
          </span><span class="p">}</span><span class="w">
        </span><span class="p">]</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">]</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h4 id="672-application-health-dashboard">6.7.2 Application Health Dashboard</h4>

<p><strong>Service-level indicators:</strong></p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"dashboard"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Application Health"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"panels"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Request Rate"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"targets"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
          </span><span class="p">{</span><span class="w">
            </span><span class="nl">"expr"</span><span class="p">:</span><span class="w"> </span><span class="s2">"sum(rate(http_requests_total[5m])) by (service)"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"legendFormat"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="w">
          </span><span class="p">}</span><span class="w">
        </span><span class="p">]</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Error Rate"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"targets"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
          </span><span class="p">{</span><span class="w">
            </span><span class="nl">"expr"</span><span class="p">:</span><span class="w"> </span><span class="s2">"sum(rate(http_requests_total{status=~</span><span class="se">\"</span><span class="s2">5..</span><span class="se">\"</span><span class="s2">}[5m])) by (service) / sum(rate(http_requests_total[5m])) by (service)"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"legendFormat"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="w">
          </span><span class="p">}</span><span class="w">
        </span><span class="p">]</span><span class="w">
      </span><span class="p">},</span><span class="w">
      </span><span class="p">{</span><span class="w">
        </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Response Time"</span><span class="p">,</span><span class="w">
        </span><span class="nl">"targets"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
          </span><span class="p">{</span><span class="w">
            </span><span class="nl">"expr"</span><span class="p">:</span><span class="w"> </span><span class="s2">"histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service))"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"legendFormat"</span><span class="p">:</span><span class="w"> </span><span class="s2">"95th percentile - "</span><span class="w">
          </span><span class="p">},</span><span class="w">
          </span><span class="p">{</span><span class="w">
            </span><span class="nl">"expr"</span><span class="p">:</span><span class="w"> </span><span class="s2">"histogram_quantile(0.50, sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service))"</span><span class="p">,</span><span class="w">
            </span><span class="nl">"legendFormat"</span><span class="p">:</span><span class="w"> </span><span class="s2">"50th percentile - "</span><span class="w">
          </span><span class="p">}</span><span class="w">
        </span><span class="p">]</span><span class="w">
      </span><span class="p">}</span><span class="w">
    </span><span class="p">]</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<hr />

<h3 id="68-monitoring-troubleshooting-runbooks">6.8 Monitoring Troubleshooting Runbooks</h3>

<h4 id="681-metrics-missing-runbook">6.8.1 “Metrics missing” Runbook</h4>

<p><strong>Symptoms:</strong> Dashboards show no data, alerts not firing</p>

<p><strong>Step 1: Check Prometheus targets</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Access Prometheus UI</span>
kubectl port-forward svc/prometheus 9090:9090

<span class="c"># Check targets status at http://localhost:9090/targets</span>
<span class="c"># Look for targets in "DOWN" state</span>
</code></pre></div></div>

<p><strong>Step 2: Verify service discovery</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check if services have correct annotations</span>
kubectl get svc <span class="nt">-o</span> yaml | <span class="nb">grep</span> <span class="nt">-A</span> 5 <span class="nt">-B</span> 5 prometheus.io

<span class="c"># Check if pods are exposing metrics</span>
kubectl <span class="nb">exec</span> &lt;pod-name&gt; <span class="nt">--</span> curl localhost:8080/metrics
</code></pre></div></div>

<p><strong>Step 3: Check network connectivity</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Test connectivity from Prometheus pod</span>
kubectl <span class="nb">exec </span>prometheus-pod <span class="nt">--</span> nc <span class="nt">-zv</span> &lt;target-service&gt; &lt;port&gt;
</code></pre></div></div>

<h4 id="682-alerts-not-firing-runbook">6.8.2 “Alerts not firing” Runbook</h4>

<p><strong>Symptoms:</strong> Known issues not triggering alerts</p>

<p><strong>Step 1: Check alert rules</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Access Prometheus rules page</span>
<span class="c"># http://localhost:9090/rules</span>

<span class="c"># Verify rule syntax</span>
promtool check rules /path/to/rules.yml
</code></pre></div></div>

<p><strong>Step 2: Check Alertmanager</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl logs deployment/alertmanager

<span class="c"># Check Alertmanager config</span>
kubectl get configmap alertmanager-config <span class="nt">-o</span> yaml
</code></pre></div></div>

<p><strong>Step 3: Test alert conditions</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Query the alert condition directly in Prometheus</span>
<span class="c"># Example: up{job="kubernetes-apiservers"} == 0</span>
</code></pre></div></div>

<hr />

<p>This observability section provides the foundation for effective incident response in EKS. The focus is on metrics and alerts that actually help during outages, not just operational dashboards. The key is building observability that distinguishes between application issues and platform issues quickly.</p>
<h2 id="7-scaling-and-performance">7. Scaling and performance</h2>

<p>Scaling failures show up as “the app is slow” when the real issue is resource contention, autoscaler misconfiguration, or hitting AWS service limits. What follows: scaling patterns that actually work under load, and the ways they break during traffic spikes.</p>

<hr />

<h3 id="71-horizontal-pod-autoscaler-hpa-operational-reality">7.1 Horizontal Pod Autoscaler (HPA) Operational Reality</h3>

<h4 id="711-hpa-architecture-and-dependencies">7.1.1 HPA Architecture and Dependencies</h4>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Metrics Server] → [HPA Controller] → [Deployment/ReplicaSet] → [Pods]
     ↑                    ↑
[kubelet cAdvisor]   [Custom Metrics API]
</code></pre></div></div>

<p><strong>Critical dependencies:</strong></p>
<ul>
  <li><strong>Metrics Server</strong> - Must be healthy for CPU/memory-based scaling</li>
  <li><strong>Resource requests</strong> - HPA cannot function without them</li>
  <li><strong>Custom metrics</strong> - For advanced scaling (queue depth, response time)</li>
  <li><strong>Node capacity</strong> - Scaling is useless if nodes can’t accommodate new pods</li>
</ul>

<h4 id="712-common-hpa-failure-modes">7.1.2 Common HPA Failure Modes</h4>

<p><strong>A) HPA shows “unknown” metrics</strong></p>

<p><strong>Symptoms:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get hpa
<span class="c"># NAME     REFERENCE          TARGETS         MINPODS   MAXPODS   REPLICAS</span>
<span class="c"># web-app  Deployment/web-app &lt;unknown&gt;/80%   2         10        2</span>
</code></pre></div></div>

<p><strong>Root causes:</strong></p>
<ul>
  <li>Metrics server down or unhealthy</li>
  <li>Pods missing resource requests</li>
  <li>Metrics server can’t reach kubelet</li>
</ul>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check metrics server</span>
kubectl <span class="nt">-n</span> kube-system get pods <span class="nt">-l</span> k8s-app<span class="o">=</span>metrics-server
kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> k8s-app<span class="o">=</span>metrics-server

<span class="c"># Check if metrics are available</span>
kubectl top pods
kubectl top nodes

<span class="c"># Check pod resource requests</span>
kubectl describe deployment web-app | <span class="nb">grep</span> <span class="nt">-A</span> 10 <span class="s2">"Requests:"</span>
</code></pre></div></div>

<p><strong>B) HPA scaling thrashing (rapid scale up/down)</strong></p>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Replica count oscillates rapidly</li>
  <li>Pods constantly being created and destroyed</li>
  <li>Performance degrades due to churn</li>
</ul>

<p><strong>Root causes:</strong></p>
<ul>
  <li>Scaling thresholds too sensitive</li>
  <li>Resource requests don’t match actual usage</li>
  <li>Missing stabilization windows</li>
</ul>

<p><strong>Fix with stabilization:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">autoscaling/v2</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">HorizontalPodAutoscaler</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">web-app-hpa</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">scaleTargetRef</span><span class="pi">:</span>
    <span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
    <span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">web-app</span>
  <span class="na">minReplicas</span><span class="pi">:</span> <span class="m">2</span>
  <span class="na">maxReplicas</span><span class="pi">:</span> <span class="m">50</span>
  <span class="na">metrics</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">type</span><span class="pi">:</span> <span class="s">Resource</span>
    <span class="na">resource</span><span class="pi">:</span>
      <span class="na">name</span><span class="pi">:</span> <span class="s">cpu</span>
      <span class="na">target</span><span class="pi">:</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">Utilization</span>
        <span class="na">averageUtilization</span><span class="pi">:</span> <span class="m">70</span>
  <span class="na">behavior</span><span class="pi">:</span>
    <span class="na">scaleUp</span><span class="pi">:</span>
      <span class="na">stabilizationWindowSeconds</span><span class="pi">:</span> <span class="m">60</span>    <span class="c1"># Wait 60s before scaling up again</span>
      <span class="na">policies</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">type</span><span class="pi">:</span> <span class="s">Percent</span>
        <span class="na">value</span><span class="pi">:</span> <span class="m">100</span>                      <span class="c1"># Max 100% increase per step</span>
        <span class="na">periodSeconds</span><span class="pi">:</span> <span class="m">60</span>
    <span class="na">scaleDown</span><span class="pi">:</span>
      <span class="na">stabilizationWindowSeconds</span><span class="pi">:</span> <span class="m">300</span>   <span class="c1"># Wait 5min before scaling down</span>
      <span class="na">policies</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">type</span><span class="pi">:</span> <span class="s">Percent</span>
        <span class="na">value</span><span class="pi">:</span> <span class="m">10</span>                       <span class="c1"># Max 10% decrease per step</span>
        <span class="na">periodSeconds</span><span class="pi">:</span> <span class="m">60</span>
</code></pre></div></div>

<h4 id="713-custom-metrics-scaling">7.1.3 Custom Metrics Scaling</h4>

<p><strong>Scaling based on queue depth:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">autoscaling/v2</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">HorizontalPodAutoscaler</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">worker-hpa</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">scaleTargetRef</span><span class="pi">:</span>
    <span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
    <span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">worker</span>
  <span class="na">minReplicas</span><span class="pi">:</span> <span class="m">1</span>
  <span class="na">maxReplicas</span><span class="pi">:</span> <span class="m">20</span>
  <span class="na">metrics</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">type</span><span class="pi">:</span> <span class="s">External</span>
    <span class="na">external</span><span class="pi">:</span>
      <span class="na">metric</span><span class="pi">:</span>
        <span class="na">name</span><span class="pi">:</span> <span class="s">sqs_queue_depth</span>
        <span class="na">selector</span><span class="pi">:</span>
          <span class="na">matchLabels</span><span class="pi">:</span>
            <span class="na">queue_name</span><span class="pi">:</span> <span class="s2">"</span><span class="s">work-queue"</span>
      <span class="na">target</span><span class="pi">:</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">AverageValue</span>
        <span class="na">averageValue</span><span class="pi">:</span> <span class="s2">"</span><span class="s">5"</span>  <span class="c1"># Scale up when queue depth &gt; 5 per pod</span>
</code></pre></div></div>

<p><strong>Prometheus adapter configuration:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ConfigMap</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">adapter-config</span>
<span class="na">data</span><span class="pi">:</span>
  <span class="na">config.yaml</span><span class="pi">:</span> <span class="pi">|</span>
    <span class="s">rules:</span>
    <span class="s">- seriesQuery: 'sqs_queue_depth{queue_name!=""}'</span>
      <span class="s">resources:</span>
        <span class="s">overrides:</span>
          <span class="s">queue_name: {resource: "queue"}</span>
      <span class="s">name:</span>
        <span class="s">matches: "^sqs_queue_depth"</span>
        <span class="s">as: "sqs_queue_depth"</span>
      <span class="s">metricsQuery: 'avg(sqs_queue_depth{queue_name="&lt;&lt;.LabelMatchers&gt;&gt;"})'</span>
</code></pre></div></div>

<hr />

<h3 id="72-cluster-autoscaler-ca-operational-patterns">7.2 Cluster Autoscaler (CA) Operational Patterns</h3>

<h4 id="721-cluster-autoscaler-configuration">7.2.1 Cluster Autoscaler Configuration</h4>

<p><strong>Production CA configuration:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">cluster-autoscaler</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">kube-system</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">image</span><span class="pi">:</span> <span class="s">k8s.gcr.io/autoscaling/cluster-autoscaler:v1.27.3</span>
        <span class="na">name</span><span class="pi">:</span> <span class="s">cluster-autoscaler</span>
        <span class="na">command</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="s">./cluster-autoscaler</span>
        <span class="pi">-</span> <span class="s">--v=4</span>
        <span class="pi">-</span> <span class="s">--stderrthreshold=info</span>
        <span class="pi">-</span> <span class="s">--cloud-provider=aws</span>
        <span class="pi">-</span> <span class="s">--skip-nodes-with-local-storage=false</span>
        <span class="pi">-</span> <span class="s">--expander=least-waste</span>
        <span class="pi">-</span> <span class="s">--node-group-auto-discovery=asg:tag=k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/eks-cluster-name</span>
        <span class="pi">-</span> <span class="s">--balance-similar-node-groups</span>
        <span class="pi">-</span> <span class="s">--scale-down-enabled=true</span>
        <span class="pi">-</span> <span class="s">--scale-down-delay-after-add=10m</span>
        <span class="pi">-</span> <span class="s">--scale-down-unneeded-time=10m</span>
        <span class="pi">-</span> <span class="s">--scale-down-utilization-threshold=0.5</span>
        <span class="pi">-</span> <span class="s">--skip-nodes-with-system-pods=false</span>
        <span class="na">env</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">AWS_REGION</span>
          <span class="na">value</span><span class="pi">:</span> <span class="s">us-west-2</span>
        <span class="na">resources</span><span class="pi">:</span>
          <span class="na">limits</span><span class="pi">:</span>
            <span class="na">cpu</span><span class="pi">:</span> <span class="s">100m</span>
            <span class="na">memory</span><span class="pi">:</span> <span class="s">300Mi</span>
          <span class="na">requests</span><span class="pi">:</span>
            <span class="na">cpu</span><span class="pi">:</span> <span class="s">100m</span>
            <span class="na">memory</span><span class="pi">:</span> <span class="s">300Mi</span>
</code></pre></div></div>

<h4 id="722-common-ca-failure-modes">7.2.2 Common CA Failure Modes</h4>

<p><strong>A) Nodes not scaling up despite pending pods</strong></p>

<p><strong>Symptoms:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get pods <span class="nt">-A</span> | <span class="nb">grep </span>Pending
kubectl describe pod &lt;pending-pod&gt;
<span class="c"># Shows: 0/X nodes are available: insufficient cpu/memory</span>
</code></pre></div></div>

<p><strong>Root causes:</strong></p>
<ul>
  <li>Node group max size reached</li>
  <li>AWS service limits (EC2, EIP, etc.)</li>
  <li>Pod resource requests too large for any instance type</li>
  <li>Taints/tolerations preventing scheduling</li>
</ul>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check CA logs</span>
kubectl <span class="nt">-n</span> kube-system logs deployment/cluster-autoscaler

<span class="c"># Check node group limits</span>
aws autoscaling describe-auto-scaling-groups <span class="nt">--auto-scaling-group-names</span> &lt;asg-name&gt;

<span class="c"># Check AWS service limits</span>
aws service-quotas get-service-quota <span class="nt">--service-code</span> ec2 <span class="nt">--quota-code</span> L-1216C47A  <span class="c"># Running On-Demand instances</span>
</code></pre></div></div>

<p><strong>B) Nodes scaling down too aggressively</strong></p>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Pods getting evicted during low traffic</li>
  <li>Services become unavailable during scale-down</li>
  <li>Frequent node churn</li>
</ul>

<p><strong>Tuning scale-down behavior:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Cluster autoscaler configuration</span>
<span class="pi">-</span> <span class="s">--scale-down-delay-after-add=10m</span>      <span class="c1"># Wait 10min after scale-up before considering scale-down</span>
<span class="pi">-</span> <span class="s">--scale-down-unneeded-time=10m</span>        <span class="c1"># Node must be unneeded for 10min before removal</span>
<span class="pi">-</span> <span class="s">--scale-down-utilization-threshold=0.5</span> <span class="c1"># Only remove nodes &lt;50% utilized</span>
<span class="pi">-</span> <span class="s">--max-node-provision-time=15m</span>         <span class="c1"># Give up on node provisioning after 15min</span>
</code></pre></div></div>

<h4 id="723-node-group-strategy">7.2.3 Node Group Strategy</h4>

<p><strong>Multiple node groups for different workload types:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># General purpose workloads</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">eksctl.io/v1alpha5</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ClusterConfig</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">production-cluster</span>
<span class="na">nodeGroups</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">general-purpose</span>
  <span class="na">instanceTypes</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">m5.large"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">m5.xlarge"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">m5.2xlarge"</span><span class="pi">]</span>
  <span class="na">minSize</span><span class="pi">:</span> <span class="m">2</span>
  <span class="na">maxSize</span><span class="pi">:</span> <span class="m">20</span>
  <span class="na">desiredCapacity</span><span class="pi">:</span> <span class="m">5</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">workload-type</span><span class="pi">:</span> <span class="s">general</span>
  <span class="na">tags</span><span class="pi">:</span>
    <span class="na">k8s.io/cluster-autoscaler/enabled</span><span class="pi">:</span> <span class="s2">"</span><span class="s">true"</span>
    <span class="na">k8s.io/cluster-autoscaler/production-cluster</span><span class="pi">:</span> <span class="s2">"</span><span class="s">owned"</span>

<span class="c1"># Compute-intensive workloads</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">compute-optimized</span>
  <span class="na">instanceTypes</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">c5.2xlarge"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">c5.4xlarge"</span><span class="pi">]</span>
  <span class="na">minSize</span><span class="pi">:</span> <span class="m">0</span>
  <span class="na">maxSize</span><span class="pi">:</span> <span class="m">10</span>
  <span class="na">desiredCapacity</span><span class="pi">:</span> <span class="m">0</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">workload-type</span><span class="pi">:</span> <span class="s">compute</span>
  <span class="na">taints</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">key</span><span class="pi">:</span> <span class="s">workload-type</span>
      <span class="na">value</span><span class="pi">:</span> <span class="s">compute</span>
      <span class="na">effect</span><span class="pi">:</span> <span class="s">NoSchedule</span>
  <span class="na">tags</span><span class="pi">:</span>
    <span class="na">k8s.io/cluster-autoscaler/enabled</span><span class="pi">:</span> <span class="s2">"</span><span class="s">true"</span>
    <span class="na">k8s.io/cluster-autoscaler/production-cluster</span><span class="pi">:</span> <span class="s2">"</span><span class="s">owned"</span>

<span class="c1"># Memory-intensive workloads  </span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">memory-optimized</span>
  <span class="na">instanceTypes</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">r5.xlarge"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">r5.2xlarge"</span><span class="pi">]</span>
  <span class="na">minSize</span><span class="pi">:</span> <span class="m">0</span>
  <span class="na">maxSize</span><span class="pi">:</span> <span class="m">5</span>
  <span class="na">desiredCapacity</span><span class="pi">:</span> <span class="m">0</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">workload-type</span><span class="pi">:</span> <span class="s">memory</span>
  <span class="na">taints</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">key</span><span class="pi">:</span> <span class="s">workload-type</span>
      <span class="na">value</span><span class="pi">:</span> <span class="s">memory</span>
      <span class="na">effect</span><span class="pi">:</span> <span class="s">NoSchedule</span>
</code></pre></div></div>

<hr />

<h3 id="73-vertical-pod-autoscaler-vpa-and-right-sizing">7.3 Vertical Pod Autoscaler (VPA) and Right-Sizing</h3>

<h4 id="731-vpa-for-resource-discovery">7.3.1 VPA for Resource Discovery</h4>

<p><strong>VPA in recommendation mode:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">autoscaling.k8s.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">VerticalPodAutoscaler</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">web-app-vpa</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">targetRef</span><span class="pi">:</span>
    <span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
    <span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">web-app</span>
  <span class="na">updatePolicy</span><span class="pi">:</span>
    <span class="na">updateMode</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Off"</span>  <span class="c1"># Only generate recommendations, don't auto-update</span>
  <span class="na">resourcePolicy</span><span class="pi">:</span>
    <span class="na">containerPolicies</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">containerName</span><span class="pi">:</span> <span class="s">web-app</span>
      <span class="na">maxAllowed</span><span class="pi">:</span>
        <span class="na">cpu</span><span class="pi">:</span> <span class="m">2</span>
        <span class="na">memory</span><span class="pi">:</span> <span class="s">4Gi</span>
      <span class="na">minAllowed</span><span class="pi">:</span>
        <span class="na">cpu</span><span class="pi">:</span> <span class="s">100m</span>
        <span class="na">memory</span><span class="pi">:</span> <span class="s">128Mi</span>
</code></pre></div></div>

<p><strong>Getting VPA recommendations:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Get current recommendations</span>
kubectl describe vpa web-app-vpa

<span class="c"># Example output:</span>
<span class="c"># Recommendation:</span>
<span class="c">#   Container Recommendations:</span>
<span class="c">#     Container Name:  web-app</span>
<span class="c">#     Lower Bound:</span>
<span class="c">#       Cpu:     100m</span>
<span class="c">#       Memory:  128Mi</span>
<span class="c">#     Target:</span>
<span class="c">#       Cpu:     250m</span>
<span class="c">#       Memory:  512Mi</span>
<span class="c">#     Uncapped Target:</span>
<span class="c">#       Cpu:     250m</span>
<span class="c">#       Memory:  512Mi</span>
<span class="c">#     Upper Bound:</span>
<span class="c">#       Cpu:     500m</span>
<span class="c">#       Memory:  1Gi</span>
</code></pre></div></div>

<h4 id="732-resource-request-right-sizing">7.3.2 Resource Request Right-Sizing</h4>

<p><strong>Common resource request mistakes:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># BAD: Overprovisioned</span>
<span class="na">resources</span><span class="pi">:</span>
  <span class="na">requests</span><span class="pi">:</span>
    <span class="na">cpu</span><span class="pi">:</span> <span class="s">2000m</span>      <span class="c1"># App only uses 200m</span>
    <span class="na">memory</span><span class="pi">:</span> <span class="s">4Gi</span>     <span class="c1"># App only uses 512Mi</span>
  <span class="na">limits</span><span class="pi">:</span>
    <span class="na">cpu</span><span class="pi">:</span> <span class="s">4000m</span>
    <span class="na">memory</span><span class="pi">:</span> <span class="s">8Gi</span>

<span class="c1"># GOOD: Right-sized based on actual usage</span>
<span class="na">resources</span><span class="pi">:</span>
  <span class="na">requests</span><span class="pi">:</span>
    <span class="na">cpu</span><span class="pi">:</span> <span class="s">250m</span>       <span class="c1"># Based on VPA recommendation + buffer</span>
    <span class="na">memory</span><span class="pi">:</span> <span class="s">512Mi</span>   <span class="c1"># Based on actual usage patterns</span>
  <span class="na">limits</span><span class="pi">:</span>
    <span class="na">cpu</span><span class="pi">:</span> <span class="s">500m</span>       <span class="c1"># 2x requests for burst capacity</span>
    <span class="na">memory</span><span class="pi">:</span> <span class="s">1Gi</span>     <span class="c1"># Hard limit to prevent OOM</span>
</code></pre></div></div>

<p><strong>Resource monitoring for right-sizing:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Monitor actual resource usage</span>
kubectl top pods <span class="nt">--containers</span>

<span class="c"># Get detailed resource usage over time</span>
kubectl <span class="nb">exec </span>prometheus-pod <span class="nt">--</span> promtool query instant <span class="se">\</span>
  <span class="s1">'avg_over_time(container_cpu_usage_seconds_total{container="web-app"}[24h])'</span>
</code></pre></div></div>

<hr />

<h3 id="74-performance-under-load">7.4 Performance Under Load</h3>

<h4 id="741-load-testing-eks-workloads">7.4.1 Load Testing EKS Workloads</h4>

<p><strong>Gradual load testing approach:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Load test job</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">batch/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Job</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">load-test</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">parallelism</span><span class="pi">:</span> <span class="m">10</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">load-test</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">loadimpact/k6:latest</span>
        <span class="na">command</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="s">k6</span>
        <span class="pi">-</span> <span class="s">run</span>
        <span class="pi">-</span> <span class="s">--vus=50</span>
        <span class="pi">-</span> <span class="s">--duration=10m</span>
        <span class="pi">-</span> <span class="s">--rps=100</span>
        <span class="pi">-</span> <span class="s">/scripts/load-test.js</span>
        <span class="na">volumeMounts</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">test-scripts</span>
          <span class="na">mountPath</span><span class="pi">:</span> <span class="s">/scripts</span>
      <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">test-scripts</span>
        <span class="na">configMap</span><span class="pi">:</span>
          <span class="na">name</span><span class="pi">:</span> <span class="s">load-test-scripts</span>
      <span class="na">restartPolicy</span><span class="pi">:</span> <span class="s">Never</span>
</code></pre></div></div>

<p><strong>Load test script example:</strong></p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// load-test.js</span>
<span class="k">import</span> <span class="nx">http</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">k6/http</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">check</span><span class="p">,</span> <span class="nx">sleep</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">k6</span><span class="dl">'</span><span class="p">;</span>

<span class="k">export</span> <span class="kd">let</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>
  <span class="na">stages</span><span class="p">:</span> <span class="p">[</span>
    <span class="p">{</span> <span class="na">duration</span><span class="p">:</span> <span class="dl">'</span><span class="s1">2m</span><span class="dl">'</span><span class="p">,</span> <span class="na">target</span><span class="p">:</span> <span class="mi">100</span> <span class="p">},</span>   <span class="c1">// Ramp up</span>
    <span class="p">{</span> <span class="na">duration</span><span class="p">:</span> <span class="dl">'</span><span class="s1">5m</span><span class="dl">'</span><span class="p">,</span> <span class="na">target</span><span class="p">:</span> <span class="mi">100</span> <span class="p">},</span>   <span class="c1">// Stay at 100 users</span>
    <span class="p">{</span> <span class="na">duration</span><span class="p">:</span> <span class="dl">'</span><span class="s1">2m</span><span class="dl">'</span><span class="p">,</span> <span class="na">target</span><span class="p">:</span> <span class="mi">200</span> <span class="p">},</span>   <span class="c1">// Ramp up to 200</span>
    <span class="p">{</span> <span class="na">duration</span><span class="p">:</span> <span class="dl">'</span><span class="s1">5m</span><span class="dl">'</span><span class="p">,</span> <span class="na">target</span><span class="p">:</span> <span class="mi">200</span> <span class="p">},</span>   <span class="c1">// Stay at 200</span>
    <span class="p">{</span> <span class="na">duration</span><span class="p">:</span> <span class="dl">'</span><span class="s1">2m</span><span class="dl">'</span><span class="p">,</span> <span class="na">target</span><span class="p">:</span> <span class="mi">0</span> <span class="p">},</span>     <span class="c1">// Ramp down</span>
  <span class="p">],</span>
  <span class="na">thresholds</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">http_req_duration</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">p(95)&lt;500</span><span class="dl">'</span><span class="p">],</span>  <span class="c1">// 95% of requests under 500ms</span>
    <span class="na">http_req_failed</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">rate&lt;0.1</span><span class="dl">'</span><span class="p">],</span>     <span class="c1">// Error rate under 10%</span>
  <span class="p">},</span>
<span class="p">};</span>

<span class="k">export</span> <span class="k">default</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">let</span> <span class="nx">response</span> <span class="o">=</span> <span class="nx">http</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">http://web-app.default.svc.cluster.local/api/health</span><span class="dl">'</span><span class="p">);</span>
  <span class="nx">check</span><span class="p">(</span><span class="nx">response</span><span class="p">,</span> <span class="p">{</span>
    <span class="dl">'</span><span class="s1">status is 200</span><span class="dl">'</span><span class="p">:</span> <span class="p">(</span><span class="nx">r</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">r</span><span class="p">.</span><span class="nx">status</span> <span class="o">===</span> <span class="mi">200</span><span class="p">,</span>
    <span class="dl">'</span><span class="s1">response time &lt; 500ms</span><span class="dl">'</span><span class="p">:</span> <span class="p">(</span><span class="nx">r</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">r</span><span class="p">.</span><span class="nx">timings</span><span class="p">.</span><span class="nx">duration</span> <span class="o">&lt;</span> <span class="mi">500</span><span class="p">,</span>
  <span class="p">});</span>
  <span class="nx">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h4 id="742-performance-bottleneck-identification">7.4.2 Performance Bottleneck Identification</h4>

<p><strong>Common EKS performance bottlenecks:</strong></p>

<ol>
  <li><strong>CPU throttling due to limits</strong></li>
  <li><strong>Memory pressure causing OOM kills</strong></li>
  <li><strong>Network bandwidth limits (AWS instance-level)</strong></li>
  <li><strong>Storage IOPS limits (EBS)</strong></li>
  <li><strong>DNS resolution delays (CoreDNS overload)</strong></li>
  <li><strong>Connection tracking limits (conntrack)</strong></li>
</ol>

<p><strong>Performance monitoring queries:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># CPU throttling detection</span>
<span class="s">sum(rate(container_cpu_cfs_throttled_seconds_total[5m])) by (namespace, pod, container) &gt; </span><span class="m">0</span>

<span class="c1"># Memory pressure detection  </span>
<span class="s">(container_memory_working_set_bytes / container_spec_memory_limit_bytes) &gt; </span><span class="m">0.8</span>

<span class="c1"># Network bandwidth utilization</span>
<span class="s">rate(node_network_receive_bytes_total[5m]) + rate(node_network_transmit_bytes_total[5m])</span>

<span class="c1"># Storage IOPS utilization</span>
<span class="s">rate(node_disk_reads_completed_total[5m]) + rate(node_disk_writes_completed_total[5m])</span>

<span class="c1"># DNS latency</span>
<span class="s">histogram_quantile(0.95, sum(rate(coredns_dns_request_duration_seconds_bucket[5m])) by (le))</span>
</code></pre></div></div>

<hr />

<h3 id="75-aws-service-limits-and-quotas">7.5 AWS Service Limits and Quotas</h3>

<h4 id="751-common-eks-related-service-limits">7.5.1 Common EKS-Related Service Limits</h4>

<p><strong>EC2 limits that affect scaling:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check current EC2 limits</span>
aws service-quotas get-service-quota <span class="nt">--service-code</span> ec2 <span class="nt">--quota-code</span> L-1216C47A  <span class="c"># On-Demand instances</span>
aws service-quotas get-service-quota <span class="nt">--service-code</span> ec2 <span class="nt">--quota-code</span> L-34B43A08  <span class="c"># All Standard Spot Instance Requests</span>
aws service-quotas get-service-quota <span class="nt">--service-code</span> ec2 <span class="nt">--quota-code</span> L-0263D0A3  <span class="c"># Security Groups per VPC</span>
aws service-quotas get-service-quota <span class="nt">--service-code</span> ec2 <span class="nt">--quota-code</span> L-FE5A380F  <span class="c"># Network Interfaces per VPC</span>

<span class="c"># Check current usage</span>
aws ec2 describe-instances <span class="nt">--filters</span> <span class="s2">"Name=instance-state-name,Values=running"</span> <span class="nt">--query</span> <span class="s1">'Reservations[*].Instances[*].InstanceType'</span> | jq <span class="nt">-r</span> <span class="s1">'.[][] | select(. != null)'</span> | <span class="nb">sort</span> | <span class="nb">uniq</span> <span class="nt">-c</span>
</code></pre></div></div>

<p><strong>EKS-specific limits:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># EKS cluster limits</span>
aws service-quotas get-service-quota <span class="nt">--service-code</span> eks <span class="nt">--quota-code</span> L-1194D53C  <span class="c"># Clusters per region</span>
aws service-quotas get-service-quota <span class="nt">--service-code</span> eks <span class="nt">--quota-code</span> L-6D54EA21  <span class="c"># Managed node groups per cluster</span>
aws service-quotas get-service-quota <span class="nt">--service-code</span> eks <span class="nt">--quota-code</span> L-CD136C55  <span class="c"># Nodes per managed node group</span>
</code></pre></div></div>

<h4 id="752-proactive-limit-monitoring">7.5.2 Proactive Limit Monitoring</h4>

<p><strong>Service limit monitoring:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># CloudWatch custom metrics for service limits</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">batch/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">CronJob</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">service-limit-monitor</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">schedule</span><span class="pi">:</span> <span class="s2">"</span><span class="s">0</span><span class="nv"> </span><span class="s">*/6</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*"</span>  <span class="c1"># Every 6 hours</span>
  <span class="na">jobTemplate</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">template</span><span class="pi">:</span>
        <span class="na">spec</span><span class="pi">:</span>
          <span class="na">containers</span><span class="pi">:</span>
          <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">limit-monitor</span>
            <span class="na">image</span><span class="pi">:</span> <span class="s">amazon/aws-cli:latest</span>
            <span class="na">command</span><span class="pi">:</span>
            <span class="pi">-</span> <span class="s">/bin/bash</span>
            <span class="pi">-</span> <span class="s">-c</span>
            <span class="pi">-</span> <span class="pi">|</span>
              <span class="s"># Get current EC2 usage</span>
              <span class="s">RUNNING_INSTANCES=$(aws ec2 describe-instances --filters "Name=instance-state-name,Values=running" --query 'Reservations[*].Instances[*].InstanceId' --output text | wc -w)</span>
              
              <span class="s"># Get EC2 limit</span>
              <span class="s">EC2_LIMIT=$(aws service-quotas get-service-quota --service-code ec2 --quota-code L-1216C47A --query 'Quota.Value' --output text)</span>
              
              <span class="s"># Calculate utilization percentage</span>
              <span class="s">UTILIZATION=$(echo "scale=2; $RUNNING_INSTANCES / $EC2_LIMIT * 100" | bc)</span>
              
              <span class="s"># Send to CloudWatch</span>
              <span class="s">aws cloudwatch put-metric-data \</span>
                <span class="s">--namespace "AWS/ServiceLimits" \</span>
                <span class="s">--metric-data MetricName=EC2InstanceUtilization,Value=$UTILIZATION,Unit=Percent</span>
              
              <span class="s">echo "EC2 utilization: $UTILIZATION%"</span>
          <span class="na">restartPolicy</span><span class="pi">:</span> <span class="s">OnFailure</span>
</code></pre></div></div>

<hr />

<h3 id="76-scaling-troubleshooting-runbooks">7.6 Scaling Troubleshooting Runbooks</h3>

<h4 id="761-hpa-not-scaling-runbook">7.6.1 “HPA not scaling” Runbook</h4>

<p><strong>Symptoms:</strong> Pods under load but HPA not creating more replicas</p>

<p><strong>Step 1: Check HPA status</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get hpa
kubectl describe hpa &lt;hpa-name&gt;
<span class="c"># Look for: current metrics, scaling events</span>
</code></pre></div></div>

<p><strong>Step 2: Verify metrics availability</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check if metrics server is working</span>
kubectl top pods
kubectl top nodes

<span class="c"># Check specific pod metrics</span>
kubectl get <span class="nt">--raw</span> <span class="s2">"/apis/metrics.k8s.io/v1beta1/namespaces/&lt;namespace&gt;/pods/&lt;pod-name&gt;"</span>
</code></pre></div></div>

<p><strong>Step 3: Check resource requests</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl describe deployment &lt;deployment-name&gt; | <span class="nb">grep</span> <span class="nt">-A</span> 5 <span class="s2">"Requests:"</span>
<span class="c"># HPA requires CPU/memory requests to be set</span>
</code></pre></div></div>

<p><strong>Step 4: Check node capacity</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl describe nodes | <span class="nb">grep</span> <span class="nt">-A</span> 5 <span class="s2">"Allocated resources"</span>
<span class="c"># Verify nodes have capacity for new pods</span>
</code></pre></div></div>

<h4 id="762-cluster-autoscaler-not-adding-nodes-runbook">7.6.2 “Cluster Autoscaler not adding nodes” Runbook</h4>

<p><strong>Symptoms:</strong> Pods pending but no new nodes being created</p>

<p><strong>Step 1: Check CA logs</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nt">-n</span> kube-system logs deployment/cluster-autoscaler | <span class="nb">tail</span> <span class="nt">-50</span>
<span class="c"># Look for: scale-up events, errors, AWS API issues</span>
</code></pre></div></div>

<p><strong>Step 2: Check pending pods</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get pods <span class="nt">-A</span> <span class="nt">--field-selector</span><span class="o">=</span>status.phase<span class="o">=</span>Pending
kubectl describe pod &lt;pending-pod&gt;
<span class="c"># Look for: resource requirements, node selector constraints</span>
</code></pre></div></div>

<p><strong>Step 3: Check node group limits</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>aws autoscaling describe-auto-scaling-groups <span class="nt">--auto-scaling-group-names</span> &lt;asg-name&gt;
<span class="c"># Check: min/max size, desired capacity, current instances</span>
</code></pre></div></div>

<p><strong>Step 4: Check AWS service limits</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>aws service-quotas get-service-quota <span class="nt">--service-code</span> ec2 <span class="nt">--quota-code</span> L-1216C47A
<span class="c"># Verify you haven't hit EC2 instance limits</span>
</code></pre></div></div>

<h4 id="763-performance-degraded-under-load-runbook">7.6.3 “Performance degraded under load” Runbook</h4>

<p><strong>Symptoms:</strong> Application slow during traffic spikes</p>

<p><strong>Step 1: Check resource utilization</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl top pods <span class="nt">--sort-by</span><span class="o">=</span>cpu
kubectl top pods <span class="nt">--sort-by</span><span class="o">=</span>memory
kubectl top nodes
</code></pre></div></div>

<p><strong>Step 2: Check for CPU throttling</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Look for throttling in Prometheus</span>
<span class="c"># Query: sum(rate(container_cpu_cfs_throttled_seconds_total[5m])) by (namespace, pod)</span>
</code></pre></div></div>

<p><strong>Step 3: Check network limits</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check AWS ENA metrics for network limits</span>
kubectl <span class="nb">exec </span>node-exporter-pod <span class="nt">--</span> <span class="nb">cat</span> /sys/class/net/eth0/statistics/rx_dropped
kubectl <span class="nb">exec </span>node-exporter-pod <span class="nt">--</span> <span class="nb">cat</span> /sys/class/net/eth0/statistics/tx_dropped
</code></pre></div></div>

<p><strong>Step 4: Check DNS performance</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Test DNS resolution speed</span>
kubectl <span class="nb">exec </span>test-pod <span class="nt">--</span> <span class="nb">time </span>nslookup kubernetes.default.svc.cluster.local

<span class="c"># Check CoreDNS metrics</span>
kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> k8s-app<span class="o">=</span>kube-dns | <span class="nb">grep</span> <span class="nt">-i</span> error
</code></pre></div></div>

<hr />

<h2 id="8-upgrades-and-maintenance">8. Upgrades and maintenance</h2>

<p>EKS upgrades are where “everything was working fine” becomes “production is down”. Unlike app deploys, cluster upgrades touch every layer simultaneously and can fail in ways that are hard to predict and harder to roll back. Below: strategies that minimize risk and how to recover when upgrades go sideways.</p>

<hr />

<h3 id="81-eks-upgrade-strategy-the-reality-of-breaking-changes">8.1 EKS Upgrade Strategy (The Reality of Breaking Changes)</h3>

<h4 id="811-eks-upgrade-components">8.1.1 EKS Upgrade Components</h4>

<p><strong>What actually gets upgraded:</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[EKS Control Plane] → [Managed by AWS]
[EKS Add-ons] → [CoreDNS, kube-proxy, VPC CNI, EBS CSI]
[Node Groups] → [AMI, Kubernetes version, instance types]
[Third-party Components] → [Ingress controllers, service mesh, monitoring]
</code></pre></div></div>

<p>Each component can break independently, and version skew between components creates new failure modes.</p>

<h4 id="812-pre-upgrade-validation-checklist">8.1.2 Pre-Upgrade Validation Checklist</h4>

<p><strong>Compatibility matrix validation:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="c"># EKS upgrade compatibility checker</span>

<span class="nv">CLUSTER_NAME</span><span class="o">=</span><span class="s2">"production-cluster"</span>
<span class="nv">CURRENT_VERSION</span><span class="o">=</span><span class="si">$(</span>aws eks describe-cluster <span class="nt">--name</span> <span class="nv">$CLUSTER_NAME</span> <span class="nt">--query</span> <span class="s1">'cluster.version'</span> <span class="nt">--output</span> text<span class="si">)</span>
<span class="nv">TARGET_VERSION</span><span class="o">=</span><span class="s2">"1.28"</span>

<span class="nb">echo</span> <span class="s2">"Current EKS version: </span><span class="nv">$CURRENT_VERSION</span><span class="s2">"</span>
<span class="nb">echo</span> <span class="s2">"Target EKS version: </span><span class="nv">$TARGET_VERSION</span><span class="s2">"</span>

<span class="c"># Check addon compatibility</span>
<span class="nb">echo</span> <span class="s2">"Checking addon versions..."</span>
aws eks describe-addon-versions <span class="nt">--kubernetes-version</span> <span class="nv">$TARGET_VERSION</span> <span class="nt">--addon-name</span> vpc-cni
aws eks describe-addon-versions <span class="nt">--kubernetes-version</span> <span class="nv">$TARGET_VERSION</span> <span class="nt">--addon-name</span> coredns
aws eks describe-addon-versions <span class="nt">--kubernetes-version</span> <span class="nv">$TARGET_VERSION</span> <span class="nt">--addon-name</span> kube-proxy

<span class="c"># Check deprecated APIs</span>
<span class="nb">echo</span> <span class="s2">"Checking for deprecated APIs..."</span>
kubectl get <span class="nt">--raw</span> /api/v1 | jq <span class="s1">'.resources[] | select(.name == "componentstatuses")'</span> 
kubectl get <span class="nt">--raw</span> /apis/extensions/v1beta1 2&gt;/dev/null <span class="o">||</span> <span class="nb">echo</span> <span class="s2">"extensions/v1beta1 not available (good)"</span>

<span class="c"># Check node group versions</span>
<span class="nb">echo</span> <span class="s2">"Current node group versions:"</span>
aws eks describe-nodegroup <span class="nt">--cluster-name</span> <span class="nv">$CLUSTER_NAME</span> <span class="nt">--nodegroup-name</span> primary <span class="nt">--query</span> <span class="s1">'nodegroup.version'</span>
</code></pre></div></div>

<p><strong>Workload compatibility testing:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Test job to validate workloads on new version</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">batch/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Job</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">upgrade-compatibility-test</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">test</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">kubectl:latest</span>
        <span class="na">command</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="s">/bin/bash</span>
        <span class="pi">-</span> <span class="s">-c</span>
        <span class="pi">-</span> <span class="pi">|</span>
          <span class="s"># Test basic functionality</span>
          <span class="s">kubectl get nodes</span>
          <span class="s">kubectl get pods -A</span>
          
          <span class="s"># Test service discovery</span>
          <span class="s">nslookup kubernetes.default.svc.cluster.local</span>
          
          <span class="s"># Test storage</span>
          <span class="s">kubectl get pvc -A</span>
          <span class="s">kubectl get pv</span>
          
          <span class="s"># Test networking</span>
          <span class="s">kubectl get svc -A</span>
          <span class="s">kubectl get ingress -A</span>
          
          <span class="s">echo "Compatibility test completed"</span>
      <span class="na">restartPolicy</span><span class="pi">:</span> <span class="s">Never</span>
</code></pre></div></div>

<h4 id="813-staged-upgrade-approach">8.1.3 Staged Upgrade Approach</h4>

<p><strong>Phase 1: Control plane upgrade</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Upgrade control plane first (managed by AWS)</span>
aws eks update-cluster-version <span class="nt">--name</span> production-cluster <span class="nt">--version</span> 1.28

<span class="c"># Monitor upgrade progress</span>
aws eks describe-update <span class="nt">--name</span> production-cluster <span class="nt">--update-id</span> &lt;update-id&gt;

<span class="c"># Validate control plane health</span>
kubectl get nodes
kubectl get pods <span class="nt">-n</span> kube-system
</code></pre></div></div>

<p><strong>Phase 2: Add-on upgrades</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Upgrade VPC CNI first (networking critical)</span>
aws eks update-addon <span class="nt">--cluster-name</span> production-cluster <span class="nt">--addon-name</span> vpc-cni <span class="nt">--addon-version</span> v1.15.1-eksbuild.1

<span class="c"># Upgrade CoreDNS</span>
aws eks update-addon <span class="nt">--cluster-name</span> production-cluster <span class="nt">--addon-name</span> coredns <span class="nt">--addon-version</span> v1.10.1-eksbuild.4

<span class="c"># Upgrade kube-proxy</span>
aws eks update-addon <span class="nt">--cluster-name</span> production-cluster <span class="nt">--addon-name</span> kube-proxy <span class="nt">--addon-version</span> v1.28.2-eksbuild.2
</code></pre></div></div>

<p><strong>Phase 3: Node group upgrades (most risky)</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Create new node group with new version</span>
aws eks create-nodegroup <span class="se">\</span>
  <span class="nt">--cluster-name</span> production-cluster <span class="se">\</span>
  <span class="nt">--nodegroup-name</span> primary-v128 <span class="se">\</span>
  <span class="nt">--kubernetes-version</span> 1.28 <span class="se">\</span>
  <span class="nt">--node-role</span> arn:aws:iam::123456789012:role/NodeInstanceRole <span class="se">\</span>
  <span class="nt">--subnets</span> subnet-12345 subnet-67890 <span class="se">\</span>
  <span class="nt">--instance-types</span> m5.large <span class="se">\</span>
  <span class="nt">--ami-type</span> AL2_x86_64 <span class="se">\</span>
  <span class="nt">--capacity-type</span> ON_DEMAND <span class="se">\</span>
  <span class="nt">--scaling-config</span> <span class="nv">minSize</span><span class="o">=</span>2,maxSize<span class="o">=</span>10,desiredSize<span class="o">=</span>3

<span class="c"># Gradually migrate workloads</span>
kubectl cordon &lt;old-node&gt;
kubectl drain &lt;old-node&gt; <span class="nt">--ignore-daemonsets</span> <span class="nt">--delete-emptydir-data</span> <span class="nt">--force</span> <span class="nt">--grace-period</span><span class="o">=</span>300

<span class="c"># Delete old node group after validation</span>
aws eks delete-nodegroup <span class="nt">--cluster-name</span> production-cluster <span class="nt">--nodegroup-name</span> primary-old
</code></pre></div></div>

<hr />

<h3 id="82-node-group-replacement-strategies">8.2 Node Group Replacement Strategies</h3>

<h4 id="821-blue-green-node-group-strategy">8.2.1 Blue-Green Node Group Strategy</h4>

<p><strong>Advantages:</strong></p>
<ul>
  <li>Zero downtime for stateless workloads</li>
  <li>Easy rollback if issues occur</li>
  <li>Full validation before switching traffic</li>
</ul>

<p><strong>Implementation:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Blue node group (current)</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">eksctl.io/v1alpha5</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ClusterConfig</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">production-cluster</span>
<span class="na">nodeGroups</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">blue-nodes</span>
  <span class="na">instanceTypes</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">m5.large"</span><span class="pi">]</span>
  <span class="na">minSize</span><span class="pi">:</span> <span class="m">3</span>
  <span class="na">maxSize</span><span class="pi">:</span> <span class="m">10</span>
  <span class="na">desiredCapacity</span><span class="pi">:</span> <span class="m">5</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">deployment-group</span><span class="pi">:</span> <span class="s">blue</span>
  <span class="na">tags</span><span class="pi">:</span>
    <span class="na">Environment</span><span class="pi">:</span> <span class="s">production</span>
    <span class="na">DeploymentGroup</span><span class="pi">:</span> <span class="s">blue</span>

<span class="c1"># Green node group (new version)</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">green-nodes</span>
  <span class="na">instanceTypes</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">m5.large"</span><span class="pi">]</span>
  <span class="na">minSize</span><span class="pi">:</span> <span class="m">3</span>
  <span class="na">maxSize</span><span class="pi">:</span> <span class="m">10</span>
  <span class="na">desiredCapacity</span><span class="pi">:</span> <span class="m">5</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">deployment-group</span><span class="pi">:</span> <span class="s">green</span>
  <span class="na">tags</span><span class="pi">:</span>
    <span class="na">Environment</span><span class="pi">:</span> <span class="s">production</span>
    <span class="na">DeploymentGroup</span><span class="pi">:</span> <span class="s">green</span>
</code></pre></div></div>

<p><strong>Migration process:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 1. Create green node group</span>
eksctl create nodegroup <span class="nt">--config-file</span><span class="o">=</span>cluster-config.yaml <span class="nt">--include</span><span class="o">=</span><span class="s2">"green-nodes"</span>

<span class="c"># 2. Validate green nodes</span>
kubectl get nodes <span class="nt">-l</span> deployment-group<span class="o">=</span>green
kubectl describe nodes <span class="nt">-l</span> deployment-group<span class="o">=</span>green

<span class="c"># 3. Migrate workloads gradually</span>
<span class="k">for </span>deployment <span class="k">in</span> <span class="si">$(</span>kubectl get deployments <span class="nt">-o</span> name<span class="si">)</span><span class="p">;</span> <span class="k">do
  </span><span class="nb">echo</span> <span class="s2">"Migrating </span><span class="nv">$deployment</span><span class="s2">"</span>
  kubectl patch <span class="nv">$deployment</span> <span class="nt">-p</span> <span class="s1">'{"spec":{"template":{"spec":{"nodeSelector":{"deployment-group":"green"}}}}}'</span>
  kubectl rollout status <span class="nv">$deployment</span>
  <span class="nb">sleep </span>30
<span class="k">done</span>

<span class="c"># 4. Validate applications on green nodes</span>
./run-smoke-tests.sh

<span class="c"># 5. Remove blue nodes</span>
kubectl cordon <span class="nt">-l</span> deployment-group<span class="o">=</span>blue
kubectl drain <span class="nt">-l</span> deployment-group<span class="o">=</span>blue <span class="nt">--ignore-daemonsets</span> <span class="nt">--delete-emptydir-data</span>
eksctl delete nodegroup <span class="nt">--cluster</span><span class="o">=</span>production-cluster <span class="nt">--name</span><span class="o">=</span>blue-nodes
</code></pre></div></div>

<h4 id="822-rolling-node-group-updates">8.2.2 Rolling Node Group Updates</h4>

<p><strong>For stateful workloads that can’t move easily:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Update node group in place with rolling replacement</span>
aws eks update-nodegroup-version <span class="se">\</span>
  <span class="nt">--cluster-name</span> production-cluster <span class="se">\</span>
  <span class="nt">--nodegroup-name</span> primary <span class="se">\</span>
  <span class="nt">--kubernetes-version</span> 1.28 <span class="se">\</span>
  <span class="nt">--launch-template-version</span> 2

<span class="c"># Monitor rolling update progress</span>
aws eks describe-nodegroup <span class="se">\</span>
  <span class="nt">--cluster-name</span> production-cluster <span class="se">\</span>
  <span class="nt">--nodegroup-name</span> primary <span class="se">\</span>
  <span class="nt">--query</span> <span class="s1">'nodegroup.updateConfig'</span>
</code></pre></div></div>

<p><strong>Custom rolling update script:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="c"># Custom node rolling update with validation</span>

<span class="nv">CLUSTER_NAME</span><span class="o">=</span><span class="s2">"production-cluster"</span>
<span class="nv">NODEGROUP_NAME</span><span class="o">=</span><span class="s2">"primary"</span>

<span class="c"># Get list of nodes in node group</span>
<span class="nv">NODES</span><span class="o">=</span><span class="si">$(</span>kubectl get nodes <span class="nt">-l</span> eks.amazonaws.com/nodegroup<span class="o">=</span><span class="nv">$NODEGROUP_NAME</span> <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.items[*].metadata.name}'</span><span class="si">)</span>

<span class="k">for </span>node <span class="k">in</span> <span class="nv">$NODES</span><span class="p">;</span> <span class="k">do
  </span><span class="nb">echo</span> <span class="s2">"Updating node: </span><span class="nv">$node</span><span class="s2">"</span>
  
  <span class="c"># Cordon node</span>
  kubectl cordon <span class="nv">$node</span>
  
  <span class="c"># Wait for new pods to be scheduled elsewhere</span>
  <span class="nb">sleep </span>60
  
  <span class="c"># Drain node</span>
  kubectl drain <span class="nv">$node</span> <span class="nt">--ignore-daemonsets</span> <span class="nt">--delete-emptydir-data</span> <span class="nt">--force</span> <span class="nt">--grace-period</span><span class="o">=</span>300
  
  <span class="c"># Terminate instance (ASG will replace it)</span>
  <span class="nv">INSTANCE_ID</span><span class="o">=</span><span class="si">$(</span>kubectl get node <span class="nv">$node</span> <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.spec.providerID}'</span> | <span class="nb">cut</span> <span class="nt">-d</span><span class="s1">'/'</span> <span class="nt">-f5</span><span class="si">)</span>
  aws ec2 terminate-instances <span class="nt">--instance-ids</span> <span class="nv">$INSTANCE_ID</span>
  
  <span class="c"># Wait for replacement node to be ready</span>
  <span class="nb">echo</span> <span class="s2">"Waiting for replacement node..."</span>
  <span class="k">while </span><span class="nb">true</span><span class="p">;</span> <span class="k">do
    </span><span class="nv">READY_NODES</span><span class="o">=</span><span class="si">$(</span>kubectl get nodes <span class="nt">-l</span> eks.amazonaws.com/nodegroup<span class="o">=</span><span class="nv">$NODEGROUP_NAME</span> <span class="nt">--no-headers</span> | <span class="nb">grep</span> <span class="s2">" Ready "</span> | <span class="nb">wc</span> <span class="nt">-l</span><span class="si">)</span>
    <span class="k">if</span> <span class="o">[</span> <span class="nv">$READY_NODES</span> <span class="nt">-ge</span> <span class="si">$(</span><span class="nb">echo</span> <span class="nv">$NODES</span> | <span class="nb">wc</span> <span class="nt">-w</span><span class="si">)</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
      </span><span class="nb">break
    </span><span class="k">fi
    </span><span class="nb">sleep </span>30
  <span class="k">done
  
  </span><span class="nb">echo</span> <span class="s2">"Node </span><span class="nv">$node</span><span class="s2"> replaced successfully"</span>
<span class="k">done</span>
</code></pre></div></div>

<hr />

<h3 id="83-application-compatibility-and-api-deprecations">8.3 Application Compatibility and API Deprecations</h3>

<h4 id="831-deprecated-api-detection">8.3.1 Deprecated API Detection</h4>

<p><strong>Automated API deprecation scanning:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="c"># Scan for deprecated APIs in cluster</span>

<span class="nb">echo</span> <span class="s2">"Scanning for deprecated APIs..."</span>

<span class="c"># Check for deprecated APIs in running resources</span>
kubectl get <span class="nt">--raw</span> /api/v1 | jq <span class="nt">-r</span> <span class="s1">'.resources[] | select(.name | contains("componentstatuses")) | .name'</span>

<span class="c"># Check extensions/v1beta1 usage (deprecated in 1.22+)</span>
kubectl get deployments.extensions <span class="nt">-A</span> 2&gt;/dev/null <span class="o">&amp;&amp;</span> <span class="nb">echo</span> <span class="s2">"WARNING: Found extensions/v1beta1 Deployments"</span>
kubectl get ingresses.extensions <span class="nt">-A</span> 2&gt;/dev/null <span class="o">&amp;&amp;</span> <span class="nb">echo</span> <span class="s2">"WARNING: Found extensions/v1beta1 Ingresses"</span>

<span class="c"># Check networking.k8s.io/v1beta1 usage (deprecated in 1.22+)</span>
kubectl get ingresses.networking.k8s.io/v1beta1 <span class="nt">-A</span> 2&gt;/dev/null <span class="o">&amp;&amp;</span> <span class="nb">echo</span> <span class="s2">"WARNING: Found networking.k8s.io/v1beta1 Ingresses"</span>

<span class="c"># Check policy/v1beta1 usage (deprecated in 1.25+)</span>
kubectl get podsecuritypolicies 2&gt;/dev/null <span class="o">&amp;&amp;</span> <span class="nb">echo</span> <span class="s2">"WARNING: Found PodSecurityPolicies (deprecated)"</span>

<span class="c"># Check autoscaling/v2beta1 usage (deprecated in 1.23+)</span>
kubectl get hpa.autoscaling/v2beta1 <span class="nt">-A</span> 2&gt;/dev/null <span class="o">&amp;&amp;</span> <span class="nb">echo</span> <span class="s2">"WARNING: Found autoscaling/v2beta1 HPAs"</span>

<span class="nb">echo</span> <span class="s2">"Deprecated API scan completed"</span>
</code></pre></div></div>

<p><strong>Pluto for comprehensive deprecation checking:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Install pluto</span>
curl <span class="nt">-L</span> https://github.com/FairwindsOps/pluto/releases/download/v5.18.4/pluto_5.18.4_linux_amd64.tar.gz | <span class="nb">tar </span>xz
<span class="nb">sudo mv </span>pluto /usr/local/bin/

<span class="c"># Scan cluster for deprecated APIs</span>
pluto detect-all-in-cluster <span class="nt">--target-versions</span> <span class="nv">k8s</span><span class="o">=</span>v1.28.0

<span class="c"># Scan Helm releases</span>
pluto detect-helm <span class="nt">--target-versions</span> <span class="nv">k8s</span><span class="o">=</span>v1.28.0

<span class="c"># Scan files</span>
pluto detect-files <span class="nt">-d</span> ./k8s-manifests <span class="nt">--target-versions</span> <span class="nv">k8s</span><span class="o">=</span>v1.28.0
</code></pre></div></div>

<h4 id="832-api-migration-strategies">8.3.2 API Migration Strategies</h4>

<p><strong>Ingress API migration (extensions/v1beta1 → networking.k8s.io/v1):</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># OLD (deprecated in 1.22+)</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">extensions/v1beta1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Ingress</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">web-app</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">rules</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">host</span><span class="pi">:</span> <span class="s">app.example.com</span>
    <span class="na">http</span><span class="pi">:</span>
      <span class="na">paths</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">path</span><span class="pi">:</span> <span class="s">/</span>
        <span class="na">backend</span><span class="pi">:</span>
          <span class="na">serviceName</span><span class="pi">:</span> <span class="s">web-app</span>
          <span class="na">servicePort</span><span class="pi">:</span> <span class="m">80</span>

<span class="c1"># NEW (required in 1.22+)</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">networking.k8s.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Ingress</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">web-app</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">rules</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">host</span><span class="pi">:</span> <span class="s">app.example.com</span>
    <span class="na">http</span><span class="pi">:</span>
      <span class="na">paths</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">path</span><span class="pi">:</span> <span class="s">/</span>
        <span class="na">pathType</span><span class="pi">:</span> <span class="s">Prefix</span>  <span class="c1"># Required field</span>
        <span class="na">backend</span><span class="pi">:</span>
          <span class="na">service</span><span class="pi">:</span>        <span class="c1"># Changed structure</span>
            <span class="na">name</span><span class="pi">:</span> <span class="s">web-app</span>
            <span class="na">port</span><span class="pi">:</span>
              <span class="na">number</span><span class="pi">:</span> <span class="m">80</span>
</code></pre></div></div>

<p><strong>HPA API migration (autoscaling/v2beta1 → autoscaling/v2):</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># OLD (deprecated in 1.23+)</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">autoscaling/v2beta1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">HorizontalPodAutoscaler</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">web-app-hpa</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">scaleTargetRef</span><span class="pi">:</span>
    <span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
    <span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">web-app</span>
  <span class="na">minReplicas</span><span class="pi">:</span> <span class="m">2</span>
  <span class="na">maxReplicas</span><span class="pi">:</span> <span class="m">10</span>
  <span class="na">metrics</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">type</span><span class="pi">:</span> <span class="s">Resource</span>
    <span class="na">resource</span><span class="pi">:</span>
      <span class="na">name</span><span class="pi">:</span> <span class="s">cpu</span>
      <span class="na">targetAverageUtilization</span><span class="pi">:</span> <span class="m">70</span>

<span class="c1"># NEW (required in 1.23+)</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">autoscaling/v2</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">HorizontalPodAutoscaler</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">web-app-hpa</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">scaleTargetRef</span><span class="pi">:</span>
    <span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
    <span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">web-app</span>
  <span class="na">minReplicas</span><span class="pi">:</span> <span class="m">2</span>
  <span class="na">maxReplicas</span><span class="pi">:</span> <span class="m">10</span>
  <span class="na">metrics</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">type</span><span class="pi">:</span> <span class="s">Resource</span>
    <span class="na">resource</span><span class="pi">:</span>
      <span class="na">name</span><span class="pi">:</span> <span class="s">cpu</span>
      <span class="na">target</span><span class="pi">:</span>
        <span class="na">type</span><span class="pi">:</span> <span class="s">Utilization</span>
        <span class="na">averageUtilization</span><span class="pi">:</span> <span class="m">70</span>  <span class="c1"># Changed structure</span>
</code></pre></div></div>

<hr />

<h3 id="84-rollback-strategies">8.4 Rollback Strategies</h3>

<h4 id="841-control-plane-rollback-limitations">8.4.1 Control Plane Rollback Limitations</h4>

<p><strong>Critical understanding:</strong> EKS control plane upgrades <strong>cannot be rolled back</strong> (as of early 2026). Once upgraded, you can only move forward.</p>

<blockquote>
  <p><strong>Note:</strong> AWS is developing an EKS control plane rollback feature for inline upgrades, but it’s not yet released. Until available, the limitations below apply.</p>
</blockquote>

<p><strong>Current mitigation strategies:</strong></p>
<ol>
  <li><strong>Thorough testing</strong> in staging environment</li>
  <li><strong>Blue-green cluster</strong> strategy for critical workloads</li>
  <li><strong>Backup and restore</strong> procedures for etcd data</li>
</ol>

<h4 id="842-node-group-rollback">8.4.2 Node Group Rollback</h4>

<p><strong>Quick node group rollback:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># If new node group has issues, switch back to old one</span>
kubectl patch deployment web-app <span class="nt">-p</span> <span class="s1">'{"spec":{"template":{"spec":{"nodeSelector":{"deployment-group":"blue"}}}}}'</span>

<span class="c"># Scale up old node group</span>
aws eks update-nodegroup-config <span class="se">\</span>
  <span class="nt">--cluster-name</span> production-cluster <span class="se">\</span>
  <span class="nt">--nodegroup-name</span> blue-nodes <span class="se">\</span>
  <span class="nt">--scaling-config</span> <span class="nv">minSize</span><span class="o">=</span>3,maxSize<span class="o">=</span>10,desiredSize<span class="o">=</span>5

<span class="c"># Delete problematic new node group</span>
aws eks delete-nodegroup <span class="se">\</span>
  <span class="nt">--cluster-name</span> production-cluster <span class="se">\</span>
  <span class="nt">--nodegroup-name</span> green-nodes
</code></pre></div></div>

<h4 id="843-add-on-rollback">8.4.3 Add-on Rollback</h4>

<p><strong>Rolling back EKS add-ons:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check available versions</span>
aws eks describe-addon-versions <span class="nt">--addon-name</span> vpc-cni <span class="nt">--kubernetes-version</span> 1.27

<span class="c"># Rollback to previous version</span>
aws eks update-addon <span class="se">\</span>
  <span class="nt">--cluster-name</span> production-cluster <span class="se">\</span>
  <span class="nt">--addon-name</span> vpc-cni <span class="se">\</span>
  <span class="nt">--addon-version</span> v1.14.1-eksbuild.1 <span class="se">\</span>
  <span class="nt">--resolve-conflicts</span> OVERWRITE
</code></pre></div></div>

<hr />

<h3 id="85-maintenance-windows-and-disruption-management">8.5 Maintenance Windows and Disruption Management</h3>

<h4 id="851-planned-maintenance-strategy">8.5.1 Planned Maintenance Strategy</h4>

<p><strong>Maintenance window planning:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Maintenance mode deployment</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">maintenance-page</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">2</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">matchLabels</span><span class="pi">:</span>
      <span class="na">app</span><span class="pi">:</span> <span class="s">maintenance-page</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">labels</span><span class="pi">:</span>
        <span class="na">app</span><span class="pi">:</span> <span class="s">maintenance-page</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">nginx</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">nginx:alpine</span>
        <span class="na">ports</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">80</span>
        <span class="na">volumeMounts</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">maintenance-content</span>
          <span class="na">mountPath</span><span class="pi">:</span> <span class="s">/usr/share/nginx/html</span>
      <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">maintenance-content</span>
        <span class="na">configMap</span><span class="pi">:</span>
          <span class="na">name</span><span class="pi">:</span> <span class="s">maintenance-content</span>
<span class="nn">---</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ConfigMap</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">maintenance-content</span>
<span class="na">data</span><span class="pi">:</span>
  <span class="na">index.html</span><span class="pi">:</span> <span class="pi">|</span>
    <span class="s">&lt;!DOCTYPE html&gt;</span>
    <span class="s">&lt;html&gt;</span>
    <span class="s">&lt;head&gt;&lt;title&gt;Maintenance&lt;/title&gt;&lt;/head&gt;</span>
    <span class="s">&lt;body&gt;</span>
      <span class="s">&lt;h1&gt;System Maintenance&lt;/h1&gt;</span>
      <span class="s">&lt;p&gt;We're performing scheduled maintenance. Please try again in 30 minutes.&lt;/p&gt;</span>
    <span class="s">&lt;/body&gt;</span>
    <span class="s">&lt;/html&gt;</span>
</code></pre></div></div>

<p><strong>Traffic switching for maintenance:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Switch ingress to maintenance page</span>
kubectl patch ingress web-app <span class="nt">-p</span> <span class="s1">'{"spec":{"rules":[{"host":"app.example.com","http":{"paths":[{"path":"/","pathType":"Prefix","backend":{"service":{"name":"maintenance-page","port":{"number":80}}}}]}}]}}'</span>

<span class="c"># Perform maintenance operations</span>
./upgrade-cluster.sh

<span class="c"># Switch back to application</span>
kubectl patch ingress web-app <span class="nt">-p</span> <span class="s1">'{"spec":{"rules":[{"host":"app.example.com","http":{"paths":[{"path":"/","pathType":"Prefix","backend":{"service":{"name":"web-app","port":{"number":80}}}}]}}]}}'</span>
</code></pre></div></div>

<h4 id="852-pod-disruption-budget-management">8.5.2 Pod Disruption Budget Management</h4>

<p><strong>Maintenance-aware PDB configuration:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">policy/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">PodDisruptionBudget</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">web-app-pdb</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">minAvailable</span><span class="pi">:</span> <span class="m">2</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">matchLabels</span><span class="pi">:</span>
      <span class="na">app</span><span class="pi">:</span> <span class="s">web-app</span>
  <span class="c1"># Allow more disruption during maintenance windows</span>
  <span class="na">unhealthyPodEvictionPolicy</span><span class="pi">:</span> <span class="s">AlwaysAllow</span>
</code></pre></div></div>

<p><strong>Temporary PDB adjustment for maintenance:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Relax PDB for maintenance</span>
kubectl patch pdb web-app-pdb <span class="nt">-p</span> <span class="s1">'{"spec":{"minAvailable":1}}'</span>

<span class="c"># Perform node drains</span>
kubectl drain node1 <span class="nt">--ignore-daemonsets</span> <span class="nt">--delete-emptydir-data</span>

<span class="c"># Restore strict PDB after maintenance</span>
kubectl patch pdb web-app-pdb <span class="nt">-p</span> <span class="s1">'{"spec":{"minAvailable":2}}'</span>
</code></pre></div></div>

<hr />

<h3 id="86-upgrade-troubleshooting-runbooks">8.6 Upgrade Troubleshooting Runbooks</h3>

<h4 id="861-control-plane-upgrade-stuck-runbook">8.6.1 “Control plane upgrade stuck” Runbook</h4>

<p><strong>Symptoms:</strong> EKS upgrade shows “InProgress” for hours</p>

<p><strong>Step 1: Check upgrade status</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>aws eks describe-update <span class="nt">--name</span> production-cluster <span class="nt">--update-id</span> &lt;update-id&gt;
<span class="c"># Look for: status, errors, created/modified timestamps</span>
</code></pre></div></div>

<p><strong>Step 2: Check control plane health</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get nodes
kubectl get pods <span class="nt">-n</span> kube-system
kubectl get <span class="nt">--raw</span><span class="o">=</span><span class="s1">'/readyz?verbose'</span>
</code></pre></div></div>

<p><strong>Step 3: Check for blocking resources</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check for stuck finalizers</span>
kubectl get all <span class="nt">-A</span> | <span class="nb">grep </span>Terminating

<span class="c"># Check for webhook issues</span>
kubectl get validatingwebhookconfigurations
kubectl get mutatingwebhookconfigurations
</code></pre></div></div>

<p><strong>Step 4: Contact AWS Support</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># If upgrade is truly stuck (&gt;4 hours), open AWS support case</span>
<span class="c"># Include: cluster name, update ID, timeline of events</span>
</code></pre></div></div>

<h4 id="862-pods-failing-after-node-upgrade-runbook">8.6.2 “Pods failing after node upgrade” Runbook</h4>

<p><strong>Symptoms:</strong> Applications not working after node group upgrade</p>

<p><strong>Step 1: Check pod status</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get pods <span class="nt">-A</span> | <span class="nb">grep</span> <span class="nt">-v</span> Running
kubectl describe pod &lt;failing-pod&gt;
<span class="c"># Look for: scheduling issues, image pull problems, volume mount failures</span>
</code></pre></div></div>

<p><strong>Step 2: Check node conditions</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl get nodes
kubectl describe node &lt;new-node&gt;
<span class="c"># Look for: Ready condition, resource availability, taints</span>
</code></pre></div></div>

<p><strong>Step 3: Check networking</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Test pod-to-pod connectivity</span>
kubectl <span class="nb">exec </span>test-pod <span class="nt">--</span> ping &lt;other-pod-ip&gt;

<span class="c"># Test DNS resolution</span>
kubectl <span class="nb">exec </span>test-pod <span class="nt">--</span> nslookup kubernetes.default.svc.cluster.local

<span class="c"># Check CNI health</span>
kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> k8s-app<span class="o">=</span>aws-node
</code></pre></div></div>

<p><strong>Step 4: Check storage</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check PVC status</span>
kubectl get pvc <span class="nt">-A</span>

<span class="c"># Check volume attachments</span>
kubectl get volumeattachment

<span class="c"># Check CSI driver health</span>
kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> <span class="nv">app</span><span class="o">=</span>ebs-csi-controller
</code></pre></div></div>

<h4 id="863-add-on-upgrade-failed-runbook">8.6.3 “Add-on upgrade failed” Runbook</h4>

<p><strong>Symptoms:</strong> EKS add-on shows “DEGRADED” status</p>

<p><strong>Step 1: Check add-on status</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>aws eks describe-addon <span class="nt">--cluster-name</span> production-cluster <span class="nt">--addon-name</span> vpc-cni
<span class="c"># Look for: status, health issues, configuration conflicts</span>
</code></pre></div></div>

<p><strong>Step 2: Check add-on pods</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl <span class="nt">-n</span> kube-system get pods <span class="nt">-l</span> k8s-app<span class="o">=</span>aws-node
kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> k8s-app<span class="o">=</span>aws-node
</code></pre></div></div>

<p><strong>Step 3: Resolve conflicts</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># If configuration conflicts exist, resolve with OVERWRITE</span>
aws eks update-addon <span class="se">\</span>
  <span class="nt">--cluster-name</span> production-cluster <span class="se">\</span>
  <span class="nt">--addon-name</span> vpc-cni <span class="se">\</span>
  <span class="nt">--resolve-conflicts</span> OVERWRITE
</code></pre></div></div>

<p><strong>Step 4: Rollback if necessary</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check available versions</span>
aws eks describe-addon-versions <span class="nt">--addon-name</span> vpc-cni <span class="nt">--kubernetes-version</span> 1.27

<span class="c"># Rollback to previous version</span>
aws eks update-addon <span class="se">\</span>
  <span class="nt">--cluster-name</span> production-cluster <span class="se">\</span>
  <span class="nt">--addon-name</span> vpc-cni <span class="se">\</span>
  <span class="nt">--addon-version</span> &lt;previous-version&gt;
</code></pre></div></div>

<hr />

<h3 id="87-graceful-deployments-and-pod-termination">8.7 Graceful Deployments and Pod Termination</h3>

<p><strong>The deployment problem:</strong> During rolling updates, pods can receive traffic while terminating or before they’re ready, causing 5xx errors.</p>

<p><strong>Pod termination sequence:</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1. Pod marked for termination (status: Terminating)
2. Pod removed from Service endpoints (async)
3. SIGTERM sent to containers (async)
4. preStop hooks executed (if configured)
5. Grace period countdown starts (default: 30s)
6. SIGKILL sent if still running after grace period
</code></pre></div></div>

<p><strong>Race condition:</strong> Steps 2 and 3 happen in parallel, so pods can receive traffic after SIGTERM.</p>

<h4 id="871-graceful-shutdown-configuration">8.7.1 Graceful Shutdown Configuration</h4>

<p><strong>Application-level graceful shutdown:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">graceful-app</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">terminationGracePeriodSeconds</span><span class="pi">:</span> <span class="m">60</span>  <span class="c1"># Allow time for cleanup</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">app</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">my-app:latest</span>
        <span class="na">lifecycle</span><span class="pi">:</span>
          <span class="na">preStop</span><span class="pi">:</span>
            <span class="na">exec</span><span class="pi">:</span>
              <span class="na">command</span><span class="pi">:</span>
              <span class="pi">-</span> <span class="s">/bin/sh</span>
              <span class="pi">-</span> <span class="s">-c</span>
              <span class="pi">-</span> <span class="pi">|</span>
                <span class="s"># Signal app to stop accepting new requests</span>
                <span class="s">kill -TERM 1</span>
                <span class="s"># Wait for load balancer to update (AWS NLB ~10s, ALB ~15s)</span>
                <span class="s">sleep 15</span>
                <span class="s"># Allow existing requests to complete</span>
                <span class="s">sleep 10</span>
        <span class="na">ports</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">8080</span>
        <span class="na">readinessProbe</span><span class="pi">:</span>
          <span class="na">httpGet</span><span class="pi">:</span>
            <span class="na">path</span><span class="pi">:</span> <span class="s">/health/ready</span>
            <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
          <span class="na">initialDelaySeconds</span><span class="pi">:</span> <span class="m">5</span>
          <span class="na">periodSeconds</span><span class="pi">:</span> <span class="m">5</span>
        <span class="na">livenessProbe</span><span class="pi">:</span>
          <span class="na">httpGet</span><span class="pi">:</span>
            <span class="na">path</span><span class="pi">:</span> <span class="s">/health/live</span>
            <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
          <span class="na">initialDelaySeconds</span><span class="pi">:</span> <span class="m">30</span>
          <span class="na">periodSeconds</span><span class="pi">:</span> <span class="m">10</span>
</code></pre></div></div>

<p><strong>Application code example (Go):</strong></p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>

<span class="k">import</span> <span class="p">(</span>
    <span class="s">"context"</span>
    <span class="s">"net/http"</span>
    <span class="s">"os"</span>
    <span class="s">"os/signal"</span>
    <span class="s">"syscall"</span>
    <span class="s">"time"</span>
<span class="p">)</span>

<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">server</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="n">http</span><span class="o">.</span><span class="n">Server</span><span class="p">{</span><span class="n">Addr</span><span class="o">:</span> <span class="s">":8080"</span><span class="p">}</span>
    
    <span class="c">// Graceful shutdown handling</span>
    <span class="k">go</span> <span class="k">func</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">sigChan</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="k">chan</span> <span class="n">os</span><span class="o">.</span><span class="n">Signal</span><span class="p">,</span> <span class="m">1</span><span class="p">)</span>
        <span class="n">signal</span><span class="o">.</span><span class="n">Notify</span><span class="p">(</span><span class="n">sigChan</span><span class="p">,</span> <span class="n">syscall</span><span class="o">.</span><span class="n">SIGTERM</span><span class="p">,</span> <span class="n">syscall</span><span class="o">.</span><span class="n">SIGINT</span><span class="p">)</span>
        <span class="o">&lt;-</span><span class="n">sigChan</span>
        
        <span class="c">// Stop accepting new requests</span>
        <span class="n">ctx</span><span class="p">,</span> <span class="n">cancel</span> <span class="o">:=</span> <span class="n">context</span><span class="o">.</span><span class="n">WithTimeout</span><span class="p">(</span><span class="n">context</span><span class="o">.</span><span class="n">Background</span><span class="p">(),</span> <span class="m">30</span><span class="o">*</span><span class="n">time</span><span class="o">.</span><span class="n">Second</span><span class="p">)</span>
        <span class="k">defer</span> <span class="n">cancel</span><span class="p">()</span>
        
        <span class="n">server</span><span class="o">.</span><span class="n">Shutdown</span><span class="p">(</span><span class="n">ctx</span><span class="p">)</span>
    <span class="p">}()</span>
    
    <span class="n">server</span><span class="o">.</span><span class="n">ListenAndServe</span><span class="p">()</span>
<span class="p">}</span>
</code></pre></div></div>

<h4 id="872-load-balancer-integration">8.7.2 Load Balancer Integration</h4>

<p><strong>AWS NLB connection draining:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Service</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">app-service</span>
  <span class="na">annotations</span><span class="pi">:</span>
    <span class="na">service.beta.kubernetes.io/aws-load-balancer-type</span><span class="pi">:</span> <span class="s2">"</span><span class="s">nlb"</span>
    <span class="na">service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout</span><span class="pi">:</span> <span class="s2">"</span><span class="s">350"</span>
    <span class="c1"># Enable connection draining</span>
    <span class="na">service.beta.kubernetes.io/aws-load-balancer-connection-draining-enabled</span><span class="pi">:</span> <span class="s2">"</span><span class="s">true"</span>
    <span class="na">service.beta.kubernetes.io/aws-load-balancer-connection-draining-timeout</span><span class="pi">:</span> <span class="s2">"</span><span class="s">60"</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">LoadBalancer</span>
  <span class="na">ports</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">port</span><span class="pi">:</span> <span class="m">80</span>
    <span class="na">targetPort</span><span class="pi">:</span> <span class="m">8080</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">graceful-app</span>
</code></pre></div></div>

<p><strong>Envoy proxy graceful shutdown:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ConfigMap</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">envoy-config</span>
<span class="na">data</span><span class="pi">:</span>
  <span class="na">envoy.yaml</span><span class="pi">:</span> <span class="pi">|</span>
    <span class="s">static_resources:</span>
      <span class="s">listeners:</span>
      <span class="s">- name: listener_0</span>
        <span class="s">address:</span>
          <span class="s">socket_address:</span>
            <span class="s">address: 0.0.0.0</span>
            <span class="s">port_value: 8080</span>
        <span class="s">filter_chains:</span>
        <span class="s">- filters:</span>
          <span class="s">- name: envoy.filters.network.http_connection_manager</span>
            <span class="s">typed_config:</span>
              <span class="s">"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager</span>
              <span class="s"># Graceful shutdown settings</span>
              <span class="s">drain_timeout: 30s</span>
              <span class="s">delayed_close_timeout: 10s</span>
              <span class="s">http_filters:</span>
              <span class="s">- name: envoy.filters.http.router</span>
              <span class="s">route_config:</span>
                <span class="s">name: local_route</span>
                <span class="s">virtual_hosts:</span>
                <span class="s">- name: backend</span>
                  <span class="s">domains: ["*"]</span>
                  <span class="s">routes:</span>
                  <span class="s">- match:</span>
                      <span class="s">prefix: "/"</span>
                    <span class="s">route:</span>
                      <span class="s">cluster: backend_cluster</span>
      <span class="s">clusters:</span>
      <span class="s">- name: backend_cluster</span>
        <span class="s">connect_timeout: 5s</span>
        <span class="s">type: STRICT_DNS</span>
        <span class="s">lb_policy: ROUND_ROBIN</span>
        <span class="s"># Connection recycling for NLB compatibility</span>
        <span class="s">max_requests_per_connection: 1000</span>
        <span class="s">max_connection_duration: 300s</span>
        <span class="s">load_assignment:</span>
          <span class="s">cluster_name: backend_cluster</span>
          <span class="s">endpoints:</span>
          <span class="s">- lb_endpoints:</span>
            <span class="s">- endpoint:</span>
                <span class="s">address:</span>
                  <span class="s">socket_address:</span>
                    <span class="s">address: app-backend</span>
                    <span class="s">port_value: 8080</span>
</code></pre></div></div>

<h4 id="873-rolling-update-strategy">8.7.3 Rolling Update Strategy</h4>

<p><strong>Deployment strategy for zero-downtime updates:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">zero-downtime-app</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">6</span>
  <span class="na">strategy</span><span class="pi">:</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">RollingUpdate</span>
    <span class="na">rollingUpdate</span><span class="pi">:</span>
      <span class="na">maxUnavailable</span><span class="pi">:</span> <span class="m">1</span>        <span class="c1"># Only terminate 1 pod at a time</span>
      <span class="na">maxSurge</span><span class="pi">:</span> <span class="m">2</span>              <span class="c1"># Allow 2 extra pods during update</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">terminationGracePeriodSeconds</span><span class="pi">:</span> <span class="m">45</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">app</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">my-app:v2</span>
        <span class="na">readinessProbe</span><span class="pi">:</span>
          <span class="na">httpGet</span><span class="pi">:</span>
            <span class="na">path</span><span class="pi">:</span> <span class="s">/health/ready</span>
            <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
          <span class="na">initialDelaySeconds</span><span class="pi">:</span> <span class="m">10</span>
          <span class="na">periodSeconds</span><span class="pi">:</span> <span class="m">5</span>
          <span class="na">failureThreshold</span><span class="pi">:</span> <span class="m">3</span>
        <span class="na">lifecycle</span><span class="pi">:</span>
          <span class="na">preStop</span><span class="pi">:</span>
            <span class="na">httpGet</span><span class="pi">:</span>
              <span class="na">path</span><span class="pi">:</span> <span class="s">/shutdown</span>
              <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
</code></pre></div></div>

<p><strong>PodDisruptionBudget for controlled disruptions:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">policy/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">PodDisruptionBudget</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">app-pdb</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">minAvailable</span><span class="pi">:</span> <span class="m">4</span>  <span class="c1"># Always keep 4 pods running</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">matchLabels</span><span class="pi">:</span>
      <span class="na">app</span><span class="pi">:</span> <span class="s">zero-downtime-app</span>
</code></pre></div></div>

<h2 id="9-disaster-recovery">9. Disaster recovery</h2>

<p>When a cluster fails catastrophically, the pressure to restore service leads to rushed decisions that make recovery slower. Below: DR strategies that work under pressure — what can be recovered, what must be rebuilt, and how to not make it worse.</p>

<hr />

<h3 id="91-eks-disaster-scenarios-what-actually-breaks">9.1 EKS Disaster Scenarios (What Actually Breaks)</h3>

<h4 id="911-control-plane-failures">9.1.1 Control Plane Failures</h4>

<p><strong>AWS-managed control plane issues:</strong></p>
<ul>
  <li>Regional AWS service outages</li>
  <li>EKS API server degradation</li>
  <li>etcd corruption (rare but catastrophic)</li>
  <li>Certificate rotation failures</li>
</ul>

<p><strong>Reality check:</strong> You cannot directly access or repair the EKS control plane. Recovery depends entirely on AWS support and your backup strategies.</p>

<h4 id="912-complete-cluster-loss">9.1.2 Complete Cluster Loss</h4>

<p><strong>Common causes:</strong></p>
<ul>
  <li>Accidental cluster deletion</li>
  <li>VPC/networking misconfiguration making cluster unreachable</li>
  <li>All node groups terminated simultaneously</li>
  <li>Region-wide AWS outages</li>
</ul>

<p><strong>Recovery time expectations:</strong></p>
<ul>
  <li>New cluster provisioning: 10-15 minutes</li>
  <li>Add-on installation: 5-10 minutes</li>
  <li>Application restoration: Depends on backup strategy</li>
  <li><strong>Total RTO: 30 minutes to several hours</strong></li>
</ul>

<h4 id="913-data-layer-failures">9.1.3 Data Layer Failures</h4>

<p><strong>EBS volume failures:</strong></p>
<ul>
  <li>Zone-wide EBS outages</li>
  <li>Volume corruption</li>
  <li>Snapshot restoration issues</li>
</ul>

<p><strong>Application data loss:</strong></p>
<ul>
  <li>StatefulSet data corruption</li>
  <li>Database failures</li>
  <li>Persistent volume claim issues</li>
</ul>

<hr />

<h3 id="92-backup-strategies-what-to-backup-and-how">9.2 Backup Strategies (What to Backup and How)</h3>

<h4 id="921-cluster-configuration-backup">9.2.1 Cluster Configuration Backup</h4>

<p><strong>Essential cluster state to backup:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="c"># Cluster backup script</span>

<span class="nv">CLUSTER_NAME</span><span class="o">=</span><span class="s2">"production-cluster"</span>
<span class="nv">BACKUP_DIR</span><span class="o">=</span><span class="s2">"./cluster-backup-</span><span class="si">$(</span><span class="nb">date</span> +%Y%m%d-%H%M%S<span class="si">)</span><span class="s2">"</span>
<span class="nb">mkdir</span> <span class="nt">-p</span> <span class="nv">$BACKUP_DIR</span>

<span class="c"># Backup cluster configuration</span>
aws eks describe-cluster <span class="nt">--name</span> <span class="nv">$CLUSTER_NAME</span> <span class="o">&gt;</span> <span class="nv">$BACKUP_DIR</span>/cluster-config.json

<span class="c"># Backup node groups</span>
aws eks list-nodegroups <span class="nt">--cluster-name</span> <span class="nv">$CLUSTER_NAME</span> <span class="nt">--query</span> <span class="s1">'nodegroups[]'</span> <span class="nt">--output</span> text | <span class="se">\</span>
<span class="k">while </span><span class="nb">read </span>nodegroup<span class="p">;</span> <span class="k">do
  </span>aws eks describe-nodegroup <span class="nt">--cluster-name</span> <span class="nv">$CLUSTER_NAME</span> <span class="nt">--nodegroup-name</span> <span class="nv">$nodegroup</span> <span class="o">&gt;</span> <span class="nv">$BACKUP_DIR</span>/nodegroup-<span class="nv">$nodegroup</span>.json
<span class="k">done</span>

<span class="c"># Backup EKS add-ons</span>
aws eks list-addons <span class="nt">--cluster-name</span> <span class="nv">$CLUSTER_NAME</span> <span class="nt">--query</span> <span class="s1">'addons[]'</span> <span class="nt">--output</span> text | <span class="se">\</span>
<span class="k">while </span><span class="nb">read </span>addon<span class="p">;</span> <span class="k">do
  </span>aws eks describe-addon <span class="nt">--cluster-name</span> <span class="nv">$CLUSTER_NAME</span> <span class="nt">--addon-name</span> <span class="nv">$addon</span> <span class="o">&gt;</span> <span class="nv">$BACKUP_DIR</span>/addon-<span class="nv">$addon</span>.json
<span class="k">done</span>

<span class="c"># Backup VPC configuration</span>
<span class="nv">VPC_ID</span><span class="o">=</span><span class="si">$(</span>aws eks describe-cluster <span class="nt">--name</span> <span class="nv">$CLUSTER_NAME</span> <span class="nt">--query</span> <span class="s1">'cluster.resourcesVpcConfig.vpcId'</span> <span class="nt">--output</span> text<span class="si">)</span>
aws ec2 describe-vpcs <span class="nt">--vpc-ids</span> <span class="nv">$VPC_ID</span> <span class="o">&gt;</span> <span class="nv">$BACKUP_DIR</span>/vpc-config.json
aws ec2 describe-subnets <span class="nt">--filters</span> <span class="s2">"Name=vpc-id,Values=</span><span class="nv">$VPC_ID</span><span class="s2">"</span> <span class="o">&gt;</span> <span class="nv">$BACKUP_DIR</span>/subnets.json
aws ec2 describe-security-groups <span class="nt">--filters</span> <span class="s2">"Name=vpc-id,Values=</span><span class="nv">$VPC_ID</span><span class="s2">"</span> <span class="o">&gt;</span> <span class="nv">$BACKUP_DIR</span>/security-groups.json

<span class="nb">echo</span> <span class="s2">"Cluster configuration backed up to </span><span class="nv">$BACKUP_DIR</span><span class="s2">"</span>
</code></pre></div></div>

<h4 id="922-application-state-backup-with-velero">9.2.2 Application State Backup with Velero</h4>

<p><strong>Velero installation for EKS:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Install Velero with AWS plugin</span>
velero <span class="nb">install</span> <span class="se">\</span>
  <span class="nt">--provider</span> aws <span class="se">\</span>
  <span class="nt">--plugins</span> velero/velero-plugin-for-aws:v1.8.0 <span class="se">\</span>
  <span class="nt">--bucket</span> velero-backups-production <span class="se">\</span>
  <span class="nt">--backup-location-config</span> <span class="nv">region</span><span class="o">=</span>us-west-2 <span class="se">\</span>
  <span class="nt">--snapshot-location-config</span> <span class="nv">region</span><span class="o">=</span>us-west-2 <span class="se">\</span>
  <span class="nt">--secret-file</span> ./credentials-velero
</code></pre></div></div>

<p><strong>Comprehensive backup schedule:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">velero.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Schedule</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">daily-backup</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">schedule</span><span class="pi">:</span> <span class="s2">"</span><span class="s">0</span><span class="nv"> </span><span class="s">2</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*"</span>  <span class="c1"># Daily at 2 AM</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">includedNamespaces</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">production</span>
    <span class="pi">-</span> <span class="s">staging</span>
    <span class="na">excludedResources</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">events</span>
    <span class="pi">-</span> <span class="s">events.events.k8s.io</span>
    <span class="na">storageLocation</span><span class="pi">:</span> <span class="s">default</span>
    <span class="na">volumeSnapshotLocations</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">default</span>
    <span class="na">ttl</span><span class="pi">:</span> <span class="s">720h</span>  <span class="c1"># 30 days retention</span>
</code></pre></div></div>

<p><strong>Critical workload backup:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">velero.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Backup</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">critical-workloads</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">includedNamespaces</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s">production</span>
  <span class="na">labelSelector</span><span class="pi">:</span>
    <span class="na">matchLabels</span><span class="pi">:</span>
      <span class="na">backup</span><span class="pi">:</span> <span class="s">critical</span>
  <span class="na">snapshotVolumes</span><span class="pi">:</span> <span class="no">true</span>
  <span class="na">includeClusterResources</span><span class="pi">:</span> <span class="no">true</span>
  <span class="na">hooks</span><span class="pi">:</span>
    <span class="na">resources</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">database-backup-hook</span>
      <span class="na">includedNamespaces</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">production</span>
      <span class="na">labelSelector</span><span class="pi">:</span>
        <span class="na">matchLabels</span><span class="pi">:</span>
          <span class="na">app</span><span class="pi">:</span> <span class="s">database</span>
      <span class="na">pre</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">exec</span><span class="pi">:</span>
          <span class="na">container</span><span class="pi">:</span> <span class="s">database</span>
          <span class="na">command</span><span class="pi">:</span>
          <span class="pi">-</span> <span class="s">/bin/bash</span>
          <span class="pi">-</span> <span class="s">-c</span>
          <span class="pi">-</span> <span class="s2">"</span><span class="s">pg_dump</span><span class="nv"> </span><span class="s">-h</span><span class="nv"> </span><span class="s">localhost</span><span class="nv"> </span><span class="s">-U</span><span class="nv"> </span><span class="s">postgres</span><span class="nv"> </span><span class="s">mydb</span><span class="nv"> </span><span class="s">&gt;</span><span class="nv"> </span><span class="s">/tmp/backup.sql"</span>
          <span class="na">timeout</span><span class="pi">:</span> <span class="s">300s</span>
</code></pre></div></div>

<h4 id="923-etcd-backup-strategy">9.2.3 etcd Backup Strategy</h4>

<p><strong>Automated etcd backup (for self-managed clusters):</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="c"># etcd backup script (not applicable to EKS managed control plane)</span>
<span class="c"># This is for reference if you have self-managed etcd</span>

<span class="nv">ETCDCTL_API</span><span class="o">=</span>3 etcdctl snapshot save backup.db <span class="se">\</span>
  <span class="nt">--endpoints</span><span class="o">=</span>https://127.0.0.1:2379 <span class="se">\</span>
  <span class="nt">--cacert</span><span class="o">=</span>/etc/kubernetes/pki/etcd/ca.crt <span class="se">\</span>
  <span class="nt">--cert</span><span class="o">=</span>/etc/kubernetes/pki/etcd/healthcheck-client.crt <span class="se">\</span>
  <span class="nt">--key</span><span class="o">=</span>/etc/kubernetes/pki/etcd/healthcheck-client.key

<span class="c"># Upload to S3</span>
aws s3 <span class="nb">cp </span>backup.db s3://etcd-backups/backup-<span class="si">$(</span><span class="nb">date</span> +%Y%m%d-%H%M%S<span class="si">)</span>.db
</code></pre></div></div>

<p><strong>Note:</strong> EKS manages etcd backups automatically. You cannot directly backup EKS etcd.</p>

<hr />

<h3 id="93-cross-region-disaster-recovery">9.3 Cross-Region Disaster Recovery</h3>

<h4 id="931-multi-region-eks-architecture">9.3.1 Multi-Region EKS Architecture</h4>

<p><strong>Active-passive setup:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Primary region cluster</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">eksctl.io/v1alpha5</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ClusterConfig</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">production-primary</span>
  <span class="na">region</span><span class="pi">:</span> <span class="s">us-west-2</span>
<span class="na">nodeGroups</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">primary-nodes</span>
  <span class="na">instanceTypes</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">m5.large"</span><span class="pi">]</span>
  <span class="na">minSize</span><span class="pi">:</span> <span class="m">3</span>
  <span class="na">maxSize</span><span class="pi">:</span> <span class="m">10</span>
  <span class="na">desiredCapacity</span><span class="pi">:</span> <span class="m">5</span>
  <span class="na">availabilityZones</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">us-west-2a"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">us-west-2b"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">us-west-2c"</span><span class="pi">]</span>

<span class="nn">---</span>
<span class="c1"># DR region cluster (smaller, can be scaled up)</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">eksctl.io/v1alpha5</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ClusterConfig</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">production-dr</span>
  <span class="na">region</span><span class="pi">:</span> <span class="s">us-east-1</span>
<span class="na">nodeGroups</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">dr-nodes</span>
  <span class="na">instanceTypes</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">m5.large"</span><span class="pi">]</span>
  <span class="na">minSize</span><span class="pi">:</span> <span class="m">1</span>
  <span class="na">maxSize</span><span class="pi">:</span> <span class="m">10</span>
  <span class="na">desiredCapacity</span><span class="pi">:</span> <span class="m">2</span>
  <span class="na">availabilityZones</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">us-east-1a"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">us-east-1b"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">us-east-1c"</span><span class="pi">]</span>
</code></pre></div></div>

<h4 id="932-cross-region-replication-strategy">9.3.2 Cross-Region Replication Strategy</h4>

<p><strong>Database replication:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># RDS cross-region read replica</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ConfigMap</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">database-config</span>
<span class="na">data</span><span class="pi">:</span>
  <span class="na">primary-endpoint</span><span class="pi">:</span> <span class="s2">"</span><span class="s">prod-db.us-west-2.rds.amazonaws.com"</span>
  <span class="na">dr-endpoint</span><span class="pi">:</span> <span class="s2">"</span><span class="s">prod-db-replica.us-east-1.rds.amazonaws.com"</span>
  <span class="na">failover-script</span><span class="pi">:</span> <span class="pi">|</span>
    <span class="s">#!/bin/bash</span>
    <span class="s"># Promote read replica to primary</span>
    <span class="s">aws rds promote-read-replica \</span>
      <span class="s">--db-instance-identifier prod-db-replica \</span>
      <span class="s">--region us-east-1</span>
</code></pre></div></div>

<p><strong>Application data replication:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Cross-region S3 replication for application assets</span>
aws s3api put-bucket-replication <span class="se">\</span>
  <span class="nt">--bucket</span> production-assets <span class="se">\</span>
  <span class="nt">--replication-configuration</span> file://replication-config.json
</code></pre></div></div>

<h4 id="933-dns-failover-configuration">9.3.3 DNS Failover Configuration</h4>

<p><strong>Route 53 health checks and failover:</strong></p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"Type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"A"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"Name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"api.example.com"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"SetIdentifier"</span><span class="p">:</span><span class="w"> </span><span class="s2">"primary"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"Failover"</span><span class="p">:</span><span class="w"> </span><span class="s2">"PRIMARY"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"AliasTarget"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"DNSName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"k8s-elb-primary.us-west-2.elb.amazonaws.com"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"EvaluateTargetHealth"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"HealthCheckId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"primary-health-check"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<hr />

<h3 id="94-recovery-procedures">9.4 Recovery Procedures</h3>

<h4 id="941-complete-cluster-recreation">9.4.1 Complete Cluster Recreation</h4>

<p><strong>Cluster recreation runbook:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="c"># Complete cluster recovery procedure</span>

<span class="nb">set</span> <span class="nt">-e</span>

<span class="nv">CLUSTER_NAME</span><span class="o">=</span><span class="s2">"production-cluster"</span>
<span class="nv">REGION</span><span class="o">=</span><span class="s2">"us-west-2"</span>
<span class="nv">BACKUP_DIR</span><span class="o">=</span><span class="s2">"./latest-backup"</span>

<span class="nb">echo</span> <span class="s2">"Starting cluster recovery for </span><span class="nv">$CLUSTER_NAME</span><span class="s2">"</span>

<span class="c"># Step 1: Recreate cluster</span>
eksctl create cluster <span class="nt">--config-file</span><span class="o">=</span><span class="nv">$BACKUP_DIR</span>/cluster-config.yaml

<span class="c"># Step 2: Wait for cluster to be ready</span>
aws eks <span class="nb">wait </span>cluster-active <span class="nt">--name</span> <span class="nv">$CLUSTER_NAME</span> <span class="nt">--region</span> <span class="nv">$REGION</span>

<span class="c"># Step 3: Install essential add-ons</span>
aws eks create-addon <span class="nt">--cluster-name</span> <span class="nv">$CLUSTER_NAME</span> <span class="nt">--addon-name</span> vpc-cni
aws eks create-addon <span class="nt">--cluster-name</span> <span class="nv">$CLUSTER_NAME</span> <span class="nt">--addon-name</span> coredns
aws eks create-addon <span class="nt">--cluster-name</span> <span class="nv">$CLUSTER_NAME</span> <span class="nt">--addon-name</span> kube-proxy

<span class="c"># Step 4: Install AWS Load Balancer Controller</span>
helm repo add eks https://aws.github.io/eks-charts
helm <span class="nb">install </span>aws-load-balancer-controller eks/aws-load-balancer-controller <span class="se">\</span>
  <span class="nt">-n</span> kube-system <span class="se">\</span>
  <span class="nt">--set</span> <span class="nv">clusterName</span><span class="o">=</span><span class="nv">$CLUSTER_NAME</span>

<span class="c"># Step 5: Install Velero</span>
velero <span class="nb">install</span> <span class="nt">--provider</span> aws <span class="nt">--plugins</span> velero/velero-plugin-for-aws:v1.8.0 <span class="se">\</span>
  <span class="nt">--bucket</span> velero-backups-production <span class="se">\</span>
  <span class="nt">--backup-location-config</span> <span class="nv">region</span><span class="o">=</span><span class="nv">$REGION</span> <span class="se">\</span>
  <span class="nt">--snapshot-location-config</span> <span class="nv">region</span><span class="o">=</span><span class="nv">$REGION</span> <span class="se">\</span>
  <span class="nt">--secret-file</span> ./credentials-velero

<span class="c"># Step 6: Restore from backup</span>
<span class="nv">LATEST_BACKUP</span><span class="o">=</span><span class="si">$(</span>velero backup get <span class="nt">--output</span> json | jq <span class="nt">-r</span> <span class="s1">'.items[0].metadata.name'</span><span class="si">)</span>
velero restore create <span class="nt">--from-backup</span> <span class="nv">$LATEST_BACKUP</span>

<span class="nb">echo</span> <span class="s2">"Cluster recovery initiated. Monitor with: kubectl get pods -A"</span>
</code></pre></div></div>

<h4 id="942-partial-recovery-scenarios">9.4.2 Partial Recovery Scenarios</h4>

<p><strong>Node group replacement:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># If only node groups are affected</span>
aws eks create-nodegroup <span class="se">\</span>
  <span class="nt">--cluster-name</span> production-cluster <span class="se">\</span>
  <span class="nt">--nodegroup-name</span> recovery-nodes <span class="se">\</span>
  <span class="nt">--kubernetes-version</span> 1.28 <span class="se">\</span>
  <span class="nt">--node-role</span> arn:aws:iam::123456789012:role/NodeInstanceRole <span class="se">\</span>
  <span class="nt">--subnets</span> subnet-12345 subnet-67890 <span class="se">\</span>
  <span class="nt">--instance-types</span> m5.large <span class="se">\</span>
  <span class="nt">--scaling-config</span> <span class="nv">minSize</span><span class="o">=</span>3,maxSize<span class="o">=</span>10,desiredSize<span class="o">=</span>5

<span class="c"># Migrate workloads to new nodes</span>
kubectl cordon <span class="nt">-l</span> eks.amazonaws.com/nodegroup<span class="o">=</span>old-nodes
kubectl drain <span class="nt">-l</span> eks.amazonaws.com/nodegroup<span class="o">=</span>old-nodes <span class="nt">--ignore-daemonsets</span> <span class="nt">--delete-emptydir-data</span>
</code></pre></div></div>

<p><strong>Application-only recovery:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># If cluster is healthy but applications are corrupted</span>
velero restore create app-recovery <span class="se">\</span>
  <span class="nt">--from-backup</span> latest-backup <span class="se">\</span>
  <span class="nt">--include-namespaces</span> production <span class="se">\</span>
  <span class="nt">--restore-volumes</span><span class="o">=</span><span class="nb">true</span>
</code></pre></div></div>

<h4 id="943-data-recovery-procedures">9.4.3 Data Recovery Procedures</h4>

<p><strong>EBS volume recovery:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Restore from EBS snapshot</span>
<span class="nv">SNAPSHOT_ID</span><span class="o">=</span><span class="s2">"snap-1234567890abcdef0"</span>
<span class="nv">VOLUME_ID</span><span class="o">=</span><span class="si">$(</span>aws ec2 create-volume <span class="se">\</span>
  <span class="nt">--snapshot-id</span> <span class="nv">$SNAPSHOT_ID</span> <span class="se">\</span>
  <span class="nt">--availability-zone</span> us-west-2a <span class="se">\</span>
  <span class="nt">--volume-type</span> gp3 <span class="se">\</span>
  <span class="nt">--query</span> <span class="s1">'VolumeId'</span> <span class="nt">--output</span> text<span class="si">)</span>

<span class="c"># Update PV to use new volume</span>
kubectl patch pv pvc-12345 <span class="nt">-p</span> <span class="s1">'{"spec":{"awsElasticBlockStore":{"volumeID":"'</span><span class="nv">$VOLUME_ID</span><span class="s1">'"}}}'</span>
</code></pre></div></div>

<p><strong>Database recovery:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># RDS point-in-time recovery</span>
aws rds restore-db-instance-to-point-in-time <span class="se">\</span>
  <span class="nt">--source-db-instance-identifier</span> prod-db <span class="se">\</span>
  <span class="nt">--target-db-instance-identifier</span> prod-db-recovered <span class="se">\</span>
  <span class="nt">--restore-time</span> 2024-01-15T10:00:00.000Z
</code></pre></div></div>

<hr />

<h3 id="95-recovery-testing-and-validation">9.5 Recovery Testing and Validation</h3>

<h4 id="951-disaster-recovery-testing-schedule">9.5.1 Disaster Recovery Testing Schedule</h4>

<p><strong>Monthly DR drill:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="c"># DR drill script - run in non-production environment</span>

<span class="nb">echo</span> <span class="s2">"Starting DR drill </span><span class="si">$(</span><span class="nb">date</span><span class="si">)</span><span class="s2">"</span>

<span class="c"># Test 1: Backup restoration</span>
velero restore create dr-test-<span class="si">$(</span><span class="nb">date</span> +%Y%m%d<span class="si">)</span> <span class="se">\</span>
  <span class="nt">--from-backup</span> latest-production-backup <span class="se">\</span>
  <span class="nt">--namespace-mappings</span> production:dr-test

<span class="c"># Test 2: Application functionality</span>
kubectl <span class="nt">-n</span> dr-test run test-pod <span class="nt">--image</span><span class="o">=</span>curlimages/curl <span class="nt">--rm</span> <span class="nt">-it</span> <span class="nt">--</span> <span class="se">\</span>
  curl http://web-app.dr-test.svc.cluster.local/health

<span class="c"># Test 3: Database connectivity</span>
kubectl <span class="nt">-n</span> dr-test <span class="nb">exec </span>deployment/app <span class="nt">--</span> <span class="se">\</span>
  pg_isready <span class="nt">-h</span> database.dr-test.svc.cluster.local

<span class="c"># Test 4: External dependencies</span>
kubectl <span class="nt">-n</span> dr-test <span class="nb">exec </span>deployment/app <span class="nt">--</span> <span class="se">\</span>
  curl <span class="nt">-f</span> https://api.external-service.com/health

<span class="nb">echo</span> <span class="s2">"DR drill completed. Check results manually."</span>
</code></pre></div></div>

<h4 id="952-recovery-time-objective-rto-validation">9.5.2 Recovery Time Objective (RTO) Validation</h4>

<p><strong>RTO measurement script:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="c"># Measure actual recovery times</span>

<span class="nv">START_TIME</span><span class="o">=</span><span class="si">$(</span><span class="nb">date</span> +%s<span class="si">)</span>

<span class="c"># Simulate cluster failure</span>
kubectl delete deployment <span class="nt">--all</span> <span class="nt">-n</span> production

<span class="c"># Start recovery</span>
velero restore create rto-test <span class="nt">--from-backup</span> latest-backup

<span class="c"># Wait for recovery completion</span>
<span class="k">while </span><span class="nb">true</span><span class="p">;</span> <span class="k">do
  </span><span class="nv">READY_PODS</span><span class="o">=</span><span class="si">$(</span>kubectl get pods <span class="nt">-n</span> production <span class="nt">--no-headers</span> | <span class="nb">grep </span>Running | <span class="nb">wc</span> <span class="nt">-l</span><span class="si">)</span>
  <span class="nv">TOTAL_PODS</span><span class="o">=</span><span class="si">$(</span>kubectl get pods <span class="nt">-n</span> production <span class="nt">--no-headers</span> | <span class="nb">wc</span> <span class="nt">-l</span><span class="si">)</span>
  
  <span class="k">if</span> <span class="o">[</span> <span class="nv">$READY_PODS</span> <span class="nt">-eq</span> <span class="nv">$TOTAL_PODS</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="o">[</span> <span class="nv">$TOTAL_PODS</span> <span class="nt">-gt</span> 0 <span class="o">]</span><span class="p">;</span> <span class="k">then
    </span><span class="nb">break
  </span><span class="k">fi
  
  </span><span class="nb">sleep </span>10
<span class="k">done

</span><span class="nv">END_TIME</span><span class="o">=</span><span class="si">$(</span><span class="nb">date</span> +%s<span class="si">)</span>
<span class="nv">RTO</span><span class="o">=</span><span class="k">$((</span>END_TIME <span class="o">-</span> START_TIME<span class="k">))</span>

<span class="nb">echo</span> <span class="s2">"Recovery completed in </span><span class="nv">$RTO</span><span class="s2"> seconds"</span>
<span class="nb">echo</span> <span class="s2">"RTO target: 1800 seconds (30 minutes)"</span>

<span class="k">if</span> <span class="o">[</span> <span class="nv">$RTO</span> <span class="nt">-lt</span> 1800 <span class="o">]</span><span class="p">;</span> <span class="k">then
  </span><span class="nb">echo</span> <span class="s2">"✅ RTO target met"</span>
<span class="k">else
  </span><span class="nb">echo</span> <span class="s2">"❌ RTO target exceeded"</span>
<span class="k">fi</span>
</code></pre></div></div>

<hr />

<h3 id="96-disaster-recovery-runbooks">9.6 Disaster Recovery Runbooks</h3>

<h4 id="961-complete-cluster-loss-runbook">9.6.1 “Complete cluster loss” Runbook</h4>

<p><strong>Symptoms:</strong> Cannot connect to cluster, AWS console shows cluster deleted/unavailable</p>

<p><strong>Step 1: Assess scope</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check if cluster exists</span>
aws eks describe-cluster <span class="nt">--name</span> production-cluster

<span class="c"># Check if it's a regional AWS issue</span>
curl <span class="nt">-s</span> https://status.aws.amazon.com/ | <span class="nb">grep</span> <span class="nt">-i</span> <span class="s2">"service issues"</span>
</code></pre></div></div>

<p><strong>Step 2: Activate DR procedures</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Switch DNS to DR region (if available)</span>
aws route53 change-resource-record-sets <span class="se">\</span>
  <span class="nt">--hosted-zone-id</span> Z123456789 <span class="se">\</span>
  <span class="nt">--change-batch</span> file://failover-to-dr.json

<span class="c"># Scale up DR cluster</span>
aws eks update-nodegroup-config <span class="se">\</span>
  <span class="nt">--cluster-name</span> production-dr <span class="se">\</span>
  <span class="nt">--nodegroup-name</span> dr-nodes <span class="se">\</span>
  <span class="nt">--scaling-config</span> <span class="nv">minSize</span><span class="o">=</span>3,maxSize<span class="o">=</span>20,desiredSize<span class="o">=</span>10
</code></pre></div></div>

<p><strong>Step 3: Recreate primary cluster</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Use backup configuration</span>
eksctl create cluster <span class="nt">--config-file</span><span class="o">=</span>./backups/cluster-config.yaml

<span class="c"># Restore applications</span>
velero restore create disaster-recovery <span class="se">\</span>
  <span class="nt">--from-backup</span> <span class="si">$(</span>velero backup get <span class="nt">-o</span> json | jq <span class="nt">-r</span> <span class="s1">'.items[0].metadata.name'</span><span class="si">)</span>
</code></pre></div></div>

<h4 id="962-data-corruption-runbook">9.6.2 “Data corruption” Runbook</h4>

<p><strong>Symptoms:</strong> Applications running but data is corrupted/missing</p>

<p><strong>Step 1: Stop writes immediately</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Scale down applications to prevent further corruption</span>
kubectl scale deployment <span class="nt">--replicas</span><span class="o">=</span>0 <span class="nt">-n</span> production <span class="nt">-l</span> <span class="nv">tier</span><span class="o">=</span>application

<span class="c"># Cordon nodes to prevent new pods</span>
kubectl cordon <span class="nt">--all</span>
</code></pre></div></div>

<p><strong>Step 2: Assess data integrity</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check database consistency</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> database-pod <span class="nt">--</span> pg_dump <span class="nt">--schema-only</span> mydb <span class="o">&gt;</span> schema-backup.sql

<span class="c"># Check persistent volume data</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> app-pod <span class="nt">--</span> find /data <span class="nt">-name</span> <span class="s2">"*.log"</span> <span class="nt">-mtime</span> <span class="nt">-1</span> | <span class="nb">head</span> <span class="nt">-10</span>
</code></pre></div></div>

<p><strong>Step 3: Restore from backup</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Restore database from point-in-time backup</span>
aws rds restore-db-instance-to-point-in-time <span class="se">\</span>
  <span class="nt">--source-db-instance-identifier</span> prod-db <span class="se">\</span>
  <span class="nt">--target-db-instance-identifier</span> prod-db-restored <span class="se">\</span>
  <span class="nt">--restore-time</span> <span class="si">$(</span><span class="nb">date</span> <span class="nt">-d</span> <span class="s1">'1 hour ago'</span> <span class="nt">-Iseconds</span><span class="si">)</span>

<span class="c"># Restore application data from Velero</span>
velero restore create data-recovery <span class="se">\</span>
  <span class="nt">--from-backup</span> latest-backup <span class="se">\</span>
  <span class="nt">--include-resources</span> persistentvolumeclaims,persistentvolumes
</code></pre></div></div>

<h4 id="963-region-wide-outage-runbook">9.6.3 “Region-wide outage” Runbook</h4>

<p><strong>Symptoms:</strong> All AWS services in primary region unavailable</p>

<p><strong>Step 1: Immediate failover</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Activate DR region immediately</span>
aws route53 change-resource-record-sets <span class="se">\</span>
  <span class="nt">--hosted-zone-id</span> Z123456789 <span class="se">\</span>
  <span class="nt">--change-batch</span> file://emergency-failover.json

<span class="c"># Scale DR cluster to handle production load</span>
kubectl scale deployment <span class="nt">--replicas</span><span class="o">=</span>5 <span class="nt">-n</span> production <span class="nt">-l</span> <span class="nv">tier</span><span class="o">=</span>web
kubectl scale deployment <span class="nt">--replicas</span><span class="o">=</span>3 <span class="nt">-n</span> production <span class="nt">-l</span> <span class="nv">tier</span><span class="o">=</span>api
</code></pre></div></div>

<p><strong>Step 2: Promote read replicas</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Promote RDS read replica to primary</span>
aws rds promote-read-replica <span class="se">\</span>
  <span class="nt">--db-instance-identifier</span> prod-db-replica <span class="se">\</span>
  <span class="nt">--region</span> us-east-1

<span class="c"># Update application configuration</span>
kubectl patch configmap app-config <span class="nt">-p</span> <span class="s1">'{"data":{"database_url":"prod-db-replica.us-east-1.rds.amazonaws.com"}}'</span>
</code></pre></div></div>

<p><strong>Step 3: Monitor and adjust</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Monitor application health in DR region</span>
kubectl get pods <span class="nt">-A</span> | <span class="nb">grep</span> <span class="nt">-v</span> Running
kubectl top nodes
kubectl top pods <span class="nt">-A</span> <span class="nt">--sort-by</span><span class="o">=</span>cpu
</code></pre></div></div>

<hr />

<h2 id="10-cost-optimization">10. Cost optimization</h2>

<p>Every engineering decision here directly hits the budget. Below: the cost levers that actually matter in production EKS and how to pull them without breaking reliability.</p>

<hr />

<h3 id="101-eks-cost-structure-where-your-money-goes">10.1 EKS Cost Structure (Where Your Money Goes)</h3>

<h4 id="1011-eks-cost-components">10.1.1 EKS Cost Components</h4>

<p><strong>Control plane costs:</strong></p>
<ul>
  <li>EKS cluster: $0.10/hour per cluster ($73/month)</li>
  <li>Fargate: $0.04048/vCPU/hour + $0.004445/GB/hour</li>
  <li>Data transfer costs (often overlooked)</li>
</ul>

<p><strong>Compute costs (largest component):</strong></p>
<ul>
  <li>EC2 instances for node groups</li>
  <li>EBS volumes for node storage</li>
  <li>Data transfer between AZs</li>
  <li>NAT Gateway costs for private subnets</li>
</ul>

<p><strong>Hidden costs:</strong></p>
<ul>
  <li>Load balancer costs (ALB/NLB)</li>
  <li>EBS snapshots and backups</li>
  <li>CloudWatch logs and metrics</li>
  <li>Cross-AZ data transfer</li>
</ul>

<h4 id="1012-cost-visibility-and-tracking">10.1.2 Cost Visibility and Tracking</h4>

<p><strong>Essential cost tracking:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Get EKS cluster costs</span>
aws ce get-cost-and-usage <span class="se">\</span>
  <span class="nt">--time-period</span> <span class="nv">Start</span><span class="o">=</span>2024-01-01,End<span class="o">=</span>2024-01-31 <span class="se">\</span>
  <span class="nt">--granularity</span> MONTHLY <span class="se">\</span>
  <span class="nt">--metrics</span> BlendedCost <span class="se">\</span>
  <span class="nt">--group-by</span> <span class="nv">Type</span><span class="o">=</span>DIMENSION,Key<span class="o">=</span>SERVICE <span class="se">\</span>
  <span class="nt">--filter</span> file://eks-cost-filter.json

<span class="c"># EKS cost filter</span>
<span class="nb">cat</span> <span class="o">&gt;</span> eks-cost-filter.json <span class="o">&lt;&lt;</span> <span class="no">EOF</span><span class="sh">
{
  "Dimensions": {
    "Key": "SERVICE",
    "Values": ["Amazon Elastic Kubernetes Service", "Amazon Elastic Compute Cloud"]
  }
}
</span><span class="no">EOF
</span></code></pre></div></div>

<p><strong>Resource tagging for cost allocation:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Node</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">cost-center</span><span class="pi">:</span> <span class="s2">"</span><span class="s">engineering"</span>
    <span class="na">environment</span><span class="pi">:</span> <span class="s2">"</span><span class="s">production"</span>
    <span class="na">team</span><span class="pi">:</span> <span class="s2">"</span><span class="s">platform"</span>
    <span class="na">project</span><span class="pi">:</span> <span class="s2">"</span><span class="s">web-app"</span>
</code></pre></div></div>

<hr />

<h3 id="102-right-sizing-workloads">10.2 Right-Sizing Workloads</h3>

<h4 id="1021-resource-request-optimization">10.2.1 Resource Request Optimization</h4>

<p><strong>The over-provisioning problem:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Find over-provisioned pods</span>
kubectl top pods <span class="nt">-A</span> <span class="nt">--sort-by</span><span class="o">=</span>cpu | <span class="nb">head</span> <span class="nt">-20</span>
kubectl top pods <span class="nt">-A</span> <span class="nt">--sort-by</span><span class="o">=</span>memory | <span class="nb">head</span> <span class="nt">-20</span>

<span class="c"># Compare requests vs actual usage</span>
kubectl get pods <span class="nt">-A</span> <span class="nt">-o</span> custom-columns<span class="o">=</span>NAME:.metadata.name,NAMESPACE:.metadata.namespace,CPU_REQ:.spec.containers[<span class="k">*</span><span class="o">]</span>.resources.requests.cpu,MEM_REQ:.spec.containers[<span class="k">*</span><span class="o">]</span>.resources.requests.memory
</code></pre></div></div>

<p><strong>VPA for right-sizing recommendations:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">autoscaling.k8s.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">VerticalPodAutoscaler</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">web-app-vpa</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">targetRef</span><span class="pi">:</span>
    <span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
    <span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">web-app</span>
  <span class="na">updatePolicy</span><span class="pi">:</span>
    <span class="na">updateMode</span><span class="pi">:</span> <span class="s2">"</span><span class="s">Off"</span>  <span class="c1"># Recommendation only</span>
  <span class="na">resourcePolicy</span><span class="pi">:</span>
    <span class="na">containerPolicies</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">containerName</span><span class="pi">:</span> <span class="s">web-app</span>
      <span class="na">maxAllowed</span><span class="pi">:</span>
        <span class="na">cpu</span><span class="pi">:</span> <span class="m">2</span>
        <span class="na">memory</span><span class="pi">:</span> <span class="s">4Gi</span>
      <span class="na">minAllowed</span><span class="pi">:</span>
        <span class="na">cpu</span><span class="pi">:</span> <span class="s">100m</span>
        <span class="na">memory</span><span class="pi">:</span> <span class="s">128Mi</span>
</code></pre></div></div>

<p><strong>Automated right-sizing script:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="c"># Generate right-sizing recommendations</span>

<span class="nb">echo</span> <span class="s2">"Analyzing resource usage for right-sizing..."</span>

<span class="k">for </span>namespace <span class="k">in</span> <span class="si">$(</span>kubectl get namespaces <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.items[*].metadata.name}'</span><span class="si">)</span><span class="p">;</span> <span class="k">do
  </span><span class="nb">echo</span> <span class="s2">"Namespace: </span><span class="nv">$namespace</span><span class="s2">"</span>
  
  kubectl top pods <span class="nt">-n</span> <span class="nv">$namespace</span> <span class="nt">--no-headers</span> | <span class="k">while </span><span class="nb">read </span>pod cpu memory<span class="p">;</span> <span class="k">do</span>
    <span class="c"># Get resource requests</span>
    <span class="nv">requests</span><span class="o">=</span><span class="si">$(</span>kubectl get pod <span class="nv">$pod</span> <span class="nt">-n</span> <span class="nv">$namespace</span> <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.spec.containers[*].resources.requests}'</span><span class="si">)</span>
    
    <span class="nb">echo</span> <span class="s2">"Pod: </span><span class="nv">$pod</span><span class="s2">"</span>
    <span class="nb">echo</span> <span class="s2">"  Current usage: CPU=</span><span class="nv">$cpu</span><span class="s2">, Memory=</span><span class="nv">$memory</span><span class="s2">"</span>
    <span class="nb">echo</span> <span class="s2">"  Requests: </span><span class="nv">$requests</span><span class="s2">"</span>
    <span class="nb">echo</span> <span class="s2">"  Recommendation: Review if requests match usage"</span>
    <span class="nb">echo</span> <span class="s2">""</span>
  <span class="k">done
done</span>
</code></pre></div></div>

<h4 id="1022-node-right-sizing">10.2.2 Node Right-Sizing</h4>

<p><strong>Instance type cost analysis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Compare instance costs per vCPU and per GB RAM</span>
aws ec2 describe-instance-types <span class="se">\</span>
  <span class="nt">--instance-types</span> m5.large m5.xlarge m5.2xlarge c5.large c5.xlarge <span class="se">\</span>
  <span class="nt">--query</span> <span class="s1">'InstanceTypes[*].[InstanceType,VCpuInfo.DefaultVCpus,MemoryInfo.SizeInMiB]'</span> <span class="se">\</span>
  <span class="nt">--output</span> table

<span class="c"># Get current pricing (requires AWS Pricing API)</span>
aws pricing get-products <span class="se">\</span>
  <span class="nt">--service-code</span> AmazonEC2 <span class="se">\</span>
  <span class="nt">--filters</span> <span class="nv">Type</span><span class="o">=</span>TERM_MATCH,Field<span class="o">=</span>instanceType,Value<span class="o">=</span>m5.large <span class="se">\</span>
  <span class="nt">--filters</span> <span class="nv">Type</span><span class="o">=</span>TERM_MATCH,Field<span class="o">=</span>location,Value<span class="o">=</span><span class="s2">"US West (Oregon)"</span>
</code></pre></div></div>

<p><strong>Node utilization analysis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check node resource utilization</span>
kubectl top nodes

<span class="c"># Detailed node analysis</span>
kubectl describe nodes | <span class="nb">grep</span> <span class="nt">-A</span> 5 <span class="s2">"Allocated resources"</span>

<span class="c"># Find underutilized nodes</span>
kubectl get nodes <span class="nt">-o</span> custom-columns<span class="o">=</span>NAME:.metadata.name,CPU_CAPACITY:.status.capacity.cpu,MEMORY_CAPACITY:.status.capacity.memory
</code></pre></div></div>

<hr />

<h3 id="103-spot-instances-and-mixed-instance-types">10.3 Spot Instances and Mixed Instance Types</h3>

<h4 id="1031-spot-instance-strategy">10.3.1 Spot Instance Strategy</h4>

<p><strong>Spot-optimized node group:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">eksctl.io/v1alpha5</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ClusterConfig</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">cost-optimized-cluster</span>
<span class="na">nodeGroups</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">spot-nodes</span>
  <span class="na">instanceTypes</span><span class="pi">:</span> 
  <span class="pi">-</span> <span class="s">m5.large</span>
  <span class="pi">-</span> <span class="s">m5.xlarge</span>
  <span class="pi">-</span> <span class="s">c5.large</span>
  <span class="pi">-</span> <span class="s">c5.xlarge</span>
  <span class="na">spot</span><span class="pi">:</span> <span class="no">true</span>
  <span class="na">minSize</span><span class="pi">:</span> <span class="m">2</span>
  <span class="na">maxSize</span><span class="pi">:</span> <span class="m">20</span>
  <span class="na">desiredCapacity</span><span class="pi">:</span> <span class="m">5</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">node-type</span><span class="pi">:</span> <span class="s">spot</span>
  <span class="na">taints</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">key</span><span class="pi">:</span> <span class="s">spot-instance</span>
    <span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">true"</span>
    <span class="na">effect</span><span class="pi">:</span> <span class="s">NoSchedule</span>
</code></pre></div></div>

<p><strong>Spot-tolerant workload configuration:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">batch-processor</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">5</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">tolerations</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">key</span><span class="pi">:</span> <span class="s">spot-instance</span>
        <span class="na">operator</span><span class="pi">:</span> <span class="s">Equal</span>
        <span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">true"</span>
        <span class="na">effect</span><span class="pi">:</span> <span class="s">NoSchedule</span>
      <span class="na">nodeSelector</span><span class="pi">:</span>
        <span class="na">node-type</span><span class="pi">:</span> <span class="s">spot</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">processor</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">batch-processor:latest</span>
        <span class="na">resources</span><span class="pi">:</span>
          <span class="na">requests</span><span class="pi">:</span>
            <span class="na">cpu</span><span class="pi">:</span> <span class="s">500m</span>
            <span class="na">memory</span><span class="pi">:</span> <span class="s">1Gi</span>
</code></pre></div></div>

<h4 id="1032-mixed-instance-type-strategy">10.3.2 Mixed Instance Type Strategy</h4>

<p><strong>Diversified node groups:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># On-demand for critical workloads</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">on-demand-critical</span>
  <span class="na">instanceTypes</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">m5.large"</span><span class="pi">]</span>
  <span class="na">minSize</span><span class="pi">:</span> <span class="m">2</span>
  <span class="na">maxSize</span><span class="pi">:</span> <span class="m">5</span>
  <span class="na">desiredCapacity</span><span class="pi">:</span> <span class="m">2</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">node-type</span><span class="pi">:</span> <span class="s">on-demand</span>
    <span class="na">workload-type</span><span class="pi">:</span> <span class="s">critical</span>

<span class="c1"># Spot for batch/stateless workloads  </span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">spot-batch</span>
  <span class="na">instanceTypes</span><span class="pi">:</span> 
  <span class="pi">-</span> <span class="s">m5.large</span>
  <span class="pi">-</span> <span class="s">m5.xlarge</span>
  <span class="pi">-</span> <span class="s">c5.large</span>
  <span class="pi">-</span> <span class="s">c5.xlarge</span>
  <span class="na">spot</span><span class="pi">:</span> <span class="no">true</span>
  <span class="na">minSize</span><span class="pi">:</span> <span class="m">0</span>
  <span class="na">maxSize</span><span class="pi">:</span> <span class="m">50</span>
  <span class="na">desiredCapacity</span><span class="pi">:</span> <span class="m">5</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">node-type</span><span class="pi">:</span> <span class="s">spot</span>
    <span class="na">workload-type</span><span class="pi">:</span> <span class="s">batch</span>
</code></pre></div></div>

<p><strong>Workload placement strategy:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Critical workloads on on-demand</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">payment-service</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">nodeSelector</span><span class="pi">:</span>
        <span class="na">node-type</span><span class="pi">:</span> <span class="s">on-demand</span>
        <span class="na">workload-type</span><span class="pi">:</span> <span class="s">critical</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">payment</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">payment-service:latest</span>

<span class="nn">---</span>
<span class="c1"># Batch workloads on spot</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">batch/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Job</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">data-processing</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">tolerations</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">key</span><span class="pi">:</span> <span class="s">spot-instance</span>
        <span class="na">operator</span><span class="pi">:</span> <span class="s">Equal</span>
        <span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">true"</span>
        <span class="na">effect</span><span class="pi">:</span> <span class="s">NoSchedule</span>
      <span class="na">nodeSelector</span><span class="pi">:</span>
        <span class="na">node-type</span><span class="pi">:</span> <span class="s">spot</span>
        <span class="na">workload-type</span><span class="pi">:</span> <span class="s">batch</span>
</code></pre></div></div>

<hr />

<h3 id="104-storage-cost-optimization">10.4 Storage Cost Optimization</h3>

<h4 id="1041-ebs-volume-optimization">10.4.1 EBS Volume Optimization</h4>

<p><strong>Storage class cost comparison:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># gp3 (newer, more cost-effective)</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">storage.k8s.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">StorageClass</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">gp3-optimized</span>
<span class="na">provisioner</span><span class="pi">:</span> <span class="s">ebs.csi.aws.com</span>
<span class="na">parameters</span><span class="pi">:</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">gp3</span>
  <span class="na">iops</span><span class="pi">:</span> <span class="s2">"</span><span class="s">3000"</span>      <span class="c1"># Baseline IOPS</span>
  <span class="na">throughput</span><span class="pi">:</span> <span class="s2">"</span><span class="s">125"</span>  <span class="c1"># Baseline throughput</span>
<span class="na">allowVolumeExpansion</span><span class="pi">:</span> <span class="no">true</span>
<span class="na">reclaimPolicy</span><span class="pi">:</span> <span class="s">Delete</span>

<span class="c1"># gp2 (legacy, more expensive for same performance)</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">storage.k8s.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">StorageClass</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">gp2-legacy</span>
<span class="na">provisioner</span><span class="pi">:</span> <span class="s">ebs.csi.aws.com</span>
<span class="na">parameters</span><span class="pi">:</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">gp2</span>
<span class="na">allowVolumeExpansion</span><span class="pi">:</span> <span class="no">true</span>
<span class="na">reclaimPolicy</span><span class="pi">:</span> <span class="s">Delete</span>
</code></pre></div></div>

<p><strong>Volume cleanup automation:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="c"># Clean up unused EBS volumes</span>

<span class="nb">echo</span> <span class="s2">"Finding unused EBS volumes..."</span>

<span class="c"># Get all EBS volumes</span>
aws ec2 describe-volumes <span class="se">\</span>
  <span class="nt">--filters</span> <span class="nv">Name</span><span class="o">=</span>status,Values<span class="o">=</span>available <span class="se">\</span>
  <span class="nt">--query</span> <span class="s1">'Volumes[*].[VolumeId,CreateTime,Size,VolumeType]'</span> <span class="se">\</span>
  <span class="nt">--output</span> table

<span class="c"># Find volumes older than 30 days with no attachments</span>
aws ec2 describe-volumes <span class="se">\</span>
  <span class="nt">--filters</span> <span class="nv">Name</span><span class="o">=</span>status,Values<span class="o">=</span>available <span class="se">\</span>
  <span class="nt">--query</span> <span class="s2">"Volumes[?CreateTime&lt;='</span><span class="si">$(</span><span class="nb">date</span> <span class="nt">-d</span> <span class="s1">'30 days ago'</span> <span class="nt">-Iseconds</span><span class="si">)</span><span class="s2">'].[VolumeId,CreateTime,Size]"</span> <span class="se">\</span>
  <span class="nt">--output</span> table

<span class="nb">echo</span> <span class="s2">"Review these volumes for deletion to reduce costs"</span>
</code></pre></div></div>

<h4 id="1042-persistent-volume-reclaim-policies">10.4.2 Persistent Volume Reclaim Policies</h4>

<p><strong>Cost-conscious reclaim policies:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># For development environments - Delete to avoid orphaned volumes</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">PersistentVolume</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">dev-pv</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">capacity</span><span class="pi">:</span>
    <span class="na">storage</span><span class="pi">:</span> <span class="s">10Gi</span>
  <span class="na">accessModes</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s">ReadWriteOnce</span>
  <span class="na">persistentVolumeReclaimPolicy</span><span class="pi">:</span> <span class="s">Delete</span>  <span class="c1"># Automatically delete when PVC is deleted</span>
  <span class="na">storageClassName</span><span class="pi">:</span> <span class="s">gp3-optimized</span>

<span class="c1"># For production - Retain for safety, but monitor for cleanup</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">PersistentVolume</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">prod-pv</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">capacity</span><span class="pi">:</span>
    <span class="na">storage</span><span class="pi">:</span> <span class="s">100Gi</span>
  <span class="na">accessModes</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s">ReadWriteOnce</span>
  <span class="na">persistentVolumeReclaimPolicy</span><span class="pi">:</span> <span class="s">Retain</span>  <span class="c1"># Manual cleanup required</span>
  <span class="na">storageClassName</span><span class="pi">:</span> <span class="s">gp3-optimized</span>
</code></pre></div></div>

<hr />

<h3 id="105-network-cost-optimization">10.5 Network Cost Optimization</h3>

<h4 id="1051-cross-az-data-transfer-reduction">10.5.1 Cross-AZ Data Transfer Reduction</h4>

<p><strong>Single-AZ node groups for specific workloads:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># For high-throughput, low-latency workloads</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">eksctl.io/v1alpha5</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ClusterConfig</span>
<span class="na">nodeGroups</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">single-az-compute</span>
  <span class="na">instanceTypes</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">c5n.xlarge"</span><span class="pi">]</span>
  <span class="na">availabilityZones</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">us-west-2a"</span><span class="pi">]</span>  <span class="c1"># Single AZ to avoid cross-AZ charges</span>
  <span class="na">minSize</span><span class="pi">:</span> <span class="m">2</span>
  <span class="na">maxSize</span><span class="pi">:</span> <span class="m">10</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">topology</span><span class="pi">:</span> <span class="s">single-az</span>
</code></pre></div></div>

<p><strong>Pod anti-affinity for AZ awareness:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">data-processor</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">3</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">affinity</span><span class="pi">:</span>
        <span class="na">podAntiAffinity</span><span class="pi">:</span>
          <span class="na">preferredDuringSchedulingIgnoredDuringExecution</span><span class="pi">:</span>
          <span class="pi">-</span> <span class="na">weight</span><span class="pi">:</span> <span class="m">100</span>
            <span class="na">podAffinityTerm</span><span class="pi">:</span>
              <span class="na">labelSelector</span><span class="pi">:</span>
                <span class="na">matchExpressions</span><span class="pi">:</span>
                <span class="pi">-</span> <span class="na">key</span><span class="pi">:</span> <span class="s">app</span>
                  <span class="na">operator</span><span class="pi">:</span> <span class="s">In</span>
                  <span class="na">values</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">data-processor"</span><span class="pi">]</span>
              <span class="na">topologyKey</span><span class="pi">:</span> <span class="s">topology.kubernetes.io/zone</span>
</code></pre></div></div>

<h4 id="1052-nat-gateway-cost-optimization">10.5.2 NAT Gateway Cost Optimization</h4>

<p><strong>NAT Gateway alternatives:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Option 1: NAT instances (cheaper for high traffic)</span>
<span class="c"># Create NAT instance instead of NAT Gateway for cost savings</span>

<span class="c"># Option 2: VPC endpoints for AWS services</span>
aws ec2 create-vpc-endpoint <span class="se">\</span>
  <span class="nt">--vpc-id</span> vpc-12345678 <span class="se">\</span>
  <span class="nt">--service-name</span> com.amazonaws.us-west-2.s3 <span class="se">\</span>
  <span class="nt">--route-table-ids</span> rtb-12345678

<span class="c"># Option 3: Public subnets for non-sensitive workloads</span>
<span class="c"># Move some workloads to public subnets to avoid NAT costs</span>
</code></pre></div></div>

<hr />

<h3 id="106-cluster-consolidation-and-multi-tenancy">10.6 Cluster Consolidation and Multi-Tenancy</h3>

<h4 id="1061-cluster-consolidation-strategy">10.6.1 Cluster Consolidation Strategy</h4>

<p><strong>When to consolidate clusters:</strong></p>
<ul>
  <li>Multiple small clusters with low utilization</li>
  <li>Similar security requirements</li>
  <li>Shared operational overhead</li>
</ul>

<p><strong>Namespace-based multi-tenancy:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Resource quotas per team</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ResourceQuota</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">team-a-quota</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">team-a</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">hard</span><span class="pi">:</span>
    <span class="na">requests.cpu</span><span class="pi">:</span> <span class="s2">"</span><span class="s">10"</span>
    <span class="na">requests.memory</span><span class="pi">:</span> <span class="s">20Gi</span>
    <span class="na">limits.cpu</span><span class="pi">:</span> <span class="s2">"</span><span class="s">20"</span>
    <span class="na">limits.memory</span><span class="pi">:</span> <span class="s">40Gi</span>
    <span class="na">persistentvolumeclaims</span><span class="pi">:</span> <span class="s2">"</span><span class="s">10"</span>
    <span class="na">services.loadbalancers</span><span class="pi">:</span> <span class="s2">"</span><span class="s">2"</span>

<span class="nn">---</span>
<span class="c1"># Network policies for isolation</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">networking.k8s.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">NetworkPolicy</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">team-a-isolation</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">team-a</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">podSelector</span><span class="pi">:</span> <span class="pi">{}</span>
  <span class="na">policyTypes</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s">Ingress</span>
  <span class="pi">-</span> <span class="s">Egress</span>
  <span class="na">ingress</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">from</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">namespaceSelector</span><span class="pi">:</span>
        <span class="na">matchLabels</span><span class="pi">:</span>
          <span class="na">name</span><span class="pi">:</span> <span class="s">team-a</span>
  <span class="na">egress</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">to</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">namespaceSelector</span><span class="pi">:</span>
        <span class="na">matchLabels</span><span class="pi">:</span>
          <span class="na">name</span><span class="pi">:</span> <span class="s">team-a</span>
  <span class="pi">-</span> <span class="na">to</span><span class="pi">:</span> <span class="pi">[]</span>  <span class="c1"># Allow egress to internet</span>
    <span class="na">ports</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
      <span class="na">port</span><span class="pi">:</span> <span class="m">443</span>
    <span class="pi">-</span> <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
      <span class="na">port</span><span class="pi">:</span> <span class="m">80</span>
</code></pre></div></div>

<h4 id="1062-shared-services-strategy">10.6.2 Shared Services Strategy</h4>

<p><strong>Centralized monitoring and logging:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Shared monitoring namespace</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Namespace</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">monitoring</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">shared-service</span><span class="pi">:</span> <span class="s2">"</span><span class="s">true"</span>

<span class="nn">---</span>
<span class="c1"># Prometheus for all teams</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">prometheus</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">monitoring</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">prometheus</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">prom/prometheus:latest</span>
        <span class="na">resources</span><span class="pi">:</span>
          <span class="na">requests</span><span class="pi">:</span>
            <span class="na">cpu</span><span class="pi">:</span> <span class="m">2</span>
            <span class="na">memory</span><span class="pi">:</span> <span class="s">4Gi</span>
          <span class="na">limits</span><span class="pi">:</span>
            <span class="na">cpu</span><span class="pi">:</span> <span class="m">4</span>
            <span class="na">memory</span><span class="pi">:</span> <span class="s">8Gi</span>
</code></pre></div></div>

<hr />

<h3 id="107-cost-monitoring-and-alerting">10.7 Cost Monitoring and Alerting</h3>

<h4 id="1071-cost-anomaly-detection">10.7.1 Cost Anomaly Detection</h4>

<p><strong>CloudWatch cost alerts:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Create cost budget with alerts</span>
aws budgets create-budget <span class="se">\</span>
  <span class="nt">--account-id</span> 123456789012 <span class="se">\</span>
  <span class="nt">--budget</span> file://eks-cost-budget.json

<span class="c"># Budget configuration</span>
<span class="nb">cat</span> <span class="o">&gt;</span> eks-cost-budget.json <span class="o">&lt;&lt;</span> <span class="no">EOF</span><span class="sh">
{
  "BudgetName": "EKS-Monthly-Budget",
  "BudgetLimit": {
    "Amount": "5000",
    "Unit": "USD"
  },
  "TimeUnit": "MONTHLY",
  "BudgetType": "COST",
  "CostFilters": {
    "Service": ["Amazon Elastic Kubernetes Service", "Amazon Elastic Compute Cloud"]
  }
}
</span><span class="no">EOF
</span></code></pre></div></div>

<h4 id="1072-resource-utilization-monitoring">10.7.2 Resource Utilization Monitoring</h4>

<p><strong>Cluster cost efficiency metrics:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="c"># Calculate cluster cost efficiency</span>

<span class="c"># Get total cluster capacity</span>
<span class="nv">TOTAL_CPU</span><span class="o">=</span><span class="si">$(</span>kubectl get nodes <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.items[*].status.capacity.cpu}'</span> | <span class="nb">tr</span> <span class="s1">' '</span> <span class="s1">'+'</span> | bc<span class="si">)</span>
<span class="nv">TOTAL_MEMORY</span><span class="o">=</span><span class="si">$(</span>kubectl get nodes <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.items[*].status.capacity.memory}'</span> | <span class="nb">sed</span> <span class="s1">'s/Ki//g'</span> | <span class="nb">tr</span> <span class="s1">' '</span> <span class="s1">'+'</span> | bc<span class="si">)</span>

<span class="c"># Get allocated resources</span>
<span class="nv">ALLOCATED_CPU</span><span class="o">=</span><span class="si">$(</span>kubectl describe nodes | <span class="nb">grep</span> <span class="nt">-A</span> 5 <span class="s2">"Allocated resources"</span> | <span class="nb">grep</span> <span class="s2">"cpu"</span> | <span class="nb">awk</span> <span class="s1">'{print $2}'</span> | <span class="nb">sed</span> <span class="s1">'s/[^0-9]//g'</span> | <span class="nb">tr</span> <span class="s1">'\n'</span> <span class="s1">'+'</span> | <span class="nb">sed</span> <span class="s1">'s/+$//'</span> | bc<span class="si">)</span>
<span class="nv">ALLOCATED_MEMORY</span><span class="o">=</span><span class="si">$(</span>kubectl describe nodes | <span class="nb">grep</span> <span class="nt">-A</span> 5 <span class="s2">"Allocated resources"</span> | <span class="nb">grep</span> <span class="s2">"memory"</span> | <span class="nb">awk</span> <span class="s1">'{print $2}'</span> | <span class="nb">sed</span> <span class="s1">'s/[^0-9]//g'</span> | <span class="nb">tr</span> <span class="s1">'\n'</span> <span class="s1">'+'</span> | <span class="nb">sed</span> <span class="s1">'s/+$//'</span> | bc<span class="si">)</span>

<span class="c"># Calculate utilization</span>
<span class="nv">CPU_UTILIZATION</span><span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="s2">"scale=2; </span><span class="nv">$ALLOCATED_CPU</span><span class="s2"> / </span><span class="nv">$TOTAL_CPU</span><span class="s2"> * 100"</span> | bc<span class="si">)</span>
<span class="nv">MEMORY_UTILIZATION</span><span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="s2">"scale=2; </span><span class="nv">$ALLOCATED_MEMORY</span><span class="s2"> / </span><span class="nv">$TOTAL_MEMORY</span><span class="s2"> * 100"</span> | bc<span class="si">)</span>

<span class="nb">echo</span> <span class="s2">"Cluster Resource Utilization:"</span>
<span class="nb">echo</span> <span class="s2">"CPU: </span><span class="k">${</span><span class="nv">CPU_UTILIZATION</span><span class="k">}</span><span class="s2">%"</span>
<span class="nb">echo</span> <span class="s2">"Memory: </span><span class="k">${</span><span class="nv">MEMORY_UTILIZATION</span><span class="k">}</span><span class="s2">%"</span>

<span class="c"># Alert if utilization is too low (waste) or too high (risk)</span>
<span class="k">if</span> <span class="o">((</span> <span class="si">$(</span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$CPU_UTILIZATION</span><span class="s2"> &lt; 30"</span> | bc <span class="nt">-l</span><span class="si">)</span> <span class="o">))</span><span class="p">;</span> <span class="k">then
  </span><span class="nb">echo</span> <span class="s2">"⚠️  Low CPU utilization - consider downsizing"</span>
<span class="k">elif</span> <span class="o">((</span> <span class="si">$(</span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$CPU_UTILIZATION</span><span class="s2"> &gt; 80"</span> | bc <span class="nt">-l</span><span class="si">)</span> <span class="o">))</span><span class="p">;</span> <span class="k">then
  </span><span class="nb">echo</span> <span class="s2">"⚠️  High CPU utilization - consider scaling up"</span>
<span class="k">fi</span>
</code></pre></div></div>

<hr />

<h3 id="108-cost-optimization-runbooks">10.8 Cost Optimization Runbooks</h3>

<h4 id="1081-monthly-cost-spike-investigation">10.8.1 “Monthly cost spike” Investigation</h4>

<p><strong>Step 1: Identify cost drivers</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Get cost breakdown by service</span>
aws ce get-cost-and-usage <span class="se">\</span>
  <span class="nt">--time-period</span> <span class="nv">Start</span><span class="o">=</span><span class="si">$(</span><span class="nb">date</span> <span class="nt">-d</span> <span class="s1">'last month'</span> +%Y-%m-01<span class="si">)</span>,End<span class="o">=</span><span class="si">$(</span><span class="nb">date</span> +%Y-%m-01<span class="si">)</span> <span class="se">\</span>
  <span class="nt">--granularity</span> MONTHLY <span class="se">\</span>
  <span class="nt">--metrics</span> BlendedCost <span class="se">\</span>
  <span class="nt">--group-by</span> <span class="nv">Type</span><span class="o">=</span>DIMENSION,Key<span class="o">=</span>SERVICE

<span class="c"># Get cost by resource tags</span>
aws ce get-cost-and-usage <span class="se">\</span>
  <span class="nt">--time-period</span> <span class="nv">Start</span><span class="o">=</span><span class="si">$(</span><span class="nb">date</span> <span class="nt">-d</span> <span class="s1">'last month'</span> +%Y-%m-01<span class="si">)</span>,End<span class="o">=</span><span class="si">$(</span><span class="nb">date</span> +%Y-%m-01<span class="si">)</span> <span class="se">\</span>
  <span class="nt">--granularity</span> MONTHLY <span class="se">\</span>
  <span class="nt">--metrics</span> BlendedCost <span class="se">\</span>
  <span class="nt">--group-by</span> <span class="nv">Type</span><span class="o">=</span>TAG,Key<span class="o">=</span>Environment
</code></pre></div></div>

<p><strong>Step 2: Analyze resource usage</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check for resource over-provisioning</span>
kubectl top nodes
kubectl top pods <span class="nt">-A</span> <span class="nt">--sort-by</span><span class="o">=</span>cpu | <span class="nb">head</span> <span class="nt">-20</span>

<span class="c"># Look for unused resources</span>
kubectl get pvc <span class="nt">-A</span> | <span class="nb">grep</span> <span class="nt">-v</span> Bound
aws ec2 describe-volumes <span class="nt">--filters</span> <span class="nv">Name</span><span class="o">=</span>status,Values<span class="o">=</span>available
</code></pre></div></div>

<p><strong>Step 3: Implement immediate cost reductions</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Scale down non-production environments</span>
kubectl scale deployment <span class="nt">--replicas</span><span class="o">=</span>0 <span class="nt">-n</span> staging <span class="nt">--all</span>
kubectl scale deployment <span class="nt">--replicas</span><span class="o">=</span>1 <span class="nt">-n</span> development <span class="nt">--all</span>

<span class="c"># Clean up unused resources</span>
kubectl delete pvc <span class="nt">-A</span> <span class="nt">--field-selector</span><span class="o">=</span>status.phase<span class="o">=</span>Pending
</code></pre></div></div>

<h4 id="1082-right-sizing-recommendations-runbook">10.8.2 “Right-sizing recommendations” Runbook</h4>

<p><strong>Step 1: Collect usage data</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Install VPA recommender</span>
kubectl apply <span class="nt">-f</span> https://github.com/kubernetes/autoscaler/releases/download/vertical-pod-autoscaler-0.13.0/vpa-release.yaml

<span class="c"># Create VPA for all deployments</span>
<span class="k">for </span>deployment <span class="k">in</span> <span class="si">$(</span>kubectl get deployments <span class="nt">-A</span> <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.items[*].metadata.name}'</span><span class="si">)</span><span class="p">;</span> <span class="k">do
  </span>kubectl create vpa <span class="k">${</span><span class="nv">deployment</span><span class="k">}</span><span class="nt">-vpa</span> <span class="nt">--target-ref</span><span class="o">=</span>Deployment/<span class="k">${</span><span class="nv">deployment</span><span class="k">}</span> <span class="nt">--update-mode</span><span class="o">=</span>Off
<span class="k">done</span>
</code></pre></div></div>

<p><strong>Step 2: Analyze recommendations</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Get VPA recommendations</span>
kubectl get vpa <span class="nt">-A</span> <span class="nt">-o</span> custom-columns<span class="o">=</span>NAME:.metadata.name,NAMESPACE:.metadata.namespace,CPU_TARGET:.status.recommendation.containerRecommendations[0].target.cpu,MEMORY_TARGET:.status.recommendation.containerRecommendations[0].target.memory
</code></pre></div></div>

<p><strong>Step 3: Apply optimizations</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Update deployment with new resource requests</span>
kubectl patch deployment web-app <span class="nt">-p</span> <span class="s1">'{"spec":{"template":{"spec":{"containers":[{"name":"web-app","resources":{"requests":{"cpu":"200m","memory":"256Mi"}}}]}}}}'</span>
</code></pre></div></div>

<hr />

<h2 id="11-troubleshooting-cookbook">11. Troubleshooting cookbook</h2>

<p>Step-by-step solutions for the most common EKS production failures. Symptoms, diagnosis, root cause, and fixes you can run under pressure.</p>

<hr />

<h3 id="111-pod-scheduling-failures">11.1 Pod Scheduling Failures</h3>

<h4 id="1111-pods-stuck-in-pending-state">11.1.1 “Pods stuck in Pending state”</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>New pods remain in Pending status</li>
  <li><code class="language-plaintext highlighter-rouge">kubectl get pods</code> shows Pending for extended periods</li>
  <li>Applications fail to scale up</li>
</ul>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check pod events for scheduling failures</span>
kubectl describe pod &lt;pending-pod&gt;

<span class="c"># Check node resource availability</span>
kubectl describe nodes | <span class="nb">grep</span> <span class="nt">-A</span> 5 <span class="s2">"Allocated resources"</span>

<span class="c"># Check for taints blocking scheduling</span>
kubectl get nodes <span class="nt">-o</span> custom-columns<span class="o">=</span>NAME:.metadata.name,TAINTS:.spec.taints
</code></pre></div></div>

<p><strong>Common root causes and fixes:</strong></p>

<p><strong>Insufficient resources:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check cluster capacity</span>
kubectl top nodes

<span class="c"># Scale cluster if needed</span>
aws eks update-nodegroup-config <span class="se">\</span>
  <span class="nt">--cluster-name</span> production-cluster <span class="se">\</span>
  <span class="nt">--nodegroup-name</span> primary <span class="se">\</span>
  <span class="nt">--scaling-config</span> <span class="nv">minSize</span><span class="o">=</span>3,maxSize<span class="o">=</span>20,desiredSize<span class="o">=</span>10
</code></pre></div></div>

<p><strong>Node selector mismatch:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check pod node selector</span>
kubectl get pod &lt;pod&gt; <span class="nt">-o</span> yaml | <span class="nb">grep</span> <span class="nt">-A</span> 5 nodeSelector

<span class="c"># Check available node labels</span>
kubectl get nodes <span class="nt">--show-labels</span>

<span class="c"># Fix: Update pod spec or add labels to nodes</span>
kubectl label node &lt;node-name&gt; <span class="nv">environment</span><span class="o">=</span>production
</code></pre></div></div>

<p><strong>Taints and tolerations:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Remove problematic taint</span>
kubectl taint node &lt;node-name&gt; key:NoSchedule-

<span class="c"># Or add toleration to pod</span>
kubectl patch deployment &lt;deployment&gt; <span class="nt">-p</span> <span class="s1">'{
  "spec": {
    "template": {
      "spec": {
        "tolerations": [{
          "key": "key",
          "operator": "Equal",
          "value": "value",
          "effect": "NoSchedule"
        }]
      }
    }
  }
}'</span>
</code></pre></div></div>

<h4 id="1112-cluster-autoscaler-thrashing-rapid-scale-updown">11.1.2 “Cluster Autoscaler thrashing (rapid scale up/down)”</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Nodes constantly being added and removed</li>
  <li>Workload instability during scaling events</li>
  <li>High AWS costs from node churn</li>
</ul>

<p><strong>Root cause:</strong> Flaky readiness probes causing pods to appear unschedulable.</p>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check Cluster Autoscaler logs</span>
kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> <span class="nv">app</span><span class="o">=</span>cluster-autoscaler <span class="nt">--tail</span><span class="o">=</span>100

<span class="c"># Look for rapid scale events</span>
kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> <span class="nv">app</span><span class="o">=</span>cluster-autoscaler | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s2">"(scale-up|scale-down)"</span>

<span class="c"># Check pod readiness probe failures</span>
kubectl get events <span class="nt">--field-selector</span> <span class="nv">reason</span><span class="o">=</span>Unhealthy <span class="nt">--sort-by</span><span class="o">=</span><span class="s1">'.lastTimestamp'</span>
</code></pre></div></div>

<p><strong>Fix:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Identify problematic deployment</span>
kubectl describe pod &lt;failing-pod&gt; | <span class="nb">grep</span> <span class="nt">-A</span> 10 <span class="s2">"Readiness probe failed"</span>

<span class="c"># Fix readiness probe configuration</span>
kubectl patch deployment &lt;deployment&gt; <span class="nt">-p</span> <span class="s1">'{
  "spec": {
    "template": {
      "spec": {
        "containers": [{
          "name": "&lt;container&gt;",
          "readinessProbe": {
            "initialDelaySeconds": 30,
            "periodSeconds": 10,
            "timeoutSeconds": 5,
            "failureThreshold": 3,
            "successThreshold": 1
          }
        }]
      }
    }
  }
}'</span>

<span class="c"># Tune Cluster Autoscaler to reduce thrashing</span>
kubectl <span class="nt">-n</span> kube-system patch deployment cluster-autoscaler <span class="nt">-p</span> <span class="s1">'{
  "spec": {
    "template": {
      "spec": {
        "containers": [{
          "name": "cluster-autoscaler",
          "command": [
            "./cluster-autoscaler",
            "--scale-down-delay-after-add=10m",
            "--scale-down-unneeded-time=10m",
            "--skip-nodes-with-local-storage=false"
          ]
        }]
      }
    }
  }
}'</span>
</code></pre></div></div>

<h4 id="1113-diskpressure-causing-pod-evictions">11.1.3 “DiskPressure causing pod evictions”</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Pods being evicted with reason “DiskPressure”</li>
  <li>Node conditions show DiskPressure=True</li>
  <li>Container image pulls failing</li>
</ul>

<p><strong>Root cause:</strong> Large container images or excessive logging filling node disk.</p>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check node disk usage</span>
kubectl get nodes <span class="nt">-o</span> custom-columns<span class="o">=</span>NAME:.metadata.name,DISK-PRESSURE:.status.conditions[?<span class="o">(</span>@.type<span class="o">==</span><span class="se">\"</span>DiskPressure<span class="se">\"</span><span class="o">)]</span>.status

<span class="c"># Check disk usage on specific node</span>
kubectl debug node/&lt;node-name&gt; <span class="nt">-it</span> <span class="nt">--image</span><span class="o">=</span>busybox <span class="nt">--</span> <span class="nb">df</span> <span class="nt">-h</span>

<span class="c"># Check container image sizes</span>
kubectl debug node/&lt;node-name&gt; <span class="nt">-it</span> <span class="nt">--image</span><span class="o">=</span>busybox <span class="nt">--</span> crictl images | <span class="nb">sort</span> <span class="nt">-k2</span> <span class="nt">-h</span>

<span class="c"># Check log sizes</span>
kubectl debug node/&lt;node-name&gt; <span class="nt">-it</span> <span class="nt">--image</span><span class="o">=</span>busybox <span class="nt">--</span> <span class="nb">du</span> <span class="nt">-sh</span> /var/log/containers/<span class="k">*</span>
</code></pre></div></div>

<p><strong>Fix:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Clean up unused images</span>
kubectl debug node/&lt;node-name&gt; <span class="nt">-it</span> <span class="nt">--image</span><span class="o">=</span>busybox <span class="nt">--</span> crictl rmi <span class="nt">--prune</span>

<span class="c"># Restart containerd to clear cache</span>
kubectl debug node/&lt;node-name&gt; <span class="nt">-it</span> <span class="nt">--image</span><span class="o">=</span>busybox <span class="nt">--</span> systemctl restart containerd

<span class="c"># For EKS managed nodes, increase disk size</span>
aws eks update-nodegroup-config <span class="se">\</span>
  <span class="nt">--cluster-name</span> production-cluster <span class="se">\</span>
  <span class="nt">--nodegroup-name</span> primary <span class="se">\</span>
  <span class="nt">--launch-template</span> <span class="nv">name</span><span class="o">=</span>eks-node-template,version<span class="o">=</span>2

<span class="c"># Configure log rotation</span>
kubectl apply <span class="nt">-f</span> - <span class="o">&lt;&lt;</span><span class="no">EOF</span><span class="sh">
apiVersion: v1
kind: ConfigMap
metadata:
  name: fluent-bit-config
  namespace: amazon-cloudwatch
data:
  fluent-bit.conf: |
    [SERVICE]
        Flush         1
        Log_Level     info
        Daemon        off
        Parsers_File  parsers.conf
        HTTP_Server   On
        HTTP_Listen   0.0.0.0
        HTTP_Port     2020
        storage.path  /var/fluent-bit/state/flb-storage/
        storage.sync  normal
        storage.checksum off
        storage.backlog.mem_limit 5M
        
    [INPUT]
        Name              tail
        Tag               application.*
        Exclude_Path      /var/log/containers/cloudwatch-agent*, /var/log/containers/fluent-bit*
        Path              /var/log/containers/*.log
        Docker_Mode       On
        Docker_Mode_Flush 5
        Docker_Mode_Parser container_firstline
        Parser            docker
        DB                /var/fluent-bit/state/flb_container.db
        Mem_Buf_Limit     50MB
        Skip_Long_Lines   On
        Refresh_Interval  10
        Rotate_Wait       30
        storage.type      filesystem
        Read_from_Head    Off
</span><span class="no">EOF
</span></code></pre></div></div>

<h4 id="1114-zombie-pods-with-stuck-finalizers">11.1.4 “Zombie pods with stuck finalizers”</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Pods stuck in Terminating state for extended periods</li>
  <li><code class="language-plaintext highlighter-rouge">kubectl delete pod --force</code> doesn’t work</li>
  <li>Namespace deletion hangs</li>
</ul>

<p><strong>Root cause:</strong> Custom finalizers not being processed due to controller failures.</p>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Find pods with finalizers</span>
kubectl get pods <span class="nt">--all-namespaces</span> <span class="nt">-o</span> json | jq <span class="nt">-r</span> <span class="s1">'.items[] | select(.metadata.finalizers != null) | "\(.metadata.namespace)/\(.metadata.name): \(.metadata.finalizers)"'</span>

<span class="c"># Check specific pod finalizers</span>
kubectl get pod &lt;pod-name&gt; <span class="nt">-o</span> json | jq <span class="s1">'.metadata.finalizers'</span>

<span class="c"># Check if controller managing finalizer is running</span>
kubectl get pods <span class="nt">-n</span> &lt;controller-namespace&gt; | <span class="nb">grep</span> &lt;controller-name&gt;
</code></pre></div></div>

<p><strong>Fix:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Remove finalizers manually (DANGEROUS - only if controller is confirmed dead)</span>
kubectl patch pod &lt;pod-name&gt; <span class="nt">-p</span> <span class="s1">'{"metadata":{"finalizers":[]}}'</span> <span class="nt">--type</span><span class="o">=</span>merge

<span class="c"># For namespace stuck in terminating</span>
kubectl get namespace &lt;namespace&gt; <span class="nt">-o</span> json | jq <span class="s1">'.spec.finalizers = []'</span> | kubectl replace <span class="nt">--raw</span> <span class="s2">"/api/v1/namespaces/&lt;namespace&gt;/finalize"</span> <span class="nt">-f</span> -

<span class="c"># Restart the controller managing the finalizer</span>
kubectl <span class="nt">-n</span> &lt;controller-namespace&gt; rollout restart deployment &lt;controller-name&gt;
</code></pre></div></div>

<h3 id="112-api-and-internal-networking-failures">11.2 API and Internal Networking Failures</h3>

<h4 id="1121-api-server-throttling-causing-cluster-inaccessibility">11.2.1 “API Server throttling causing cluster inaccessibility”</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">kubectl</code> commands timing out or returning 429 errors</li>
  <li>Applications unable to communicate with API server</li>
  <li>High API server latency in metrics</li>
</ul>

<p><strong>Root cause:</strong> Too many concurrent API requests overwhelming the API server.</p>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check API server metrics</span>
kubectl top pods <span class="nt">-n</span> kube-system | <span class="nb">grep </span>kube-apiserver

<span class="c"># Check for throttling in API server logs (EKS managed - use CloudWatch)</span>
aws logs filter-log-events <span class="se">\</span>
  <span class="nt">--log-group-name</span> /aws/eks/production-cluster/cluster <span class="se">\</span>
  <span class="nt">--filter-pattern</span> <span class="s2">"throttling"</span>

<span class="c"># Identify high-volume API clients</span>
kubectl get events <span class="nt">--sort-by</span><span class="o">=</span><span class="s1">'.lastTimestamp'</span> | <span class="nb">head</span> <span class="nt">-20</span>
</code></pre></div></div>

<p><strong>Fix:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Identify and throttle problematic controllers</span>
kubectl get deployments <span class="nt">--all-namespaces</span> <span class="nt">-o</span> wide | <span class="nb">grep</span> <span class="nt">-v</span> <span class="s2">"1/1"</span>

<span class="c"># Scale down misbehaving controllers temporarily</span>
kubectl scale deployment &lt;problematic-controller&gt; <span class="nt">--replicas</span><span class="o">=</span>0 <span class="nt">-n</span> &lt;namespace&gt;

<span class="c"># For custom controllers, implement exponential backoff</span>
<span class="c"># Add rate limiting to controller reconcile loops</span>
</code></pre></div></div>

<h4 id="1122-pod-to-pod-communication-failures">11.2.2 “Pod-to-pod communication failures”</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Services unreachable from other pods</li>
  <li>Intermittent connection timeouts</li>
  <li>DNS resolution working but connections failing</li>
</ul>

<p><strong>Root cause:</strong> Network policies, security groups, or CNI issues.</p>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Test basic connectivity</span>
kubectl run debug-pod <span class="nt">--image</span><span class="o">=</span>busybox <span class="nt">-it</span> <span class="nt">--rm</span> <span class="nt">--</span> sh
<span class="c"># Inside pod: nslookup &lt;service-name&gt;.&lt;namespace&gt;.svc.cluster.local</span>
<span class="c"># Inside pod: wget -qO- &lt;service-name&gt;.&lt;namespace&gt;.svc.cluster.local:8080</span>

<span class="c"># Check network policies</span>
kubectl get networkpolicies <span class="nt">--all-namespaces</span>

<span class="c"># Check AWS security groups (for pods using security groups)</span>
aws ec2 describe-security-groups <span class="nt">--group-ids</span> &lt;sg-id&gt;

<span class="c"># Check CNI plugin status</span>
kubectl <span class="nt">-n</span> kube-system get pods <span class="nt">-l</span> k8s-app<span class="o">=</span>aws-node
kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> k8s-app<span class="o">=</span>aws-node
</code></pre></div></div>

<p><strong>Fix:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Allow traffic in network policy</span>
kubectl apply <span class="nt">-f</span> - <span class="o">&lt;&lt;</span><span class="no">EOF</span><span class="sh">
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-debug-traffic
  namespace: &lt;target-namespace&gt;
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: &lt;source-namespace&gt;
    ports:
    - protocol: TCP
      port: 8080
</span><span class="no">EOF

</span><span class="c"># Restart CNI pods if needed</span>
kubectl <span class="nt">-n</span> kube-system delete pods <span class="nt">-l</span> k8s-app<span class="o">=</span>aws-node
</code></pre></div></div>

<h3 id="113-resource-management-issues">11.3 Resource Management Issues</h3>

<h4 id="1131-cronjobs-exhausting-cluster-resources">11.3.1 “CronJobs exhausting cluster resources”</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Cluster resource exhaustion during scheduled job runs</li>
  <li>Multiple CronJobs running simultaneously</li>
  <li>Node resource pressure during specific time windows</li>
</ul>

<p><strong>Root cause:</strong> CronJobs without resource limits running concurrently.</p>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check running CronJobs</span>
kubectl get cronjobs <span class="nt">--all-namespaces</span>

<span class="c"># Check job resource usage</span>
kubectl top pods <span class="nt">--all-namespaces</span> | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s2">"(job|cron)"</span>

<span class="c"># Check CronJob schedules for overlap</span>
kubectl get cronjobs <span class="nt">--all-namespaces</span> <span class="nt">-o</span> custom-columns<span class="o">=</span>NAME:.metadata.name,SCHEDULE:.spec.schedule,SUSPEND:.spec.suspend
</code></pre></div></div>

<p><strong>Fix:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Add resource limits to CronJob</span>
kubectl patch cronjob &lt;cronjob-name&gt; <span class="nt">-p</span> <span class="s1">'{
  "spec": {
    "jobTemplate": {
      "spec": {
        "template": {
          "spec": {
            "containers": [{
              "name": "&lt;container-name&gt;",
              "resources": {
                "requests": {
                  "cpu": "100m",
                  "memory": "256Mi"
                },
                "limits": {
                  "cpu": "500m",
                  "memory": "512Mi"
                }
              }
            }]
          }
        }
      }
    }
  }
}'</span>

<span class="c"># Prevent concurrent executions</span>
kubectl patch cronjob &lt;cronjob-name&gt; <span class="nt">-p</span> <span class="s1">'{
  "spec": {
    "concurrencyPolicy": "Forbid"
  }
}'</span>

<span class="c"># Stagger CronJob schedules</span>
kubectl patch cronjob &lt;cronjob-name&gt; <span class="nt">-p</span> <span class="s1">'{
  "spec": {
    "schedule": "5 2 * * *"
  }
}'</span>
</code></pre></div></div>

<h4 id="1132-excessive-logging-filling-node-disk">11.3.2 “Excessive logging filling node disk”</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Node DiskPressure conditions</li>
  <li>Pod evictions due to disk space</li>
  <li><code class="language-plaintext highlighter-rouge">/var/log/containers/</code> consuming excessive space</li>
</ul>

<p><strong>Root cause:</strong> Applications logging at debug level or without log rotation.</p>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check disk usage on nodes</span>
kubectl get nodes <span class="nt">-o</span> custom-columns<span class="o">=</span>NAME:.metadata.name,DISK-PRESSURE:.status.conditions[?<span class="o">(</span>@.type<span class="o">==</span><span class="se">\"</span>DiskPressure<span class="se">\"</span><span class="o">)]</span>.status

<span class="c"># Find largest log files</span>
kubectl debug node/&lt;node-name&gt; <span class="nt">-it</span> <span class="nt">--image</span><span class="o">=</span>busybox <span class="nt">--</span> <span class="nb">du</span> <span class="nt">-sh</span> /var/log/containers/<span class="k">*</span> | <span class="nb">sort</span> <span class="nt">-h</span> | <span class="nb">tail</span> <span class="nt">-10</span>

<span class="c"># Check specific pod log size</span>
kubectl debug node/&lt;node-name&gt; <span class="nt">-it</span> <span class="nt">--image</span><span class="o">=</span>busybox <span class="nt">--</span> <span class="nb">ls</span> <span class="nt">-lah</span> /var/log/containers/&lt;pod-name&gt;<span class="k">*</span>
</code></pre></div></div>

<p><strong>Fix:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Reduce log level in application</span>
kubectl <span class="nb">set env </span>deployment/&lt;deployment-name&gt; <span class="nv">LOG_LEVEL</span><span class="o">=</span>INFO

<span class="c"># Configure log rotation via containerd</span>
kubectl debug node/&lt;node-name&gt; <span class="nt">-it</span> <span class="nt">--image</span><span class="o">=</span>busybox <span class="nt">--</span> sh <span class="nt">-c</span> <span class="s1">'
cat &gt; /etc/containerd/config.toml &lt;&lt; EOF
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
  SystemdCgroup = true

[plugins."io.containerd.grpc.v1.cri"]
  max_container_log_line_size = 16384
  
[plugins."io.containerd.grpc.v1.cri".registry]
  [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
    [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
      endpoint = ["https://registry-1.docker.io"]
EOF
systemctl restart containerd
'</span>

<span class="c"># Clean up large log files immediately</span>
kubectl debug node/&lt;node-name&gt; <span class="nt">-it</span> <span class="nt">--image</span><span class="o">=</span>busybox <span class="nt">--</span> sh <span class="nt">-c</span> <span class="s1">'truncate -s 0 /var/log/containers/&lt;large-log-file&gt;'</span>
</code></pre></div></div>

<h3 id="114-security-and-rbac-issues">11.4 Security and RBAC Issues</h3>

<h4 id="1141-aws-auth-configmap-corruption-causing-cluster-lockout">11.4.1 “aws-auth ConfigMap corruption causing cluster lockout”</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Unable to access cluster with existing IAM roles/users</li>
  <li><code class="language-plaintext highlighter-rouge">kubectl</code> commands return “Unauthorized” errors</li>
  <li>Previously working IAM authentication suddenly fails</li>
  <li>New nodes unable to join cluster</li>
</ul>

<p><strong>Root cause:</strong> Malformed YAML in aws-auth ConfigMap due to indentation errors.</p>

<p>The aws-auth ConfigMap is the single point of failure for EKS cluster access. A single space or tab error can lock out all users.</p>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check current aws-auth ConfigMap</span>
kubectl get configmap aws-auth <span class="nt">-n</span> kube-system <span class="nt">-o</span> yaml

<span class="c"># Validate YAML syntax</span>
kubectl get configmap aws-auth <span class="nt">-n</span> kube-system <span class="nt">-o</span> yaml | yq <span class="nb">eval</span> <span class="s1">'.'</span>

<span class="c"># Check for common issues</span>
kubectl get configmap aws-auth <span class="nt">-n</span> kube-system <span class="nt">-o</span> yaml | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s2">"^</span><span class="se">\s</span><span class="s2">*-</span><span class="se">\s</span><span class="s2">*rolearn|^</span><span class="se">\s</span><span class="s2">*-</span><span class="se">\s</span><span class="s2">*userarn"</span> | <span class="nb">cat</span> <span class="nt">-A</span>
</code></pre></div></div>

<p><strong>Emergency access recovery:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># If locked out, use cluster creator credentials or root user</span>
aws sts get-caller-identity

<span class="c"># Access via AWS Console EKS service or CloudShell</span>
<span class="c"># Or use emergency break-glass role if configured</span>
</code></pre></div></div>

<p><strong>Fix malformed aws-auth:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Backup current ConfigMap first</span>
kubectl get configmap aws-auth <span class="nt">-n</span> kube-system <span class="nt">-o</span> yaml <span class="o">&gt;</span> aws-auth-backup.yaml

<span class="c"># Fix common indentation issues</span>
kubectl patch configmap aws-auth <span class="nt">-n</span> kube-system <span class="nt">-p</span> <span class="s1">'{
  "data": {
    "mapRoles": "- rolearn: arn:aws:iam::123456789012:role/eksctl-cluster-nodegroup-NodeInstanceRole\n  username: system:node:\n  groups:\n    - system:bootstrappers\n    - system:nodes\n- rolearn: arn:aws:iam::123456789012:role/EKSAdminRole\n  username: admin\n  groups:\n    - system:masters",
    "mapUsers": "- userarn: arn:aws:iam::123456789012:user/developer\n  username: developer\n  groups:\n    - developers"
  }
}'</span>

<span class="c"># Validate the fix</span>
kubectl auth can-i <span class="s1">'*'</span> <span class="s1">'*'</span> <span class="nt">--as</span><span class="o">=</span>arn:aws:iam::123456789012:role/EKSAdminRole
</code></pre></div></div>

<p><strong>Correct aws-auth format:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ConfigMap</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">aws-auth</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">kube-system</span>
<span class="na">data</span><span class="pi">:</span>
  <span class="na">mapRoles</span><span class="pi">:</span> <span class="pi">|</span>
    <span class="s">- rolearn: arn:aws:iam::123456789012:role/eksctl-cluster-nodegroup-NodeInstanceRole</span>
      <span class="s">username: system:node:</span>
      <span class="s">groups:</span>
        <span class="s">- system:bootstrappers</span>
        <span class="s">- system:nodes</span>
    <span class="s">- rolearn: arn:aws:iam::123456789012:role/EKSAdminRole</span>
      <span class="s">username: admin</span>
      <span class="s">groups:</span>
        <span class="s">- system:masters</span>
  <span class="na">mapUsers</span><span class="pi">:</span> <span class="pi">|</span>
    <span class="s">- userarn: arn:aws:iam::123456789012:user/developer</span>
      <span class="s">username: developer</span>
      <span class="s">groups:</span>
        <span class="s">- developers</span>
</code></pre></div></div>

<p><strong>Common aws-auth mistakes:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># WRONG - Mixed tabs and spaces</span>
<span class="na">mapRoles</span><span class="pi">:</span> <span class="pi">|</span>
  <span class="s">- rolearn: arn:aws:iam::123456789012:role/NodeRole</span>
<span class="na">	username</span><span class="pi">:</span> <span class="s">system:node:</span>  <span class="c1"># Tab here</span>
    <span class="na">groups</span><span class="pi">:</span>  <span class="c1"># Spaces here</span>
      <span class="pi">-</span> <span class="s">system:nodes</span>

<span class="c1"># WRONG - Incorrect indentation</span>
<span class="na">mapRoles</span><span class="pi">:</span> <span class="pi">|</span>
<span class="err">-</span><span class="s"> rolearn: arn:aws:iam::123456789012:role/NodeRole  # Should be indented</span>
  <span class="s">username: system:node:</span>

<span class="err">#</span><span class="s"> WRONG - Missing pipe character</span>
<span class="err">m</span><span class="s">apRoles:  # Missing |</span>
  <span class="s">- rolearn: arn:aws:iam::123456789012:role/NodeRole</span>

<span class="err">#</span><span class="s"> WRONG - Extra characters</span>
<span class="err">m</span><span class="s">apRoles: |</span>
  <span class="s">- rolearn: arn:aws:iam::123456789012:role/NodeRole,  # Comma at end</span>
    <span class="s">username: system:node:</span>
</code></pre></div></div>

<p><strong>Prevention and monitoring:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Validate before applying</span>
yq <span class="nb">eval</span> <span class="s1">'.data.mapRoles'</span> aws-auth.yaml
yq <span class="nb">eval</span> <span class="s1">'.data.mapUsers'</span> aws-auth.yaml

<span class="c"># Set up monitoring for aws-auth changes</span>
kubectl create <span class="nt">-f</span> - <span class="o">&lt;&lt;</span><span class="no">EOF</span><span class="sh">
apiVersion: v1
kind: Event
metadata:
  name: aws-auth-monitor
  namespace: kube-system
</span><span class="no">EOF

</span><span class="c"># Use eksctl for safer aws-auth management</span>
eksctl create iamidentitymapping <span class="se">\</span>
  <span class="nt">--cluster</span> production-cluster <span class="se">\</span>
  <span class="nt">--region</span> us-west-2 <span class="se">\</span>
  <span class="nt">--arn</span> arn:aws:iam::123456789012:role/EKSAdminRole <span class="se">\</span>
  <span class="nt">--group</span> system:masters <span class="se">\</span>
  <span class="nt">--username</span> admin

<span class="c"># Always backup before changes</span>
kubectl get configmap aws-auth <span class="nt">-n</span> kube-system <span class="nt">-o</span> yaml <span class="o">&gt;</span> <span class="s2">"aws-auth-backup-</span><span class="si">$(</span><span class="nb">date</span> +%Y%m%d-%H%M%S<span class="si">)</span><span class="s2">.yaml"</span>
</code></pre></div></div>

<h4 id="1142-pod-security-policy-not-enforcing-restrictions">11.4.2 “Pod Security Policy not enforcing restrictions”</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Privileged containers running despite PSP configuration</li>
  <li>Security policies being bypassed</li>
  <li>Containers running as root when they shouldn’t</li>
</ul>

<p><strong>Root cause:</strong> Missing admission controller or misconfigured PSP.</p>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check if PSP admission controller is enabled (EKS doesn't enable by default)</span>
kubectl get pods <span class="nt">-n</span> kube-system kube-apiserver-<span class="k">*</span> <span class="nt">-o</span> yaml | <span class="nb">grep</span> <span class="nt">-A</span> 5 admission-control

<span class="c"># Check existing PSPs</span>
kubectl get psp

<span class="c"># Check pod security context</span>
kubectl get pod &lt;pod-name&gt; <span class="nt">-o</span> yaml | <span class="nb">grep</span> <span class="nt">-A</span> 10 securityContext

<span class="c"># Check if pod is using PSP</span>
kubectl describe pod &lt;pod-name&gt; | <span class="nb">grep</span> <span class="nt">-i</span> <span class="s2">"psp</span><span class="se">\|</span><span class="s2">security"</span>
</code></pre></div></div>

<p><strong>Fix:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># For EKS, use Pod Security Standards instead of PSP</span>
kubectl label namespace &lt;namespace&gt; pod-security.kubernetes.io/enforce<span class="o">=</span>restricted
kubectl label namespace &lt;namespace&gt; pod-security.kubernetes.io/audit<span class="o">=</span>restricted
kubectl label namespace &lt;namespace&gt; pod-security.kubernetes.io/warn<span class="o">=</span>restricted

<span class="c"># Create restrictive security context in deployment</span>
kubectl patch deployment &lt;deployment&gt; <span class="nt">-p</span> <span class="s1">'{
  "spec": {
    "template": {
      "spec": {
        "securityContext": {
          "runAsNonRoot": true,
          "runAsUser": 1000,
          "fsGroup": 2000
        },
        "containers": [{
          "name": "&lt;container&gt;",
          "securityContext": {
            "allowPrivilegeEscalation": false,
            "readOnlyRootFilesystem": true,
            "capabilities": {
              "drop": ["ALL"]
            }
          }
        }]
      }
    }
  }
}'</span>
</code></pre></div></div>

<h4 id="1143-rbac-permissions-too-broad-or-too-restrictive">11.4.3 “RBAC permissions too broad or too restrictive”</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Users can access resources they shouldn’t</li>
  <li>Service accounts failing with permission errors</li>
  <li>Applications unable to perform required operations</li>
</ul>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check current user permissions</span>
kubectl auth can-i <span class="nt">--list</span>

<span class="c"># Check service account permissions</span>
kubectl auth can-i <span class="nt">--list</span> <span class="nt">--as</span><span class="o">=</span>system:serviceaccount:&lt;namespace&gt;:&lt;service-account&gt;

<span class="c"># Check role bindings for user/service account</span>
kubectl get rolebindings,clusterrolebindings <span class="nt">--all-namespaces</span> <span class="nt">-o</span> wide | <span class="nb">grep</span> &lt;user-or-sa&gt;

<span class="c"># Test specific permission</span>
kubectl auth can-i create pods <span class="nt">--as</span><span class="o">=</span>system:serviceaccount:&lt;namespace&gt;:&lt;service-account&gt;
</code></pre></div></div>

<p><strong>Fix:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Create minimal role for service account</span>
kubectl apply <span class="nt">-f</span> - <span class="o">&lt;&lt;</span><span class="no">EOF</span><span class="sh">
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: &lt;namespace&gt;
  name: &lt;app&gt;-role
rules:
- apiGroups: [""]
  resources: ["pods", "configmaps"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: &lt;app&gt;-binding
  namespace: &lt;namespace&gt;
subjects:
- kind: ServiceAccount
  name: &lt;service-account&gt;
  namespace: &lt;namespace&gt;
roleRef:
  kind: Role
  name: &lt;app&gt;-role
  apiGroup: rbac.authorization.k8s.io
</span><span class="no">EOF

</span><span class="c"># Remove overly broad cluster role binding</span>
kubectl delete clusterrolebinding &lt;overly-broad-binding&gt;
</code></pre></div></div>

<h4 id="1144-secrets-exposed-in-environment-variables-or-logs">11.4.4 “Secrets exposed in environment variables or logs”</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Sensitive data visible in pod environment</li>
  <li>Secrets appearing in application logs</li>
  <li>Configuration containing plaintext credentials</li>
</ul>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check environment variables in running pod</span>
kubectl <span class="nb">exec</span> &lt;pod-name&gt; <span class="nt">--</span> <span class="nb">env</span> | <span class="nb">grep</span> <span class="nt">-i</span> <span class="nt">-E</span> <span class="s2">"(password|secret|key|token)"</span>

<span class="c"># Check if secrets are mounted as files vs env vars</span>
kubectl describe pod &lt;pod-name&gt; | <span class="nb">grep</span> <span class="nt">-A</span> 10 <span class="nt">-B</span> 5 <span class="nt">-i</span> secret

<span class="c"># Check recent logs for exposed secrets</span>
kubectl logs &lt;pod-name&gt; | <span class="nb">grep</span> <span class="nt">-i</span> <span class="nt">-E</span> <span class="s2">"(password|secret|key|token)"</span> | <span class="nb">head</span> <span class="nt">-5</span>
</code></pre></div></div>

<p><strong>Fix:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Mount secrets as files instead of env vars</span>
kubectl patch deployment &lt;deployment&gt; <span class="nt">-p</span> <span class="s1">'{
  "spec": {
    "template": {
      "spec": {
        "containers": [{
          "name": "&lt;container&gt;",
          "volumeMounts": [{
            "name": "secret-volume",
            "mountPath": "/etc/secrets",
            "readOnly": true
          }]
        }],
        "volumes": [{
          "name": "secret-volume",
          "secret": {
            "secretName": "&lt;secret-name&gt;",
            "defaultMode": 256
          }
        }]
      }
    }
  }
}'</span>

<span class="c"># Remove secret from environment variables</span>
kubectl patch deployment &lt;deployment&gt; <span class="nt">-p</span> <span class="s1">'{
  "spec": {
    "template": {
      "spec": {
        "containers": [{
          "name": "&lt;container&gt;",
          "env": null
        }]
      }
    }
  }
}'</span>

<span class="c"># Use External Secrets Operator for better secret management</span>
kubectl apply <span class="nt">-f</span> - <span class="o">&lt;&lt;</span><span class="no">EOF</span><span class="sh">
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: &lt;app&gt;-secret
  namespace: &lt;namespace&gt;
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: aws-secrets-manager
    kind: SecretStore
  target:
    name: &lt;app&gt;-secret
    creationPolicy: Owner
  data:
  - secretKey: password
    remoteRef:
      key: &lt;secret-arn&gt;
      property: password
</span><span class="no">EOF
</span></code></pre></div></div>

<h4 id="1145-poddisruptionbudget-blocking-evictions">11.4.5 “PodDisruptionBudget blocking evictions”</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Node drain operations hang</li>
  <li>Cluster autoscaler cannot scale down</li>
  <li>Rolling updates stuck</li>
</ul>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check PDB status</span>
kubectl get pdb <span class="nt">-A</span>

<span class="c"># Check which pods are blocking eviction</span>
kubectl describe pdb &lt;pdb-name&gt;
</code></pre></div></div>

<p><strong>Fix:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Temporarily relax PDB</span>
kubectl patch pdb &lt;pdb-name&gt; <span class="nt">-p</span> <span class="s1">'{"spec":{"minAvailable":1}}'</span>

<span class="c"># Or scale up replicas to meet PDB requirements</span>
kubectl scale deployment &lt;deployment&gt; <span class="nt">--replicas</span><span class="o">=</span>5

<span class="c"># Complete maintenance, then restore PDB</span>
kubectl patch pdb &lt;pdb-name&gt; <span class="nt">-p</span> <span class="s1">'{"spec":{"minAvailable":3}}'</span>
</code></pre></div></div>

<hr />

<h3 id="115-egress-and-service-discovery-failures">11.5 Egress and Service Discovery Failures</h3>

<h4 id="1151-pods-cant-reach-external-services">11.5.1 “Pods can’t reach external services”</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Timeouts connecting to external APIs</li>
  <li>DNS resolution works but connections fail</li>
  <li>Intermittent connectivity issues</li>
</ul>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Test connectivity from pod</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod&gt; <span class="nt">--</span> curl <span class="nt">-v</span> https://api.external.com

<span class="c"># Check NAT Gateway health</span>
aws ec2 describe-nat-gateways <span class="nt">--nat-gateway-ids</span> &lt;nat-gw-id&gt;

<span class="c"># Check security group rules</span>
aws ec2 describe-security-groups <span class="nt">--group-ids</span> &lt;sg-id&gt;
</code></pre></div></div>

<p><strong>Common fixes:</strong></p>

<p><strong>NAT Gateway issues:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check NAT Gateway metrics</span>
aws cloudwatch get-metric-statistics <span class="se">\</span>
  <span class="nt">--namespace</span> AWS/NATGateway <span class="se">\</span>
  <span class="nt">--metric-name</span> PacketsDropCount <span class="se">\</span>
  <span class="nt">--dimensions</span> <span class="nv">Name</span><span class="o">=</span>NatGatewayId,Value<span class="o">=</span>&lt;nat-gw-id&gt; <span class="se">\</span>
  <span class="nt">--start-time</span> <span class="si">$(</span><span class="nb">date</span> <span class="nt">-d</span> <span class="s1">'1 hour ago'</span> <span class="nt">-Iseconds</span><span class="si">)</span> <span class="se">\</span>
  <span class="nt">--end-time</span> <span class="si">$(</span><span class="nb">date</span> <span class="nt">-Iseconds</span><span class="si">)</span> <span class="se">\</span>
  <span class="nt">--period</span> 300 <span class="se">\</span>
  <span class="nt">--statistics</span> Sum
</code></pre></div></div>

<p><strong>Security group blocking traffic:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Add egress rule for HTTPS</span>
aws ec2 authorize-security-group-egress <span class="se">\</span>
  <span class="nt">--group-id</span> &lt;sg-id&gt; <span class="se">\</span>
  <span class="nt">--protocol</span> tcp <span class="se">\</span>
  <span class="nt">--port</span> 443 <span class="se">\</span>
  <span class="nt">--cidr</span> 0.0.0.0/0
</code></pre></div></div>

<h4 id="1152-service-discovery-not-working">11.5.2 “Service discovery not working”</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Pods can’t reach other services by name</li>
  <li><code class="language-plaintext highlighter-rouge">nslookup</code> fails for service names</li>
  <li>Intermittent DNS failures</li>
</ul>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Test DNS resolution</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod&gt; <span class="nt">--</span> nslookup kubernetes.default.svc.cluster.local

<span class="c"># Check CoreDNS health</span>
kubectl <span class="nt">-n</span> kube-system get pods <span class="nt">-l</span> k8s-app<span class="o">=</span>kube-dns
kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> k8s-app<span class="o">=</span>kube-dns <span class="nt">--tail</span><span class="o">=</span>100

<span class="c"># Check service endpoints</span>
kubectl get endpoints &lt;service-name&gt;
</code></pre></div></div>

<p><strong>Fixes:</strong></p>

<p><strong>CoreDNS not ready:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Scale up CoreDNS</span>
kubectl <span class="nt">-n</span> kube-system scale deployment coredns <span class="nt">--replicas</span><span class="o">=</span>3

<span class="c"># Check CoreDNS configuration</span>
kubectl <span class="nt">-n</span> kube-system get configmap coredns <span class="nt">-o</span> yaml
</code></pre></div></div>

<p><strong>Service has no endpoints:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check if pods are ready</span>
kubectl get pods <span class="nt">-l</span> <span class="nv">app</span><span class="o">=</span>&lt;service-selector&gt;

<span class="c"># Check service selector</span>
kubectl describe service &lt;service-name&gt;

<span class="c"># Fix selector mismatch</span>
kubectl patch service &lt;service-name&gt; <span class="nt">-p</span> <span class="s1">'{"spec":{"selector":{"app":"correct-label"}}}'</span>
</code></pre></div></div>

<hr />

<h3 id="116-storage-issues">11.6 Storage Issues</h3>

<h4 id="1161-pods-stuck-in-containercreating-due-to-volume-mount-failures">11.6.1 “Pods stuck in ContainerCreating due to volume mount failures”</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Pods stuck in ContainerCreating state</li>
  <li>Events show volume mount errors</li>
  <li>StatefulSet pods fail to start</li>
</ul>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check pod events</span>
kubectl describe pod &lt;pod&gt;

<span class="c"># Check PVC status</span>
kubectl get pvc

<span class="c"># Check volume attachment</span>
kubectl get volumeattachment
</code></pre></div></div>

<p><strong>Common fixes:</strong></p>

<p><strong>EBS volume in wrong AZ:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check pod and volume zones</span>
kubectl get pod &lt;pod&gt; <span class="nt">-o</span> wide
kubectl describe pv &lt;pv-name&gt; | <span class="nb">grep </span>zone

<span class="c"># Delete pod to reschedule in correct AZ</span>
kubectl delete pod &lt;pod&gt;
</code></pre></div></div>

<p><strong>CSI driver issues:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check CSI driver health</span>
kubectl <span class="nt">-n</span> kube-system get pods <span class="nt">-l</span> <span class="nv">app</span><span class="o">=</span>ebs-csi-controller
kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> <span class="nv">app</span><span class="o">=</span>ebs-csi-controller

<span class="c"># Restart CSI driver if needed</span>
kubectl <span class="nt">-n</span> kube-system rollout restart deployment ebs-csi-controller
</code></pre></div></div>

<h4 id="1162-pvc-stuck-in-pending-state">11.6.2 “PVC stuck in Pending state”</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>PVC remains in Pending status</li>
  <li>No PV created for dynamic provisioning</li>
  <li>Storage class issues</li>
</ul>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check PVC events</span>
kubectl describe pvc &lt;pvc-name&gt;

<span class="c"># Check storage class</span>
kubectl describe storageclass &lt;storage-class&gt;

<span class="c"># Check CSI provisioner logs</span>
kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> <span class="nv">app</span><span class="o">=</span>ebs-csi-controller
</code></pre></div></div>

<p><strong>Fixes:</strong></p>

<p><strong>Storage class misconfiguration:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check available storage classes</span>
kubectl get storageclass

<span class="c"># Create correct storage class</span>
kubectl apply <span class="nt">-f</span> - <span class="o">&lt;&lt;</span><span class="no">EOF</span><span class="sh">
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gp3
provisioner: ebs.csi.aws.com
parameters:
  type: gp3
  iops: "3000"
  throughput: "125"
allowVolumeExpansion: true
reclaimPolicy: Delete
</span><span class="no">EOF
</span></code></pre></div></div>

<hr />

<h3 id="117-application-failures">11.7 Application Failures</h3>

<h4 id="1171-pods-crashing-with-oomkilled">11.7.1 “Pods crashing with OOMKilled”</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Pods restart frequently</li>
  <li>Exit code 137 (OOMKilled)</li>
  <li>Application performance degradation</li>
</ul>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check pod resource usage</span>
kubectl top pod &lt;pod&gt;

<span class="c"># Check pod events for OOM</span>
kubectl describe pod &lt;pod&gt; | <span class="nb">grep</span> <span class="nt">-i</span> oom

<span class="c"># Check memory limits</span>
kubectl get pod &lt;pod&gt; <span class="nt">-o</span> yaml | <span class="nb">grep</span> <span class="nt">-A</span> 5 resources
</code></pre></div></div>

<p><strong>Fixes:</strong></p>

<p><strong>Increase memory limits:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Update deployment with higher memory limits</span>
kubectl patch deployment &lt;deployment&gt; <span class="nt">-p</span> <span class="s1">'{"spec":{"template":{"spec":{"containers":[{"name":"&lt;container&gt;","resources":{"limits":{"memory":"2Gi"},"requests":{"memory":"1Gi"}}}]}}}}'</span>
</code></pre></div></div>

<p><strong>Optimize application memory usage:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check for memory leaks</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod&gt; <span class="nt">--</span> ps aux <span class="nt">--sort</span><span class="o">=</span>-%mem | <span class="nb">head</span>

<span class="c"># Enable memory profiling (application-specific)</span>
kubectl <span class="nb">set env </span>deployment/&lt;deployment&gt; <span class="nv">GOMAXPROCS</span><span class="o">=</span>2 <span class="nv">GOMEMLIMIT</span><span class="o">=</span>1GiB
</code></pre></div></div>

<h4 id="1172-readiness-probe-failures-causing-traffic-issues">11.7.2 “Readiness probe failures causing traffic issues”</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Pods not receiving traffic</li>
  <li>Service endpoints empty</li>
  <li>Load balancer health checks failing</li>
</ul>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check pod readiness</span>
kubectl get pods <span class="nt">-o</span> wide

<span class="c"># Check readiness probe configuration</span>
kubectl describe pod &lt;pod&gt; | <span class="nb">grep</span> <span class="nt">-A</span> 10 <span class="s2">"Readiness"</span>

<span class="c"># Test probe endpoint manually</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod&gt; <span class="nt">--</span> curl localhost:8080/health
</code></pre></div></div>

<p><strong>Fixes:</strong></p>

<p><strong>Adjust probe timing:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Update probe configuration</span>
kubectl patch deployment &lt;deployment&gt; <span class="nt">-p</span> <span class="s1">'{"spec":{"template":{"spec":{"containers":[{"name":"&lt;container&gt;","readinessProbe":{"initialDelaySeconds":30,"periodSeconds":10,"timeoutSeconds":5,"failureThreshold":3}}]}}}}'</span>
</code></pre></div></div>

<p><strong>Fix probe endpoint:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check if health endpoint is correct</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod&gt; <span class="nt">--</span> netstat <span class="nt">-tlnp</span> | <span class="nb">grep</span> :8080

<span class="c"># Update probe path if needed</span>
kubectl patch deployment &lt;deployment&gt; <span class="nt">-p</span> <span class="s1">'{"spec":{"template":{"spec":{"containers":[{"name":"&lt;container&gt;","readinessProbe":{"httpGet":{"path":"/healthz","port":8080}}}]}}}}'</span>
</code></pre></div></div>

<hr />

<h3 id="118-cluster-level-issues">11.8 Cluster-Level Issues</h3>

<h4 id="1181-api-server-timeouts-and-high-latency">11.8.1 “API server timeouts and high latency”</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">kubectl</code> commands timeout</li>
  <li>High API server response times</li>
  <li>Cluster operations slow</li>
</ul>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check API server metrics</span>
kubectl get <span class="nt">--raw</span> /metrics | <span class="nb">grep </span>apiserver_request_duration

<span class="c"># Check etcd health</span>
kubectl get <span class="nt">--raw</span> /healthz/etcd

<span class="c"># Check for resource pressure</span>
kubectl top nodes
</code></pre></div></div>

<p><strong>Fixes:</strong></p>

<p><strong>Reduce API server load:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Find clients making excessive requests</span>
kubectl get events <span class="nt">--sort-by</span><span class="o">=</span><span class="s1">'.lastTimestamp'</span> | <span class="nb">tail</span> <span class="nt">-20</span>

<span class="c"># Scale down chatty controllers</span>
kubectl scale deployment &lt;noisy-controller&gt; <span class="nt">--replicas</span><span class="o">=</span>0

<span class="c"># Increase API server resources (managed by AWS for EKS)</span>
<span class="c"># Contact AWS support if persistent</span>
</code></pre></div></div>

<h4 id="1182-cluster-autoscaler-not-scaling">11.8.2 “Cluster autoscaler not scaling”</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Pending pods but no new nodes</li>
  <li>Cluster autoscaler logs show errors</li>
  <li>Node groups not scaling up</li>
</ul>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check cluster autoscaler logs</span>
kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> <span class="nv">app</span><span class="o">=</span>cluster-autoscaler

<span class="c"># Check node group configuration</span>
aws eks describe-nodegroup <span class="nt">--cluster-name</span> &lt;cluster&gt; <span class="nt">--nodegroup-name</span> &lt;nodegroup&gt;

<span class="c"># Check IAM permissions</span>
aws sts get-caller-identity
</code></pre></div></div>

<p><strong>Fixes:</strong></p>

<p><strong>IAM permission issues:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check autoscaler service account</span>
kubectl <span class="nt">-n</span> kube-system describe sa cluster-autoscaler

<span class="c"># Verify IAM role has required permissions</span>
aws iam get-role-policy <span class="nt">--role-name</span> &lt;autoscaler-role&gt; <span class="nt">--policy-name</span> &lt;policy-name&gt;
</code></pre></div></div>

<p><strong>Node group limits:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Increase node group max size</span>
aws eks update-nodegroup-config <span class="se">\</span>
  <span class="nt">--cluster-name</span> &lt;cluster&gt; <span class="se">\</span>
  <span class="nt">--nodegroup-name</span> &lt;nodegroup&gt; <span class="se">\</span>
  <span class="nt">--scaling-config</span> <span class="nv">minSize</span><span class="o">=</span>2,maxSize<span class="o">=</span>20,desiredSize<span class="o">=</span>5
</code></pre></div></div>

<hr />

<h3 id="119-performance-issues">11.9 Performance Issues</h3>

<h4 id="1191-high-cpu-throttling-affecting-performance">11.9.1 “High CPU throttling affecting performance”</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Application response times high</li>
  <li>CPU usage appears low but performance poor</li>
  <li>Intermittent slowdowns</li>
</ul>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check CPU throttling</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod&gt; <span class="nt">--</span> <span class="nb">cat</span> /sys/fs/cgroup/cpu/cpu.stat | <span class="nb">grep </span>throttled

<span class="c"># Check CPU limits vs requests</span>
kubectl describe pod &lt;pod&gt; | <span class="nb">grep</span> <span class="nt">-A</span> 10 Limits

<span class="c"># Monitor CPU usage patterns</span>
kubectl top pod &lt;pod&gt; <span class="nt">--containers</span>
</code></pre></div></div>

<p><strong>Fixes:</strong></p>

<p><strong>Adjust CPU limits:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Remove CPU limits for CPU-intensive workloads</span>
kubectl patch deployment &lt;deployment&gt; <span class="nt">-p</span> <span class="s1">'{"spec":{"template":{"spec":{"containers":[{"name":"&lt;container&gt;","resources":{"limits":{"cpu":null}}}]}}}}'</span>

<span class="c"># Or increase CPU limits</span>
kubectl patch deployment &lt;deployment&gt; <span class="nt">-p</span> <span class="s1">'{"spec":{"template":{"spec":{"containers":[{"name":"&lt;container&gt;","resources":{"limits":{"cpu":"2000m"}}}]}}}}'</span>
</code></pre></div></div>

<h4 id="1192-disk-io-bottlenecks">11.9.2 “Disk I/O bottlenecks”</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>High disk wait times</li>
  <li>Application timeouts during disk operations</li>
  <li>EBS volume performance issues</li>
</ul>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check disk I/O from pod</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod&gt; <span class="nt">--</span> iostat <span class="nt">-x</span> 1 5

<span class="c"># Check EBS volume metrics</span>
aws cloudwatch get-metric-statistics <span class="se">\</span>
  <span class="nt">--namespace</span> AWS/EBS <span class="se">\</span>
  <span class="nt">--metric-name</span> VolumeReadOps <span class="se">\</span>
  <span class="nt">--dimensions</span> <span class="nv">Name</span><span class="o">=</span>VolumeId,Value<span class="o">=</span>&lt;volume-id&gt; <span class="se">\</span>
  <span class="nt">--start-time</span> <span class="si">$(</span><span class="nb">date</span> <span class="nt">-d</span> <span class="s1">'1 hour ago'</span> <span class="nt">-Iseconds</span><span class="si">)</span> <span class="se">\</span>
  <span class="nt">--end-time</span> <span class="si">$(</span><span class="nb">date</span> <span class="nt">-Iseconds</span><span class="si">)</span> <span class="se">\</span>
  <span class="nt">--period</span> 300 <span class="se">\</span>
  <span class="nt">--statistics</span> Average
</code></pre></div></div>

<p><strong>Fixes:</strong></p>

<p><strong>Upgrade to higher IOPS volume:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Modify EBS volume type</span>
aws ec2 modify-volume <span class="se">\</span>
  <span class="nt">--volume-id</span> &lt;volume-id&gt; <span class="se">\</span>
  <span class="nt">--volume-type</span> gp3 <span class="se">\</span>
  <span class="nt">--iops</span> 10000
</code></pre></div></div>

<hr />

<h3 id="1110-emergency-procedures">11.10 Emergency Procedures</h3>

<h4 id="11101-cluster-completely-unresponsive">11.10.1 “Cluster completely unresponsive”</h4>

<p><strong>Immediate actions:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 1. Check if it's a regional AWS issue</span>
curl <span class="nt">-s</span> https://status.aws.amazon.com/

<span class="c"># 2. Try different kubectl context/region</span>
kubectl config use-context &lt;backup-context&gt;

<span class="c"># 3. Check EKS cluster status</span>
aws eks describe-cluster <span class="nt">--name</span> &lt;cluster&gt; <span class="nt">--region</span> &lt;region&gt;

<span class="c"># 4. If control plane is down, activate DR procedures</span>
<span class="c"># Switch DNS to DR region</span>
aws route53 change-resource-record-sets <span class="se">\</span>
  <span class="nt">--hosted-zone-id</span> &lt;zone-id&gt; <span class="se">\</span>
  <span class="nt">--change-batch</span> file://failover.json
</code></pre></div></div>

<h4 id="11102-mass-pod-failures-across-cluster">11.10.2 “Mass pod failures across cluster”</h4>

<p><strong>Immediate actions:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 1. Stop any ongoing deployments</span>
kubectl rollout pause deployment/&lt;deployment&gt;

<span class="c"># 2. Check for cluster-wide issues</span>
kubectl get nodes
kubectl <span class="nt">-n</span> kube-system get pods

<span class="c"># 3. Check recent changes</span>
kubectl get events <span class="nt">--sort-by</span><span class="o">=</span><span class="s1">'.lastTimestamp'</span> | <span class="nb">tail</span> <span class="nt">-50</span>

<span class="c"># 4. Rollback recent changes if identified</span>
kubectl rollout undo deployment/&lt;deployment&gt;
</code></pre></div></div>

<hr />

<h3 id="1111-quick-reference-commands">11.11 Quick Reference Commands</h3>

<h4 id="11111-essential-debugging-commands">11.11.1 Essential Debugging Commands</h4>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Pod debugging</span>
kubectl describe pod &lt;pod&gt;
kubectl logs &lt;pod&gt; <span class="nt">--previous</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod&gt; <span class="nt">--</span> /bin/bash

<span class="c"># Service debugging</span>
kubectl describe service &lt;service&gt;
kubectl get endpoints &lt;service&gt;
kubectl port-forward service/&lt;service&gt; 8080:80

<span class="c"># Node debugging</span>
kubectl describe node &lt;node&gt;
kubectl top node &lt;node&gt;
kubectl get pods <span class="nt">-A</span> <span class="nt">-o</span> wide <span class="nt">--field-selector</span> spec.nodeName<span class="o">=</span>&lt;node&gt;

<span class="c"># Cluster debugging</span>
kubectl cluster-info
kubectl get events <span class="nt">--sort-by</span><span class="o">=</span><span class="s1">'.lastTimestamp'</span>
kubectl get all <span class="nt">-A</span> | <span class="nb">grep</span> <span class="nt">-v</span> Running
</code></pre></div></div>

<h4 id="11112-emergency-recovery-commands">11.11.2 Emergency Recovery Commands</h4>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Force delete stuck resources</span>
kubectl delete pod &lt;pod&gt; <span class="nt">--force</span> <span class="nt">--grace-period</span><span class="o">=</span>0
kubectl patch pvc &lt;pvc&gt; <span class="nt">-p</span> <span class="s1">'{"metadata":{"finalizers":null}}'</span>

<span class="c"># Emergency scaling</span>
kubectl scale deployment &lt;deployment&gt; <span class="nt">--replicas</span><span class="o">=</span>0
kubectl scale deployment &lt;deployment&gt; <span class="nt">--replicas</span><span class="o">=</span>3

<span class="c"># Quick rollback</span>
kubectl rollout undo deployment/&lt;deployment&gt;
kubectl rollout status deployment/&lt;deployment&gt;
</code></pre></div></div>

<hr />

<p>This troubleshooting cookbook provides step-by-step solutions for the most common EKS production failures. Each scenario is designed to be used under pressure, with clear symptoms, diagnosis steps, and proven fixes. The key is systematic diagnosis before attempting fixes, and having emergency procedures ready for critical situations.</p>
<h2 id="12-eks-at-scale">12. EKS at scale</h2>

<p>Scale introduces failure modes that simply don’t exist in smaller clusters. Hundreds of nodes, thousands of pods, multiple clusters — different operational patterns, different failure scenarios.</p>

<hr />

<h3 id="121-multi-cluster-patterns">12.1 Multi-Cluster Patterns</h3>

<h4 id="1211-when-to-use-multiple-clusters">12.1.1 When to Use Multiple Clusters</h4>

<p><strong>Cluster boundaries that make sense:</strong></p>
<ul>
  <li><strong>Environment isolation</strong> (prod/staging/dev)</li>
  <li><strong>Team isolation</strong> (different blast radius requirements)</li>
  <li><strong>Compliance boundaries</strong> (PCI/SOX/HIPAA workloads)</li>
  <li><strong>Geographic distribution</strong> (latency/data residency)</li>
  <li><strong>Scale limits</strong> (approaching EKS/EC2 quotas)</li>
</ul>

<p><strong>Anti-patterns to avoid:</strong></p>
<ul>
  <li>One cluster per microservice (operational overhead)</li>
  <li>Clusters for cost allocation (use namespaces + tagging)</li>
  <li>Clusters for different Kubernetes versions (use node groups)</li>
</ul>

<h4 id="1212-multi-cluster-networking">12.1.2 Multi-Cluster Networking</h4>

<p><strong>Cross-cluster service communication:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># External DNS for cross-cluster service discovery</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Service</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">user-service</span>
  <span class="na">annotations</span><span class="pi">:</span>
    <span class="na">external-dns.alpha.kubernetes.io/hostname</span><span class="pi">:</span> <span class="s">user-service.prod.internal</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">LoadBalancer</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">user-service</span>
  <span class="na">ports</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">port</span><span class="pi">:</span> <span class="m">80</span>
    <span class="na">targetPort</span><span class="pi">:</span> <span class="m">8080</span>
</code></pre></div></div>

<p><strong>VPC peering for cluster connectivity:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Create VPC peering between clusters</span>
aws ec2 create-vpc-peering-connection <span class="se">\</span>
  <span class="nt">--vpc-id</span> vpc-cluster1 <span class="se">\</span>
  <span class="nt">--peer-vpc-id</span> vpc-cluster2 <span class="se">\</span>
  <span class="nt">--peer-region</span> us-west-2

<span class="c"># Update route tables for cross-cluster communication</span>
aws ec2 create-route <span class="se">\</span>
  <span class="nt">--route-table-id</span> rtb-cluster1 <span class="se">\</span>
  <span class="nt">--destination-cidr-block</span> 10.1.0.0/16 <span class="se">\</span>
  <span class="nt">--vpc-peering-connection-id</span> pcx-12345
</code></pre></div></div>

<h4 id="1213-multi-cluster-management">12.1.3 Multi-Cluster Management</h4>

<p><strong>Centralized cluster management with ArgoCD:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">argoproj.io/v1alpha1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Application</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">production-workloads</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">argocd</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">project</span><span class="pi">:</span> <span class="s">default</span>
  <span class="na">source</span><span class="pi">:</span>
    <span class="na">repoURL</span><span class="pi">:</span> <span class="s">https://github.com/company/k8s-manifests</span>
    <span class="na">targetRevision</span><span class="pi">:</span> <span class="s">HEAD</span>
    <span class="na">path</span><span class="pi">:</span> <span class="s">production</span>
  <span class="na">destination</span><span class="pi">:</span>
    <span class="na">server</span><span class="pi">:</span> <span class="s">https://prod-cluster.us-west-2.eks.amazonaws.com</span>
    <span class="na">namespace</span><span class="pi">:</span> <span class="s">production</span>
  <span class="na">syncPolicy</span><span class="pi">:</span>
    <span class="na">automated</span><span class="pi">:</span>
      <span class="na">prune</span><span class="pi">:</span> <span class="no">true</span>
      <span class="na">selfHeal</span><span class="pi">:</span> <span class="no">true</span>
</code></pre></div></div>

<p><strong>Cluster inventory management:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="c"># Multi-cluster inventory script</span>

<span class="nv">CLUSTERS</span><span class="o">=(</span>
  <span class="s2">"prod-us-west-2"</span>
  <span class="s2">"prod-us-east-1"</span>
  <span class="s2">"staging-us-west-2"</span>
  <span class="s2">"dev-us-west-2"</span>
<span class="o">)</span>

<span class="k">for </span>cluster <span class="k">in</span> <span class="s2">"</span><span class="k">${</span><span class="nv">CLUSTERS</span><span class="p">[@]</span><span class="k">}</span><span class="s2">"</span><span class="p">;</span> <span class="k">do
  </span><span class="nb">echo</span> <span class="s2">"=== Cluster: </span><span class="nv">$cluster</span><span class="s2"> ==="</span>
  
  <span class="c"># Update kubeconfig</span>
  aws eks update-kubeconfig <span class="nt">--name</span> <span class="nv">$cluster</span> <span class="nt">--region</span> <span class="k">${</span><span class="nv">cluster</span><span class="p">##*-</span><span class="k">}</span>
  
  <span class="c"># Get cluster info</span>
  <span class="nb">echo</span> <span class="s2">"Nodes: </span><span class="si">$(</span>kubectl get nodes <span class="nt">--no-headers</span> | <span class="nb">wc</span> <span class="nt">-l</span><span class="si">)</span><span class="s2">"</span>
  <span class="nb">echo</span> <span class="s2">"Pods: </span><span class="si">$(</span>kubectl get pods <span class="nt">-A</span> <span class="nt">--no-headers</span> | <span class="nb">wc</span> <span class="nt">-l</span><span class="si">)</span><span class="s2">"</span>
  <span class="nb">echo</span> <span class="s2">"Version: </span><span class="si">$(</span>kubectl version <span class="nt">--short</span> <span class="nt">--client</span><span class="o">=</span><span class="nb">false</span> | <span class="nb">grep </span>Server<span class="si">)</span><span class="s2">"</span>
  
  <span class="c"># Check critical components</span>
  kubectl get pods <span class="nt">-n</span> kube-system | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s2">"(coredns|aws-node|kube-proxy)"</span> | <span class="nb">grep</span> <span class="nt">-v</span> Running <span class="o">&amp;&amp;</span> <span class="nb">echo</span> <span class="s2">"⚠️  System pods not ready"</span>
  
  <span class="nb">echo</span> <span class="s2">""</span>
<span class="k">done</span>
</code></pre></div></div>

<hr />

<h3 id="122-large-node-pool-management">12.2 Large Node Pool Management</h3>

<h4 id="1221-node-pool-strategies-at-scale">12.2.1 Node Pool Strategies at Scale</h4>

<p><strong>Diversified instance types for resilience:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">eksctl.io/v1alpha5</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ClusterConfig</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">large-scale-cluster</span>
<span class="na">nodeGroups</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">general-purpose</span>
  <span class="na">instanceTypes</span><span class="pi">:</span> 
  <span class="pi">-</span> <span class="s">m5.large</span>
  <span class="pi">-</span> <span class="s">m5.xlarge</span>
  <span class="pi">-</span> <span class="s">m5a.large</span>
  <span class="pi">-</span> <span class="s">m5a.xlarge</span>
  <span class="pi">-</span> <span class="s">m4.large</span>
  <span class="pi">-</span> <span class="s">m4.xlarge</span>
  <span class="na">minSize</span><span class="pi">:</span> <span class="m">10</span>
  <span class="na">maxSize</span><span class="pi">:</span> <span class="m">500</span>
  <span class="na">desiredCapacity</span><span class="pi">:</span> <span class="m">50</span>
  <span class="na">spot</span><span class="pi">:</span> <span class="no">true</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">node-class</span><span class="pi">:</span> <span class="s">general-purpose</span>
</code></pre></div></div>

<p><strong>Dedicated node pools for specific workloads:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># High-memory workloads</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">memory-optimized</span>
  <span class="na">instanceTypes</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">r5.xlarge"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">r5.2xlarge"</span><span class="pi">]</span>
  <span class="na">minSize</span><span class="pi">:</span> <span class="m">2</span>
  <span class="na">maxSize</span><span class="pi">:</span> <span class="m">50</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">node-class</span><span class="pi">:</span> <span class="s">memory-optimized</span>
  <span class="na">taints</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">key</span><span class="pi">:</span> <span class="s">workload-type</span>
    <span class="na">value</span><span class="pi">:</span> <span class="s">memory-intensive</span>
    <span class="na">effect</span><span class="pi">:</span> <span class="s">NoSchedule</span>

<span class="c1"># GPU workloads</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">gpu-nodes</span>
  <span class="na">instanceTypes</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">p3.2xlarge"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">p3.8xlarge"</span><span class="pi">]</span>
  <span class="na">minSize</span><span class="pi">:</span> <span class="m">0</span>
  <span class="na">maxSize</span><span class="pi">:</span> <span class="m">20</span>
  <span class="na">labels</span><span class="pi">:</span>
    <span class="na">node-class</span><span class="pi">:</span> <span class="s">gpu</span>
  <span class="na">taints</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">key</span><span class="pi">:</span> <span class="s">nvidia.com/gpu</span>
    <span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">true"</span>
    <span class="na">effect</span><span class="pi">:</span> <span class="s">NoSchedule</span>
</code></pre></div></div>

<h4 id="1222-node-lifecycle-management-at-scale">12.2.2 Node Lifecycle Management at Scale</h4>

<p><strong>Automated node replacement:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
<span class="c"># Automated node replacement for large clusters</span>

<span class="c"># Find nodes older than 30 days</span>
<span class="nv">OLD_NODES</span><span class="o">=</span><span class="si">$(</span>kubectl get nodes <span class="nt">-o</span> json | jq <span class="nt">-r</span> <span class="s1">'.items[] | select(.metadata.creationTimestamp | fromdateiso8601 &lt; (now - 30*24*3600)) | .metadata.name'</span><span class="si">)</span>

<span class="k">for </span>node <span class="k">in</span> <span class="nv">$OLD_NODES</span><span class="p">;</span> <span class="k">do
  </span><span class="nb">echo</span> <span class="s2">"Replacing old node: </span><span class="nv">$node</span><span class="s2">"</span>
  
  <span class="c"># Cordon node</span>
  kubectl cordon <span class="nv">$node</span>
  
  <span class="c"># Drain node with timeout</span>
  <span class="nb">timeout </span>600 kubectl drain <span class="nv">$node</span> <span class="nt">--ignore-daemonsets</span> <span class="nt">--delete-emptydir-data</span> <span class="nt">--force</span>
  
  <span class="c"># Get instance ID and terminate</span>
  <span class="nv">INSTANCE_ID</span><span class="o">=</span><span class="si">$(</span>kubectl get node <span class="nv">$node</span> <span class="nt">-o</span> <span class="nv">jsonpath</span><span class="o">=</span><span class="s1">'{.spec.providerID}'</span> | <span class="nb">cut</span> <span class="nt">-d</span><span class="s1">'/'</span> <span class="nt">-f5</span><span class="si">)</span>
  aws ec2 terminate-instances <span class="nt">--instance-ids</span> <span class="nv">$INSTANCE_ID</span>
  
  <span class="c"># Wait for replacement</span>
  <span class="nb">sleep </span>300
<span class="k">done</span>
</code></pre></div></div>

<p><strong>Node health monitoring:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">DaemonSet</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">node-health-monitor</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">matchLabels</span><span class="pi">:</span>
      <span class="na">app</span><span class="pi">:</span> <span class="s">node-health-monitor</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">hostNetwork</span><span class="pi">:</span> <span class="no">true</span>
      <span class="na">hostPID</span><span class="pi">:</span> <span class="no">true</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">monitor</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">node-health-monitor:latest</span>
        <span class="na">securityContext</span><span class="pi">:</span>
          <span class="na">privileged</span><span class="pi">:</span> <span class="no">true</span>
        <span class="na">volumeMounts</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">proc</span>
          <span class="na">mountPath</span><span class="pi">:</span> <span class="s">/host/proc</span>
          <span class="na">readOnly</span><span class="pi">:</span> <span class="no">true</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">sys</span>
          <span class="na">mountPath</span><span class="pi">:</span> <span class="s">/host/sys</span>
          <span class="na">readOnly</span><span class="pi">:</span> <span class="no">true</span>
        <span class="na">env</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">NODE_NAME</span>
          <span class="na">valueFrom</span><span class="pi">:</span>
            <span class="na">fieldRef</span><span class="pi">:</span>
              <span class="na">fieldPath</span><span class="pi">:</span> <span class="s">spec.nodeName</span>
        <span class="na">command</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="s">/bin/bash</span>
        <span class="pi">-</span> <span class="s">-c</span>
        <span class="pi">-</span> <span class="pi">|</span>
          <span class="s">while true; do</span>
            <span class="s"># Check disk space</span>
            <span class="s">DISK_USAGE=$(df /host/proc/1/root | tail -1 | awk '{print $5}' | sed 's/%//')</span>
            <span class="s">if [ $DISK_USAGE -gt 85 ]; then</span>
              <span class="s">kubectl annotate node $NODE_NAME node.kubernetes.io/disk-pressure=true --overwrite</span>
            <span class="s">fi</span>
          <span class="no">  </span>
            <span class="s"># Check memory pressure</span>
            <span class="s">MEM_AVAILABLE=$(cat /host/proc/meminfo | grep MemAvailable | awk '{print $2}')</span>
            <span class="s">MEM_TOTAL=$(cat /host/proc/meminfo | grep MemTotal | awk '{print $2}')</span>
            <span class="s">MEM_USAGE=$(echo "scale=2; (1 - $MEM_AVAILABLE/$MEM_TOTAL) * 100" | bc)</span>
          <span class="no">  </span>
            <span class="s">if (( $(echo "$MEM_USAGE &gt; 90" | bc -l) )); then</span>
              <span class="s">kubectl annotate node $NODE_NAME node.kubernetes.io/memory-pressure=true --overwrite</span>
            <span class="s">fi</span>
          <span class="no">  </span>
            <span class="s">sleep 60</span>
          <span class="s">done</span>
      <span class="na">volumes</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">proc</span>
        <span class="na">hostPath</span><span class="pi">:</span>
          <span class="na">path</span><span class="pi">:</span> <span class="s">/proc</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">sys</span>
        <span class="na">hostPath</span><span class="pi">:</span>
          <span class="na">path</span><span class="pi">:</span> <span class="s">/sys</span>
</code></pre></div></div>

<hr />

<h3 id="123-high-density-pod-scheduling">12.3 High-Density Pod Scheduling</h3>

<h4 id="1231-pod-density-optimization">12.3.1 Pod Density Optimization</h4>

<p><strong>Understanding EKS pod limits:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check maximum pods per node type</span>
curl <span class="nt">-s</span> https://raw.githubusercontent.com/aws/amazon-vpc-cni-k8s/refs/heads/master/misc/eni-max-pods.txt | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s2">"(m5|c5|r5)"</span>

<span class="c"># Current pod density</span>
kubectl get nodes <span class="nt">-o</span> custom-columns<span class="o">=</span>NAME:.metadata.name,PODS:.status.capacity.pods,RUNNING:.status.allocatable.pods
</code></pre></div></div>

<p><strong>High-density scheduling configuration:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ConfigMap</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">amazon-vpc-cni</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">kube-system</span>
<span class="na">data</span><span class="pi">:</span>
  <span class="na">enable-prefix-delegation</span><span class="pi">:</span> <span class="s2">"</span><span class="s">true"</span>  <span class="c1"># Increases pod density</span>
  <span class="na">warm-prefix-target</span><span class="pi">:</span> <span class="s2">"</span><span class="s">1"</span>
  <span class="na">warm-ip-target</span><span class="pi">:</span> <span class="s2">"</span><span class="s">5"</span>
  <span class="na">minimum-ip-target</span><span class="pi">:</span> <span class="s2">"</span><span class="s">10"</span>
</code></pre></div></div>

<h4 id="1232-resource-fragmentation-prevention">12.3.2 Resource Fragmentation Prevention</h4>

<p><strong>Pod resource standardization:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Standard resource classes</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">LimitRange</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">standard-resources</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">limits</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">type</span><span class="pi">:</span> <span class="s">Container</span>
    <span class="na">default</span><span class="pi">:</span>
      <span class="na">cpu</span><span class="pi">:</span> <span class="s">500m</span>
      <span class="na">memory</span><span class="pi">:</span> <span class="s">512Mi</span>
    <span class="na">defaultRequest</span><span class="pi">:</span>
      <span class="na">cpu</span><span class="pi">:</span> <span class="s">100m</span>
      <span class="na">memory</span><span class="pi">:</span> <span class="s">128Mi</span>
    <span class="na">min</span><span class="pi">:</span>
      <span class="na">cpu</span><span class="pi">:</span> <span class="s">50m</span>
      <span class="na">memory</span><span class="pi">:</span> <span class="s">64Mi</span>
    <span class="na">max</span><span class="pi">:</span>
      <span class="na">cpu</span><span class="pi">:</span> <span class="m">4</span>
      <span class="na">memory</span><span class="pi">:</span> <span class="s">8Gi</span>
</code></pre></div></div>

<p><strong>Anti-fragmentation scheduling:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">high-density-app</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">100</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">affinity</span><span class="pi">:</span>
        <span class="na">podAntiAffinity</span><span class="pi">:</span>
          <span class="na">preferredDuringSchedulingIgnoredDuringExecution</span><span class="pi">:</span>
          <span class="pi">-</span> <span class="na">weight</span><span class="pi">:</span> <span class="m">100</span>
            <span class="na">podAffinityTerm</span><span class="pi">:</span>
              <span class="na">labelSelector</span><span class="pi">:</span>
                <span class="na">matchExpressions</span><span class="pi">:</span>
                <span class="pi">-</span> <span class="na">key</span><span class="pi">:</span> <span class="s">app</span>
                  <span class="na">operator</span><span class="pi">:</span> <span class="s">In</span>
                  <span class="na">values</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">high-density-app"</span><span class="pi">]</span>
              <span class="na">topologyKey</span><span class="pi">:</span> <span class="s">kubernetes.io/hostname</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">app</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">app:latest</span>
        <span class="na">resources</span><span class="pi">:</span>
          <span class="na">requests</span><span class="pi">:</span>
            <span class="na">cpu</span><span class="pi">:</span> <span class="s">100m</span>
            <span class="na">memory</span><span class="pi">:</span> <span class="s">128Mi</span>
          <span class="na">limits</span><span class="pi">:</span>
            <span class="na">cpu</span><span class="pi">:</span> <span class="s">200m</span>
            <span class="na">memory</span><span class="pi">:</span> <span class="s">256Mi</span>
</code></pre></div></div>

<hr />

<h3 id="124-api-server-performance-at-scale">12.4 API Server Performance at Scale</h3>

<h4 id="1241-api-server-load-management">12.4.1 API Server Load Management</h4>

<p><strong>Client-side rate limiting:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Configure kubectl rate limiting</span>
<span class="nb">export </span><span class="nv">KUBECTL_QPS</span><span class="o">=</span>50
<span class="nb">export </span><span class="nv">KUBECTL_BURST</span><span class="o">=</span>100

<span class="c"># For applications using client-go</span>
kubectl patch deployment controller-manager <span class="nt">-p</span> <span class="s1">'{"spec":{"template":{"spec":{"containers":[{"name":"manager","env":[{"name":"QPS","value":"20"},{"name":"BURST","value":"30"}]}]}}}}'</span>
</code></pre></div></div>

<p><strong>Watch optimization:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Efficient controller pattern</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">efficient-controller</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">controller</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">controller:latest</span>
        <span class="na">env</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">WATCH_NAMESPACE</span>
          <span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">production"</span>  <span class="c1"># Limit watch scope</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">RESYNC_PERIOD</span>
          <span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">10m"</span>  <span class="c1"># Reduce resync frequency</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">WORKER_COUNT</span>
          <span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">5"</span>  <span class="c1"># Limit concurrent workers</span>
</code></pre></div></div>

<h4 id="1242-etcd-performance-optimization">12.4.2 etcd Performance Optimization</h4>

<p><strong>etcd monitoring at scale:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Monitor etcd performance metrics</span>
kubectl get <span class="nt">--raw</span> /metrics | <span class="nb">grep </span>etcd_request_duration_seconds

<span class="c"># Check etcd database size</span>
kubectl get <span class="nt">--raw</span> /metrics | <span class="nb">grep </span>etcd_mvcc_db_total_size_in_bytes

<span class="c"># Monitor watch streams</span>
kubectl get <span class="nt">--raw</span> /metrics | <span class="nb">grep </span>etcd_network_client_grpc_received_bytes_total
</code></pre></div></div>

<hr />

<h3 id="125-cross-region-and-multi-region-patterns">12.5 Cross-Region and Multi-Region Patterns</h3>

<h4 id="1251-active-active-multi-region-setup">12.5.1 Active-Active Multi-Region Setup</h4>

<p><strong>Regional cluster configuration:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># US-West-2 cluster</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">eksctl.io/v1alpha5</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ClusterConfig</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">prod-us-west-2</span>
  <span class="na">region</span><span class="pi">:</span> <span class="s">us-west-2</span>
  <span class="na">tags</span><span class="pi">:</span>
    <span class="na">Environment</span><span class="pi">:</span> <span class="s">production</span>
    <span class="na">Region</span><span class="pi">:</span> <span class="s">us-west-2</span>
    <span class="na">Role</span><span class="pi">:</span> <span class="s">primary</span>

<span class="c1"># US-East-1 cluster  </span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">eksctl.io/v1alpha5</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ClusterConfig</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">prod-us-east-1</span>
  <span class="na">region</span><span class="pi">:</span> <span class="s">us-east-1</span>
  <span class="na">tags</span><span class="pi">:</span>
    <span class="na">Environment</span><span class="pi">:</span> <span class="s">production</span>
    <span class="na">Region</span><span class="pi">:</span> <span class="s">us-east-1</span>
    <span class="na">Role</span><span class="pi">:</span> <span class="s">secondary</span>
</code></pre></div></div>

<p><strong>Cross-region service mesh:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Istio multi-cluster configuration</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">networking.istio.io/v1alpha3</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Gateway</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">cross-cluster-gateway</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">istio</span><span class="pi">:</span> <span class="s">eastwestgateway</span>
  <span class="na">servers</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">port</span><span class="pi">:</span>
      <span class="na">number</span><span class="pi">:</span> <span class="m">15443</span>
      <span class="na">name</span><span class="pi">:</span> <span class="s">tls</span>
      <span class="na">protocol</span><span class="pi">:</span> <span class="s">TLS</span>
    <span class="na">tls</span><span class="pi">:</span>
      <span class="na">mode</span><span class="pi">:</span> <span class="s">ISTIO_MUTUAL</span>
    <span class="na">hosts</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s2">"</span><span class="s">*.local"</span>
</code></pre></div></div>

<h4 id="1252-global-load-balancing">12.5.2 Global Load Balancing</h4>

<p><strong>Route 53 health checks for multi-region:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Create health check for each region</span>
aws route53 create-health-check <span class="se">\</span>
  <span class="nt">--caller-reference</span> <span class="s2">"eks-us-west-2-</span><span class="si">$(</span><span class="nb">date</span> +%s<span class="si">)</span><span class="s2">"</span> <span class="se">\</span>
  <span class="nt">--health-check-config</span> <span class="nv">Type</span><span class="o">=</span>HTTPS,ResourcePath<span class="o">=</span>/health,FullyQualifiedDomainName<span class="o">=</span>api-us-west-2.example.com,Port<span class="o">=</span>443

<span class="c"># Create weighted routing policy</span>
aws route53 change-resource-record-sets <span class="se">\</span>
  <span class="nt">--hosted-zone-id</span> Z123456789 <span class="se">\</span>
  <span class="nt">--change-batch</span> file://multi-region-routing.json
</code></pre></div></div>

<hr />

<h3 id="126-scaling-challenges-and-solutions">12.6 Scaling Challenges and Solutions</h3>

<h4 id="1261-cluster-autoscaler-at-scale">12.6.1 Cluster Autoscaler at Scale</h4>

<p><strong>Multi-AZ autoscaling configuration:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">cluster-autoscaler</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">kube-system</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">cluster-autoscaler</span>
        <span class="na">image</span><span class="pi">:</span> <span class="s">k8s.gcr.io/autoscaling/cluster-autoscaler:v1.21.0</span>
        <span class="na">command</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="s">./cluster-autoscaler</span>
        <span class="pi">-</span> <span class="s">--v=4</span>
        <span class="pi">-</span> <span class="s">--stderrthreshold=info</span>
        <span class="pi">-</span> <span class="s">--cloud-provider=aws</span>
        <span class="pi">-</span> <span class="s">--skip-nodes-with-local-storage=false</span>
        <span class="pi">-</span> <span class="s">--expander=least-waste</span>
        <span class="pi">-</span> <span class="s">--node-group-auto-discovery=asg:tag=k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/production-cluster</span>
        <span class="pi">-</span> <span class="s">--balance-similar-node-groups</span>
        <span class="pi">-</span> <span class="s">--skip-nodes-with-system-pods=false</span>
        <span class="pi">-</span> <span class="s">--scale-down-delay-after-add=10m</span>
        <span class="pi">-</span> <span class="s">--scale-down-unneeded-time=10m</span>
        <span class="pi">-</span> <span class="s">--max-node-provision-time=15m</span>
</code></pre></div></div>

<h4 id="1262-karpenter-for-large-scale-provisioning">12.6.2 Karpenter for Large-Scale Provisioning</h4>

<p><strong>Karpenter configuration for scale:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">karpenter.sh/v1alpha5</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Provisioner</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">large-scale-provisioner</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">limits</span><span class="pi">:</span>
    <span class="na">resources</span><span class="pi">:</span>
      <span class="na">cpu</span><span class="pi">:</span> <span class="m">10000</span>
      <span class="na">memory</span><span class="pi">:</span> <span class="s">10000Gi</span>
  <span class="na">requirements</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">key</span><span class="pi">:</span> <span class="s">karpenter.sh/capacity-type</span>
    <span class="na">operator</span><span class="pi">:</span> <span class="s">In</span>
    <span class="na">values</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">spot"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">on-demand"</span><span class="pi">]</span>
  <span class="pi">-</span> <span class="na">key</span><span class="pi">:</span> <span class="s">kubernetes.io/arch</span>
    <span class="na">operator</span><span class="pi">:</span> <span class="s">In</span>
    <span class="na">values</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">amd64"</span><span class="pi">]</span>
  <span class="pi">-</span> <span class="na">key</span><span class="pi">:</span> <span class="s">node.kubernetes.io/instance-type</span>
    <span class="na">operator</span><span class="pi">:</span> <span class="s">In</span>
    <span class="na">values</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">m5.large"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">m5.xlarge"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">m5.2xlarge"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">c5.large"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">c5.xlarge"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">c5.2xlarge"</span><span class="pi">]</span>
  <span class="na">providerRef</span><span class="pi">:</span>
    <span class="na">name</span><span class="pi">:</span> <span class="s">large-scale-nodepool</span>
  <span class="na">ttlSecondsAfterEmpty</span><span class="pi">:</span> <span class="m">30</span>
  <span class="na">ttlSecondsUntilExpired</span><span class="pi">:</span> <span class="m">2592000</span>  <span class="c1"># 30 days</span>
</code></pre></div></div>

<hr />

<h3 id="127-operational-patterns-at-scale">12.7 Operational Patterns at Scale</h3>

<h4 id="1271-centralized-logging-and-monitoring">12.7.1 Centralized Logging and Monitoring</h4>

<p><strong>Fluent Bit configuration for high-throughput:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">ConfigMap</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">fluent-bit-config</span>
<span class="na">data</span><span class="pi">:</span>
  <span class="na">fluent-bit.conf</span><span class="pi">:</span> <span class="pi">|</span>
    <span class="s">[SERVICE]</span>
        <span class="s">Flush         5</span>
        <span class="s">Log_Level     info</span>
        <span class="s">Daemon        off</span>
        <span class="s">Parsers_File  parsers.conf</span>
        <span class="s">HTTP_Server   On</span>
        <span class="s">HTTP_Listen   0.0.0.0</span>
        <span class="s">HTTP_Port     2020</span>
        <span class="s">storage.path  /var/fluent-bit/state/flb-storage/</span>
        <span class="s">storage.sync  normal</span>
        <span class="s">storage.checksum off</span>
        <span class="s">storage.backlog.mem_limit 50M</span>
    
    <span class="s">[INPUT]</span>
        <span class="s">Name              tail</span>
        <span class="s">Path              /var/log/containers/*.log</span>
        <span class="s">multiline.parser  docker, cri</span>
        <span class="s">Tag               kube.*</span>
        <span class="s">Mem_Buf_Limit     50MB</span>
        <span class="s">Skip_Long_Lines   On</span>
        <span class="s">Skip_Empty_Lines  On</span>
        <span class="s">storage.type      filesystem</span>
        <span class="s">Refresh_Interval  10</span>
</code></pre></div></div>

<h4 id="1272-gitops-at-scale">12.7.2 GitOps at Scale</h4>

<p><strong>ArgoCD application-of-applications pattern:</strong></p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">argoproj.io/v1alpha1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Application</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">production-apps</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">argocd</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">project</span><span class="pi">:</span> <span class="s">default</span>
  <span class="na">source</span><span class="pi">:</span>
    <span class="na">repoURL</span><span class="pi">:</span> <span class="s">https://github.com/company/k8s-apps</span>
    <span class="na">targetRevision</span><span class="pi">:</span> <span class="s">HEAD</span>
    <span class="na">path</span><span class="pi">:</span> <span class="s">production/applications</span>
  <span class="na">destination</span><span class="pi">:</span>
    <span class="na">server</span><span class="pi">:</span> <span class="s">https://kubernetes.default.svc</span>
    <span class="na">namespace</span><span class="pi">:</span> <span class="s">argocd</span>
  <span class="na">syncPolicy</span><span class="pi">:</span>
    <span class="na">automated</span><span class="pi">:</span>
      <span class="na">prune</span><span class="pi">:</span> <span class="no">true</span>
      <span class="na">selfHeal</span><span class="pi">:</span> <span class="no">true</span>
    <span class="na">syncOptions</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">CreateNamespace=true</span>
</code></pre></div></div>

<hr />

<h3 id="128-scale-related-failure-patterns">12.8 Scale-Related Failure Patterns</h3>

<h4 id="1281-too-many-pods-per-node-failure">12.8.1 “Too many pods per node” Failure</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>Pods stuck in Pending with “Too many pods” error</li>
  <li>Node capacity reached but resources available</li>
</ul>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check pod limits per node</span>
kubectl describe node &lt;node&gt; | <span class="nb">grep</span> <span class="nt">-A</span> 10 <span class="s2">"Allocatable"</span>

<span class="c"># Check current pod count</span>
kubectl get pods <span class="nt">-A</span> <span class="nt">-o</span> wide | <span class="nb">grep</span> &lt;node&gt; | <span class="nb">wc</span> <span class="nt">-l</span>
</code></pre></div></div>

<p><strong>Solutions:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Enable prefix delegation for higher pod density</span>
kubectl <span class="nb">set env </span>daemonset aws-node <span class="nt">-n</span> kube-system <span class="nv">ENABLE_PREFIX_DELEGATION</span><span class="o">=</span><span class="nb">true</span>

<span class="c"># Or use larger instance types</span>
aws eks update-nodegroup-config <span class="se">\</span>
  <span class="nt">--cluster-name</span> production-cluster <span class="se">\</span>
  <span class="nt">--nodegroup-name</span> primary <span class="se">\</span>
  <span class="nt">--instance-types</span> m5.xlarge
</code></pre></div></div>

<h4 id="1282-api-server-overwhelmed-failure">12.8.2 “API server overwhelmed” Failure</h4>

<p><strong>Symptoms:</strong></p>
<ul>
  <li>kubectl commands timeout</li>
  <li>High API server CPU/memory</li>
  <li>etcd performance degradation</li>
</ul>

<p><strong>Diagnosis:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check API server metrics</span>
kubectl get <span class="nt">--raw</span> /metrics | <span class="nb">grep </span>apiserver_request_total

<span class="c"># Check for excessive watch connections</span>
kubectl get <span class="nt">--raw</span> /metrics | <span class="nb">grep </span>apiserver_registered_watchers
</code></pre></div></div>

<p><strong>Solutions:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Implement client-side rate limiting</span>
kubectl patch deployment &lt;controller&gt; <span class="nt">-p</span> <span class="s1">'{"spec":{"template":{"spec":{"containers":[{"name":"controller","env":[{"name":"QPS","value":"10"},{"name":"BURST","value":"15"}]}]}}}}'</span>

<span class="c"># Scale down chatty controllers</span>
kubectl scale deployment &lt;noisy-controller&gt; <span class="nt">--replicas</span><span class="o">=</span>1
</code></pre></div></div>

<hr />

<h2 id="appendices">Appendices</h2>

<h3 id="appendix-a-reference-materials-and-cheat-sheets">Appendix A: Reference Materials and Cheat Sheets</h3>

<h4 id="a1-essential-kubectl-commands-for-eks-troubleshooting">A.1 Essential kubectl Commands for EKS Troubleshooting</h4>

<p><strong>Pod debugging:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Get pod details with events</span>
kubectl describe pod &lt;pod-name&gt;

<span class="c"># Get logs from previous container instance</span>
kubectl logs &lt;pod-name&gt; <span class="nt">--previous</span>

<span class="c"># Get logs from specific container in multi-container pod</span>
kubectl logs &lt;pod-name&gt; <span class="nt">-c</span> &lt;container-name&gt;

<span class="c"># Execute commands in pod</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod-name&gt; <span class="nt">--</span> /bin/bash

<span class="c"># Port forward for local debugging</span>
kubectl port-forward pod/&lt;pod-name&gt; 8080:80
</code></pre></div></div>

<p><strong>Service and networking:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check service endpoints</span>
kubectl get endpoints &lt;service-name&gt;

<span class="c"># Debug service connectivity</span>
kubectl run debug-pod <span class="nt">--image</span><span class="o">=</span>nicolaka/netshoot <span class="nt">--rm</span> <span class="nt">-it</span> <span class="nt">--</span> bash

<span class="c"># Check DNS resolution</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod&gt; <span class="nt">--</span> nslookup kubernetes.default.svc.cluster.local

<span class="c"># Test service connectivity</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod&gt; <span class="nt">--</span> curl &lt;service-name&gt;.&lt;namespace&gt;.svc.cluster.local
</code></pre></div></div>

<p><strong>Node and cluster debugging:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Get node resource usage</span>
kubectl top nodes

<span class="c"># Describe node conditions and capacity</span>
kubectl describe node &lt;node-name&gt;

<span class="c"># Get all pods on a specific node</span>
kubectl get pods <span class="nt">-A</span> <span class="nt">-o</span> wide <span class="nt">--field-selector</span> spec.nodeName<span class="o">=</span>&lt;node-name&gt;

<span class="c"># Check cluster component health</span>
kubectl get componentstatuses
kubectl cluster-info
</code></pre></div></div>

<h4 id="a2-aws-cli-commands-for-eks-operations">A.2 AWS CLI Commands for EKS Operations</h4>

<p><strong>Cluster management:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Update kubeconfig for EKS cluster</span>
aws eks update-kubeconfig <span class="nt">--region</span> &lt;region&gt; <span class="nt">--name</span> &lt;cluster-name&gt;

<span class="c"># Get cluster information</span>
aws eks describe-cluster <span class="nt">--name</span> &lt;cluster-name&gt;

<span class="c"># List all EKS clusters</span>
aws eks list-clusters

<span class="c"># Get cluster endpoint and certificate</span>
aws eks describe-cluster <span class="nt">--name</span> &lt;cluster-name&gt; <span class="nt">--query</span> <span class="s1">'cluster.{endpoint:endpoint,ca:certificateAuthority.data}'</span>
</code></pre></div></div>

<p><strong>Node group operations:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># List node groups</span>
aws eks list-nodegroups <span class="nt">--cluster-name</span> &lt;cluster-name&gt;

<span class="c"># Describe node group</span>
aws eks describe-nodegroup <span class="nt">--cluster-name</span> &lt;cluster-name&gt; <span class="nt">--nodegroup-name</span> &lt;nodegroup-name&gt;

<span class="c"># Update node group scaling</span>
aws eks update-nodegroup-config <span class="nt">--cluster-name</span> &lt;cluster-name&gt; <span class="nt">--nodegroup-name</span> &lt;nodegroup-name&gt; <span class="nt">--scaling-config</span> <span class="nv">minSize</span><span class="o">=</span>2,maxSize<span class="o">=</span>10,desiredSize<span class="o">=</span>5
</code></pre></div></div>

<p><strong>Add-on management:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># List available add-ons</span>
aws eks describe-addon-versions <span class="nt">--kubernetes-version</span> 1.28

<span class="c"># Install add-on</span>
aws eks create-addon <span class="nt">--cluster-name</span> &lt;cluster-name&gt; <span class="nt">--addon-name</span> vpc-cni <span class="nt">--addon-version</span> &lt;version&gt;

<span class="c"># Update add-on</span>
aws eks update-addon <span class="nt">--cluster-name</span> &lt;cluster-name&gt; <span class="nt">--addon-name</span> vpc-cni <span class="nt">--addon-version</span> &lt;new-version&gt;
</code></pre></div></div>

<h4 id="a3-useful-tools-and-utilities">A.3 Useful Tools and Utilities</h4>

<p><strong>Network debugging tools:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Install netshoot for comprehensive network debugging</span>
kubectl run netshoot <span class="nt">--image</span><span class="o">=</span>nicolaka/netshoot <span class="nt">--rm</span> <span class="nt">-it</span> <span class="nt">--</span> bash

<span class="c"># Inside netshoot pod:</span>
nslookup kubernetes.default.svc.cluster.local
dig @8.8.8.8 google.com
curl <span class="nt">-v</span> http://service-name.namespace.svc.cluster.local
traceroute 8.8.8.8
ss <span class="nt">-tulpn</span>
</code></pre></div></div>

<p><strong>Resource analysis tools:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Install kube-capacity for resource analysis</span>
kubectl krew <span class="nb">install </span>resource-capacity
kubectl resource-capacity

<span class="c"># Install kubectl-top for enhanced resource monitoring</span>
kubectl krew <span class="nb">install </span>top
kubectl top pod <span class="nt">--sort-by</span><span class="o">=</span>cpu
</code></pre></div></div>

<hr />

<h3 id="appendix-b-eks-networking-deep-dive---pod-egress-traffic-flow">Appendix B: EKS Networking Deep Dive - Pod Egress Traffic Flow</h3>

<p>This appendix provides a detailed technical analysis of how pods in EKS use link-local addresses for egress traffic, using the <code class="language-plaintext highlighter-rouge">ip</code> command family to trace the complete network path.</p>

<h4 id="b1-eks-pod-networking-architecture">B.1 EKS Pod Networking Architecture</h4>

<p><strong>Understanding the network stack:</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Pod Container] 
    ↓ (veth pair)
[Pod Network Namespace] 
    ↓ (veth peer)
[Node Root Network Namespace]
    ↓ (AWS VPC CNI routing)
[ENI on EC2 Instance]
    ↓ (VPC routing)
[Internet Gateway / NAT Gateway]
    ↓
[Internet]
</code></pre></div></div>

<h4 id="b2-tracing-pod-egress-traffic-step-by-step">B.2 Tracing Pod Egress Traffic Step-by-Step</h4>

<p><strong>Step 1: Examine pod network namespace</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Get pod details and node</span>
kubectl get pod &lt;pod-name&gt; <span class="nt">-o</span> wide

<span class="c"># SSH to the node or use kubectl exec</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod-name&gt; <span class="nt">--</span> bash

<span class="c"># Inside the pod, examine network configuration</span>
ip addr show
ip route show
ip route show table all
</code></pre></div></div>

<p><strong>Example output from inside pod:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># ip addr show</span>
1: lo: &lt;LOOPBACK,UP,LOWER_UP&gt; mtu 65536 qdisc noqueue state UNKNOWN
    inet 127.0.0.1/8 scope host lo
    
3: eth0@if123: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 9001 qdisc noqueue state UP
    inet 10.0.1.45/32 scope global eth0  <span class="c"># Pod IP from VPC CIDR</span>
    
<span class="c"># ip route show</span>
default via 169.254.1.1 dev eth0  <span class="c"># Link-local gateway</span>
169.254.1.1 dev eth0 scope <span class="nb">link</span>   <span class="c"># Link-local route</span>
</code></pre></div></div>

<p><strong>Step 2: Examine the veth pair connection</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># From the node (not inside pod), find the pod's network namespace</span>
docker ps | <span class="nb">grep</span> &lt;pod-name&gt;
docker inspect &lt;container-id&gt; | <span class="nb">grep </span>NetworkMode

<span class="c"># Find the veth pair</span>
ip <span class="nb">link </span>show | <span class="nb">grep</span> <span class="nt">-A1</span> <span class="nt">-B1</span> <span class="s2">"veth"</span>

<span class="c"># Example output:</span>
<span class="c"># 123: veth12345@if3: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 9001 qdisc noqueue master eni-abc123</span>
<span class="c">#     link/ether 12:34:56:78:9a:bc brd ff:ff:ff:ff:ff:ff link-netnsid 1</span>
</code></pre></div></div>

<p><strong>Step 3: Trace the link-local gateway (169.254.1.1)</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># From the node, examine routing for link-local traffic</span>
ip route show table all | <span class="nb">grep </span>169.254.1.1

<span class="c"># Check ARP table for link-local gateway</span>
ip neigh show | <span class="nb">grep </span>169.254.1.1

<span class="c"># Example output:</span>
<span class="c"># 169.254.1.1 dev eni-abc123 lladdr 12:34:56:78:9a:bc REACHABLE</span>
</code></pre></div></div>

<p><strong>The key insight:</strong> The link-local address 169.254.1.1 is not a real gateway. It’s a virtual gateway created by the AWS VPC CNI plugin that maps to the node’s primary ENI.</p>

<h4 id="b3-aws-vpc-cni-link-local-implementation">B.3 AWS VPC CNI Link-Local Implementation</h4>

<p><strong>Step 4: Understanding the CNI’s link-local magic</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Examine the ENI that serves as the "gateway"</span>
ip addr show eni-abc123

<span class="c"># Check routing rules for the ENI</span>
ip rule show
ip route show table main
ip route show table <span class="nb">local</span>

<span class="c"># Example output:</span>
<span class="c"># ip addr show eni-abc123</span>
<span class="c"># 4: eni-abc123: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 9001 qdisc mq state UP</span>
<span class="c">#     inet 10.0.1.10/24 brd 10.0.1.255 scope global eni-abc123  # Node's primary IP</span>
</code></pre></div></div>

<p><strong>Step 5: Tracing the actual egress path</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># From inside the pod, trace route to external destination</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod-name&gt; <span class="nt">--</span> traceroute 8.8.8.8

<span class="c"># From the node, capture traffic to see the actual path</span>
tcpdump <span class="nt">-i</span> eni-abc123 host 8.8.8.8 <span class="nt">-n</span>

<span class="c"># Check iptables rules that handle the traffic</span>
iptables <span class="nt">-t</span> nat <span class="nt">-L</span> POSTROUTING <span class="nt">-n</span> <span class="nt">-v</span>
iptables <span class="nt">-t</span> filter <span class="nt">-L</span> FORWARD <span class="nt">-n</span> <span class="nt">-v</span>
</code></pre></div></div>

<h4 id="b4-the-complete-traffic-flow-analysis">B.4 The Complete Traffic Flow Analysis</h4>

<p><strong>Detailed packet flow for pod egress:</strong></p>

<ol>
  <li><strong>Pod generates traffic:</strong></li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Inside pod: curl https://api.github.com</span>
<span class="c"># Packet: src=10.0.1.45 (pod IP), dst=140.82.112.3 (github.com)</span>
<span class="c"># Route lookup: default via 169.254.1.1 dev eth0</span>
</code></pre></div></div>

<ol>
  <li><strong>Traffic hits veth pair:</strong></li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Packet moves from pod's eth0 to node's veth123</span>
<span class="c"># Node receives packet on veth123 interface</span>
<span class="c"># Source IP still: 10.0.1.45, Destination: 140.82.112.3</span>
</code></pre></div></div>

<ol>
  <li><strong>Node routing decision:</strong></li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Node routing table lookup</span>
ip route get 140.82.112.3 from 10.0.1.45

<span class="c"># Example output:</span>
<span class="c"># 140.82.112.3 from 10.0.1.45 via 10.0.1.1 dev eni-abc123 src 10.0.1.10</span>
<span class="c">#     cache</span>
</code></pre></div></div>

<ol>
  <li><strong>SNAT (Source NAT) transformation:</strong></li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># iptables POSTROUTING chain applies SNAT</span>
iptables <span class="nt">-t</span> nat <span class="nt">-L</span> POSTROUTING <span class="nt">-n</span> <span class="nt">-v</span> | <span class="nb">grep</span> <span class="nt">-A5</span> <span class="nt">-B5</span> <span class="s2">"10.0.1.45"</span>

<span class="c"># Packet transformation:</span>
<span class="c"># Before SNAT: src=10.0.1.45 (pod IP), dst=140.82.112.3</span>
<span class="c"># After SNAT:  src=10.0.1.10 (node IP), dst=140.82.112.3</span>
</code></pre></div></div>

<ol>
  <li><strong>Egress via ENI:</strong></li>
</ol>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Packet exits via node's primary ENI</span>
<span class="c"># AWS VPC routing takes over</span>
<span class="c"># If private subnet: packet goes to NAT Gateway</span>
<span class="c"># If public subnet: packet goes to Internet Gateway</span>
</code></pre></div></div>

<h4 id="b5-advanced-debugging-techniques">B.5 Advanced Debugging Techniques</h4>

<p><strong>Monitoring link-local traffic:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Monitor ARP traffic for link-local gateway</span>
tcpdump <span class="nt">-i</span> any arp and host 169.254.1.1

<span class="c"># Monitor all traffic to/from link-local subnet</span>
tcpdump <span class="nt">-i</span> any net 169.254.0.0/16

<span class="c"># Check conntrack entries for pod traffic</span>
conntrack <span class="nt">-L</span> | <span class="nb">grep </span>10.0.1.45
</code></pre></div></div>

<p><strong>Understanding AWS VPC CNI’s iptables rules:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># AWS VPC CNI creates specific iptables rules</span>
iptables <span class="nt">-t</span> nat <span class="nt">-L</span> AWS-VPC-CNI-POSTROUTING <span class="nt">-n</span> <span class="nt">-v</span>
iptables <span class="nt">-t</span> filter <span class="nt">-L</span> AWS-VPC-CNI-FORWARD <span class="nt">-n</span> <span class="nt">-v</span>

<span class="c"># Example rules:</span>
<span class="c"># Chain AWS-VPC-CNI-POSTROUTING (1 references)</span>
<span class="c"># target     prot opt source               destination         </span>
<span class="c"># MASQUERADE  all  --  10.0.1.0/24         !10.0.0.0/16        /* AWS VPC CNI */</span>
</code></pre></div></div>

<p><strong>Debugging external SNAT mode:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check if external SNAT is enabled</span>
kubectl <span class="nt">-n</span> kube-system get daemonset aws-node <span class="nt">-o</span> yaml | <span class="nb">grep </span>AWS_VPC_K8S_CNI_EXTERNALSNAT

<span class="c"># If external SNAT is disabled (default), node does SNAT</span>
<span class="c"># If external SNAT is enabled, NAT Gateway/Instance does SNAT</span>
</code></pre></div></div>

<h4 id="b6-common-link-local-issues-and-debugging">B.6 Common Link-Local Issues and Debugging</h4>

<p><strong>Issue 1: Link-local gateway unreachable</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Symptoms: Pod can't reach external services</span>
<span class="c"># Debug from inside pod:</span>
ping 169.254.1.1  <span class="c"># Should succeed</span>
ip route get 8.8.8.8  <span class="c"># Should show via 169.254.1.1</span>

<span class="c"># If ping fails, check veth pair:</span>
<span class="c"># From node:</span>
ip <span class="nb">link </span>show | <span class="nb">grep </span>veth
ethtool veth123  <span class="c"># Check if link is up</span>
</code></pre></div></div>

<p><strong>Issue 2: ARP resolution failures</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check ARP table from pod's perspective</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod&gt; <span class="nt">--</span> ip neigh show

<span class="c"># Should show:</span>
<span class="c"># 169.254.1.1 dev eth0 lladdr xx:xx:xx:xx:xx:xx REACHABLE</span>

<span class="c"># If FAILED or missing, check CNI plugin health:</span>
kubectl <span class="nt">-n</span> kube-system logs <span class="nt">-l</span> k8s-app<span class="o">=</span>aws-node
</code></pre></div></div>

<p><strong>Issue 3: SNAT not working</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check if pod traffic is being SNATed correctly</span>
<span class="c"># From node, monitor outgoing traffic:</span>
tcpdump <span class="nt">-i</span> eni-abc123 src host 10.0.1.45  <span class="c"># Should see no traffic (SNATed)</span>
tcpdump <span class="nt">-i</span> eni-abc123 src host 10.0.1.10  <span class="c"># Should see SNATed traffic</span>

<span class="c"># Check iptables SNAT rules:</span>
iptables <span class="nt">-t</span> nat <span class="nt">-L</span> POSTROUTING <span class="nt">-n</span> <span class="nt">-v</span> | <span class="nb">grep </span>10.0.1.45
</code></pre></div></div>

<h4 id="b7-performance-implications-of-link-local-routing">B.7 Performance Implications of Link-Local Routing</h4>

<p><strong>Understanding the overhead:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Measure latency through the link-local path</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod&gt; <span class="nt">--</span> ping <span class="nt">-c</span> 10 169.254.1.1

<span class="c"># Compare with direct node communication</span>
ping <span class="nt">-c</span> 10 10.0.1.10  <span class="c"># From another node</span>

<span class="c"># Monitor CPU usage of network processing</span>
top <span class="nt">-p</span> <span class="si">$(</span>pgrep <span class="nt">-f</span> aws-node<span class="si">)</span>
</code></pre></div></div>

<p><strong>Optimizing for high-throughput workloads:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check network buffer sizes</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod&gt; <span class="nt">--</span> <span class="nb">cat</span> /proc/sys/net/core/rmem_max
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod&gt; <span class="nt">--</span> <span class="nb">cat</span> /proc/sys/net/core/wmem_max

<span class="c"># Monitor network interface statistics</span>
kubectl <span class="nb">exec</span> <span class="nt">-it</span> &lt;pod&gt; <span class="nt">--</span> <span class="nb">cat</span> /proc/net/dev
</code></pre></div></div>

<p>This deep dive shows that the “link-local gateway” at 169.254.1.1 is actually a clever abstraction by the AWS VPC CNI. It’s not a real gateway but a virtual endpoint that allows pods to send traffic to the node’s ENI through the veth pair, where iptables rules then handle SNAT and routing to the actual destination.</p>

<hr />

<h3 id="appendix-c-prometheus-queries-and-eks-limits">Appendix C: Prometheus queries and EKS limits</h3>

<blockquote>
  <p>Tools and utilities are in <a href="#a3-useful-tools-and-utilities">Appendix A.3</a> to avoid duplication.</p>
</blockquote>

<h4 id="c2-prometheus-queries-for-eks-monitoring">C.2 Prometheus Queries for EKS Monitoring</h4>

<p><strong>Node health:</strong></p>

<pre><code class="language-promql"># Node CPU usage
100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)

# Node memory usage
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100

# Node disk usage
(1 - (node_filesystem_avail_bytes / node_filesystem_size_bytes)) * 100
</code></pre>

<p><strong>Pod resource usage:</strong></p>

<pre><code class="language-promql"># Pod CPU usage
rate(container_cpu_usage_seconds_total[5m])

# Pod memory usage
container_memory_working_set_bytes

# Pod network I/O
rate(container_network_receive_bytes_total[5m])
rate(container_network_transmit_bytes_total[5m])
</code></pre>

<h4 id="c3-common-eks-limits-and-quotas">C.3 Common EKS Limits and Quotas</h4>

<p><strong>EKS Service Limits:</strong></p>
<ul>
  <li>Clusters per region: 100</li>
  <li>Node groups per cluster: 30</li>
  <li>Nodes per node group: 450</li>
  <li>Fargate profiles per cluster: 10</li>
</ul>

<p><strong>EC2 Instance Limits (affects node groups):</strong></p>
<ul>
  <li>Default vCPU limit varies by instance family</li>
  <li>Spot instance limits separate from On-Demand</li>
  <li>Elastic IP addresses: 5 per region (affects NAT Gateways)</li>
</ul>

<p><strong>VPC Limits (affects networking):</strong></p>
<ul>
  <li>VPCs per region: 5</li>
  <li>Subnets per VPC: 200</li>
  <li>Route tables per VPC: 200</li>
  <li>Security groups per VPC: 2,500</li>
</ul>

<hr />

<p>This comprehensive appendix provides essential reference materials, deep technical analysis of EKS networking, and practical tools for day-to-day EKS operations. The link-local address deep dive reveals the sophisticated networking abstraction that makes EKS pod networking appear simple while handling complex VPC integration behind the scenes.</p>]]></content><author><name>M Samuel Vijaykumar</name></author><category term="kubernetes, eks, troubleshooting" /><summary type="html"><![CDATA[NOTE: Any discussions can be had on Lobsters]]></summary></entry><entry><title type="html">Life of a Packet in Amazon EKS</title><link href="https://samof76.github.io/life-of-a-packet-in-aws-eks.html" rel="alternate" type="text/html" title="Life of a Packet in Amazon EKS" /><published>2026-05-01T00:00:00+00:00</published><updated>2026-05-01T00:00:00+00:00</updated><id>https://samof76.github.io/life-of-a-packet-in-aws-eks</id><content type="html" xml:base="https://samof76.github.io/life-of-a-packet-in-aws-eks.html"><![CDATA[<p>If you already know Kubernetes architecture, skip to section 3.</p>

<hr />

<h2 id="1-kubernetes-architecture">1. Kubernetes architecture</h2>

<p>Kubernetes has two planes. The <strong>control plane</strong> runs the API server, etcd, scheduler, controller manager, and cloud controller manager. The <strong>data plane</strong> is worker nodes where pods run. Each node has kubelet (starts pods, checks health) and kube-proxy (configures network rules for service traffic).</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+----------------------- Kubernetes Cluster ---------------------+
|                                                                |
|  +--------------- Control Plane ----------------+              |
|  |                                              |              |
|  |  +-----------+  +------+  +-----------+      |              |
|  |  |API Server |  | etcd |  | Scheduler |      |              |
|  |  +-----------+  +------+  +-----------+      |              |
|  |  +------------------+  +----------------+    |              |
|  |  |Controller Manager|  |Cloud Controller|    |              |
|  |  +------------------+  +----------------+    |              |
|  +----------------------------------------------+              |
|                          |                                     |
|                     kubectl / API                              |
|                          |                                     |
|  +--------------- Data Plane -------------------------------+  |
|  |                                                          |  |
|  |  +--- Node 1 ---+  +--- Node 2 ---+  +--- Node N ---+    |  |
|  |  | kubelet      |  | kubelet      |  | kubelet      |    |  |
|  |  | kube-proxy   |  | kube-proxy   |  | kube-proxy   |    |  |
|  |  | +----++----+ |  | +----++----+ |  | +----++----+ |    |  |
|  |  | |Pod ||Pod | |  | |Pod ||Pod | |  | |Pod ||Pod | |    |  |
|  |  | +----++----+ |  | +----++----+ |  | +----++----+ |    |  |
|  |  +--------------+  +--------------+  +--------------+    |  |
|  +----------------------------------------------------------+  |
+----------------------------------------------------------------+
</code></pre></div></div>

<p>In EKS, the control plane lives in an AWS-managed VPC (not yours). It runs at least 2 API server instances and 3 etcd instances across 3 AZs, all in an EC2 Auto Scaling Group. The Kubernetes API sits behind an NLB.</p>

<p>Your worker nodes live in your VPC. They reach the control plane through cross-account ENIs (X-ENIs) in at least two AZs.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+---------- AWS-Managed VPC (EKS Service) ----------+
|                                                    |
|  +--- AZ-a ----+  +--- AZ-b -----+ +--- AZ-c ----+ |
|  | API Server   | | API Server   | |             | |
|  | + Scheduler  | | + Scheduler  | |             | |
|  | + Ctrl Mgr   | | + Ctrl Mgr   | |             | |
|  |              | |              | |             | |
|  |  etcd        | |  etcd        | |  etcd       | |
|  +------+-------+ +------+-------+ +------+------+ |
|         |                |                |        |
+---------+----------------+----------------+--------+
          |                |                |
     +----+----------------+----------------+----+
     |           NLB (Kubernetes API)            |
     +----+----------------+----------------+----+
          |                |                |
  ========+================+================+======== Cross-Account ENIs (X-ENIs)
          |                |                |
+---------+----------------+----------------+--------+
|  +------+------+ +------+------+ +------+-------+  |
|  |  Worker     | |  Worker     | |  Worker      |  |
|  |  Node(s)    | |  Node(s)    | |  Node(s)     |  |
|  |  AZ-a       | |  AZ-b       | |  AZ-c        |  |
|  +-------------+ +-------------+ +--------------+  |
|                                                    |
|                Your VPC                            |
+----------------------------------------------------+
</code></pre></div></div>

<hr />

<h2 id="2-the-kubernetes-network-model">2. The Kubernetes network model</h2>

<p>Kubernetes requires that every pod gets its own IP, pods can talk to each other without NAT, and agents on a node can reach all pods on that node.</p>

<p>A pod can have multiple containers. They share a network namespace: they talk over localhost and share a single <code class="language-plaintext highlighter-rouge">eth0</code> for everything external.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+------------- Pod ----------------+
|                                  |
|  +-----------+  +-----------+    |
|  |Container A|  |Container B|    |
|  +-----+-----+  +-----+-----+    |
|        |   localhost   |         |
|        +-------+-------+         |
|                |                 |
|          +-----+------+          |
|          |    lo      |          |
|          | 127.0.0.1  |          |
|          +------------+          |
|          +------------+          |
|          |   eth0     |          |
|          | 10.0.3.42  |          |
|          +-----+------+          |
|                |                 |
+----------------+-----------------+
                 |
           to the network
</code></pre></div></div>

<hr />

<h2 id="3-how-pods-connect-to-the-network">3. How pods connect to the network</h2>

<p>Each pod gets its own Linux network namespace, connected to the host’s root namespace through a <strong>veth pair</strong>, a virtual Ethernet cable with one end in the pod and one end on the host.</p>

<p>How those veth interfaces plug into the host’s IP stack varies by implementation. Kubernetes doesn’t own this part. A spec called <strong>CNI</strong> (Container Network Interface) defines plugins that handle the wiring: creating the pod’s interface, assigning an IP, setting up the veth pair.</p>

<p>Built-in plugins include loopback, bridge, and ipvlan. Third-party ones include Calico, Cilium, and Amazon VPC CNI.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+---- Pod Netns (Pod A) -------+      +---- Root Network Namespace (Node) ----------+
|                              |      |                                             |
|  +----------+                |      |    +----------+                             |
|  |   eth0   |&lt;-- veth pair --+------+---&gt;|  veth1   |                             |
|  |10.0.3.42 |                |      |    +----------+                             |
|  +----------+                |      |                                             |
|                              |      |    +----------+     +----------+            |
+------------------------------+      |    |  veth2   |     |  ENI-0   |--&gt; VPC     |
                                      |    +----------+     | (primary)|            |
+---- Pod Netns (Pod B) -------+      |         ^           +----------+            |
|                              |      |         |           +----------+            |
|  +----------+                |      |         |           |  ENI-1   |--&gt; VPC     |
|  |   eth0   |&lt;-- veth pair --+------+---------+           |(secondary|            |
|  |10.0.3.55 |                |      |                     +----------+            |
|  +----------+                |      |                                             |
|                              |      |     Linux IP Stack / Routing Tables         |
+------------------------------+      +---------------------------------------------+
</code></pre></div></div>

<h3 id="what-vpc-cni-does">What VPC CNI does</h3>

<p>VPC CNI assigns pod IPs from the VPC CIDR using secondary IPs or prefix delegation on the node’s EC2 ENIs. Pod IPs are real, routable VPC IPs, not overlay addresses.</p>

<p>As pods come and go, VPC CNI adds or removes ENIs on the node to keep enough IPs available. It also configures routing entries on the host and routing + ARP entries inside each pod’s namespace.</p>

<hr />

<h2 id="4-inside-the-pod-and-node">4. Inside the pod and node</h2>

<h3 id="inside-the-pod">Inside the pod</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ip addr show                          $ ip route show
lo:   127.0.0.1/8                       default via 169.254.1.1 dev eth0
eth0: 10.0.3.42/32   &lt;-- /32 mask!     169.254.1.1 dev eth0 scope link

$ arp -a
? (169.254.1.1) at ee:35:a3:c4:21:b7 [ether] PERM   &lt;-- 'M' flag = manual entry
                     ^
                     |
                     +-- This MAC belongs to the veth on the HOST side
</code></pre></div></div>

<p>Three things to notice: the pod’s <code class="language-plaintext highlighter-rouge">eth0</code> has a <strong>/32 subnet mask</strong>, the default gateway is <strong>169.254.1.1</strong> (a link-local address), and the ARP entry for that gateway is a <strong>permanent manual entry</strong> pointing to the host-side veth’s MAC.</p>

<p>These three pieces form a system. Each one exists for a specific reason.</p>

<h3 id="why-32">Why <code class="language-plaintext highlighter-rouge">/32</code>?</h3>

<p>An interface with a <code class="language-plaintext highlighter-rouge">/24</code> (say <code class="language-plaintext highlighter-rouge">10.0.3.42/24</code>) tells the kernel “there are 254 other hosts on this subnet, ARP for their MAC and send directly.” The pod would broadcast ARP on the veth, bypassing the host’s routing tables.</p>

<p>A <code class="language-plaintext highlighter-rouge">/32</code> means the subnet contains exactly one IP, the pod itself. No other IP is “on-link.” The kernel routes every packet through the default gateway, which delivers it to the host-side veth. The node’s policy-based routing tables take it from there.</p>

<p>Without the <code class="language-plaintext highlighter-rouge">/32</code>, pods could ARP for each other directly over the veth, bypassing host routing. That would break VPC CNI’s control over traffic paths.</p>

<h3 id="why-a-link-local-address">Why a link-local address?</h3>

<p>A link-local address (<code class="language-plaintext highlighter-rouge">169.254.0.0/16</code> for IPv4, <code class="language-plaintext highlighter-rouge">fe80::/10</code> for IPv6) is only valid on a single network link. Routers will never forward it. You’ve seen this range before: Windows/Mac APIPA fallback when DHCP fails, and the AWS metadata service at <code class="language-plaintext highlighter-rouge">169.254.169.254</code>.</p>

<p>VPC CNI uses <code class="language-plaintext highlighter-rouge">169.254.1.1</code> because it can’t collide with any real VPC IP (VPCs use <code class="language-plaintext highlighter-rouge">10.x</code>, <code class="language-plaintext highlighter-rouge">172.16-31.x</code>, <code class="language-plaintext highlighter-rouge">192.168.x</code>), it can’t leak beyond the veth pair, and it doesn’t need coordination. Every pod on every node uses the same address.</p>

<p>Nobody actually “owns” <code class="language-plaintext highlighter-rouge">169.254.1.1</code>. The host-side veth has no IP at all. The trick works entirely through the <code class="language-plaintext highlighter-rouge">PERM</code> ARP entry: the kernel looks up <code class="language-plaintext highlighter-rouge">169.254.1.1</code> in its cache, finds the veth MAC, and sends the frame there. No ARP exchange happens on the wire. Running <code class="language-plaintext highlighter-rouge">arping -I eth0 169.254.1.1</code> from inside the pod returns zero responses. Normal traffic works fine because the kernel uses the cache, not the wire.</p>

<h3 id="on-the-node">On the node</h3>

<p>The node has multiple routing tables. Policy-based routing uses the source IP of the traffic to pick which table to consult.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+--- Node Routing Architecture ----------------------------------------+
|                                                                      |
|  Policy-Based Routing Table (ip rule)                                |
|  +---------------------------------------------------------------+   |
|  | from 10.0.3.42  lookup main    &lt;-- Pod 51 (sec IP on ENI-1)   |   |
|  | from 10.0.3.55  lookup 2       &lt;-- Pod 61 (sec IP on ENI-2)   |   |
|  | from all        lookup main                                   |   |
|  +----------+----------------------------+-----------------------+   |
|             |                            |                           |
|             v                            v                           |
|  +--- Main Routing Table ---+  +--- Routing Table 2 -------+         |
|  |                          |  |                            |        |
|  | 10.0.3.42 dev veth1      |  | default via 10.0.0.1       |        |
|  | 10.0.3.55 dev veth2      |  |         dev eni2           |        |
|  | 10.0.0.0/24 dev eni0     |  | (single entry!)            |        |
|  | default via 10.0.0.1     |  |                            |        |
|  |         dev eni0         |  +----------------------------+        |
|  +---------------------------+                                       |
|                                                                      |
|  The main table has routes to local pods AND the subnet.             |
|  Table 2 only has a default gateway. Traffic from pods on            |
|  ENI-2 always hits the VPC router, even for same-subnet              |
|  destinations.                                                       |
+----------------------------------------------------------------------+
</code></pre></div></div>

<hr />

<h2 id="5-packet-walks">5. Packet walks</h2>

<h3 id="5a-ingress-to-a-pod">5a. Ingress to a pod</h3>

<p>Traffic arrives at the node for Pod 61 (a secondary IP on ENI 2):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>                   +--------------- Node -------------------+
                   |                                        |
 Incoming traffic  |  Policy Routing    Main Routing Table  |
 dst: Pod 61 IP -&gt;-|  -------------&gt;   ------------------&gt;  |
                   |  "lookup main"     "Pod 61 -&gt; veth2"   |
                   |                          |             |
                   |                          v             |
                   |                   +--------------+     |
                   |                   |   Pod 61     |     |
                   |                   |  (via veth2) |     |
                   |                   +--------------+     |
                   +----------------------------------------+
</code></pre></div></div>

<p>Policy routing says “look up main table.” Main table has a <code class="language-plaintext highlighter-rouge">/32</code> route for Pod 61 pointing at its veth.</p>

<h3 id="5b-pod-egress-to-the-vpc">5b. Pod egress to the VPC</h3>

<p>Pod 61 sends traffic somewhere in the VPC:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+---------------- Node ------------------------------------------+
|                                                                |
|  +--------+   Policy Routing       Routing Table 2             |
|  | Pod 61 |--&gt;---------------&gt;    ------------------&gt;          |
|  |        |   "from Pod61 IP       "default gw via ENI-2"      |
|  +--------+    lookup table 2"            |                    |
|                                           v                    |
|                                     +----------+               |
|                                     |  ENI-2   |----&gt; VPC Router
|                                     +----------+               |
|                                                                |
+----------------------------------------------------------------+
</code></pre></div></div>

<p>Source IP is Pod 61’s, so policy routing sends it to table 2. Table 2 only has a default gateway through ENI-2. Traffic always goes to the VPC router, even if the destination is on the same subnet. There’s no other route in that table.</p>

<h3 id="5c-pod-to-pod-same-node">5c. Pod-to-pod, same node</h3>

<p>Pod 51 talks to Pod 61, both on the same node:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+------------------------- Node --------------------------------+
|                                                               |
|  +--------+                                     +--------+    |
|  | Pod 51 |                                     | Pod 61 |    |
|  +---+----+                                     +---^----+    |
|      | src MAC: Pod51 MAC                           |         |
|      | dst MAC: veth1 MAC                           |         |
|      v                                              |         |
|  +--------+   Policy    Main Table    +--------+    |         |
|  | veth1  |--&gt;Routing--&gt;Pod61-&gt;veth2-&gt;| veth2  |----+         |
|  +--------+   "lookup                 +--------+              |
|                main"     src MAC: veth2 MAC                   |
|                           dst MAC: Pod61 MAC                  |
|                                                               |
|           No ENIs involved -- traffic stays within the node   |
+---------------------------------------------------------------+
</code></pre></div></div>

<p>Main table, <code class="language-plaintext highlighter-rouge">/32</code> route for Pod 61, forwarded through its veth. Never touches an ENI.</p>

<h3 id="5d-pod-to-pod-across-nodes">5d. Pod-to-pod, across nodes</h3>

<p>Pod 51 on Node A to Pod 81 on Node B:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+----------- Node A ----------------+          +----------- Node B -----------------+
|                                    |          |                                   |
| +--------+                         |          |                       +--------+  |
| | Pod 51 |                         |          |                       | Pod 81 |  |
| +---+----+                         |          |                       +---^----+  |
|     |                              |          |                           |       |
|     v                              |          |                           |       |
|  Policy -&gt; Main Table              |          |  Policy -&gt; Main Table     |       |
|  "Pod81 IP on same subnet          |          |  "Pod81 -&gt; veth"          |       |
|   as ENI-0 -&gt; forward via ENI-0"   |          |                           |       |
|           |                        |          |      +--------+           |       |
|           v                        |          |      | veth   |-----------+       |
|     +----------+                   |          |      +----^---+                   |
|     |  ENI-0   |-------------------+---(VPC)--+----------+                        |
|     +----------+                   |          |      +--------+                   |
|                                    |          |      | ENI-0  |                   |
| src MAC: NodeA ENI-0 MAC           |          |      +--------+                   |
| dst MAC: NodeB ENI-0 MAC           |          |                                   |
+------------------------------------+          | src MAC: veth MAC                 |
                                                | dst MAC: Pod81 MAC                |
                                                +-----------------------------------+
</code></pre></div></div>

<p>Node A’s main table sees Pod 81’s IP on the same subnet as ENI-0, forwards via ENI-0 across the VPC. Node B receives it, main table finds the <code class="language-plaintext highlighter-rouge">/32</code> route for Pod 81, delivers through the veth.</p>

<h3 id="5e-the-return-path-secondary-eni-asymmetry">5e. The return path (secondary ENI asymmetry)</h3>

<p>Pod 81 responds.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+----------- Node B ----------------+          +----------- Node A -----------------+
|                                    |          |                                   |
| +--------+                         |          |                       +--------+  |
| | Pod 81 |                         |          |                       | Pod 51 |  |
| +---+----+                         |          |                       +---^----+  |
|     |                              |          |                           |       |
|     v                              |          |  Policy -&gt; Main Table     |       |
|  Policy -&gt; Table 2                 |          |  "Pod51 -&gt; veth"          |       |
|  "default gw via ENI-2" &lt;--!       |          |                           |       |
|           |                        |          |                           |       |
|           v                        |          |                           |       |
|     +----------+                   |          |     +----------+          |       |
|     |  ENI-2   |-----+             |          |     |  ENI-0   |----------+       |
|     +----------+     |             |          |     +-----^----+                  |
|                      |             |          |           |                       |
+----------------------+-------------+          +-----------+-----------------------+
                       |                                    |
                       v                                    |
               +--------------+                             |
               |  VPC Router  |-----------------------------+
               | (default gw) |
               +--------------+

    Even though Pod 81 and Pod 51 are on the SAME SUBNET,
    traffic goes through the VPC router because routing
    table 2 only has a default gateway entry!
</code></pre></div></div>

<p>Pod 81 is on a secondary ENI, so its traffic uses table 2. Table 2 only knows the default gateway. Even though Pod 51 is on the same subnet, the response goes through the VPC router. Extra hop, but transparent.</p>

<hr />

<h2 id="6-kubernetes-services">6. Kubernetes Services</h2>

<p>Pods are ephemeral. They die, get recreated, come back with new IPs. This can happen thousands of times a day. Other services can’t track individual pod IPs.</p>

<p>A <strong>Kubernetes Service</strong> groups pods by label selectors and gives them a stable virtual IP (the “ClusterIP”). An <strong>endpoints controller</strong> keeps the backing pod list current.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>                          +----------------------+
                          |  Kubernetes Service  |
                          |  name: app1-service  |
                          |  VIP: 172.20.0.100   |
                          |  selector: name=app1 |
                          +----------+-----------+
                                     |
                         +-----------+-----------+
                         |           |           |
                    +----v---+  +----v---+  +----v---+
                    | Pod    |  | Pod    |  | Pod    |
                    | app1   |  | app1   |  | app1   |
                    |10.0.1.5|  |10.0.2.8|  |10.0.3.2|
                    |(ep 1)  |  |(ep 2)  |  |(ep 3)  |
                    +--------+  +--------+  +--------+
                     Node A      Node B      Node C
</code></pre></div></div>

<p>Three service types, each building on the previous:</p>

<table>
  <thead>
    <tr>
      <th>Type</th>
      <th>What it does</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>ClusterIP</strong></td>
      <td>Virtual IP reachable only inside the cluster</td>
    </tr>
    <tr>
      <td><strong>NodePort</strong></td>
      <td>Opens a port on every node, forwards to the service. Built on ClusterIP.</td>
    </tr>
    <tr>
      <td><strong>LoadBalancer</strong></td>
      <td>Provisions a cloud LB in front of NodePort. Built on NodePort.</td>
    </tr>
  </tbody>
</table>

<hr />

<h2 id="7-clusterip">7. ClusterIP</h2>

<p>When you create a ClusterIP service, kube-proxy watches the API server and programs iptables rules for the service’s VIP on every node. Kubernetes DNS assigns a name like <code class="language-plaintext highlighter-rouge">app1-service.default.svc.cluster.local</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>       Node 51                                     Node 71
+-----------------------------------------+   +----------------------+
|                                         |   |                      |
| +-------+ +-------------------------+   |   | +-------+            |
| |Pod 51 |&gt;|      iptables           |   |   | |Pod 71 | (part of   |
| |(app2) | |                         |   |   | |(app1) |  app1 svc) |
| +-------+ | 1. Load balance:        |   |   | +---^---+            |
|  dst:     |    pick Pod 71 IP       |   |   |     |                |
|  SVC VIP  |    (round-robin, may    |   |   |     |                |
|           |     pick ANY pod, even  |   |   | +---+---------------+|
|           |     local ones are not  |   |   | | Node 71 forwards  ||
|           |     preferred!)         |   |   | | to local Pod 71   ||
|           |                         |   |   | +-------------------+|
|           | 2. DNAT:                |   |   |                      |
|           |    dst: VIP -&gt; Pod71 IP |   |   +---+------------------+
|           |                         |   |       ^
|           | 3. Mark flow:           |   |       |
|           |    (stateful tracking   |   |   +---+
|           |     for return traffic) |   |   |
|           +----------+--------------+   |   |
|                      |                  |   |
|                      v                  |   |
|               Forward to Node 71 ------+---+
|               dst IP: Pod 71 IP        |
|                                        |
+----------------------------------------+
</code></pre></div></div>

<p>iptables picks a backend pod (round-robin, no preference for local pods), DNATs the destination from the VIP to the pod IP, and marks the flow for stateful tracking.</p>

<p>Pod 51 doesn’t know the VIP by default. It resolves the service DNS name first, and that DNS query itself goes through a ClusterIP service (kube-dns):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Pod 51 --DNS query--&gt; kube-dns Service VIP --iptables DNAT--&gt; CoreDNS Pod
                                                                   |
Pod 51 &lt;--DNS response (Service VIP: 172.20.0.100)----------------+
</code></pre></div></div>

<p>On the return path, iptables on Node 51 matches the response as return traffic (stateful match) and SNATs the source IP from Pod 71 back to the service VIP. Pod 51 never sees Pod 71’s IP.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> Node 71                                    Node 51
+------------------+    +---------------------------------------------+
|                  |    |                                             |
|  +-------+       |    |    +-----------------------------+ +-------+|
|  |Pod 71 |-------+----+---&gt;|         iptables            |&gt;|Pod 51 ||
|  |       |       |    |    |                             | |       ||
|  +-------+       |    |    | 1. Identify return traffic  | +-------+|
|  src: Pod71 IP   |    |    |    (stateful match)         |          |
|  dst: Pod51 IP   |    |    |                             |          |
|                  |    |    | 2. SNAT:                    |          |
+------------------+    |    |    src IP: Pod71 -&gt; SVC VIP |          |
                        |    |    (Pod 51 thinks it's      |          |
                        |    |     talking to the VIP,     |          |
                        |    |     not Pod 71 directly)    |          |
                        |    +-----------------------------+          |
                        +---------------------------------------------+
</code></pre></div></div>

<p>kube-proxy can also use IPVS or eBPF instead of iptables for better load balancing characteristics.</p>

<hr />

<h2 id="8-nodeport">8. NodePort</h2>

<p>NodePort exposes applications externally, mostly for testing. It builds on ClusterIP but also configures iptables to listen on a specific port (range 30000-32767) on every node.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>       Node 51 (receives traffic)                Node 71
+--------------------------------------------+  +------------------+
|                                            |  |                  |
| External +--------------------------------+|  | +-------+        |
| Client -&gt;|       iptables (4 tasks)       ||  | |Pod 71 |        |
|          |                                ||  | |(app1) |        |
| dst:     | 1. Load balance -&gt; Pod 71 IP   ||  | +---^---+        |
| Node51   |    (may pick remote pod even   ||  |     |            |
| :31234   |     if local pod exists!)      ||  |     |            |
|          |                                ||  +-----+------------+
|          | 2. DNAT: dst -&gt; Pod 71 IP      ||        |
|          |                                ||        |
|          | 3. SNAT: src -&gt; Node 51 IP     |+--------+
|          |    (flow symmetry! without     ||
|          |     this, Pod71 would respond  ||
|          |     directly to client from    ||
|          |     a different IP, breaking   ||
|          |     the connection)            ||
|          |                                ||
|          | 4. Mark flow (stateful)        ||
|          +--------------------------------+|
+--------------------------------------------+
</code></pre></div></div>

<p>The difference from ClusterIP: iptables now does four things instead of three. The extra one is SNAT, rewriting the source IP to the node’s IP. Without it, Pod 71 would respond directly to the client from a different IP, and the client would drop the response:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>WITHOUT SNAT (broken):                   WITH SNAT (works):

Client --&gt; Node51:31234                   Client --&gt; Node51:31234
             |  dst NAT to Pod71               |  dst NAT to Pod71
             v                                 |  src NAT to Node51 IP
           Pod71                               v
             |                               Pod71
             |  responds to Client IP          |
             v  src: Pod71 IP  X BROKEN!       |  responds to Node51 IP
           Client sees response from           v
           unknown IP -- drops it!           Node51
                                               |  reverse NAT
                                               v
                                             Client sees response from
                                             Node51:31234  OK works!
</code></pre></div></div>

<p>The client IP is always SNATed away, even when the destination pod is on the same node. The application never sees the real client IP.</p>

<p>The operational problem: you need to track node IPs (which change as nodes fail and get replaced) and distribute traffic across them. You need a load balancer.</p>

<hr />

<h2 id="9-loadbalancer">9. LoadBalancer</h2>

<p>LoadBalancer builds on NodePort. A service controller provisions a cloud load balancer that forwards to the NodePort on each node.</p>

<p>AWS has two controllers:</p>

<table>
  <thead>
    <tr>
      <th>Controller</th>
      <th>Source</th>
      <th>Provisions</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Service controller</td>
      <td>Built into K8s</td>
      <td>CLB (legacy) or NLB</td>
    </tr>
    <tr>
      <td>AWS Load Balancer Controller</td>
      <td>K8s SIG project on GitHub</td>
      <td>NLB + ALB, Target Type IP, Ingress</td>
    </tr>
  </tbody>
</table>

<h3 id="default-behavior-target-type--instance">Default behavior (target type = instance)</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>                                              +------------ PROBLEM ---------------+
                                              | Node 51 has NO pods for this       |
                                              | service, but NLB thinks it's       |
                                              | healthy because the NodePort       |
                                              | health check passes on ALL nodes   |
                                              +------------------------------------+

  Client              NLB                  Node 51                  Node 71
    |                  |                     |                        |
    |--- request -----&gt;|                     |                        |
    |   dst: NLB IP    |                     |                        |
    |   port: 80       |                     |                        |
    |                  |-- forward ---------&gt;|                        |
    |                  |   dst: Node51 IP    |                        |
    |                  |   port: 31234       |                        |
    |                  |   (NodePort)        |                        |
    |                  |                     | iptables:              |
    |                  |                     |  1. LB -&gt; Pod71 IP     |
    |                  |                     |  2. DNAT dst -&gt; Pod71  |
    |                  |                     |  3. SNAT src -&gt; Node51 |
    |                  |                     |  4. Mark flow          |
    |                  |                     |                        |
    |                  |                     |---- forward ---------&gt; |
    |                  |                     |                        | -&gt; Pod 71
    |                  |                     |                        |
    |                  |                     |&lt;--- response --------- |
    |                  |&lt;-- response --------|                        |
    |                  |    (after iptables  |                        |
    |&lt;-- response -----|     reverse NAT)    |                        |
    |                  |                     |                        |
</code></pre></div></div>

<p>NLB health-checks the NodePort, and all nodes pass, even ones with no pods for that service. Traffic can land on Node 51 (no local pod) and bounce to Node 71 (has the pod). This is <strong>traffic tromboning</strong>: extra hops, latency, cross-AZ data transfer charges, and the client IP gets SNATed.</p>

<h3 id="fix-1-externaltrafficpolicy-local">Fix 1: externalTrafficPolicy: Local</h3>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">spec</span><span class="pi">:</span>
  <span class="na">externalTrafficPolicy</span><span class="pi">:</span> <span class="s">Local</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  Client              NLB                   Node 51            Node 71
    |                  |                      |                   |
    |--- request -----&gt;|                      |                   |
    |                  |                      |                   |
    |                  |  health check        |                   |
    |                  |  (NodePort) --------&gt;| PASS              |
    |                  |                      |                   |
    |                  |  health check        |                   |
    |                  |  (EXTRA port) ------&gt;| FAIL              |
    |                  |  "any local pods?"   |  (no local pods)  |
    |                  |                      |                   |
    |                  |  health check        |                   |
    |                  |  (EXTRA port) -------+------------------&gt;| PASS
    |                  |                      |                   |  (has Pod71)
    |                  |                      |                   |
    |                  |-- forward directly --+------------------&gt;|
    |                  |   (skips Node 51!)   |                   |
    |                  |                      |                   | iptables:
    |                  |                      |                   |  DNAT only
    |                  |                      |                   |  (no SNAT!)
    |                  |                      |                   |  -&gt; Pod 71
    |                  |                      |                   |
    |&lt;-----------------+----------------------+--- response ----- |
    |                  |                      |  client IP visible|
</code></pre></div></div>

<p>NLB runs an additional health check on a different port. Nodes without local pods fail it. iptables only forwards to local pods and skips SNAT, so the application sees the real client IP.</p>

<p>Trade-off: uneven pod distribution means uneven traffic distribution.</p>

<h3 id="fix-2-target-type-ip-requires-aws-load-balancer-controller">Fix 2: Target Type IP (requires AWS Load Balancer Controller)</h3>

<p>NLB targets pods directly instead of nodes.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  Client              NLB                            Node 71
    |                  |                                |
    |--- request -----&gt;|                                |
    |   dst: NLB IP    |                                |
    |                  |                                |
    |                  |  Target Group:                 |
    |                  |  +----------------------+      |
    |                  |  | Pod71 IP    healthy  |      |
    |                  |  | Pod81 IP    healthy  |      |
    |                  |  | Pod91 IP    healthy  |      |
    |                  |  | (pods, NOT nodes!)   |      |
    |                  |  +----------------------+      |
    |                  |                                |
    |                  |------ forward ----------------&gt;|
    |                  |  dst IP: Pod71 IP directly     |
    |                  |                                |
    |                  |       iptables does NOTHING    |
    |                  |       (dst IP already is       |
    |                  |        Pod71 IP)               |
    |                  |                                | -&gt; Pod 71
    |                  |                                |
    |&lt;-----------------+-------- response ------------- |
    |                  |                                |
</code></pre></div></div>

<p>No NodePort, no iptables. The destination IP is already the pod IP, so the node just forwards through the veth. Source IP is the NLB’s by default; enable <code class="language-plaintext highlighter-rouge">preserve client IP</code> via annotation if needed.</p>

<p>Watch ELB service quotas. Thousands of pods means thousands of targets in a single target group.</p>

<hr />

<h2 id="10-ingress-layer-7">10. Ingress (Layer 7)</h2>

<p>Everything above is Layer 4. Kubernetes Services don’t understand HTTP paths or hostnames.</p>

<p><strong>Ingress</strong> handles Layer 7 routing. You define rules like <code class="language-plaintext highlighter-rouge">/order</code> goes to order-service, <code class="language-plaintext highlighter-rouge">/rating</code> goes to rating-service:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">networking.k8s.io/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Ingress</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">rules</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">http</span><span class="pi">:</span>
      <span class="na">paths</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">path</span><span class="pi">:</span> <span class="s">/order</span>
        <span class="na">backend</span><span class="pi">:</span>
          <span class="na">service</span><span class="pi">:</span>
            <span class="na">name</span><span class="pi">:</span> <span class="s">order-service</span>
      <span class="pi">-</span> <span class="na">path</span><span class="pi">:</span> <span class="s">/rating</span>
        <span class="na">backend</span><span class="pi">:</span>
          <span class="na">service</span><span class="pi">:</span>
            <span class="na">name</span><span class="pi">:</span> <span class="s">rating-service</span>
</code></pre></div></div>

<p>Kubernetes expects an Ingress controller to provision a load balancer that implements these rules. The AWS Load Balancer Controller provisions an ALB.</p>

<p>With Target Type IP, the ALB sends traffic directly to pods:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  Client              ALB                          Node 71
    |                  |                              |
    |-- GET /order ---&gt;|                              |
    |                  |                              |
    |                  |  L7 routing decision:        |
    |                  |  /order -&gt; order-service     |
    |                  |  pick Pod 71 from targets    |
    |                  |                              |
    |                  |---- forward ----------------&gt;|
    |                  |  dst: Pod71 IP               |
    |                  |  src: ALB IP  (always SNAT!) |
    |                  |                              |
    |                  |     iptables: does NOTHING   |
    |                  |                              | -&gt; Pod 71
    |                  |                              |
    |&lt;-- response -----|&lt;---- response -------------- |
    |  src: ALB IP     |                              |
</code></pre></div></div>

<p>ALB always SNATs. You can’t preserve the client IP at L4, but <code class="language-plaintext highlighter-rouge">X-Forwarded-For</code> headers carry it at L7.</p>

<h3 id="full-end-to-end-dns-to-pod">Full end-to-end: DNS to pod</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  Client            Route 53       Internet GW        ALB                Pod
    |                  |               |                |                  |
    | DNS: portal.     |               |                |                  |
    | example.com ----&gt;|               |                |                  |
    |                  |               |                |                  |
    |&lt;- alias record --|               |                |                  |
    |   returns ALB    |               |                |                  |
    |   public IPs:    |               |                |                  |
    |   [IP1,IP2,IP3]  |               |                |                  |
    |                  |               |                |                  |
    | picks IP3                        |                |                  |
    |-------- request (dst: IP3) -----&gt;|                |                  |
    |                                  |                |                  |
    |                                  | DNAT:          |                  |
    |                                  | dst: Public IP3|                  |
    |                                  |  -&gt; ALB Private|                  |
    |                                  |     IP (AZ-c)  |                  |
    |                                  |                |                  |
    |                                  |--- forward ---&gt;|                  |
    |                                  |                |                  |
    |                                  |                | L7 routing       |
    |                                  |                | decision         |
    |                                  |                |                  |
    |                                  |                | Cross-zone LB ON |
    |                                  |                | may pick pod in  |
    |                                  |                | ANY AZ           |
    |                                  |                |                  |
    |                                  |                |--- forward -----&gt;|
    |                                  |                |  dst: Pod IP     |
    |                                  |                |  src: ALB IP     |
    |                                  |                |                  |
    |&lt;------------------------- response ----------------------------------|
</code></pre></div></div>

<p>Route 53 returns ALB public IPs. The Internet Gateway DNATs the public IP to the ALB’s private IP. Cross-zone load balancing is on by default, so the ALB may forward to any AZ. Disable it for AZ-local routing.</p>

<hr />

<h2 id="11-pod-egress">11. Pod egress</h2>

<p>By default (<code class="language-plaintext highlighter-rouge">AWS_VPC_K8S_CNI_EXTERNALSNAT=false</code>), VPC CNI applies SNAT on the node itself for all pod traffic leaving the VPC CIDR. The pod IP gets rewritten to the node’s primary ENI IP before the packet even leaves the node:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+------------ Node ----------------------+
|                                        |
|  +---------------+                     |
|  |    Pod        |                     |
|  | 192.168.1.51  |                     |
|  +-------+-------+                     |
|          | dst: 8.8.8.8 (internet)     |        Internet Gateway
|          | src: 192.168.1.51           |             |
|          v                             |             |
|  +---------------+                     |             |
|  |   VPC CNI     |                     |             |
|  |   SNAT #1     |                     |             |
|  |   src: .51 -&gt; |                     |             |
|  |   .50 (node   |                     |             |
|  |   primary ENI |                     |             |
|  |   primary IP) |                     |             |
|  +-------+-------+                     |             |
|          | src: 192.168.1.50           |             |
|          v                             |             |
|     +----------+                       |             |
|     | ENI-0    |-----------------------+------------&gt;|
|     | (primary)|                       |             | SNAT #2
|     +----------+                       |             | src: .50 -&gt; Public IP
|                                        |             | (associated with .50)
+----------------------------------------+             |
                                                       +-------&gt; Internet
                                                       |  src: Public IP
                                                       |  dst: 8.8.8.8
</code></pre></div></div>

<p>Two-stage SNAT: VPC CNI rewrites the pod IP to the node’s primary ENI IP, then the IGW/NAT Gateway rewrites it to the public IP.</p>

<h3 id="aws_vpc_k8s_cni_externalsnat">AWS_VPC_K8S_CNI_EXTERNALSNAT</h3>

<p>The default (<code class="language-plaintext highlighter-rouge">false</code>) means VPC CNI does SNAT on the node. All pods on a node share that node’s IP for outbound traffic. External services see one IP per node, not per pod.</p>

<p>Setting <code class="language-plaintext highlighter-rouge">AWS_VPC_K8S_CNI_EXTERNALSNAT=true</code> disables the node-level SNAT. The pod’s real IP survives all the way to the NAT Gateway, which does the only SNAT (pod IP directly to public IP). The node doesn’t rewrite anything.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>EXTERNALSNAT=false (default):           EXTERNALSNAT=true:

Pod (.51) --&gt; Node SNAT (.50) --&gt;       Pod (.51) --&gt; Node (no SNAT) --&gt;
  NAT GW SNAT (public IP) --&gt;            NAT GW SNAT (public IP) --&gt;
  Internet                                Internet

External sees: node IP                  External sees: pod IP
(all pods on this node                  (until NAT GW, where it
 look the same)                          becomes the public IP)
</code></pre></div></div>

<p>When <code class="language-plaintext highlighter-rouge">EXTERNALSNAT=true</code>:</p>
<ul>
  <li>Pod IPs are visible in VPC flow logs and security group tracking, making it easier to trace which pod is talking to what</li>
  <li>External APIs that rate-limit by source IP won’t treat all pods on a node as one client</li>
  <li>Service meshes (Istio, Linkerd) work correctly since they expect pod IPs in the traffic</li>
  <li>You need your VPC routing and NAT Gateway set up to handle pod CIDR traffic (the pod IPs need a route to the NAT Gateway)</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># check current setting</span>
kubectl get daemonset aws-node <span class="nt">-n</span> kube-system <span class="nt">-o</span> json | <span class="se">\</span>
  jq <span class="s1">'.spec.template.spec.containers[0].env[] | select(.name=="AWS_VPC_K8S_CNI_EXTERNALSNAT")'</span>

<span class="c"># enable it</span>
kubectl <span class="nb">set env </span>daemonset <span class="nt">-n</span> kube-system aws-node <span class="nv">AWS_VPC_K8S_CNI_EXTERNALSNAT</span><span class="o">=</span><span class="nb">true</span>
</code></pre></div></div>

<p>The experiments in the appendix were run with <code class="language-plaintext highlighter-rouge">EXTERNALSNAT=true</code>. That’s why the <code class="language-plaintext highlighter-rouge">checkip.amazonaws.com</code> result shows the NAT Gateway’s public IP directly, not the node IP — VPC CNI isn’t doing node-level SNAT.</p>

<hr />

<h2 id="summary">Summary</h2>

<p>Inbound external traffic, from least to most efficient:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  LEAST EFFICIENT                                            MOST EFFICIENT
  &lt;---------------------------------------------------------------------------&gt;

  LoadBalancer         + externalTraffic    + Target Type IP   Ingress + ALB
  (default)              Policy: Local       (NLB)             + Target IP
  +----------+         +----------+         +----------+      +----------+
  | NLB      |         | NLB      |         | NLB      |      | ALB      |
  |   |      |         |   |      |         |   |      |      |   |      |
  | Node     |         | Node     |         | Pod      |      | Pod      |
  | (any!)   |         | (w/ pod) |         | directly |      | directly |
  |   |      |         |   |      |         |          |      |          |
  | iptables |         | iptables |         | no       |      | no       |
  | DNAT+SNAT|         | DNAT only|         | iptables |      | iptables |
  |   |      |         |   |      |         |          |      |          |
  | Node     |         | Pod      |         +----------+      +----------+
  | (maybe)  |         | (local)  |
  |   |      |         |          |         Client IP:         Client IP:
  | Pod      |         +----------+         via annotation    via X-Forwarded-For
  +----------+
                       Client IP:
  Client IP:           visible
  SNATed away
</code></pre></div></div>

<table>
  <thead>
    <tr>
      <th>Approach</th>
      <th>Hops</th>
      <th>SNAT?</th>
      <th>Client IP visible?</th>
      <th>iptables?</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>LoadBalancer (default)</td>
      <td>NLB -&gt; Node -&gt; maybe another Node -&gt; Pod</td>
      <td>DNAT+SNAT</td>
      <td>No</td>
      <td>Yes</td>
    </tr>
    <tr>
      <td>+ <code class="language-plaintext highlighter-rouge">externalTrafficPolicy: Local</code></td>
      <td>NLB -&gt; Node (with pod) -&gt; Pod</td>
      <td>DNAT only</td>
      <td>Yes</td>
      <td>Yes</td>
    </tr>
    <tr>
      <td>+ Target Type IP (NLB)</td>
      <td>NLB -&gt; Pod</td>
      <td>NLB IP</td>
      <td>Via annotation</td>
      <td>No</td>
    </tr>
    <tr>
      <td>Ingress + ALB + Target IP</td>
      <td>ALB -&gt; Pod</td>
      <td>ALB IP</td>
      <td>Via X-Forwarded-For</td>
      <td>No</td>
    </tr>
  </tbody>
</table>

<hr />

<h2 id="appendix-experiments-on-a-live-cluster">Appendix: Experiments on a live cluster</h2>

<p>Everything above was verified on a live EKS cluster using two <a href="https://github.com/nicolaka/netshoot">netshoot</a> toolbox pods and an echoserver deployment. If you want to follow along, apply these manifests:</p>

<p><strong>tools.yaml</strong> — namespace + netshoot toolbox pod for debugging:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">---</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Namespace</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">packetlab</span>
<span class="nn">---</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">toolbox</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">packetlab</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">1</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">matchLabels</span><span class="pi">:</span>
      <span class="na">app</span><span class="pi">:</span> <span class="s">toolbox</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">labels</span><span class="pi">:</span>
        <span class="na">app</span><span class="pi">:</span> <span class="s">toolbox</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">containers</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">netshoot</span>
          <span class="na">image</span><span class="pi">:</span> <span class="s">ghcr.io/nicolaka/netshoot:latest</span>
          <span class="na">imagePullPolicy</span><span class="pi">:</span> <span class="s">IfNotPresent</span>
          <span class="na">command</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">sleep"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">infinity"</span><span class="pi">]</span>
          <span class="na">securityContext</span><span class="pi">:</span>
            <span class="na">allowPrivilegeEscalation</span><span class="pi">:</span> <span class="no">true</span>
            <span class="na">capabilities</span><span class="pi">:</span>
              <span class="na">add</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">NET_ADMIN"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">NET_RAW"</span><span class="pi">]</span>
          <span class="na">resources</span><span class="pi">:</span>
            <span class="na">requests</span><span class="pi">:</span>
              <span class="na">cpu</span><span class="pi">:</span> <span class="s2">"</span><span class="s">100m"</span>
              <span class="na">memory</span><span class="pi">:</span> <span class="s2">"</span><span class="s">128Mi"</span>
            <span class="na">limits</span><span class="pi">:</span>
              <span class="na">cpu</span><span class="pi">:</span> <span class="s2">"</span><span class="s">500m"</span>
              <span class="na">memory</span><span class="pi">:</span> <span class="s2">"</span><span class="s">512Mi"</span>
</code></pre></div></div>

<p><strong>echoserver.yaml</strong> — echo server with ClusterIP and NodePort services:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">---</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">apps/v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Deployment</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">echoserver</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">packetlab</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">replicas</span><span class="pi">:</span> <span class="m">2</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">matchLabels</span><span class="pi">:</span>
      <span class="na">app</span><span class="pi">:</span> <span class="s">echoserver</span>
  <span class="na">template</span><span class="pi">:</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">labels</span><span class="pi">:</span>
        <span class="na">app</span><span class="pi">:</span> <span class="s">echoserver</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">topologySpreadConstraints</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">maxSkew</span><span class="pi">:</span> <span class="m">1</span>
          <span class="na">topologyKey</span><span class="pi">:</span> <span class="s">kubernetes.io/hostname</span>
          <span class="na">whenUnsatisfiable</span><span class="pi">:</span> <span class="s">DoNotSchedule</span>
          <span class="na">labelSelector</span><span class="pi">:</span>
            <span class="na">matchLabels</span><span class="pi">:</span>
              <span class="na">app</span><span class="pi">:</span> <span class="s">echoserver</span>
      <span class="na">containers</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">echoserver</span>
          <span class="na">image</span><span class="pi">:</span> <span class="s">registry.k8s.io/echoserver:1.10</span>
          <span class="na">ports</span><span class="pi">:</span>
            <span class="pi">-</span> <span class="na">containerPort</span><span class="pi">:</span> <span class="m">8080</span>
          <span class="na">resources</span><span class="pi">:</span>
            <span class="na">requests</span><span class="pi">:</span>
              <span class="na">cpu</span><span class="pi">:</span> <span class="s2">"</span><span class="s">50m"</span>
              <span class="na">memory</span><span class="pi">:</span> <span class="s2">"</span><span class="s">64Mi"</span>
            <span class="na">limits</span><span class="pi">:</span>
              <span class="na">cpu</span><span class="pi">:</span> <span class="s2">"</span><span class="s">200m"</span>
              <span class="na">memory</span><span class="pi">:</span> <span class="s2">"</span><span class="s">128Mi"</span>
<span class="nn">---</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Service</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">echo-clusterip</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">packetlab</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">ClusterIP</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">echoserver</span>
  <span class="na">ports</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">port</span><span class="pi">:</span> <span class="m">80</span>
      <span class="na">targetPort</span><span class="pi">:</span> <span class="m">8080</span>
      <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
<span class="nn">---</span>
<span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Service</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">echo-nodeport</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">packetlab</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">NodePort</span>
  <span class="na">selector</span><span class="pi">:</span>
    <span class="na">app</span><span class="pi">:</span> <span class="s">echoserver</span>
  <span class="na">ports</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">port</span><span class="pi">:</span> <span class="m">80</span>
      <span class="na">targetPort</span><span class="pi">:</span> <span class="m">8080</span>
      <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
</code></pre></div></div>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kubectl apply <span class="nt">-f</span> tools.yaml
<span class="nv">$ </span>kubectl apply <span class="nt">-f</span> echoserver.yaml
</code></pre></div></div>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kubectl <span class="nt">-n</span> packetlab get pods <span class="nt">-o</span> wide
NAME                         READY   STATUS    IP              NODE
toolbox-a-6fb5b9b786-lszr4   1/1     Running   10.0.1.51   ip-10-0-0-75.ec2.internal
toolbox-b-754f9d4d47-qbnzl   1/1     Running   10.0.2.29   ip-10-0-0-49.ec2.internal
echoserver-545755bc68-v5kdv  1/1     Running   10.0.3.193  ip-10-0-1-228.ec2.internal
</code></pre></div></div>

<ul>
  <li>Toolbox A: <code class="language-plaintext highlighter-rouge">10.0.1.51</code> on node <code class="language-plaintext highlighter-rouge">10.0.0.75</code></li>
  <li>Toolbox B: <code class="language-plaintext highlighter-rouge">10.0.2.29</code> on node <code class="language-plaintext highlighter-rouge">10.0.0.49</code></li>
  <li>Echoserver: <code class="language-plaintext highlighter-rouge">10.0.3.193</code> on node <code class="language-plaintext highlighter-rouge">10.0.1.228</code></li>
</ul>

<h3 id="experiment-1-pod-interfaces-routes-and-arp">Experiment 1: Pod interfaces, routes, and ARP</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kubectl <span class="nt">-n</span> packetlab <span class="nb">exec </span>toolbox-a-6fb5b9b786-lszr4 <span class="nt">--</span> ip addr show
3: eth0@if336: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 9001 qdisc noqueue state UP group default
    <span class="nb">link</span>/ether 0a:12:c5:d0:e4:e1 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.0.1.51/32 scope global eth0         &lt;<span class="nt">--</span> /32!

<span class="nv">$ </span>kubectl <span class="nt">-n</span> packetlab <span class="nb">exec </span>toolbox-a-6fb5b9b786-lszr4 <span class="nt">--</span> ip route show
default via 169.254.1.1 dev eth0
169.254.1.1 dev eth0 scope <span class="nb">link</span>

<span class="nv">$ </span>kubectl <span class="nt">-n</span> packetlab <span class="nb">exec </span>toolbox-a-6fb5b9b786-lszr4 <span class="nt">--</span> arp <span class="nt">-a</span>
? <span class="o">(</span>169.254.1.1<span class="o">)</span> at 5a:f3:56:0b:86:d4 <span class="o">[</span>ether] PERM on eth0

<span class="nv">$ </span>kubectl <span class="nt">-n</span> packetlab <span class="nb">exec </span>toolbox-a-6fb5b9b786-lszr4 <span class="nt">--</span> <span class="nb">cat</span> /etc/resolv.conf
search packetlab.svc.cluster.local svc.cluster.local cluster.local ec2.internal
nameserver 172.20.0.10
options ndots:5
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">/32</code> on eth0, <code class="language-plaintext highlighter-rouge">169.254.1.1</code> fake gateway, <code class="language-plaintext highlighter-rouge">PERM</code> ARP entry with MAC <code class="language-plaintext highlighter-rouge">5a:f3:56:0b:86:d4</code>, kube-dns at <code class="language-plaintext highlighter-rouge">172.20.0.10</code>.</p>

<h3 id="experiment-2-node-side-veth-enis-and-routing-tables">Experiment 2: Node-side veth, ENIs, and routing tables</h3>

<p>SSH to the node:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ip <span class="nb">link </span>show eniaee705c1f69
336: eniaee705c1f69@if3: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 9001 qdisc noqueue state UP
    <span class="nb">link</span>/ether 5a:f3:56:0b:86:d4 brd ff:ff:ff:ff:ff:ff link-netns cni-01fc068f-c24b-470b-7551-ac4d8bc24a67
</code></pre></div></div>

<p>MAC <code class="language-plaintext highlighter-rouge">5a:f3:56:0b:86:d4</code> matches the pod’s ARP entry. The pod sends frames here thinking it’s the gateway.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ip addr show ens5   <span class="c"># Primary ENI: 10.0.0.75 (main table)</span>
<span class="nv">$ </span>ip addr show ens6   <span class="c"># Secondary ENI: 10.0.0.85 (table 2)</span>
<span class="nv">$ </span>ip addr show ens7   <span class="c"># Third ENI: 10.0.0.84 (table 3)</span>
</code></pre></div></div>

<table>
  <thead>
    <tr>
      <th>ENI</th>
      <th>Interface</th>
      <th>IP</th>
      <th>Routing Table</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Primary</td>
      <td>ens5</td>
      <td>10.0.0.75</td>
      <td>main</td>
    </tr>
    <tr>
      <td>Secondary</td>
      <td>ens6</td>
      <td>10.0.0.85</td>
      <td>2</td>
    </tr>
    <tr>
      <td>Third</td>
      <td>ens7</td>
      <td>10.0.0.84</td>
      <td>3</td>
    </tr>
  </tbody>
</table>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ip rule show
512:    from all to 10.0.1.51 lookup main     &lt;<span class="nt">--</span> ingress TO our pod
1536:   from 10.0.1.51 lookup 2               &lt;<span class="nt">--</span> egress FROM our pod

<span class="nv">$ </span>ip route show table main
default via 10.0.0.1 dev ens5 proto dhcp src 10.0.0.75 metric 512
10.0.0.0/24 dev ens5 proto kernel scope <span class="nb">link </span>src 10.0.0.75 metric 512
10.0.1.51 dev eniaee705c1f69 scope <span class="nb">link</span>        &lt;<span class="nt">--</span> our pod<span class="s1">'s veth

$ ip route show table 2
default via 10.0.0.1 dev ens6
10.0.0.1 dev ens6 scope link
</span></code></pre></div></div>

<p>Table 2: two entries, just the default gateway via ens6. Pods on secondary ENIs always go through the VPC router.</p>

<h3 id="experiment-3-pod-to-pod-connectivity">Experiment 3: Pod-to-pod connectivity</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kubectl <span class="nt">-n</span> packetlab <span class="nb">exec </span>toolbox-a-6fb5b9b786-lszr4 <span class="nt">--</span> ping <span class="nt">-c</span> 3 10.0.0.75
64 bytes from 10.0.0.75: <span class="nv">icmp_seq</span><span class="o">=</span>1 <span class="nv">ttl</span><span class="o">=</span>127 <span class="nb">time</span><span class="o">=</span>0.062 ms     &lt;<span class="nt">--</span> same node, ~0.06ms

<span class="nv">$ </span>kubectl <span class="nt">-n</span> packetlab <span class="nb">exec </span>toolbox-a-6fb5b9b786-lszr4 <span class="nt">--</span> ping <span class="nt">-c</span> 3 10.0.2.29
64 bytes from 10.0.2.29: <span class="nv">icmp_seq</span><span class="o">=</span>1 <span class="nv">ttl</span><span class="o">=</span>125 <span class="nb">time</span><span class="o">=</span>1.67 ms      &lt;<span class="nt">--</span> cross-node, ~0.4ms
64 bytes from 10.0.2.29: <span class="nv">icmp_seq</span><span class="o">=</span>2 <span class="nv">ttl</span><span class="o">=</span>125 <span class="nb">time</span><span class="o">=</span>0.424 ms
</code></pre></div></div>

<p>TTL 125 = three hops (pod to host, host to VPC router, VPC router to remote host).</p>

<h3 id="experiment-4-dns-through-clusterip">Experiment 4: DNS through ClusterIP</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kubectl get svc <span class="nt">-n</span> kube-system kube-dns <span class="nt">-o</span> wide
NAME       TYPE        CLUSTER-IP    EXTERNAL-IP   PORT<span class="o">(</span>S<span class="o">)</span>                  SELECTOR
kube-dns   ClusterIP   172.20.0.10   &lt;none&gt;        53/UDP,53/TCP,9153/TCP   k8s-app<span class="o">=</span>kube-dns

<span class="nv">$ </span>kubectl <span class="nt">-n</span> packetlab <span class="nb">exec </span>toolbox-a-6fb5b9b786-lszr4 <span class="nt">--</span> dig +short kubernetes.default.svc.cluster.local
172.20.0.1
</code></pre></div></div>

<p>iptables on the node:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>iptables <span class="nt">-t</span> nat <span class="nt">-L</span> KUBE-SVC-TCOU7JCQXEZGVUNU <span class="nt">-n</span>
Chain KUBE-SVC-TCOU7JCQXEZGVUNU <span class="o">(</span>1 references<span class="o">)</span>
target                      prot opt <span class="nb">source    </span>destination
KUBE-SEP-43APF7RZ6NYHDOWA  all  <span class="nt">--</span>  0.0.0.0/0  0.0.0.0/0  /<span class="k">*</span> -&gt; 10.0.1.181:53 <span class="k">*</span>/ statistic mode random probability 0.50000000000
KUBE-SEP-5PFK5H2IGDZ73DVD  all  <span class="nt">--</span>  0.0.0.0/0  0.0.0.0/0  /<span class="k">*</span> -&gt; 10.0.3.231:53 <span class="k">*</span>/
</code></pre></div></div>

<p>50/50 random split across two CoreDNS pods.</p>

<h3 id="experiment-5-clusterip-service">Experiment 5: ClusterIP service</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kubectl <span class="nt">-n</span> packetlab get svc echo-clusterip <span class="nt">-o</span> wide
NAME             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT<span class="o">(</span>S<span class="o">)</span>   SELECTOR
echo-clusterip   ClusterIP   172.20.122.189   &lt;none&gt;        80/TCP    <span class="nv">app</span><span class="o">=</span>echoserver

<span class="nv">$ </span>kubectl <span class="nt">-n</span> packetlab <span class="nb">exec </span>toolbox-a-6fb5b9b786-lszr4 <span class="nt">--</span> curl <span class="nt">-s</span> http://echo-clusterip.packetlab.svc.cluster.local
Request Information:
    <span class="nv">client_address</span><span class="o">=</span>10.0.1.51          &lt;<span class="nt">--</span> real pod IP, no SNAT
</code></pre></div></div>

<p>iptables DNAT chain:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>iptables <span class="nt">-t</span> nat <span class="nt">-L</span> KUBE-SVC-B23I5IPCVQPNADR7 <span class="nt">-n</span>
KUBE-SEP-MNWX5K5OIII64PTW  all  <span class="nt">--</span>  0.0.0.0/0  0.0.0.0/0  /<span class="k">*</span> -&gt; 10.0.2.127:8080 <span class="k">*</span>/ statistic mode random probability 0.50000000000
KUBE-SEP-XAUWYAMA2TYGF5RO  all  <span class="nt">--</span>  0.0.0.0/0  0.0.0.0/0  /<span class="k">*</span> -&gt; 10.0.3.193:8080 <span class="k">*</span>/
</code></pre></div></div>

<p>Conntrack after a request:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>conntrack <span class="nt">-L</span> | <span class="nb">grep </span>172.20.122.189
tcp  6  60  TIME_WAIT
  <span class="nv">src</span><span class="o">=</span>10.0.1.51 <span class="nv">dst</span><span class="o">=</span>172.20.122.189 <span class="nv">sport</span><span class="o">=</span>59432 <span class="nv">dport</span><span class="o">=</span>80
  <span class="nv">src</span><span class="o">=</span>10.0.3.193 <span class="nv">dst</span><span class="o">=</span>10.0.1.51 <span class="nv">sport</span><span class="o">=</span>8080 <span class="nv">dport</span><span class="o">=</span>59432
  <span class="o">[</span>ASSURED] <span class="nv">mark</span><span class="o">=</span>0
</code></pre></div></div>

<p>Original: toolbox -&gt; service VIP. Reply: echoserver -&gt; toolbox. iptables reverse-NATs so the toolbox thinks it talked to the VIP.</p>

<h3 id="experiment-6-nodeport-service">Experiment 6: NodePort service</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kubectl <span class="nt">-n</span> packetlab get svc echo-nodeport <span class="nt">-o</span> wide
NAME            TYPE       CLUSTER-IP      EXTERNAL-IP   PORT<span class="o">(</span>S<span class="o">)</span>        SELECTOR
echo-nodeport   NodePort   172.20.152.84   &lt;none&gt;        80:32385/TCP   <span class="nv">app</span><span class="o">=</span>echoserver
</code></pre></div></div>

<p>iptables: <code class="language-plaintext highlighter-rouge">KUBE-MARK-MASQ</code> fires on all NodePort traffic (not just hairpin like ClusterIP), marking packets for SNAT in <code class="language-plaintext highlighter-rouge">KUBE-POSTROUTING</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>iptables <span class="nt">-t</span> nat <span class="nt">-L</span> KUBE-POSTROUTING <span class="nt">-n</span>
RETURN      all  <span class="nt">--</span>  0.0.0.0/0  0.0.0.0/0  mark match <span class="o">!</span> 0x4000/0x4000
MARK        all  <span class="nt">--</span>  0.0.0.0/0  0.0.0.0/0  MARK xor 0x4000
MASQUERADE  all  <span class="nt">--</span>  0.0.0.0/0  0.0.0.0/0  /<span class="k">*</span> kubernetes service traffic requiring SNAT <span class="k">*</span>/ random-fully
</code></pre></div></div>

<p>From the same node:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>curl <span class="nt">-s</span> http://localhost:32385
    <span class="nv">client_address</span><span class="o">=</span>10.0.0.75          &lt;<span class="nt">--</span> node IP, not us
</code></pre></div></div>

<p>From a different node:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ssh ip-10-0-0-49.ec2.internal <span class="nt">--</span> curl <span class="nt">-s</span> http://10.0.0.75:32385
    <span class="nv">client_address</span><span class="o">=</span>10.0.0.75          &lt;<span class="nt">--</span> still the receiving node
</code></pre></div></div>

<p>Both times echoserver sees the receiving node’s IP. The actual client gets SNATed for flow symmetry.</p>

<h3 id="experiment-7-pod-egress">Experiment 7: Pod egress</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>kubectl <span class="nt">-n</span> packetlab <span class="nb">exec </span>toolbox-a-6fb5b9b786-lszr4 <span class="nt">--</span> curl <span class="nt">-s</span> https://checkip.amazonaws.com
44.218.31.211
</code></pre></div></div>

<p>Neither pod IP nor node IP. Two-stage SNAT: VPC CNI rewrites pod IP to node primary ENI IP, then IGW/NAT GW rewrites to public IP.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>iptables <span class="nt">-t</span> mangle <span class="nt">-L</span> PREROUTING <span class="nt">-n</span>
CONNMARK  all  <span class="nt">--</span>  0.0.0.0/0  0.0.0.0/0  /<span class="k">*</span> AWS, primary ENI <span class="k">*</span>/ ADDRTYPE match dst-type LOCAL limit-in CONNMARK or 0x80
CONNMARK  all  <span class="nt">--</span>  0.0.0.0/0  0.0.0.0/0  /<span class="k">*</span> AWS, primary ENI <span class="k">*</span>/ CONNMARK restore mask 0x80
</code></pre></div></div>

<p>VPC CNI marks with <code class="language-plaintext highlighter-rouge">0x80</code>. Policy rule <code class="language-plaintext highlighter-rouge">fwmark 0x80/0x80 lookup main</code> routes return traffic through the main table.</p>

<h3 id="experiment-8-full-node-interface-view">Experiment 8: Full node interface view</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ip <span class="nb">link </span>show
2: ens5: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 9001          &lt;<span class="nt">--</span> Primary ENI
165: ens6: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 9001        &lt;<span class="nt">--</span> Secondary ENI
264: ens7: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 9001        &lt;<span class="nt">--</span> Third ENI
4: eni36f7c958f87@if3: ...                                    &lt;<span class="nt">--</span> veth <span class="k">for </span>pod
336: eniaee705c1f69@if3: ...                                  &lt;<span class="nt">--</span> OUR TOOLBOX POD
</code></pre></div></div>

<p>Three ENIs (<code class="language-plaintext highlighter-rouge">ensN</code>), a veth pair per pod (<code class="language-plaintext highlighter-rouge">eniXXX@if3</code>). VPC CNI added secondary ENIs as pod count grew.</p>

<h3 id="what-we-verified">What we verified</h3>

<table>
  <thead>
    <tr>
      <th>What</th>
      <th>Experiment</th>
      <th>Result</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>/32 mask on pod eth0</td>
      <td>1</td>
      <td><code class="language-plaintext highlighter-rouge">10.0.1.51/32</code></td>
    </tr>
    <tr>
      <td>Fake gateway 169.254.1.1</td>
      <td>1</td>
      <td>default route via 169.254.1.1</td>
    </tr>
    <tr>
      <td>Static ARP entry</td>
      <td>1</td>
      <td><code class="language-plaintext highlighter-rouge">5a:f3:56:0b:86:d4 PERM</code></td>
    </tr>
    <tr>
      <td>ARP MAC = host-side veth</td>
      <td>2</td>
      <td>both show <code class="language-plaintext highlighter-rouge">5a:f3:56:0b:86:d4</code></td>
    </tr>
    <tr>
      <td>Policy routing (ip rule)</td>
      <td>2</td>
      <td><code class="language-plaintext highlighter-rouge">to pod -&gt; main</code>, <code class="language-plaintext highlighter-rouge">from pod -&gt; table 2</code></td>
    </tr>
    <tr>
      <td>Secondary table only has default gw</td>
      <td>2</td>
      <td><code class="language-plaintext highlighter-rouge">table 2</code>: just <code class="language-plaintext highlighter-rouge">default via ... dev ens6</code></td>
    </tr>
    <tr>
      <td>Same-node: sub-ms</td>
      <td>3</td>
      <td>0.05ms</td>
    </tr>
    <tr>
      <td>Cross-node: ~0.4ms</td>
      <td>3</td>
      <td>0.4-1.7ms via VPC fabric</td>
    </tr>
    <tr>
      <td>DNS through ClusterIP</td>
      <td>4</td>
      <td>172.20.0.10 -&gt; DNAT to CoreDNS pods</td>
    </tr>
    <tr>
      <td>ClusterIP: round-robin</td>
      <td>4</td>
      <td><code class="language-plaintext highlighter-rouge">statistic mode random probability 0.5</code></td>
    </tr>
    <tr>
      <td>ClusterIP: no SNAT</td>
      <td>5</td>
      <td>echoserver sees real pod IP</td>
    </tr>
    <tr>
      <td>Conntrack tracks NAT</td>
      <td>5</td>
      <td>bidirectional mapping visible</td>
    </tr>
    <tr>
      <td>NodePort: SNAT masks client</td>
      <td>6</td>
      <td>echoserver sees node IP</td>
    </tr>
    <tr>
      <td>NodePort: SNAT even on localhost</td>
      <td>6</td>
      <td>localhost curl still shows node IP</td>
    </tr>
    <tr>
      <td>NodePort: cross-node SNAT</td>
      <td>6</td>
      <td>remote curl shows receiving node IP</td>
    </tr>
    <tr>
      <td>Pod egress works</td>
      <td>7</td>
      <td>checkip returns public IP</td>
    </tr>
    <tr>
      <td>VPC CNI mangle marks</td>
      <td>7</td>
      <td>CONNMARK 0x80 for primary ENI</td>
    </tr>
    <tr>
      <td>Multiple ENIs per node</td>
      <td>8</td>
      <td>ens5, ens6, ens7</td>
    </tr>
  </tbody>
</table>

<h3 id="cleanup">Cleanup</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl delete namespace packetlab
</code></pre></div></div>]]></content><author><name>M Samuel Vijaykumar</name></author><category term="aws, eks, network" /><summary type="html"><![CDATA[If you already know Kubernetes architecture, skip to section 3.]]></summary></entry><entry><title type="html">AWS’s Diwali Damaka</title><link href="https://samof76.github.io/aws-diwali-outage.html" rel="alternate" type="text/html" title="AWS’s Diwali Damaka" /><published>2025-10-25T00:00:00+00:00</published><updated>2025-10-25T00:00:00+00:00</updated><id>https://samof76.github.io/aws-diwali-outage</id><content type="html" xml:base="https://samof76.github.io/aws-diwali-outage.html"><![CDATA[<blockquote>
  <p><strong>UPDATE:</strong> Added some lingering questions at the end of this. May be there will be more added, but added those that are most pertinent to this issue.</p>
</blockquote>

<blockquote>
  <p><strong>UPDATE2:</strong> Adding ways in which applications could have failed.</p>
</blockquote>

<p>I suppose this was one of the worst outages in AWS history, and it had significant impact across many internet services, products and platforms. And sadly for many of us Indians it happened on a Diwali day, and we were barely trying to keep the lights on on this festival of lights. The <a href="https://aws.amazon.com/message/101925/">AWS’s public RCA</a> is dense, and in PDT, so these are my notes(accurate to the best of my knowledge), concise and the timeline is in IST.</p>

<h2 id="timeline-converted-to-ist">Timeline (Converted to IST)</h2>

<p><em>(PDT -&gt; IST = +12 hours 30 minutes)</em></p>

<table>
  <thead>
    <tr>
      <th>Time (IST)</th>
      <th>Duration</th>
      <th>Event Summary</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Oct 20, 2025 – 12:18 PM -&gt; 3:10 PM</strong></td>
      <td>~3 hrs</td>
      <td><strong>DynamoDB DNS failure:</strong> API errors in us-east-1 due to DNS race condition.</td>
    </tr>
    <tr>
      <td><strong>Oct 20, 2025 – 2:55 PM -&gt; 5:55 PM</strong></td>
      <td>~3 hrs</td>
      <td><strong>EC2 lease timeout phase:</strong> Droplet leases expired while DynamoDB was unreachable.</td>
    </tr>
    <tr>
      <td><strong>Oct 20, 2025 – 5:55 PM -&gt; 7:30 PM</strong></td>
      <td>~1.5 hrs</td>
      <td><strong>DWFM recovery congestion:</strong> EC2 droplet recovery stalled -&gt; throttling + restart of DWFM hosts.</td>
    </tr>
    <tr>
      <td><strong>Oct 20, 2025 – 6:55 PM -&gt; 9:06 PM</strong></td>
      <td>~2 hrs</td>
      <td><strong>Network Manager backlog:</strong> Network state propagation delayed; new EC2s had no network connectivity.</td>
    </tr>
    <tr>
      <td><strong>Oct 20, 2025 – 7:00 PM -&gt; 9:39 PM</strong></td>
      <td>~2.5 hrs</td>
      <td><strong>NLB health check instability:</strong> Alternating health check failures -&gt; capacity removed by DNS failover.</td>
    </tr>
    <tr>
      <td><strong>Oct 20, 2025 – 11:53 PM -&gt; Oct 21, 2025 – 3:20 AM</strong></td>
      <td>~3.5 hrs</td>
      <td><strong>EC2 full recovery:</strong> DWFM and Network Manager stabilized, throttles lifted, full recovery achieved.</td>
    </tr>
    <tr>
      <td><strong>Oct 20, 2025 – 12:21 PM -&gt; Oct 21, 2025 – 3:50 AM</strong></td>
      <td>~15 hrs</td>
      <td><strong>Cascading impact:</strong> Lambda, ECS, EKS, Fargate, Connect, Redshift, STS, IAM, Support all experienced downstream failures recovering in same order.</td>
    </tr>
  </tbody>
</table>

<h2 id="notes-on-the-subsystems-failures">Notes on the Subsystems Failures</h2>

<h3 id="1-dynamodb-dns-management-system">1. DynamoDB DNS Management System</h3>

<ul>
  <li><strong>DNS Planner:</strong> Periodically generates new DNS plans mapping load balancers to endpoints.</li>
  <li><strong>DNS Enactors (3 per AZ):</strong> Independently apply these plans to Route53 using transactions.</li>
  <li>
    <p><strong>Root Cause:</strong>
A <em>race condition</em> occurred when a slow Enactor applied an old plan after a newer plan was already in place.</p>

    <ul>
      <li>The stale check didn’t prevent the overwrite.</li>
      <li>Cleanup deleted the active plan -&gt; <strong>DNS record went empty</strong> (<code class="language-plaintext highlighter-rouge">dynamodb.us-east-1.amazonaws.com</code>).</li>
      <li>Required <strong>manual repair</strong> of Route53 entries.</li>
    </ul>
  </li>
</ul>

<p><strong>Impact:</strong> DynamoDB endpoint unreachable -&gt; API errors -&gt; ripple failure in all services depending on DynamoDB (EC2, Lambda, STS, IAM, etc.).</p>

<h3 id="2-ec2-control-subsystems">2. EC2 Control Subsystems</h3>

<h4 id="a-droplet-workflow-manager-dwfm">(a) Droplet Workflow Manager (DWFM)</h4>

<ul>
  <li>Manages physical hosts (droplets) for EC2.</li>
  <li>Maintains a <strong>lease</strong> with each droplet to keep instance state synchronized.</li>
  <li>Depends on DynamoDB for state persistence.</li>
  <li><strong>Failure:</strong> When DynamoDB failed, leases expired, and DWFM couldn’t reestablish them -&gt; <strong>EC2 instance launches failed</strong> with “insufficient capacity”.</li>
</ul>

<h4 id="b-network-manager">(b) Network Manager</h4>

<ul>
  <li>Propagates <strong>network configuration</strong> (VPC routes, IP associations, etc.) to instances.</li>
  <li><strong>Failure:</strong> After DWFM recovery, backlog of network updates caused delays -&gt; new instances launched but had no connectivity.</li>
</ul>

<p><strong>Recovery:</strong></p>

<ul>
  <li>DWFM congested -&gt; throttled &amp; restarted.</li>
  <li>Network Manager backlog cleared by ~10:36 AM PDT (9:06 PM IST).</li>
  <li>Full EC2 API recovery at 1:50 PM PDT (3:20 AM IST).</li>
</ul>

<h3 id="3-network-load-balancer-nlb">3. Network Load Balancer (NLB)</h3>

<ul>
  <li>Performs load balancing with <strong>DNS-based health checks</strong> per AZ.</li>
  <li>Health checks failed when new EC2 targets’ network states weren’t yet propagated.</li>
  <li>This caused <strong>flapping</strong> (nodes repeatedly removed/added).</li>
  <li>Degraded the health check subsystem, triggering <strong>automatic AZ DNS failovers</strong> -&gt; capacity loss -&gt; connection errors.</li>
  <li>Engineers <strong>disabled automatic failover at 9:06 PM IST</strong> -&gt; stabilized; re-enabled after EC2 recovery (~2:39 AM IST).</li>
</ul>

<h2 id="cascading-impact-to-other-aws-services">Cascading Impact to Other AWS Services</h2>

<ul>
  <li><strong>Lambda:</strong> Function creation and async invocations failed due to DynamoDB and EC2 dependency; recovered by <strong>11:45 PM IST</strong>.</li>
  <li><strong>ECS/EKS/Fargate:</strong> Container launch failures due to EC2 unavailability; recovered by <strong>3:50 AM IST (Oct 21)</strong>.</li>
  <li><strong>Connect:</strong> Multi-phase impact - initial DynamoDB unreachability, then NLB + Lambda dependency failures; full recovery <strong>1:50 AM IST (Oct 21)</strong>.</li>
  <li><strong>STS/IAM:</strong> Authentication issues due to DynamoDB outage; recovered <strong>1:49 AM IST (Oct 20)</strong>, relapsed briefly during NLB instability.</li>
  <li><strong>Redshift:</strong> Dependent on DynamoDB + EC2 for query and host replacement; partial recovery <strong>2:51 AM IST</strong>, full by <strong>4:35 PM IST (Oct 21)</strong>.</li>
  <li><strong>AWS Support Console:</strong> Failed over but blocked by invalid metadata responses -&gt; recovered <strong>3:10 PM IST</strong>.</li>
</ul>

<h2 id="root-cause-in-one-line">Root Cause in One Line</h2>

<p>A <strong>race condition in DynamoDB’s DNS automation</strong> deleted the regional endpoint record, triggering a <strong>chain reaction</strong> of failures across dependent control planes - primarily <strong>EC2 (DWFM + Network Manager)</strong> and <strong>NLB</strong> - leading to multi-service disruption in us-east-1.</p>

<h2 id="key-fixes-announced">Key Fixes Announced</h2>

<ul>
  <li><strong>DynamoDB:</strong> Disable DNS Planner/Enactor automation; fix race condition and add safeguards.</li>
  <li><strong>EC2:</strong> Add DWFM recovery tests, rate-limit queues dynamically, improve throttling.</li>
  <li><strong>NLB:</strong> Add velocity controls to cap AZ failover capacity removal.</li>
  <li><strong>Cross-Service:</strong> Broader resilience testing and dependency isolation review.</li>
</ul>

<h2 id="timeline">Timeline</h2>

<pre><code class="language-mermaid">gantt
    title AWS us-east-1 Outage Timeline (Converted to IST)
    dateFormat HH:mm
    axisFormat %H:%M
    section DynamoDB
    DNS race condition &amp; API failures        :active, 12:18, 03:10
    Manual DNS restoration completed         :done, 15:10, 15:40
    section EC2
    DWFM leases expire (due to DDB failure)  :active, 14:55, 17:55
    DWFM congestive collapse &amp; throttling    :active, 17:55, 19:30
    Network Manager backlog (no connectivity):active, 18:55, 21:06
    EC2 throttles lifted &amp; full recovery     :done, 23:53, 03:20
    section NLB
    Health check flapping begins             :active, 19:00, 21:09
    AZ DNS failover disabled (stabilized)    :done, 21:09, 21:39
    section Cascading Services
    Lambda errors &amp; throttling               :active, 12:21, 23:45
    ECS / EKS / Fargate failures             :active, 12:45, 03:50
    Connect multi-phase degradation          :active, 12:26, 01:50
    STS / IAM sign-in failures               :active, 12:21, 01:49
    Redshift partial recovery                :active, 12:17, 16:35
    Support Console blocked (metadata bug)   :done, 12:18, 15:10
</code></pre>

<h3 id="reading-guide">Reading Guide</h3>

<ul>
  <li><strong>Left column:</strong> Subsystem</li>
  <li><strong>Bars:</strong> Active impact periods (converted to IST)</li>
  <li><strong>Overlap:</strong> Shows cascading dependencies (e.g., EC2 and NLB lagged DynamoDB recovery)</li>
</ul>

<h2 id="appendix">Appendix</h2>

<h3 id="about-dwfm-leases">About DWFM Leases</h3>

<h4 id="what-the-lease-is">What the Lease Is</h4>

<ul>
  <li>Each <strong>DWFM host</strong> manages a subset of physical machines (“droplets”) that run EC2 instances.</li>
  <li>To <strong>ensure authoritative control</strong>, DWFM periodically <em>renews a lease</em> for each droplet.</li>
  <li>
    <p>This lease confirms:</p>

    <ol>
      <li>The DWFM is <em>actively managing</em> that droplet.</li>
      <li>The droplet’s <strong>state (running, stopped, rebooting, etc.)</strong> is in sync with EC2’s control plane.</li>
      <li>No other DWFM host should issue commands to that droplet during the lease window.</li>
    </ol>
  </li>
</ul>

<h4 id="why-it-matters">Why It Matters</h4>

<p>Leases prevent conflicting actions like:</p>

<ul>
  <li>Two DWFM managers trying to reboot the same host simultaneously.</li>
  <li>A DWFM acting on stale state (e.g., thinking a droplet is idle when it’s already reassigned).</li>
  <li>Control drift between the <strong>EC2 API</strong> and the <strong>physical host reality</strong>.</li>
</ul>

<h4 id="what-happened-during-the-outage">What Happened During the Outage</h4>

<ul>
  <li>DWFM stores its lease state in <strong>DynamoDB</strong>.</li>
  <li>When DynamoDB went unreachable, <strong>leases expired</strong> (DWFM couldn’t renew them).</li>
  <li>Once expired, the droplets were <strong>marked unmanaged</strong>, so EC2 couldn’t schedule new instances on them.</li>
  <li>This caused EC2 API calls to fail with <strong>“insufficient capacity”</strong> even though hardware was available.</li>
</ul>

<p>When DynamoDB came back, DWFM tried to <strong>re-establish all leases at once</strong>, overloaded itself (a <em>congestive collapse</em>), and required throttling/restarts to recover.</p>

<h4 id="analogy">Analogy</h4>

<p>Think of each lease like a <strong>“control token”</strong> renewed every few minutes:</p>

<ul>
  <li>If DWFM holds it -&gt; droplet is manageable.</li>
  <li>If lease expires -&gt; droplet goes offline (from EC2’s point of view) until the lease is reestablished.</li>
</ul>

<h4 id="summary">Summary</h4>

<ul>
  <li>Leases ensure authoritative control over droplets.</li>
  <li>DynamoDB outage caused leases to expire, leading to EC2 API failures.</li>
  <li>DWFM overload during lease re-establishment required throttling/restarts.</li>
</ul>

<h3 id="updates-route53-using-transaction">Updates Route53 using transaction</h3>

<h4 id="what-transactions-mean-in-route-53-context">What “transactions” mean in Route 53 context</h4>

<p>When the DNS Enactor updates endpoint records (e.g., <code class="language-plaintext highlighter-rouge">dynamodb.us-east-1.amazonaws.com</code>), it doesn’t just push one record at a time.
Instead, it performs the update as a <strong>transaction</strong> - an <strong>atomic “change batch”</strong> operation.</p>

<p>That means:</p>

<ul>
  <li>Multiple record changes (additions, deletions, or replacements) are grouped together.</li>
  <li>Either <strong>all changes succeed</strong>, or <strong>none of them are applied</strong>.</li>
  <li>This guarantees <strong>consistency</strong> of DNS state even when several Enactors are updating in parallel.</li>
</ul>

<p>So “Route 53 using transactions” =</p>

<blockquote>
  <p><em>Apply DNS updates atomically via a change batch so no half-updated DNS zone ever exists.</em></p>
</blockquote>

<h4 id="why-this-matters-for-dynamodb">Why this matters for DynamoDB</h4>

<p>DynamoDB maintains hundreds of thousands of DNS records for its internal load balancers.
To keep these consistent:</p>

<ol>
  <li>The <strong>Planner</strong> generates a new DNS plan (mapping LB -&gt; weights).</li>
  <li>Each <strong>Enactor</strong> picks up the plan and applies it to Route 53 <strong>as a single transaction</strong>.</li>
  <li>If another Enactor tries to update at the same time, Route 53 ensures only one consistent version wins.</li>
</ol>

<h4 id="what-went-wrong">What went wrong</h4>

<p>During the outage:</p>

<ul>
  <li>One Enactor was delayed and tried to commit an <strong>old plan transaction</strong> after a newer one had already succeeded.</li>
  <li>Because its freshness check was stale, Route 53 accepted the transaction -&gt; overwrote valid records.</li>
  <li>Then cleanup deleted that plan, leaving an <strong>empty record set</strong> - meaning the DNS name literally had <strong>no A/AAAA records</strong>, breaking all endpoint resolution.</li>
</ul>

<h3 id="how-did-plan-cleanup-nullify-a-records">How did plan cleanup nullify A records?</h3>

<h4 id="what-a-dns-plan-actually-is">What a “DNS plan” actually is</h4>

<p>A <strong>plan</strong> in DynamoDB’s DNS automation is not just metadata - it’s the <strong>authoritative configuration</strong> that defines:</p>

<ul>
  <li>Which load balancers (targets) should serve the endpoint</li>
  <li>The DNS record set (A/AAAA) for each endpoint</li>
  <li>The corresponding weights and health states</li>
</ul>

<p>Think of it like a JSON object that says:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"endpoint"</span><span class="p">:</span><span class="w"> </span><span class="s2">"dynamodb.us-east-1.amazonaws.com"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"records"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="nl">"ip"</span><span class="p">:</span><span class="w"> </span><span class="s2">"54.221.10.1"</span><span class="p">,</span><span class="w"> </span><span class="nl">"weight"</span><span class="p">:</span><span class="w"> </span><span class="mi">50</span><span class="p">},</span><span class="w">
    </span><span class="p">{</span><span class="nl">"ip"</span><span class="p">:</span><span class="w"> </span><span class="s2">"54.221.10.2"</span><span class="p">,</span><span class="w"> </span><span class="nl">"weight"</span><span class="p">:</span><span class="w"> </span><span class="mi">50</span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Each DNS Enactor reads this plan and applies it to Route 53 using a transactional update.</p>

<h4 id="what-the-cleanup-process-does">What the cleanup process does</h4>

<p>When a new plan is successfully applied, the Enactor:</p>

<ol>
  <li>Marks older plans as obsolete.</li>
  <li>Periodically deletes plans older than a threshold (for housekeeping).</li>
</ol>

<p>Deletion means <strong>removing the underlying data source</strong> that Route 53 uses to maintain that DNS mapping.</p>

<h4 id="what-happened-here">What happened here</h4>

<p>During the race condition:</p>

<ol>
  <li><strong>Enactor A (slow)</strong> - still holding an <em>old plan</em> - applied it after a much newer plan was already in place.</li>
  <li><strong>Enactor B (fast)</strong> - finished applying the newest plan and then triggered the <strong>cleanup</strong> process, which deletes all <em>older plans</em> (including the one just incorrectly re-applied by A).</li>
  <li>When cleanup deleted that plan, Route 53 no longer had any valid data for that endpoint.</li>
</ol>

<p>Because Route 53 was told to <strong>remove the resource record set defined by that plan</strong>, it executed a “delete” change batch like:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"Action"</span><span class="p">:</span><span class="w"> </span><span class="s2">"DELETE"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"ResourceRecordSet"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"Name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"dynamodb.us-east-1.amazonaws.com"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"Type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"A"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Result:
<strong>All A/AAAA records were wiped.</strong>
DNS lookups for <code class="language-plaintext highlighter-rouge">dynamodb.us-east-1.amazonaws.com</code> returned <strong>NXDOMAIN</strong> (no such domain).</p>

<h4 id="in-simpler-terms">In simpler terms</h4>

<ul>
  <li>Each “plan” is the <em>source of truth</em> for DNS state.</li>
  <li>Deleting the plan = telling Route 53 that “this endpoint should no longer exist.”</li>
  <li>So, when cleanup removed that plan, Route 53’s zone lost all IPs for that name - effectively nullifying the endpoint.</li>
</ul>

<h4 id="why-this-required-manual-recovery">Why this required manual recovery</h4>

<p>Once the plan database was inconsistent:</p>

<ul>
  <li>No Enactor could proceed (they refused to overwrite a “missing” plan).</li>
  <li>Engineers had to manually restore a valid DNS record set into Route 53 to bring <code class="language-plaintext highlighter-rouge">dynamodb.us-east-1.amazonaws.com</code> back online.</li>
</ul>

<blockquote>
  <p><strong>DISCLAIMER:</strong> I am somehow not really satisfied with the above explanation myself, but I will try to update with a better one.</p>
</blockquote>

<h3 id="ruling-out-cyclic-dependency">Ruling out cyclic dependency</h3>

<p>Here’s how the dependency boundaries work:</p>

<table>
  <thead>
    <tr>
      <th>Layer</th>
      <th>Data / State Backend</th>
      <th>Depends on DynamoDB?</th>
      <th>Notes</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>DNS Planner / Enactor system</strong></td>
      <td>Its own internal control store (likely replicated metadata DB or internal service)</td>
      <td>No</td>
      <td>Designed to stay operational even if any regional database service—including DynamoDB—is impaired.</td>
    </tr>
    <tr>
      <td><strong>Route 53</strong></td>
      <td>Route 53’s own authoritative DNS infrastructure</td>
      <td>No</td>
      <td>Route 53 is independent; DNS updates go through Route 53 APIs, not through DynamoDB.</td>
    </tr>
    <tr>
      <td><strong>DynamoDB service control plane</strong></td>
      <td>The DNS Planner/Enactor automation we’re discussing</td>
      <td>Yes (indirectly)</td>
      <td>DynamoDB’s <em>own</em> DNS endpoints depend on that automation to advertise the service, but the automation itself doesn’t use DynamoDB.</td>
    </tr>
  </tbody>
</table>

<h4 id="so-theres-no-circular-loop-like">So there’s no circular loop like:</h4>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>DynamoDB -&gt; DNS system -&gt; DynamoDB
</code></pre></div></div>

<p>If that were the case, the service could never bootstrap DNS during a recovery.
AWS explicitly stated that the <strong>DNS Enactor “is designed to have minimal dependencies to allow for system recovery in any scenario.”</strong>
That line is their assurance that the Enactor can still run, update Route 53, and heal DNS even if DynamoDB itself is degraded.</p>

<h4 id="the-boundary-instead-looks-like">The boundary instead looks like</h4>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[DynamoDB DNS automation system]
        │
        └── writes -&gt; Route 53  (to publish dynamodb.us-east-1.amazonaws.com)

DynamoDB’s own API servers ←── depend on DNS working
</code></pre></div></div>

<p>So it’s a <strong>one-way dependency</strong>:
DynamoDB depends on the DNS system, but the DNS system doesn’t depend on DynamoDB.</p>

<p>That one-way design is what prevented a true circular dependency and allowed AWS engineers to restore the Route 53 records manually when automation got stuck.</p>

<hr />

<h2 id="unanswered-questions">Unanswered questions!</h2>

<h3 id="what-caused-the-initial-slowdown-of-one-dns-enactor">What caused the initial slowdown of one DNS Enactor?</h3>
<p>Was it CPU contention, Route 53 API throttling, network saturation, or a dependency stall?
Understanding <em>why</em> the first Enactor lagged is crucial — without that trigger, the race condition might never have materialized.</p>

<h3 id="why-did-the-enactors-freshness-check-not-re-validate-before-committing">Why did the Enactor’s “freshness check” not re-validate before committing?</h3>
<p>The Enactor verified plan freshness only once at start.
Shouldn’t there be a re-validation step right before applying, especially if processing is delayed?</p>

<h3 id="why-wasnt-there-a-safeguard-preventing-deletion-of-the-currently-active-plan">Why wasn’t there a safeguard preventing deletion of the <em>currently active</em> plan?</h3>
<p>Cleanup logic deleted all “old” plans, including the one still live.
Should the system have tracked which plan is currently serving traffic before deletion?</p>

<h3 id="why-did-dependent-control-planes-ec2-dwfm-network-manager-nlb-health-checks-lack-graceful-degradation-when-dynamodb-failed">Why did dependent control planes (EC2 DWFM, Network Manager, NLB health checks) lack graceful degradation when DynamoDB failed?</h3>
<p>Each subsystem cascaded failure instead of isolating or caching state.
Could stronger local caching or fallback mechanisms have prevented the multi-hour recovery chain?</p>

<h2 id="how-could-have-applications-failed">How could have applications failed?</h2>

<p>Though this list is not exhaustive, it highlights several potential failure points, and keep in mind that there might have been other subtle, cascading, or hidden dependencies</p>

<h4 id="internal-aws-dependencies-you-dont-see">Internal AWS dependencies you don’t see</h4>

<ul>
  <li><strong>Your AWS SDK / CLI / Terraform / CloudFormation</strong> calls fail because <em>they</em> rely on STS, IAM, or Route 53 DNS resolution — not just your own APIs.</li>
  <li><strong>KMS-encrypted parameters/secrets</strong> (SSM Parameter Store, Secrets Manager, EBS, RDS, Lambda env vars) can’t decrypt if KMS or STS throttles.</li>
  <li><strong>Cross-service dependency loops</strong> inside AWS (e.g., EC2 control plane -&gt; DynamoDB -&gt; DNS) ripple outward even if your app never touches DynamoDB directly.</li>
</ul>

<h4 id="region-specific-metadata-and-bootstrap">Region-specific metadata and bootstrap</h4>

<ul>
  <li>EC2 <strong>IMDSv2</strong> (instance metadata) transient failures break apps that fetch credentials or configuration at startup.</li>
  <li><strong>User-data scripts or agents</strong> stuck on first boot (can’t reach S3/Yum repos for bootstrap).</li>
  <li><strong>Auto Scaling Groups</strong> endlessly retry instance launches due to control-plane API limits -&gt; cascading retries -&gt; cost spikes.</li>
</ul>

<h4 id="cached-credentials-and-token-expiry">Cached credentials and token expiry</h4>

<ul>
  <li><strong>STS tokens</strong> cached in app containers expire mid-outage -&gt; background workers suddenly lose access to S3, KMS, or DynamoDB.</li>
  <li><strong>ECS task roles</strong> or <strong>IRSA</strong> (IAM Roles for Service Accounts) fail token refresh when IAM / STS is degraded.</li>
</ul>

<h4 id="dns--caching-oddities">DNS &amp; caching oddities</h4>

<ul>
  <li><strong>Resolvers caching NXDOMAINs</strong> from transient Route 53 responses -&gt; self-sustaining blackholes until TTLs expire.</li>
  <li><strong>Split-horizon DNS</strong> (internal vs. public zones) becomes inconsistent when one zone updates and the other lags.</li>
  <li><strong>Long TTLs on service endpoints</strong> keep pointing clients to unhealthy IPs long after AWS recovers.</li>
</ul>

<h4 id="cross-region-replication-drift">Cross-region replication drift</h4>

<ul>
  <li><strong>S3 CRR / DynamoDB Global Tables / RDS read replicas</strong> silently accumulate backlog -&gt; hours/days of delayed sync.</li>
  <li><strong>CloudWatch cross-region metrics</strong> or alarms show stale data -&gt; false negatives in monitoring.</li>
</ul>

<h4 id="control-plane-and-data-plane-mismatch">Control-plane and data-plane mismatch</h4>

<ul>
  <li>Data-plane still fine (e.g., existing EC2s run), but control-plane APIs (EC2 Describe, Stop, Attach, EBS) hang — blocking scale-out, failover, or CI/CD.</li>
  <li><strong>EKS nodes</strong> remain healthy but new pods can’t attach ENIs or mount EBS -&gt; perceived “partial” outage.</li>
</ul>

<h4 id="authentication-and-third-party-linkages">Authentication and third-party linkages</h4>

<ul>
  <li><strong>OIDC / SAML / Cognito / Identity Center</strong> failures break your SaaS admin login portal.</li>
  <li><strong>External integrations</strong> (e.g., Slack, Salesforce, Stripe) that fetch data from your AWS endpoints time out -&gt; customer impact despite your core infra surviving.</li>
</ul>

<h4 id="observability-blind-spots">Observability blind spots</h4>

<ul>
  <li><strong>CloudWatch metrics delayed</strong>, so autoscalers or anomaly detectors react too late.</li>
  <li><strong>Centralized logging in OpenSearch/S3</strong> pauses ingestion -&gt; on-call engineers lack visibility during the outage itself.</li>
</ul>

<h4 id="internal-service-throttling-feedback">Internal service throttling feedback</h4>

<ul>
  <li>One microservice retries aggressively -&gt; SQS/Kinesis backlog grows -&gt; Lambda concurrency exhaustion -&gt; regional throttling cascade.</li>
</ul>

<h4 id="recovery-edge-cases">Recovery edge cases</h4>

<ul>
  <li>After AWS restores APIs, your system comes back <em>out of order</em>:
    <ul>
      <li>orphaned EC2 instances, zombie pods, duplicate Kafka consumers, stale DNS caches.</li>
      <li>background replayers (e.g., queue drainers) overshoot and create <strong>write storms</strong>.</li>
    </ul>
  </li>
</ul>

<h3 id="in-short">In short:</h3>
<p>Even if your app’s direct dependencies look simple (EKS + RDS + S3 + ElastiCache), hidden edges like <strong>credential refresh, DNS caching, cross-region replication, and control-plane lag</strong> can still create nonlinear failures.</p>]]></content><author><name>M Samuel Vijaykumar</name></author><category term="aws, outage, rca" /><summary type="html"><![CDATA[UPDATE: Added some lingering questions at the end of this. May be there will be more added, but added those that are most pertinent to this issue.]]></summary></entry><entry><title type="html">Understanding the Kubectl’s `drain` command</title><link href="https://samof76.github.io/kubectl-drain.html" rel="alternate" type="text/html" title="Understanding the Kubectl’s `drain` command" /><published>2025-08-18T00:00:00+00:00</published><updated>2025-08-18T00:00:00+00:00</updated><id>https://samof76.github.io/kubectl-drain</id><content type="html" xml:base="https://samof76.github.io/kubectl-drain.html"><![CDATA[<p>Command’s help</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl drain <span class="nt">--help</span>
Drain node <span class="k">in </span>preparation <span class="k">for </span>maintenance.

 The given node will be marked unschedulable to prevent new pods from arriving. <span class="s1">'drain'</span> evicts the pods <span class="k">if </span>the API
server supports https://kubernetes.io/docs/concepts/workloads/pods/disruptions/ <span class="nb">.</span> Otherwise, it will use normal DELETE
to delete the pods. The <span class="s1">'drain'</span> evicts or deletes all pods except mirror pods <span class="o">(</span>which cannot be deleted through the API
server<span class="o">)</span><span class="nb">.</span>  If there are daemon set-managed pods, drain will not proceed without <span class="nt">--ignore-daemonsets</span>, and regardless it
will not delete any daemon set-managed pods, because those pods would be immediately replaced by the daemon <span class="nb">set
</span>controller, which ignores unschedulable markings.  If there are any pods that are neither mirror pods nor managed by a
replication controller, replica <span class="nb">set</span>, daemon <span class="nb">set</span>, stateful <span class="nb">set</span>, or job, <span class="k">then </span>drain will not delete any pods unless you
use <span class="nt">--force</span><span class="nb">.</span>  <span class="nt">--force</span> will also allow deletion to proceed <span class="k">if </span>the managing resource of one or more pods is missing.

 <span class="s1">'drain'</span> waits <span class="k">for </span>graceful termination. You should not operate on the machine <span class="k">until </span>the <span class="nb">command </span>completes.

 When you are ready to put the node back into service, use kubectl uncordon, which will make the node schedulable again.

 https://kubernetes.io/images/docs/kubectl_drain.svg

Examples:
  <span class="c"># Drain node "foo", even if there are pods not managed by a replication controller, replica set, job, daemon set or</span>
stateful <span class="nb">set </span>on it
  kubectl drain foo <span class="nt">--force</span>

  <span class="c"># As above, but abort if there are pods not managed by a replication controller, replica set, job, daemon set or</span>
stateful <span class="nb">set</span>, and use a grace period of 15 minutes
  kubectl drain foo <span class="nt">--grace-period</span><span class="o">=</span>900

Options:
      <span class="nt">--chunk-size</span><span class="o">=</span>500: Return large lists <span class="k">in </span>chunks rather than all at once. Pass 0 to disable. This flag is beta and
may change <span class="k">in </span>the future.
      <span class="nt">--delete-emptydir-data</span><span class="o">=</span><span class="nb">false</span>: Continue even <span class="k">if </span>there are pods using emptyDir <span class="o">(</span><span class="nb">local </span>data that will be deleted when
the node is drained<span class="o">)</span><span class="nb">.</span>
      <span class="nt">--disable-eviction</span><span class="o">=</span><span class="nb">false</span>: Force drain to use delete, even <span class="k">if </span>eviction is supported. This will bypass checking
PodDisruptionBudgets, use with caution.
      <span class="nt">--dry-run</span><span class="o">=</span><span class="s1">'none'</span>: Must be <span class="s2">"none"</span>, <span class="s2">"server"</span>, or <span class="s2">"client"</span><span class="nb">.</span> If client strategy, only print the object that would be
sent, without sending it. If server strategy, submit server-side request without persisting the resource.
      <span class="nt">--force</span><span class="o">=</span><span class="nb">false</span>: Continue even <span class="k">if </span>there are pods not managed by a ReplicationController, ReplicaSet, Job, DaemonSet
or StatefulSet.
      <span class="nt">--grace-period</span><span class="o">=</span><span class="nt">-1</span>: Period of <span class="nb">time </span><span class="k">in </span>seconds given to each pod to terminate gracefully. If negative, the default
value specified <span class="k">in </span>the pod will be used.
      <span class="nt">--ignore-daemonsets</span><span class="o">=</span><span class="nb">false</span>: Ignore DaemonSet-managed pods.
      <span class="nt">--ignore-errors</span><span class="o">=</span><span class="nb">false</span>: Ignore errors occurred between drain nodes <span class="k">in </span>group.
      <span class="nt">--pod-selector</span><span class="o">=</span><span class="s1">''</span>: Label selector to filter pods on the node
  <span class="nt">-l</span>, <span class="nt">--selector</span><span class="o">=</span><span class="s1">''</span>: Selector <span class="o">(</span>label query<span class="o">)</span> to filter on
      <span class="nt">--skip-wait-for-delete-timeout</span><span class="o">=</span>0: If pod DeletionTimestamp older than N seconds, skip waiting <span class="k">for </span>the pod.
Seconds must be greater than 0 to skip.
      <span class="nt">--timeout</span><span class="o">=</span>0s: The length of <span class="nb">time </span>to <span class="nb">wait </span>before giving up, zero means infinite

Usage:
  kubectl drain NODE <span class="o">[</span>options]

Use <span class="s2">"kubectl options"</span> <span class="k">for </span>a list of global command-line options <span class="o">(</span>applies to all commands<span class="o">)</span><span class="nb">.</span>
</code></pre></div></div>
<h2 id="sequence-diagram">Sequence Diagram</h2>

<p><img src="https://kubernetes.io/images/docs/kubectl_drain.svg" alt="Sequence Diagram" /></p>

<h2 id="code">Code</h2>

<p>The kubectl commads are implemented <a href="https://sourcegraph.com/github.com/kubernetes/kubernetes@810e9e212ec5372d16b655f57b9231d8654a2179/-/tree/staging/src/k8s.io/kubectl">here</a>. And the code for drain begins <a href="https://sourcegraph.com/github.com/kubernetes/kubernetes@810e9e212ec5372d16b655f57b9231d8654a2179/-/tree/staging/src/k8s.io/kubectl/pkg/cmd/drain">here</a> in <code class="language-plaintext highlighter-rouge">drain.go</code> with the command definition.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">NewCmdDrain</span><span class="p">(</span><span class="n">f</span> <span class="n">cmdutil</span><span class="o">.</span><span class="n">Factory</span><span class="p">,</span> <span class="n">ioStreams</span> <span class="n">genericiooptions</span><span class="o">.</span><span class="n">IOStreams</span><span class="p">)</span> <span class="o">*</span><span class="n">cobra</span><span class="o">.</span><span class="n">Command</span> <span class="p">{</span>
	<span class="n">o</span> <span class="o">:=</span> <span class="n">NewDrainCmdOptions</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">ioStreams</span><span class="p">)</span>

	<span class="n">cmd</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="n">cobra</span><span class="o">.</span><span class="n">Command</span><span class="p">{</span>
		<span class="n">Use</span><span class="o">:</span>                   <span class="s">"drain NODE"</span><span class="p">,</span>
		<span class="n">DisableFlagsInUseLine</span><span class="o">:</span> <span class="no">true</span><span class="p">,</span>
		<span class="n">Short</span><span class="o">:</span>                 <span class="n">i18n</span><span class="o">.</span><span class="n">T</span><span class="p">(</span><span class="s">"Drain node in preparation for maintenance"</span><span class="p">),</span>
		<span class="n">Long</span><span class="o">:</span>                  <span class="n">drainLong</span><span class="p">,</span>
		<span class="n">Example</span><span class="o">:</span>               <span class="n">drainExample</span><span class="p">,</span>
		<span class="n">ValidArgsFunction</span><span class="o">:</span>     <span class="n">completion</span><span class="o">.</span><span class="n">ResourceNameCompletionFunc</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"node"</span><span class="p">),</span>
		<span class="n">Run</span><span class="o">:</span> <span class="k">func</span><span class="p">(</span><span class="n">cmd</span> <span class="o">*</span><span class="n">cobra</span><span class="o">.</span><span class="n">Command</span><span class="p">,</span> <span class="n">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">cmdutil</span><span class="o">.</span><span class="n">CheckErr</span><span class="p">(</span><span class="n">o</span><span class="o">.</span><span class="n">Complete</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">cmd</span><span class="p">,</span> <span class="n">args</span><span class="p">))</span>
			<span class="n">cmdutil</span><span class="o">.</span><span class="n">CheckErr</span><span class="p">(</span><span class="n">o</span><span class="o">.</span><span class="n">RunDrain</span><span class="p">())</span>
		<span class="p">},</span>
	<span class="p">}</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">Flags</span><span class="p">()</span><span class="o">.</span><span class="n">BoolVar</span><span class="p">(</span><span class="o">&amp;</span><span class="n">o</span><span class="o">.</span><span class="n">drainer</span><span class="o">.</span><span class="n">Force</span><span class="p">,</span> <span class="s">"force"</span><span class="p">,</span> <span class="n">o</span><span class="o">.</span><span class="n">drainer</span><span class="o">.</span><span class="n">Force</span><span class="p">,</span> <span class="s">"Continue even if there are pods that do not declare a controller."</span><span class="p">)</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">Flags</span><span class="p">()</span><span class="o">.</span><span class="n">BoolVar</span><span class="p">(</span><span class="o">&amp;</span><span class="n">o</span><span class="o">.</span><span class="n">drainer</span><span class="o">.</span><span class="n">IgnoreAllDaemonSets</span><span class="p">,</span> <span class="s">"ignore-daemonsets"</span><span class="p">,</span> <span class="n">o</span><span class="o">.</span><span class="n">drainer</span><span class="o">.</span><span class="n">IgnoreAllDaemonSets</span><span class="p">,</span> <span class="s">"Ignore DaemonSet-managed pods."</span><span class="p">)</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">Flags</span><span class="p">()</span><span class="o">.</span><span class="n">BoolVar</span><span class="p">(</span><span class="o">&amp;</span><span class="n">o</span><span class="o">.</span><span class="n">drainer</span><span class="o">.</span><span class="n">DeleteEmptyDirData</span><span class="p">,</span> <span class="s">"delete-emptydir-data"</span><span class="p">,</span> <span class="n">o</span><span class="o">.</span><span class="n">drainer</span><span class="o">.</span><span class="n">DeleteEmptyDirData</span><span class="p">,</span> <span class="s">"Continue even if there are pods using emptyDir (local data that will be deleted when the node is drained)."</span><span class="p">)</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">Flags</span><span class="p">()</span><span class="o">.</span><span class="n">IntVar</span><span class="p">(</span><span class="o">&amp;</span><span class="n">o</span><span class="o">.</span><span class="n">drainer</span><span class="o">.</span><span class="n">GracePeriodSeconds</span><span class="p">,</span> <span class="s">"grace-period"</span><span class="p">,</span> <span class="n">o</span><span class="o">.</span><span class="n">drainer</span><span class="o">.</span><span class="n">GracePeriodSeconds</span><span class="p">,</span> <span class="s">"Period of time in seconds given to each pod to terminate gracefully. If negative, the default value specified in the pod will be used."</span><span class="p">)</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">Flags</span><span class="p">()</span><span class="o">.</span><span class="n">DurationVar</span><span class="p">(</span><span class="o">&amp;</span><span class="n">o</span><span class="o">.</span><span class="n">drainer</span><span class="o">.</span><span class="n">Timeout</span><span class="p">,</span> <span class="s">"timeout"</span><span class="p">,</span> <span class="n">o</span><span class="o">.</span><span class="n">drainer</span><span class="o">.</span><span class="n">Timeout</span><span class="p">,</span> <span class="s">"The length of time to wait before giving up, zero means infinite"</span><span class="p">)</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">Flags</span><span class="p">()</span><span class="o">.</span><span class="n">StringVarP</span><span class="p">(</span><span class="o">&amp;</span><span class="n">o</span><span class="o">.</span><span class="n">drainer</span><span class="o">.</span><span class="n">PodSelector</span><span class="p">,</span> <span class="s">"pod-selector"</span><span class="p">,</span> <span class="s">""</span><span class="p">,</span> <span class="n">o</span><span class="o">.</span><span class="n">drainer</span><span class="o">.</span><span class="n">PodSelector</span><span class="p">,</span> <span class="s">"Label selector to filter pods on the node"</span><span class="p">)</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">Flags</span><span class="p">()</span><span class="o">.</span><span class="n">BoolVar</span><span class="p">(</span><span class="o">&amp;</span><span class="n">o</span><span class="o">.</span><span class="n">drainer</span><span class="o">.</span><span class="n">DisableEviction</span><span class="p">,</span> <span class="s">"disable-eviction"</span><span class="p">,</span> <span class="n">o</span><span class="o">.</span><span class="n">drainer</span><span class="o">.</span><span class="n">DisableEviction</span><span class="p">,</span> <span class="s">"Force drain to use delete, even if eviction is supported. This will bypass checking PodDisruptionBudgets, use with caution."</span><span class="p">)</span>
	<span class="n">cmd</span><span class="o">.</span><span class="n">Flags</span><span class="p">()</span><span class="o">.</span><span class="n">IntVar</span><span class="p">(</span><span class="o">&amp;</span><span class="n">o</span><span class="o">.</span><span class="n">drainer</span><span class="o">.</span><span class="n">SkipWaitForDeleteTimeoutSeconds</span><span class="p">,</span> <span class="s">"skip-wait-for-delete-timeout"</span><span class="p">,</span> <span class="n">o</span><span class="o">.</span><span class="n">drainer</span><span class="o">.</span><span class="n">SkipWaitForDeleteTimeoutSeconds</span><span class="p">,</span> <span class="s">"If pod DeletionTimestamp older than N seconds, skip waiting for the pod.  Seconds must be greater than 0 to skip."</span><span class="p">)</span>

	<span class="n">cmdutil</span><span class="o">.</span><span class="n">AddChunkSizeFlag</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">o</span><span class="o">.</span><span class="n">drainer</span><span class="o">.</span><span class="n">ChunkSize</span><span class="p">)</span>
	<span class="n">cmdutil</span><span class="o">.</span><span class="n">AddDryRunFlag</span><span class="p">(</span><span class="n">cmd</span><span class="p">)</span>
	<span class="n">cmdutil</span><span class="o">.</span><span class="n">AddLabelSelectorFlagVar</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">o</span><span class="o">.</span><span class="n">drainer</span><span class="o">.</span><span class="n">Selector</span><span class="p">)</span>
	<span class="k">return</span> <span class="n">cmd</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The command calls <code class="language-plaintext highlighter-rouge">RunDrain</code> function as you can see in this snippet.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span>
		<span class="n">Run</span><span class="o">:</span> <span class="k">func</span><span class="p">(</span><span class="n">cmd</span> <span class="o">*</span><span class="n">cobra</span><span class="o">.</span><span class="n">Command</span><span class="p">,</span> <span class="n">args</span> <span class="p">[]</span><span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">cmdutil</span><span class="o">.</span><span class="n">CheckErr</span><span class="p">(</span><span class="n">o</span><span class="o">.</span><span class="n">Complete</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">cmd</span><span class="p">,</span> <span class="n">args</span><span class="p">))</span>
			<span class="n">cmdutil</span><span class="o">.</span><span class="n">CheckErr</span><span class="p">(</span><span class="n">o</span><span class="o">.</span><span class="n">RunDrain</span><span class="p">())</span>
		<span class="p">},</span>
<span class="o">...</span>
</code></pre></div></div>

<blockquote>
  <p><code class="language-plaintext highlighter-rouge">Complete</code> populates some fields from the factory, grabs command line arguments and looks up the node using Builder</p>
</blockquote>

<p>Let’s just focus on the happy path, and <a href="https://sourcegraph.com/github.com/kubernetes/kubernetes@810e9e212ec5372d16b655f57b9231d8654a2179/-/blob/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain.go?L324:27-324:35">follow <code class="language-plaintext highlighter-rouge">RunDrain</code></a>, and since its a method of <code class="language-plaintext highlighter-rouge">DrainCmdOptions</code> all its properties are readily available. Now let go with the flow.</p>

<ol>
  <li>First calls <code class="language-plaintext highlighter-rouge">RunCordonOrUncordon(true)</code> this ensures that <a href="https://sourcegraph.com/github.com/kubernetes/kubernetes@810e9e212ec5372d16b655f57b9231d8654a2179/-/blob/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain.go?L401:27-401:46">node cordoned before evicting the node</a>.</li>
  <li>Then it calls the <code class="language-plaintext highlighter-rouge">deleteOrEvictPodsSimple</code> passing <a href="https://sourcegraph.com/github.com/kubernetes/kubernetes@810e9e212ec5372d16b655f57b9231d8654a2179/-/blob/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain.go?L365:27-365:50">it the node info</a>.</li>
  <li><code class="language-plaintext highlighter-rouge">&lt;deleteOrEvictPodsSimple&gt;</code> this calls <code class="language-plaintext highlighter-rouge">GetPodsForDeletion</code> which get the <a href="https://sourcegraph.com/github.com/kubernetes/kubernetes@810e9e212ec5372d16b655f57b9231d8654a2179/-/blob/staging/src/k8s.io/kubectl/pkg/drain/drain.go?L181:18-181:36">list of pods</a>, filtering based on the option provided to drain.</li>
  <li><code class="language-plaintext highlighter-rouge">&lt;deleteOrEvictPodsSimple&gt;</code> calls the <code class="language-plaintext highlighter-rouge">DeleteOrEvictPods</code> on the list of pods got for deletion in the previous steps, which is actually is <a href="https://sourcegraph.com/github.com/kubernetes/kubernetes@810e9e212ec5372d16b655f57b9231d8654a2179/-/blob/staging/src/k8s.io/kubectl/pkg/drain/drain.go?L244:18-244:35">responsible for evicting pods</a>.</li>
  <li><code class="language-plaintext highlighter-rouge">&lt;deleteOrEvictPodsSimple / DeleteOrEvictPods&gt;</code> if <code class="language-plaintext highlighter-rouge">disableEnviction</code> flag is not set(which is the default, other pods are delete and not evicted), it will call the <a href="https://sourcegraph.com/github.com/kubernetes/kubernetes@810e9e212ec5372d16b655f57b9231d8654a2179/-/blob/staging/src/k8s.io/kubectl/pkg/drain/drain.go?L268:18-268:27"><code class="language-plaintext highlighter-rouge">evictPods</code> on the list if pods</a>. This <code class="language-plaintext highlighter-rouge">evictPods</code> method also gets, <a href="https://kubernetes.io/docs/concepts/scheduling-eviction/api-eviction/">Eviction API Group Version</a>,  and a callback function to get pod info, apart from pod list.</li>
  <li><code class="language-plaintext highlighter-rouge">&lt;deleteOrEvictPodsSimple / DeleteOrEvictPods / evictPods&gt;</code> this makes the call to <code class="language-plaintext highlighter-rouge">EvictPod</code>, passing the pod object and Eviction API Group version, <a href="https://sourcegraph.com/github.com/kubernetes/kubernetes@810e9e212ec5372d16b655f57b9231d8654a2179/-/blob/staging/src/k8s.io/kubectl/pkg/drain/drain.go?L149:18-149:26">ref</a>.</li>
  <li><code class="language-plaintext highlighter-rouge">&lt;deleteOrEvictPodsSimple / DeleteOrEvictPods / evictPods / EvictPod&gt;</code> based on the Eviction API Group version scheme, calls <a href="https://sourcegraph.com/github.com/kubernetes/kubernetes@810e9e212ec5372d16b655f57b9231d8654a2179/-/blob/staging/src/k8s.io/kubectl/pkg/drain/drain.go?L162">Evict(and not delete)</a>.</li>
</ol>

<p>So what’s happening here…</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="k">case</span> <span class="n">policyv1</span><span class="o">.</span><span class="n">SchemeGroupVersion</span><span class="o">:</span>
		<span class="c">// send policy/v1 if the server supports it</span>
		<span class="n">eviction</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="n">policyv1</span><span class="o">.</span><span class="n">Eviction</span><span class="p">{</span>
			<span class="n">ObjectMeta</span><span class="o">:</span> <span class="n">metav1</span><span class="o">.</span><span class="n">ObjectMeta</span><span class="p">{</span>
				<span class="n">Name</span><span class="o">:</span>      <span class="n">pod</span><span class="o">.</span><span class="n">Name</span><span class="p">,</span>
				<span class="n">Namespace</span><span class="o">:</span> <span class="n">pod</span><span class="o">.</span><span class="n">Namespace</span><span class="p">,</span>
			<span class="p">},</span>
			<span class="n">DeleteOptions</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">delOpts</span><span class="p">,</span>
		<span class="p">}</span>
		<span class="k">return</span> <span class="n">d</span><span class="o">.</span><span class="n">Client</span><span class="o">.</span><span class="n">PolicyV1</span><span class="p">()</span><span class="o">.</span><span class="n">Evictions</span><span class="p">(</span><span class="n">eviction</span><span class="o">.</span><span class="n">Namespace</span><span class="p">)</span><span class="o">.</span><span class="n">Evict</span><span class="p">(</span><span class="n">d</span><span class="o">.</span><span class="n">getContext</span><span class="p">(),</span> <span class="n">eviction</span><span class="p">)</span>
</code></pre></div></div>

<p>This is creating something like this,</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"apiVersion"</span><span class="p">:</span><span class="w"> </span><span class="s2">"policy/v1"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"kind"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Eviction"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"metadata"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"&lt;podname&gt;"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"namespace"</span><span class="p">:</span><span class="w"> </span><span class="s2">"&lt;namespace&gt;"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>And making a <code class="language-plaintext highlighter-rouge">POST</code> call to the API server as you could <a href="https://sourcegraph.com/github.com/kubernetes/kubernetes@810e9e212ec5372d16b655f57b9231d8654a2179/-/blob/staging/src/k8s.io/client-go/kubernetes/typed/policy/v1/eviction_expansion.go?L30:21-30:26">see here</a>.</p>

<p>Now how does this Evict API work is documented <a href="https://kubernetes.io/docs/concepts/scheduling-eviction/api-eviction/#how-api-initiated-eviction-works">here</a>. As you can see that, it honours the PDB of pod in question. You would end up with <a href="https://sourcegraph.com/github.com/kubernetes/kubernetes@810e9e212ec5372d16b655f57b9231d8654a2179/-/blob/staging/src/k8s.io/kubectl/pkg/drain/drain.go?L332">this</a>.</p>]]></content><author><name>M Samuel Vijaykumar</name></author><category term="kubernetes, kubectl, cli" /><summary type="html"><![CDATA[Command’s help]]></summary></entry><entry><title type="html">A phoenix.new Experiment 🌍 FLAME Ping Monitor</title><link href="https://samof76.github.io/phoenix-new-fly-ping.html" rel="alternate" type="text/html" title="A phoenix.new Experiment 🌍 FLAME Ping Monitor" /><published>2025-07-07T00:00:00+00:00</published><updated>2025-07-07T00:00:00+00:00</updated><id>https://samof76.github.io/phoenix-new-fly-ping</id><content type="html" xml:base="https://samof76.github.io/phoenix-new-fly-ping.html"><![CDATA[<blockquote>
  <p><strong>NOTE:</strong> This is a <strong>phoenix.new</strong> experiment, not a production-ready application. It is intended for educational purposes only. Also this blog and the code are completely vibe coded.</p>
</blockquote>

<p>A <strong>production-ready, enterprise-grade distributed ping monitoring system</strong> built with Phoenix LiveView and FLAME (Serverless Elixir). Monitor website availability and response times across <strong>5 continents</strong> in real-time with <strong>automated webhook notifications</strong> for critical failures.</p>

<h2 id="-live-demo">🚀 <strong>Live Demo</strong></h2>

<p>🔗 <a href="https://flame-ping-monitor.fly.dev">https://flame-ping-monitor.fly.dev</a>
🔗 <a href="https://github.com/samof76/flame-ping">Code</a></p>
<h2 id="-enterprise-features">✨ <strong>Enterprise Features</strong></h2>

<h3 id="-multi-region-monitoring">🌐 <strong>Multi-Region Monitoring</strong></h3>
<ul>
  <li><strong>5-Continent Coverage</strong>: North America 🇺🇸, Europe 🇪🇺, Asia 🇯🇵, South America 🇧🇷, Oceania 🇦🇺</li>
  <li><strong>Distributed FLAME Workers</strong>: Serverless ping execution across global regions</li>
  <li><strong>10-Second Ping Intervals</strong>: Automated monitoring with sub-second response times</li>
  <li><strong>Regional Status Tracking</strong>: Individual availability metrics per continent</li>
</ul>

<h3 id="-webhook-notification-system">🔔 <strong>Webhook Notification System</strong></h3>
<ul>
  <li><strong>Automated Failure Alerts</strong>: JSON notifications on 6 consecutive ping failures</li>
  <li><strong>Customizable Endpoints</strong>: Configure webhook URLs per domain</li>
  <li><strong>Rich Payload Data</strong>: Includes failure count, timestamps, error details</li>
  <li><strong>Delivery Tracking</strong>: Monitor webhook notification history</li>
  <li><strong>Enterprise Integration</strong>: Works with Slack, Discord, PagerDuty, custom endpoints</li>
</ul>

<h3 id="-professional-dashboard">📊 <strong>Professional Dashboard</strong></h3>
<ul>
  <li><strong>Real-time Status Dots</strong>: 🟢 Online, 🟡 Slow, 🔴 Offline</li>
  <li><strong>Response Time Display</strong>: Live latency metrics per region</li>
  <li><strong>Availability Percentages</strong>: 1-hour rolling availability statistics</li>
  <li><strong>Statistics Overview</strong>: Domain counts, region status, system health</li>
  <li><strong>Webhook Status Indicators</strong>: Visual confirmation of notification setup</li>
</ul>

<h3 id="️-enterprise-architecture">🏗️ <strong>Enterprise Architecture</strong></h3>
<ul>
  <li><strong>Phoenix LiveView</strong>: Real-time UI with WebSocket updates</li>
  <li><strong>FLAME Integration</strong>: Distributed serverless ping workers</li>
  <li><strong>PubSub Broadcasting</strong>: Instant status propagation across clients</li>
  <li><strong>SQLite Database</strong>: Persistent ping history and analytics</li>
  <li><strong>GenServer Scheduling</strong>: Reliable 10-second ping intervals</li>
  <li><strong>Bot Protection Bypass</strong>: Smart user-agent headers for accurate monitoring</li>
</ul>

<h3 id="-performance--reliability">⚡ <strong>Performance &amp; Reliability</strong></h3>
<ul>
  <li><strong>Sub-100ms Response Times</strong>: Optimized ping execution</li>
  <li><strong>Zero Downtime</strong>: Fault-tolerant distributed architecture</li>
  <li><strong>Clean Error Handling</strong>: Graceful failure recovery</li>
  <li><strong>Production Logging</strong>: Comprehensive monitoring and debugging</li>
  <li><strong>Failure Tracking</strong>: Consecutive failure counting with automatic reset</li>
</ul>

<h2 id="️-system-architecture">🏗️ <strong>System Architecture</strong></h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>┌─────────────────────────────────────────────────────────────┐
│                    Phoenix LiveView Frontend                 │
│  ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐│
│  │   Dashboard     │ │  Real-time UI   │ │   Statistics    ││
│  │   Controls      │ │    Updates      │ │    Overview     ││
│  └─────────────────┘ └─────────────────┘ └─────────────────┘│
└─────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────┐
│                     Phoenix PubSub                         │
│              Real-time Message Broadcasting                 │
└─────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────┐
│                   PingScheduler GenServer                   │
│            10-Second Interval Management                    │
└─────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────┐
│                 FLAME Distributed Workers                   │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────┐  │
│  │    NA    │ │    EU    │ │    AS    │ │    SA    │ │ OC │  │
│  │ 🇺🇸 Worker│ │ 🇪🇺 Worker│ │ 🇯🇵 Worker│ │ 🇧🇷 Worker│ │🇦🇺 W││
│  └──────────┘ └──────────┘ └──────────┘ └──────────┘ └────┘  │
└─────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────┐
│              Webhook Notification Engine                    │
│  ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐│
│  │ Failure Tracker │ │ JSON Payloads   │ │ HTTP Delivery   ││
│  │ (6 Consecutive) │ │ &amp; Timestamps    │ │ to Endpoints    ││
│  └─────────────────┘ └─────────────────┘ └─────────────────┘│
└─────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────┐
│                     SQLite Database                        │
│  ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐│
│  │    Domains      │ │   PingResults   │ │  Webhook Data   ││
│  │   &amp; Webhooks    │ │   with Regions  │ │   &amp; History     ││
│  └─────────────────┘ └─────────────────┘ └─────────────────┘│
└─────────────────────────────────────────────────────────────┘
</code></pre></div></div>

<h2 id="-quick-start">🚀 <strong>Quick Start</strong></h2>

<h3 id="prerequisites">Prerequisites</h3>
<ul>
  <li><strong>Elixir 1.15+</strong></li>
  <li><strong>Erlang/OTP 26+</strong></li>
  <li><strong>Phoenix 1.8+</strong></li>
  <li><strong>SQLite</strong></li>
</ul>

<h3 id="installation">Installation</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Clone the repository</span>
git clone https://github.com/your-org/flame_ping_monitor.git
<span class="nb">cd </span>flame_ping_monitor

<span class="c"># Install dependencies</span>
mix setup

<span class="c"># Start the server</span>
mix phx.server
</code></pre></div></div>

<p>Visit <strong><a href="http://localhost:4000">http://localhost:4000</a></strong> to access the dashboard.</p>

<h3 id="adding-domains-with-webhook-notifications">Adding Domains with Webhook Notifications</h3>

<ol>
  <li>Click <strong>“Add New Domain”</strong></li>
  <li>Enter the domain URL (e.g., <code class="language-plaintext highlighter-rouge">https://github.com</code>)</li>
  <li>Optionally provide a display name</li>
  <li><strong>Configure webhook URL</strong> for failure notifications (optional)
    <ul>
      <li>Example: <code class="language-plaintext highlighter-rouge">https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK</code></li>
      <li>Supports any HTTP endpoint that accepts JSON POST requests</li>
    </ul>
  </li>
  <li>Click <strong>“Add Domain”</strong></li>
</ol>

<p>The system will automatically begin monitoring across all 5 regions with 10-second intervals. If 6 consecutive ping failures occur, a JSON notification will be sent to your webhook endpoint.</p>

<h2 id="-tech-stack">📦 <strong>Tech Stack</strong></h2>

<table>
  <thead>
    <tr>
      <th>Component</th>
      <th>Technology</th>
      <th>Purpose</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Backend</strong></td>
      <td>Phoenix 1.8</td>
      <td>Web framework and real-time features</td>
    </tr>
    <tr>
      <td><strong>Frontend</strong></td>
      <td>LiveView</td>
      <td>Real-time UI with WebSocket updates</td>
    </tr>
    <tr>
      <td><strong>Workers</strong></td>
      <td>FLAME</td>
      <td>Distributed serverless ping execution</td>
    </tr>
    <tr>
      <td><strong>Database</strong></td>
      <td>SQLite + Ecto</td>
      <td>Data persistence and analytics</td>
    </tr>
    <tr>
      <td><strong>Styling</strong></td>
      <td>Tailwind CSS + DaisyUI</td>
      <td>Professional responsive design</td>
    </tr>
    <tr>
      <td><strong>Scheduling</strong></td>
      <td>GenServer</td>
      <td>Reliable ping interval management</td>
    </tr>
    <tr>
      <td><strong>Broadcasting</strong></td>
      <td>Phoenix PubSub</td>
      <td>Real-time status propagation</td>
    </tr>
    <tr>
      <td><strong>HTTP Client</strong></td>
      <td>Req</td>
      <td>High-performance HTTP requests</td>
    </tr>
    <tr>
      <td><strong>Notifications</strong></td>
      <td>Webhook System</td>
      <td>JSON failure alerts</td>
    </tr>
  </tbody>
</table>

<h2 id="-regional-coverage">🌍 <strong>Regional Coverage</strong></h2>

<table>
  <thead>
    <tr>
      <th>Region</th>
      <th>Flag</th>
      <th>Identifier</th>
      <th>Coverage</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>North America</strong></td>
      <td>🇺🇸</td>
      <td><code class="language-plaintext highlighter-rouge">na</code></td>
      <td>US East Coast</td>
    </tr>
    <tr>
      <td><strong>Europe</strong></td>
      <td>🇪🇺</td>
      <td><code class="language-plaintext highlighter-rouge">eu</code></td>
      <td>Western Europe</td>
    </tr>
    <tr>
      <td><strong>Asia</strong></td>
      <td>🇯🇵</td>
      <td><code class="language-plaintext highlighter-rouge">as</code></td>
      <td>East Asia</td>
    </tr>
    <tr>
      <td><strong>South America</strong></td>
      <td>🇧🇷</td>
      <td><code class="language-plaintext highlighter-rouge">sa</code></td>
      <td>Brazil</td>
    </tr>
    <tr>
      <td><strong>Oceania</strong></td>
      <td>🇦🇺</td>
      <td><code class="language-plaintext highlighter-rouge">oc</code></td>
      <td>Australia</td>
    </tr>
  </tbody>
</table>

<h2 id="-dashboard-features">📈 <strong>Dashboard Features</strong></h2>

<h3 id="status-indicators">Status Indicators</h3>
<ul>
  <li><strong>🟢 Online</strong>: Response time &lt; 1000ms</li>
  <li><strong>🟡 Slow</strong>: Response time 1000-5000ms</li>
  <li><strong>🔴 Offline</strong>: Timeout or HTTP error</li>
  <li><strong>🔔 Webhook Enabled</strong>: Visual indicator for notification setup</li>
</ul>

<h3 id="statistics-cards">Statistics Cards</h3>
<ul>
  <li><strong>Total Domains</strong>: Number of monitored websites</li>
  <li><strong>Online Regions</strong>: Active monitoring regions across continents</li>
  <li><strong>Offline Regions</strong>: Regions experiencing connectivity issues</li>
  <li><strong>Active Regions</strong>: Total operational ping regions</li>
</ul>

<h3 id="real-time-updates">Real-time Updates</h3>
<ul>
  <li><strong>Live Status Changes</strong>: Instant visual feedback</li>
  <li><strong>Response Time Display</strong>: Current latency per region</li>
  <li><strong>Availability Metrics</strong>: 1-hour rolling percentages</li>
  <li><strong>Error Reporting</strong>: Detailed failure information</li>
</ul>

<h2 id="-webhook-notification-system-1">🔔 <strong>Webhook Notification System</strong></h2>

<h3 id="json-payload-format">JSON Payload Format</h3>
<p>When 6 consecutive ping failures occur, the system sends this JSON payload:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"domain"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://example.com"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"My Website"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"status"</span><span class="p">:</span><span class="w"> </span><span class="s2">"critical_failure"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"consecutive_failures"</span><span class="p">:</span><span class="w"> </span><span class="mi">6</span><span class="p">,</span><span class="w">
  </span><span class="nl">"last_failure_at"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-01-15T10:30:00Z"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"error_message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Connection timeout"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"timestamp"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024-01-15T10:30:15Z"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h3 id="supported-integrations">Supported Integrations</h3>
<ul>
  <li><strong>Slack Webhooks</strong>: <code class="language-plaintext highlighter-rouge">https://hooks.slack.com/services/...</code></li>
  <li><strong>Discord Webhooks</strong>: <code class="language-plaintext highlighter-rouge">https://discord.com/api/webhooks/...</code></li>
  <li><strong>Microsoft Teams</strong>: <code class="language-plaintext highlighter-rouge">https://outlook.office.com/webhook/...</code></li>
  <li><strong>PagerDuty Events API</strong>: <code class="language-plaintext highlighter-rouge">https://events.pagerduty.com/v2/enqueue</code></li>
  <li><strong>Custom Endpoints</strong>: Any HTTP endpoint accepting JSON POST</li>
</ul>

<h3 id="webhook-configuration">Webhook Configuration</h3>
<ol>
  <li>Configure your webhook endpoint to accept HTTP POST requests</li>
  <li>Parse the JSON payload for failure details</li>
  <li>Implement your notification logic (Slack message, email, SMS, etc.)</li>
  <li>Return HTTP 200 status for successful delivery confirmation</li>
</ol>

<h2 id="-configuration">🔧 <strong>Configuration</strong></h2>

<h3 id="environment-variables">Environment Variables</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Database</span>
<span class="nv">DATABASE_URL</span><span class="o">=</span><span class="s2">"ecto://localhost/flame_ping_monitor_dev"</span>

<span class="c"># Phoenix</span>
<span class="nv">SECRET_KEY_BASE</span><span class="o">=</span><span class="s2">"your-secret-key"</span>
<span class="nv">PHX_HOST</span><span class="o">=</span><span class="s2">"localhost"</span>
<span class="nv">PHX_PORT</span><span class="o">=</span><span class="s2">"4000"</span>

<span class="c"># FLAME (Production)</span>
<span class="nv">FLAME_BACKEND</span><span class="o">=</span><span class="s2">"fly"</span>
<span class="nv">FLY_API_TOKEN</span><span class="o">=</span><span class="s2">"your-fly-token"</span>
</code></pre></div></div>

<h3 id="ping-configuration">Ping Configuration</h3>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># lib/flame_ping_monitor/monitoring/ping_scheduler.ex</span>
<span class="nv">@ping_interval</span> <span class="mi">10_000</span>  <span class="c1"># 10 seconds</span>
<span class="nv">@timeout</span> <span class="mi">30_000</span>       <span class="c1"># 30 second timeout</span>
<span class="nv">@failure_threshold</span> <span class="mi">6</span>  <span class="c1"># Webhook trigger threshold</span>
</code></pre></div></div>

<h2 id="-deployment">🚀 <strong>Deployment</strong></h2>

<h3 id="flyio-recommended">Fly.io (Recommended)</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Initialize Fly app</span>
fly launch

<span class="c"># Deploy</span>
fly deploy

<span class="c"># Set production secrets</span>
fly secrets <span class="nb">set </span><span class="nv">SECRET_KEY_BASE</span><span class="o">=</span><span class="si">$(</span>mix phx.gen.secret<span class="si">)</span>
fly secrets <span class="nb">set </span><span class="nv">FLY_API_TOKEN</span><span class="o">=</span><span class="s2">"your-fly-api-token"</span>
</code></pre></div></div>

<p><strong>Note</strong>: This application is optimized for <strong>Fly.io</strong> deployment due to FLAME’s multi-region capabilities. For other platforms, you may need to modify the FLAME backend configuration.</p>

<h2 id="-monitoring--debugging">🔍 <strong>Monitoring &amp; Debugging</strong></h2>

<h3 id="production-logs">Production Logs</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># View live logs</span>
fly logs

<span class="c"># Filter by component</span>
fly logs <span class="nt">--app</span> your-app-name | <span class="nb">grep</span> <span class="s2">"PingMonitor"</span>

<span class="c"># Monitor webhook deliveries</span>
fly logs <span class="nt">--app</span> your-app-name | <span class="nb">grep</span> <span class="s2">"Webhook"</span>
</code></pre></div></div>

<h3 id="database-queries">Database Queries</h3>

<div class="language-elixir highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Recent ping results with region data</span>
<span class="n">iex</span><span class="o">&gt;</span> <span class="no">FlamePingMonitor</span><span class="o">.</span><span class="no">Repo</span><span class="o">.</span><span class="n">all</span><span class="p">(</span>
  <span class="n">from</span> <span class="n">p</span> <span class="ow">in</span> <span class="no">FlamePingMonitor</span><span class="o">.</span><span class="no">Monitoring</span><span class="o">.</span><span class="no">PingResult</span><span class="p">,</span>
  <span class="ss">where:</span> <span class="n">p</span><span class="o">.</span><span class="n">inserted_at</span> <span class="o">&gt;</span> <span class="n">ago</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s2">"hour"</span><span class="p">),</span>
  <span class="ss">order_by:</span> <span class="p">[</span><span class="ss">desc:</span> <span class="n">p</span><span class="o">.</span><span class="n">inserted_at</span><span class="p">],</span>
  <span class="ss">limit:</span> <span class="mi">100</span>
<span class="p">)</span>

<span class="c1"># Domain availability statistics by region</span>
<span class="n">iex</span><span class="o">&gt;</span> <span class="no">FlamePingMonitor</span><span class="o">.</span><span class="no">Monitoring</span><span class="o">.</span><span class="no">PingMonitor</span><span class="o">.</span><span class="n">get_domain_region_status</span><span class="p">(</span><span class="n">domain_id</span><span class="p">)</span>

<span class="c1"># Domains with webhook notifications enabled</span>
<span class="n">iex</span><span class="o">&gt;</span> <span class="n">from</span><span class="p">(</span><span class="n">d</span> <span class="ow">in</span> <span class="no">FlamePingMonitor</span><span class="o">.</span><span class="no">Monitoring</span><span class="o">.</span><span class="no">Domain</span><span class="p">,</span>
          <span class="ss">where:</span> <span class="ow">not</span> <span class="n">is_nil</span><span class="p">(</span><span class="n">d</span><span class="o">.</span><span class="n">webhook_url</span><span class="p">)</span> <span class="ow">and</span> <span class="n">d</span><span class="o">.</span><span class="n">webhook_url</span> <span class="o">!=</span> <span class="s2">""</span><span class="p">)</span>
     <span class="o">|&gt;</span> <span class="no">FlamePingMonitor</span><span class="o">.</span><span class="no">Repo</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
</code></pre></div></div>

<h2 id="-contributing">🤝 <strong>Contributing</strong></h2>

<ol>
  <li><strong>Fork the repository</strong></li>
  <li><strong>Create a feature branch</strong> (<code class="language-plaintext highlighter-rouge">git checkout -b feature/amazing-feature</code>)</li>
  <li><strong>Commit changes</strong> (<code class="language-plaintext highlighter-rouge">git commit -m 'Add amazing feature'</code>)</li>
  <li><strong>Push to branch</strong> (<code class="language-plaintext highlighter-rouge">git push origin feature/amazing-feature</code>)</li>
  <li><strong>Open a Pull Request</strong></li>
</ol>

<h2 id="-license">📄 <strong>License</strong></h2>

<p>This project is licensed under the <strong>MIT License</strong> - see the <a href="LICENSE">LICENSE</a> file for details.</p>

<h2 id="-production-status">🏆 <strong>Production Status</strong></h2>

<p>✅ <strong>Live Production Deployment</strong>: <a href="https://flame-ping-monitor.fly.dev">https://flame-ping-monitor.fly.dev</a></p>

<p>✅ <strong>Multi-Region Architecture</strong>: 5-continent distributed monitoring</p>

<p>✅ <strong>Enterprise-Grade Reliability</strong>: Zero-downtime fault-tolerant design</p>

<p>✅ <strong>Real-time Performance</strong>: Sub-100ms response times with 10-second intervals</p>

<p>✅ <strong>Webhook Notification System</strong>: Automated JSON alerts for critical failures</p>

<p>✅ <strong>Bot Protection Bypass</strong>: Smart user-agent headers for accurate monitoring</p>

<hr />

<p><strong>Built with ❤️ using Phoenix LiveView + FLAME</strong></p>

<p><em>Monitor the world, get notified instantly.</em></p>]]></content><author><name>M Samuel Vijaykumar</name></author><category term="tools, ide, editor" /><summary type="html"><![CDATA[NOTE: This is a phoenix.new experiment, not a production-ready application. It is intended for educational purposes only. Also this blog and the code are completely vibe coded.]]></summary></entry><entry><title type="html">DNS Lookups glibc vs musl-libc wrt K8s</title><link href="https://samof76.github.io/dns-lookups-k8s-glibc-vs-musl.html" rel="alternate" type="text/html" title="DNS Lookups glibc vs musl-libc wrt K8s" /><published>2025-05-26T00:00:00+00:00</published><updated>2025-05-26T00:00:00+00:00</updated><id>https://samof76.github.io/dns-lookups-k8s-glibc-vs-musl</id><content type="html" xml:base="https://samof76.github.io/dns-lookups-k8s-glibc-vs-musl.html"><![CDATA[<blockquote>
  <p>ASIDE: Love the way <a href="https://harelang.org/">hare-lang</a> has implemented <a href="https://git.sr.ht/~sircmpwn/hare/tree/989d021522d24cede090f38e1a00704a40b9e798/item/net/dns/query.ha">DNS lookups</a>, it’s so clean to read and understand. Just to note, they have followed the musl-libc way of implementing, but only cleaner :).</p>
</blockquote>

<p>Recently while going through the <a href="https://musl.libc.org/">musl-libc</a> documentation, I noticed that it has a <a href="https://wiki.musl-libc.org/functional-differences-from-glibc.html">different approach to DNS lookups</a> compared to glibc. Just want to document this from the kubernetes context here.</p>

<blockquote>
  <p>NOTE: <code class="language-plaintext highlighter-rouge">musl-libc</code> is the standard libc packaged with the popular the <a href="https://www.alpinelinux.org/">Alpine Linux</a>.</p>
</blockquote>

<p>Kubernetes handles DNS lookups by using a DNS server that is deployed as a pod in the cluster. This DNS server is responsible for resolving DNS queries for services and pods in the cluster. Kubernetes also provides a DNS service that can be used to resolve DNS queries for external domains. Also Kubernetes provides a mechanism for configuring DNS settings for pods, such as setting the DNS search domain or specifying DNS servers to use.</p>

<h2 id="dns-lookups-from-a-pod">DNS Lookups from a Pod</h2>

<p>When a normal pod(with no custom DNS settings) is created the pods comes up with default <code class="language-plaintext highlighter-rouge">resolve.conf</code>, that looks like this:</p>

<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">nameserver</span> <span class="m">10</span>.<span class="m">96</span>.<span class="m">0</span>.<span class="m">10</span>
<span class="n">search</span> <span class="n">default</span>.<span class="n">svc</span>.<span class="n">cluster</span>.<span class="n">local</span> <span class="n">svc</span>.<span class="n">cluster</span>.<span class="n">local</span> <span class="n">cluster</span>.<span class="n">local</span>
<span class="n">options</span> <span class="n">ndots</span>:<span class="m">5</span>
</code></pre></div></div>

<h3 id="the-nameserver-entry">The <code class="language-plaintext highlighter-rouge">nameserver</code> entry</h3>

<p>The <code class="language-plaintext highlighter-rouge">nameserver</code> entry in the <code class="language-plaintext highlighter-rouge">resolve.conf</code> file specifies the IP address of the DNS server that the pod should use to resolve DNS queries. In Kubernetes, the DNS server is deployed as a pod(usually CoreDNS) in the cluster and is accessible as service with <code class="language-plaintext highlighter-rouge">clusterIP</code> at the IP address <code class="language-plaintext highlighter-rouge">10.96.0.10</code>. This IP address is used as the <code class="language-plaintext highlighter-rouge">nameserver</code> entry in the <code class="language-plaintext highlighter-rouge">resolve.conf</code> file for all pods in the cluster.</p>

<p>At this moment is important to note that there could be <code class="language-plaintext highlighter-rouge">nameserver</code> entries in the <code class="language-plaintext highlighter-rouge">resolve.conf</code> file. These entries are used to specify additional DNS servers that the pod should use to resolve DNS queries. This can be configured using the <code class="language-plaintext highlighter-rouge">dnsPolicy</code> field in the pod’s specification, which we will discuss.</p>

<h3 id="the-search-entry">The <code class="language-plaintext highlighter-rouge">search</code> entry</h3>

<p>The <code class="language-plaintext highlighter-rouge">search</code> entry in the <code class="language-plaintext highlighter-rouge">resolve.conf</code> file specifies the search domains that the pod should use to resolve DNS queries. In Kubernetes, the search domains are automatically configured based on the namespace(in this case <code class="language-plaintext highlighter-rouge">default</code>) and cluster domain of the pod. The search domains are used to resolve DNS queries for services and pods in the cluster. A good example is a service named <code class="language-plaintext highlighter-rouge">my-service</code> in the <code class="language-plaintext highlighter-rouge">default</code> namespace. The search domain for this service would be <code class="language-plaintext highlighter-rouge">default.svc.cluster.local</code>. This means that if a pod in the <code class="language-plaintext highlighter-rouge">default</code> namespace tries to resolve the DNS name <code class="language-plaintext highlighter-rouge">my-service</code>, it will first try to resolve it as <code class="language-plaintext highlighter-rouge">my-service.default.svc.cluster.local</code>.</p>

<h3 id="the-ndots-entry-in-options">The <code class="language-plaintext highlighter-rouge">ndots</code> entry in <code class="language-plaintext highlighter-rouge">options</code></h3>

<p>The <code class="language-plaintext highlighter-rouge">ndots</code> entry in the <code class="language-plaintext highlighter-rouge">options</code> section of the <code class="language-plaintext highlighter-rouge">resolve.conf</code> file specifies the number of dots that must appear in a hostname before the DNS resolver will try to resolve it as a fully qualified domain name. Just to clarify,</p>

<ul>
  <li>The <code class="language-plaintext highlighter-rouge">ndots</code> option in <code class="language-plaintext highlighter-rouge">/etc/resolv.conf</code> specifies a threshold for the number of dots (<code class="language-plaintext highlighter-rouge">.</code>) in a hostname. It determines whether the resolver treats the hostname as an FQDN or applies search domains from the search or domain directives before querying.</li>
  <li>If a hostname has fewer dots than ndots, the resolver first tries appending each search domain (e.g., <code class="language-plaintext highlighter-rouge">default.svc.cluster.local</code>) before attempting the literal hostname.</li>
</ul>

<p>Example:</p>

<ul>
  <li>If <code class="language-plaintext highlighter-rouge">ndots</code> is set to 5, the resolver will try to resolve <code class="language-plaintext highlighter-rouge">my-service.prod.internal</code>(no. of dots less than <code class="language-plaintext highlighter-rouge">ndots</code>) as <code class="language-plaintext highlighter-rouge">my-service.prod.internal.default.svc.cluster.local</code> and go through the <code class="language-plaintext highlighter-rouge">search</code> domains(until a successful lookup) before trying <code class="language-plaintext highlighter-rouge">my-service.prod.internal</code>.</li>
  <li>But if <code class="language-plaintext highlighter-rouge">ndots</code> is set to 2, and <code class="language-plaintext highlighter-rouge">my-service.prod.internal</code>(no. of dots 2 &gt;= <code class="language-plaintext highlighter-rouge">ndots</code>) will be considered as an FQDN.</li>
</ul>

<p>NOTE: A full qualified domain is a domain that ends with a . in the end, that <code class="language-plaintext highlighter-rouge">example.com.</code> is a fully qualified domain, so now that means <code class="language-plaintext highlighter-rouge">example.com</code> is not a fully qualified domain. So when we type a domain in the browser the browsers do the appending of this <code class="language-plaintext highlighter-rouge">.</code> for you. And it is important to note <code class="language-plaintext highlighter-rouge">ndots</code> does not count the trailing dot.</p>

<h3 id="custom-dns-configuration">Custom DNS Configuration</h3>

<p>Kubernetes provides <code class="language-plaintext highlighter-rouge">dnsPolicy</code> as part of the Pod Specitication, that could be used customize what goes into <code class="language-plaintext highlighter-rouge">resolve.conf</code>. That would need seperate indepth coverage, but here is an an example configuration.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">...</span>
<span class="na">dnsConfig</span><span class="pi">:</span>
  <span class="na">nameservers</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s">192.0.2.1</span>
  <span class="na">searches</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="s">ns1.svc.cluster-domain.example</span>
  <span class="pi">-</span> <span class="s">my.dns.search.suffix</span>
  <span class="na">options</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">ndots</span>
    <span class="na">value</span><span class="pi">:</span> <span class="s2">"</span><span class="s">2"</span>
<span class="nn">...</span>
</code></pre></div></div>

<h2 id="overview-of-dns-resolution-in-c-libraries">Overview of DNS Resolution in C Libraries</h2>

<p>DNS (Domain Name System) resolution is a critical function of any C library, allowing applications to translate human-readable domain names into IP addresses. Both glibc and musl-libc provide implementations of standard functions like <code class="language-plaintext highlighter-rouge">getaddrinfo()</code>, but their internal approaches differ significantly in design philosophy, performance characteristics, and feature support.</p>

<h2 id="key-differences-in-implementation-approaches">Key Differences in Implementation Approaches</h2>

<h3 id="resolver-behavior">Resolver Behavior</h3>

<h4 id="glibc">glibc</h4>
<ul>
  <li>Queries nameservers in <code class="language-plaintext highlighter-rouge">/etc/resolv.conf</code> <strong>sequentially, one by one</strong></li>
  <li>If a hostname is found in a source (a positive answer), the resolver looks up all address types requested and stops searching</li>
  <li>If there is a negative answer (hostname does not exist), the resolver also stops searching</li>
  <li>If neither positive nor negative answer is obtained, the resolver continues searching until all sources have been searched</li>
  <li>Supports falling back to search domains even for hostnames with dots exceeding the <code class="language-plaintext highlighter-rouge">ndots</code> threshold</li>
</ul>

<h4 id="musl-libc">musl-libc</h4>
<ul>
  <li>Queries all nameservers in <code class="language-plaintext highlighter-rouge">/etc/resolv.conf</code> <strong>in parallel</strong> and returns the fastest response</li>
  <li>Completely new resolver code with different behavior in certain situations</li>
  <li>For hostnames with at least as many dots as <code class="language-plaintext highlighter-rouge">ndots</code>, only tries in the global namespace (never falling back to search domains)</li>
  <li>Prior to version 1.1.13, did not support the <code class="language-plaintext highlighter-rouge">domain</code> and <code class="language-plaintext highlighter-rouge">search</code> keywords in <code class="language-plaintext highlighter-rouge">resolv.conf</code></li>
</ul>

<h3 id="configuration-file-handling">Configuration File Handling</h3>

<h4 id="glibc-1">glibc</h4>
<ul>
  <li>Reads and processes <code class="language-plaintext highlighter-rouge">/etc/nsswitch.conf</code> to determine the order of name resolution sources</li>
  <li>Opens and uses the <code class="language-plaintext highlighter-rouge">nscd</code> socket for name service caching</li>
  <li>Reads and processes <code class="language-plaintext highlighter-rouge">/etc/gai.conf</code> for additional configuration</li>
  <li>Supports <code class="language-plaintext highlighter-rouge">single-request</code> and <code class="language-plaintext highlighter-rouge">single-request-reopen</code> options in <code class="language-plaintext highlighter-rouge">/etc/resolv.conf</code></li>
</ul>

<h4 id="musl-libc-1">musl-libc</h4>
<ul>
  <li>Does not read <code class="language-plaintext highlighter-rouge">/etc/nsswitch.conf</code></li>
  <li>Does not use the <code class="language-plaintext highlighter-rouge">nscd</code> socket</li>
  <li>Does not read <code class="language-plaintext highlighter-rouge">/etc/gai.conf</code></li>
  <li>Does not support the <code class="language-plaintext highlighter-rouge">single-request</code> and <code class="language-plaintext highlighter-rouge">single-request-reopen</code> options in <code class="language-plaintext highlighter-rouge">/etc/resolv.conf</code></li>
  <li>Only reads <code class="language-plaintext highlighter-rouge">/etc/services</code>, <code class="language-plaintext highlighter-rouge">/etc/hosts</code>, and <code class="language-plaintext highlighter-rouge">/etc/resolv.conf</code></li>
</ul>

<h3 id="default-flags-and-behavior">Default Flags and Behavior</h3>

<h4 id="glibc-2">glibc</h4>
<ul>
  <li>Default <code class="language-plaintext highlighter-rouge">ai_flags</code> for <code class="language-plaintext highlighter-rouge">getaddrinfo()</code>: <code class="language-plaintext highlighter-rouge">AI_ADDRCONFIG|AI_V4MAPPED</code></li>
  <li>More extensive error handling and retry mechanisms</li>
</ul>

<h4 id="musl-libc-2">musl-libc</h4>
<ul>
  <li>Default <code class="language-plaintext highlighter-rouge">ai_flags</code> for <code class="language-plaintext highlighter-rouge">getaddrinfo()</code>: <code class="language-plaintext highlighter-rouge">0</code> (no flags set)</li>
  <li>Simpler, more lightweight implementation focused on efficiency</li>
</ul>

<h3 id="protocol-support">Protocol Support</h3>

<h4 id="glibc-3">glibc</h4>
<ul>
  <li>Has long supported both UDP and TCP for DNS queries</li>
</ul>

<h4 id="musl-libc-3">musl-libc</h4>
<ul>
  <li>TCP support for DNS queries was only introduced in version 1.2.4</li>
  <li>Prior to this, there could be issues handling larger packets, such as those required for DNSSEC or when a large number of records are returned</li>
</ul>

<h3 id="search-domain-handling">Search Domain Handling</h3>

<h4 id="glibc-4">glibc</h4>
<ul>
  <li>Processes queries with fewer dots than <code class="language-plaintext highlighter-rouge">ndots</code> with search domains first, then tries literally</li>
  <li>For queries with at least as many dots as <code class="language-plaintext highlighter-rouge">ndots</code>, tries literally first, then falls back to search domains if not found</li>
</ul>

<h4 id="musl-libc-4">musl-libc</h4>
<ul>
  <li>Processes queries with fewer dots than <code class="language-plaintext highlighter-rouge">ndots</code> with search domains first, then tries literally (like glibc)</li>
  <li>For queries with at least as many dots as <code class="language-plaintext highlighter-rouge">ndots</code>, only tries in the global namespace (never falling back to search domains)</li>
</ul>

<h3 id="implementation-philosophy">Implementation Philosophy</h3>

<h4 id="glibc-5">glibc</h4>
<ul>
  <li>More feature-rich with extensive compatibility options</li>
  <li>Focuses on compatibility with various network configurations and legacy systems</li>
  <li>More complex implementation with additional features and options</li>
</ul>

<h4 id="musl-libc-5">musl-libc</h4>
<ul>
  <li>Lightweight, fast, and simple implementation</li>
  <li>Focuses on efficiency, standards compliance, and security</li>
  <li>Simpler implementation with fewer features but potentially better performance in standard cases</li>
</ul>

<h3 id="code-implementation-analysis">Code Implementation Analysis</h3>

<h4 id="glibc-dns-resolver-implementation">glibc DNS Resolver Implementation</h4>

<p>The core of glibc’s DNS resolver is implemented in the <code class="language-plaintext highlighter-rouge">resolv</code> directory, with key files including <code class="language-plaintext highlighter-rouge">res_query.c</code>, <code class="language-plaintext highlighter-rouge">res_init.c</code>, and related files. The implementation is derived from BIND 8, with modifications to fit into the glibc framework.</p>

<p><a href="https://github.com/bminor/glibc/blob/319f94dea2b7eeff12adb22ee50b46b64dd6a52d/resolv/res_query.c#L109">Key function in <code class="language-plaintext highlighter-rouge">res_query.c</code></a>:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">__res_context_query</span> <span class="p">(</span><span class="k">struct</span> <span class="n">resolv_context</span> <span class="o">*</span><span class="n">ctx</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">name</span><span class="p">,</span> <span class="kt">int</span> <span class="n">class</span><span class="p">,</span>
                        <span class="kt">int</span> <span class="n">type</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">answer</span><span class="p">,</span> <span class="kt">int</span> <span class="n">anslen</span><span class="p">,</span>
                        <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">**</span><span class="n">answerp</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">**</span><span class="n">answerp2</span><span class="p">,</span>
                        <span class="kt">int</span> <span class="o">*</span><span class="n">nanswerp2</span><span class="p">,</span> <span class="kt">int</span> <span class="o">*</span><span class="n">resplen2</span><span class="p">,</span> <span class="kt">int</span> <span class="o">*</span><span class="n">answerp2_malloced</span><span class="p">)</span>
<span class="p">{</span>
  <span class="k">struct</span> <span class="n">__res_state</span> <span class="o">*</span><span class="n">statp</span> <span class="o">=</span> <span class="n">ctx</span><span class="o">-&gt;</span><span class="n">resp</span><span class="p">;</span>
  <span class="n">UHEADER</span> <span class="o">*</span><span class="n">hp</span> <span class="o">=</span> <span class="p">(</span><span class="n">UHEADER</span> <span class="o">*</span><span class="p">)</span> <span class="n">answer</span><span class="p">;</span>
  <span class="n">UHEADER</span> <span class="o">*</span><span class="n">hp2</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">n</span><span class="p">;</span>
  <span class="n">bool</span> <span class="n">retried</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>

  <span class="cm">/* It requires 2 times QUERYSIZE for type == T_QUERY_A_AND_AAAA. */</span>
  <span class="k">struct</span> <span class="n">scratch_buffer</span> <span class="n">buf</span><span class="p">;</span>
  <span class="n">scratch_buffer_init</span> <span class="p">(</span><span class="o">&amp;</span><span class="n">buf</span><span class="p">);</span>
  <span class="k">_Static_assert</span> <span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">QUERYSIZE</span> <span class="o">&lt;=</span> <span class="k">sizeof</span> <span class="p">(</span><span class="n">buf</span><span class="p">.</span><span class="n">__space</span><span class="p">.</span><span class="n">__c</span><span class="p">),</span>
                 <span class="s">"scratch_buffer too small"</span><span class="p">);</span>
  <span class="n">u_char</span> <span class="o">*</span><span class="n">query1</span> <span class="o">=</span> <span class="n">buf</span><span class="p">.</span><span class="n">data</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">nquery1</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
  <span class="n">u_char</span> <span class="o">*</span><span class="n">query2</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">nquery2</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

 <span class="nl">again:</span>
  <span class="n">hp</span><span class="o">-&gt;</span><span class="n">rcode</span> <span class="o">=</span> <span class="n">NOERROR</span><span class="p">;</span>  <span class="cm">/* default */</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">type</span> <span class="o">==</span> <span class="n">T_QUERY_A_AND_AAAA</span><span class="p">)</span>
    <span class="p">{</span>
      <span class="c1">// Code for handling both A and AAAA queries</span>
      <span class="c1">// ...</span>
    <span class="p">}</span>
  <span class="k">else</span>
    <span class="p">{</span>
      <span class="n">n</span> <span class="o">=</span> <span class="n">__res_context_mkquery</span> <span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">QUERY</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">class</span><span class="p">,</span> <span class="n">type</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span>
                                <span class="n">query1</span><span class="p">,</span> <span class="n">buf</span><span class="p">.</span><span class="n">length</span><span class="p">);</span>
      <span class="k">if</span> <span class="p">(</span><span class="n">n</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="n">statp</span><span class="o">-&gt;</span><span class="n">options</span> <span class="o">&amp;</span> <span class="p">(</span><span class="n">RES_USE_EDNS0</span><span class="o">|</span><span class="n">RES_USE_DNSSEC</span><span class="p">))</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
        <span class="p">{</span>
          <span class="cm">/* Use RESOLV_EDNS_BUFFER_SIZE if the receive buffer can
             be reallocated. */</span>
          <span class="kt">size_t</span> <span class="n">advertise</span><span class="p">;</span>
          <span class="k">if</span> <span class="p">(</span><span class="n">answerp</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span>
            <span class="n">advertise</span> <span class="o">=</span> <span class="n">anslen</span><span class="p">;</span>
          <span class="k">else</span>
            <span class="n">advertise</span> <span class="o">=</span> <span class="n">RESOLV_EDNS_BUFFER_SIZE</span><span class="p">;</span>
          <span class="n">n</span> <span class="o">=</span> <span class="n">__res_nopt</span> <span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">query1</span><span class="p">,</span> <span class="n">buf</span><span class="p">.</span><span class="n">length</span><span class="p">,</span> <span class="n">advertise</span><span class="p">);</span>
        <span class="p">}</span>
      <span class="n">nquery1</span> <span class="o">=</span> <span class="n">n</span><span class="p">;</span>
    <span class="p">}</span>

  <span class="c1">// Error handling and query processing</span>
  <span class="c1">// ...</span>

  <span class="c1">// Send the query and process the response</span>
  <span class="n">n</span> <span class="o">=</span> <span class="n">__res_context_send</span> <span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">query1</span><span class="p">,</span> <span class="n">nquery1</span><span class="p">,</span> <span class="n">query2</span><span class="p">,</span> <span class="n">nquery2</span><span class="p">,</span>
                         <span class="n">answer</span><span class="p">,</span> <span class="n">anslen</span><span class="p">,</span> <span class="n">answerp</span><span class="p">,</span> <span class="n">answerp2</span><span class="p">,</span> <span class="n">nanswerp2</span><span class="p">,</span>
                         <span class="n">resplen2</span><span class="p">,</span> <span class="n">answerp2_malloced</span><span class="p">);</span>

  <span class="c1">// Process the response</span>
  <span class="c1">// ...</span>

  <span class="k">return</span> <span class="p">(</span><span class="n">n</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This function is responsible for formulating DNS queries, sending them to nameservers, and processing the responses. It handles both IPv4 (A) and IPv6 (AAAA) queries, and supports various options like EDNS0 and DNSSEC.</p>

<p>The glibc implementation is characterized by:</p>
<ul>
  <li>Complex error handling and retry mechanisms</li>
  <li>Support for various DNS extensions and options</li>
  <li>Sequential querying of nameservers</li>
  <li>Extensive compatibility with different network configurations</li>
</ul>

<h4 id="musl-libc-dns-resolver-implementation">musl-libc DNS Resolver Implementation</h4>

<p>The musl-libc DNS resolver is implemented in the <code class="language-plaintext highlighter-rouge">src/network</code> directory, with key files including <code class="language-plaintext highlighter-rouge">getaddrinfo.c</code>, <code class="language-plaintext highlighter-rouge">lookup.h</code>, and related files. Unlike glibc, musl-libc’s implementation is written from scratch rather than being derived from BIND.</p>

<p><a href="https://github.com/bminor/musl/blob/c47ad25ea3b484e10326f933e927c0bc8cded3da/src/network/getaddrinfo.c#L12">Key function in <code class="language-plaintext highlighter-rouge">getaddrinfo.c</code></a>:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">getaddrinfo</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="kr">restrict</span> <span class="n">host</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="kr">restrict</span> <span class="n">serv</span><span class="p">,</span> <span class="k">const</span> <span class="k">struct</span> <span class="n">addrinfo</span> <span class="o">*</span><span class="kr">restrict</span> <span class="n">hint</span><span class="p">,</span> <span class="k">struct</span> <span class="n">addrinfo</span> <span class="o">**</span><span class="kr">restrict</span> <span class="n">res</span><span class="p">)</span>
<span class="p">{</span>
  <span class="k">struct</span> <span class="n">service</span> <span class="n">ports</span><span class="p">[</span><span class="n">MAXSERVS</span><span class="p">];</span>
  <span class="k">struct</span> <span class="n">address</span> <span class="n">addrs</span><span class="p">[</span><span class="n">MAXADDRS</span><span class="p">];</span>
  <span class="kt">char</span> <span class="n">canon</span><span class="p">[</span><span class="mi">256</span><span class="p">],</span> <span class="o">*</span><span class="n">outcanon</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">nservs</span><span class="p">,</span> <span class="n">naddrs</span><span class="p">,</span> <span class="n">nais</span><span class="p">,</span> <span class="n">canon_len</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">j</span><span class="p">,</span> <span class="n">k</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">family</span> <span class="o">=</span> <span class="n">AF_UNSPEC</span><span class="p">,</span> <span class="n">flags</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">proto</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="n">socktype</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">no_family</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="k">struct</span> <span class="n">aibuf</span> <span class="o">*</span><span class="n">out</span><span class="p">;</span>

  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">host</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">serv</span><span class="p">)</span> <span class="k">return</span> <span class="n">EAI_NONAME</span><span class="p">;</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">hint</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">family</span> <span class="o">=</span> <span class="n">hint</span><span class="o">-&gt;</span><span class="n">ai_family</span><span class="p">;</span>
    <span class="n">flags</span> <span class="o">=</span> <span class="n">hint</span><span class="o">-&gt;</span><span class="n">ai_flags</span><span class="p">;</span>
    <span class="n">proto</span> <span class="o">=</span> <span class="n">hint</span><span class="o">-&gt;</span><span class="n">ai_protocol</span><span class="p">;</span>
    <span class="n">socktype</span> <span class="o">=</span> <span class="n">hint</span><span class="o">-&gt;</span><span class="n">ai_socktype</span><span class="p">;</span>

    <span class="k">const</span> <span class="kt">int</span> <span class="n">mask</span> <span class="o">=</span> <span class="n">AI_PASSIVE</span> <span class="o">|</span> <span class="n">AI_CANONNAME</span> <span class="o">|</span> <span class="n">AI_NUMERICHOST</span> <span class="o">|</span>
      <span class="n">AI_V4MAPPED</span> <span class="o">|</span> <span class="n">AI_ALL</span> <span class="o">|</span> <span class="n">AI_ADDRCONFIG</span> <span class="o">|</span> <span class="n">AI_NUMERICSERV</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">((</span><span class="n">flags</span> <span class="o">&amp;</span> <span class="n">mask</span><span class="p">)</span> <span class="o">!=</span> <span class="n">flags</span><span class="p">)</span>
      <span class="k">return</span> <span class="n">EAI_BADFLAGS</span><span class="p">;</span>

    <span class="k">switch</span> <span class="p">(</span><span class="n">family</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">case</span> <span class="n">AF_INET</span><span class="p">:</span>
    <span class="k">case</span> <span class="n">AF_INET6</span><span class="p">:</span>
    <span class="k">case</span> <span class="n">AF_UNSPEC</span><span class="p">:</span>
      <span class="k">break</span><span class="p">;</span>
    <span class="nl">default:</span>
      <span class="k">return</span> <span class="n">EAI_FAMILY</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>

  <span class="c1">// Handle AI_ADDRCONFIG flag</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">flags</span> <span class="o">&amp;</span> <span class="n">AI_ADDRCONFIG</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Check if IPv4 and IPv6 are configured</span>
    <span class="c1">// ...</span>
  <span class="p">}</span>

  <span class="c1">// Look up service and name</span>
  <span class="n">nservs</span> <span class="o">=</span> <span class="n">__lookup_serv</span><span class="p">(</span><span class="n">ports</span><span class="p">,</span> <span class="n">serv</span><span class="p">,</span> <span class="n">proto</span><span class="p">,</span> <span class="n">socktype</span><span class="p">,</span> <span class="n">flags</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">nservs</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="n">nservs</span><span class="p">;</span>

  <span class="n">naddrs</span> <span class="o">=</span> <span class="n">__lookup_name</span><span class="p">(</span><span class="n">addrs</span><span class="p">,</span> <span class="n">canon</span><span class="p">,</span> <span class="n">host</span><span class="p">,</span> <span class="n">family</span><span class="p">,</span> <span class="n">flags</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">naddrs</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="n">naddrs</span><span class="p">;</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">no_family</span><span class="p">)</span> <span class="k">return</span> <span class="n">EAI_NODATA</span><span class="p">;</span>

  <span class="c1">// Allocate and populate result structures</span>
  <span class="c1">// ...</span>

  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The musl-libc implementation is characterized by:</p>
<ul>
  <li>Simpler, more streamlined code</li>
  <li>Parallel querying of nameservers</li>
  <li>Limited configuration options</li>
  <li>Focus on efficiency and standards compliance</li>
</ul>

<p>Key structures defined in <code class="language-plaintext highlighter-rouge">lookup.h</code>:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">aibuf</span> <span class="p">{</span>
  <span class="k">struct</span> <span class="n">addrinfo</span> <span class="n">ai</span><span class="p">;</span>
  <span class="k">union</span> <span class="n">sa</span> <span class="p">{</span>
    <span class="k">struct</span> <span class="n">sockaddr_in</span> <span class="n">sin</span><span class="p">;</span>
    <span class="k">struct</span> <span class="n">sockaddr_in6</span> <span class="n">sin6</span><span class="p">;</span>
  <span class="p">}</span> <span class="n">sa</span><span class="p">;</span>
  <span class="k">volatile</span> <span class="kt">int</span> <span class="n">lock</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
  <span class="kt">short</span> <span class="n">slot</span><span class="p">,</span> <span class="n">ref</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">struct</span> <span class="n">address</span> <span class="p">{</span>
  <span class="kt">int</span> <span class="n">family</span><span class="p">;</span>
  <span class="kt">unsigned</span> <span class="n">scopeid</span><span class="p">;</span>
  <span class="kt">uint8_t</span> <span class="n">addr</span><span class="p">[</span><span class="mi">16</span><span class="p">];</span>
  <span class="kt">int</span> <span class="n">sortkey</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">struct</span> <span class="n">service</span> <span class="p">{</span>
  <span class="kt">uint16_t</span> <span class="n">port</span><span class="p">;</span>
  <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">proto</span><span class="p">,</span> <span class="n">socktype</span><span class="p">;</span>
<span class="p">};</span>

<span class="cp">#define MAXNS 3
</span><span class="k">struct</span> <span class="n">resolvconf</span> <span class="p">{</span>
  <span class="k">struct</span> <span class="n">address</span> <span class="n">ns</span><span class="p">[</span><span class="n">MAXNS</span><span class="p">];</span>
  <span class="kt">unsigned</span> <span class="n">nns</span><span class="p">,</span> <span class="n">attempts</span><span class="p">,</span> <span class="n">ndots</span><span class="p">;</span>
  <span class="kt">unsigned</span> <span class="n">timeout</span><span class="p">;</span>
<span class="p">};</span>

<span class="cm">/* The limit of 48 results is a non-sharp bound on the number of addresses
 * that can fit in one 512-byte DNS packet full of v4 results and a second
 * packet full of v6 results. Due to headers, the actual limit is lower. */</span>
<span class="cp">#define MAXADDRS 48
#define MAXSERVS 2
</span></code></pre></div></div>

<p>These structures show the constraints and design choices in musl-libc:</p>
<ul>
  <li>Limited to 3 nameservers (MAXNS)</li>
  <li>Maximum of 48 addresses per lookup (MAXADDRS)</li>
  <li>Maximum of 2 services per lookup (MAXSERVS)</li>
</ul>

<h2 id="practical-implications">Practical Implications</h2>

<h3 id="performance-considerations">Performance Considerations</h3>

<ol>
  <li><strong>Query Parallelism</strong>:
    <ul>
      <li>musl-libc’s parallel querying of nameservers can provide faster responses in environments with multiple nameservers</li>
      <li>glibc’s sequential approach may be more reliable in certain network conditions but potentially slower</li>
    </ul>
  </li>
  <li><strong>Memory Usage</strong>:
    <ul>
      <li>musl-libc generally uses less memory due to its simpler implementation</li>
      <li>glibc’s more complex implementation requires more memory but provides more features</li>
    </ul>
  </li>
  <li><strong>Configuration Flexibility</strong>:
    <ul>
      <li>glibc offers more configuration options through multiple configuration files</li>
      <li>musl-libc’s simpler approach may be easier to understand but less flexible</li>
    </ul>
  </li>
</ol>

<h3 id="compatibility-issues">Compatibility Issues</h3>

<ol>
  <li><strong>Search Domain Handling</strong>:
    <ul>
      <li>Applications that rely on glibc’s behavior of falling back to search domains for all queries may not work as expected with musl-libc</li>
      <li>This can cause issues in environments where fully qualified domain names are not used consistently</li>
    </ul>
  </li>
  <li><strong>DNS Extensions</strong>:
    <ul>
      <li>Applications requiring DNSSEC or returning large numbers of records may have issues with older versions of musl-libc due to the lack of TCP support</li>
    </ul>
  </li>
  <li><strong>Configuration Options</strong>:
    <ul>
      <li>Applications that rely on <code class="language-plaintext highlighter-rouge">single-request</code> or <code class="language-plaintext highlighter-rouge">single-request-reopen</code> options will not work as expected with musl-libc</li>
    </ul>
  </li>
</ol>

<h2 id="impact-on-coredns">Impact on CoreDNS</h2>

<p>Based on above research , the differences between glibc and musl-libc DNS resolvers have significant implications for CoreDNS load in Kubernetes environments:</p>

<ol>
  <li>
    <p><strong>Query Amplification</strong>: musl-libc’s parallel DNS querying behavior can multiply the number of simultaneous queries to CoreDNS by a factor of 2-10x compared to glibc’s sequential approach.</p>
  </li>
  <li>
    <p><strong>Resource Consumption</strong>: This amplification directly translates to higher CPU and memory utilization in CoreDNS pods, potentially leading to performance degradation or service disruptions.</p>
  </li>
  <li>
    <p><strong>Scaling Challenges</strong>: Clusters with many Alpine/musl-based containers require more careful CoreDNS scaling and resource allocation than those primarily using glibc-based images.</p>
  </li>
  <li>
    <p><strong>Configuration Sensitivity</strong>: Certain Kubernetes DNS configurations (like “search .” in resolv.conf) can cause complete DNS resolution failure in musl-libc containers.</p>
  </li>
</ol>

<h2 id="analysis-of-coredns-load-impact">Analysis of CoreDNS Load Impact</h2>

<h3 id="query-pattern-differences">Query Pattern Differences</h3>

<p>When a pod needs to resolve a domain name:</p>

<ul>
  <li><strong>glibc</strong>: Queries nameservers sequentially and stops after receiving a positive or negative answer</li>
  <li><strong>musl-libc</strong>: Queries all nameservers in parallel and returns the fastest response</li>
</ul>

<p>This fundamental difference means that for each DNS lookup:</p>
<ul>
  <li>A glibc-based container generates 1 query at a time to CoreDNS</li>
  <li>A musl-libc-based container generates N queries simultaneously (where N is the number of nameservers)</li>
</ul>

<h3 id="search-domain-multiplication">Search Domain Multiplication</h3>

<p>Kubernetes configures pods with search domains and ndots settings that further amplify this difference:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Typical Kubernetes pod /etc/resolv.conf
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
</code></pre></div></div>

<p>For a request to “www.example.com”:</p>
<ul>
  <li>Both resolvers will try multiple search domains if the domain has fewer dots than ndots</li>
  <li>For domains with at least ndots dots:
    <ul>
      <li>glibc tries the literal domain first, then falls back to search domains if not found</li>
      <li>musl-libc only tries the literal domain (never falling back to search domains)</li>
    </ul>
  </li>
</ul>

<p>This means:</p>
<ul>
  <li>For simple service names: both generate multiple queries</li>
  <li>For FQDNs: musl-libc may fail to resolve certain internal domains that glibc would resolve</li>
</ul>

<h3 id="real-world-impact-measurements">Real-world Impact Measurements</h3>

<p>According to real-world observations documented in the referenced sources:</p>

<ol>
  <li>
    <p><strong>Query Volume</strong>: Clusters with predominantly Alpine/musl-based containers can experience 2-10x higher query volume to CoreDNS compared to similar clusters with glibc-based containers.</p>
  </li>
  <li>
    <p><strong>CPU Utilization</strong>: CoreDNS pods in musl-heavy clusters typically show 30-50% higher CPU utilization than in glibc-heavy clusters of similar size.</p>
  </li>
  <li>
    <p><strong>Failure Rates</strong>: During high-load periods, musl-heavy clusters are more likely to experience DNS resolution timeouts due to CoreDNS resource exhaustion.</p>
  </li>
  <li>
    <p><strong>Scaling Requirements</strong>: Clusters with many musl-based containers often require a higher CoreDNS-to-node ratio, and depends on the your pods’ DNS requirements.</p>
  </li>
</ol>

<h2 id="specific-kubernetes-issues">Specific Kubernetes Issues</h2>

<h3 id="kubernetes-issue-112135">Kubernetes Issue #112135</h3>

<p>A critical issue documented in Kubernetes issue #112135 shows how musl-based DNS resolution can completely break in Kubernetes v1.25.0 with certain configurations:</p>

<ul>
  <li>When systemd populates “search .” in resolv.conf (which happens when the hostname is a FQDN)</li>
  <li>And Kubelet propagates that “search .” into pods (which started happening in v1.25.0)</li>
  <li>musl-libc based containers (Alpine, Busybox-musl, etc.) fail to resolve any DNS queries</li>
</ul>

<p>This issue demonstrates how the different resolver behaviors can lead to complete service disruption in certain configurations.</p>

<h3 id="nodelocal-dnscache-effectiveness">NodeLocal DNSCache Effectiveness</h3>

<p>The effectiveness of NodeLocal DNSCache is also impacted by resolver differences:</p>

<ul>
  <li><strong>With glibc</strong>: NodeLocal DNSCache is highly effective, as sequential queries benefit from cached responses</li>
  <li><strong>With musl-libc</strong>: The parallel query pattern can reduce cache hit rates, as multiple simultaneous queries may arrive before the cache is populated</li>
</ul>

<p>As noted in the <a href="https://www.contentful.com/blog/coredns-nodecache-blog/">Contentful blog post</a>, this is particularly problematic because:</p>
<blockquote>
  <p>“If you have some containers that use Alpine like many do, this is not possible because Alpine uses Musl instead of Glibc, which doesn’t support disabling IPv6.”</p>
</blockquote>

<h2 id="best-practices-for-managing-coredns-load">Best Practices for Managing CoreDNS Load</h2>

<p>Based on the findings, here are actionable best practices for managing CoreDNS load in Kubernetes environments with mixed resolver types:</p>

<h3 id="1-optimize-container-image-selection">1. Optimize Container Image Selection</h3>

<ul>
  <li>For DNS-intensive workloads, prefer glibc-based images (Debian, Ubuntu, CentOS) over Alpine/musl-based images</li>
  <li>Consider the DNS query pattern when selecting base images for microservices</li>
  <li>Document and communicate the DNS implications of image choices to development teams</li>
</ul>

<h3 id="2-implement-nodelocal-dnscache">2. Implement NodeLocal DNSCache</h3>

<ul>
  <li>Deploy NodeLocal DNSCache to reduce the number of queries reaching CoreDNS</li>
  <li>Configure high availability for NodeLocal DNSCache to prevent DNS resolution failures during updates</li>
  <li>Monitor cache hit rates to evaluate effectiveness</li>
</ul>

<h3 id="3-scale-coredns-appropriately">3. Scale CoreDNS Appropriately</h3>

<ul>
  <li>Set a minimum of 2 CoreDNS pods for all clusters</li>
  <li>For clusters with many musl-based containers, use a higher CoreDNS-to-node ratio</li>
  <li>Allocate sufficient CPU and memory resources to CoreDNS pods</li>
  <li>Use Keda with custom metrics for scaling operations</li>
</ul>

<h3 id="4-monitor-dns-query-patterns">4. Monitor DNS Query Patterns</h3>

<ul>
  <li>Implement monitoring for CoreDNS query volume, latency, and error rates</li>
  <li>Set alerts for abnormal query patterns or elevated error rates</li>
  <li>Track CoreDNS resource utilization and correlate with container image types</li>
</ul>

<h3 id="5-configure-dns-resolution-optimally">5. Configure DNS Resolution Optimally</h3>

<ul>
  <li>Add trailing dots to FQDNs in application code when possible to avoid search domain expansion</li>
  <li>Know your application’s DNS requirements well, and configure <code class="language-plaintext highlighter-rouge">ndots</code> accordingly.</li>
  <li>General Recommendation: Consider using custom DNS policies for pods with different DNS resolution needs</li>
  <li>General Recommendation: Avoid problematic configurations like “search .” in resolv.conf for clusters with musl-based containers</li>
</ul>

<h3 id="6-distribute-coredns-pods-strategically">6. Distribute CoreDNS Pods Strategically</h3>

<ul>
  <li>Spread CoreDNS pods across different nodes and availability zones</li>
  <li>General Recommendation: Consider using node affinity rules to place CoreDNS pods on nodes with sufficient resources</li>
  <li>General Recommendation: For large clusters, consider dedicating specific nodes to CoreDNS to ensure stable performance</li>
</ul>

<h2 id="conclusion">Conclusion</h2>

<p>The DNS lookup implementations in glibc and musl-libc reflect their broader design philosophies:</p>

<ul>
  <li>
    <p><strong>glibc</strong> provides a feature-rich, highly configurable implementation focused on compatibility and supporting a wide range of use cases, at the cost of complexity and higher resource usage.</p>
  </li>
  <li>
    <p><strong>musl-libc</strong> offers a simpler, more efficient implementation focused on standards compliance and performance, at the cost of fewer features and configuration options.</p>
  </li>
  <li>
    <p>The different DNS resolution behaviors between glibc and musl-libc have significant implications for CoreDNS load in Kubernetes environments. The parallel querying approach of musl-libc can substantially increase the query volume to CoreDNS, potentially leading to performance issues in large clusters.</p>
  </li>
</ul>

<p>Generally, understanding these differences is crucial when developing applications that need to work across different Linux distributions, especially those that use musl-libc (like Alpine Linux) instead of the more common glibc.</p>

<p>Specifically, understanding these differences is crucial for properly sizing, configuring, and monitoring CoreDNS in Kubernetes environments, especially in clusters with a mix of glibc and musl-libc based containers. By implementing the recommended best practices, organizations can mitigate the impact of these differences and ensure reliable DNS resolution in their Kubernetes clusters.</p>

<h2 id="references">References</h2>
<ol>
  <li><a href="https://sourceware.org/glibc/wiki/NameResolver">glibc NameResolver documentation</a></li>
  <li><a href="https://wiki.musl-libc.org/functional-differences-from-glibc.html">musl-libc functional differences from glibc</a></li>
  <li><a href="https://github.com/bminor/glibc/tree/master/resolv">glibc source code</a></li>
  <li><a href="https://github.com/bminor/musl/blob/master/src/network">musl-libc source code</a></li>
  <li><a href="https://github.com/kubernetes/kubernetes/issues/112135">Kubernetes Issue #112135- musl-based DNS resolution will break on v1.25.0 in certain configurations</a></li>
  <li><a href="https://ermiaqasemi.me/enhancing-dns-efficiency-for-smoother-kubernetes-clusters-d0b565ec5db8">“Enhancing DNS Efficiency for Smoother Kubernetes Clusters” by Ermia Qasemi</a></li>
  <li><a href="https://www.contentful.com/blog/coredns-nodecache-blog/">“Creating greater reliability: CoreDNS-nodecache” by Contentful</a></li>
  <li><a href="https://www.alibabacloud.com/help/en/ack/ack-managed-and-ack-dedicated/user-guide/dns-best-practices">“Best practices for DNS services” by Alibaba Cloud Container Service for Kubernetes</a></li>
  <li><a href="https://povilasv.me/understanding-dns-in-kubernetes/">“Understanding DNS in Kubernetes” by Povilas Versockas</a></li>
  <li><a href="https://jpetazzo.github.io/2024/05/12/understanding-kubernetes-dns-hostnetwork-dnspolicy-dnsconfigforming/">“Understanding DNS resolution on Linux and Kubernetes” by Jérôme Petazzoni</a></li>
</ol>]]></content><author><name>M Samuel Vijaykumar</name></author><category term="kubernetes, glibc, musl, dns" /><summary type="html"><![CDATA[ASIDE: Love the way hare-lang has implemented DNS lookups, it’s so clean to read and understand. Just to note, they have followed the musl-libc way of implementing, but only cleaner :).]]></summary></entry><entry><title type="html">asdf goes go</title><link href="https://samof76.github.io/adsf-goes-go.html" rel="alternate" type="text/html" title="asdf goes go" /><published>2025-05-11T00:00:00+00:00</published><updated>2025-05-11T00:00:00+00:00</updated><id>https://samof76.github.io/adsf-goes-go</id><content type="html" xml:base="https://samof76.github.io/adsf-goes-go.html"><![CDATA[<p>I use <a href="https://asdf-vm.com/">asdf</a> for installing and managing all my package versions, and recently with the realse of 0.16.0 version, the asdf moved to an entirely go with single binary installation. Since I was already using the asdf, and use them on multiple machine, this records how to upgrade from the legacy asdf to the new asdf.</p>

<h2 id="download-asdf">Download asdf</h2>

<p>The moment I was writing this, the current version of asdf was <code class="language-plaintext highlighter-rouge">v0.16.7</code> and since I use the x86_64 architecture with Linux; downloading the right executable looked like this.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pushd /tmp
# Download the asdf tarball (which you already have)
wget https://github.com/asdf-vm/asdf/releases/download/v0.16.7/asdf-v0.16.7-linux-amd64.tar.gz
# Download the MD5 checksum file
wget https://github.com/asdf-vm/asdf/releases/download/v0.16.7/asdf-v0.16.7-linux-amd64.tar.gz.md5
# Compare the calculated MD5 with the expected MD5
echo "$(cat asdf-v0.16.7-linux-amd64.tar.gz.md5)  asdf-v0.16.7-linux-amd64.tar.gz" | md5sum -c
# If `OK` then do the following
tar -xvf asdf-v0.16.7-linux-amd64.tar.gz
cp asdf ~/.local/bin
chmod +x ~/.local/bin
popd
</code></pre></div></div>

<p>That takes care of install it to the <code class="language-plaintext highlighter-rouge">PATH</code>. But since I have already got legacy installation I would want to reuse already installed tools.</p>

<h2 id="setting-up-the-environment">Setting up the environment</h2>

<p>Now that I asdf in my <code class="language-plaintext highlighter-rouge">PATH</code>, I would need to setup my <code class="language-plaintext highlighter-rouge">fish</code> shell environment to use the latest asdf but the existing installed and versioned tools. Here we go.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>echo "set -gx ASDF_DATA_DIR /home/msv/.asdf" &gt;&gt; ~/.config/fish/config.fish
echo "set -gx PATH $ASDF_DATA_DIR/shims $PATH" &gt;&gt; ~/.config/fish/config.fish
</code></pre></div></div>

<p>Now to source it.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>source ~/.config/fish/config.fish
</code></pre></div></div>

<blockquote>
  <p>IMPORTANT: There are many breacking changes expected so we need to be wary about them. <a href="https://asdf-vm.com/guide/upgrading-to-v0-16#breaking-changes">Here</a> is the complete list.</p>
</blockquote>]]></content><author><name>M Samuel Vijaykumar</name></author><category term="asdf, go, setup, fish" /><summary type="html"><![CDATA[I use asdf for installing and managing all my package versions, and recently with the realse of 0.16.0 version, the asdf moved to an entirely go with single binary installation. Since I was already using the asdf, and use them on multiple machine, this records how to upgrade from the legacy asdf to the new asdf.]]></summary></entry><entry><title type="html">CPUManagerPolicy Under the Hood</title><link href="https://samof76.github.io/cpumanagerpolicy-under-the-hood.html" rel="alternate" type="text/html" title="CPUManagerPolicy Under the Hood" /><published>2025-03-23T00:00:00+00:00</published><updated>2025-03-23T00:00:00+00:00</updated><id>https://samof76.github.io/cpumanagerpolicy-under-the-hood</id><content type="html" xml:base="https://samof76.github.io/cpumanagerpolicy-under-the-hood.html"><![CDATA[<blockquote>
  <p>WARNING: There are known issues with using this method, that might cause diruptions in some edge cases and are yet to be fixed. Here are the <a href="https://github.com/orgs/kubernetes/projects/185/views/1?filterQuery=cpumanager">bugs</a> that are being tracked.</p>
</blockquote>

<p>According to the documentation, when <code class="language-plaintext highlighter-rouge">static</code> CPUManager is used, the <a href="https://docs.kernel.org/admin-guide/cgroup-v1/cpusets.html"><code class="language-plaintext highlighter-rouge">cpuset</code> cgroup controller</a> is used to assign specific cores to the container with guaranteed CPU resources. This ensures that the container has exclusive access to the specified cores, preventing interference from other processes, reducing noisy neighbor issues and also ensuring the container itself is not noisy neighor. First a look at the section where this configuration is defined in <code class="language-plaintext highlighter-rouge">kubelet-config.json</code>.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@ip-w-x-y-z ~]# <span class="nb">cat</span> /etc/kubernetes/kubelet/kubelet-config.json
...
  <span class="s2">"cpuManagerPolicy"</span>: <span class="s2">"static"</span>,
  <span class="s2">"cpuManagerPolicyOptions"</span>: <span class="o">{</span>
    <span class="s2">"full-pcpus-only"</span>: <span class="s2">"true"</span>
  <span class="o">}</span>,
...
</code></pre></div></div>

<p>And this could be analyzed by inspecting the processes in question. In this case the process we are concerned about is <code class="language-plaintext highlighter-rouge">clickhouse</code>, we can use the <code class="language-plaintext highlighter-rouge">crictl ps</code> command to get the container ID of the process.</p>

<p>First lets take a look at get an node where we have not enabled CPUManager, let’s the container ID of clickhouse.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@ip-a-b-c-d ~]# crictl ps <span class="nt">--name</span> clickhouse
CONTAINER           IMAGE               CREATED             STATE               NAME                      ATTEMPT             POD ID              POD
29ebb158a0a16       2ac35038b7301       3 hours ago         Running             clickhouse-backup         0                   21cea07c4139a       chi-clickhouse-vcputest-0-0-0
0921f5a6a8ab2       639814aaef121       3 hours ago         Running             clickhouse                0                   21cea07c4139a       chi-clickhouse-vcputest-0-0-0
</code></pre></div></div>

<p>Notice, <code class="language-plaintext highlighter-rouge">0921f5a6a8ab2</code> is <code class="language-plaintext highlighter-rouge">ID</code> of the <code class="language-plaintext highlighter-rouge">clickhouse</code> container.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@ip-a-b-c-d ~]# crictl inspect 0921f5a6a8ab2 | jq .status.resources.linux
<span class="o">{</span>
  <span class="s2">"cpuPeriod"</span>: <span class="s2">"100000"</span>,
  <span class="s2">"cpuQuota"</span>: <span class="s2">"500000"</span>,
  <span class="s2">"cpuShares"</span>: <span class="s2">"5120"</span>,
  <span class="s2">"cpusetCpus"</span>: <span class="s2">""</span>,
  <span class="s2">"cpusetMems"</span>: <span class="s2">""</span>,
  <span class="s2">"hugepageLimits"</span>: <span class="o">[]</span>,
  <span class="s2">"memoryLimitInBytes"</span>: <span class="s2">"8589934592"</span>,
  <span class="s2">"memorySwapLimitInBytes"</span>: <span class="s2">"8589934592"</span>,
  <span class="s2">"oomScoreAdj"</span>: <span class="s2">"-997"</span>,
  <span class="s2">"unified"</span>: <span class="o">{}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Notice in this case, where CPUManager is not used/enabled, the <code class="language-plaintext highlighter-rouge">cpusetCpus</code> is empty. This means that the container is not restricted to specific cores and can use any available CPU resources. Also, note cpu_manager_state.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@ip-a-b-c-d ~]# <span class="nb">cat</span> /var/lib/kubelet/cpu_manager_state | jq <span class="nb">.</span>
<span class="o">{</span>
  <span class="s2">"policyName"</span>: <span class="s2">"none"</span>,
  <span class="s2">"defaultCpuSet"</span>: <span class="s2">""</span>,
  <span class="s2">"checksum"</span>: 1353318690
<span class="o">}</span>
</code></pre></div></div>

<p>Now, lets take a look at a node where we have enabled CPUManager. Let’s take a look at the <code class="language-plaintext highlighter-rouge">clickhouse</code> container on this node.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@ip-w-x-y-z ~]# crictl ps <span class="nt">--name</span> clickhouse
CONTAINER           IMAGE               CREATED             STATE               NAME                ATTEMPT             POD ID              POD
e166cc07430cd       2ac35038b7301       5 hours ago         Running             clickhouse-backup   0                   4f28ab9656be0       chi-clickhouse-pcputest-0-0-0
27f87a744a759       639814aaef121       5 hours ago         Running             clickhouse          0                   4f28ab9656be0       chi-clickhouse-pcputest-0-0-0
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">27f87a744a759</code> is our container of interest.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@ip-w-x-y-z ~]# crictl inspect 27f87a744a759 | jq .status.resources.linux
<span class="o">{</span>
  <span class="s2">"cpuPeriod"</span>: <span class="s2">"100000"</span>,
  <span class="s2">"cpuQuota"</span>: <span class="s2">"500000"</span>,
  <span class="s2">"cpuShares"</span>: <span class="s2">"5120"</span>,
  <span class="s2">"cpusetCpus"</span>: <span class="s2">"1-5"</span>,
  <span class="s2">"cpusetMems"</span>: <span class="s2">""</span>,
  <span class="s2">"hugepageLimits"</span>: <span class="o">[]</span>,
  <span class="s2">"memoryLimitInBytes"</span>: <span class="s2">"8589934592"</span>,
  <span class="s2">"memorySwapLimitInBytes"</span>: <span class="s2">"8589934592"</span>,
  <span class="s2">"oomScoreAdj"</span>: <span class="s2">"-997"</span>,
  <span class="s2">"unified"</span>: <span class="o">{}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Notice in this case, where CPUManager is used/enabled, the <code class="language-plaintext highlighter-rouge">cpusetCpus</code> is set to <code class="language-plaintext highlighter-rouge">1-5</code>. This means that the container is restricted to specific cores and can only use the specified cores, and those CPU cores are dedicated to the container. And the <code class="language-plaintext highlighter-rouge">cpu_manager_state</code> is also set to <code class="language-plaintext highlighter-rouge">1-5</code>.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@ip-w-x-y-z ~]# <span class="nb">cat</span> /var/lib/kubelet/cpu_manager_state | jq <span class="nb">.</span>
<span class="o">{</span>
  <span class="s2">"policyName"</span>: <span class="s2">"static"</span>,
  <span class="s2">"defaultCpuSet"</span>: <span class="s2">"0,6-7"</span>,
  <span class="s2">"entries"</span>: <span class="o">{</span>
    <span class="s2">"d8d68b09-ce62-455c-88ba-57050e4169f0"</span>: <span class="o">{</span>
      <span class="s2">"clickhouse"</span>: <span class="s2">"1-5"</span>
    <span class="o">}</span>
  <span class="o">}</span>,
  <span class="s2">"checksum"</span>: 2344224312
<span class="o">}</span>
</code></pre></div></div>

<h2 id="what-is-varlibkubeletcpu_manager_state">What is <code class="language-plaintext highlighter-rouge">/var/lib/kubelet/cpu_manager_state</code>?</h2>

<p>The <code class="language-plaintext highlighter-rouge">/var/lib/kubelet/cpu_manager_state</code> file is a key component of Kubernetes’ CPU management system. By inspecting this file, you can verify that CPU allocations are being handled correctly and consistently. When combined with containerd’s implementation of CPU pinning via cgroups, this ensures that your Pods receive the exclusive CPU resources they require, especially when using the <code class="language-plaintext highlighter-rouge">full-pcpus-only</code> policy. Also this file maintains the state of CPU allocations across kubelet restarts, ensuring that CPU assignments are consistent and persistent.</p>

<h2 id="verifying-pcpu-is-not-shared">Verifying pcpu is not shared</h2>

<p>To verify the CPUs provided to the guaranteed pod’s clickhouse container, we did the following experiment:</p>

<ol>
  <li>Created the ClickHouse pod with guaranteed resources</li>
  <li>Created a burstable pod running <code class="language-plaintext highlighter-rouge">stress-ng</code> container utilizing cores beyond the limit set</li>
</ol>

<p>In this case the expected behavior is that burstable pod’s <code class="language-plaintext highlighter-rouge">stress-ng</code> container will not be able to utilize the CPU cores allocated to the guaranteed pod’s clickhouse container.</p>

<p>Here is the <code class="language-plaintext highlighter-rouge">/var/lib/kubelet/cpu_manager_state</code> on that node.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@ip-w-x-y-z ~]# <span class="nb">cat</span> /var/lib/kubelet/cpu_manager_state | jq <span class="nb">.</span>
<span class="o">{</span>
  <span class="s2">"policyName"</span>: <span class="s2">"static"</span>,
  <span class="s2">"defaultCpuSet"</span>: <span class="s2">"0,4-7"</span>,
  <span class="s2">"entries"</span>: <span class="o">{</span>
    <span class="s2">"54b3c938-b248-4373-992d-adec7ea2692a"</span>: <span class="o">{</span>
      <span class="s2">"clickhouse"</span>: <span class="s2">"1-3"</span>
    <span class="o">}</span>
  <span class="o">}</span>,
  <span class="s2">"checksum"</span>: 1600324503
<span class="o">}</span>
</code></pre></div></div>

<p>And there is the pod yaml for the <code class="language-plaintext highlighter-rouge">stress-ng</code>.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Pod</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">stress-ng</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">stress-test</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">nodeSelector</span><span class="pi">:</span>
      <span class="na">node.kubernetes.io/instance-type</span><span class="pi">:</span> <span class="s">r7g.2xlarge</span>
      <span class="na">karpenter.sh/nodepool</span><span class="pi">:</span> <span class="s">arm-cpumanager-nodepool</span>
      <span class="na">kubernetes.io/hostname</span><span class="pi">:</span> <span class="s">ip-w-x-y-z.ec2.internal</span>
  <span class="na">containers</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">stress-ng</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">alpine:latest</span>
    <span class="na">command</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">sh</span>
    <span class="pi">-</span> <span class="s">-c</span>
    <span class="pi">-</span> <span class="s">apk add --no-cache stress-ng &amp;&amp; stress-ng --cpu 4 --timeout 300s</span>
    <span class="na">resources</span><span class="pi">:</span>
      <span class="na">limits</span><span class="pi">:</span>
        <span class="na">cpu</span><span class="pi">:</span> <span class="m">3</span>
        <span class="na">memory</span><span class="pi">:</span> <span class="s">8Gi</span>
      <span class="na">requests</span><span class="pi">:</span>
        <span class="na">cpu</span><span class="pi">:</span> <span class="m">2</span>
        <span class="na">memory</span><span class="pi">:</span> <span class="s">8Gi</span>
  <span class="na">restartPolicy</span><span class="pi">:</span> <span class="s">Never</span>
</code></pre></div></div>

<p>So noticed the following using <code class="language-plaintext highlighter-rouge">htop</code>, the <code class="language-plaintext highlighter-rouge">stress-ng</code> container was not able to utilize the CPU cores allocated to the guaranteed pod’s clickhouse container.</p>

<p><img src="assets/images/cpumanager_full_pcpu_htop.png" alt="Stress-NG Outside PCPU" /></p>

<p><strong>ALSO</strong>, we flip the switch to check if a container inside of guaranteed pod only uses the CPU that it is provide using cpuset. So we run <code class="language-plaintext highlighter-rouge">stress-ng</code> as a guaranteed pod.</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
<span class="na">kind</span><span class="pi">:</span> <span class="s">Pod</span>
<span class="na">metadata</span><span class="pi">:</span>
  <span class="na">name</span><span class="pi">:</span> <span class="s">stress-ng</span>
  <span class="na">namespace</span><span class="pi">:</span> <span class="s">stress-test</span>
<span class="na">spec</span><span class="pi">:</span>
  <span class="na">nodeSelector</span><span class="pi">:</span>
      <span class="na">node.kubernetes.io/instance-type</span><span class="pi">:</span> <span class="s">r7g.2xlarge</span>
      <span class="na">karpenter.sh/nodepool</span><span class="pi">:</span> <span class="s">arm-cpumanager-nodepool</span>
      <span class="na">kubernetes.io/hostname</span><span class="pi">:</span> <span class="s">ip-w-x-y-z.ec2.internal</span>
  <span class="na">containers</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">stress-ng</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">alpine:latest</span>
    <span class="na">command</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">sh</span>
    <span class="pi">-</span> <span class="s">-c</span>
    <span class="pi">-</span> <span class="s">apk add --no-cache stress-ng &amp;&amp; stress-ng --cpu 4 --timeout 300s</span>
    <span class="na">resources</span><span class="pi">:</span>
      <span class="na">limits</span><span class="pi">:</span>
        <span class="na">cpu</span><span class="pi">:</span> <span class="m">3</span>
        <span class="na">memory</span><span class="pi">:</span> <span class="s">4Gi</span>
      <span class="na">requests</span><span class="pi">:</span>
        <span class="na">cpu</span><span class="pi">:</span> <span class="m">3</span>
        <span class="na">memory</span><span class="pi">:</span> <span class="s">4Gi</span>
  <span class="na">restartPolicy</span><span class="pi">:</span> <span class="s">Never</span>
</code></pre></div></div>

<p>Check the cpu assignment for this container.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>root@ip-w-x-y-z ~]# <span class="nb">cat</span> /var/lib/kubelet/cpu_manager_state | jq <span class="nb">.</span>
<span class="o">{</span>
  <span class="s2">"policyName"</span>: <span class="s2">"static"</span>,
  <span class="s2">"defaultCpuSet"</span>: <span class="s2">"0,4-7"</span>,
  <span class="s2">"entries"</span>: <span class="o">{</span>
    <span class="s2">"c2748791-0c91-4c75-9adb-cbc9d7cd1855"</span>: <span class="o">{</span>
      <span class="s2">"stress-ng"</span>: <span class="s2">"1-3"</span>
    <span class="o">}</span>
  <span class="o">}</span>,
  <span class="s2">"checksum"</span>: 1477588470
<span class="o">}</span>
</code></pre></div></div>

<p>Here is the htop output, notice only assigned CPUs are used.</p>

<p><img src="assets/images/cpumanager_full_pcp_htop2.png" alt="Stress-NG Inside PCPU" /></p>]]></content><author><name>M Samuel Vijaykumar</name></author><category term="kubernetes, kubelet, cpu-manager, cpu-manager-policy" /><summary type="html"><![CDATA[WARNING: There are known issues with using this method, that might cause diruptions in some edge cases and are yet to be fixed. Here are the bugs that are being tracked.]]></summary></entry><entry><title type="html">Helix for Code Reading</title><link href="https://samof76.github.io/helix-for-code-reading.html" rel="alternate" type="text/html" title="Helix for Code Reading" /><published>2025-02-22T00:00:00+00:00</published><updated>2025-02-22T00:00:00+00:00</updated><id>https://samof76.github.io/helix-for-code-reading</id><content type="html" xml:base="https://samof76.github.io/helix-for-code-reading.html"><![CDATA[<p>While looking through the Kubernetes codebase to look at <a href="https://samof76.space/kubelet-initialization.html">code for kubelet initialization</a>, I wanted lesser distractions and wanted something lighter, <a href="https://helix-editor.com/">Helix</a> was the perfect choice. Helix is a lightweight text editor that is designed for code reading and editing. It has a minimalistic interface that is easy to navigate and has a fast startup time. Helix excellent language server support and also helps in debugging(while I have not used it extensively yet).</p>

<h2 id="installation">Installation</h2>

<p>To install Helix, you can use the following command:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>add-apt-repository ppa:maveonair/helix-editor
<span class="nb">sudo </span>apt update
<span class="nb">sudo </span>apt <span class="nb">install </span>helix
</code></pre></div></div>

<blockquote>
  <p>NOTE: Helix installation is supported for many Linux Distros, you can check them all out <a href="https://docs.helix-editor.com/package-managers.html">here</a>.</p>
</blockquote>

<p>The above command will install Helix into your system path, to be accessible using the <code class="language-plaintext highlighter-rouge">hx</code> command. Helix is got an interesting command options <code class="language-plaintext highlighter-rouge">--health</code> that displays language server support.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>hx <span class="nt">--health</span>
Config file: default
Language file: default
Log file: /home/samof76/.cache/helix/helix.log
Runtime directories: /home/samof76/.config/helix/runtime<span class="p">;</span>/usr/lib/helix/runtime<span class="p">;</span>/usr/lib/helix/runtime
Runtime directory does not exist: /home/samof76/.config/helix/runtime
Clipboard provider: xclip
System clipboard provider: xclip

Language                   LSP                          DAP                             ...
ada                        ✘ ada_language_server       None                            ...
...
go                         ✓ gopls                     ✓ dlv                          ...
python                     ✓ pylsp                     ✓ ptvsd                        ...
...
rust                       ✓ rust_analyzer             ✓ lldb-dap                     ...
...
zig                        ✓ zls                       ✓ lldb-dap                     ...
...
</code></pre></div></div>

<table>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">LSP</code> - Language Server Protocol</td>
      <td><code class="language-plaintext highlighter-rouge">DAP</code> - Debug Adapter Protocol</td>
    </tr>
  </tbody>
</table>

<p>Now <code class="language-plaintext highlighter-rouge">hx</code> could be used for both code traversal and debugging. There are just my notes on what are the various options I used for code traversal. But first lets look at the modes.</p>

<h2 id="modes">Modes</h2>

<p>Helix essentially has three modes:</p>
<ol>
  <li><strong>Normal Mode</strong>: Normal mode is the default mode when you launch helix. You can return to it from other modes by pressing the <code class="language-plaintext highlighter-rouge">Esc</code> key.</li>
  <li><strong>Insert Mode</strong>: The insert mode is used for editing text. You can enter insert mode by pressing the <code class="language-plaintext highlighter-rouge">i</code> key in normal mode.</li>
  <li><strong>Select Mode</strong>: The select mode is used for selecting text. You can enter select mode by pressing the <code class="language-plaintext highlighter-rouge">v</code> key in normal mode.</li>
</ol>

<p>But in this article all that is needed is the <em>normal</em> mode, as I hardly did any editing or selecting text. Before I go there there some interactive elements of helix you should be aware of. There are two interactive elements:</p>
<ol>
  <li><strong>Picker</strong>: Is an interactive element that allows that pops up an internal window to type or select an option. Try <code class="language-plaintext highlighter-rouge">space</code> to open the picker.</li>
  <li><strong>Prompt</strong>: Is an interactive element that allows you to enter text. used like vim <code class="language-plaintext highlighter-rouge">Esc</code> and <code class="language-plaintext highlighter-rouge">:</code>. This mode has a auto-completion feature.</li>
</ol>

<h2 id="interactions-for-traversal">Interactions for Traversal</h2>

<p>The when you open up Helix, passing is directory as an argument.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>hx ~/github/kubernetes
</code></pre></div></div>

<p>It would present you with a list of files and directories in the specified directory. You can navigate through the list using the arrow keys or type in the name of the file or directory, it does an fzf kind of fuzzy matching. Press <code class="language-plaintext highlighter-rouge">Enter</code> to open the file. Assuming I type <code class="language-plaintext highlighter-rouge">kubelet.go</code>, the picker will display all fuzzy matched files, then I would select <code class="language-plaintext highlighter-rouge">cmd/kubelet/kubelet.go</code>, and <code class="language-plaintext highlighter-rouge">Enter</code> to open it.</p>

<p>From here you could use arrow keys to navigate through the file. Suppose you wanted to <strong>jump to the definition of the function</strong> <code class="language-plaintext highlighter-rouge">NewKubeletCommand</code>, you can put your cursor at or on the function call, and do…</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>g + d
</code></pre></div></div>

<p>And suppose you wanted to <strong>go back</strong> to where you came from you could just move backward by pressing</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ctrl + o
</code></pre></div></div>

<p>and <strong>go forward</strong> by pressing</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ctrl + i
</code></pre></div></div>

<p>Now suppose you wanted <strong>list all reference</strong> of the function <code class="language-plaintext highlighter-rouge">NewKubeletCommand</code>, you can put your cursor at or on the function call, and do…</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>g + r
</code></pre></div></div>

<p>Suppose you lost your way come want to <strong>navigate through hops</strong>, you could use the following key combination:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>space + j
</code></pre></div></div>

<p>This will list all what Helix calls a <strong>jumplist</strong>, as picker. Now suppose you want to go to an implementation of an interface function, you could use the following key combination:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>g + i
</code></pre></div></div>

<p>Suppose you are aware of symbol, wanted to <strong>search for that symbol</strong> inside the project, you could use the following key combination:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>space + shift(s)
</code></pre></div></div>

<p>Suppose you wanted <strong>copy something into the system clipboard</strong>, you first enter the <em>Select Mode</em> by pressing <code class="language-plaintext highlighter-rouge">v</code>, and then use the following key combination:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;arrow-keys-to-select&gt; + space + shift(y)
</code></pre></div></div>

<p>I guess with this I could get through most of the stuff for writing a my notes on <a href="https://samof76.space/kubelet-initialization.html">kubelet initialization</a>. And one thing for sure Helix is great IDE, with very sane defaults, mouse support and much of it works out of the box; as a matter of fact I really did not configure anything at all. One last thing befor I wrap this article, one command I use is to wrap text, I rececommand you try this…</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>: + set-option soft...
</code></pre></div></div>

<p>Go figure! I am sure you will be pleseantly surprised.</p>]]></content><author><name>M Samuel Vijaykumar</name></author><category term="tools, ide, editor" /><summary type="html"><![CDATA[While looking through the Kubernetes codebase to look at code for kubelet initialization, I wanted lesser distractions and wanted something lighter, Helix was the perfect choice. Helix is a lightweight text editor that is designed for code reading and editing. It has a minimalistic interface that is easy to navigate and has a fast startup time. Helix excellent language server support and also helps in debugging(while I have not used it extensively yet).]]></summary></entry><entry><title type="html">Kubelet Initialization</title><link href="https://samof76.github.io/kubelet-initialization.html" rel="alternate" type="text/html" title="Kubelet Initialization" /><published>2025-02-16T00:00:00+00:00</published><updated>2025-02-16T00:00:00+00:00</updated><id>https://samof76.github.io/kubelet-initialization</id><content type="html" xml:base="https://samof76.github.io/kubelet-initialization.html"><![CDATA[<blockquote>
  <p>Note: This document contains my personal notes from exploring the Kubelet initialization process in Kubernetes. Due to its nature as working notes, it may not follow a strictly formal structure and might contain areas marked for further investigation or clarification. The content is technical and assumes familiarity with Kubernetes internals.</p>
</blockquote>

<blockquote>
  <p><strong>UPDATE:</strong> I used the <a href="https://helix-editor.com/">Helix Editor</a>, and felt it was the perfect tool for walking through code from the terminal. And recently wrote <a href="https://samof76.space/helix-for-code-reading.html">a blog post</a> about it.</p>
</blockquote>

<p>To keep consistency, the code is maintained at …</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git log <span class="nt">--oneline</span> | <span class="nb">head</span> <span class="nt">-1</span>
7bfdda4696f Merge pull request <span class="c">#129380 from jdtuhui/testifylint/len@component-base</span>
</code></pre></div></div>

<p>The Kubelet initialization begins at <code class="language-plaintext highlighter-rouge">cmd/kubelet/kubelet.go: 34</code></p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// cmd/kubelet/kubelet.go</span>
<span class="o">...</span>
<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">command</span> <span class="o">:=</span> <span class="n">app</span><span class="o">.</span><span class="n">NewKubeletCommand</span><span class="p">()</span>
	<span class="n">code</span> <span class="o">:=</span> <span class="n">cli</span><span class="o">.</span><span class="n">Run</span><span class="p">(</span><span class="n">command</span><span class="p">)</span>
	<span class="n">os</span><span class="o">.</span><span class="n">Exit</span><span class="p">(</span><span class="n">code</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">app.NewKubeletCommand()</code> function, defined at line 136 in <code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go</code>, sets defaults using <code class="language-plaintext highlighter-rouge">options.NewKubeletFlags()</code> and <code class="language-plaintext highlighter-rouge">options.NewKubeletConfiguration()</code>.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span>
<span class="k">func</span> <span class="n">NewKubeletCommand</span><span class="p">()</span> <span class="o">*</span><span class="n">cobra</span><span class="o">.</span><span class="n">Command</span> <span class="p">{</span>
	<span class="n">cleanFlagSet</span> <span class="o">:=</span> <span class="n">pflag</span><span class="o">.</span><span class="n">NewFlagSet</span><span class="p">(</span><span class="n">server</span><span class="o">.</span><span class="n">ComponentKubelet</span><span class="p">,</span> <span class="n">pflag</span><span class="o">.</span><span class="n">ContinueOnError</span><span class="p">)</span>
	<span class="n">cleanFlagSet</span><span class="o">.</span><span class="n">SetNormalizeFunc</span><span class="p">(</span><span class="n">cliflag</span><span class="o">.</span><span class="n">WordSepNormalizeFunc</span><span class="p">)</span>
	<span class="n">kubeletFlags</span> <span class="o">:=</span> <span class="n">options</span><span class="o">.</span><span class="n">NewKubeletFlags</span><span class="p">()</span>

	<span class="n">kubeletConfig</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">options</span><span class="o">.</span><span class="n">NewKubeletConfiguration</span><span class="p">()</span>
<span class="o">...</span>
</code></pre></div></div>

<p>Now what are the defaults set in <code class="language-plaintext highlighter-rouge">options.NewKubeletFlags()</code>(more or less initialization)?</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// cmd/kubelet/app/options/options.go: 139</span>
<span class="k">func</span> <span class="n">NewKubeletFlags</span><span class="p">()</span> <span class="o">*</span><span class="n">KubeletFlags</span> <span class="p">{</span>
	<span class="k">return</span> <span class="o">&amp;</span><span class="n">KubeletFlags</span><span class="p">{</span> <span class="c">// declared in cmd/kubelet/app/options/options.go: 54</span>
		<span class="n">ContainerRuntimeOptions</span><span class="o">:</span> <span class="o">*</span><span class="n">NewContainerRuntimeOptions</span><span class="p">(),</span>
		<span class="n">CertDirectory</span><span class="o">:</span>           <span class="s">"/var/lib/kubelet/pki"</span><span class="p">,</span>
		<span class="n">RootDirectory</span><span class="o">:</span>           <span class="n">filepath</span><span class="o">.</span><span class="n">Clean</span><span class="p">(</span><span class="n">defaultRootDir</span><span class="p">),</span> <span class="c">// /var/lib/kubelet</span>
		<span class="n">MaxContainerCount</span><span class="o">:</span>       <span class="o">-</span><span class="m">1</span><span class="p">,</span>
		<span class="n">MaxPerPodContainerCount</span><span class="o">:</span> <span class="m">1</span><span class="p">,</span>
		<span class="n">MinimumGCAge</span><span class="o">:</span>            <span class="n">metav1</span><span class="o">.</span><span class="n">Duration</span><span class="p">{</span><span class="n">Duration</span><span class="o">:</span> <span class="m">0</span><span class="p">},</span>
		<span class="n">RegisterSchedulable</span><span class="o">:</span>     <span class="no">true</span><span class="p">,</span>
		<span class="n">NodeLabels</span><span class="o">:</span>              <span class="nb">make</span><span class="p">(</span><span class="k">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">string</span><span class="p">),</span>
	<span class="p">}</span>
<span class="p">}</span>
<span class="o">...</span>

</code></pre></div></div>

<p><strong>DEEPER DIVE</strong></p>

<p>What are the defaults set in <code class="language-plaintext highlighter-rouge">NewContainerRuntimeOptions()</code>?</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// cmd/kubelet/app/options/container_runtime.go: 36</span>
<span class="o">...</span>
<span class="k">func</span> <span class="n">NewContainerRuntimeOptions</span><span class="p">()</span> <span class="o">*</span><span class="n">config</span><span class="o">.</span><span class="n">ContainerRuntimeOptions</span> <span class="p">{</span>
	<span class="k">return</span> <span class="o">&amp;</span><span class="n">config</span><span class="o">.</span><span class="n">ContainerRuntimeOptions</span><span class="p">{</span> <span class="c">// declared in pkg/kubelet/config/flags.go: 26</span>
		<span class="n">PodSandboxImage</span><span class="o">:</span> <span class="n">defaultPodSandboxImage</span><span class="p">,</span> <span class="c">// k8s.gcr.io/pause:3.10</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>/DEEPER DIVE</strong></p>

<p>Now let’s see what <code class="language-plaintext highlighter-rouge">options.NewKubeletConfiguration()</code> does.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// cmd/kubelet/app/options/options.go: 200</span>
<span class="o">...</span>
<span class="k">func</span> <span class="n">NewKubeletConfiguration</span><span class="p">()</span> <span class="p">(</span><span class="o">*</span><span class="n">kubeletconfig</span><span class="o">.</span><span class="n">KubeletConfiguration</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">scheme</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">kubeletscheme</span><span class="o">.</span><span class="n">NewSchemeAndCodecs</span><span class="p">()</span> <span class="c">// NewSchemeAndCodecs is a utility function that returns a Scheme and CodecFactory</span>
	<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
		<span class="k">return</span> <span class="no">nil</span><span class="p">,</span> <span class="n">err</span>
	<span class="p">}</span>
	<span class="n">versioned</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="n">v1beta1</span><span class="o">.</span><span class="n">KubeletConfiguration</span><span class="p">{}</span> <span class="c">// Lookup staging/src/k8s.io/kubelet/config/v1beta1/types.go: 89</span>
	<span class="n">scheme</span><span class="o">.</span><span class="n">Default</span><span class="p">(</span><span class="n">versioned</span><span class="p">)</span>
	<span class="n">config</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="n">kubeletconfig</span><span class="o">.</span><span class="n">KubeletConfiguration</span><span class="p">{}</span> <span class="c">// Lookup pkg/kubelet/apis/config/types.go: 82</span>
	<span class="k">if</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">scheme</span><span class="o">.</span><span class="n">Convert</span><span class="p">(</span><span class="n">versioned</span><span class="p">,</span> <span class="n">config</span><span class="p">,</span> <span class="no">nil</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span> <span class="c">// Convert will attempt to convert in into out. Both must be pointers</span>
		<span class="k">return</span> <span class="no">nil</span><span class="p">,</span> <span class="n">err</span>
	<span class="p">}</span>
	<span class="n">applyLegacyDefaults</span><span class="p">(</span><span class="n">config</span><span class="p">)</span> <span class="c">// applies legacy default values to the KubeletConfiguration</span>
	<span class="k">return</span> <span class="n">config</span><span class="p">,</span> <span class="no">nil</span>
<span class="p">}</span>

<span class="o">...</span>
</code></pre></div></div>

<blockquote>
  <p>The kubelet configuration has <a href="https://kubernetes.io/docs/reference/config-api/kubelet-config.v1beta1/">these options</a></p>
</blockquote>

<p><strong>DEEPER DIVE</strong></p>

<p>The <code class="language-plaintext highlighter-rouge">applyLegacyDefaults</code> function is defined in <code class="language-plaintext highlighter-rouge">cmd/kubelet/app/options/options.go: 218</code>.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">applyLegacyDefaults</span><span class="p">(</span><span class="n">kc</span> <span class="o">*</span><span class="n">kubeletconfig</span><span class="o">.</span><span class="n">KubeletConfiguration</span><span class="p">)</span> <span class="p">{</span>
	<span class="c">// --anonymous-auth</span>
	<span class="n">kc</span><span class="o">.</span><span class="n">Authentication</span><span class="o">.</span><span class="n">Anonymous</span><span class="o">.</span><span class="n">Enabled</span> <span class="o">=</span> <span class="no">true</span>
	<span class="c">// --authentication-token-webhook</span>
	<span class="n">kc</span><span class="o">.</span><span class="n">Authentication</span><span class="o">.</span><span class="n">Webhook</span><span class="o">.</span><span class="n">Enabled</span> <span class="o">=</span> <span class="no">false</span>
	<span class="c">// --authorization-mode</span>
	<span class="n">kc</span><span class="o">.</span><span class="n">Authorization</span><span class="o">.</span><span class="n">Mode</span> <span class="o">=</span> <span class="n">kubeletconfig</span><span class="o">.</span><span class="n">KubeletAuthorizationModeAlwaysAllow</span>
	<span class="c">// --read-only-port</span>
	<span class="n">kc</span><span class="o">.</span><span class="n">ReadOnlyPort</span> <span class="o">=</span> <span class="n">ports</span><span class="o">.</span><span class="n">KubeletReadOnlyPort</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>/DEEPER DIVE</strong></p>

<p>Going further on in function <code class="language-plaintext highlighter-rouge">NewKubeletConfiguration()</code>, we would notice that it setup the CLI using <code class="language-plaintext highlighter-rouge">Cobra</code> and <code class="language-plaintext highlighter-rouge">Pflag</code>.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// cmd/kubelet/app/server.go: 148...</span>
	<span class="n">cmd</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="n">cobra</span><span class="o">.</span><span class="n">Command</span><span class="p">{</span>
		<span class="n">Use</span><span class="o">:</span> <span class="n">server</span><span class="o">.</span><span class="n">ComponentKubelet</span><span class="p">,</span>
		<span class="n">Long</span><span class="o">:</span> <span class="s">`The kubelet is the primary "node agent" that runs on each
...
</span></code></pre></div></div>

<p>Then the following validates the initial KubeletFlags</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// cmd/kubelet/app/server.go: 205</span>
			<span class="k">if</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">options</span><span class="o">.</span><span class="n">ValidateKubeletFlags</span><span class="p">(</span><span class="n">kubeletFlags</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
				<span class="k">return</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Errorf</span><span class="p">(</span><span class="s">"failed to validate kubelet flags: %w"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
			<span class="p">}</span>
</code></pre></div></div>

<p>Then the following loads the configuration file if it is provided in <code class="language-plaintext highlighter-rouge">kubeletFlags</code></p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// cmd/kubelet/app/server.go: 215</span>
			<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">kubeletFlags</span><span class="o">.</span><span class="n">KubeletConfigFile</span><span class="p">)</span> <span class="o">&gt;</span> <span class="m">0</span> <span class="p">{</span> <span class="c">// comes from the `--config` flag</span>
				<span class="n">kubeletConfig</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">loadConfigFile</span><span class="p">(</span><span class="n">kubeletFlags</span><span class="o">.</span><span class="n">KubeletConfigFile</span><span class="p">)</span>
				<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
					<span class="k">return</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Errorf</span><span class="p">(</span><span class="s">"failed to load kubelet config file, path: %s, error: %w"</span><span class="p">,</span> <span class="n">kubeletFlags</span><span class="o">.</span><span class="n">KubeletConfigFile</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
				<span class="p">}</span>
			<span class="p">}</span>
</code></pre></div></div>

<p><strong>ASIDE</strong></p>

<p>In case EKS Nodes, the kubelet service is managed by <code class="language-plaintext highlighter-rouge">systemd</code>, and the Kubelet configuration file is passed as an argument to the –config flag as you can <a href="https://github.com/awslabs/amazon-eks-ami/blob/3b667df7797233b170bec20033a20a47aaa5d0fc/templates/al2/runtime/kubelet-containerd.service#L11">see in the <code class="language-plaintext highlighter-rouge">kubelet.service</code> file</a>.</p>

<p>The Kubelet’s configuration is primarily established during node initialization through the <a href="https://github.com/awslabs/amazon-eks-ami/blob/3b667df7797233b170bec20033a20a47aaa5d0fc/templates/al2/runtime/bootstrap.sh"><code class="language-plaintext highlighter-rouge">bootstrap.sh</code> script</a>.</p>

<p>The bootstrap script configures two critical environment variables for the Kubelet service: <code class="language-plaintext highlighter-rouge">KUBELET_ARGS</code> and <code class="language-plaintext highlighter-rouge">KUBELET_EXTRA_ARGS</code>. These variables are defined in systemd service configuration files located at <code class="language-plaintext highlighter-rouge">/etc/systemd/system/kubelet.service.d/</code>…</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># https://github.com/awslabs/amazon-eks-ami/blob/3b667df7797233b170bec20033a20a47aaa5d0fc/templates/al2/runtime/bootstrap.sh#L625-L627</span>
<span class="c"># /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf</span>
<span class="o">[</span>Service]
<span class="nv">Environment</span><span class="o">=</span><span class="s1">'KUBELET_ARGS=$KUBELET_ARGS'</span>
</code></pre></div></div>

<p>and</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># https://github.com/awslabs/amazon-eks-ami/blob/3b667df7797233b170bec20033a20a47aaa5d0fc/templates/al2/runtime/bootstrap.sh#L630-L635</span>
<span class="c"># /etc/systemd/system/kubelet.service.d/30-kubelet-extra-args.conf</span>
<span class="o">[</span>Service]
<span class="nv">Environment</span><span class="o">=</span><span class="s1">'KUBELET_EXTRA_ARGS=$KUBELET_EXTRA_ARGS'</span>
</code></pre></div></div>

<p>…respectively.</p>

<blockquote>
  <p>NOTE: On CPU specific nodes, the bootstrap script extends itself, with <a href="https://github.com/awslabs/amazon-eks-ami/blob/3b667df7797233b170bec20033a20a47aaa5d0fc/templates/al2/runtime/bootstrap.sh#L639-L644">this</a>.</p>
</blockquote>

<p><strong>/ASIDE</strong></p>

<p>Now coming back to the <code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go</code> file, post setting up of logging the configuration elements are validated.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// cmd/kubelet/app/server.go: 248</span>
			<span class="c">// We always validate the local configuration (command line + config file).</span>
			<span class="c">// This is the default "last-known-good" config for dynamic config, and must always remain valid.</span>
			<span class="k">if</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">kubeletconfigvalidation</span><span class="o">.</span><span class="n">ValidateKubeletConfiguration</span><span class="p">(</span><span class="n">kubeletConfig</span><span class="p">,</span> <span class="n">utilfeature</span><span class="o">.</span><span class="n">DefaultFeatureGate</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
				<span class="k">return</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Errorf</span><span class="p">(</span><span class="s">"failed to validate kubelet configuration, error: %w, path: %s"</span><span class="p">,</span> <span class="n">err</span><span class="p">,</span> <span class="n">kubeletConfig</span><span class="p">)</span>
			<span class="p">}</span>
</code></pre></div></div>

<p>Then the <code class="language-plaintext highlighter-rouge">KubeletServer</code> constructed from the <code class="language-plaintext highlighter-rouge">kubeletConfig</code> and <code class="language-plaintext highlighter-rouge">kubeletFlags</code>.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// KubeletServer encapsulates all of the parameters necessary for starting up</span>
<span class="c">// a kubelet. These can either be set via command line or directly.</span>
<span class="c">// cmd/kubelet/app/server.go: 259</span>
			<span class="n">kubeletServer</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="n">options</span><span class="o">.</span><span class="n">KubeletServer</span><span class="p">{</span>
				<span class="n">KubeletFlags</span><span class="o">:</span>         <span class="o">*</span><span class="n">kubeletFlags</span><span class="p">,</span>
				<span class="n">KubeletConfiguration</span><span class="o">:</span> <span class="o">*</span><span class="n">kubeletConfig</span><span class="p">,</span>
			<span class="p">}</span>
</code></pre></div></div>

<p>Then <code class="language-plaintext highlighter-rouge">kubeletServer</code> is used to construct the default KubeletDeps</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// cmd/kubelet/app/server.go: 265</span>
			<span class="n">kubeletDeps</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">UnsecuredDependencies</span><span class="p">(</span><span class="n">kubeletServer</span><span class="p">,</span> <span class="n">utilfeature</span><span class="o">.</span><span class="n">DefaultFeatureGate</span><span class="p">)</span>
</code></pre></div></div>

<p><strong>DEEPER DIVE</strong></p>

<p>The <code class="language-plaintext highlighter-rouge">UnsecuredDependencies</code> function is defined in <code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go: 275</code>.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// UnsecuredDependencies returns a Dependencies suitable for being run, or an error if the server setup</span>
<span class="c">// is not valid.  It will not start any background processes, and does not include authentication/authorization</span>
<span class="k">func</span> <span class="n">UnsecuredDependencies</span><span class="p">(</span><span class="n">s</span> <span class="o">*</span><span class="n">options</span><span class="o">.</span><span class="n">KubeletServer</span><span class="p">,</span> <span class="n">featureGate</span> <span class="n">featuregate</span><span class="o">.</span><span class="n">FeatureGate</span><span class="p">)</span> <span class="p">(</span><span class="o">*</span><span class="n">kubelet</span><span class="o">.</span><span class="n">Dependencies</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
	<span class="c">// Initialize the TLS Options</span>
	<span class="n">tlsOptions</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">InitializeTLS</span><span class="p">(</span><span class="o">&amp;</span><span class="n">s</span><span class="o">.</span><span class="n">KubeletFlags</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">s</span><span class="o">.</span><span class="n">KubeletConfiguration</span><span class="p">)</span>
	<span class="o">...</span>
	<span class="k">return</span> <span class="o">&amp;</span><span class="n">kubelet</span><span class="o">.</span><span class="n">Dependencies</span><span class="p">{</span>
		<span class="o">...</span>
		<span class="n">TLSOptions</span><span class="o">:</span>          <span class="n">tlsOptions</span><span class="p">},</span> <span class="no">nil</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">kubeDeps</code> are runtime dependencies that are necessary for the Kubelet to run, the struct is define in <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 287</code>.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="n">Dependencies</span> <span class="k">struct</span> <span class="p">{</span>
	<span class="n">Options</span> <span class="p">[]</span><span class="n">Option</span>

	<span class="c">// Injected Dependencies</span>
	<span class="n">Auth</span>                      <span class="n">server</span><span class="o">.</span><span class="n">AuthInterface</span>
	<span class="n">CAdvisorInterface</span>         <span class="n">cadvisor</span><span class="o">.</span><span class="n">Interface</span>
	<span class="n">Cloud</span>                     <span class="n">cloudprovider</span><span class="o">.</span><span class="n">Interface</span>
	<span class="n">ContainerManager</span>          <span class="n">cm</span><span class="o">.</span><span class="n">ContainerManager</span>
	<span class="n">EventClient</span>               <span class="n">v1core</span><span class="o">.</span><span class="n">EventsGetter</span>
	<span class="n">HeartbeatClient</span>           <span class="n">clientset</span><span class="o">.</span><span class="n">Interface</span>
	<span class="n">OnHeartbeatFailure</span>        <span class="k">func</span><span class="p">()</span>
	<span class="n">KubeClient</span>                <span class="n">clientset</span><span class="o">.</span><span class="n">Interface</span>
	<span class="n">Mounter</span>                   <span class="n">mount</span><span class="o">.</span><span class="n">Interface</span>
	<span class="n">HostUtil</span>                  <span class="n">hostutil</span><span class="o">.</span><span class="n">HostUtils</span>
	<span class="n">OOMAdjuster</span>               <span class="o">*</span><span class="n">oom</span><span class="o">.</span><span class="n">OOMAdjuster</span>
	<span class="n">OSInterface</span>               <span class="n">kubecontainer</span><span class="o">.</span><span class="n">OSInterface</span>
	<span class="n">PodConfig</span>                 <span class="o">*</span><span class="n">config</span><span class="o">.</span><span class="n">PodConfig</span>
	<span class="n">ProbeManager</span>              <span class="n">prober</span><span class="o">.</span><span class="n">Manager</span>
	<span class="n">Recorder</span>                  <span class="n">record</span><span class="o">.</span><span class="n">EventRecorder</span>
	<span class="n">Subpather</span>                 <span class="n">subpath</span><span class="o">.</span><span class="n">Interface</span>
	<span class="n">TracerProvider</span>            <span class="n">trace</span><span class="o">.</span><span class="n">TracerProvider</span>
	<span class="n">VolumePlugins</span>             <span class="p">[]</span><span class="n">volume</span><span class="o">.</span><span class="n">VolumePlugin</span>
	<span class="n">DynamicPluginProber</span>       <span class="n">volume</span><span class="o">.</span><span class="n">DynamicPluginProber</span>
	<span class="n">TLSOptions</span>                <span class="o">*</span><span class="n">server</span><span class="o">.</span><span class="n">TLSOptions</span>
	<span class="n">RemoteRuntimeService</span>      <span class="n">internalapi</span><span class="o">.</span><span class="n">RuntimeService</span>
	<span class="n">RemoteImageService</span>        <span class="n">internalapi</span><span class="o">.</span><span class="n">ImageManagerService</span>
	<span class="n">PodStartupLatencyTracker</span>  <span class="n">util</span><span class="o">.</span><span class="n">PodStartupLatencyTracker</span>
	<span class="n">NodeStartupLatencyTracker</span> <span class="n">util</span><span class="o">.</span><span class="n">NodeStartupLatencyTracker</span>
	<span class="c">// remove it after cadvisor.UsingLegacyCadvisorStats dropped.</span>
	<span class="n">useLegacyCadvisorStats</span> <span class="kt">bool</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This sets up the TLS for the Kubelet, using the <code class="language-plaintext highlighter-rouge">InitializeTLS</code> function.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// InitializeTLS checks for a configured TLSCertFile and TLSPrivateKeyFile: if unspecified a new self-signed</span>
<span class="c">// certificate and key file are generated. Returns a configured server.TLSOptions object.</span>
<span class="c">// cmd/kubelet/app/server.go: 1124</span>
<span class="k">func</span> <span class="n">InitializeTLS</span><span class="p">(</span><span class="n">kf</span> <span class="o">*</span><span class="n">options</span><span class="o">.</span><span class="n">KubeletFlags</span><span class="p">,</span> <span class="n">kc</span> <span class="o">*</span><span class="n">kubeletconfiginternal</span><span class="o">.</span><span class="n">KubeletConfiguration</span><span class="p">)</span> <span class="p">(</span><span class="o">*</span><span class="n">server</span><span class="o">.</span><span class="n">TLSOptions</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
	<span class="k">if</span> <span class="o">!</span><span class="n">kc</span><span class="o">.</span><span class="n">ServerTLSBootstrap</span> <span class="o">&amp;&amp;</span> <span class="n">kc</span><span class="o">.</span><span class="n">TLSCertFile</span> <span class="o">==</span> <span class="s">""</span> <span class="o">&amp;&amp;</span> <span class="n">kc</span><span class="o">.</span><span class="n">TLSPrivateKeyFile</span> <span class="o">==</span> <span class="s">""</span> <span class="p">{</span>
		<span class="n">kc</span><span class="o">.</span><span class="n">TLSCertFile</span> <span class="o">=</span> <span class="n">filepath</span><span class="o">.</span><span class="n">Join</span><span class="p">(</span><span class="n">kf</span><span class="o">.</span><span class="n">CertDirectory</span><span class="p">,</span> <span class="s">"kubelet.crt"</span><span class="p">)</span>
		<span class="n">kc</span><span class="o">.</span><span class="n">TLSPrivateKeyFile</span> <span class="o">=</span> <span class="n">filepath</span><span class="o">.</span><span class="n">Join</span><span class="p">(</span><span class="n">kf</span><span class="o">.</span><span class="n">CertDirectory</span><span class="p">,</span> <span class="s">"kubelet.key"</span><span class="p">)</span>
		<span class="o">...</span>
	<span class="p">}</span>
	<span class="o">...</span>
	<span class="n">tlsOptions</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="n">server</span><span class="o">.</span><span class="n">TLSOptions</span><span class="p">{</span>
		<span class="n">Config</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">tls</span><span class="o">.</span><span class="n">Config</span><span class="p">{</span>
			<span class="n">MinVersion</span><span class="o">:</span>   <span class="n">minTLSVersion</span><span class="p">,</span>
			<span class="n">CipherSuites</span><span class="o">:</span> <span class="n">tlsCipherSuites</span><span class="p">,</span>
		<span class="p">},</span>
		<span class="n">CertFile</span><span class="o">:</span> <span class="n">kc</span><span class="o">.</span><span class="n">TLSCertFile</span><span class="p">,</span>
		<span class="n">KeyFile</span><span class="o">:</span>  <span class="n">kc</span><span class="o">.</span><span class="n">TLSPrivateKeyFile</span><span class="p">,</span>
	<span class="p">}</span>

	<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">kc</span><span class="o">.</span><span class="n">Authentication</span><span class="o">.</span><span class="n">X509</span><span class="o">.</span><span class="n">ClientCAFile</span><span class="p">)</span> <span class="o">&gt;</span> <span class="m">0</span> <span class="p">{</span>
		<span class="n">clientCAs</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">certutil</span><span class="o">.</span><span class="n">NewPool</span><span class="p">(</span><span class="n">kc</span><span class="o">.</span><span class="n">Authentication</span><span class="o">.</span><span class="n">X509</span><span class="o">.</span><span class="n">ClientCAFile</span><span class="p">)</span>
		<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
			<span class="k">return</span> <span class="no">nil</span><span class="p">,</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Errorf</span><span class="p">(</span><span class="s">"unable to load client CA file %s: %w"</span><span class="p">,</span> <span class="n">kc</span><span class="o">.</span><span class="n">Authentication</span><span class="o">.</span><span class="n">X509</span><span class="o">.</span><span class="n">ClientCAFile</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
		<span class="p">}</span>
		<span class="c">// Specify allowed CAs for client certificates</span>
		<span class="n">tlsOptions</span><span class="o">.</span><span class="n">Config</span><span class="o">.</span><span class="n">ClientCAs</span> <span class="o">=</span> <span class="n">clientCAs</span>
		<span class="c">// Populate PeerCertificates in requests, but don't reject connections without verified certificates</span>
		<span class="n">tlsOptions</span><span class="o">.</span><span class="n">Config</span><span class="o">.</span><span class="n">ClientAuth</span> <span class="o">=</span> <span class="n">tls</span><span class="o">.</span><span class="n">RequestClientCert</span>
	<span class="p">}</span>

	<span class="k">return</span> <span class="n">tlsOptions</span><span class="p">,</span> <span class="no">nil</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Since in EKS <a href="https://github.com/awslabs/amazon-eks-ami/blob/3b667df7797233b170bec20033a20a47aaa5d0fc/templates/al2/runtime/kubelet-config.json#L34">default configuration</a>, the <code class="language-plaintext highlighter-rouge">ServerTLSBootstrap</code> is set to <code class="language-plaintext highlighter-rouge">true</code>, the Kubelet will request a certificate from the certificates.k8s.io API. This requires an approver to approve the certificate signing requests. The <code class="language-plaintext highlighter-rouge">RotateKubeletServerCertificate</code> feature must be enabled.</p>

<p><strong>/DEEPER DIVE</strong></p>

<p>The kubelet must always with uid <code class="language-plaintext highlighter-rouge">0</code>, and this is checked here.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// cmd/kubelet/app/server.go: 270</span>
			<span class="k">if</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">checkPermissions</span><span class="p">();</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
				<span class="n">klog</span><span class="o">.</span><span class="n">ErrorS</span><span class="p">(</span><span class="n">err</span><span class="p">,</span> <span class="s">"kubelet running with insufficient permissions"</span><span class="p">)</span>
			<span class="p">}</span>
</code></pre></div></div>

<p>Before eventually starting the Kubelet server, the singnal handlers are setup and feature enablement metrics is setup.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// cmd/kubelet/app/server.go: 283</span>
			<span class="n">ctx</span> <span class="o">:=</span> <span class="n">genericapiserver</span><span class="o">.</span><span class="n">SetupSignalContext</span><span class="p">()</span>

			<span class="n">utilfeature</span><span class="o">.</span><span class="n">DefaultMutableFeatureGate</span><span class="o">.</span><span class="n">AddMetrics</span><span class="p">()</span>
</code></pre></div></div>

<p>The following section will go into the details <code class="language-plaintext highlighter-rouge">Run</code> function of the called from <code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go:287</code>.</p>

<h3 id="run-calls-run"><code class="language-plaintext highlighter-rouge">Run</code> calls <code class="language-plaintext highlighter-rouge">run</code></h3>

<p>Run runs the specified <code class="language-plaintext highlighter-rouge">KubeletServer</code> with the given Dependencies. This should never exit. The <code class="language-plaintext highlighter-rouge">kubeDeps</code> argument may be nil - if so, it is initialized from the settings on <code class="language-plaintext highlighter-rouge">KubeletServer</code>. Otherwise, the caller is assumed to have set up the <code class="language-plaintext highlighter-rouge">Dependencies</code> object and a default one will not be generated.</p>

<p>The <code class="language-plaintext highlighter-rouge">Run fuction actually calls the </code>run` function.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// cmd/kubelet/app/server.go: 287</span>
			<span class="k">if</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">run</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">kubeDeps</span><span class="p">,</span> <span class="n">featureGate</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
				<span class="k">return</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Errorf</span><span class="p">(</span><span class="s">"failed to run Kubelet: %w"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
			<span class="p">}</span>
</code></pre></div></div>

<p>So the <code class="language-plaintext highlighter-rouge">run</code> function is defined in <code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go: 292</code>, which does the following as it builds up to creating the Kubelet server.</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go: 599</code> - Validate the KubeletServer by calling the <code class="language-plaintext highlighter-rouge">ValidateKubeletServer</code></li>
  <li><code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go: 627</code> - Register the current configuration to be exposed as <code class="language-plaintext highlighter-rouge">/configz</code> by calling <code class="language-plaintext highlighter-rouge">initConfigz</code> function</li>
  <li><code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go: 633</code> - Register the current configuration to be exposed as <code class="language-plaintext highlighter-rouge">/configz</code> by calling <code class="language-plaintext highlighter-rouge">initConfigz</code> function</li>
  <li><code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go: 674</code> - Sets up <code class="language-plaintext highlighter-rouge">kubeDeps.KubeClient</code>, <code class="language-plaintext highlighter-rouge">kubeDeps.EventClient</code> and <code class="language-plaintext highlighter-rouge">kubeDeps.HeartbeatClient</code>, if any of them is <code class="language-plaintext highlighter-rouge">nil</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go: 715</code> - Creates an authenticator, an authorizer, and a matching authorizer attributes getter compatible with the kubelet’s needs, by calling <code class="language-plaintext highlighter-rouge">BuildAuth</code>, if <code class="language-plaintext highlighter-rouge">kubeDeps.Auth</code> is <code class="language-plaintext highlighter-rouge">nil</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go: 723</code> - Ensure the container runtime is up and running and sets up the KubeDeps references to those by calling the <code class="language-plaintext highlighter-rouge">PreInitRuntimeService</code> function.</li>
</ul>

<p><strong>DEEPER DIVE</strong></p>

<p>The <code class="language-plaintext highlighter-rouge">PreInitRuntimeService</code> function is defined in <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 359</code>. This function in the Kubelet is responsible for initializing the container runtime service before the main kubelet initialization.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// pkg/kubelet/kubelet.go: 360</span>
<span class="n">remoteImageEndpoint</span> <span class="o">:=</span> <span class="n">kubeCfg</span><span class="o">.</span><span class="n">ImageServiceEndpoint</span>
<span class="k">if</span> <span class="n">remoteImageEndpoint</span> <span class="o">==</span> <span class="s">""</span> <span class="o">&amp;&amp;</span> <span class="n">kubeCfg</span><span class="o">.</span><span class="n">ContainerRuntimeEndpoint</span> <span class="o">!=</span> <span class="s">""</span> <span class="p">{</span>
    <span class="n">remoteImageEndpoint</span> <span class="o">=</span> <span class="n">kubeCfg</span><span class="o">.</span><span class="n">ContainerRuntimeEndpoint</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Sets up image service endpoint, and falls back to container runtime endpoint if image endpoint is not specified.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// pkg/kubelet/kubelet.go: 372</span>
	<span class="k">if</span> <span class="n">kubeDeps</span><span class="o">.</span><span class="n">RemoteRuntimeService</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">remote</span><span class="o">.</span><span class="n">NewRemoteRuntimeService</span><span class="p">(</span>
	    <span class="n">kubeCfg</span><span class="o">.</span><span class="n">ContainerRuntimeEndpoint</span><span class="p">,</span>
	    <span class="n">kubeCfg</span><span class="o">.</span><span class="n">RuntimeRequestTimeout</span><span class="o">.</span><span class="n">Duration</span><span class="p">,</span>
	    <span class="n">tp</span><span class="p">,</span>
	    <span class="o">&amp;</span><span class="n">logger</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
	    <span class="k">return</span> <span class="n">err</span>
	<span class="p">}</span>
</code></pre></div></div>
<p>Creates connection to container runtime (e.g., containerd), sets up runtime request timeout, integrates with tracing, and sets up logging.</p>

<p>This function is crucial as it sets up the core services needed for container and image operations before the main kubelet initialization.</p>

<p><strong>/DEEPER DIVE</strong></p>

<p>Coming back to the <code class="language-plaintext highlighter-rouge">run</code> function in <code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go: 735</code>, this sets cgroupRoot to <code class="language-plaintext highlighter-rouge">/</code>.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="n">nodeAllocatableRoot</span> <span class="o">:=</span> <span class="n">cm</span><span class="o">.</span><span class="n">NodeAllocatableRoot</span><span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">CgroupRoot</span><span class="p">,</span> <span class="n">s</span><span class="o">.</span><span class="n">CgroupsPerQOS</span><span class="p">,</span> <span class="n">s</span><span class="o">.</span><span class="n">CgroupDriver</span><span class="p">)</span>
</code></pre></div></div>

<p>Internally the <code class="language-plaintext highlighter-rouge">nodeAllocatableRoot</code> is set to <code class="language-plaintext highlighter-rouge">/</code> if <code class="language-plaintext highlighter-rouge">s.CgroupRoot</code> is empty.</p>

<p>Coming back to the <code class="language-plaintext highlighter-rouge">run</code> function in <code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go: 727</code>, the following happens…</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// Get cgroup driver setting from CRI</span>
	<span class="k">if</span> <span class="n">utilfeature</span><span class="o">.</span><span class="n">DefaultFeatureGate</span><span class="o">.</span><span class="n">Enabled</span><span class="p">(</span><span class="n">features</span><span class="o">.</span><span class="n">KubeletCgroupDriverFromCRI</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">if</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">getCgroupDriverFromCRI</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">s</span><span class="p">,</span> <span class="n">kubeDeps</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
			<span class="k">return</span> <span class="n">err</span>
		<span class="p">}</span>
	<span class="p">}</span>
</code></pre></div></div>

<p>This feature behind a <a href="https://kubernetes.io/blog/2024/08/21/cri-cgroup-driver-lookup-now-beta/">feature gate</a>. And the kubelet uses the Cgroup driver from the CRI, if the feature is enabled. The current Cgroup driver is set in the <code class="language-plaintext highlighter-rouge">kubeletServer.CgroupDriver</code> field. And if it is linux is could be on these two values <code class="language-plaintext highlighter-rouge">systemd</code> or <code class="language-plaintext highlighter-rouge">cgroupfs</code>, and <code class="language-plaintext highlighter-rouge">kubeDeps.RemoteRuntimeService</code> is used to get the Cgroup driver from the CRI.</p>

<p><strong>JUST NOTE</strong></p>

<p>There is a utility function called the <code class="language-plaintext highlighter-rouge">ToSystemd</code> and here is a snippet, the comments explain it well.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// cgroupName.ToSystemd converts the internal cgroup name to a systemd name.</span>
<span class="c">// For example, the name {"kubepods", "burstable", "pod1234-abcd-5678-efgh"} becomes</span>
<span class="c">// "/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod1234_abcd_5678_efgh.slice"</span>
<span class="c">// This function always expands the systemd name into the cgroupfs form. If only</span>
<span class="c">// the last part is needed, use path.Base(...) on it to discard the rest.</span>
<span class="k">func</span> <span class="p">(</span><span class="n">cgroupName</span> <span class="n">CgroupName</span><span class="p">)</span> <span class="n">ToSystemd</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
	<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">cgroupName</span><span class="p">)</span> <span class="o">==</span> <span class="m">0</span> <span class="o">||</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">cgroupName</span><span class="p">)</span> <span class="o">==</span> <span class="m">1</span> <span class="o">&amp;&amp;</span> <span class="n">cgroupName</span><span class="p">[</span><span class="m">0</span><span class="p">]</span> <span class="o">==</span> <span class="s">""</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">return</span> <span class="s">"/"</span>
	<span class="p">}</span>
	<span class="n">newparts</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{}</span>
	<span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">part</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">cgroupName</span> <span class="p">{</span>
		<span class="n">part</span> <span class="o">=</span> <span class="n">escapeSystemdCgroupName</span><span class="p">(</span><span class="n">part</span><span class="p">)</span>
		<span class="n">newparts</span> <span class="o">=</span> <span class="nb">append</span><span class="p">(</span><span class="n">newparts</span><span class="p">,</span> <span class="n">part</span><span class="p">)</span>
	<span class="p">}</span>

	<span class="n">result</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">cgroupsystemd</span><span class="o">.</span><span class="n">ExpandSlice</span><span class="p">(</span><span class="n">strings</span><span class="o">.</span><span class="n">Join</span><span class="p">(</span><span class="n">newparts</span><span class="p">,</span> <span class="s">"-"</span><span class="p">)</span> <span class="o">+</span> <span class="n">systemdSuffix</span><span class="p">)</span>
	<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
		<span class="c">// Should never happen...</span>
		<span class="nb">panic</span><span class="p">(</span><span class="n">fmt</span><span class="o">.</span><span class="n">Errorf</span><span class="p">(</span><span class="s">"error converting cgroup name [%v] to systemd format: %v"</span><span class="p">,</span> <span class="n">cgroupName</span><span class="p">,</span> <span class="n">err</span><span class="p">))</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="n">result</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>/JUST NOTE</strong></p>

<p>Then the CAdvisor interface is initialized and asssigned to <code class="language-plaintext highlighter-rouge">kubeDeps.CAdvisorInterface</code>.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// cmd/kubelet/app/server.go: 754</span>
    <span class="k">if</span> <span class="n">kubeDeps</span><span class="o">.</span><span class="n">CAdvisorInterface</span> <span class="o">==</span> <span class="no">nil</span> <span class="p">{</span>
        <span class="o">...</span>
        <span class="n">kubeDeps</span><span class="o">.</span><span class="n">CAdvisorInterface</span> <span class="o">=</span> <span class="n">cadvisor</span><span class="o">.</span><span class="n">New</span><span class="p">(</span><span class="o">...</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>The kubelet requires the cAdvisor interface for several critical monitoring and resource tracking functions. If the interface is not provided, the kubelet will create a new cAdvisor interface. The interface provides container metrics, machine metrics, and other monitoring data, which include:</p>

<ul>
  <li>Container metrics: CPU, memory, filesystem, and network usage</li>
  <li>Machine metrics: CPU, memory, filesystem, and network usage</li>
  <li>Other monitoring data: Container and machine events, container and machine specifications</li>
</ul>

<p>Then the <code class="language-plaintext highlighter-rouge">kubeDeps.Recorder</code> is initialized at the <code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go: 763</code>, with a call to <code class="language-plaintext highlighter-rouge">makeEventRecorder</code>.</p>

<p>Then the <code class="language-plaintext highlighter-rouge">kubeDeps.ContainerManager</code> is initialized at the <code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go: 840</code>, is it is <code class="language-plaintext highlighter-rouge">nil</code>. But before the following values are updates…</p>

<ul>
  <li>The reservations for resources such as Kubelet and System are set</li>
  <li>The eviction thresholds are set. (defaults exists in <code class="language-plaintext highlighter-rouge">pkg/kubelet/eviction/defaults_linux.go: 23</code>.)</li>
  <li>The cpuManagerPolicy is set to <code class="language-plaintext highlighter-rouge">None</code> if it is not set.</li>
  <li>The topologyManagerPolicy is set to <code class="language-plaintext highlighter-rouge">None</code> if it is not set.</li>
  <li>The nodeSwap is set if the experimental feature is enabled and the node is enabled cgroupv2</li>
</ul>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// cmd/kubelet/app/server.go: 765</span>
    <span class="k">if</span> <span class="n">kubeDeps</span><span class="o">.</span><span class="n">ContainerManager</span> <span class="o">==</span> <span class="no">nil</span> <span class="p">{</span>
        <span class="o">...</span>
        <span class="c">// line: 840</span>
        <span class="n">kubeDeps</span><span class="o">.</span><span class="n">ContainerManager</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">cm</span><span class="o">.</span><span class="n">NewContainerManager</span><span class="p">(</span><span class="o">...</span>
    <span class="p">}</span>
</code></pre></div></div>

<p><strong>DEEPER DIVE</strong></p>

<p>The <code class="language-plaintext highlighter-rouge">NewContainerManager</code> function is defined in <code class="language-plaintext highlighter-rouge">pkg/kubelet/cm/container_manager.go: 47</code>. This function is responsible for creating a new container manager, which is the core component of the kubelet that manages containers and their lifecycle.</p>

<ul>
  <li>Get the cgroup subsystem v1/v2 mode</li>
  <li>Check if swap is on</li>
  <li>Construcrt the capacity on the node the kubelet is dealing with from the <code class="language-plaintext highlighter-rouge">MachineInfo</code>(<code class="language-plaintext highlighter-rouge">vendor/github.com/google/cadvisor/info/v1/machine.go: 180</code>)</li>
</ul>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// pkg/kubelet/cm/container_manager_linux.go: 239</span>
	<span class="n">capacity</span> <span class="o">:=</span> <span class="n">cadvisor</span><span class="o">.</span><span class="n">CapacityFromMachineInfo</span><span class="p">(</span><span class="n">machineInfo</span><span class="p">)</span>
	<span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">capacity</span> <span class="p">{</span>
		<span class="n">internalCapacity</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="n">v</span>
	<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>Set the pid limits to the internal capacity</li>
</ul>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// pkg/kubelet/cm/container_manager_linux.go: 243</span>
	<span class="n">pidlimits</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">pidlimit</span><span class="o">.</span><span class="n">Stats</span><span class="p">()</span> <span class="c">// pkg/kubelet/stats/pidlimit/pidlimit_linux.go: 35</span>
	<span class="k">if</span> <span class="n">err</span> <span class="o">==</span> <span class="no">nil</span> <span class="o">&amp;&amp;</span> <span class="n">pidlimits</span> <span class="o">!=</span> <span class="no">nil</span> <span class="o">&amp;&amp;</span> <span class="n">pidlimits</span><span class="o">.</span><span class="n">MaxPID</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
		<span class="n">internalCapacity</span><span class="p">[</span><span class="n">pidlimit</span><span class="o">.</span><span class="n">PIDs</span><span class="p">]</span> <span class="o">=</span> <span class="o">*</span><span class="n">resource</span><span class="o">.</span><span class="n">NewQuantity</span><span class="p">(</span>
			<span class="kt">int64</span><span class="p">(</span><span class="o">*</span><span class="n">pidlimits</span><span class="o">.</span><span class="n">MaxPID</span><span class="p">),</span>
			<span class="n">resource</span><span class="o">.</span><span class="n">DecimalSI</span><span class="p">)</span>
	<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>Initializes the cgroup hierarchy by:
    <ul>
      <li>Configuring the <code class="language-plaintext highlighter-rouge">cgroupRoot</code> directory</li>
      <li>Creating a <code class="language-plaintext highlighter-rouge">cgroupManager</code> factory with the appropriate subsystem (cgroup v1 or v2)</li>
      <li>Specifying the cgroup driver implementation (systemd or cgroupfs)</li>
    </ul>
  </li>
  <li>Updates the <code class="language-plaintext highlighter-rouge">nodeConfig.cgroupVersion</code> to reflect the active cgroup version from the manager</li>
</ul>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// pkg/kubelet/cm/container_manager_linux.go: 251</span>
    <span class="n">cgroupRoot</span> <span class="o">:=</span> <span class="n">ParseCgroupfsToCgroupName</span><span class="p">(</span><span class="n">nodeConfig</span><span class="o">.</span><span class="n">CgroupRoot</span><span class="p">)</span>
	<span class="n">cgroupManager</span> <span class="o">:=</span> <span class="n">NewCgroupManager</span><span class="p">(</span><span class="n">subsystems</span><span class="p">,</span> <span class="n">nodeConfig</span><span class="o">.</span><span class="n">CgroupDriver</span><span class="p">)</span>
	<span class="n">nodeConfig</span><span class="o">.</span><span class="n">CgroupVersion</span> <span class="o">=</span> <span class="n">cgroupManager</span><span class="o">.</span><span class="n">Version</span><span class="p">()</span>
</code></pre></div></div>

<ul>
  <li>Based on <code class="language-plaintext highlighter-rouge">CgroupsPerQOS</code>, the new cgroupRoot is set to <code class="language-plaintext highlighter-rouge">kubepods</code></li>
</ul>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// pkg/kubelet/cm/container_manager_linux.go: 255</span>
	<span class="k">if</span> <span class="n">nodeConfig</span><span class="o">.</span><span class="n">CgroupsPerQOS</span> <span class="p">{</span> <span class="c">// default is true staging/src/k8s.io/kubelet/config/v1beta1/types.go: 346</span>
	   <span class="o">...</span>
		<span class="c">// line: 271</span>
		<span class="n">cgroupRoot</span> <span class="o">=</span> <span class="n">NewCgroupName</span><span class="p">(</span><span class="n">cgroupRoot</span><span class="p">,</span> <span class="n">defaultNodeAllocatableCgroupName</span><span class="p">)</span> <span class="c">// defaultNodeAllocatableCgroupName = "kubepods"</span>
	<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>The new QOSContainerManager is initialized into which implements <code class="language-plaintext highlighter-rouge">qosContainerManagerImpl</code></li>
</ul>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// pkg/kubelet/cm/container_manager_linux.go: 275</span>
<span class="n">qosContainerManager</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">NewQOSContainerManager</span><span class="p">(</span><span class="n">subsystems</span><span class="p">,</span> <span class="n">cgroupRoot</span><span class="p">,</span> <span class="n">nodeConfig</span><span class="p">,</span> <span class="n">cgroupManager</span><span class="p">)</span>
</code></pre></div></div>

<ul>
  <li>Create a new <code class="language-plaintext highlighter-rouge">containerMnaagerImpl</code> and set the <code class="language-plaintext highlighter-rouge">qosContainerManager</code> and <code class="language-plaintext highlighter-rouge">cgroupManager</code> on it</li>
</ul>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// pkg/kubelet/cm/container_manager_linux.go: 280</span>
	<span class="n">cm</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="n">containerManagerImpl</span><span class="p">{</span>
		<span class="n">cadvisorInterface</span><span class="o">:</span>   <span class="n">cadvisorInterface</span><span class="p">,</span>
		<span class="n">mountUtil</span><span class="o">:</span>           <span class="n">mountUtil</span><span class="p">,</span>
		<span class="n">NodeConfig</span><span class="o">:</span>          <span class="n">nodeConfig</span><span class="p">,</span>
		<span class="n">subsystems</span><span class="o">:</span>          <span class="n">subsystems</span><span class="p">,</span>
		<span class="n">cgroupManager</span><span class="o">:</span>       <span class="n">cgroupManager</span><span class="p">,</span>
		<span class="n">capacity</span><span class="o">:</span>            <span class="n">capacity</span><span class="p">,</span>
		<span class="n">internalCapacity</span><span class="o">:</span>    <span class="n">internalCapacity</span><span class="p">,</span>
		<span class="n">cgroupRoot</span><span class="o">:</span>          <span class="n">cgroupRoot</span><span class="p">,</span>
		<span class="n">recorder</span><span class="o">:</span>            <span class="n">recorder</span><span class="p">,</span>
		<span class="n">qosContainerManager</span><span class="o">:</span> <span class="n">qosContainerManager</span><span class="p">,</span>
	<span class="p">}</span>

</code></pre></div></div>

<ul>
  <li>Assigned the instance of <code class="language-plaintext highlighter-rouge">topologyManager</code> to <code class="language-plaintext highlighter-rouge">cm.topologyManager</code>.</li>
</ul>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// pkg/kubelet/cm/container_manager_linux.go: 293</span>
	<span class="n">cm</span><span class="o">.</span><span class="n">topologyManager</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">topologymanager</span><span class="o">.</span><span class="n">NewManager</span><span class="p">(</span>
		<span class="n">machineInfo</span><span class="o">.</span><span class="n">Topology</span><span class="p">,</span>
		<span class="n">nodeConfig</span><span class="o">.</span><span class="n">TopologyManagerPolicy</span><span class="p">,</span>
		<span class="n">nodeConfig</span><span class="o">.</span><span class="n">TopologyManagerScope</span><span class="p">,</span>
		<span class="n">nodeConfig</span><span class="o">.</span><span class="n">TopologyManagerPolicyOptions</span><span class="p">,</span>
	<span class="p">)</span>
</code></pre></div></div>
<ul>
  <li>A new instance of the <code class="language-plaintext highlighter-rouge">devicemanager.MangerImpl</code> is created and assigned to <code class="language-plaintext highlighter-rouge">cm.deviceManager</code></li>
</ul>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// pkg/kubelet/cm/container_manager_linux.go: 305</span>
	<span class="n">cm</span><span class="o">.</span><span class="n">deviceManager</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">devicemanager</span><span class="o">.</span><span class="n">NewManagerImpl</span><span class="p">(</span><span class="n">machineInfo</span><span class="o">.</span><span class="n">Topology</span><span class="p">,</span> <span class="n">cm</span><span class="o">.</span><span class="n">topologyManager</span><span class="p">)</span>
</code></pre></div></div>

<ul>
  <li>The Dynamic Resource Allocation is new feature in Kubernetes, and is behind a feature gate, and disabled by default.</li>
</ul>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// pkg/kubelet/cm/container_manager_linux.go: 312</span>
	<span class="k">if</span> <span class="n">utilfeature</span><span class="o">.</span><span class="n">DefaultFeatureGate</span><span class="o">.</span><span class="n">Enabled</span><span class="p">(</span><span class="n">kubefeatures</span><span class="o">.</span><span class="n">DynamicResourceAllocation</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">klog</span><span class="o">.</span><span class="n">InfoS</span><span class="p">(</span><span class="s">"Creating Dynamic Resource Allocation (DRA) manager"</span><span class="p">)</span>
		<span class="n">cm</span><span class="o">.</span><span class="n">draManager</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">dra</span><span class="o">.</span><span class="n">NewManagerImpl</span><span class="p">(</span><span class="n">kubeClient</span><span class="p">,</span> <span class="n">nodeConfig</span><span class="o">.</span><span class="n">KubeletRootDir</span><span class="p">,</span> <span class="n">nodeConfig</span><span class="o">.</span><span class="n">NodeName</span><span class="p">)</span>
		<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
			<span class="k">return</span> <span class="no">nil</span><span class="p">,</span> <span class="n">err</span>
		<span class="p">}</span>
	<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>The passed argument <code class="language-plaintext highlighter-rouge">kubeClient</code> is assigned to <code class="language-plaintext highlighter-rouge">cm.kubeClient</code>(pkg/kubelet/cm/container_manager_linux.go: 319)</li>
  <li>The instancce of <code class="language-plaintext highlighter-rouge">cpuManager</code> is created and assigned to <code class="language-plaintext highlighter-rouge">cm.cpuManager</code></li>
  <li>The instance of <code class="language-plaintext highlighter-rouge">memoryManager</code> is created and assigned to <code class="language-plaintext highlighter-rouge">cm.memoryManager</code></li>
</ul>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	<span class="c">// Initialize CPU manager</span>
	<span class="n">cm</span><span class="o">.</span><span class="n">cpuManager</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">cpumanager</span><span class="o">.</span><span class="n">NewManager</span><span class="p">(</span>
		<span class="o">...</span>
	<span class="p">)</span>
	<span class="o">...</span>

	<span class="n">cm</span><span class="o">.</span><span class="n">memoryManager</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">memorymanager</span><span class="o">.</span><span class="n">NewManager</span><span class="p">(</span>
		<span class="o">...</span>
	<span class="p">)</span>
	<span class="o">...</span>
</code></pre></div></div>
<ul>
  <li>You will notice that each of these managers, <code class="language-plaintext highlighter-rouge">topologyManager</code>, <code class="language-plaintext highlighter-rouge">deviceManager</code>, <code class="language-plaintext highlighter-rouge">cpuManager</code>, <code class="language-plaintext highlighter-rouge">memoryManager</code>, have a method to initialize <code class="language-plaintext highlighter-rouge">HintProvider</code>, which is a function that returns a <code class="language-plaintext highlighter-rouge">HintProvider</code> interface. The <code class="language-plaintext highlighter-rouge">HintProvider</code> interface is used to provide hints to the kubelet about the resources that are available on the node, and play a significant role in resource allocation in NUMA locality and topology aware resource allocation.</li>
</ul>

<p><strong>/DEEPER DIVE</strong></p>

<p>Now back to the lines after the call to <code class="language-plaintext highlighter-rouge">NewContainerManager</code> in <code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go</code>, two <code class="language-plaintext highlighter-rouge">kudeDeps</code> variables <code class="language-plaintext highlighter-rouge">PodStartupLatencyTracker</code> and <code class="language-plaintext highlighter-rouge">NodeStartupLatencyTracker</code> are  intitialized.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">...</span>
	<span class="c">// cmd/kubelet/app/server.go: 887</span>
	<span class="n">kubeDeps</span><span class="o">.</span><span class="n">PodStartupLatencyTracker</span> <span class="o">=</span> <span class="n">kubeletutil</span><span class="o">.</span><span class="n">NewPodStartupLatencyTracker</span><span class="p">()</span>
	<span class="c">// &gt;&gt;&gt; pkg/kubelet/kubeletutil/pod_startup_latency_tracker.go:60</span>
	<span class="c">// &gt;&gt;&gt; returns an instance to struct `basicPodStartupLatencyTracker`</span>
	<span class="c">// &gt;&gt;&gt; pkg/kubelet/kubeletutil/pod_startup_latency_tracker.go:40</span>
	<span class="c">// &gt;&gt;&gt; struct `basicPodStartupLatencyTracker` implements `PodStartupLatencyTracker`</span>
	<span class="c">// &gt;&gt;&gt; pkg/kubelet/kubeletutil/pod_startup_latency_tracker.go:50</span>
	<span class="c">// &gt;&gt;&gt; struct `basicPodStartupLatencyTracker` has `perPodState` map with...</span>
	<span class="c">// &gt;&gt;&gt; ... `firstStartedPulling`, `lastFinishedPulling`, `observedRunningTime`</span>
	<span class="c">// cmd/kubelet/app/server.go: 891</span>
	<span class="n">kubeDeps</span><span class="o">.</span><span class="n">NodeStartupLatencyTracker</span> <span class="o">=</span> <span class="n">kubeletutil</span><span class="o">.</span><span class="n">NewNodeStartupLatencyTracker</span><span class="p">()</span>
	<span class="c">// &gt;&gt;&gt; pkg/kubelet/kubeletutil/node_startup_latency_tracker.go:49</span>
	<span class="c">// &gt;&gt;&gt; returns an instance to struct `basicNodeStartupLatencyTracker`</span>
	<span class="c">// &gt;&gt;&gt; `bascicNodeStartupLatencyTracker` implements `NodeStartupLatencyTracker`</span>
	<span class="c">// &gt;&gt;&gt; pkg/kubelet/kubeletutil/node_startup_latency_tracker.go:36</span>
	<span class="c">// &gt;&gt;&gt; struct `basicNodeStartupLatencyTracker` has  node `bootTime`, ...</span>
	<span class="c">// &gt;&gt;&gt; ... `kubeletStartTime`, `firstRegistrationAttemptTime`, ...</span>
	<span class="c">// &gt;&gt;&gt; ... `firstRegisteredNewNodeTime`, `firstNodeReadyTime`</span>
<span class="o">...</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">PodStartupLatencyTracker</code> is used to track the startup latency of pods, and the <code class="language-plaintext highlighter-rouge">NodeStartupLatencyTracker</code> is used to track the startup latency of nodes. Also note the <code class="language-plaintext highlighter-rouge">bootTime</code> is got from <code class="language-plaintext highlighter-rouge">btime</code> in <code class="language-plaintext highlighter-rouge">/proc/stat</code>.</p>

<p>Then the kubelet’s OOM Score is adjusted based on what is provided in tghe kubelet configuration for the <code class="language-plaintext highlighter-rouge">oomScoreAdj</code> field(default: <code class="language-plaintext highlighter-rouge">-999</code>), by calling the <code class="language-plaintext highlighter-rouge">ApplyOOMScoreAdj</code> function(<code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go: 895</code>). <code class="language-plaintext highlighter-rouge">pkg/util/oom/oom_linux.go: 51</code> implements <code class="language-plaintext highlighter-rouge">applyOOMScoreAdj</code> which is used to set the OOM score of a process.</p>

<p><strong>NOTE:</strong> The highly negative score means the Kubelet is very unlikely to be killed by the Linux OOM killer,</p>

<p>The next call is to <code class="language-plaintext highlighter-rouge">RunKubelet</code>, passing it the <code class="language-plaintext highlighter-rouge">Context</code>(<code class="language-plaintext highlighter-rouge">ctx</code>), <code class="language-plaintext highlighter-rouge">KubeletServer</code>(<code class="language-plaintext highlighter-rouge">s</code>), and <code class="language-plaintext highlighter-rouge">Dependencies</code>(<code class="language-plaintext highlighter-rouge">kubeDeps</code>) instances as arguments at <code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go: 900</code>.</p>

<p><strong>DEEPER DIVE</strong></p>

<p>RunKubelet is responsible for setting up and running a kubelet, and defined at <code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go: 1227</code>. It takes the <code class="language-plaintext highlighter-rouge">ctx.Context</code>, <code class="language-plaintext highlighter-rouge">options.KubeletServer</code> and <code class="language-plaintext highlighter-rouge">kubelet.Dependencies</code> as arguments. And is the sequences of calls that lead to the actual running of the kubelet.</p>

<ul>
  <li>Set thhe <code class="language-plaintext highlighter-rouge">hostname</code> to OS given hostname of no <code class="language-plaintext highlighter-rouge">s.hostnameOverride</code> is specified.</li>
  <li>Set the <code class="language-plaintext highlighter-rouge">nodeName</code> to cloud provider given name or else default to <code class="language-plaintext highlighter-rouge">hostname</code>.</li>
  <li>Set the boolean flag <code class="language-plaintext highlighter-rouge">hostnameOverridden</code> bases on the <code class="language-plaintext highlighter-rouge">s.hostnameOverride</code> being set.</li>
  <li>Setup the EventRecorder by calling <code class="language-plaintext highlighter-rouge">makeEventRecorder</code>. <code class="language-plaintext highlighter-rouge">makeEventRecorder</code> sets up kubeDeps.Recorder if it’s nil. It’s a no-op otherwise. <code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go: 555</code></li>
  <li>Set the <code class="language-plaintext highlighter-rouge">nodeIPs</code> array by parsing the <code class="language-plaintext highlighter-rouge">s.NodeIP</code> and <code class="language-plaintext highlighter-rouge">s.CloudProvider</code>. <code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go: 1241</code></li>
  <li>Initialize the <code class="language-plaintext highlighter-rouge">capabilities</code> to <code class="language-plaintext highlighter-rouge">AllowPrivileged</code> to <code class="language-plaintext highlighter-rouge">true</code>.</li>
  <li>Set the preferred docker configuration path to <code class="language-plaintext highlighter-rouge">s.RootDirectory</code>, by calling <code class="language-plaintext highlighter-rouge">credentialprovider.SetPreferredDockercfgPath</code>.</li>
  <li>Set the <code class="language-plaintext highlighter-rouge">kubeDeps.OSInterface</code> to an instance of <code class="language-plaintext highlighter-rouge">RealOS</code> struct, which implements the <code class="language-plaintext highlighter-rouge">OSInterface</code> interface. <code class="language-plaintext highlighter-rouge">pkg/kubelet/container/os.go: 27</code></li>
</ul>

<p>The next call is to <code class="language-plaintext highlighter-rouge">createAndInitKubelet</code> at <code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go: 1260</code>, which is responsible for creating and initializing the kubelet. The <code class="language-plaintext highlighter-rouge">createAndInitKubelet</code> function is defined at <code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go: 1301</code>. It takes <code class="language-plaintext highlighter-rouge">s</code>(instance of <code class="language-plaintext highlighter-rouge">options.KubeletServer</code>),<code class="language-plaintext highlighter-rouge">kubeDeps</code>(instance of <code class="language-plaintext highlighter-rouge">kubelet.Dependencies</code>), <code class="language-plaintext highlighter-rouge">hostname</code>, <code class="language-plaintext highlighter-rouge">hostnameOverridden</code>, <code class="language-plaintext highlighter-rouge">nodeName</code>, <code class="language-plaintext highlighter-rouge">nodeIPs</code>, as arguments, and returns an instance of <code class="language-plaintext highlighter-rouge">kubelet.Bootstrap</code> and an error. The <code class="language-plaintext highlighter-rouge">createAndInitKubelet</code> function in turn calls….</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// cmd/kubelet/app/server.go: 1301</span>
	<span class="n">k</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">kubelet</span><span class="o">.</span><span class="n">NewMainKubelet</span><span class="p">(</span><span class="o">&amp;</span><span class="n">kubeServer</span><span class="o">.</span><span class="n">KubeletConfiguration</span><span class="p">,</span>
		<span class="n">kubeDeps</span><span class="p">,</span>
		<span class="o">&amp;</span><span class="n">kubeServer</span><span class="o">.</span><span class="n">ContainerRuntimeOptions</span><span class="p">,</span>
		<span class="n">hostname</span><span class="p">,</span>
		<span class="n">hostnameOverridden</span><span class="p">,</span>
		<span class="n">nodeName</span><span class="p">,</span>
		<span class="n">nodeIPs</span><span class="p">,</span>
		<span class="n">kubeServer</span><span class="o">.</span><span class="n">ProviderID</span><span class="p">,</span>
		<span class="n">kubeServer</span><span class="o">.</span><span class="n">CloudProvider</span><span class="p">,</span>
		<span class="n">kubeServer</span><span class="o">.</span><span class="n">CertDirectory</span><span class="p">,</span>
		<span class="n">kubeServer</span><span class="o">.</span><span class="n">RootDirectory</span><span class="p">,</span>
		<span class="n">kubeServer</span><span class="o">.</span><span class="n">PodLogsDir</span><span class="p">,</span>
		<span class="n">kubeServer</span><span class="o">.</span><span class="n">ImageCredentialProviderConfigFile</span><span class="p">,</span>
		<span class="n">kubeServer</span><span class="o">.</span><span class="n">ImageCredentialProviderBinDir</span><span class="p">,</span>
		<span class="n">kubeServer</span><span class="o">.</span><span class="n">RegisterNode</span><span class="p">,</span>
		<span class="n">kubeServer</span><span class="o">.</span><span class="n">RegisterWithTaints</span><span class="p">,</span>
		<span class="n">kubeServer</span><span class="o">.</span><span class="n">AllowedUnsafeSysctls</span><span class="p">,</span>
		<span class="n">kubeServer</span><span class="o">.</span><span class="n">ExperimentalMounterPath</span><span class="p">,</span>
		<span class="n">kubeServer</span><span class="o">.</span><span class="n">KernelMemcgNotification</span><span class="p">,</span>
		<span class="n">kubeServer</span><span class="o">.</span><span class="n">ExperimentalNodeAllocatableIgnoreEvictionThreshold</span><span class="p">,</span>
		<span class="n">kubeServer</span><span class="o">.</span><span class="n">MinimumGCAge</span><span class="p">,</span>
		<span class="n">kubeServer</span><span class="o">.</span><span class="n">MaxPerPodContainerCount</span><span class="p">,</span>
		<span class="n">kubeServer</span><span class="o">.</span><span class="n">MaxContainerCount</span><span class="p">,</span>
		<span class="n">kubeServer</span><span class="o">.</span><span class="n">RegisterSchedulable</span><span class="p">,</span>
		<span class="n">kubeServer</span><span class="o">.</span><span class="n">NodeLabels</span><span class="p">,</span>
		<span class="n">kubeServer</span><span class="o">.</span><span class="n">NodeStatusMaxImages</span><span class="p">,</span>
		<span class="n">kubeServer</span><span class="o">.</span><span class="n">KubeletFlags</span><span class="o">.</span><span class="n">SeccompDefault</span> <span class="o">||</span> <span class="n">kubeServer</span><span class="o">.</span><span class="n">KubeletConfiguration</span><span class="o">.</span><span class="n">SeccompDefault</span><span class="p">)</span>
</code></pre></div></div>

<p>Now lets dive into what happens in <code class="language-plaintext highlighter-rouge">kubelet.NewMainKubelet</code>.</p>

<p><strong>DEEPER DIVE</strong></p>

<p>The <code class="language-plaintext highlighter-rouge">NewMainKubelet</code> is defined in <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 386</code>. <code class="language-plaintext highlighter-rouge">NewMainKubelet</code> instantiates a new Kubelet object along with all the required internal modules. No initialization of Kubelet and its modules happen here.</p>

<ul>
  <li>At this point the following things need to set for the kubelet to be initialized:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">rootDirectory</code>(default <code class="language-plaintext highlighter-rouge">/var/lib/kubelet</code>, <code class="language-plaintext highlighter-rouge">cmd/kubelet/app/options/options.go: 44</code>)</li>
      <li><code class="language-plaintext highlighter-rouge">podLogsDir</code> (default <code class="language-plaintext highlighter-rouge">/var/log/pods</code>, <code class="language-plaintext highlighter-rouge">pkg/kubelet/apis/config/types.go: 92</code>)</li>
      <li><code class="language-plaintext highlighter-rouge">kubeCfg.SyncFrequency.Duration</code> is greater than <code class="language-plaintext highlighter-rouge">0</code>.</li>
      <li>The <code class="language-plaintext highlighter-rouge">cloudProvider</code> must <code class="language-plaintext highlighter-rouge">external</code> if not empty.
        <ol>
          <li><strong>IMPORTANT</strong>: Check if kubelet is running in standalone mode or cluster mode, checking the <code class="language-plaintext highlighter-rouge">nil</code>-ness of <code class="language-plaintext highlighter-rouge">kubeDeps.KubeClient</code>. If it is <code class="language-plaintext highlighter-rouge">nil</code>, then it is running in standalone mode. Otherwise it is running in cluster mode. In cluster mode is sets up <code class="language-plaintext highlighter-rouge">kubeInformers</code>, and starts the sync with the API server.</li>
          <li><strong>IMPORTANT</strong>: Sets up the <code class="language-plaintext highlighter-rouge">kubeDeps.PodConfig</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 457</code>) instance, helps the kubelet to with the following functions:</li>
        </ol>
      </li>
      <li>Receive pod configurations from various sources</li>
      <li>Merge these configurations consistently</li>
      <li>Track changes to pod configurations</li>
      <li>Notify components about pod configuration changes</li>
      <li>Maintain pod state synchronization</li>
      <li>NOTE: This object in-memory “source of truth” for pod configurations
  Lets dived into this a bit, and look at the <code class="language-plaintext highlighter-rouge">makePodSourceConfig</code> function that is defined in <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 323</code>.</li>
    </ul>
  </li>
</ul>

<p><strong>DEEPER DIVE</strong></p>

<p>The <code class="language-plaintext highlighter-rouge">makePodSourceConfig</code> function establishes the various sources from which the Kubelet can receive Pod configurations. Here’s a detailed breakdown of its functionality:</p>

<ul>
  <li>The function takes several parameters:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">kubeCfg</code>: Kubelet configuration settings</li>
      <li><code class="language-plaintext highlighter-rouge">kubeDeps</code>: Dependencies required by kubelet</li>
      <li><code class="language-plaintext highlighter-rouge">nodeName</code>: The name of the node</li>
      <li><code class="language-plaintext highlighter-rouge">nodeHasSynced</code>: A function that checks if the node has synced</li>
    </ul>
  </li>
  <li>The function starts by setting up HTTP headers for static pod URLs:
    <div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">manifestURLHeader</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="n">http</span><span class="o">.</span><span class="n">Header</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">kubeCfg</span><span class="o">.</span><span class="n">StaticPodURLHeader</span><span class="p">)</span> <span class="o">&gt;</span> <span class="m">0</span> <span class="p">{</span>
  <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">kubeCfg</span><span class="o">.</span><span class="n">StaticPodURLHeader</span> <span class="p">{</span>
      <span class="k">for</span> <span class="n">i</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">v</span> <span class="p">{</span>
          <span class="n">manifestURLHeader</span><span class="o">.</span><span class="n">Add</span><span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
      <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>    </div>
  </li>
  <li>It creates a new PodConfig object that will handle pod configuration notifications:
    <div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">cfg</span> <span class="o">:=</span> <span class="n">config</span><span class="o">.</span><span class="n">NewPodConfig</span><span class="p">(</span><span class="n">config</span><span class="o">.</span><span class="n">PodConfigNotificationIncremental</span><span class="p">,</span> <span class="n">kubeDeps</span><span class="o">.</span><span class="n">Recorder</span><span class="p">,</span> <span class="n">kubeDeps</span><span class="o">.</span><span class="n">PodStartupLatencyTracker</span><span class="p">)</span>
</code></pre></div>    </div>
    <p>This creates an in memory “source of truth” for pod configurations, which will be used by the kubelet to monitor and apply pod configurations.</p>
  </li>
  <li>
    <p>The function then sets up three possible sources for pod configurations:</p>

    <ul>
      <li>File Source - for static pods defined in files:
        <div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">if</span> <span class="n">kubeCfg</span><span class="o">.</span><span class="n">StaticPodPath</span> <span class="o">!=</span> <span class="s">""</span> <span class="p">{</span>
   <span class="n">config</span><span class="o">.</span><span class="n">NewSourceFile</span><span class="p">(</span><span class="n">kubeCfg</span><span class="o">.</span><span class="n">StaticPodPath</span><span class="p">,</span> <span class="n">nodeName</span><span class="p">,</span> <span class="n">kubeCfg</span><span class="o">.</span><span class="n">FileCheckFrequency</span><span class="o">.</span><span class="n">Duration</span><span class="p">,</span> <span class="n">cfg</span><span class="o">.</span><span class="n">Channel</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">kubetypes</span><span class="o">.</span><span class="n">FileSource</span><span class="p">))</span>
 <span class="p">}</span>
</code></pre></div>        </div>
      </li>
    </ul>

    <p>b. URL Source - for static pods defined via HTTP:</p>
    <div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">if</span> <span class="n">kubeCfg</span><span class="o">.</span><span class="n">StaticPodURL</span> <span class="o">!=</span> <span class="s">""</span> <span class="p">{</span>
     <span class="n">config</span><span class="o">.</span><span class="n">NewSourceURL</span><span class="p">(</span><span class="n">kubeCfg</span><span class="o">.</span><span class="n">StaticPodURL</span><span class="p">,</span> <span class="n">manifestURLHeader</span><span class="p">,</span> <span class="n">nodeName</span><span class="p">,</span> <span class="n">kubeCfg</span><span class="o">.</span><span class="n">HTTPCheckFrequency</span><span class="o">.</span><span class="n">Duration</span><span class="p">,</span> <span class="n">cfg</span><span class="o">.</span><span class="n">Channel</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">kubetypes</span><span class="o">.</span><span class="n">HTTPSource</span><span class="p">))</span>
 <span class="p">}</span>
</code></pre></div>    </div>

    <p>c. API Server Source - for pods managed through the Kubernetes API:</p>
    <div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">if</span> <span class="n">kubeDeps</span><span class="o">.</span><span class="n">KubeClient</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
     <span class="n">config</span><span class="o">.</span><span class="n">NewSourceApiserver</span><span class="p">(</span><span class="n">kubeDeps</span><span class="o">.</span><span class="n">KubeClient</span><span class="p">,</span> <span class="n">nodeName</span><span class="p">,</span> <span class="n">nodeHasSynced</span><span class="p">,</span> <span class="n">cfg</span><span class="o">.</span><span class="n">Channel</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">kubetypes</span><span class="o">.</span><span class="n">ApiserverSource</span><span class="p">))</span>
 <span class="p">}</span>
</code></pre></div>    </div>
  </li>
</ul>

<p>This function is crucial for Kubernetes node operation as it establishes all the ways a node can receive pod configurations:</p>
<ul>
  <li>Through static files on disk</li>
  <li>Through HTTP endpoints</li>
  <li>Through the Kubernetes API server</li>
</ul>

<p>Each source is configured with its own update frequency and communication channel. The function returns the configured PodConfig object which will be used by the kubelet to monitor and apply pod configurations from all these sources.</p>

<p><strong>/DEEPER DIVE</strong></p>

<ul>
  <li>Create an instance of <code class="language-plaintext highlighter-rouge">kubecontainer.GCPolicy</code> at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 463</code>, which is used to manage the garbage collection of dead containers.</li>
  <li><strong>IMPORTANT</strong>: Create an instance of <code class="language-plaintext highlighter-rouge">v1.NodeDaemonEndpoints</code> at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 469</code>, which is used to store the daemon endpoints of the node. Node Daemon Endpoints are specific endpoints that represent services running on a node in a Kubernetes cluster. They are defined by the <code class="language-plaintext highlighter-rouge">NodeDaemonEndpoints</code> struct, which currently contains a single endpoint type, <code class="language-plaintext highlighter-rouge">KubeletEndpoint</code>. This represents the endpoint where the Kubelet is listening. The Kubelet is the primary node agent that runs on each node in the cluster.</li>
  <li>Create an instance of <code class="language-plaintext highlighter-rouge">images.ImageGCPolicy</code> at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 473</code>, which is used to manage the garbage collection of unused images on the node.</li>
  <li>If the <code class="language-plaintext highlighter-rouge">ImageMaximumGCAge</code> feature is enabled, set the <code class="language-plaintext highlighter-rouge">imageGcPolicy.MaxAge.Duration</code> to the value of <code class="language-plaintext highlighter-rouge">kubeCfg.ImageGcMaximumAge.Duration</code>.</li>
  <li>Create an instance of <code class="language-plaintext highlighter-rouge">kubelet.LifecycleHandler</code> at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 477</code>, which is used to handle the lifecycle of pods.</li>
  <li><code class="language-plaintext highlighter-rouge">kubeCfg.EnforceNodeAllocatable</code> is passed into a variable called <code class="language-plaintext highlighter-rouge">enforceNodeAllocatable</code> at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 485</code>, which is used to determine if the node allocatable should be enforced. This will be an empty array if we are not enforcing Evictions.</li>
  <li><code class="language-plaintext highlighter-rouge">thresholds</code> is set based on the parsing of the Node Eviction Thresholds in <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 488</code>. These thresholds are critical for:
    <ul>
      <li>Maintaining node stability</li>
      <li>Preventing out-of-resource scenarios</li>
      <li>Ensuring system processes have enough resources</li>
      <li>Managing pod evictions in a controlled manner</li>
    </ul>
  </li>
  <li><strong>IMPORTANT</strong>: An instance of <code class="language-plaintext highlighter-rouge">eviction.Config</code> is created at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 493</code>, which is used to configure the eviction manager.</li>
</ul>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	<span class="c">// pkg/kubelet/eviction/types.go: 46</span>
	<span class="k">type</span> <span class="n">Config</span> <span class="k">struct</span> <span class="p">{</span>
		<span class="c">// PressureTransitionPeriod is duration the kubelet has to wait before transitioning out of a pressure condition.</span>
		<span class="n">PressureTransitionPeriod</span> <span class="n">time</span><span class="o">.</span><span class="n">Duration</span>
		<span class="c">// Maximum allowed grace period (in seconds) to use when terminating pods in response to a soft eviction threshold being met.</span>
		<span class="n">MaxPodGracePeriodSeconds</span> <span class="kt">int64</span>
		<span class="c">// Thresholds define the set of conditions monitored to trigger eviction.</span>
		<span class="n">Thresholds</span> <span class="p">[]</span><span class="n">evictionapi</span><span class="o">.</span><span class="n">Threshold</span>
		<span class="c">// KernelMemcgNotification if true will integrate with the kernel memcg notification to determine if memory thresholds are crossed.</span>
		<span class="n">KernelMemcgNotification</span> <span class="kt">bool</span>
		<span class="c">// PodCgroupRoot is the cgroup which contains all pods.</span>
		<span class="n">PodCgroupRoot</span> <span class="kt">string</span>
	<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>Instance of <code class="language-plaintext highlighter-rouge">corelisters.ServiceLister</code> and <code class="language-plaintext highlighter-rouge">cache.InformerSynced</code> is created to list and and be informed about services ion the cluster.</li>
</ul>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	<span class="c">// pkg/kubelet/kubelet.go: 502</span>
		<span class="k">var</span> <span class="n">serviceLister</span> <span class="n">corelisters</span><span class="o">.</span><span class="n">ServiceLister</span>
		<span class="k">var</span> <span class="n">serviceHasSynced</span> <span class="n">cache</span><span class="o">.</span><span class="n">InformerSynced</span>
		<span class="k">if</span> <span class="n">kubeDeps</span><span class="o">.</span><span class="n">KubeClient</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
			<span class="c">// don't watch headless services, they are not needed since this informer is only used to create the environment variables for pods.</span>
			<span class="c">// See https://issues.k8s.io/122394</span>
			<span class="n">kubeInformers</span> <span class="o">:=</span> <span class="n">informers</span><span class="o">.</span><span class="n">NewSharedInformerFactoryWithOptions</span><span class="p">(</span><span class="n">kubeDeps</span><span class="o">.</span><span class="n">KubeClient</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">informers</span><span class="o">.</span><span class="n">WithTweakListOptions</span><span class="p">(</span><span class="k">func</span><span class="p">(</span><span class="n">options</span> <span class="o">*</span><span class="n">metav1</span><span class="o">.</span><span class="n">ListOptions</span><span class="p">)</span> <span class="p">{</span>
				<span class="n">options</span><span class="o">.</span><span class="n">FieldSelector</span> <span class="o">=</span> <span class="n">fields</span><span class="o">.</span><span class="n">OneTermNotEqualSelector</span><span class="p">(</span><span class="s">"spec.clusterIP"</span><span class="p">,</span> <span class="n">v1</span><span class="o">.</span><span class="n">ClusterIPNone</span><span class="p">)</span><span class="o">.</span><span class="n">String</span><span class="p">()</span>
			<span class="p">}))</span>
			<span class="n">serviceLister</span> <span class="o">=</span> <span class="n">kubeInformers</span><span class="o">.</span><span class="n">Core</span><span class="p">()</span><span class="o">.</span><span class="n">V1</span><span class="p">()</span><span class="o">.</span><span class="n">Services</span><span class="p">()</span><span class="o">.</span><span class="n">Lister</span><span class="p">()</span>
			<span class="n">serviceHasSynced</span> <span class="o">=</span> <span class="n">kubeInformers</span><span class="o">.</span><span class="n">Core</span><span class="p">()</span><span class="o">.</span><span class="n">V1</span><span class="p">()</span><span class="o">.</span><span class="n">Services</span><span class="p">()</span><span class="o">.</span><span class="n">Informer</span><span class="p">()</span><span class="o">.</span><span class="n">HasSynced</span>
			<span class="n">kubeInformers</span><span class="o">.</span><span class="n">Start</span><span class="p">(</span><span class="n">wait</span><span class="o">.</span><span class="n">NeverStop</span><span class="p">)</span>
		<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
			<span class="n">serviceIndexer</span> <span class="o">:=</span> <span class="n">cache</span><span class="o">.</span><span class="n">NewIndexer</span><span class="p">(</span><span class="n">cache</span><span class="o">.</span><span class="n">MetaNamespaceKeyFunc</span><span class="p">,</span> <span class="n">cache</span><span class="o">.</span><span class="n">Indexers</span><span class="p">{</span><span class="n">cache</span><span class="o">.</span><span class="n">NamespaceIndex</span><span class="o">:</span> <span class="n">cache</span><span class="o">.</span><span class="n">MetaNamespaceIndexFunc</span><span class="p">})</span>
			<span class="n">serviceLister</span> <span class="o">=</span> <span class="n">corelisters</span><span class="o">.</span><span class="n">NewServiceLister</span><span class="p">(</span><span class="n">serviceIndexer</span><span class="p">)</span>
			<span class="n">serviceHasSynced</span> <span class="o">=</span> <span class="k">func</span><span class="p">()</span> <span class="kt">bool</span> <span class="p">{</span> <span class="k">return</span> <span class="no">true</span> <span class="p">}</span>
		<span class="p">}</span>
</code></pre></div></div>

<ul>
  <li>Create a  node reference object <code class="language-plaintext highlighter-rouge">nodeRef</code> of <code class="language-plaintext highlighter-rouge">v1.ObjectReference</code> at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 520</code>, this will be used for events.</li>
  <li>Create a OOMWatcher instance <code class="language-plaintext highlighter-rouge">oomWatcher</code> at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 527</code>, by calling <code class="language-plaintext highlighter-rouge">oomwatcher.NewOOMWatcher</code> which is used to monitor the OOM events on the node.</li>
  <li>Create an string slice <code class="language-plaintext highlighter-rouge">clusterDNS</code> at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 544</code>, and populate it based on the <code class="language-plaintext highlighter-rouge">kubeCfg.ClusterDNS</code> ip addresses.</li>
  <li><strong>IMPORTANT</strong>: Create an insecure http client instance <code class="language-plaintext highlighter-rouge">insecureContainerLifecycleHTTPClient</code> at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 559</code>, which is used to make HTTP requests to the container lifecycle handler. The comments here are interesting:</li>
</ul>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	<span class="c">// A TLS transport is needed to make HTTPS-based container lifecycle requests,</span>
	<span class="c">// but we do not have the information necessary to do TLS verification.</span>
	<span class="c">//</span>
	<span class="c">// This client must not be modified to include credentials, because it is</span>
	<span class="c">// critical that credentials not leak from the client to arbitrary hosts.</span>
</code></pre></div></div>

<ul>
  <li>An instance of <code class="language-plaintext highlighter-rouge">kubeDeps.TracerProvider.Tracer</code> is created, <code class="language-plaintext highlighter-rouge">tracer</code>, at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 566</code>.</li>
  <li>The <code class="language-plaintext highlighter-rouge">klet</code> an instance of the <code class="language-plaintext highlighter-rouge">Kubelet</code> struct is created at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 571</code>. Here is the code…</li>
</ul>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">klet</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="n">Kubelet</span><span class="p">{</span>
	<span class="n">hostname</span><span class="o">:</span>                       <span class="n">hostname</span><span class="p">,</span>
	<span class="n">hostnameOverridden</span><span class="o">:</span>             <span class="n">hostnameOverridden</span><span class="p">,</span>
	<span class="n">nodeName</span><span class="o">:</span>                       <span class="n">nodeName</span><span class="p">,</span>
	<span class="n">kubeClient</span><span class="o">:</span>                     <span class="n">kubeDeps</span><span class="o">.</span><span class="n">KubeClient</span><span class="p">,</span>
	<span class="n">heartbeatClient</span><span class="o">:</span>                <span class="n">kubeDeps</span><span class="o">.</span><span class="n">HeartbeatClient</span><span class="p">,</span>
	<span class="n">onRepeatedHeartbeatFailure</span><span class="o">:</span>     <span class="n">kubeDeps</span><span class="o">.</span><span class="n">OnHeartbeatFailure</span><span class="p">,</span>
	<span class="n">rootDirectory</span><span class="o">:</span>                  <span class="n">filepath</span><span class="o">.</span><span class="n">Clean</span><span class="p">(</span><span class="n">rootDirectory</span><span class="p">),</span>
	<span class="n">podLogsDirectory</span><span class="o">:</span>               <span class="n">podLogsDirectory</span><span class="p">,</span>
	<span class="n">resyncInterval</span><span class="o">:</span>                 <span class="n">kubeCfg</span><span class="o">.</span><span class="n">SyncFrequency</span><span class="o">.</span><span class="n">Duration</span><span class="p">,</span>
	<span class="n">sourcesReady</span><span class="o">:</span>                   <span class="n">config</span><span class="o">.</span><span class="n">NewSourcesReady</span><span class="p">(</span><span class="n">kubeDeps</span><span class="o">.</span><span class="n">PodConfig</span><span class="o">.</span><span class="n">SeenAllSources</span><span class="p">),</span>
	<span class="n">registerNode</span><span class="o">:</span>                   <span class="n">registerNode</span><span class="p">,</span>
	<span class="n">registerWithTaints</span><span class="o">:</span>             <span class="n">registerWithTaints</span><span class="p">,</span>
	<span class="n">registerSchedulable</span><span class="o">:</span>            <span class="n">registerSchedulable</span><span class="p">,</span>
	<span class="n">dnsConfigurer</span><span class="o">:</span>                  <span class="n">dns</span><span class="o">.</span><span class="n">NewConfigurer</span><span class="p">(</span><span class="n">kubeDeps</span><span class="o">.</span><span class="n">Recorder</span><span class="p">,</span> <span class="n">nodeRef</span><span class="p">,</span> <span class="n">nodeIPs</span><span class="p">,</span> <span class="n">clusterDNS</span><span class="p">,</span> <span class="n">kubeCfg</span><span class="o">.</span><span class="n">ClusterDomain</span><span class="p">,</span> <span class="n">kubeCfg</span><span class="o">.</span><span class="n">ResolverConfig</span><span class="p">),</span>
	<span class="n">serviceLister</span><span class="o">:</span>                  <span class="n">serviceLister</span><span class="p">,</span>
	<span class="n">serviceHasSynced</span><span class="o">:</span>               <span class="n">serviceHasSynced</span><span class="p">,</span>
	<span class="n">nodeLister</span><span class="o">:</span>                     <span class="n">nodeLister</span><span class="p">,</span>
	<span class="n">nodeHasSynced</span><span class="o">:</span>                  <span class="n">nodeHasSynced</span><span class="p">,</span>
	<span class="n">streamingConnectionIdleTimeout</span><span class="o">:</span> <span class="n">kubeCfg</span><span class="o">.</span><span class="n">StreamingConnectionIdleTimeout</span><span class="o">.</span><span class="n">Duration</span><span class="p">,</span>
	<span class="n">recorder</span><span class="o">:</span>                       <span class="n">kubeDeps</span><span class="o">.</span><span class="n">Recorder</span><span class="p">,</span>
	<span class="n">cadvisor</span><span class="o">:</span>                       <span class="n">kubeDeps</span><span class="o">.</span><span class="n">CAdvisorInterface</span><span class="p">,</span>
	<span class="n">cloud</span><span class="o">:</span>                          <span class="n">kubeDeps</span><span class="o">.</span><span class="n">Cloud</span><span class="p">,</span>
	<span class="n">externalCloudProvider</span><span class="o">:</span>          <span class="n">cloudprovider</span><span class="o">.</span><span class="n">IsExternal</span><span class="p">(</span><span class="n">cloudProvider</span><span class="p">),</span>
	<span class="n">providerID</span><span class="o">:</span>                     <span class="n">providerID</span><span class="p">,</span>
	<span class="n">nodeRef</span><span class="o">:</span>                        <span class="n">nodeRef</span><span class="p">,</span>
	<span class="n">nodeLabels</span><span class="o">:</span>                     <span class="n">nodeLabels</span><span class="p">,</span>
	<span class="n">nodeStatusUpdateFrequency</span><span class="o">:</span>      <span class="n">kubeCfg</span><span class="o">.</span><span class="n">NodeStatusUpdateFrequency</span><span class="o">.</span><span class="n">Duration</span><span class="p">,</span>
	<span class="n">nodeStatusReportFrequency</span><span class="o">:</span>      <span class="n">kubeCfg</span><span class="o">.</span><span class="n">NodeStatusReportFrequency</span><span class="o">.</span><span class="n">Duration</span><span class="p">,</span>
	<span class="n">os</span><span class="o">:</span>                             <span class="n">kubeDeps</span><span class="o">.</span><span class="n">OSInterface</span><span class="p">,</span>
	<span class="n">oomWatcher</span><span class="o">:</span>                     <span class="n">oomWatcher</span><span class="p">,</span>
	<span class="n">cgroupsPerQOS</span><span class="o">:</span>                  <span class="n">kubeCfg</span><span class="o">.</span><span class="n">CgroupsPerQOS</span><span class="p">,</span>
	<span class="n">cgroupRoot</span><span class="o">:</span>                     <span class="n">kubeCfg</span><span class="o">.</span><span class="n">CgroupRoot</span><span class="p">,</span>
	<span class="n">mounter</span><span class="o">:</span>                        <span class="n">kubeDeps</span><span class="o">.</span><span class="n">Mounter</span><span class="p">,</span>
	<span class="n">hostutil</span><span class="o">:</span>                       <span class="n">kubeDeps</span><span class="o">.</span><span class="n">HostUtil</span><span class="p">,</span>
	<span class="n">subpather</span><span class="o">:</span>                      <span class="n">kubeDeps</span><span class="o">.</span><span class="n">Subpather</span><span class="p">,</span>
	<span class="n">maxPods</span><span class="o">:</span>                        <span class="kt">int</span><span class="p">(</span><span class="n">kubeCfg</span><span class="o">.</span><span class="n">MaxPods</span><span class="p">),</span>
	<span class="n">podsPerCore</span><span class="o">:</span>                    <span class="kt">int</span><span class="p">(</span><span class="n">kubeCfg</span><span class="o">.</span><span class="n">PodsPerCore</span><span class="p">),</span>
	<span class="n">syncLoopMonitor</span><span class="o">:</span>                <span class="n">atomic</span><span class="o">.</span><span class="n">Value</span><span class="p">{},</span>
	<span class="n">daemonEndpoints</span><span class="o">:</span>                <span class="n">daemonEndpoints</span><span class="p">,</span>
	<span class="n">containerManager</span><span class="o">:</span>               <span class="n">kubeDeps</span><span class="o">.</span><span class="n">ContainerManager</span><span class="p">,</span>
	<span class="n">nodeIPs</span><span class="o">:</span>                        <span class="n">nodeIPs</span><span class="p">,</span>
	<span class="n">nodeIPValidator</span><span class="o">:</span>                <span class="n">validateNodeIP</span><span class="p">,</span>
	<span class="n">clock</span><span class="o">:</span>                          <span class="n">clock</span><span class="o">.</span><span class="n">RealClock</span><span class="p">{},</span>
	<span class="n">enableControllerAttachDetach</span><span class="o">:</span>   <span class="n">kubeCfg</span><span class="o">.</span><span class="n">EnableControllerAttachDetach</span><span class="p">,</span>
	<span class="n">makeIPTablesUtilChains</span><span class="o">:</span>         <span class="n">kubeCfg</span><span class="o">.</span><span class="n">MakeIPTablesUtilChains</span><span class="p">,</span>
	<span class="n">nodeStatusMaxImages</span><span class="o">:</span>            <span class="n">nodeStatusMaxImages</span><span class="p">,</span>
	<span class="n">tracer</span><span class="o">:</span>                         <span class="n">tracer</span><span class="p">,</span>
	<span class="n">nodeStartupLatencyTracker</span><span class="o">:</span>      <span class="n">kubeDeps</span><span class="o">.</span><span class="n">NodeStartupLatencyTracker</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>
<p><strong>TODO</strong>: Annotate each of the values of those variables in the <code class="language-plaintext highlighter-rouge">Kubelet</code> struct with references to where they are defined.</p>
<ul>
  <li>If <code class="language-plaintext highlighter-rouge">klet.Cloud</code> is not <code class="language-plaintext highlighter-rouge">nil</code>, the <code class="language-plaintext highlighter-rouge">klet.CloudResourceSyncManager</code> is set to a new instance of <code class="language-plaintext highlighter-rouge">cloudprovider.CloudResourceSyncManager</code> at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 620</code>.</li>
  <li><strong>IMPORTANT</strong>: <code class="language-plaintext highlighter-rouge">klet.secretManager</code> and <code class="language-plaintext highlighter-rouge">klet.configMapManager</code> are set. As you can see below</li>
</ul>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="c">// pkg/kubelet/kubelet.go: 623</span>
    <span class="k">var</span> <span class="n">secretManager</span> <span class="n">secret</span><span class="o">.</span><span class="n">Manager</span>
		<span class="k">var</span> <span class="n">configMapManager</span> <span class="n">configmap</span><span class="o">.</span><span class="n">Manager</span>
		<span class="k">if</span> <span class="n">klet</span><span class="o">.</span><span class="n">kubeClient</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
			<span class="k">switch</span> <span class="n">kubeCfg</span><span class="o">.</span><span class="n">ConfigMapAndSecretChangeDetectionStrategy</span> <span class="p">{</span>
			<span class="k">case</span> <span class="n">kubeletconfiginternal</span><span class="o">.</span><span class="n">WatchChangeDetectionStrategy</span><span class="o">:</span>
				<span class="n">secretManager</span> <span class="o">=</span> <span class="n">secret</span><span class="o">.</span><span class="n">NewWatchingSecretManager</span><span class="p">(</span><span class="n">klet</span><span class="o">.</span><span class="n">kubeClient</span><span class="p">,</span> <span class="n">klet</span><span class="o">.</span><span class="n">resyncInterval</span><span class="p">)</span>
				<span class="n">configMapManager</span> <span class="o">=</span> <span class="n">configmap</span><span class="o">.</span><span class="n">NewWatchingConfigMapManager</span><span class="p">(</span><span class="n">klet</span><span class="o">.</span><span class="n">kubeClient</span><span class="p">,</span> <span class="n">klet</span><span class="o">.</span><span class="n">resyncInterval</span><span class="p">)</span>
			<span class="k">case</span> <span class="n">kubeletconfiginternal</span><span class="o">.</span><span class="n">TTLCacheChangeDetectionStrategy</span><span class="o">:</span>
				<span class="n">secretManager</span> <span class="o">=</span> <span class="n">secret</span><span class="o">.</span><span class="n">NewCachingSecretManager</span><span class="p">(</span>
					<span class="n">klet</span><span class="o">.</span><span class="n">kubeClient</span><span class="p">,</span> <span class="n">manager</span><span class="o">.</span><span class="n">GetObjectTTLFromNodeFunc</span><span class="p">(</span><span class="n">klet</span><span class="o">.</span><span class="n">GetNode</span><span class="p">))</span>
				<span class="n">configMapManager</span> <span class="o">=</span> <span class="n">configmap</span><span class="o">.</span><span class="n">NewCachingConfigMapManager</span><span class="p">(</span>
					<span class="n">klet</span><span class="o">.</span><span class="n">kubeClient</span><span class="p">,</span> <span class="n">manager</span><span class="o">.</span><span class="n">GetObjectTTLFromNodeFunc</span><span class="p">(</span><span class="n">klet</span><span class="o">.</span><span class="n">GetNode</span><span class="p">))</span>
			<span class="k">case</span> <span class="n">kubeletconfiginternal</span><span class="o">.</span><span class="n">GetChangeDetectionStrategy</span><span class="o">:</span>
				<span class="n">secretManager</span> <span class="o">=</span> <span class="n">secret</span><span class="o">.</span><span class="n">NewSimpleSecretManager</span><span class="p">(</span><span class="n">klet</span><span class="o">.</span><span class="n">kubeClient</span><span class="p">)</span>
				<span class="n">configMapManager</span> <span class="o">=</span> <span class="n">configmap</span><span class="o">.</span><span class="n">NewSimpleConfigMapManager</span><span class="p">(</span><span class="n">klet</span><span class="o">.</span><span class="n">kubeClient</span><span class="p">)</span>
			<span class="k">default</span><span class="o">:</span>
				<span class="k">return</span> <span class="no">nil</span><span class="p">,</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Errorf</span><span class="p">(</span><span class="s">"unknown configmap and secret manager mode: %v"</span><span class="p">,</span> <span class="n">kubeCfg</span><span class="o">.</span><span class="n">ConfigMapAndSecretChangeDetectionStrategy</span><span class="p">)</span>
			<span class="p">}</span>

			<span class="n">klet</span><span class="o">.</span><span class="n">secretManager</span> <span class="o">=</span> <span class="n">secretManager</span>
			<span class="n">klet</span><span class="o">.</span><span class="n">configMapManager</span> <span class="o">=</span> <span class="n">configMapManager</span>
		<span class="p">}</span>
</code></pre></div></div>
<p>And by default <code class="language-plaintext highlighter-rouge">kubeCfg.ConfigMapAndSecretChangeDetectionStrategy</code> is set to <code class="language-plaintext highlighter-rouge">Watch</code>.</p>
<ul>
  <li>An instance of <code class="language-plaintext highlighter-rouge">MachineInfo</code> is created, <code class="language-plaintext highlighter-rouge">machineInfo</code>, at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 646</code>, which is used to store the machine information. And the <code class="language-plaintext highlighter-rouge">machineInfo</code> is assigned <code class="language-plaintext highlighter-rouge">klet.machineInfo</code>.</li>
  <li>An instance of <code class="language-plaintext highlighter-rouge">BackOff</code> is created, <code class="language-plaintext highlighter-rouge">imageBackOff</code>, at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 655</code>, which is used to manage the backoff for image pull failures. This variable will be passed to the container runtime.</li>
  <li><code class="language-plaintext highlighter-rouge">Manager</code>(s) for the <code class="language-plaintext highlighter-rouge">liveness</code>, <code class="language-plaintext highlighter-rouge">readiness</code> and <code class="language-plaintext highlighter-rouge">startup</code> probes is created into <code class="language-plaintext highlighter-rouge">klet.livenessManager</code>, <code class="language-plaintext highlighter-rouge">klet.readinessManager</code>, and <code class="language-plaintext highlighter-rouge">klet.startupManager</code> respectively at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 657-659</code> by call <code class="language-plaintext highlighter-rouge">proberesults.NewManager</code>.</li>
  <li>An instance of <code class="language-plaintext highlighter-rouge">cache</code> is created, <code class="language-plaintext highlighter-rouge">klet.podCache</code>, at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 660</code>, which is used to store the pod information.</li>
  <li>An instance of <code class="language-plaintext highlighter-rouge">MirrorClient</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/pod/mirror_client.go: 34</code>) interface is created into <code class="language-plaintext highlighter-rouge">klet.mirrorPodClient</code> by calling <code class="language-plaintext highlighter-rouge">kubepod.NewBasicMirrorClient</code> at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 662</code>, which is used to manage the pod mirroring of Kubelet’s static pods. <code class="language-plaintext highlighter-rouge">MirrorClient</code> knows how to create/delete a mirror pod in the API server.</li>
</ul>

<p><strong>DEFINITION</strong></p>

<p>Mirror pods in Kubernetes are special pod objects that exist for static pods. Here’s how they work:</p>

<ul>
  <li>Static Pod -&gt; Mirror Pod Relationship:
    <ul>
      <li>When you create a static pod (by placing a pod manifest in the node’s static pod directory)</li>
      <li>The kubelet automatically creates a mirror pod on the API server</li>
      <li>The mirror pod is essentially a read-only copy/representation of the static pod</li>
    </ul>
  </li>
  <li>Key Characteristics:
    <ul>
      <li>Mirror pods are only visible on the API server (you can see them with <code class="language-plaintext highlighter-rouge">kubectl</code>)</li>
      <li>You cannot modify mirror pods directly through the API server</li>
      <li>They have the same name as the static pod but are bound to the specific node</li>
      <li>The kubelet adds a label <code class="language-plaintext highlighter-rouge">kubernetes.io/config.mirror</code> to identify them</li>
    </ul>
  </li>
  <li>Purpose:
    <ul>
      <li>They make static pods visible to the control plane</li>
      <li>Allow monitoring and visibility of static pods through standard Kubernetes tools</li>
      <li>Help in tracking the status of static pods at the cluster level</li>
    </ul>
  </li>
  <li>Example Use Case:
    <ul>
      <li>Common for system components like the kubelet itself</li>
      <li>Often used for monitoring agents or networking plugins that need to run on every node</li>
      <li>Useful when you need pods that must exist even if the API server is down</li>
    </ul>
  </li>
</ul>

<p>Think of mirror pods as the API server’s “window” into static pods running on nodes. They let you see what static pods exist, but you can only modify the actual static pods through their manifest files on the node.</p>

<p><strong>/DEFINITION</strong></p>

<ul>
  <li>An instance of <code class="language-plaintext highlighter-rouge">basicManager</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/pod/pod_manager.go: 108</code>) struct, which implements the <code class="language-plaintext highlighter-rouge">Manager</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/pod/pod_manager.go: 45</code>) interface is created, <code class="language-plaintext highlighter-rouge">klet.podManager</code>, at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 664</code>, which is used to manage the pods on the node.</li>
</ul>

<p><strong>DEEPER DIVE</strong></p>

<p>The <code class="language-plaintext highlighter-rouge">podManager</code> is a critical component of the kubelet that manages the pods on the node. Here’s a deeper look at its role and functionality:</p>

<ol>
  <li>Storage Role:
    <ul>
      <li>Stores the desired/intended set of:
        <ul>
          <li>Admitted pods</li>
          <li>Mirror pods</li>
        </ul>
      </li>
      <li>Does NOT store the actual running pods (that’s handled by <code class="language-plaintext highlighter-rouge">podWorkers</code>)</li>
    </ul>
  </li>
  <li>Configuration:
    <ul>
      <li>Gets populated by kubelet config loops</li>
      <li>Receives pod configurations from multiple sources:
        <ul>
          <li>API server (for regular pods)</li>
          <li>Local filesystem or HTTP (for static pods)</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Important Distinctions:
    <ul>
      <li>Not all desired pods in podManager are actually running</li>
      <li>Not all running pods are in podManager (e.g., force-deleted pods may still be terminating)</li>
    </ul>
  </li>
  <li>Future Improvements (TODO):
    <ul>
      <li>There’s a planned review to move components from using podManager to podWorkers when they need actual pod state</li>
      <li>This change might add some latency but will:
        <ul>
          <li>Avoid race conditions</li>
          <li>Better handle terminating pods</li>
          <li>Better handle updated static pods</li>
        </ul>
      </li>
    </ul>
  </li>
</ol>

<p><strong>/DEEPER DIVE</strong></p>

<ul>
  <li>An instance of status <code class="language-plaintext highlighter-rouge">manager</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/status/status_manager.go: 71</code>) which implements the <code class="language-plaintext highlighter-rouge">Manager</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/status/status_manager.go: 121</code>) interface, is created, <code class="language-plaintext highlighter-rouge">klet.statusManager</code>, at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 666</code>, which is used to manage the status of the pods on the node.</li>
  <li>An instance of <code class="language-plaintext highlighter-rouge">resourceAnalyzer</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/server/stats/resource_analyzer.go: 33</code>) struct which implements the <code class="language-plaintext highlighter-rouge">ResourceAnalyzer</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/server/stats/resource_analyzer.go: 25</code>) interface, is created, <code class="language-plaintext highlighter-rouge">klet.resourceAnalyzer</code>, at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 667</code>, which is used to analyze the resources on the node.</li>
  <li><code class="language-plaintext highlighter-rouge">kubeDeps.RemoteRuntimeService</code> is assigned to <code class="language-plaintext highlighter-rouge">klet.runtimeService</code> at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 669</code>, which is used to manage the container runtime service.</li>
  <li>An instance of <code class="language-plaintext highlighter-rouge">containerLogManager</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/logs/container_log_manager.go: 143</code>) struct which implements the <code class="language-plaintext highlighter-rouge">ContainerLogManager</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/logs/container_log_manager.go: 55</code>) interface, is created, <code class="language-plaintext highlighter-rouge">klet.containerLogManager</code>, at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 676-687</code>, which is used to manage the container logs on the node.</li>
</ul>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="c">// pkg/kubelet/kubelet.go: 676</span>
   	<span class="n">containerLogManager</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">logs</span><span class="o">.</span><span class="n">NewContainerLogManager</span><span class="p">(</span>
    		<span class="n">klet</span><span class="o">.</span><span class="n">runtimeService</span><span class="p">,</span>
    		<span class="n">kubeDeps</span><span class="o">.</span><span class="n">OSInterface</span><span class="p">,</span>
    		<span class="n">kubeCfg</span><span class="o">.</span><span class="n">ContainerLogMaxSize</span><span class="p">,</span>
    		<span class="kt">int</span><span class="p">(</span><span class="n">kubeCfg</span><span class="o">.</span><span class="n">ContainerLogMaxFiles</span><span class="p">),</span>
    		<span class="kt">int</span><span class="p">(</span><span class="n">kubeCfg</span><span class="o">.</span><span class="n">ContainerLogMaxWorkers</span><span class="p">),</span>
    		<span class="n">kubeCfg</span><span class="o">.</span><span class="n">ContainerLogMonitorInterval</span><span class="p">,</span>
   	<span class="p">)</span>
   	<span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
    		<span class="k">return</span> <span class="no">nil</span><span class="p">,</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Errorf</span><span class="p">(</span><span class="s">"failed to initialize container log manager: %v"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
   	<span class="p">}</span>
  	<span class="n">klet</span><span class="o">.</span><span class="n">containerLogManager</span> <span class="o">=</span> <span class="n">containerLogManager</span>
</code></pre></div></div>

<ul>
  <li>An instance of <code class="language-plaintext highlighter-rouge">ReasonCache</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/reason_cache.go: 38</code>) struct is created, <code class="language-plaintext highlighter-rouge">klet.reasonCache</code>, at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 673</code>, which is used to to store the failure reason for the latest container start, it’s an <code class="language-plaintext highlighter-rouge">lru</code> cache hence it might not be reliable, but goal is propogate the reason to container stature.</li>
  <li>An instance of <code class="language-plaintext highlighter-rouge">basicWorkQueue</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/util/queue/work_queue.go: 36</code>) struct which implements the <code class="language-plaintext highlighter-rouge">WorkQueue</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/util/queue/work_queue.go: 29</code>), is created, <code class="language-plaintext highlighter-rouge">klet.workQueue</code>, at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 690</code>, WorkQueue allows queuing items with a timestamp and an item is considered ready to process if the timestamp has expired.</li>
  <li>An instance of <code class="language-plaintext highlighter-rouge">podWorkers</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/pod_workers.go: 551</code>) struct which implements the <code class="language-plaintext highlighter-rouge">PodWorkers</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/pod_workers.go: 36</code>), is created, <code class="language-plaintext highlighter-rouge">klet.podWorkers</code>, at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 692</code>.  The <code class="language-plaintext highlighter-rouge">PodWorkers</code> interface is used to manage the workers that process pods on the node. It provides methods to add, remove, and get workers, as well as to get the number of workers and the number of pods being processed by each worker. And here is a deeper look at this.</li>
</ul>

<p><strong>DEEPER DIVE</strong></p>

<p>Let look the call the following call.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// pkg/kubelet/kubelet.go: 692</span>
<span class="n">klet</span><span class="o">.</span><span class="n">podWorkers</span> <span class="o">=</span> <span class="n">newPodWorkers</span><span class="p">(</span>
		<span class="c">// an instance of Kubelet struct, pkg/kubelet/kubelet.go: 568</span>
		<span class="n">klet</span><span class="p">,</span>
		<span class="c">// an instance of record.EventRecorder, pkg/kubelet/kubelet.go: 570</span>
		<span class="n">kubeDeps</span><span class="o">.</span><span class="n">Recorder</span><span class="p">,</span>
		<span class="c">// a queue used to trigger pod workers.</span>
		<span class="n">klet</span><span class="o">.</span><span class="n">workQueue</span><span class="p">,</span>
		<span class="c">// resyncInterval is the interval between periodic full reconciliations of</span>
		<span class="n">klet</span><span class="o">.</span><span class="n">resyncInterval</span><span class="p">,</span>
		<span class="c">// backOffPeriod is the period to back off when pod syncing results in an error.</span>
		<span class="n">backOffPeriod</span><span class="p">,</span>
		<span class="c">// Cache stores the PodStatus for the pods. It represents *all* the visible</span>
		<span class="c">// pods/containers in the container runtime. All cache entries are at least as</span>
		<span class="c">// new or newer than the global timestamp (set by UpdateTime()), while</span>
		<span class="c">// individual entries may be slightly newer than the global timestamp. If a pod</span>
		<span class="c">// has no states known by the runtime, Cache returns an empty PodStatus object</span>
		<span class="c">// with ID populated.</span>
		<span class="c">//</span>
		<span class="c">// Cache provides two methods to retrieve the PodStatus: the non-blocking Get()</span>
		<span class="c">// and the blocking GetNewerThan() method. The component responsible for</span>
		<span class="c">// populating the cache is expected to call Delete() to explicitly free the</span>
		<span class="c">// cache entries.</span>
		<span class="c">// an instance of Cache interface, pkg/kubelet/container/cache.go: 39</span>
		<span class="n">klet</span><span class="o">.</span><span class="n">podCache</span><span class="p">,</span>
	<span class="p">)</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">podWorkers</code> is a core component in the kubelet that manages the lifecycle of pods. Here are its key responsibilities:</p>

<ul>
  <li>State Management:
    <ul>
      <li>It manages pods through three main states:
        <ul>
          <li>syncing: pod should be running</li>
          <li>terminating: pod should be stopped</li>
          <li>terminated: pod should have all resources cleaned up</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Exclusive Pod Execution:
    <ul>
      <li>Once a pod is accepted, no other pod with the same UID (or name+namespace for static pods) can start until the first pod has fully terminated and been cleaned up.</li>
      <li>This means even if a pod is desired in the API and admitted, it might have to wait for a prior pod to terminate.</li>
    </ul>
  </li>
  <li>Event-Driven Architecture:
    <ul>
      <li>It works as an event-driven controller through <code class="language-plaintext highlighter-rouge">UpdatePod</code></li>
      <li>Requires periodic resyncing through <code class="language-plaintext highlighter-rouge">SyncKnownPods</code> to maintain desired state</li>
      <li>Handles force-deleted pods that still need to complete their termination</li>
    </ul>
  </li>
  <li>Authority:
    <ul>
      <li>Acts as the authoritative source within kubelet for:
        <ul>
          <li>Which pods are actually running</li>
          <li>Current state of each pod</li>
        </ul>
      </li>
      <li>Other kubelet components must consult podWorkers to know pod phases for resource management</li>
    </ul>
  </li>
</ul>

<p>In essence, it’s the traffic controller for pod lifecycle management within the kubelet, ensuring orderly execution and cleanup of pods.</p>

<p><strong>/DEEPER DIVE</strong></p>

<ul>
  <li>A variable <code class="language-plaintext highlighter-rouge">singleProcessOOMKill</code> is set <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 700</code>, which is used to determine if the kubelet should group kill all process within a container or kill on the concerned process. The default value for this cgroupv1 is <code class="language-plaintext highlighter-rouge">true</code>, and cannot be false, but in the cgroupv2 it could be either <code class="language-plaintext highlighter-rouge">true</code> or <code class="language-plaintext highlighter-rouge">false</code>, and the default is <code class="language-plaintext highlighter-rouge">false</code>.</li>
</ul>

<p><strong>JUST NOTE</strong></p>

<p>When we talk about group kill in the context of OOM (Out Of Memory) in containers, it means all processes within the same container (not the pod) are killed when an OOM event occurs.</p>

<p>To clarify the different scopes:</p>

<ul>
  <li>When <code class="language-plaintext highlighter-rouge">SingleProcessOOMKill=true</code>, the Out-Of-Memory (OOM) killer behavior is configured to:
    <ul>
      <li>Selectively terminate only the specific process that exceeds its memory limit</li>
      <li>Preserve other processes within the same container</li>
      <li>Maintain the integrity of other containers within the pod</li>
    </ul>
  </li>
</ul>

<p>This granular OOM control helps maintain system stability while minimizing the impact of memory-related terminations.</p>

<ul>
  <li>When <code class="language-plaintext highlighter-rouge">SingleProcessOOMKill=false</code>, the OOM killer operates in group termination mode:
    <ul>
      <li>Terminates all processes within the memory-exceeding container</li>
      <li>This conservative approach ensures complete cleanup of potentially unstable containers</li>
      <li>Helps prevent memory leaks and cascading failures</li>
      <li>Maintains pod-level isolation by preserving other containers in the pod</li>
      <li>Container restart behavior is governed by the pod’s <code class="language-plaintext highlighter-rouge">restartPolicy</code> specification</li>
    </ul>
  </li>
</ul>

<p>The term “group” in this context specifically denotes the process group within a single container’s namespace, not the broader pod scope. This container-level isolation ensures that OOM events are handled independently for each container, maintaining the pod’s overall stability and resource boundaries.</p>

<p>Consider a pod with two containers A and B:</p>
<ul>
  <li>An OOM event in container A triggers the configured OOM killer behavior (single-process or group) exclusively within container A’s namespace</li>
  <li>Container B remains unaffected, maintaining its resource allocation and runtime state
This isolation is fundamental to Kubernetes’ container orchestration model, ensuring that resource-related issues in one container do not cascade to other containers within the same pod.</li>
</ul>

<p><strong>/JUST NOTE</strong></p>

<ul>
  <li>A generic runtime manager is created, <code class="language-plaintext highlighter-rouge">runtime</code>, at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 714</code>, which is used to manage the runtime of the containers. <code class="language-plaintext highlighter-rouge">runtime</code> is an instance to <code class="language-plaintext highlighter-rouge">kubeGenericRuntimeManager</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/kuberuntime/kuberuntime_manager.go: 98</code>) which implements interface <code class="language-plaintext highlighter-rouge">KubeGenericRuntime</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/kuberuntime/kuberuntime_manager.go: 182</code>). <code class="language-plaintext highlighter-rouge">KubeGenericRuntime</code> is a interface contains interfaces for container runtime and command.</li>
</ul>

<p><strong>DEEPER DIVE</strong></p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	<span class="c">// pkg/kubelet/kubelet.go: 714</span>
	<span class="n">runtime</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">kuberuntime</span><span class="o">.</span><span class="n">NewKubeGenericRuntimeManager</span><span class="p">(</span>
		<span class="c">// FilterEventRecorder creates an event recorder to record object's event except implicitly required container's, like infra container.</span>
		<span class="n">kubecontainer</span><span class="o">.</span><span class="n">FilterEventRecorder</span><span class="p">(</span><span class="n">kubeDeps</span><span class="o">.</span><span class="n">Recorder</span><span class="p">),</span>
		<span class="c">// Manages container health check results.</span>
		<span class="n">klet</span><span class="o">.</span><span class="n">livenessManager</span><span class="p">,</span>
		<span class="n">klet</span><span class="o">.</span><span class="n">readinessManager</span><span class="p">,</span>
		<span class="n">klet</span><span class="o">.</span><span class="n">startupManager</span><span class="p">,</span>
		<span class="c">// /var/lib/kubelet is the root directory of the kubelet.</span>
		<span class="n">rootDirectory</span><span class="p">,</span>
		<span class="c">// podLogsDir is a custom root directory path kubelet will use to place pod's log files.</span>
		<span class="c">// /var/log/pods</span>
		<span class="n">podLogsDirectory</span><span class="p">,</span>
		<span class="c">// An instance of the MachineInfo struct.</span>
		<span class="n">machineInfo</span><span class="p">,</span>
		<span class="c">// serves as the authoritative source for pod state within the kubelet</span>
		<span class="n">klet</span><span class="o">.</span><span class="n">podWorkers</span><span class="p">,</span>
		<span class="c">// An iterface to OS specific operations.</span>
		<span class="n">kubeDeps</span><span class="o">.</span><span class="n">OSInterface</span><span class="p">,</span>
		<span class="c">// An instance of the kubelet object</span>
		<span class="n">klet</span><span class="p">,</span>
		<span class="c">// HTTP client to make requests to the container lifecycle handler.</span>
		<span class="n">insecureContainerLifecycleHTTPClient</span><span class="p">,</span>
		<span class="c">// Period for the exponential backoff for image pulls.</span>
		<span class="n">imageBackOff</span><span class="p">,</span>
		<span class="c">// Tells kubelet to serialize image pulls, default: true</span>
		<span class="n">kubeCfg</span><span class="o">.</span><span class="n">SerializeImagePulls</span><span class="p">,</span>
		<span class="c">// The maximum number of concurrent image pull operations.</span>
		<span class="n">kubeCfg</span><span class="o">.</span><span class="n">MaxParallelImagePulls</span><span class="p">,</span>
		<span class="c">// RegistryPullQPS is the limit of registry pulls per second.</span>
		<span class="kt">float32</span><span class="p">(</span><span class="n">kubeCfg</span><span class="o">.</span><span class="n">RegistryPullQPS</span><span class="p">),</span>
		<span class="c">// RegistryBurst is the maximum size of bursty pulls,</span>
		<span class="c">// temporarily allows pulls to burst to this number,</span>
		<span class="c">// while still not exceeding RegistryPullQPS</span>
		<span class="kt">int</span><span class="p">(</span><span class="n">kubeCfg</span><span class="o">.</span><span class="n">RegistryBurst</span><span class="p">),</span>
		<span class="n">imageCredentialProviderConfigFile</span><span class="p">,</span>
		<span class="n">imageCredentialProviderBinDir</span><span class="p">,</span>
		<span class="c">// As menstioned above, the singleProcessOOMKill is set to true.</span>
		<span class="n">singleProcessOOMKill</span><span class="p">,</span>
		<span class="c">// Boolean, enables CPU CFS quota enforcement for containers that specify CPU limits.</span>
		<span class="n">kubeCfg</span><span class="o">.</span><span class="n">CPUCFSQuota</span><span class="p">,</span>
		<span class="c">// CPU CFS quota period value, cpu.cfs_period_us. Default: 100ms</span>
		<span class="n">kubeCfg</span><span class="o">.</span><span class="n">CPUCFSQuotaPeriod</span><span class="p">,</span>
		<span class="c">// An instance of thread-safe instance to RuntimeService interface.</span>
		<span class="c">// returned from staging/src/k8s.io/cri-client/pkg/remote_runtime.go: 83</span>
		<span class="n">kubeDeps</span><span class="o">.</span><span class="n">RemoteRuntimeService</span><span class="p">,</span>
		<span class="c">// An instance to thread-safe instance of ImageManagerService interface.</span>
	 	<span class="c">// returned from staging/src/k8s.io/cri-client/pkg/remote_image.go: 50</span>
		<span class="n">kubeDeps</span><span class="o">.</span><span class="n">RemoteImageService</span><span class="p">,</span>
		<span class="c">// Interface to manage containers on the machine</span>
		<span class="c">// returned from pkg/kubelet/cm/container_manager_linux.go: 206</span>
		<span class="n">kubeDeps</span><span class="o">.</span><span class="n">ContainerManager</span><span class="p">,</span>
		<span class="c">// Manager for container logs</span>
		<span class="n">klet</span><span class="o">.</span><span class="n">containerLogManager</span><span class="p">,</span>
		<span class="c">// Handles RuntimeClass objects for the Kubelet.</span>
		<span class="n">klet</span><span class="o">.</span><span class="n">runtimeClassManager</span><span class="p">,</span>
		<span class="c">// seccomp profile for all workloads, default: false</span>
		<span class="n">seccompDefault</span><span class="p">,</span>
		<span class="c">// swapBehavior configures swap memory available to container workloads</span>
		<span class="c">// For cgroupsv1 this is not configured</span>
		<span class="n">kubeCfg</span><span class="o">.</span><span class="n">MemorySwap</span><span class="o">.</span><span class="n">SwapBehavior</span><span class="p">,</span>
		<span class="c">// Returns the absolute value of the Node Allocatable</span>
		<span class="c">// which is primarily useful for enforcement</span>
		<span class="n">kubeDeps</span><span class="o">.</span><span class="n">ContainerManager</span><span class="o">.</span><span class="n">GetNodeAllocatableAbsolute</span><span class="p">,</span>
		<span class="c">// MemoryThrottlingFactor specifies the factor multiplied by the memory limit or node allocatable memory</span>
		<span class="c">// when setting the cgroupv2 memory.high value to enforce MemoryQoS.</span>
		<span class="c">// Decreasing this factor will set lower high limit for container cgroups and put heavier reclaim pressure</span>
		<span class="c">// while increasing will put less reclaim pressure.</span>
		<span class="o">*</span><span class="n">kubeCfg</span><span class="o">.</span><span class="n">MemoryThrottlingFactor</span><span class="p">,</span>
		<span class="c">// An instance of PodStartupLatencyTracker interface.</span>
		<span class="n">kubeDeps</span><span class="o">.</span><span class="n">PodStartupLatencyTracker</span><span class="p">,</span>
		<span class="c">// TracerProvider provides Tracers that are used by instrumentation code</span>
		<span class="n">kubeDeps</span><span class="o">.</span><span class="n">TracerProvider</span><span class="p">,</span>
	<span class="p">)</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">NewKubeGenericRuntimeManager</code> constructor function initializes the Kubernetes Container Runtime Interface (CRI) manager, which serves as the primary interface between the Kubelet and the container runtime.</p>

<p>Architectural Components:</p>

<ul>
  <li>Purpose:
    <ul>
      <li>Creates a container runtime manager that handles container operations in Kubernetes</li>
      <li>Interfaces with the underlying container runtime (like Docker or containerd) through CRI (Container Runtime Interface)</li>
    </ul>
  </li>
  <li>Important Parameters:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">recorder</code>: For recording events</li>
      <li>Various managers for handling container probes (liveness, readiness, startup)</li>
      <li><code class="language-plaintext highlighter-rouge">runtimeService</code>: Interface to the container runtime</li>
      <li><code class="language-plaintext highlighter-rouge">imageService</code>: Service for handling container images</li>
      <li>Configuration options like:
        <ul>
          <li><code class="language-plaintext highlighter-rouge">serializeImagePulls</code>: Controls parallel image pulling</li>
          <li><code class="language-plaintext highlighter-rouge">maxParallelImagePulls</code>: Limits concurrent image pulls</li>
          <li><code class="language-plaintext highlighter-rouge">seccompDefault</code>: Security computing mode settings</li>
          <li><code class="language-plaintext highlighter-rouge">memorySwapBehavior</code>: Memory swap configuration</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Main Operations:
    <ul>
      <li>Creates an instrumented runtime and image service, with metrics</li>
      <li>Initializes a new <code class="language-plaintext highlighter-rouge">kubeGenericRuntimeManager</code> with the provided parameters</li>
      <li>Verifies runtime API version compatibility</li>
      <li>Sets up credential providers for container image pulling</li>
      <li>Initializes several sub-components:
        <ul>
          <li>Image puller</li>
          <li>Lifecycle handler runner</li>
          <li>Container garbage collector</li>
          <li>Version cache</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Version Checking:</li>
</ul>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="n">typedVersion</span><span class="o">.</span><span class="n">Version</span> <span class="o">!=</span> <span class="n">kubeRuntimeAPIVersion</span> <span class="p">{</span>
    <span class="k">return</span> <span class="no">nil</span><span class="p">,</span> <span class="n">ErrVersionNotSupported</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
  <li>Ensures the runtime API version matches the supported version</li>
  <li>
    <p>Returns an error if versions are incompatible</p>
  </li>
  <li>Component Initialization:
    <ul>
      <li>Sets up image pulling management:</li>
      <li>Sets up lifecycle handler(<code class="language-plaintext highlighter-rouge">postStart</code> and <code class="language-plaintext highlighter-rouge">preStop</code>) for container</li>
      <li>Sets up new container garbage collector</li>
      <li>The <code class="language-plaintext highlighter-rouge">podStateProvider</code> is assigned the <code class="language-plaintext highlighter-rouge">podWorkers</code> instance</li>
    </ul>
  </li>
</ul>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">kubeRuntimeManager</span><span class="o">.</span><span class="n">imagePuller</span> <span class="o">=</span> <span class="n">images</span><span class="o">.</span><span class="n">NewImageManager</span><span class="p">(</span>
    <span class="n">kubecontainer</span><span class="o">.</span><span class="n">FilterEventRecorder</span><span class="p">(</span><span class="n">recorder</span><span class="p">),</span>
    <span class="n">kubeRuntimeManager</span><span class="p">,</span>
    <span class="n">imageBackOff</span><span class="p">,</span>
    <span class="n">serializeImagePulls</span><span class="p">,</span>
    <span class="n">maxParallelImagePulls</span><span class="p">,</span>
    <span class="n">imagePullQPS</span><span class="p">,</span>
    <span class="n">imagePullBurst</span><span class="p">,</span>
    <span class="n">podPullingTimeRecorder</span><span class="p">)</span>
<span class="n">kubeRuntimeManager</span><span class="o">.</span><span class="n">runner</span> <span class="o">=</span> <span class="n">lifecycle</span><span class="o">.</span><span class="n">NewHandlerRunner</span><span class="p">(</span><span class="n">insecureContainerLifecycleHTTPClient</span><span class="p">,</span> <span class="n">kubeRuntimeManager</span><span class="p">,</span> <span class="n">kubeRuntimeManager</span><span class="p">,</span> <span class="n">recorder</span><span class="p">)</span>
<span class="n">kubeRuntimeManager</span><span class="o">.</span><span class="n">containerGC</span> <span class="o">=</span> <span class="n">newContainerGC</span><span class="p">(</span><span class="n">runtimeService</span><span class="p">,</span> <span class="n">podStateProvider</span><span class="p">,</span> <span class="n">kubeRuntimeManager</span><span class="p">,</span> <span class="n">tracer</span><span class="p">)</span>
<span class="n">kubeRuntimeManager</span><span class="o">.</span><span class="n">podStateProvider</span> <span class="o">=</span> <span class="n">podStateProvider</span>
</code></pre></div></div>

<p>The lifecycle handler is an interface of <code class="language-plaintext highlighter-rouge">HandlerRunner</code> for a container(<code class="language-plaintext highlighter-rouge">pkg/kubelet/container/helpers.go: 42</code>). This interface implements a <code class="language-plaintext highlighter-rouge">Run</code> function(<code class="language-plaintext highlighter-rouge">pkg/kubelet/lifecycle/handlers.go: 72</code>) which is responsible for executing different handler types on the container.</p>

<ol>
  <li>Error Handling:
    <ul>
      <li>Handles initialization errors gracefully</li>
      <li>Logs errors with detailed information</li>
      <li>Exits if critical components (like credential provider plugins) fail to initialize</li>
    </ul>
  </li>
</ol>

<p>This function is a crucial part of Kubernetes’ container runtime abstraction layer, allowing it to work with different container runtimes while maintaining a consistent interface for the rest of the system.</p>

<p><strong>/DEEPER DIVE</strong></p>

<ul>
  <li>The <code class="language-plaintext highlighter-rouge">runtime</code> is assigned to:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">klet.containerRuntime</code> at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 751</code>, which is used to manage the container runtime.</li>
      <li><code class="language-plaintext highlighter-rouge">klet.streamingRuntime</code> at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 752</code>, which handles container streaming.</li>
      <li><code class="language-plaintext highlighter-rouge">klet.runner</code> at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 753</code>, which will be used to run commands inside of the container.</li>
    </ul>
  </li>
  <li>Create an instance of <code class="language-plaintext highlighter-rouge">RuntimeCache</code> in <code class="language-plaintext highlighter-rouge">runtimeCache</code> at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 755</code>, runtimeCache caches a list of pods. It records a timestamp (cacheTime) right before updating the pods, so the timestamp is at most as new as the pods (and can be slightly older). The timestamp always moves forward. Callers are expected not to modify the pods returned from GetPods. And then that is assigned to <code class="language-plaintext highlighter-rouge">klet.runtimeCache</code>.</li>
  <li>Create an instance of <code class="language-plaintext highlighter-rouge">HostStatsProvider</code> that defines an interface for providing host stats associated with pods that are managed by the kubelet, and assigns it <code class="language-plaintext highlighter-rouge">hostStatsProvider</code> at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 762</code>.</li>
  <li>The container runtime stats providers is initialized into <code class="language-plaintext highlighter-rouge">klet.StatsProvider</code>, creating an instance of <code class="language-plaintext highlighter-rouge">Provider</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/stats/provider.go: 91</code>) which provides the stats of the node and the pod-managed containers, at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 766</code>.</li>
  <li>An <code class="language-plaintext highlighter-rouge">eventChannel</code> is created for <code class="language-plaintext highlighter-rouge">PodLifecycleEvent</code> at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 786</code>. And since the <code class="language-plaintext highlighter-rouge">EventedPLEG</code> feature flag is still in <code class="language-plaintext highlighter-rouge">Alpha</code> a generic pleg interface is created at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 812</code> with an instance of <code class="language-plaintext highlighter-rouge">PodLifecycleEventGenerator</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/pleg/pleg.go</code>), which container functions for generating pod lifecycle events, and stored in <code class="language-plaintext highlighter-rouge">klet.gleg</code>.</li>
  <li>A <code class="language-plaintext highlighter-rouge">newRuntimeState</code> is assiged to <code class="language-plaintext highlighter-rouge">klet.runtimeState</code>, by calling the function with the same, passing the <code class="language-plaintext highlighter-rouge">maxWaitForContainerRuntime</code> which defines the maximum wait time for the runtime to be available. Ref, <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 815</code></li>
  <li>A PLEG health check is added to <code class="language-plaintext highlighter-rouge">klet.runtimeState</code>, by calling the <code class="language-plaintext highlighter-rouge">addHealthCheck</code> at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 816</code></li>
  <li><strong>IMPORTANT</strong>: The <code class="language-plaintext highlighter-rouge">runtimeState.cidr</code> is update with CIDR obtained from <code class="language-plaintext highlighter-rouge">kudeCfg.PodCIDR</code>, by calling the <code class="language-plaintext highlighter-rouge">klet.updatePodCIDR</code> function at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 820</code>. <code class="language-plaintext highlighter-rouge">updatePodCIDR</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet_network.go: 44</code>) updates the pod CIDR in the runtime state if it is different from the current CIDR. Return true if pod CIDR is actually changed.</li>
  <li>An instance to <code class="language-plaintext highlighter-rouge">GC</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/container/container_gc.go: 43</code>) interface, which manages garbage collection of dead containers, is created by calling <code class="language-plaintext highlighter-rouge">NewContainerGC</code> at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 825</code>. And upon error, this assigned to <code class="language-plaintext highlighter-rouge">klet.containerGC</code> at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 829</code></li>
  <li>An instance of <code class="language-plaintext highlighter-rouge">podContainerDeletor</code> struct(<code class="language-plaintext highlighter-rouge">pkg/kubelet/pod_container_deletor.go: 36</code>), created into <code class="language-plaintext highlighter-rouge">klet.containerDeletor</code> by calling the <code class="language-plaintext highlighter-rouge">newPodContainerDeletor</code> at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 830</code>. A <code class="language-plaintext highlighter-rouge">podContainerDeletor</code>, is an asynchronous worker(a goroutine) that receives containers ids from a channel, and calls <code class="language-plaintext highlighter-rouge">runtime.DeleteContaier</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/pod_container_deletor.go: 52</code>) which in turn calls <code class="language-plaintext highlighter-rouge">removeContainer</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/kuberuntime/kuberuntime_container.go: 1314</code>), to remove container and container logs.</li>
  <li>An instance of <code class="language-plaintext highlighter-rouge">ImageGCManager</code> interface(<code class="language-plaintext highlighter-rouge">pkg/kubelet/images/image_gc_manager.go: 69</code>), which managed the lifecycle of all images, is create by calling <code class="language-plaintext highlighter-rouge">NewImageGCManager</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/images/images_gc_manager.go: 184</code>) at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 833</code>. And upon no error that instance is assigned to <code class="language-plaintext highlighter-rouge">klet.imageManager</code>(yes, not <code class="language-plaintext highlighter-rouge">*GCManager</code>).</li>
  <li>Based on the <code class="language-plaintext highlighter-rouge">kubeDeps.TLSOptions</code>, the <code class="language-plaintext highlighter-rouge">klet.serverCertificateManager</code> is setup at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 839-851</code>.</li>
  <li>The <code class="language-plaintext highlighter-rouge">klet.probeManager</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 867</code>) is initialized to an instance of prode manager interface, <code class="language-plaintext highlighter-rouge">Manager</code> defined at <code class="language-plaintext highlighter-rouge">pkg/kubelet/prober/prober_manager.go: 71</code> which creates a probe worker for every container that specifies a probe, and it also updates each of the pod container status.</li>
  <li>A <code class="language-plaintext highlighter-rouge">tokenManager</code> is created using the <code class="language-plaintext highlighter-rouge">NewManager</code> function from the <code class="language-plaintext highlighter-rouge">token</code> package at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 876</code>, this must be used later one. The <code class="language-plaintext highlighter-rouge">tokenManager</code> is responsible for managing the service account tokens.</li>
  <li>At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 878-891</code>,
    <ul>
      <li><code class="language-plaintext highlighter-rouge">clusterTrustBundleManger</code> is initialized to manager <code class="language-plaintext highlighter-rouge">ClusterTrustBundle</code>(<code class="language-plaintext highlighter-rouge">staging/src/k8s.io/api/certificate/v1alpha1/types.go: 44</code>)
        <ul>
          <li>If <code class="language-plaintext highlighter-rouge">ClusterTrustBundleProjection</code> feature flag is enabled, an informer based manager is created. Otherwise, a no-op manager is used instead.</li>
          <li>TODO: Check the above in the existing clusters as default for <code class="language-plaintext highlighter-rouge">ClusterTrustBundleProjection</code> is <code class="language-plaintext highlighter-rouge">false</code>, and its still an alpha feature.</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 896</code>
    <ul>
      <li>The <code class="language-plaintext highlighter-rouge">klet.volumePluginMgr</code> is initialized using the <code class="language-plaintext highlighter-rouge">NewInitializedVolumePluginMgr</code> function.
        <ul>
          <li>The <code class="language-plaintext highlighter-rouge">volumePluginMgr</code>, manages all volume plugins (e.g., CSI, FlexVolume, in-tree plugins) that the Kubelet uses to provision and manage storage for pods.</li>
          <li>if <code class="language-plaintext highlighter-rouge">err</code>, then this function returns</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 901</code>, create a <code class="language-plaintext highlighter-rouge">PluginManager</code> instance by calling <code class="language-plaintext highlighter-rouge">pluginmanager.NewPluginManager</code>, and assigns it to <code class="language-plaintext highlighter-rouge">klet.pluginManager</code>. <code class="language-plaintext highlighter-rouge">pluginManager</code>, manages the lifecycle of kubelet plugins.</li>
  <li>At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 908-912</code>, if <code class="language-plaintext highlighter-rouge">experimentalMounterPath</code> is set it configures DNS in the containerized mounter’s environment.</li>
  <li>At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 915-927</code>, creates a <code class="language-plaintext highlighter-rouge">VolumeManager</code> instance by calling <code class="language-plaintext highlighter-rouge">volumemanager.NewVolumeManager</code> and assigns it to <code class="language-plaintext highlighter-rouge">klet.volumeManager</code>. <code class="language-plaintext highlighter-rouge">volumeManager</code>, is responsible for managing the lifecycle of volumes (e.g., attaching, mounting, unmounting, detaching) for pods running on the node.</li>
  <li>At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 929</code> variable <code class="language-plaintext highlighter-rouge">boMax</code> is initialized to <code class="language-plaintext highlighter-rouge">MaxContainerBackOff</code>(which is <code class="language-plaintext highlighter-rouge">300 * time.Second</code>).</li>
  <li>At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 930</code> variable <code class="language-plaintext highlighter-rouge">base</code> is initialized to <code class="language-plaintext highlighter-rouge">containerBackOffPeriod</code>(which is <code class="language-plaintext highlighter-rouge">time.Second * 10</code>)</li>
  <li>At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 931-936</code> if the <code class="language-plaintext highlighter-rouge">KubeletCrashLoopBackOffMax</code> feature gate(currently in <code class="language-plaintext highlighter-rouge">Alpha</code> and default <code class="language-plaintext highlighter-rouge">false</code>) is enabled, <code class="language-plaintext highlighter-rouge">boMax</code> is reset accordingly.</li>
  <li>At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 937</code> creates a <code class="language-plaintext highlighter-rouge">BackOff</code> instance by calling <code class="language-plaintext highlighter-rouge">flowcontrol.NewBackOff</code> and assigns it <code class="language-plaintext highlighter-rouge">klet.backOff</code>. And at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 938</code>, assign an anonymous function to the <code class="language-plaintext highlighter-rouge">klet.backOff.HasExpiredFunc</code>, which returns a boolean on the expiry backOff which reset the backOff counter.</li>
  <li>At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 943</code> creates an <code class="language-plaintext highlighter-rouge">Manager</code> instance(<code class="language-plaintext highlighter-rouge">pkg/kubelet/eviction/types.go</code>) which is implemented by the <code class="language-plaintext highlighter-rouge">managerImpl</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/eviction/eviction_manager.go: 66</code>) by calling the <code class="language-plaintext highlighter-rouge">eviction.NewManager</code>. The instance of <code class="language-plaintext highlighter-rouge">Manager</code> is passed in both <code class="language-plaintext highlighter-rouge">evictionManager</code> and <code class="language-plaintext highlighter-rouge">evictionAdmitHandler</code>. And at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 946-947</code>, these are assigned to <code class="language-plaintext highlighter-rouge">klet.evictionManager.evictionManager</code> and <code class="language-plaintext highlighter-rouge">klet.admitHandlers</code> respectively.</li>
  <li>At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 951</code> the slice <code class="language-plaintext highlighter-rouge">safeAndUnsafeSysctls</code> is assigned the slice containing both <code class="language-plaintext highlighter-rouge">sysctl.SafeSysctlAllowlist()</code>, and user provided(though configuration) <code class="language-plaintext highlighter-rouge">allowedUnsafeSysctls</code>. And at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 952</code>, the <code class="language-plaintext highlighter-rouge">safeAndUnsafeSysctls</code> is verified by passing to <code class="language-plaintext highlighter-rouge">NewAllowList</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/sysctl/allowlist.go: 44</code>), and the returns <code class="language-plaintext highlighter-rouge">patternAllowlist</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/sysctl/allowlist.go: 36</code>) in to <code class="language-plaintext highlighter-rouge">sysctlsAllowList</code>. On <code class="language-plaintext highlighter-rouge">err</code> in this case this function returns with an <code class="language-plaintext highlighter-rouge">error</code>.</li>
  <li>At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 956</code> the <code class="language-plaintext highlighter-rouge">sysctlsAllowlist</code> is add to <code class="language-plaintext highlighter-rouge">klet.admitHandlers</code>.</li>
  <li>At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 959</code> an instance of <code class="language-plaintext highlighter-rouge">activeDeadlineHandler</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/active_deadline.go: 36</code>) is created by calling <code class="language-plaintext highlighter-rouge">newActiveDeadlineHandler</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/active_deadline.go: 46</code>), and assigned to <code class="language-plaintext highlighter-rouge">activeDeadlineHandler</code>. A deadline handler, manages the pod deadline. And this is added to <code class="language-plaintext highlighter-rouge">klet</code>’s  <code class="language-plaintext highlighter-rouge">PodSyncLoopHandler</code> and <code class="language-plaintext highlighter-rouge">PodSyncHandler</code> at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 963</code> and <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 964</code> respectively. On <code class="language-plaintext highlighter-rouge">err</code> in this case this function returns with an <code class="language-plaintext highlighter-rouge">error</code>.</li>
  <li>At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 966</code>, an admit handler responsible for allocating resources to pod <code class="language-plaintext highlighter-rouge">GetAllocateResourcesPodAdmitHandler</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/cm/container_manager.go: 136</code>), added to <code class="language-plaintext highlighter-rouge">klet.admitHandler</code>.</li>
  <li>At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 968-969</code>, an instance of the <code class="language-plaintext highlighter-rouge">CriticalPodAdmissionHandler</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/preemption/preemption.go: 45</code>) is created by calling the <code class="language-plaintext highlighter-rouge">NewCriticalPodAdmissionHandler</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/preemption/preemption.go: 63</code>) and assigned to <code class="language-plaintext highlighter-rouge">criticalPodAdmissionHandler</code>, if not <code class="language-plaintext highlighter-rouge">error</code> and then it added to the <code class="language-plaintext highlighter-rouge">klet.admitHandlers</code> with predicates. Its critical to understand the <code class="language-plaintext highlighter-rouge">CriticalPodAdmissionHandler</code> is in fact an <code class="language-plaintext highlighter-rouge">AdmissionFailureHandler</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/lifecycle/predicate.go: 87</code>)</li>
  <li>If <code class="language-plaintext highlighter-rouge">linux</code>, at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 977</code> an instance of <code class="language-plaintext highlighter-rouge">Validator</code>(<code class="language-plaintext highlighter-rouge">pkg/security/apparmor/validate.go: 34</code>) interface which validates that a pod with AppArmor profile can be run by that Node, and assigned to <code class="language-plaintext highlighter-rouge">klet.appArmorValidator</code></li>
  <li>If <code class="language-plaintext highlighter-rouge">linux</code>, at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 977</code> an instance of <code class="language-plaintext highlighter-rouge">Validator</code>(<code class="language-plaintext highlighter-rouge">pkg/security/apparmor/validate.go: 34</code>) interface which validates that a pod with AppArmor profile can be run by that Node, and assigned to <code class="language-plaintext highlighter-rouge">klet.appArmorValidator</code>, this is added to the <code class="language-plaintext highlighter-rouge">klet.adminHandlers</code> at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 978</code>.</li>
  <li><strong>IMPORTANT</strong>: At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 981-992</code>, the <code class="language-plaintext highlighter-rouge">klet.nodeLeaseController</code> is initialized, this controller is responsible for renewing lease of the kubelet with the api-server. A lease is the indicator the node’s health. The <code class="language-plaintext highlighter-rouge">leaseDuration</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 981</code>) default to <code class="language-plaintext highlighter-rouge">40s</code>, and <code class="language-plaintext highlighter-rouge">renewInterval</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 982</code>) is one-fourth of the <code class="language-plaintext highlighter-rouge">leasDuration</code>.</li>
  <li><strong>IMPORTANT:</strong> At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 995</code>, the node <code class="language-plaintext highlighter-rouge">shutdownManager</code> is assigned an instance of <code class="language-plaintext highlighter-rouge">Manager</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/nodeshutdown/nodeshutdown_manager.go: 42</code>) interface, which will handle aspects of the node shutdown, including honoring the graceful shutdown periods, for critical pods or pods by priority. The <code class="language-plaintext highlighter-rouge">shutdownManager</code>, is then assigned to the <code class="language-plaintext highlighter-rouge">klet.shutdownManager</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 1009</code>)</li>
  <li>At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 1010</code> an instance of the <code class="language-plaintext highlighter-rouge">UsernsManager</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/userns/userns_manager.go: 54</code>), is created which ensures avoiding the UID and GID with those of on the node, and effective manage User Namespaces, and assigned to <code class="language-plaintext highlighter-rouge">klet.usernsManager</code>.</li>
  <li>At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 1014</code> the <code class="language-plaintext highlighter-rouge">shutdownManager</code>(from above) is added to the <code class="language-plaintext highlighter-rouge">klet.admitHandlers</code></li>
  <li>At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 1018</code> the latest <code class="language-plaintext highlighter-rouge">kubeCfg</code>, is assigned to the <code class="language-plaintext highlighter-rouge">klet.kubeletConfiguration</code>.</li>
  <li>At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 1022</code> the node status functions are generated and assinged to <code class="language-plaintext highlighter-rouge">klet.setNodeStatusFuncs</code> by calling the <code class="language-plaintext highlighter-rouge">defaultNodeStatusFuncs</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet_node_status.go: 760</code>), which returns an array of callback functions.</li>
  <li>If the <code class="language-plaintext highlighter-rouge">SystemdWatchdog</code> feature flag is enabled, which in most mordern Kubernetes clusters(&gt;= 1.32), this allows using systemd watchdog to monitor the health status of kubelet. At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 1028</code> the <code class="language-plaintext highlighter-rouge">klet.healthChecker</code> is updated with the new checkers.</li>
</ul>

<p>Then at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 1033</code> the function returns created <code class="language-plaintext highlighter-rouge">klet</code> instance.</p>

<p><strong>/DEEPER DIVE</strong></p>

<p>Upon successful initialization at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 1310</code>, the Kubelet performs two critical operations:</p>
<ol>
  <li>Emits a startup event via <code class="language-plaintext highlighter-rouge">k.BirthCry()</code> (<code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 1341</code>)</li>
  <li>Initiates garbage collection routines through <code class="language-plaintext highlighter-rouge">k.StartGarbageCollection()</code> (<code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 1343</code>)</li>
</ol>

<p>The <code class="language-plaintext highlighter-rouge">createAndInitKubelet</code> function at <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 1345</code> returns the initialized Kubelet instance <code class="language-plaintext highlighter-rouge">k</code> to the <code class="language-plaintext highlighter-rouge">RunKubelet</code> function. Before proceeding to the <code class="language-plaintext highlighter-rouge">startKubelet</code> function (<code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go:1281</code>), two configuration parameters are set:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">podCfg</code> is initialized with <code class="language-plaintext highlighter-rouge">kubeDeps.PodConfig</code></li>
  <li>The Linux resource limit (rlimit) for maximum open files is configured via <code class="language-plaintext highlighter-rouge">kubeServer.MaxOpenFiles</code> (<code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go: 1277</code>)</li>
</ul>

<p>Now back to the <code class="language-plaintext highlighter-rouge">startKubelet</code> function at <code class="language-plaintext highlighter-rouge">cmd/kubelet/app/server.go:1281</code>. Lets dive deeper into the <code class="language-plaintext highlighter-rouge">startKubelet</code> function.</p>

<p><strong>DEEPER DIVE</strong></p>

<p>This function, <code class="language-plaintext highlighter-rouge">startKubelet</code>, is responsible for starting the Kubelet, which is a core Kubernetes component that runs on each node in the cluster. The Kubelet’s primary role is to ensure that containers are running in Pods as expected. The function starts the Kubelet’s main logic and optionally enables its HTTP servers for serving metrics, debugging, and other endpoints. Here’s a detailed breakdown of what this function does:</p>

<hr />

<p><strong>Start the Kubelet’s Main Logic</strong></p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// cmd/kubelet/app/server.go:1289</span>
<span class="k">go</span> <span class="n">k</span><span class="o">.</span><span class="n">Run</span><span class="p">(</span><span class="n">podCfg</span><span class="o">.</span><span class="n">Updates</span><span class="p">())</span>
</code></pre></div></div>
<ul>
  <li><code class="language-plaintext highlighter-rouge">k.Run(podCfg.Updates())</code> starts the Kubelet’s main event loop.
    <ul>
      <li><code class="language-plaintext highlighter-rouge">podCfg.Updates()</code> returns a channel that provides updates to the Kubelet about changes in Pod configurations (e.g., new Pods, updates to existing Pods, or deletions).</li>
      <li>The Kubelet listens to this channel and takes appropriate actions, such as creating, updating, or deleting Pods on the node.</li>
    </ul>
  </li>
  <li>The <code class="language-plaintext highlighter-rouge">go</code> keyword runs this logic in a separate goroutine, allowing the Kubelet to operate asynchronously.</li>
  <li><strong>TODO</strong>: Define the logic for handling Pod updates.</li>
</ul>

<hr />

<p><strong>Start the Kubelet’s Secure HTTP Server (if enabled)</strong></p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// cmd/kubelet/app/server.go:1292</span>
<span class="k">if</span> <span class="n">enableServer</span> <span class="p">{</span>
    <span class="k">go</span> <span class="n">k</span><span class="o">.</span><span class="n">ListenAndServe</span><span class="p">(</span><span class="n">kubeCfg</span><span class="p">,</span> <span class="n">kubeDeps</span><span class="o">.</span><span class="n">TLSOptions</span><span class="p">,</span> <span class="n">kubeDeps</span><span class="o">.</span><span class="n">Auth</span><span class="p">,</span> <span class="n">kubeDeps</span><span class="o">.</span><span class="n">TracerProvider</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
  <li>If <code class="language-plaintext highlighter-rouge">enableServer</code> is <code class="language-plaintext highlighter-rouge">true</code>, the Kubelet starts a secure HTTP server.
    <ul>
      <li><code class="language-plaintext highlighter-rouge">k.ListenAndServe</code> starts the server, which listens for HTTPS requests.</li>
      <li>The server is configured using:
        <ul>
          <li><code class="language-plaintext highlighter-rouge">kubeCfg</code>: The Kubelet’s configuration.</li>
          <li><code class="language-plaintext highlighter-rouge">kubeDeps.TLSOptions</code>: TLS settings for securing the server.</li>
          <li><code class="language-plaintext highlighter-rouge">kubeDeps.Auth</code>: Authentication settings for securing access to the server.</li>
          <li><code class="language-plaintext highlighter-rouge">kubeDeps.TracerProvider</code>: Used for distributed tracing (e.g., OpenTelemetry).</li>
        </ul>
      </li>
    </ul>
  </li>
  <li>This server typically serves endpoints for metrics, debugging, and other administrative functions.</li>
  <li>The <code class="language-plaintext highlighter-rouge">go</code> keyword runs the server in a separate goroutine.</li>
  <li><strong>TODO</strong>: Dive deeper into the implementation details of the secure HTTP server.</li>
</ul>

<hr />

<p><strong>Start the Kubelet’s Read-Only HTTP Server (if enabled)</strong></p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// cmd/kubelet/app/server.go:1295</span>
<span class="k">if</span> <span class="n">kubeCfg</span><span class="o">.</span><span class="n">ReadOnlyPort</span> <span class="o">&gt;</span> <span class="m">0</span> <span class="p">{</span>
    <span class="k">go</span> <span class="n">k</span><span class="o">.</span><span class="n">ListenAndServeReadOnly</span><span class="p">(</span><span class="n">netutils</span><span class="o">.</span><span class="n">ParseIPSloppy</span><span class="p">(</span><span class="n">kubeCfg</span><span class="o">.</span><span class="n">Address</span><span class="p">),</span> <span class="kt">uint</span><span class="p">(</span><span class="n">kubeCfg</span><span class="o">.</span><span class="n">ReadOnlyPort</span><span class="p">),</span> <span class="n">kubeDeps</span><span class="o">.</span><span class="n">TracerProvider</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
  <li>If the <code class="language-plaintext highlighter-rouge">ReadOnlyPort</code> in the Kubelet’s configuration is greater than 0, a read-only HTTP server is started.
    <ul>
      <li><code class="language-plaintext highlighter-rouge">k.ListenAndServeReadOnly</code> starts the server, which listens for HTTP requests.</li>
      <li>The server binds to the IP address specified in <code class="language-plaintext highlighter-rouge">kubeCfg.Address</code> and the port specified in <code class="language-plaintext highlighter-rouge">kubeCfg.ReadOnlyPort</code>.</li>
      <li>This server provides read-only access to metrics and debugging endpoints, typically without authentication or encryption.</li>
      <li><code class="language-plaintext highlighter-rouge">kubeDeps.TracerProvider</code> is used for distributed tracing.</li>
    </ul>
  </li>
  <li>The <code class="language-plaintext highlighter-rouge">go</code> keyword runs the server in a separate goroutine.</li>
</ul>

<hr />

<p><strong>Start the Kubelet’s Pod Resources Server</strong></p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// cmd/kubelet/app/server.go:1298</span>
<span class="k">go</span> <span class="n">k</span><span class="o">.</span><span class="n">ListenAndServePodResources</span><span class="p">()</span>
</code></pre></div></div>
<ul>
  <li><code class="language-plaintext highlighter-rouge">k.ListenAndServePodResources()</code> starts a gRPC server that serves the Pod Resources API.
    <ul>
      <li>This API provides information about the resources (e.g., CPU, memory, devices) allocated to Pods running on the node.</li>
      <li>It is used by components like the Kubernetes Device Plugins and other resource management tools.</li>
    </ul>
  </li>
  <li>The <code class="language-plaintext highlighter-rouge">go</code> keyword runs this server in a separate goroutine.</li>
  <li><strong>TODO</strong>: Dive into the implementation details of the Pod Resources API.</li>
</ul>

<p>By running these components in separate goroutines, the Kubelet can handle multiple tasks concurrently, ensuring that it can manage Pods, serve metrics, and provide debugging information efficiently.</p>

<p><strong>/DEEPER DIVE</strong></p>

<p><strong>NOTE</strong> Come out of the <code class="language-plaintext highlighter-rouge">startKubelet</code> function.</p>

<p><strong>/DEEPER DIVE</strong></p>

<h2 id="references">References:</h2>
<ul>
  <li><a href="https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/">Kubelet flags</a></li>
  <li><a href="https://kubernetes.io/docs/reference/config-api/kubelet-config.v1beta1/#kubelet-config-k8s-io-v1beta1-KubeletConfiguration">Kubelet configuration</a></li>
  <li><a href="https://kubernetes.io/docs/reference/node/kubelet-config-directory-merging/">Kubelet configuration merging</a></li>
  <li><a href="https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/">Kubelet </a></li>
  <li><a href="https://kubernetes.io/docs/tasks/administer-cluster/reserve-compute-resources/#node-allocatable">Node Allocatable</a></li>
  <li><a href="https://kubernetes.io/docs/concepts/scheduling-eviction/node-pressure-eviction/">Node Eviction</a></li>
  <li><a href="https://kubernetes.io/docs/tasks/administer-cluster/topology-manager">Topology Manager</a></li>
  <li><a href="https://kubernetes.io/docs/concepts/scheduling-eviction/dynamic-resource-allocation/">Dynamic Resource Allocation</a></li>
  <li><a href="https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/">Create a static pod</a></li>
</ul>

<h2 id="some-important-data-structures">Some Important Data Structures</h2>

<ol>
  <li><a href="https://github.com/kubernetes/kubernetes/blob/7bfdda4696f78fc789fe91420f7c8609c71002d0/pkg/kubelet/kubelet.go#L1041">Kubelet</a></li>
  <li><a href="https://github.com/kubernetes/kubernetes/blob/7bfdda4696f78fc789fe91420f7c8609c71002d0/pkg/kubelet/apis/config/types.go#L82">KubeletConfiguration</a></li>
  <li><a href="https://github.com/kubernetes/kubernetes/blob/7bfdda4696f78fc789fe91420f7c8609c71002d0/pkg/kubelet/kubelet.go#L289">Kubelet Dependencies</a></li>
  <li><a href="https://github.com/kubernetes/kubernetes/blob/7bfdda4696f78fc789fe91420f7c8609c71002d0/cmd/kubelet/app/options/options.go#L54">Kubelet Flags</a></li>
  <li><a href="https://github.com/kubernetes/kubernetes/blob/7bfdda4696f78fc789fe91420f7c8609c71002d0/pkg/kubelet/kuberuntime/kuberuntime_manager.go#L98">Kube Generic Runtime Manager</a></li>
  <li><a href="https://github.com/kubernetes/kubernetes/blob/7bfdda4696f78fc789fe91420f7c8609c71002d0/pkg/kubelet/pleg/evented.go#L63">EventedPLEG</a></li>
  <li><a href="https://github.com/kubernetes/kubernetes/blob/7bfdda4696f78fc789fe91420f7c8609c71002d0/pkg/kubelet/pleg/generic.go#L53">GenericPLEG</a></li>
</ol>

<h2 id="contribution-opportunities">Contribution Opportunities:</h2>
<ul>
  <li>Can definitely improve the logic in this <a href="https://vscode.dev/github/kubernetes/kubernetes/blob/master/staging/src/k8s.io/component-helpers/node/util/ips.go#L33">code</a> for <code class="language-plaintext highlighter-rouge">parseNodeIP</code> function, it so difficult to read and digest.</li>
  <li>
    <p>Possible simple contribution to Kubernetes codebase, fix the error log message on line <code class="language-plaintext highlighter-rouge">pkg/kubelet/util/boottime_util_linux.go: 39</code> from</p>

    <div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="n">klog</span><span class="o">.</span><span class="n">InfoS</span><span class="p">(</span><span class="s">"Failed to get boot time from /proc/uptime. Will retry with unix.Sysinfo."</span><span class="p">,</span> <span class="s">"error"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
</code></pre></div>    </div>

    <p>to</p>

    <div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="n">klog</span><span class="o">.</span><span class="n">InfoS</span><span class="p">(</span><span class="s">"Failed to get boot time from /proc/stat. Will retry with unix.Sysinfo."</span><span class="p">,</span> <span class="s">"error"</span><span class="p">,</span> <span class="n">err</span><span class="p">)</span>
</code></pre></div>    </div>
  </li>
</ul>

<h2 id="good-to-explore">Good to Explore</h2>

<ol>
  <li><code class="language-plaintext highlighter-rouge">CPUManagerPolicy</code> for CPU bound workloads, and for stateful workloads</li>
  <li><code class="language-plaintext highlighter-rouge">TopologyManagerPolicy</code> for NUMA aware workloads</li>
  <li><code class="language-plaintext highlighter-rouge">KubeletTracing</code> to be enabled for tracing kubelet calls</li>
</ol>

<h2 id="just-random-notes">Just Random Notes</h2>

<h3 id="container-stats-provider">Container stats provider</h3>

<p>There is a feature flag in <code class="language-plaintext highlighter-rouge">Alpha</code> to move container stats from CAdvisor to CRI stats provider, this will be populated to the <code class="language-plaintext highlighter-rouge">kubeDeps.useLegacyCadvisorStats</code>. Code is for checking this flag is enabled is here.</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// pkg/kubelet/cadvisor/util.go: 77</span>

<span class="c">// UsingLegacyCadvisorStats returns true if container stats are provided by cadvisor instead of through the CRI.</span>
<span class="c">// CRI integrations should get container metrics via CRI.</span>
<span class="c">// TODO: cri-o relies on cadvisor as a temporary workaround. The code should</span>
<span class="c">// be removed. Related issue:</span>
<span class="c">// https://github.com/kubernetes/kubernetes/issues/51798</span>
<span class="k">func</span> <span class="n">UsingLegacyCadvisorStats</span><span class="p">(</span><span class="n">runtimeEndpoint</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
	<span class="c">// If PodAndContainerStatsFromCRI feature is enabled, then assume the user</span>
	<span class="c">// wants to use CRI stats, as the aforementioned workaround isn't needed</span>
	<span class="c">// when this feature is enabled.</span>
	<span class="k">if</span> <span class="n">utilfeature</span><span class="o">.</span><span class="n">DefaultFeatureGate</span><span class="o">.</span><span class="n">Enabled</span><span class="p">(</span><span class="n">features</span><span class="o">.</span><span class="n">PodAndContainerStatsFromCRI</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">return</span> <span class="no">false</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="n">strings</span><span class="o">.</span><span class="n">HasSuffix</span><span class="p">(</span><span class="n">runtimeEndpoint</span><span class="p">,</span> <span class="n">CrioSocketSuffix</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="how-to-get-the-kubelets-runtime-configuration">How to get the kubelet’s runtime configuration?</h3>
<p>Here’s how you can curl the kubelet’s runtime configuration:</p>

<h4 id="prerequisites">Prerequisites</h4>
<ul>
  <li><strong>Ensure you have access to the kubelet API</strong>
    <ul>
      <li>The kubelet API is usually protected by authentication and authorization mechanisms. You may need to provide a valid token or client certificate to access it.</li>
      <li>If you’re running this command from a node where the kubelet is running, you can access it via <code class="language-plaintext highlighter-rouge">localhost</code>.</li>
    </ul>
  </li>
</ul>

<h4 id="accessing-configuration">Accessing Configuration</h4>
<ul>
  <li><strong>Use <code class="language-plaintext highlighter-rouge">curl</code> to access the <code class="language-plaintext highlighter-rouge">/configz</code> endpoint</strong>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> curl <span class="nt">-sSk</span> <span class="nt">-H</span> <span class="s2">"Authorization: Bearer </span><span class="si">$(</span><span class="nb">cat</span> /var/run/secrets/kubernetes.io/serviceaccount/token<span class="si">)</span><span class="s2">"</span> <span class="se">\</span>
 https://localhost:10250/configz
</code></pre></div>    </div>

    <ul>
      <li><code class="language-plaintext highlighter-rouge">-sSk</code>: Silent mode, skip SSL certificate verification (since the kubelet uses a self-signed certificate by default).</li>
      <li><code class="language-plaintext highlighter-rouge">-H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"</code>: This adds the authorization header using the service account token. This assumes you’re running this command from a pod with access to the service account token.</li>
      <li><code class="language-plaintext highlighter-rouge">https://localhost:10250/configz</code>: The kubelet’s <code class="language-plaintext highlighter-rouge">/configz</code> endpoint.</li>
    </ul>
  </li>
</ul>

<h4 id="3-alternative-use-kubectl-proxy">3. <strong>Alternative: Use <code class="language-plaintext highlighter-rouge">kubectl proxy</code></strong></h4>
<p>If you don’t have direct access to the kubelet API, you can use <code class="language-plaintext highlighter-rouge">kubectl proxy</code> to access it securely.</p>

<ul>
  <li>Start a proxy:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kubectl proxy <span class="nt">--port</span><span class="o">=</span>8080
</code></pre></div>    </div>
  </li>
  <li>Access the kubelet’s <code class="language-plaintext highlighter-rouge">/configz</code> endpoint via the proxy:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl http://localhost:8080/api/v1/nodes/&lt;node-name&gt;/proxy/configz
</code></pre></div>    </div>
    <p>Replace <code class="language-plaintext highlighter-rouge">&lt;node-name&gt;</code> with the name of the node where the kubelet is running.</p>
  </li>
</ul>

<h4 id="4-parse-the-output">4. <strong>Parse the output</strong></h4>
<p>The output will be in JSON format. You can use tools like <code class="language-plaintext highlighter-rouge">jq</code> to parse and format it:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>   curl <span class="nt">-sSk</span> <span class="nt">-H</span> <span class="s2">"Authorization: Bearer </span><span class="si">$(</span><span class="nb">cat</span> /var/run/secrets/kubernetes.io/serviceaccount/token<span class="si">)</span><span class="s2">"</span> <span class="se">\</span>
   https://localhost:10250/configz | jq <span class="nb">.</span>
</code></pre></div></div>

<h4 id="notes">Notes:</h4>
<ul>
  <li>If the kubelet is not configured to allow anonymous access, you may need to provide a valid token or client certificate.</li>
  <li>Be cautious when accessing the kubelet API, as it has privileged access to the node and its workloads.</li>
  <li>Ensure that your network policies or firewall rules allow access to the kubelet’s port (default: <code class="language-plaintext highlighter-rouge">10250</code>).</li>
</ul>

<h3 id="feature-gates-in-kubernetes">Feature Gates in Kubernetes</h3>

<p>In the Kubernetes codebase, feature gates are defined in the <code class="language-plaintext highlighter-rouge">pkg/features/kube_features.go</code> file. And <code class="language-plaintext highlighter-rouge">pkg/features/versioned_kube_features.go</code> file contains the versioned feature gates and their default states (enabled or disabled) for each Kubernetes release.</p>

<h4 id="steps-to-find-versioned-feature-gates">Steps to Find Versioned Feature Gates:</h4>

<ol>
  <li><strong>Navigate to the Kubernetes GitHub Repository</strong>:
    <ul>
      <li>The main Kubernetes repository is located at: <a href="https://github.com/kubernetes/kubernetes">https://github.com/kubernetes/kubernetes</a>.</li>
    </ul>
  </li>
  <li><strong>Locate the <code class="language-plaintext highlighter-rouge">pkg/features/kube_features.go</code> File</strong>:
    <ul>
      <li>The feature gates are defined in the <code class="language-plaintext highlighter-rouge">pkg/features/kube_features.go</code> file. You can find it here: <a href="https://github.com/kubernetes/kubernetes/blob/master/pkg/features/kube_features.go">pkg/features/kube_features.go</a>.
        <ul>
          <li>The versioned feature gates are defined in the <code class="language-plaintext highlighter-rouge">pkg/features/versioned_kube_features.go</code> file. You can find it here: <a href="https://github.com/kubernetes/kubernetes/blob/master/pkg/features/kube_features.go">pkg/features/versioned_kube_features.go</a>.</li>
        </ul>
      </li>
    </ul>
  </li>
  <li><strong>Search for Feature Gate Definitions</strong>:
    <ul>
      <li>
        <p>In the <code class="language-plaintext highlighter-rouge">kube_features.go</code> file, you’ll find a <code class="language-plaintext highlighter-rouge">const</code> definitions for all known kubernetes specific feature keys. Example:</p>

        <div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c">// owner: @psch</span>
 <span class="c">//</span>
 <span class="c">// Enables a StatefulSet to start from an arbitrary non zero ordinal</span>
 <span class="n">StatefulSetStartOrdinal</span> <span class="n">featuregate</span><span class="o">.</span><span class="n">Feature</span> <span class="o">=</span> <span class="s">"StatefulSetStartOrdinal"</span>
</code></pre></div>        </div>
        <ul>
          <li>In the <code class="language-plaintext highlighter-rouge">versioned_kube_features.go</code> file you will find all the known kubernetes-specific feature keys with <code class="language-plaintext highlighter-rouge">VersionedSpecs</code>. Example:</li>
        </ul>

        <div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">var</span> <span class="n">defaultVersionedKubernetesFeatureGates</span> <span class="o">=</span> <span class="k">map</span><span class="p">[</span><span class="n">featuregate</span><span class="o">.</span><span class="n">Feature</span><span class="p">]</span><span class="n">featuregate</span><span class="o">.</span><span class="n">VersionedSpecs</span><span class="p">{</span>
 <span class="o">...</span>
   <span class="o">...</span>
         <span class="n">StatefulSetStartOrdinal</span><span class="o">:</span> <span class="p">{</span>
         <span class="p">{</span><span class="n">Version</span><span class="o">:</span> <span class="n">version</span><span class="o">.</span><span class="n">MustParse</span><span class="p">(</span><span class="s">"1.26"</span><span class="p">),</span> <span class="n">Default</span><span class="o">:</span> <span class="no">false</span><span class="p">,</span> <span class="n">PreRelease</span><span class="o">:</span> <span class="n">featuregate</span><span class="o">.</span><span class="n">Alpha</span><span class="p">},</span>
         <span class="p">{</span><span class="n">Version</span><span class="o">:</span> <span class="n">version</span><span class="o">.</span><span class="n">MustParse</span><span class="p">(</span><span class="s">"1.27"</span><span class="p">),</span> <span class="n">Default</span><span class="o">:</span> <span class="no">true</span><span class="p">,</span> <span class="n">PreRelease</span><span class="o">:</span> <span class="n">featuregate</span><span class="o">.</span><span class="n">Beta</span><span class="p">},</span>
         <span class="p">{</span><span class="n">Version</span><span class="o">:</span> <span class="n">version</span><span class="o">.</span><span class="n">MustParse</span><span class="p">(</span><span class="s">"1.31"</span><span class="p">),</span> <span class="n">Default</span><span class="o">:</span> <span class="no">true</span><span class="p">,</span> <span class="n">PreRelease</span><span class="o">:</span> <span class="n">featuregate</span><span class="o">.</span><span class="n">GA</span><span class="p">,</span> <span class="n">LockToDefault</span><span class="o">:</span> <span class="no">true</span><span class="p">},</span> <span class="c">// GA in 1.31, remove in 1.33</span>
     <span class="p">},</span>
   <span class="o">...</span>
   <span class="o">...</span>
</code></pre></div>        </div>
      </li>
      <li>
        <p>Each feature gate is associated with an array of <code class="language-plaintext highlighter-rouge">FeatureSpec</code> that specifies:</p>
        <ul>
          <li><code class="language-plaintext highlighter-rouge">Default</code>: Whether the feature is enabled by default.</li>
          <li><code class="language-plaintext highlighter-rouge">PreRelease</code>: The maturity stage of the feature (e.g., Alpha, Beta, GA).
            <ul>
              <li><code class="language-plaintext highlighter-rouge">LockToDefault</code>: Indicates that the feature is locked to its default and cannot be changed</li>
              <li><code class="language-plaintext highlighter-rouge">Version</code>: ndicates the earliest version from which this FeatureSpec is valid.</li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </li>
</ol>

<ul>
  <li><strong>Check Version-Specific Feature Gates</strong>:
    <ul>
      <li>The file often includes comments indicating in which Kubernetes version a feature gate was introduced or graduated (e.g., from Alpha to Beta or GA). For example:
        <div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c">// FeatureGateExample is an example feature gate.</span>
 <span class="c">// Owner: @community</span>
 <span class="c">// Alpha: v1.20</span>
 <span class="c">// Beta: v1.22</span>
 <span class="c">// GA: v1.24</span>
 <span class="n">FeatureGateExample</span> <span class="n">featuregate</span><span class="o">.</span><span class="n">Feature</span> <span class="o">=</span> <span class="s">"FeatureGateExample"</span>
</code></pre></div>        </div>
      </li>
    </ul>
  </li>
</ul>

<ol>
  <li><strong>Search for Feature Gate Usage</strong>:
    <ul>
      <li>To see how a specific feature gate is used in the codebase, you can search for its name in the repository. For example, search for <code class="language-plaintext highlighter-rouge">FeatureGateExample</code> to find where it is referenced.</li>
    </ul>
  </li>
</ol>

<h4 id="example-of-feature-gate-definition">Example of Feature Gate Definition:</h4>
<p>Here’s an example of how feature gates are defined in <code class="language-plaintext highlighter-rouge">kube_features.go</code>:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">const</span> <span class="p">(</span>
    <span class="c">// FeatureGateExample is an example feature gate.</span>
    <span class="n">FeatureGateExample</span> <span class="n">featuregate</span><span class="o">.</span><span class="n">Feature</span> <span class="o">=</span> <span class="s">"ExampleFeatureGate"</span>
<span class="p">)</span>
</code></pre></div></div>
<p>And then you will add a version feature gate entry in <code class="language-plaintext highlighter-rouge">versioned_kube_feature.go</code>:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	  <span class="k">var</span> <span class="n">defaultVersionedKubernetesFeatureGates</span> <span class="o">=</span> <span class="k">map</span><span class="p">[</span><span class="n">featuregate</span><span class="o">.</span><span class="n">Feature</span><span class="p">]</span><span class="n">featuregate</span><span class="o">.</span><span class="n">VersionedSpecs</span><span class="p">{</span>
	  <span class="o">...</span>
		<span class="o">...</span>
			  <span class="n">FeatureGateExample</span><span class="o">:</span> <span class="p">{</span>
		      <span class="p">{</span><span class="n">Version</span><span class="o">:</span> <span class="n">version</span><span class="o">.</span><span class="n">MustParse</span><span class="p">(</span><span class="s">"1.26"</span><span class="p">),</span> <span class="n">Default</span><span class="o">:</span> <span class="no">false</span><span class="p">,</span> <span class="n">PreRelease</span><span class="o">:</span> <span class="n">featuregate</span><span class="o">.</span><span class="n">Alpha</span><span class="p">},</span>
	      <span class="p">},</span>
		<span class="o">...</span>
		<span class="o">...</span>

</code></pre></div></div>

<h4 id="additional-notes">Additional Notes:</h4>
<ul>
  <li>Feature gates are used to enable or disable experimental or optional features in Kubernetes.</li>
  <li>The state of a feature gate (enabled or disabled) can be controlled using the <code class="language-plaintext highlighter-rouge">--feature-gates</code> flag when starting Kubernetes components like the kubelet, API server, or controller manager.</li>
  <li>Feature gates often progress through stages: Alpha → Beta → GA (General Availability).</li>
</ul>

<h3 id="about-backoff">About backOff</h3>

<p>Couple of things note about about backOff configuration that is set on the kubelet here…</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// pkg/kubelet/kubelet.go: 937</span>
	<span class="n">klet</span><span class="o">.</span><span class="n">backOff</span> <span class="o">=</span> <span class="n">flowcontrol</span><span class="o">.</span><span class="n">NewBackOff</span><span class="p">(</span><span class="n">base</span><span class="p">,</span> <span class="n">boMax</span><span class="p">)</span>
	<span class="n">klet</span><span class="o">.</span><span class="n">backOff</span><span class="o">.</span><span class="n">HasExpiredFunc</span> <span class="o">=</span> <span class="k">func</span><span class="p">(</span><span class="n">eventTime</span> <span class="n">time</span><span class="o">.</span><span class="n">Time</span><span class="p">,</span> <span class="n">lastUpdate</span> <span class="n">time</span><span class="o">.</span><span class="n">Time</span><span class="p">,</span> <span class="n">maxDuration</span> <span class="n">time</span><span class="o">.</span><span class="n">Duration</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
		<span class="k">return</span> <span class="n">eventTime</span><span class="o">.</span><span class="n">Sub</span><span class="p">(</span><span class="n">lastUpdate</span><span class="p">)</span> <span class="o">&gt;</span> <span class="m">600</span><span class="o">*</span><span class="n">time</span><span class="o">.</span><span class="n">Second</span>
	<span class="p">}</span>
</code></pre></div></div>

<p>… is that strangely <code class="language-plaintext highlighter-rouge">NewBackOff</code> do not apply any jitter, if you notice the function.</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">NewBackOff</span><span class="p">(</span><span class="n">initial</span><span class="p">,</span> <span class="n">max</span> <span class="n">time</span><span class="o">.</span><span class="n">Duration</span><span class="p">)</span> <span class="o">*</span><span class="n">Backoff</span> <span class="p">{</span>
	<span class="k">return</span> <span class="n">NewBackOffWithJitter</span><span class="p">(</span><span class="n">initial</span><span class="p">,</span> <span class="n">max</span><span class="p">,</span> <span class="m">0.0</span><span class="p">)</span>
<span class="p">}</span>

</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">maxJitterFactor</code> is <code class="language-plaintext highlighter-rouge">0.0</code>.</p>

<p>Also, if you not <code class="language-plaintext highlighter-rouge">func</code> that is assigned to <code class="language-plaintext highlighter-rouge">HasExpiredFunc</code>, it does not take <code class="language-plaintext highlighter-rouge">maxDuration</code> into account, but rather uses hard coded <code class="language-plaintext highlighter-rouge">600*time.Second</code>, I would think that this should be <code class="language-plaintext highlighter-rouge">maxDuration</code> according to the comment on <code class="language-plaintext highlighter-rouge">HasExpiredFunc</code>.</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	<span class="c">// HasExpiredFunc controls the logic that determines whether the backoff</span>
	<span class="c">// counter should be reset, and when to GC old backoff entries. If nil, the</span>
	<span class="c">// default hasExpired function will restart the backoff factor to the</span>
	<span class="c">// beginning after observing time has passed at least equal to 2*maxDuration</span>

</code></pre></div></div>

<h3 id="todo-deepdive-probemanager">TODO: Deepdive ProbeManager</h3>

<p>Start from this…</p>
<ol>
  <li>The <code class="language-plaintext highlighter-rouge">klet.probeManager</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 867</code>) is initialized to an instance of prode manager interface, <code class="language-plaintext highlighter-rouge">Manager</code> defined at <code class="language-plaintext highlighter-rouge">pkg/kubelet/prober/prober_manager.go: 71</code> which creates a probe worker for every container that specifies a probe, and it also updates each of the pod container status.</li>
</ol>

<p>To curl the kubelet’s runtime configuration, you need to access the kubelet’s API endpoint, which typically listens on port <code class="language-plaintext highlighter-rouge">10250</code> by default. The kubelet provides a <code class="language-plaintext highlighter-rouge">/configz</code> endpoint that exposes its runtime configuration.</p>

<h3 id="todo-evictionmanager">TODO: EvictionManager</h3>

<p>As found here…</p>

<ol>
  <li>At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 943</code> creates an <code class="language-plaintext highlighter-rouge">Manager</code> instance(<code class="language-plaintext highlighter-rouge">pkg/kubelet/eviction/types.go</code>) which is implemented by the <code class="language-plaintext highlighter-rouge">managerImpl</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/eviction/eviction_manager.go: 66</code>) by calling the <code class="language-plaintext highlighter-rouge">eviction.NewManager</code>. The instance of <code class="language-plaintext highlighter-rouge">Manager</code> is passed in both <code class="language-plaintext highlighter-rouge">evictionManager</code> and <code class="language-plaintext highlighter-rouge">evictionAdmitHandler</code>.</li>
</ol>

<h3 id="todo-how-does-critical-pod-admission-work">TODO: How does Critical Pod Admission work?</h3>

<p>Need to work on this…</p>
<ol>
  <li>At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 968-969</code>, an instance of the <code class="language-plaintext highlighter-rouge">CriticalPodAdmissionHandler</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/preemption/preemption.go: 45</code>) is created by calling the <code class="language-plaintext highlighter-rouge">NewCriticalPodAdmissionHandler</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/preemption/preemption.go: 63</code>) and assigned to <code class="language-plaintext highlighter-rouge">criticalPodAdmissionHandler</code>, if not <code class="language-plaintext highlighter-rouge">error</code> and then it added to the <code class="language-plaintext highlighter-rouge">klet.admitHandlers</code> with predicates. Its critical to understand the <code class="language-plaintext highlighter-rouge">CriticalPodAdmissionHandler</code> is in fact an <code class="language-plaintext highlighter-rouge">AdmissionFailureHandler</code>(<code class="language-plaintext highlighter-rouge">pkg/kubelet/lifecycle/predicate.go: 87</code>)</li>
</ol>

<h3 id="todo-buildkubeletclientconfig">TODO: buildKubeletClientConfig</h3>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// cmd/kubelet/app/server.go: 930</span>
<span class="k">func</span> <span class="n">buildKubeletClientConfig</span><span class="p">(</span><span class="n">ctx</span> <span class="n">context</span><span class="o">.</span><span class="n">Context</span><span class="p">,</span> <span class="n">s</span> <span class="o">*</span><span class="n">options</span><span class="o">.</span><span class="n">KubeletServer</span><span class="p">,</span> <span class="n">tp</span> <span class="n">oteltrace</span><span class="o">.</span><span class="n">TracerProvider</span><span class="p">,</span> <span class="n">nodeName</span> <span class="n">types</span><span class="o">.</span><span class="n">NodeName</span><span class="p">)</span> <span class="p">(</span><span class="o">*</span><span class="n">restclient</span><span class="o">.</span><span class="n">Config</span><span class="p">,</span> <span class="k">func</span><span class="p">(),</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</code></pre></div></div>

<h3 id="todo-describe-how-volumepluginmgr-works">TODO: Describe how volumePluginMgr works</h3>

<p>Lookup this …</p>

<p>At <code class="language-plaintext highlighter-rouge">pkg/kubelet/kubelet.go: 896</code></p>
<ul>
  <li>The <code class="language-plaintext highlighter-rouge">klet.volumePluginMgr</code> is initialized using the <code class="language-plaintext highlighter-rouge">NewInitializedVolumePluginMgr</code> function.
    <ul>
      <li>The <code class="language-plaintext highlighter-rouge">volumePluginMgr</code>, manages all volume plugins (e.g., CSI, FlexVolume, in-tree plugins) that the Kubelet uses to provision and manage storage for pods.</li>
      <li>if <code class="language-plaintext highlighter-rouge">err</code>, then this function returns</li>
    </ul>
  </li>
</ul>]]></content><author><name>M Samuel Vijaykumar</name></author><category term="kubernetes" /><summary type="html"><![CDATA[Note: This document contains my personal notes from exploring the Kubelet initialization process in Kubernetes. Due to its nature as working notes, it may not follow a strictly formal structure and might contain areas marked for further investigation or clarification. The content is technical and assumes familiarity with Kubernetes internals.]]></summary></entry></feed>