<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Ashok Srirama Blog</title>
        <link>https://ashoksrirama.com/blog</link>
        <description>Ashok Srirama Blog</description>
        <lastBuildDate>Wed, 18 Mar 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[Reducing LLM Cold-Start Times on Amazon EKS: A Benchmark of Eight Model Loading Strategies]]></title>
            <link>https://ashoksrirama.com/blog/reducing-llm-cold-start-times-eks</link>
            <guid>https://ashoksrirama.com/blog/reducing-llm-cold-start-times-eks</guid>
            <pubDate>Wed, 18 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Large language models (LLMs) are reshaping how organizations build intelligent applications, from conversational agents to code generation to enterprise search. But deploying these models in production introduces a challenge that doesn't get enough attention: cold-start time. When a new pod starts on a fresh GPU node, how long does it take before it can serve its first inference request?]]></description>
            <content:encoded><![CDATA[<p>Large language models (LLMs) are reshaping how organizations build intelligent applications, from conversational agents to code generation to enterprise search. But deploying these models in production introduces a challenge that doesn't get enough attention: <strong>cold-start time</strong>. When a new pod starts on a fresh GPU node, how long does it take before it can serve its first inference request?</p>
<p>For a model like Meta's Llama 3.1 405B-Instruct-FP8 weighing in at <strong>454 GB across 109 safetensor shards</strong>, the answer can range from 5 minutes to over 30 minutes, depending entirely on how you deliver the model weights to the GPU. In autoscaling scenarios, this cold-start time directly impacts how quickly your infrastructure responds to demand spikes.</p>
<p>In this post, we benchmark eight different strategies for loading LLM weights on <a href="https://aws.amazon.com/eks/" target="_blank" rel="noopener noreferrer" class="">Amazon Elastic Kubernetes Service (Amazon EKS)</a> and share our findings on performance, cost, and operational trade-offs.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-cold-start-problem">The Cold-Start Problem<a href="https://ashoksrirama.com/blog/reducing-llm-cold-start-times-eks#the-cold-start-problem" class="hash-link" aria-label="Direct link to The Cold-Start Problem" title="Direct link to The Cold-Start Problem" translate="no">​</a></h2>
<p>When <a href="https://docs.vllm.ai/en/stable/" target="_blank" rel="noopener noreferrer" class="">vLLM</a> (or any model serving framework) starts up on a GPU node, it goes through several phases:</p>
<ol>
<li class=""><strong>Node provisioning</strong> — <a href="https://karpenter.sh/" target="_blank" rel="noopener noreferrer" class="">Karpenter</a> provisions a GPU instance and the node joins the cluster</li>
<li class=""><strong>Container image pull</strong> — The container runtime pulls the serving framework image from a registry</li>
<li class=""><strong>Model weight loading</strong> — The framework reads model shards from storage and loads them into GPU memory</li>
<li class=""><strong>KV cache initialization</strong> — The framework allocates GPU memory for inference caching and runs warmup</li>
</ol>
<p>For large models, <strong>step 3 dominates the cold-start time</strong>. A 454 GB model must be read from storage and transferred to GPU memory, and the serving framework (vLLM in our case) reads shards <strong>sequentially</strong>, one at a time. This means the storage throughput for a single sequential read stream directly determines how long the model takes to load.</p>
<p>The question becomes: what's the fastest, most cost-effective way to get 454 GB of model weights into GPU memory?</p>
<p><img decoding="async" loading="lazy" alt="Cold-start timeline showing that model weight loading dominates at 90% of total time" src="https://ashoksrirama.com/assets/images/cold-start-timeline-3b4a03253dc788b29defb1c59b29f094.png" width="2385" height="732" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="benchmark-environment">Benchmark Environment<a href="https://ashoksrirama.com/blog/reducing-llm-cold-start-times-eks#benchmark-environment" class="hash-link" aria-label="Direct link to Benchmark Environment" title="Direct link to Benchmark Environment" translate="no">​</a></h2>
<p>We ran all benchmarks on an Amazon EKS cluster using <a href="https://docs.aws.amazon.com/eks/latest/userguide/automode.html" target="_blank" rel="noopener noreferrer" class="">EKS Auto Mode</a>, which automates node provisioning, scaling, and lifecycle management using Karpenter.</p>
<table><thead><tr><th>Component</th><th>Details</th></tr></thead><tbody><tr><td><strong>Cluster</strong></td><td>Amazon EKS Auto Mode (us-west-2)</td></tr><tr><td><strong>Model</strong></td><td>Meta Llama 3.1 405B-Instruct-FP8 (~454 GB, 109 safetensor shards)</td></tr><tr><td><strong>Inference Framework</strong></td><td>vLLM v0.17.1 with 8-way <a href="https://docs.vllm.ai/en/stable/serving/parallelism_scaling/" target="_blank" rel="noopener noreferrer" class="">tensor parallelism</a></td></tr><tr><td><strong>Instance Types</strong></td><td>p5.48xlarge (8x NVIDIA H100 80 GB) and p5en.48xlarge (8x NVIDIA H200 141 GB)</td></tr><tr><td><strong>Instance Specs</strong></td><td>192 vCPUs, 2 TB RAM, 8x 3.5 TB NVMe SSDs (28 TB RAID0), 3200 Gbps EFA</td></tr><tr><td><strong>Node Management</strong></td><td>Karpenter with Bottlerocket NVIDIA AMI</td></tr><tr><td><strong>VPC Endpoints</strong></td><td>S3 Gateway Endpoint, ECR Private Endpoints (ECR API, ECR DKR, S3 Gateway)</td></tr></tbody></table>
<p>Each strategy was tested with a true cold start, a fresh GPU node with no prior image or data cache, unless otherwise noted. We measured <strong>cold-start time</strong> as the interval from pod scheduled to pod ready (serving inference).</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-eight-strategies">The Eight Strategies<a href="https://ashoksrirama.com/blog/reducing-llm-cold-start-times-eks#the-eight-strategies" class="hash-link" aria-label="Direct link to The Eight Strategies" title="Direct link to The Eight Strategies" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="strategy-1-huggingface-hub-direct-download">Strategy 1: HuggingFace Hub Direct Download<a href="https://ashoksrirama.com/blog/reducing-llm-cold-start-times-eks#strategy-1-huggingface-hub-direct-download" class="hash-link" aria-label="Direct link to Strategy 1: HuggingFace Hub Direct Download" title="Direct link to Strategy 1: HuggingFace Hub Direct Download" translate="no">​</a></h3>
<p><strong>How it works:</strong> vLLM downloads model weights directly from the <a href="https://huggingface.co/" target="_blank" rel="noopener noreferrer" class="">HuggingFace Hub</a> during startup. The <code>hf_transfer</code> library accelerates the download by using multiple concurrent connections. Files are written to the node's local NVMe storage, and vLLM loads shards into GPU memory from there.</p>
<p>This is the simplest approach — no pre-staging, no external storage to manage. Just point vLLM at a model ID and let it pull the weights at startup.</p>
<p><img decoding="async" loading="lazy" alt="Strategy 1: vLLM downloads model from HuggingFace Hub to local NVMe, then loads to GPU" src="https://ashoksrirama.com/assets/images/strategy-hf-direct-b2f6638b5cf7429cd177fbf14dc0d203.png" width="1305" height="520" class="img_ev3q"></p>
<p><strong>Results:</strong></p>
<table><thead><tr><th>Metric</th><th style="text-align:center">Value</th></tr></thead><tbody><tr><td>Image Pull</td><td style="text-align:center">1 min 1 sec</td></tr><tr><td>Model Download + Weight Loading</td><td style="text-align:center">~18 min</td></tr><tr><td><strong>Total Cold Start</strong></td><td style="text-align:center"><strong>19 min 33 sec</strong></td></tr><tr><td>Reliability</td><td style="text-align:center">1 of 3 runs failed (download hung)</td></tr></tbody></table>
<p><strong>What we observed:</strong> The download phase ran at approximately 500 MB/s, which is reasonable for a cross-internet download. However, in one of our three runs, the download hung indefinitely on the final shard at 418 GB — requiring a full pod restart. This reliability concern makes HuggingFace direct download risky for production workloads at this model scale.</p>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="strategy-2-amazon-fsx-for-lustre">Strategy 2: Amazon FSx for Lustre<a href="https://ashoksrirama.com/blog/reducing-llm-cold-start-times-eks#strategy-2-amazon-fsx-for-lustre" class="hash-link" aria-label="Direct link to Strategy 2: Amazon FSx for Lustre" title="Direct link to Strategy 2: Amazon FSx for Lustre" translate="no">​</a></h3>
<p><strong>How it works:</strong> We pre-cached the model weights on an <a href="https://aws.amazon.com/fsx/lustre/" target="_blank" rel="noopener noreferrer" class="">Amazon FSx for Lustre</a> filesystem — a high-performance parallel filesystem commonly used for HPC and machine learning workloads. The Lustre volume was mounted into the vLLM pod using the FSx CSI driver. vLLM reads shards directly from the mounted filesystem.</p>
<p>We provisioned the filesystem with the PERSISTENT_2 deployment type on SSD storage at the 1000 MB/s/TiB throughput tier — a 4,800 GiB filesystem providing a theoretical aggregate throughput of 4.8 GB/s.</p>
<p><img decoding="async" loading="lazy" alt="Strategy 2: FSx for Lustre filesystem mounted into vLLM pod via CSI driver" src="https://ashoksrirama.com/assets/images/strategy-fsx-lustre-6a467b0970af4aa5f3553ac022dfd834.png" width="1335" height="520" class="img_ev3q"></p>
<p><strong>Results:</strong></p>
<table><thead><tr><th>Metric</th><th style="text-align:center">Value</th></tr></thead><tbody><tr><td>Image Pull</td><td style="text-align:center">57 sec</td></tr><tr><td>Weight Loading (109 shards)</td><td style="text-align:center">29 min 27 sec</td></tr><tr><td>Per-Shard Average</td><td style="text-align:center">16.2 sec</td></tr><tr><td>Effective Read Throughput</td><td style="text-align:center">~260 MB/s</td></tr><tr><td><strong>Total Cold Start</strong></td><td style="text-align:center"><strong>32 min 34 sec</strong></td></tr></tbody></table>
<p><strong>What we observed:</strong> Despite provisioning a filesystem capable of 4.8 GB/s aggregate throughput, we observed only ~260 MB/s during weight loading. This is because FSx for Lustre is architected for <strong>parallel, multi-stream I/O across many clients</strong> — the kind of workload you see in distributed training. Our workload is fundamentally different: a single client reading shards sequentially. The 1000 MB/s/TiB throughput tier represents aggregate capacity across multiple concurrent streams, not single-stream performance.</p>
<p>At $1,440/month for the filesystem alone, this was also the most expensive strategy we tested.</p>
<p><strong>With Run<!-- -->:ai<!-- --> Model Streamer:</strong> We also tested FSx for Lustre with the <a href="https://github.com/run-ai/runai-model-streamer" target="_blank" rel="noopener noreferrer" class="">Run<!-- -->:ai<!-- --> Model Streamer</a> (<code>--load-format runai_streamer</code>, concurrency=32) to see if concurrent tensor streaming could overcome the sequential read bottleneck. The results were dramatic:</p>
<table><thead><tr><th>Metric</th><th style="text-align:center">Default Loader</th><th style="text-align:center">Run<!-- -->:ai<!-- --> Streamer</th></tr></thead><tbody><tr><td>Weight Loading</td><td style="text-align:center">29 min 27 sec</td><td style="text-align:center"><strong>6 min 38 sec</strong></td></tr><tr><td>Effective Read Throughput</td><td style="text-align:center">~260 MB/s</td><td style="text-align:center"><strong>~1.1 GiB/s</strong></td></tr></tbody></table>
<p>The Run<!-- -->:ai<!-- --> streamer improved Lustre throughput by <strong>4.4x</strong> — using 32 concurrent I/O threads per GPU worker (256 total across 8 TP workers) to saturate the filesystem's parallel bandwidth. This confirms that Lustre's low default throughput was caused by the sequential access pattern, not a limitation of the filesystem itself. However, even with this improvement, the $1,440/month storage cost makes this a less compelling option compared to S3-based strategies.</p>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="strategy-3-amazon-efs-elastic-throughput">Strategy 3: Amazon EFS (Elastic Throughput)<a href="https://ashoksrirama.com/blog/reducing-llm-cold-start-times-eks#strategy-3-amazon-efs-elastic-throughput" class="hash-link" aria-label="Direct link to Strategy 3: Amazon EFS (Elastic Throughput)" title="Direct link to Strategy 3: Amazon EFS (Elastic Throughput)" translate="no">​</a></h3>
<p><strong>How it works:</strong> We pre-cached the model weights on <a href="https://aws.amazon.com/efs/" target="_blank" rel="noopener noreferrer" class="">Amazon Elastic File System (Amazon EFS)</a> using the Elastic throughput mode. The EFS volume was mounted into the vLLM pod via the EFS CSI driver. Like the Lustre strategy, vLLM reads shards sequentially from the mounted filesystem.</p>
<p>EFS Elastic throughput automatically scales read and write throughput based on demand — no need to pre-provision capacity or throughput. We pre-loaded the model onto EFS using <a href="https://github.com/peak/s5cmd" target="_blank" rel="noopener noreferrer" class="">s5cmd</a> to copy from an <a href="https://aws.amazon.com/s3/" target="_blank" rel="noopener noreferrer" class="">Amazon Simple Storage Service (Amazon S3)</a> staging bucket.</p>
<p><img decoding="async" loading="lazy" alt="Strategy 3: Amazon EFS mounted into vLLM pod via CSI driver with Elastic throughput" src="https://ashoksrirama.com/assets/images/strategy-efs-f08575afc8537486182000aa00d8dfe7.png" width="1294" height="520" class="img_ev3q"></p>
<p><strong>Results:</strong></p>
<table><thead><tr><th>Metric</th><th style="text-align:center">Value</th></tr></thead><tbody><tr><td>Image Pull</td><td style="text-align:center">59 sec</td></tr><tr><td>Weight Loading (109 shards)</td><td style="text-align:center">8 min 2 sec</td></tr><tr><td>Per-Shard Average</td><td style="text-align:center">4.43 sec</td></tr><tr><td>Effective Read Throughput</td><td style="text-align:center">~1 GB/s</td></tr><tr><td><strong>Total Cold Start</strong></td><td style="text-align:center"><strong>10 min 50 sec</strong></td></tr></tbody></table>
<p><strong>What we observed:</strong> EFS delivered ~1 GB/s sequential read throughput — nearly 4x faster than FSx for Lustre for this workload. EFS Elastic throughput automatically scaled to meet our read demand without any configuration. This was a surprising result: a general-purpose managed file system outperformed a purpose-built HPC filesystem for our specific access pattern.</p>
<p>The trade-off is cost: EFS Elastic throughput charges $0.03 per GB of data read. At 454 GB per cold start, that's approximately $14 per startup event — which adds up at scale.</p>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="strategy-4-mountpoint-for-amazon-s3">Strategy 4: Mountpoint for Amazon S3<a href="https://ashoksrirama.com/blog/reducing-llm-cold-start-times-eks#strategy-4-mountpoint-for-amazon-s3" class="hash-link" aria-label="Direct link to Strategy 4: Mountpoint for Amazon S3" title="Direct link to Strategy 4: Mountpoint for Amazon S3" translate="no">​</a></h3>
<p><strong>How it works:</strong> We mounted the Amazon S3 bucket containing the model as a POSIX filesystem using <a href="https://aws.amazon.com/s3/features/mountpoint/" target="_blank" rel="noopener noreferrer" class="">Mountpoint for Amazon S3</a>, accessed through the Mountpoint for Amazon S3 CSI driver. vLLM reads shards through the FUSE mount layer as if they were local files. No data is staged locally — reads go directly to S3.</p>
<p><img decoding="async" loading="lazy" alt="Strategy 4: S3 bucket mounted via Mountpoint for Amazon S3 with VPC Gateway Endpoint" src="https://ashoksrirama.com/assets/images/strategy-s3-mountpoint-153fdab2a51e1a294d188c931d514688.png" width="1498" height="520" class="img_ev3q"></p>
<p><strong>Results:</strong></p>
<table><thead><tr><th>Metric</th><th style="text-align:center">Value</th></tr></thead><tbody><tr><td>Image Pull</td><td style="text-align:center">56 sec</td></tr><tr><td>Weight Loading (109 shards)</td><td style="text-align:center">22 min 27 sec</td></tr><tr><td>Per-Shard Average</td><td style="text-align:center">12.4 sec</td></tr><tr><td>Effective Read Throughput</td><td style="text-align:center">~340 MB/s</td></tr><tr><td><strong>Total Cold Start</strong></td><td style="text-align:center"><strong>27 min 38 sec</strong></td></tr></tbody></table>
<p><strong>What we observed:</strong> Mountpoint for Amazon S3 provides a convenient POSIX interface to S3, but the FUSE layer adds overhead for sequential reads. Per-shard latency was a consistent ~12.4 seconds, resulting in over 22 minutes of sequential weight loading for 109 shards.</p>
<p>The key advantage here is cost: S3 Standard storage for 454 GB costs just ~$10/month, making this the cheapest storage option. But the slow sequential read speed means expensive GPU time is wasted waiting for data.</p>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="strategy-5-amazon-s3--s5cmd-init-container">Strategy 5: Amazon S3 + s5cmd Init Container<a href="https://ashoksrirama.com/blog/reducing-llm-cold-start-times-eks#strategy-5-amazon-s3--s5cmd-init-container" class="hash-link" aria-label="Direct link to Strategy 5: Amazon S3 + s5cmd Init Container" title="Direct link to Strategy 5: Amazon S3 + s5cmd Init Container" translate="no">​</a></h3>
<p><strong>How it works:</strong> This strategy takes a fundamentally different approach. Instead of reading model weights from network storage during vLLM's sequential loading phase, we use a Kubernetes init container to <strong>download all model shards from S3 in parallel</strong> to the node's local NVMe storage <em>before</em> vLLM starts.</p>
<p>The init container runs <a href="https://github.com/peak/s5cmd" target="_blank" rel="noopener noreferrer" class="">s5cmd</a>, a high-performance S3 client, with <code>--concurrency 50</code> to download all 109 shards simultaneously. Once the download completes, vLLM starts and loads shards from local NVMe — which provides ~5 GB/s sequential read throughput.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">initContainers</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> s3</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">download</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> peakcom/s5cmd</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">v2.2.2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">command</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"/s5cmd"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"--log"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"error"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"cp"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token string" style="color:#e3116c">"--concurrency"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"50"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"--part-size"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"100"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token string" style="color:#e3116c">"s3://llm-model-cache-usw2/meta-llama/Llama-3.1-405B-Instruct-FP8/*"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token string" style="color:#e3116c">"/scratch/model/"</span><span class="token punctuation" style="color:#393A34">]</span><br></span></code></pre></div></div>
<p>This approach decouples the <strong>parallel download</strong> from vLLM's <strong>sequential loading</strong>, leveraging S3's massive aggregate bandwidth and the node's high-speed local NVMe storage.</p>
<p><img decoding="async" loading="lazy" alt="Strategy 5: Init container uses s5cmd to download shards from S3 in parallel to NVMe, then vLLM loads locally" src="https://ashoksrirama.com/assets/images/strategy-s5cmd-d1dbf40d221a64e30b6e7484b4972edd.png" width="1309" height="780" class="img_ev3q"></p>
<p><strong>Results:</strong></p>
<table><thead><tr><th>Metric</th><th style="text-align:center">Value</th></tr></thead><tbody><tr><td>s5cmd S3 Download</td><td style="text-align:center">2 min 17 sec (454 GB at ~3.2 GB/s)</td></tr><tr><td>Weight Loading (109 shards)</td><td style="text-align:center">1 min 32 sec (~0.85 sec/shard from NVMe)</td></tr><tr><td>KV Cache Init</td><td style="text-align:center">~12.6 sec</td></tr><tr><td><strong>Total Cold Start</strong></td><td style="text-align:center"><strong>5 min 31 sec</strong></td></tr></tbody></table>
<p><strong>What we observed:</strong> This was the fastest strategy by a significant margin. The p5.48xlarge's 3200 Gbps EFA networking enabled s5cmd to pull 454 GB from S3 at ~3.2 GB/s. Once on local NVMe, vLLM loaded all 109 shards in just 92 seconds at ~5 GB/s.</p>
<p>The insight is simple but powerful: <strong>downloading in parallel and reading locally is much faster than reading sequentially from network storage</strong>. By using an init container, we exploit S3's massive aggregate bandwidth without being constrained by vLLM's single-stream sequential read pattern.</p>
<blockquote>
<p><strong>Note:</strong> This benchmark used a warm container image cache (the base vLLM image was already on the node from a prior strategy test). Add approximately 1 minute for a cold image pull of the 9.3 GB base image, bringing the estimated true cold start to ~6 min 30 sec.</p>
</blockquote>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="strategy-6-amazon-s3--run-model-streamer">Strategy 6: Amazon S3 + Run<!-- -->:ai<!-- --> Model Streamer<a href="https://ashoksrirama.com/blog/reducing-llm-cold-start-times-eks#strategy-6-amazon-s3--run-model-streamer" class="hash-link" aria-label="Direct link to strategy-6-amazon-s3--run-model-streamer" title="Direct link to strategy-6-amazon-s3--run-model-streamer" translate="no">​</a></h3>
<p><strong>How it works:</strong> This strategy uses the <a href="https://github.com/run-ai/runai-model-streamer" target="_blank" rel="noopener noreferrer" class="">Run<!-- -->:ai<!-- --> Model Streamer</a>, an open-source library that replaces vLLM's default sequential weight loader with a <strong>concurrent tensor streaming</strong> engine. Instead of reading safetensor shards one at a time, the streamer uses multiple C++ threads to read tensors from S3 in parallel, overlapping I/O (S3 → CPU memory) with GPU transfer (CPU → GPU).</p>
<p>The key difference from the s5cmd approach: there is no init container and no local copy step. vLLM reads directly from the S3 path using the <code>--load-format runai_streamer</code> flag. The streamer handles all the parallelism internally.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">command</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> python3</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">m</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> vllm.entrypoints.openai.api_server</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">-</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">model</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> s3</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">//llm</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">model</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">cache</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">usw2/meta</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">llama/Llama</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">3.1</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">405B</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">Instruct</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">FP8</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">-</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">load</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">format</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> runai_streamer</span><br></span></code></pre></div></div>
<p>We built a minimal custom container image that adds <code>runai-model-streamer</code> and <code>runai-model-streamer-s3</code> packages on top of the base vLLM image.</p>
<p><strong>Results:</strong></p>
<table><thead><tr><th>Metric</th><th style="text-align:center">Value</th></tr></thead><tbody><tr><td>Image Pull</td><td style="text-align:center">38 sec (9.3 GB custom image)</td></tr><tr><td>Weight Loading (1509 tensors from S3)</td><td style="text-align:center">6 min 13 sec</td></tr><tr><td>S3 Streaming Throughput</td><td style="text-align:center">~1.2 GB/s</td></tr><tr><td>KV Cache Init</td><td style="text-align:center">~29 sec</td></tr><tr><td><strong>Total Cold Start</strong></td><td style="text-align:center"><strong>8 min 28 sec</strong></td></tr></tbody></table>
<p><strong>What we observed:</strong> The Run<!-- -->:ai<!-- --> streamer achieved <strong>1.2 GB/s</strong> streaming directly from S3 — nearly 4x faster than Mountpoint for Amazon S3 (340 MB/s) for the same S3 bucket. It works at the tensor level (1509 individual tensors across 109 shards), reading and transferring them concurrently rather than waiting for each shard to complete before starting the next.</p>
<p>This approach is the <strong>simplest S3-based strategy</strong> — no init container, no local NVMe storage, no volume mounts. Just point vLLM at an S3 path. The trade-off is that it's ~3 minutes slower than s5cmd because S3 streaming at 1.2 GB/s can't match the NVMe local read speed of 5 GB/s after a parallel download.</p>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="strategy-7-amazon-ecr-container-image">Strategy 7: Amazon ECR Container Image<a href="https://ashoksrirama.com/blog/reducing-llm-cold-start-times-eks#strategy-7-amazon-ecr-container-image" class="hash-link" aria-label="Direct link to Strategy 7: Amazon ECR Container Image" title="Direct link to Strategy 7: Amazon ECR Container Image" translate="no">​</a></h3>
<p><strong>How it works:</strong> We baked the model weights directly into the container image and stored it in <a href="https://aws.amazon.com/ecr/" target="_blank" rel="noopener noreferrer" class="">Amazon Elastic Container Registry (Amazon ECR)</a>. The 454 GB model was split across 11 OCI image layers (~44 GB each) to comply with Amazon ECR's 49 GB per-layer limit. When the pod starts, containerd pulls all layers in parallel from ECR. vLLM then reads model weights from the local overlay filesystem, which is backed by the node's NVMe storage.</p>
<div class="language-dockerfile codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-dockerfile codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Each batch downloads 10 shards as a separate layer (~44 GB)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">RUN for i in $(seq -w 1 10); do \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      echo "cp ${S3}/model-000${i}-of-00109.safetensors ${D}/"; \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    done | s5cmd --log error run</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># ... repeated for batches 2-11</span><br></span></code></pre></div></div>
<p>This approach treats the model as an immutable part of the container image — no runtime downloads, no external storage mounts.</p>
<p><img decoding="async" loading="lazy" alt="Strategy 7: Containerd pulls 11 image layers in parallel from ECR, vLLM reads model from overlay filesystem" src="https://ashoksrirama.com/assets/images/strategy-ecr-image-7f782edda620b904c333135a330ac337.png" width="1595" height="537" class="img_ev3q"></p>
<p><strong>Results:</strong></p>
<table><thead><tr><th>Metric</th><th style="text-align:center">Value</th></tr></thead><tbody><tr><td>Image Pull (parallel layer pull)</td><td style="text-align:center">8 min 4 sec (369 GB compressed)</td></tr><tr><td>Weight Loading (100 shards)</td><td style="text-align:center">1 min 7 sec (~0.67 sec/shard from overlay)</td></tr><tr><td>KV Cache Init</td><td style="text-align:center">~12.7 sec</td></tr><tr><td><strong>Total Cold Start</strong></td><td style="text-align:center"><strong>10 min 41 sec</strong></td></tr></tbody></table>
<p><strong>What we observed:</strong> Containerd on EKS Auto Mode (Bottlerocket) pulled all 11 model layers in parallel from ECR, downloading 369 GB (compressed) in just over 8 minutes. Once unpacked to the local overlay filesystem, weight loading completed in 67 seconds — the fastest weight loading of any strategy.</p>
<p>The trade-off is operational complexity: you need to build and maintain a ~460 GB container image, the build process requires a machine with 600+ GB of disk, and the ECR layer size limit necessitates splitting the model across multiple Dockerfile <code>RUN</code> commands. Any model update requires rebuilding and re-pushing the entire image.</p>
<hr>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="strategy-8-amazon-ebs-snapshot">Strategy 8: Amazon EBS Snapshot<a href="https://ashoksrirama.com/blog/reducing-llm-cold-start-times-eks#strategy-8-amazon-ebs-snapshot" class="hash-link" aria-label="Direct link to Strategy 8: Amazon EBS Snapshot" title="Direct link to Strategy 8: Amazon EBS Snapshot" translate="no">​</a></h3>
<p><strong>How it works:</strong> We pre-loaded the model weights onto an <a href="https://aws.amazon.com/ebs/" target="_blank" rel="noopener noreferrer" class="">Amazon EBS</a> volume, created a snapshot, and used <a href="https://karpenter.sh/docs/concepts/nodeclasses/#specblockdevicemappings" target="_blank" rel="noopener noreferrer" class="">Karpenter's <code>blockDeviceMappings</code></a> to attach the snapshot as a secondary volume on every GPU node at launch. The node's userData script mounts the snapshot volume at <code>/mnt/model-data</code>, and vLLM reads model weights from the host-mounted path via a <code>hostPath</code> volume.</p>
<p>We enabled <a href="https://docs.aws.amazon.com/ebs/latest/userguide/ebs-fast-snapshot-restore.html" target="_blank" rel="noopener noreferrer" class="">EBS Fast Snapshot Restore (FSR)</a> on the snapshot in the target Availability Zone to eliminate the lazy-loading penalty that EBS snapshots incur on first read. Without FSR, the first read of each block must be fetched from S3, adding over 100 seconds per shard — making cold starts impractical.</p>
<p>The volume was provisioned as gp3 with 1000 MB/s throughput and 16,000 IOPS — the maximum for gp3. We used the <a href="https://github.com/run-ai/runai-model-streamer" target="_blank" rel="noopener noreferrer" class="">Run<!-- -->:ai<!-- --> Model Streamer</a> (<code>--load-format runai_streamer</code>, concurrency=32) to read tensors concurrently from the mounted volume.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">blockDeviceMappings</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">deviceName</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> /dev/xvdb</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ebs</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">snapshotID</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> snap</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">0db355e5da8895b94</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">volumeSize</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 750Gi</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">volumeType</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> gp3</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">throughput</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1000</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">iops</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">16000</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">encrypted</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">deleteOnTermination</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="Strategy 8: EBS snapshot attached via Karpenter, mounted by userData, vLLM reads via hostPath" src="https://ashoksrirama.com/assets/images/strategy-ebs-snapshot-0f480e245223d954f3616f616ae5e089.png" width="1685" height="537" class="img_ev3q"></p>
<p><strong>Results:</strong></p>
<table><thead><tr><th>Metric</th><th style="text-align:center">Value</th></tr></thead><tbody><tr><td>Image Pull</td><td style="text-align:center">~2 min</td></tr><tr><td>Weight Loading (1509 tensors)</td><td style="text-align:center">7 min 43 sec</td></tr><tr><td>Streaming Throughput</td><td style="text-align:center">~1003 MiB/s</td></tr><tr><td><strong>Total Cold Start</strong></td><td style="text-align:center"><strong>~11 min</strong></td></tr></tbody></table>
<p><strong>What we observed:</strong> The Run<!-- -->:ai<!-- --> streamer fully saturated the gp3 throughput ceiling at <strong>1003 MiB/s</strong> — reading 453.8 GiB across all 8 tensor-parallel workers in 462.9 seconds. For comparison, the default sequential loader on the same gp3 volume achieved only ~367 MB/s (20 min 36 sec), meaning the concurrent streamer delivered a <strong>2.7x speedup</strong> by better utilizing the available bandwidth.</p>
<p>Without the Run<!-- -->:ai<!-- --> streamer, vLLM's default sequential loader on the same gp3 volume took <strong>20 min 36 sec</strong> at only ~367 MB/s — leaving over 60% of the gp3 bandwidth unused. Without FSR enabled, the first read of each snapshot block triggers a lazy fetch from S3, inflating per-shard load times to over 130 seconds and making cold starts completely impractical.</p>
<p>The key advantage of EBS snapshots is <strong>zero download time</strong> — model data is available the moment the node boots, with no init container or network transfer required. The trade-off is operational: you need to manage snapshots, enable FSR in each target AZ ($0.75/hr per AZ), and pin your NodePool to snapshot-enabled zones.</p>
<p>To go faster with EBS, you could use io2 Block Express volumes (up to 4000 MB/s throughput), though at significantly higher cost. The gp3 throughput ceiling is the bottleneck here, not the loader.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="results-at-a-glance">Results at a Glance<a href="https://ashoksrirama.com/blog/reducing-llm-cold-start-times-eks#results-at-a-glance" class="hash-link" aria-label="Direct link to Results at a Glance" title="Direct link to Results at a Glance" translate="no">​</a></h2>
<table><thead><tr><th style="text-align:center">Rank</th><th>Strategy</th><th style="text-align:center">Cold Start</th><th style="text-align:center">Weight Loading</th><th style="text-align:center">Effective Throughput</th></tr></thead><tbody><tr><td style="text-align:center">1</td><td><strong>Amazon S3 + s5cmd Init Container</strong></td><td style="text-align:center"><strong>5 min 31 sec</strong></td><td style="text-align:center">1 min 32 sec</td><td style="text-align:center">~5.0 GB/s (NVMe)</td></tr><tr><td style="text-align:center">2</td><td><strong>Amazon S3 + Run<!-- -->:ai<!-- --> Model Streamer</strong></td><td style="text-align:center"><strong>8 min 28 sec</strong></td><td style="text-align:center">6 min 13 sec</td><td style="text-align:center">~1.2 GB/s (S3 direct)</td></tr><tr><td style="text-align:center">3</td><td><strong>Amazon ECR Container Image</strong></td><td style="text-align:center"><strong>10 min 41 sec</strong></td><td style="text-align:center">1 min 7 sec</td><td style="text-align:center">~5.0 GB/s (NVMe overlay)</td></tr><tr><td style="text-align:center">4</td><td><strong>Amazon EFS (Elastic Throughput)</strong></td><td style="text-align:center"><strong>10 min 50 sec</strong></td><td style="text-align:center">8 min 2 sec</td><td style="text-align:center">~1.0 GB/s</td></tr><tr><td style="text-align:center">5</td><td><strong>Amazon EBS Snapshot + Run<!-- -->:ai<!-- --> Streamer</strong></td><td style="text-align:center"><strong>~11 min</strong></td><td style="text-align:center">7 min 43 sec</td><td style="text-align:center">~1003 MiB/s</td></tr><tr><td style="text-align:center">6</td><td><strong>HuggingFace Hub Direct Download</strong></td><td style="text-align:center"><strong>19 min 33 sec</strong></td><td style="text-align:center">~18 min</td><td style="text-align:center">~500 MB/s</td></tr><tr><td style="text-align:center">7</td><td><strong>Mountpoint for Amazon S3</strong></td><td style="text-align:center"><strong>27 min 38 sec</strong></td><td style="text-align:center">22 min 27 sec</td><td style="text-align:center">~340 MB/s</td></tr><tr><td style="text-align:center">8</td><td><strong>Amazon FSx for Lustre</strong></td><td style="text-align:center"><strong>32 min 34 sec</strong></td><td style="text-align:center">29 min 27 sec</td><td style="text-align:center">~260 MB/s</td></tr><tr><td style="text-align:center">—</td><td><em>FSx for Lustre + Run<!-- -->:ai<!-- --> Streamer</em></td><td style="text-align:center"><em>~9 min 30 sec</em>*</td><td style="text-align:center"><em>6 min 38 sec</em></td><td style="text-align:center"><em>~1.1 GiB/s</em></td></tr></tbody></table>
<p>* <em>Estimated cold start based on warm Lustre cache with Run<!-- -->:ai<!-- --> Model Streamer (concurrency=32). See Strategy 2 for details.</em></p>
<p><img decoding="async" loading="lazy" alt="Bar chart comparing cold-start times across all eight strategies" src="https://ashoksrirama.com/assets/images/comparison-chart-cda46aff3f030292ec7dbe67f4d94910.png" width="2084" height="1033" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-the-s5cmd-approach-wins">Why the s5cmd Approach Wins<a href="https://ashoksrirama.com/blog/reducing-llm-cold-start-times-eks#why-the-s5cmd-approach-wins" class="hash-link" aria-label="Direct link to Why the s5cmd Approach Wins" title="Direct link to Why the s5cmd Approach Wins" translate="no">​</a></h2>
<p>The results highlight a fundamental architectural insight: <strong>the bottleneck isn't storage throughput — it's the sequential access pattern</strong>.</p>
<p>vLLM's default loader reads safetensor shards one at a time. When reading from network-attached storage (EFS, Lustre, S3 Mountpoint), each shard becomes a blocking I/O operation. The per-shard latency ranges from 4.4 seconds on EFS to 16.2 seconds on Lustre — and with 109 shards to load, these delays compound to 8–30 minutes of GPU idle time.</p>
<p>The s5cmd, Run<!-- -->:ai<!-- --> streamer, and concurrent loading approaches break this bottleneck in different ways:</p>
<p><strong>s5cmd init container (fastest — 5 min 31 sec):</strong> Separates the problem into two phases — parallel download all 109 shards from S3 at ~3.2 GB/s, then vLLM reads sequentially from local NVMe at ~5 GB/s. This two-phase approach turns a 30-minute network I/O problem into a 2-minute download plus a 90-second local read.</p>
<p><strong>Run<!-- -->:ai<!-- --> Model Streamer (simplest — 8 min 28 sec):</strong> Replaces vLLM's sequential loader with a concurrent tensor streaming engine that reads directly from S3 at ~1.2 GB/s. No init container, no local storage — just a single flag change. It's 3 minutes slower because S3 streaming can't match NVMe local read speeds, but the operational simplicity is compelling.</p>
<p><strong>Run<!-- -->:ai<!-- --> streamer on filesystems:</strong> The concurrent streaming approach also dramatically improved FSx for Lustre — from 260 MB/s to 1.1 GiB/s (4.4x faster). Lustre's parallel architecture was being underutilized by vLLM's sequential reader, and the Run<!-- -->:ai<!-- --> streamer's 256 concurrent threads (32 per GPU worker × 8 workers) were able to saturate the filesystem bandwidth. However, we found that Run<!-- -->:ai<!-- --> streamer did <em>not</em> improve EFS performance (~859 MiB/s vs ~1 GB/s with the default loader), likely because EFS Elastic throughput already delivers optimal single-client bandwidth for sequential reads.</p>
<p><img decoding="async" loading="lazy" alt="Comparison of sequential network read vs parallel download plus local read approaches" src="https://ashoksrirama.com/assets/images/sequential-vs-parallel-54b72843a5626f66d919f735810df788.png" width="1833" height="700" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cost-analysis">Cost Analysis<a href="https://ashoksrirama.com/blog/reducing-llm-cold-start-times-eks#cost-analysis" class="hash-link" aria-label="Direct link to Cost Analysis" title="Direct link to Cost Analysis" translate="no">​</a></h2>
<p>Faster cold starts aren't just about user experience — they directly reduce costs. Every minute a GPU instance spends loading a model is a minute it's not serving inference. On a p5.48xlarge Spot Instance at ~$32/hour, that's $0.53 per minute of wasted compute.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="monthly-cost-comparison-1-cold-startday-spot-pricing">Monthly Cost Comparison (1 cold start/day, Spot pricing)<a href="https://ashoksrirama.com/blog/reducing-llm-cold-start-times-eks#monthly-cost-comparison-1-cold-startday-spot-pricing" class="hash-link" aria-label="Direct link to Monthly Cost Comparison (1 cold start/day, Spot pricing)" title="Direct link to Monthly Cost Comparison (1 cold start/day, Spot pricing)" translate="no">​</a></h3>
<table><thead><tr><th>Strategy</th><th style="text-align:center">Storage</th><th style="text-align:center">GPU Idle</th><th style="text-align:center">Other</th><th style="text-align:center"><strong>Total</strong></th></tr></thead><tbody><tr><td><strong>Amazon S3 + s5cmd</strong></td><td style="text-align:center">$10</td><td style="text-align:center">$88</td><td style="text-align:center">—</td><td style="text-align:center"><strong>$98</strong></td></tr><tr><td><strong>Amazon S3 + Run<!-- -->:ai<!-- --> Streamer</strong></td><td style="text-align:center">$10</td><td style="text-align:center">$135</td><td style="text-align:center">—</td><td style="text-align:center"><strong>$145</strong></td></tr><tr><td><strong>Amazon ECR Image</strong></td><td style="text-align:center">$46</td><td style="text-align:center">$170</td><td style="text-align:center">—</td><td style="text-align:center"><strong>$216</strong></td></tr><tr><td><strong>Amazon EBS Snapshot</strong></td><td style="text-align:center">$38</td><td style="text-align:center">$175</td><td style="text-align:center">$540 FSR</td><td style="text-align:center"><strong>$753</strong></td></tr><tr><td>HuggingFace Direct</td><td style="text-align:center">$0</td><td style="text-align:center">$311</td><td style="text-align:center">—</td><td style="text-align:center"><strong>$311</strong></td></tr><tr><td>Mountpoint for Amazon S3</td><td style="text-align:center">$10</td><td style="text-align:center">$440</td><td style="text-align:center">—</td><td style="text-align:center"><strong>$450</strong></td></tr><tr><td>Amazon EFS (Elastic)</td><td style="text-align:center">$136</td><td style="text-align:center">$173</td><td style="text-align:center">$409 read fees</td><td style="text-align:center"><strong>$718</strong></td></tr><tr><td>FSx for Lustre</td><td style="text-align:center">$1,440</td><td style="text-align:center">$518</td><td style="text-align:center">—</td><td style="text-align:center"><strong>$1,958</strong></td></tr></tbody></table>
<p>The s5cmd strategy delivers the lowest total cost at <strong>$98</strong> — combining the cheapest storage option (S3 at $10/month) with the least GPU idle time ($88). FSx for Lustre, despite being the most expensive storage at $1,440/month, also incurs the highest GPU idle cost due to its slow sequential read performance.</p>
<p>A notable observation: Amazon EFS appears cost-competitive at first glance ($136/month storage + $173 GPU idle), but the Elastic throughput read charges of $0.03/GB add approximately $14 per cold start — totaling $409/month in read fees alone at one cold start per day.</p>
<p>Amazon EBS Snapshot storage itself is cheap ($0.05/GB/month = ~$38 for 750 GiB), but the <strong>Fast Snapshot Restore (FSR)</strong> charge of $0.75/hr per AZ adds $540/month if left enabled continuously. Without FSR, lazy-loading penalties make cold starts impractical — so you must either keep FSR enabled or implement automation to enable it only before scaling events and disable it afterward.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="choosing-the-right-strategy">Choosing the Right Strategy<a href="https://ashoksrirama.com/blog/reducing-llm-cold-start-times-eks#choosing-the-right-strategy" class="hash-link" aria-label="Direct link to Choosing the Right Strategy" title="Direct link to Choosing the Right Strategy" translate="no">​</a></h2>
<p>There is no one-size-fits-all answer. The best strategy depends on your operational priorities:</p>
<p><strong>Optimize for cold-start speed and cost → Amazon S3 + s5cmd Init Container</strong></p>
<p>This came out as top strategy balancing both speed and cost. It delivers the fastest cold start (5 min 31 sec), the lowest monthly cost ($98), and relies only on Amazon S3 — a service you're likely already using. The init container pattern is straightforward to implement and doesn't require any specialized storage infrastructure.</p>
<p>Best practices for this approach:</p>
<ul>
<li class="">Use s5cmd's <code>--concurrency 50 --part-size 100</code> flags to maximize parallelism</li>
<li class="">Ensure your pod's <a href="https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html" target="_blank" rel="noopener noreferrer" class="">service account has IAM permissions</a> to read from the S3 bucket</li>
</ul>
<p><strong>Optimize for simplicity → Amazon S3 + Run<!-- -->:ai<!-- --> Model Streamer</strong></p>
<p>If you want the simplest possible setup with no init containers, no volume mounts, and no local storage management, the Run<!-- -->:ai<!-- --> Model Streamer is an excellent choice. At 8 min 28 sec, it's faster than EFS and ECR while requiring nothing beyond a custom vLLM image with the streamer packages installed. Just point <code>--model</code> at an S3 path and set <code>--load-format runai_streamer</code>.</p>
<p><strong>Optimize for zero-download startup → Amazon EBS Snapshot</strong></p>
<p>If eliminating the download phase entirely is a priority, EBS snapshots deliver model data at node boot time — no init container, no network transfer. Combined with the Run<!-- -->:ai<!-- --> Model Streamer, the snapshot volume saturates gp3 bandwidth at ~1003 MiB/s for an 11-minute cold start. However, this approach carries the most operational overhead of any strategy:</p>
<ul>
<li class=""><strong>Snapshot lifecycle management:</strong> You must create and maintain EBS snapshots for each model version. Updating a model means creating a new snapshot and updating the Karpenter EC2NodeClass with the new snapshot ID.</li>
<li class=""><strong>Fast Snapshot Restore (FSR):</strong> FSR must be enabled in every target Availability Zone before nodes launch. At $0.75/hr per AZ, this costs $540/month if left on continuously. Consider automating FSR enable/disable around scaling events to reduce cost.</li>
<li class=""><strong>AZ pinning:</strong> Since FSR is per-AZ, your NodePool must be pinned to specific zones where FSR is enabled, reducing scheduling flexibility and Spot availability.</li>
<li class=""><strong>Non-standard AMI requirements:</strong> EBS snapshots require userData scripting to mount the volume, which means using AL2023 (not Bottlerocket) and managing NVIDIA device plugin installation separately.</li>
<li class=""><strong>NVMe device detection:</strong> On Nitro instances, NVMe device ordering is non-deterministic. The userData script must detect the snapshot volume by filesystem type (e.g., <code>blkid</code> for ext4) rather than hardcoding device names.</li>
</ul>
<p>This strategy is best suited for environments where download-free startup is a hard requirement and you have the operational maturity to manage snapshot lifecycles and FSR automation.</p>
<p><strong>Optimize for deployment immutability → Amazon ECR Container Image</strong></p>
<p>If you value the container-as-deployment-unit model — where the container image is the single artifact that contains everything needed to run — baking the model into the image is compelling. There are no runtime dependencies on external storage, and rollbacks are as simple as reverting to a prior image tag.</p>
<p>Consider this when:</p>
<ul>
<li class="">You need strict reproducibility across environments</li>
<li class="">Your CI/CD pipeline can handle building and pushing ~460 GB images</li>
<li class="">Model updates are infrequent</li>
</ul>
<p><strong>Optimize for shared access across pods → Amazon EFS</strong></p>
<p>When multiple pods or services need to read the same model weights — for example, running different model versions side by side, or sharing a model across development and staging environments — EFS provides a familiar ReadWriteMany filesystem that simplifies the architecture. Just be aware of the Elastic throughput read charges at scale.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://ashoksrirama.com/blog/reducing-llm-cold-start-times-eks#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>Cold-start time is a critical but often overlooked factor in LLM serving architectures. Our benchmarks show a <strong>6x difference</strong> between the fastest and slowest strategies — from 5 minutes with the s5cmd init container approach to 32 minutes with FSx for Lustre. The Run<!-- -->:ai<!-- --> Model Streamer emerged as a compelling middle ground, delivering 8-minute cold starts with the simplest possible configuration.</p>
<p>The key insight is that <strong>how</strong> you deliver model weights matters more than the raw throughput of your storage system. A high-throughput parallel filesystem like FSx for Lustre can underperform a simple S3 download when the access pattern is single-client sequential reads — but applying concurrent loading (via the Run<!-- -->:ai<!-- --> Model Streamer) to that same Lustre filesystem improved throughput by 4.4x. EBS snapshots with the Run<!-- -->:ai<!-- --> streamer similarly saturated the gp3 bandwidth ceiling (1003 MiB/s) where the default loader achieved only 367 MB/s — a 2.7x improvement. By rethinking the loading architecture — whether downloading in parallel to fast local storage or using concurrent tensor streaming — we achieved cold-start times that make GPU autoscaling practical for even the largest open-source models.</p>
<p>All Kubernetes manifests, Dockerfiles, and detailed benchmark data from this post are available in our <a href="https://github.com/ashoksrirama/genai-model-loading-strategies-eks" target="_blank" rel="noopener noreferrer" class="">GitHub repository</a>.</p>
<hr>
<p><em>Ashok Srirama is a Principal Solutions Architect at Amazon Web Services, based in Washington Crossing, PA. He specializes in serverless applications, containers, and architecting distributed systems. When he's not spending time with his family, he enjoys watching cricket, and driving his bimmer.</em></p>]]></content:encoded>
            <category>aws</category>
            <category>blogs</category>
        </item>
        <item>
            <title><![CDATA[Running Argo Workflows Across Multiple EKS Clusters]]></title>
            <link>https://ashoksrirama.com/blog/cross-cluster-argo-workflows-eks</link>
            <guid>https://ashoksrirama.com/blog/cross-cluster-argo-workflows-eks</guid>
            <pubDate>Mon, 15 Jan 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[In this walkthrough, we will explore how to run argo-workflow-controller in a hub EKS cluster and execute the actual workflows in a spoke EKS cluster. These two EKS clusters are deployed to different AWS Accounts and we will utilize IAM Roles and EKS Pod Identity for authentication.]]></description>
            <content:encoded><![CDATA[<p>In this walkthrough, we will explore how to run argo-workflow-controller in a hub EKS cluster and execute the actual workflows in a spoke EKS cluster. These two EKS clusters are deployed to different AWS Accounts and we will utilize IAM Roles and EKS Pod Identity for authentication.</p>
<p>In this approach, the entire workflow is executed in the spoke cluster. argo-workflow-controller deployed to the hub cluster watches for workflow CRD changes in the spoke cluster and launches k8s pods to execute the various steps.</p>
<p><img decoding="async" loading="lazy" alt="ArgoWorkflows" src="https://ashoksrirama.com/assets/images/argo-workflows-0483bc9be7dcc012f5fd912feacd92be.png" width="2632" height="1044" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="prerequisites">Prerequisites<a href="https://ashoksrirama.com/blog/cross-cluster-argo-workflows-eks#prerequisites" class="hash-link" aria-label="Direct link to Prerequisites" title="Direct link to Prerequisites" translate="no">​</a></h3>
<ul>
<li class="">Install the <code>eksctl</code></li>
<li class="">2 AWS Accounts</li>
<li class="">AWS CLI profiles setup for both AWS Accounts</li>
<li class="">Setup the env variables</li>
</ul>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">export AWS_REGION=us-west-2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export EKS_HUB_CLUSTER=ekshub</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export EKS_SPOKE_CLUSTER=eksSPOKE</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export EKS_HUB_ACCOUNT=112233445566</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export EKS_SPOKE_ACCOUNT=998877665544</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-1-create-eks-cluster-in-the-hub-aws-account">Step 1: Create EKS Cluster in the hub AWS Account<a href="https://ashoksrirama.com/blog/cross-cluster-argo-workflows-eks#step-1-create-eks-cluster-in-the-hub-aws-account" class="hash-link" aria-label="Direct link to Step 1: Create EKS Cluster in the hub AWS Account" title="Direct link to Step 1: Create EKS Cluster in the hub AWS Account" translate="no">​</a></h3>
<p>Create a hub EKS cluster with default configuration</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">eksctl create cluster --name $EKS_HUB_CLUSTER --region $AWS_REGION --profile hub-profile</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">.......</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[✔]  EKS cluster "ekshub" in "us-west-2" region is ready</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">aws eks update-kubeconfig --name $EKS_HUB_CLUSTER --region $AWS_REGION --alias $EKS_HUB_CLUSTER --profile hub-profile</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-2-create-eks-cluster-in-the-spoke-aws-account">Step 2: Create EKS Cluster in the spoke AWS Account<a href="https://ashoksrirama.com/blog/cross-cluster-argo-workflows-eks#step-2-create-eks-cluster-in-the-spoke-aws-account" class="hash-link" aria-label="Direct link to Step 2: Create EKS Cluster in the spoke AWS Account" title="Direct link to Step 2: Create EKS Cluster in the spoke AWS Account" translate="no">​</a></h3>
<p>Create a spoke EKS cluster with default configuration in same or different AWS Account</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">eksctl create cluster --name $EKS_SPOKE_CLUSTER --region $AWS_REGION --profile spoke-profile</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">.......</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[✔]  EKS cluster "eksspoke" in "us-west-2" region is ready</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">aws eks update-kubeconfig --name $EKS_SPOKE_CLUSTER --region $AWS_REGION --alias $EKS_SPOKE_CLUSTER --profile spoke-profile</span><br></span></code></pre></div></div>
<p>Wait for the cluster messages as shown above and proceed with next steps.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-3-create-custom-images-of-argo-workflows-and-argo-server-as-they-require-aws-cli-to-interact-with-spoke-eks-clusters">Step 3: Create custom images of Argo Workflows and Argo Server as they require AWS CLI to interact with spoke EKS clusters<a href="https://ashoksrirama.com/blog/cross-cluster-argo-workflows-eks#step-3-create-custom-images-of-argo-workflows-and-argo-server-as-they-require-aws-cli-to-interact-with-spoke-eks-clusters" class="hash-link" aria-label="Direct link to Step 3: Create custom images of Argo Workflows and Argo Server as they require AWS CLI to interact with spoke EKS clusters" title="Direct link to Step 3: Create custom images of Argo Workflows and Argo Server as they require AWS CLI to interact with spoke EKS clusters" translate="no">​</a></h3>
<p>Create a new Argo Workflow container image with AWS CLI</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cat &lt;&lt; EOF &gt; DockerfileArgow</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">FROM quay.io/argoproj/workflow-controller:v3.5.5 as argow</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">FROM public.ecr.aws/aws-cli/aws-cli:2.15.33</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">COPY --from=argow /etc/ssh/ssh_known_hosts /etc/ssh/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">COPY --from=argow /etc/nsswitch.conf /etc/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">COPY --from=argow /bin/workflow-controller /bin/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ENTRYPOINT [ "workflow-controller" ]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">docker build -t sriram430/workflow-controller-aws:v3.5.5 -f DockerfileArgow .</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">docker push sriram430/workflow-controller-aws:v3.5.5</span><br></span></code></pre></div></div>
<p>Create a new Argo Server container image with AWS CLI</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cat &lt;&lt; EOF &gt; DockerfileArgoServer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">FROM quay.io/argoproj/argocli:v3.5.5 as argocli</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">FROM public.ecr.aws/aws-cli/aws-cli:2.15.33</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">COPY --from=argocli /etc/ssh/ssh_known_hosts /etc/ssh/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">COPY --from=argocli /etc/nsswitch.conf /etc/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">COPY --from=argocli /bin/argo /bin/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ENTRYPOINT [ "argo" ]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">docker build -t sriram430/argocli-aws:v3.5.5 -f DockerfileArgoServer .</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">docker push sriram430/argocli-aws:v3.5.5</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-4-create-an-iam-role-for-the-hub-cluster-to-authenticate-with-spoke-cluster">Step 4: Create an IAM role for the hub cluster to authenticate with spoke cluster<a href="https://ashoksrirama.com/blog/cross-cluster-argo-workflows-eks#step-4-create-an-iam-role-for-the-hub-cluster-to-authenticate-with-spoke-cluster" class="hash-link" aria-label="Direct link to Step 4: Create an IAM role for the hub cluster to authenticate with spoke cluster" title="Direct link to Step 4: Create an IAM role for the hub cluster to authenticate with spoke cluster" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws iam create-policy \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    --policy-name AmazonEKSReadPolicy \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    --profile hub-profile \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    --policy-document \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">'{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "Version": "2012-10-17",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "Statement": [</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            "Sid": "VisualEditor0",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            "Effect": "Allow",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            "Action": [</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                "eks:AccessKubernetesApi",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                "eks:DescribeCluster"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            ],</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            "Resource": "*"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}'</span><br></span></code></pre></div></div>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">eksctl create addon --name eks-pod-identity-agent \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--cluster $EKS_HUB_CLUSTER --region $AWS_REGION \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--profile hub-profile</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">eksctl create podidentityassociation \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--cluster $EKS_HUB_CLUSTER \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--namespace argo \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--service-account-name argo \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--role-name eks-hub-cluster-role \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--permission-policy-arns arn:aws:iam::${EKS_HUB_ACCOUNT}:policy/AmazonEKSReadPolicy \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--region $AWS_REGION \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--profile hub-profile</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">eksctl create podidentityassociation \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--cluster $EKS_HUB_CLUSTER \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--namespace argo \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--service-account-name argo-server \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--role-arn arn:aws:iam::${EKS_HUB_ACCOUNT}:role/eks-hub-cluster-role \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--region $AWS_REGION \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--profile hub-profile</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-5-create-a-cluster-access-entry-for-the-hub-clusters-iam-role-in-the-spoke-eks-cluster">Step 5: Create a cluster access entry for the Hub Cluster's IAM role in the spoke EKS cluster<a href="https://ashoksrirama.com/blog/cross-cluster-argo-workflows-eks#step-5-create-a-cluster-access-entry-for-the-hub-clusters-iam-role-in-the-spoke-eks-cluster" class="hash-link" aria-label="Direct link to Step 5: Create a cluster access entry for the Hub Cluster's IAM role in the spoke EKS cluster" title="Direct link to Step 5: Create a cluster access entry for the Hub Cluster's IAM role in the spoke EKS cluster" translate="no">​</a></h3>
<p><em>For testing purposes we are using AmazonEKSClusterAdminPolicy, scope down these permissions based on argo-workflow requirements.</em></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">aws eks create-access-entry --cluster-name $EKS_SPOKE_CLUSTER --principal-arn arn:aws:iam::${EKS_HUB_ACCOUNT}:role/eks-hub-cluster-role \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    --profile spoke-profile --region $AWS_REGION</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">aws eks associate-access-policy --cluster-name $EKS_SPOKE_CLUSTER --principal-arn arn:aws:iam::${EKS_HUB_ACCOUNT}:role/eks-hub-cluster-role \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    --access-scope type=cluster --policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    --profile spoke-profile --region $AWS_REGION</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-6-create-the-argo-workflows-crds-service-account-roles-role-bindings-in-spoke-eks-cluster">Step 6: Create the Argo Workflows CRDs, Service Account, Roles, Role Bindings in spoke EKS cluster<a href="https://ashoksrirama.com/blog/cross-cluster-argo-workflows-eks#step-6-create-the-argo-workflows-crds-service-account-roles-role-bindings-in-spoke-eks-cluster" class="hash-link" aria-label="Direct link to Step 6: Create the Argo Workflows CRDs, Service Account, Roles, Role Bindings in spoke EKS cluster" title="Direct link to Step 6: Create the Argo Workflows CRDs, Service Account, Roles, Role Bindings in spoke EKS cluster" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -f https://raw.githubusercontent.com/ashoksrirama/argo-workflows-demo/main/argo-workflows-spoke.yaml --context $EKS_SPOKE_CLUSTER</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-7-create-a-k8s-secret-in-hub-cluster-with-kubeconfig-file-of-spoke-cluster">Step 7: Create a k8s secret in hub cluster with kubeconfig file of spoke cluster<a href="https://ashoksrirama.com/blog/cross-cluster-argo-workflows-eks#step-7-create-a-k8s-secret-in-hub-cluster-with-kubeconfig-file-of-spoke-cluster" class="hash-link" aria-label="Direct link to Step 7: Create a k8s secret in hub cluster with kubeconfig file of spoke cluster" title="Direct link to Step 7: Create a k8s secret in hub cluster with kubeconfig file of spoke cluster" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Create the kubeconfig file of the spoke cluster</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">aws eks update-kubeconfig --name $EKS_SPOKE_CLUSTER --region $AWS_REGION --profile spoke-profile --kubeconfig spoke-kubeconfig</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export KUBECONFIG_SPOKE=$(head -n -3 spoke-kubeconfig) #If fails, replace head with ghead</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Create namespace</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl create namespace argo --context $EKS_HUB_CLUSTER</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Create k8s secret with spoke cluster's kubeconfig</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl create secret generic spoke-kubeconfig --from-literal kubeconfig=$KUBECONFIG_SPOKE -n argo --context $EKS_HUB_CLUSTER</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-8-install-the-argo-workflow-controller-and-argo-server-in-the-hub-eks-cluster">Step 8: Install the Argo Workflow Controller and Argo Server in the Hub EKS cluster<a href="https://ashoksrirama.com/blog/cross-cluster-argo-workflows-eks#step-8-install-the-argo-workflow-controller-and-argo-server-in-the-hub-eks-cluster" class="hash-link" aria-label="Direct link to Step 8: Install the Argo Workflow Controller and Argo Server in the Hub EKS cluster" title="Direct link to Step 8: Install the Argo Workflow Controller and Argo Server in the Hub EKS cluster" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Replace ARGO_WORKFLOWS_VERSION with latest Argo Workflows version</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl apply -f https://github.com/argoproj/argo-workflows/releases/download/v&lt;&lt;ARGO_WORKFLOWS_VERSION&gt;&gt;/install.yaml --context $EKS_HUB_CLUSTER</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-9-patch-argo-server-authentication">Step 9: Patch argo-server authentication<a href="https://ashoksrirama.com/blog/cross-cluster-argo-workflows-eks#step-9-patch-argo-server-authentication" class="hash-link" aria-label="Direct link to Step 9: Patch argo-server authentication" title="Direct link to Step 9: Patch argo-server authentication" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cat &lt;&lt; EOF &gt; argo-server-patch.yaml</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  template:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      containers:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      - name: argo-server</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        image: sriram430/argocli-aws:v3.5.5 #replace with your image</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        args:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        - server</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        - '--auth-mode=server'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        - '--loglevel=debug' #setup loglevel</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        - '--kubeconfig=/kube/config/kubeconfig'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        volumeMounts:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        - name: spoke-kubeconfig</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          mountPath: /kube/config</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        securityContext:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          capabilities:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          runAsNonRoot: false</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          readOnlyRootFilesystem: false</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          allowPrivilegeEscalation: false</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      volumes:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      - name: spoke-kubeconfig</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        secret:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          secretName: spoke-kubeconfig</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl patch deployment -n argo argo-server --patch-file argo-server-patch.yaml --context $EKS_HUB_CLUSTER</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-10-patch-argo-workflow-controller">Step 10: Patch argo-workflow controller<a href="https://ashoksrirama.com/blog/cross-cluster-argo-workflows-eks#step-10-patch-argo-workflow-controller" class="hash-link" aria-label="Direct link to Step 10: Patch argo-workflow controller" title="Direct link to Step 10: Patch argo-workflow controller" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cat &lt;&lt; EOF &gt; workflow-controller-patch.yaml</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  template:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      containers:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      - name: workflow-controller</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        image: sriram430/workflow-controller-aws:v3.5.5 #replace with your image</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        command:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        - workflow-controller</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        - '--loglevel=debug' #setup loglevel</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        - '--kubeconfig=/kube/config/kubeconfig'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        volumeMounts:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        - name: spoke-kubeconfig</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          mountPath: /kube/config</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        securityContext:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          capabilities:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          runAsNonRoot: false</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          readOnlyRootFilesystem: false</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          allowPrivilegeEscalation: false</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      volumes:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      - name: spoke-kubeconfig</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        secret:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          secretName: spoke-kubeconfig</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl patch deployment -n argo workflow-controller --patch-file workflow-controller-patch.yaml --context $EKS_HUB_CLUSTER</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-11-confirm-the-pods-are-up-and-running">Step 11: Confirm the pods are up and running<a href="https://ashoksrirama.com/blog/cross-cluster-argo-workflows-eks#step-11-confirm-the-pods-are-up-and-running" class="hash-link" aria-label="Direct link to Step 11: Confirm the pods are up and running" title="Direct link to Step 11: Confirm the pods are up and running" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get pods -n argo --context $EKS_HUB_CLUSTER</span><br></span></code></pre></div></div>
<div class="language-output codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-output codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">NAME                                  READY   STATUS    RESTARTS   AGE</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">argo-server-56547dc758-842v5          1/1     Running   0          4m13s</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">workflow-controller-c699d6f49-9vc5j   1/1     Running   0          108s</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-12-submit-a-sample-workflow-in-spoke-eks-cluster">Step 12: Submit a sample workflow in Spoke EKS Cluster<a href="https://ashoksrirama.com/blog/cross-cluster-argo-workflows-eks#step-12-submit-a-sample-workflow-in-spoke-eks-cluster" class="hash-link" aria-label="Direct link to Step 12: Submit a sample workflow in Spoke EKS Cluster" title="Direct link to Step 12: Submit a sample workflow in Spoke EKS Cluster" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cat &lt;&lt; EOF &gt; hello-workflow.yaml</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: argoproj.io/v1alpha1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: Workflow                  # new type of k8s spec</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  generateName: hello-world-    # name of the workflow spec</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  entrypoint: whalesay          # invoke the whalesay template</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  templates:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    - name: whalesay              # name of the template</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      container:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        image: docker/whalesay</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        command: [ cowsay ]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        args: [ "hello world" ]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        resources: # limit the resources</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          limits:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            memory: 32Mi</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            cpu: 100m</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kubectl create -f hello-workflow.yaml --context $EKS_SPOKE_CLUSTER</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-13-verify-the-workflow-execution">Step 13: Verify the workflow execution<a href="https://ashoksrirama.com/blog/cross-cluster-argo-workflows-eks#step-13-verify-the-workflow-execution" class="hash-link" aria-label="Direct link to Step 13: Verify the workflow execution" title="Direct link to Step 13: Verify the workflow execution" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get pods --context $EKS_SPOKE_CLUSTER</span><br></span></code></pre></div></div>
<div class="language-output codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-output codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">NAME                READY   STATUS      RESTARTS   AGE</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">hello-world-klhjq   0/2     Completed   0          97s</span><br></span></code></pre></div></div>
<p>We completed the walkthrough of running argo-workflow controller in Hub EKS cluster and actual workflow executions in a spoke EKS cluster.</p>]]></content:encoded>
            <category>eks</category>
            <category>kubernetes</category>
            <category>argo-workflows</category>
        </item>
        <item>
            <title><![CDATA[Bin-Packing Pods with MostAllocated Scheduler on EKS]]></title>
            <link>https://ashoksrirama.com/blog/custom-scheduler-mostallocated-eks</link>
            <guid>https://ashoksrirama.com/blog/custom-scheduler-mostallocated-eks</guid>
            <pubDate>Wed, 10 Jan 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[As of this writing, Amazon EKS doesn't allow you to customize the kube-scheduler configuration. Many customers ask how they can use MostAllocated strategy to efficiently binpack their worker nodes. Let's see how we can create a custom scheduler with MostAllocated strategy and assign it to our k8s deployments.]]></description>
            <content:encoded><![CDATA[<p>As of this writing, Amazon EKS doesn't allow you to customize the <code>kube-scheduler</code> configuration. Many customers ask how they can use <code>MostAllocated</code> strategy to efficiently binpack their worker nodes. Let's see how we can create a custom scheduler with <code>MostAllocated</code> strategy and assign it to our k8s deployments.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="prerequisites">Prerequisites<a href="https://ashoksrirama.com/blog/custom-scheduler-mostallocated-eks#prerequisites" class="hash-link" aria-label="Direct link to Prerequisites" title="Direct link to Prerequisites" translate="no">​</a></h3>
<ul>
<li class="">Install the <code>eksctl</code></li>
</ul>
<p>Create a basic cluster with default configuration</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">eksctl create cluster --name eksdemo --region us-west-2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">.......</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[✔]  EKS cluster "eksdemo" in "us-west-2" region is ready</span><br></span></code></pre></div></div>
<p>Wait for the cluster message as shown above and export the following <code>env</code> variables.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">export AWS_REGION=us-west-2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export EKS_CLUSTER=eksdemo</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-1-create-mostallocated-kube-scheduler-configuration">Step 1: Create <code>MostAllocated</code> kube-scheduler configuration<a href="https://ashoksrirama.com/blog/custom-scheduler-mostallocated-eks#step-1-create-mostallocated-kube-scheduler-configuration" class="hash-link" aria-label="Direct link to step-1-create-mostallocated-kube-scheduler-configuration" title="Direct link to step-1-create-mostallocated-kube-scheduler-configuration" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cat &lt;&lt;EOF | kubectl apply -f -</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">data:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  my-scheduler-config.yaml: |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    apiVersion: kubescheduler.config.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    kind: KubeSchedulerConfiguration</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    profiles:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    - pluginConfig:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      - args:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          scoringStrategy:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            resources:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            # Update these weights according to your requirements</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            - name: cpu</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              weight: 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            - name: memory</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              weight: 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            # Considering GPU resources in utilization calculation</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            - name: nvidia.com/gpu</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              weight: 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            type: MostAllocated</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        name: NodeResourcesFit</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      plugins:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        score:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          enabled:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          - name: NodeResourcesFit</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            weight: 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          disabled:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          # Disabling these plugins to avoid spreading the pods</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          - name: NodeResourcesBalancedAllocation</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          - name: PodTopologySpread</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      schedulerName: my-scheduler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    # Enabling leaderElection require additional permissions to the SA</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    leaderElection:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      leaderElect: true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      resourceNamespace: kube-system    </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      resourceName: my-scheduler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: ConfigMap</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: most-allocated-scheduler-policy</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  namespace: kube-system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span></code></pre></div></div>
<div class="language-output codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-output codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">configmap/most-allocated-scheduler-policy created</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-2-create-serviceaccount-and-clusterrolebindings-for-the-custom-scheduler">Step 2: Create ServiceAccount and ClusterRolebindings for the custom scheduler<a href="https://ashoksrirama.com/blog/custom-scheduler-mostallocated-eks#step-2-create-serviceaccount-and-clusterrolebindings-for-the-custom-scheduler" class="hash-link" aria-label="Direct link to Step 2: Create ServiceAccount and ClusterRolebindings for the custom scheduler" title="Direct link to Step 2: Create ServiceAccount and ClusterRolebindings for the custom scheduler" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cat &lt;&lt;EOF | kubectl apply -f -</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: ServiceAccount</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: my-scheduler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  namespace: kube-system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">---</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: rbac.authorization.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: ClusterRoleBinding</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: my-scheduler-as-kube-scheduler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">subjects:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">- kind: ServiceAccount</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: my-scheduler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  namespace: kube-system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">roleRef:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  kind: ClusterRole</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: system:kube-scheduler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  apiGroup: rbac.authorization.k8s.io</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">---</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: rbac.authorization.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: ClusterRoleBinding</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: my-scheduler-as-volume-scheduler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">subjects:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">- kind: ServiceAccount</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: my-scheduler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  namespace: kube-system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">roleRef:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  kind: ClusterRole</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: system:volume-scheduler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  apiGroup: rbac.authorization.k8s.io</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">---</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: rbac.authorization.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: RoleBinding</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: my-scheduler-extension-apiserver-authentication-reader</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  namespace: kube-system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">roleRef:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  kind: Role</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: extension-apiserver-authentication-reader</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  apiGroup: rbac.authorization.k8s.io</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">subjects:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">- kind: ServiceAccount</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: my-scheduler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  namespace: kube-system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">---</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: ClusterRole</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: rbac.authorization.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: my-scheduler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">rules:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">- apiGroups:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - coordination.k8s.io</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  resources:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - leases</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  verbs:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - create</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - get</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - list</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - update</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">---</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: ClusterRoleBinding</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: rbac.authorization.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: my-scheduler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  namespace: kube-system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">roleRef:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  apiGroup: rbac.authorization.k8s.io</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  kind: ClusterRole</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: my-scheduler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">subjects:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">- kind: ServiceAccount</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: my-scheduler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  namespace: kube-system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span></code></pre></div></div>
<div class="language-output codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-output codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">serviceaccount/my-scheduler created</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">clusterrolebinding.rbac.authorization.k8s.io/my-scheduler-as-kube-scheduler created</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">clusterrolebinding.rbac.authorization.k8s.io/my-scheduler-as-volume-scheduler created</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">rolebinding.rbac.authorization.k8s.io/my-scheduler-extension-apiserver-authentication-reader created</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">clusterrole.rbac.authorization.k8s.io/my-scheduler created</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">clusterrolebinding.rbac.authorization.k8s.io/my-scheduler created</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-3-deploy-the-custom-scheduler-using-above-configuration">Step 3: Deploy the custom scheduler using above configuration<a href="https://ashoksrirama.com/blog/custom-scheduler-mostallocated-eks#step-3-deploy-the-custom-scheduler-using-above-configuration" class="hash-link" aria-label="Direct link to Step 3: Deploy the custom scheduler using above configuration" title="Direct link to Step 3: Deploy the custom scheduler using above configuration" translate="no">​</a></h3>
<p><strong>Make sure your kube-scheduler image matches your EKS cluster version</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cat &lt;&lt;EOF | kubectl apply -f -</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: apps/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: Deployment</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  labels:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    component: scheduler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    tier: control-plane</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: my-scheduler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  namespace: kube-system</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  selector:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    matchLabels:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      component: scheduler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      tier: control-plane</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  replicas: 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  template:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      labels:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        component: scheduler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        tier: control-plane</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        version: second</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      serviceAccountName: my-scheduler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      containers:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      - command:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        - /usr/local/bin/kube-scheduler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        args:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        - --config=/etc/kubernetes/my-scheduler/my-scheduler-config.yaml</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        - --leader-elect=true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        # log verbose level</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        - --v=4</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        # update the image based on your EKS version</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        image: registry.k8s.io/kube-scheduler:v1.28.5</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        livenessProbe:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          httpGet:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            path: /healthz</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            port: 10259</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            scheme: HTTPS</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          initialDelaySeconds: 15</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        name: kube-second-scheduler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        readinessProbe:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          httpGet:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            path: /healthz</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            port: 10259</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            scheme: HTTPS</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        resources:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          requests:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            cpu: '0.1'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        securityContext:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          privileged: false</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        volumeMounts:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          - name: config-volume</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            mountPath: /etc/kubernetes/my-scheduler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      hostNetwork: false</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      hostPID: false</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      volumes:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        - name: config-volume</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          configMap:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            name: most-allocated-scheduler-policy</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span></code></pre></div></div>
<div class="language-output codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-output codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">deployment.apps/my-scheduler created</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-4-deploy-a-sample-application">Step 4: Deploy a sample application<a href="https://ashoksrirama.com/blog/custom-scheduler-mostallocated-eks#step-4-deploy-a-sample-application" class="hash-link" aria-label="Direct link to Step 4: Deploy a sample application" title="Direct link to Step 4: Deploy a sample application" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cat &lt;&lt;EOF | kubectl apply -f -</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: apps/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: Deployment</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  labels:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    app: nginx</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: nginx</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  replicas: 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  selector:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    matchLabels:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      app: nginx</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  strategy: {}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  template:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      labels:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        app: nginx</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      containers:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      - image: nginx</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        name: nginx</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      schedulerName: my-scheduler</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span></code></pre></div></div>
<div class="language-output codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-output codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">deployment.apps/nginx created</span><br></span></code></pre></div></div>
<p>nginx pods will be scheduled to a node with higher utilization based on the scoring strategy defined in the Scheduler Configuration.</p>]]></content:encoded>
            <category>eks</category>
            <category>kubernetes</category>
            <category>scheduling</category>
        </item>
        <item>
            <title><![CDATA[Running Nginx Ingress Behind AWS ALB on EKS]]></title>
            <link>https://ashoksrirama.com/blog/nginx-ingress-controller-eks</link>
            <guid>https://ashoksrirama.com/blog/nginx-ingress-controller-eks</guid>
            <pubDate>Fri, 05 Jan 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[In this walkthrough, we will use two ingress controllers setup in our Amazon EKS cluster. Nginx ingress is used to expose all applications running in the cluster and nginx ingress controller itself is exposed via AWS Application LoadBalancer (Ingress resource) by AWS LB controller. By doing this, you can attach AWS Web Application Firewall (AWS WAF) on the ALB to get protection against Layer 7 attacks, and overcome the hard limits of AWS ALB (such as 100 target groups per ALB, etc.,).]]></description>
            <content:encoded><![CDATA[<p>In this walkthrough, we will use two ingress controllers setup in our Amazon EKS cluster. Nginx ingress is used to expose all applications running in the cluster and nginx ingress controller itself is exposed via AWS Application LoadBalancer (Ingress resource) by AWS LB controller. By doing this, you can attach AWS Web Application Firewall (AWS WAF) on the ALB to get protection against Layer 7 attacks, and overcome the hard limits of AWS ALB (such as 100 target groups per ALB, etc.,).</p>
<p><img decoding="async" loading="lazy" alt="Nginx Ingress Controller" src="https://ashoksrirama.com/assets/images/eks-nginx-controller-9c3ed3485989c704037999fe2af74fd4.png" width="998" height="427" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="prerequisites">Prerequisites<a href="https://ashoksrirama.com/blog/nginx-ingress-controller-eks#prerequisites" class="hash-link" aria-label="Direct link to Prerequisites" title="Direct link to Prerequisites" translate="no">​</a></h3>
<ul>
<li class="">Install the <code>eksctl</code></li>
</ul>
<p>Create a basic cluster with default configuration</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">eksctl create cluster --name eksdemo --region us-west-2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">.......</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[✔]  EKS cluster "eksdemo" in "us-west-2" region is ready</span><br></span></code></pre></div></div>
<p>Wait for the cluster message as shown above and export the following <code>env</code> variables.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">export AWS_REGION=us-west-2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export EKS_CLUSTER=eksdemo</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-1-install-aws-lb-controller-to-create-aws-elb-resources-for-k8s-servicetype-loadbalancer-and-ingress-resources">Step 1: Install AWS LB Controller to create AWS ELB resources for k8s ServiceType <code>LoadBalancer</code> and Ingress resources<a href="https://ashoksrirama.com/blog/nginx-ingress-controller-eks#step-1-install-aws-lb-controller-to-create-aws-elb-resources-for-k8s-servicetype-loadbalancer-and-ingress-resources" class="hash-link" aria-label="Direct link to step-1-install-aws-lb-controller-to-create-aws-elb-resources-for-k8s-servicetype-loadbalancer-and-ingress-resources" title="Direct link to step-1-install-aws-lb-controller-to-create-aws-elb-resources-for-k8s-servicetype-loadbalancer-and-ingress-resources" translate="no">​</a></h3>
<p>Follow the instructions <a href="https://docs.aws.amazon.com/eks/latest/userguide/lbc-helm.html" target="_blank" rel="noopener noreferrer" class="">here</a> to install the AWS LB controller addon on the EKS cluster.</p>
<p>Validate the deployment after the installation</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get deployment -n kube-system aws-load-balancer-controller</span><br></span></code></pre></div></div>
<div class="language-output codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-output codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">NAME                           READY   UP-TO-DATE   AVAILABLE   AGE</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">aws-load-balancer-controller   2/2     2            2           84s</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-2-install-the-nginx-ingress-controller">Step 2: Install the nginx-ingress controller<a href="https://ashoksrirama.com/blog/nginx-ingress-controller-eks#step-2-install-the-nginx-ingress-controller" class="hash-link" aria-label="Direct link to Step 2: Install the nginx-ingress controller" title="Direct link to Step 2: Install the nginx-ingress controller" translate="no">​</a></h3>
<p>We are installing the nginx-ingress controller on the cluster and exposing it internally via <code>ClusterIP</code> service.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">helm install my-nginx ingress-nginx/ingress-nginx \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--namespace nginx-ingress \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--set controller.service.type=ClusterIP \</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--create-namespace</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-3-expose-the-nginx-ingress-controller-via-aws-application-loadbalancer">Step 3: Expose the nginx-ingress controller via AWS Application LoadBalancer<a href="https://ashoksrirama.com/blog/nginx-ingress-controller-eks#step-3-expose-the-nginx-ingress-controller-via-aws-application-loadbalancer" class="hash-link" aria-label="Direct link to Step 3: Expose the nginx-ingress controller via AWS Application LoadBalancer" title="Direct link to Step 3: Expose the nginx-ingress controller via AWS Application LoadBalancer" translate="no">​</a></h3>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>warning</div><div class="admonitionContent_BuS1"><p>In the below example, only port <code>80</code> is exposed via ALB. Its highly recommended to use https (443) along with a SSL certificate from AWS Certificate Manager.</p></div></div>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cat &lt;&lt;EOF | kubectl apply -f -</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: networking.k8s.io/v1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: Ingress</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  namespace: nginx-ingress</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: ingress-nginx-controller</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  annotations:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    alb.ingress.kubernetes.io/scheme: internet-facing</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    alb.ingress.kubernetes.io/target-type: ip</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    alb.ingress.kubernetes.io/target-group-attributes: deregistration_delay.timeout_seconds=10</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    alb.ingress.kubernetes.io/healthcheck-path: /healthz</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ingressClassName: alb</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  rules:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    - http:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        paths:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        - path: /</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          pathType: Prefix</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          backend:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            service:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              name: my-nginx-ingress-nginx-controller</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              port:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                number: 80</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span></code></pre></div></div>
<p>wait for the ALB creation, nginx-ingress pod registration to complete. And grab the ALB dns name from the below command:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">kubectl get ingress -n nginx-ingress</span><br></span></code></pre></div></div>
<div class="language-output codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-output codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">NAME                       CLASS   HOSTS   ADDRESS                                                                   PORTS   AGE</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ingress-nginx-controller   alb     *       k8s-nginxing-ingressn-af3887fb52-1426628659.us-west-2.elb.amazonaws.com   80      2m</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-4-deploy-sample-application-and-exposed-via-nginx-ingress">Step 4: Deploy sample application and exposed via nginx ingress<a href="https://ashoksrirama.com/blog/nginx-ingress-controller-eks#step-4-deploy-sample-application-and-exposed-via-nginx-ingress" class="hash-link" aria-label="Direct link to Step 4: Deploy sample application and exposed via nginx ingress" title="Direct link to Step 4: Deploy sample application and exposed via nginx ingress" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">cat &lt;&lt;EOF | kubectl apply -f -</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: apps/v1 </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: Deployment </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: httpd-app</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  namespace: default </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  replicas: 2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  selector:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    matchLabels:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      app: httpd-app</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  template:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      labels:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        app: httpd-app</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      containers:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      - name: httpd-app</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        image: httpd:latest</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        ports:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        - containerPort: 80 </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--- </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: v1 </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: Service </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: httpd-app-svc</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  namespace: default</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ports:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  - port: 80</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    targetPort: 80</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    protocol: TCP</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  selector:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    app: httpd-app</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">---</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">apiVersion: networking.k8s.io/v1 </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">kind: Ingress </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">metadata:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name: httpd-app-ingress</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  namespace: default</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  annotations:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    nginx.ingress.kubernetes.io/rewrite-target: /</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">spec:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ingressClassName: nginx</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  rules:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    - http:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        paths:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        - backend:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            service:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              name: httpd-app-svc</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              port:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                number: 80</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          path: /httpd</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          pathType: Prefix</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">EOF</span><br></span></code></pre></div></div>
<div class="language-output codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-output codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">deployment.apps/httpd-app created</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">service/httpd-app-svc created</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ingress.networking.k8s.io/httpd-app-ingress created</span><br></span></code></pre></div></div>
<p>Step 5: Test the setup</p>
<p>Access the sample <code>httpd</code> application using ALB Ingress url (from Step 3)</p>
<div class="language-curl codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-curl codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">curl http://k8s-nginxing-ingressn-af3887fb52-1426628659.us-west-2.elb.amazonaws.com/httpd</span><br></span></code></pre></div></div>
<div class="language-output codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-output codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">&lt;html&gt;&lt;body&gt;&lt;h1&gt;It works!&lt;/h1&gt;&lt;/body&gt;&lt;/html&gt;</span><br></span></code></pre></div></div>
<p>We successfully validated two ingress controllers setup in our Amazon EKS cluster.</p>]]></content:encoded>
            <category>eks</category>
            <category>kubernetes</category>
            <category>networking</category>
        </item>
        <item>
            <title><![CDATA[CON314: Accelerating Modern Application Development with Amazon ECS]]></title>
            <link>https://ashoksrirama.com/blog/con314-reinvent-ecs-best-practices</link>
            <guid>https://ashoksrirama.com/blog/con314-reinvent-ecs-best-practices</guid>
            <pubDate>Tue, 28 Nov 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Building architecture based on best practices helps organizations build performant, cost-efficient, and secure applications. This chalk talk provides insights into Amazon ECS best practices grounded in the AWS Well-Architected Framework. After reviewing the essential best practices of each pillar, see through whiteboarding how these best practices are actually applied to Amazon ECS workloads.]]></description>
            <content:encoded><![CDATA[<p>Building architecture based on best practices helps organizations build performant, cost-efficient, and secure applications. This chalk talk provides insights into Amazon ECS best practices grounded in the AWS Well-Architected Framework. After reviewing the essential best practices of each pillar, see through whiteboarding how these best practices are actually applied to Amazon ECS workloads.</p>
<p><img decoding="async" loading="lazy" alt="Drawings" src="https://ashoksrirama.com/assets/images/con314-drawings-0b504194ea13bce030268e848f5a2f4a.png" width="13577" height="10756" class="img_ev3q"></p>]]></content:encoded>
            <category>aws</category>
            <category>reinvent</category>
            <category>ecs</category>
        </item>
    </channel>
</rss>