<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: John</title>
    <description>The latest articles on DEV Community by John (@tamecalm).</description>
    <link>https://dev.to/tamecalm</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F210398%2F487d5cc0-9604-4fc1-866c-c57704b2314b.jpeg</url>
      <title>DEV Community: John</title>
      <link>https://dev.to/tamecalm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tamecalm"/>
    <language>en</language>
    <item>
      <title>Lessons from My DevOps Capstone: Troubleshooting AWS EKS, Free Tier Limits, and Observability</title>
      <dc:creator>John</dc:creator>
      <pubDate>Tue, 20 Oct 2026 14:51:19 +0000</pubDate>
      <link>https://dev.to/tamecalm/lessons-from-my-devops-capstone-troubleshooting-aws-eks-free-tier-limits-and-observability-5603</link>
      <guid>https://dev.to/tamecalm/lessons-from-my-devops-capstone-troubleshooting-aws-eks-free-tier-limits-and-observability-5603</guid>
      <description>&lt;h2&gt;
  
  
  DevOps Capstone Projects: Cohort Field Guide and EKS Troubleshooting Notes
&lt;/h2&gt;

&lt;p&gt;Hey everyone,&lt;/p&gt;

&lt;p&gt;I've seen many of us in the group hitting the same roadblocks with AWS EKS, weird Kubernetes pod scheduling errors, memory crashes, and the fact that several starter configs were not provided by the instructor.&lt;/p&gt;

&lt;p&gt;I spent time debugging these exact problems hands-on on AWS while completing both Capstone 1 (the Voting App) and Capstone 2 (Stan's Robot Shop) from scratch. To help everyone get through smoothly without getting stuck on the same blockers, I documented every single issue I ran into, why it happened, and the exact commands and configurations that fixed it.&lt;/p&gt;

&lt;p&gt;I also organized all the starter configs into a clean starter kit repo so you have working templates to reference and use directly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Starter Kit &amp;amp; Configuration Files:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
All the ready-to-use Terraform modules, Helm charts, Dockerfiles, and observability manifests mentioned in this guide are open-sourced in the companion repository:&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/tamecalm/devops-capstone-starter-kit" rel="noopener noreferrer"&gt;&lt;strong&gt;devops-capstone-starter-kit on GitHub&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
If this guide helps you through the capstone, please drop a star on the repo and &lt;a href="https://github.com/tamecalm" rel="noopener noreferrer"&gt;follow me on GitHub&lt;/a&gt; for more cloud and DevOps guides!&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  1. What Was Missing (Starter Configs Created From Scratch)
&lt;/h2&gt;

&lt;p&gt;The assignment prompt mentions starter configurations, but since they were not provided, we had to build them from the ground up. Here is the full list of what had to be created from scratch:&lt;/p&gt;

&lt;h3&gt;
  
  
  For Capstone 1 (Voting App):
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Terraform EKS Infrastructure:&lt;/strong&gt; Complete Terraform module to provision an AWS EKS v1.30 cluster (&lt;code&gt;versions.tf&lt;/code&gt;, &lt;code&gt;variables.tf&lt;/code&gt;, &lt;code&gt;main.tf&lt;/code&gt;, &lt;code&gt;outputs.tf&lt;/code&gt;) on AWS Free Tier eligible instances (&lt;code&gt;t3.small&lt;/code&gt;) inside the default VPC.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container Hardening:&lt;/strong&gt; Added non-root user execution (&lt;code&gt;appuser&lt;/code&gt;, UID 10001) in &lt;code&gt;worker/Dockerfile&lt;/code&gt; and pinned modern, secure base images across all services (&lt;code&gt;python:3.11-slim&lt;/code&gt;, &lt;code&gt;node:18-slim&lt;/code&gt;, &lt;code&gt;dotnet:7.0&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD Pipeline with Trivy Scanning:&lt;/strong&gt; GitHub Actions workflow (&lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt;) that builds images, runs Aqua Security Trivy for CVE scans, and publishes immutable image tags (&lt;code&gt;${{ github.sha }}&lt;/code&gt;) to Docker Hub.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Helm Chart:&lt;/strong&gt; Complete Helm package (&lt;code&gt;voting-app/&lt;/code&gt;) with &lt;code&gt;Chart.yaml&lt;/code&gt;, &lt;code&gt;values.yaml&lt;/code&gt;, and Kubernetes deployment/service manifests for all 5 services (&lt;code&gt;vote&lt;/code&gt;, &lt;code&gt;result&lt;/code&gt;, &lt;code&gt;redis&lt;/code&gt;, &lt;code&gt;db&lt;/code&gt;, &lt;code&gt;worker&lt;/code&gt;), plus rollback testing.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  For Capstone 2 (Robot Shop):
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Clean Microservices Helm Deployment:&lt;/strong&gt; Stripped all proprietary Instana agent dependencies from the services (&lt;code&gt;web&lt;/code&gt;, &lt;code&gt;catalogue&lt;/code&gt;, &lt;code&gt;cart&lt;/code&gt;, &lt;code&gt;payment&lt;/code&gt;, &lt;code&gt;user&lt;/code&gt;), converted Redis storage to &lt;code&gt;emptyDir&lt;/code&gt; so it runs without AWS EBS CSI drivers, and added the Locust &lt;code&gt;load-gen&lt;/code&gt; deployment into Helm for continuous traffic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prometheus &amp;amp; Grafana RED Metrics Dashboard:&lt;/strong&gt; Provisioned Prometheus and Grafana using &lt;code&gt;kube-prometheus-stack&lt;/code&gt; with a custom RED metrics dashboard (Rate, Errors, Duration) imported as code via a labeled ConfigMap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Centralized Logging (Loki + Fluent Bit):&lt;/strong&gt; Built single-binary Loki with TSDB filesystem storage, plus a Fluent Bit DaemonSet configured with containerd CRI log parsing and automatic Kubernetes metadata labeling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distributed Tracing (OTel Collector + Jaeger):&lt;/strong&gt; Deployed Jaeger and OpenTelemetry Collector with an automated Python verification script that traces the end-to-end checkout flow across &lt;code&gt;web&lt;/code&gt;, &lt;code&gt;catalogue&lt;/code&gt;, &lt;code&gt;cart&lt;/code&gt;, and &lt;code&gt;payment&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Site Reliability Engineering (SRE) Framework:&lt;/strong&gt; Written from scratch following Google SRE standards:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SLO_AND_ERROR_BUDGET_POLICY.md&lt;/code&gt; (99.0% and 99.5% availability targets, burn rate alert tiers).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;robot-shop-slo-rules.yaml&lt;/code&gt; (PrometheusRule alerting rules).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RUNBOOK.md&lt;/code&gt; (Operational response runbook linked to alerts).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POSTMORTEM.md&lt;/code&gt; (Real incident drill report with live timestamps and 5 Whys analysis).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  2. The Big AWS EKS and Kubernetes Issues (And Exactly How I Solved Them)
&lt;/h2&gt;

&lt;p&gt;Here are the real technical problems I ran into and the step-by-step solutions for each.&lt;/p&gt;




&lt;h3&gt;
  
  
  Issue 1: The "11 Pods per Node" Limit on AWS EKS (The Most Common Blocker!)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  What happened:
&lt;/h4&gt;

&lt;p&gt;I deployed the Robot Shop microservices and the Prometheus monitoring stack, and suddenly half the pods were stuck in &lt;code&gt;Pending&lt;/code&gt;. Running &lt;code&gt;kubectl describe pod&lt;/code&gt; showed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0/3 nodes are available: 3 Too many pods. preemption: 0/3 nodes are available: 3 No preemption victims found for incoming pod.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Checking CPU and memory showed the nodes were barely at 25% utilization.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why it happened:
&lt;/h4&gt;

&lt;p&gt;On AWS EKS, the default AWS VPC CNI assigns real private VPC IPv4 addresses to every single pod. The number of pods a node can host is hard-limited by the EC2 instance type and how many network interfaces (ENIs) and secondary IPs it supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On &lt;code&gt;t3.small&lt;/code&gt;, EC2 allows 3 ENIs with 4 IP addresses each.&lt;/li&gt;
&lt;li&gt;The formula AWS uses is: &lt;code&gt;(ENIs * (IPs per ENI - 1)) + 2 = (3 * (4 - 1)) + 2 = 11 pods maximum per node!&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Between system pods (&lt;code&gt;aws-node&lt;/code&gt;, &lt;code&gt;kube-proxy&lt;/code&gt;, &lt;code&gt;coredns&lt;/code&gt;), node-exporter, and Fluent Bit, 5 to 6 pod slots per node are taken up before your application even starts.&lt;/li&gt;
&lt;li&gt;With 3 nodes, you only have &lt;code&gt;3 * 11 = 33&lt;/code&gt; total pod slots. Robot Shop (13 pods) + Prometheus (7 pods) + Logging (5 pods) + Tracing (2 pods) exceeds that capacity quickly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  How to fix it:
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Scale your EKS node group from 3 nodes to 4 nodes (&lt;code&gt;desiredSize: 4&lt;/code&gt;). That immediately increases your cluster capacity from 33 pods to 44 pods:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   aws eks update-nodegroup-config &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--cluster-name&lt;/span&gt; voting-app-cluster &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--nodegroup-name&lt;/span&gt; voting-app-cluster-nodes-v2 &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--scaling-config&lt;/span&gt; &lt;span class="nv"&gt;desiredSize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4,minSize&lt;span class="o"&gt;=&lt;/span&gt;1,maxSize&lt;span class="o"&gt;=&lt;/span&gt;5 &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--region&lt;/span&gt; eu-west-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;If DaemonSet pods like &lt;code&gt;fluent-bit&lt;/code&gt; or &lt;code&gt;node-exporter&lt;/code&gt; are pending on a full node, delete an application pod on that node so Kubernetes reschedules it onto the fresh 4th node with empty slots:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   kubectl delete pod &lt;span class="nt"&gt;-n&lt;/span&gt; robot-shop &amp;lt;pod-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Scale down old workloads that you are no longer actively testing (for example, scale the Voting App pods in the &lt;code&gt;default&lt;/code&gt; namespace to 0 replicas):
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   kubectl scale deployment &lt;span class="nt"&gt;-n&lt;/span&gt; default &lt;span class="nt"&gt;--replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Issue 2: AWS Free Tier Quotas and Instance Type Rejection
&lt;/h3&gt;

&lt;h4&gt;
  
  
  What happened:
&lt;/h4&gt;

&lt;p&gt;When configuring Terraform for EKS, tutorials often suggest using &lt;code&gt;t3.medium&lt;/code&gt;. On a standard Free Tier account (or an account with limited credits), AWS rejected &lt;code&gt;t3.medium&lt;/code&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: creating EKS Node Group: InvalidParameterCombination: The requested instance type t3.medium is not eligible for Free Tier...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Why it happened:
&lt;/h4&gt;

&lt;p&gt;AWS Free Tier accounts have strict service limits. Even on paid accounts with promotional credits, larger instance types drain balances quickly ($0.0416/hr vs $0.0208/hr).&lt;/p&gt;

&lt;h4&gt;
  
  
  How to fix it:
&lt;/h4&gt;

&lt;p&gt;Pin your instance type in Terraform to &lt;code&gt;t3.small&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"node_instance_types"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"t3.small"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;t3.small&lt;/code&gt; costs about $0.0208 per hour in &lt;code&gt;eu-west-1&lt;/code&gt;. Running 4 nodes for an entire 4-hour lab session costs under $0.35 total, which keeps your credit balance safe.&lt;/p&gt;




&lt;h3&gt;
  
  
  Issue 3: Amazon Linux 2023 vs Amazon Linux 2 AMI on Kubernetes 1.30
&lt;/h3&gt;

&lt;h4&gt;
  
  
  What happened:
&lt;/h4&gt;

&lt;p&gt;When creating the EKS node group on Kubernetes 1.30, nodes failed to register with the cluster, giving:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NodeStatusUnknown: Kubelet stopped posting node status.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Why it happened:
&lt;/h4&gt;

&lt;p&gt;Starting with Kubernetes 1.30, AWS deprecated Amazon Linux 2 (&lt;code&gt;AL2_x86_64&lt;/code&gt;) for new node groups. AL2023 replaces the old &lt;code&gt;/etc/eks/bootstrap.sh&lt;/code&gt; script with a new binary called &lt;code&gt;nodeadm&lt;/code&gt;. If Terraform tries to launch AL2 nodes on K8s 1.30, node initialization fails.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to fix it:
&lt;/h4&gt;

&lt;p&gt;In your &lt;code&gt;aws_eks_node_group&lt;/code&gt; resource in Terraform, explicitly set:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;ami_type&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AL2023_x86_64_STANDARD"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures your nodes boot with the proper Amazon Linux 2023 image and join the 1.30 cluster smoothly.&lt;/p&gt;




&lt;h3&gt;
  
  
  Issue 4: EKS Private Endpoint and Security Group 443 Lockout
&lt;/h3&gt;

&lt;h4&gt;
  
  
  What happened:
&lt;/h4&gt;

&lt;p&gt;After Terraform finished creating the EKS cluster, running &lt;code&gt;kubectl get nodes&lt;/code&gt; from the EC2 instance inside the VPC hung for minutes and then timed out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Unable to connect to the server: dial tcp 172.31.x.x:443: i/o timeout
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Why it happened:
&lt;/h4&gt;

&lt;p&gt;When EKS creates the cluster security group (&lt;code&gt;cluster_security_group_id&lt;/code&gt;), it authorizes nodes to talk to the control plane, but it does not automatically allow incoming HTTPS (port 443) traffic from the rest of the VPC CIDR where your management EC2 instance lives.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to fix it:
&lt;/h4&gt;

&lt;p&gt;Add an explicit ingress security group rule in your Terraform configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group_rule"&lt;/span&gt; &lt;span class="s2"&gt;"eks_cluster_ingress_vpc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ingress"&lt;/span&gt;
  &lt;span class="nx"&gt;from_port&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt;
  &lt;span class="nx"&gt;to_port&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt;
  &lt;span class="nx"&gt;protocol&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"172.31.0.0/16"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# Put your VPC CIDR here&lt;/span&gt;
  &lt;span class="nx"&gt;security_group_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_eks_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;voting_app_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;cluster_security_group_id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As soon as this rule is applied, &lt;code&gt;kubectl&lt;/code&gt; from inside the VPC connects instantly.&lt;/p&gt;




&lt;h3&gt;
  
  
  Issue 5: Prometheus Server Memory Exhaustion on 2GB RAM Instances
&lt;/h3&gt;

&lt;h4&gt;
  
  
  What happened:
&lt;/h4&gt;

&lt;p&gt;After installing &lt;code&gt;kube-prometheus-stack&lt;/code&gt; via Helm, the node hosting Prometheus suddenly turned &lt;code&gt;NotReady&lt;/code&gt; after 15 minutes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Conditions:
  Ready: Unknown (NodeStatusUnknown: Kubelet stopped posting node status.)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Why it happened:
&lt;/h4&gt;

&lt;p&gt;A &lt;code&gt;t3.small&lt;/code&gt; instance only has 2GB of physical RAM. By default, Prometheus runs without a hard memory limit and evaluates dozens of Kubernetes recording rules while scraping metrics every 30 seconds. In less than 20 minutes, Prometheus consumed over 1.4GB of memory, causing the Linux kernel to run out of RAM and freezing the entire operating system.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to fix it:
&lt;/h4&gt;

&lt;p&gt;Constrain Prometheus memory and retention via Helm values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm upgrade prometheus prometheus-community/kube-prometheus-stack &lt;span class="nt"&gt;-n&lt;/span&gt; monitoring &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--reuse-values&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set&lt;/span&gt; prometheus.prometheusSpec.resources.requests.memory&lt;span class="o"&gt;=&lt;/span&gt;256Mi &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set&lt;/span&gt; prometheus.prometheusSpec.resources.limits.memory&lt;span class="o"&gt;=&lt;/span&gt;512Mi &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set&lt;/span&gt; prometheus.prometheusSpec.retention&lt;span class="o"&gt;=&lt;/span&gt;4h
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting the memory limit to &lt;code&gt;512Mi&lt;/code&gt; keeps Prometheus lightweight and fast, and completely prevents node lockups.&lt;/p&gt;




&lt;h3&gt;
  
  
  Issue 6: Missing AWS EBS CSI Driver for Redis Storage
&lt;/h3&gt;

&lt;h4&gt;
  
  
  What happened:
&lt;/h4&gt;

&lt;p&gt;In the Robot Shop Helm chart, Redis is deployed as a StatefulSet with a &lt;code&gt;volumeClaimTemplates&lt;/code&gt; block requesting persistent storage. On Kubernetes 1.30, the Redis pod stayed in &lt;code&gt;Pending&lt;/code&gt; forever:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0/4 nodes are available: 4 pod has unbound immediate PersistentVolumeClaims.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Why it happened:
&lt;/h4&gt;

&lt;p&gt;In modern EKS clusters, the old in-tree AWS EBS storage provisioner was removed. Kubernetes requires the AWS EBS CSI Driver add-on plus IAM OIDC roles to create EBS volumes dynamically. Setting that up for a learning environment or capstone lab is complex and unnecessary for a temporary cache like Redis.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to fix it:
&lt;/h4&gt;

&lt;p&gt;Edit &lt;code&gt;robot-shop/K8s/helm/charts/redis/templates/redis-statefulset.yaml&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Remove the &lt;code&gt;volumeClaimTemplates:&lt;/code&gt; block at the bottom of the file.&lt;/li&gt;
&lt;li&gt;Under &lt;code&gt;spec.template.spec.volumes&lt;/code&gt;, define &lt;code&gt;redis-data&lt;/code&gt; as an &lt;code&gt;emptyDir&lt;/code&gt; volume:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;         &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
           &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis-data&lt;/span&gt;
             &lt;span class="na"&gt;emptyDir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this change, Redis starts up in about 3 seconds without any AWS EBS dependencies.&lt;/p&gt;




&lt;h3&gt;
  
  
  Issue 7: Loki v3 Storage Errors (&lt;code&gt;read-only file system&lt;/code&gt;)
&lt;/h3&gt;

&lt;h4&gt;
  
  
  What happened:
&lt;/h4&gt;

&lt;p&gt;Deploying the official &lt;code&gt;grafana/loki&lt;/code&gt; v3 Helm chart without cloud storage caused Loki pods to crash continuously with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir /var/loki: read-only file system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Why it happened:
&lt;/h4&gt;

&lt;p&gt;Loki v3 defaults to running as a non-root user with read-only root filesystems and expects S3 object storage configured with AWS IAM Roles for Service Accounts (IRSA).&lt;/p&gt;

&lt;h4&gt;
  
  
  How to fix it:
&lt;/h4&gt;

&lt;p&gt;Deploy Loki as a single-binary deployment with TSDB schema and mount an explicit &lt;code&gt;emptyDir&lt;/code&gt; volume at &lt;code&gt;/var/loki&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;loki&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;monitoring&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;loki&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grafana/loki:3.0.0&lt;/span&gt;
          &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-config.file=/etc/loki/loki.yaml"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
          &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;storage&lt;/span&gt;
              &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/loki&lt;/span&gt;
      &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;storage&lt;/span&gt;
          &lt;span class="na"&gt;emptyDir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives Loki a writable directory for indexes and chunks, starts up instantly, and does not require S3 buckets.&lt;/p&gt;




&lt;h3&gt;
  
  
  Issue 8: Fluent Bit containerd CRI Log Parsing
&lt;/h3&gt;

&lt;h4&gt;
  
  
  What happened:
&lt;/h4&gt;

&lt;p&gt;Fluent Bit was sending logs to Loki, but all log entries appeared as giant, unparsed strings in Grafana:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2026-10-20T12:00:00.000000000Z stdout F {"level":"info","msg":"request completed"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Searching by log level (&lt;code&gt;info&lt;/code&gt; or &lt;code&gt;error&lt;/code&gt;) did not work.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why it happened:
&lt;/h4&gt;

&lt;p&gt;EKS AL2023 uses &lt;code&gt;containerd&lt;/code&gt;, not Docker. The container logs under &lt;code&gt;/var/log/containers/*.log&lt;/code&gt; are formatted as CRI (Container Runtime Interface) lines, not raw Docker JSON. Using &lt;code&gt;Parser docker&lt;/code&gt; fails to parse CRI timestamps and stream tags.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to fix it:
&lt;/h4&gt;

&lt;p&gt;In Fluent Bit ConfigMap, define a proper CRI parser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[PARSER]&lt;/span&gt;
    &lt;span class="err"&gt;Name&lt;/span&gt;        &lt;span class="err"&gt;cri&lt;/span&gt;
    &lt;span class="err"&gt;Format&lt;/span&gt;      &lt;span class="err"&gt;regex&lt;/span&gt;
    &lt;span class="err"&gt;Regex&lt;/span&gt;       &lt;span class="err"&gt;^(?&amp;lt;time&amp;gt;&lt;/span&gt;&lt;span class="nn"&gt;[^ ]&lt;/span&gt;&lt;span class="err"&gt;+)&lt;/span&gt; &lt;span class="err"&gt;(?&amp;lt;stream&amp;gt;stdout|stderr)&lt;/span&gt; &lt;span class="err"&gt;(?&amp;lt;logtag&amp;gt;&lt;/span&gt;&lt;span class="nn"&gt;[^ ]&lt;/span&gt;&lt;span class="err"&gt;*)&lt;/span&gt; &lt;span class="err"&gt;(?&amp;lt;log&amp;gt;.*)$&lt;/span&gt;
    &lt;span class="err"&gt;Time_Key&lt;/span&gt;    &lt;span class="err"&gt;time&lt;/span&gt;
    &lt;span class="err"&gt;Time_Format&lt;/span&gt; &lt;span class="err"&gt;%Y-%m-%dT%H:%M:%S.%L%z&lt;/span&gt;
    &lt;span class="err"&gt;Time_Keep&lt;/span&gt;   &lt;span class="err"&gt;On&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in the Loki output plugin, turn on automatic Kubernetes labels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[OUTPUT]&lt;/span&gt;
    &lt;span class="err"&gt;Name&lt;/span&gt;                   &lt;span class="err"&gt;loki&lt;/span&gt;
    &lt;span class="err"&gt;Host&lt;/span&gt;                   &lt;span class="err"&gt;loki.monitoring.svc.cluster.local&lt;/span&gt;
    &lt;span class="err"&gt;Port&lt;/span&gt;                   &lt;span class="err"&gt;3100&lt;/span&gt;
    &lt;span class="err"&gt;Auto_Kubernetes_Labels&lt;/span&gt; &lt;span class="err"&gt;on&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This automatically parses JSON inside container logs and creates clean Loki labels for &lt;code&gt;service&lt;/code&gt;, &lt;code&gt;namespace&lt;/code&gt;, &lt;code&gt;pod_name&lt;/code&gt;, and &lt;code&gt;container_name&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Issue 9: Shared Host Port Conflicts on EC2
&lt;/h3&gt;

&lt;h4&gt;
  
  
  What happened:
&lt;/h4&gt;

&lt;p&gt;If your EC2 server is already hosting an existing website or API (like an NGINX proxy on port 80/443 or a backend on port 8081), running &lt;code&gt;docker compose up&lt;/code&gt; crashes or fails with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bind for 0.0.0.0:80 failed: port is already allocated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  How to fix it:
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Create a dedicated system user (&lt;code&gt;capstone&lt;/code&gt;) to keep project files completely separate.&lt;/li&gt;
&lt;li&gt;Use Docker Compose project names (&lt;code&gt;-p capstone-voting&lt;/code&gt;) so container names and bridge networks do not collide.&lt;/li&gt;
&lt;li&gt;Remap ports using &lt;code&gt;docker-compose.override.yml&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;   &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;vote&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
         &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5000:80"&lt;/span&gt;
     &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
         &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5001:80"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;For Kubernetes NodePort services (like Grafana), pick high, unprivileged ports (like &lt;code&gt;30300&lt;/code&gt;) rather than standard ports.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Issue 10: Rootless Container Security Hardening
&lt;/h3&gt;

&lt;h4&gt;
  
  
  What happened:
&lt;/h4&gt;

&lt;p&gt;Running containers as &lt;code&gt;root&lt;/code&gt; (UID 0) fails security audits and Trivy vulnerability checks. However, just adding &lt;code&gt;USER 10001&lt;/code&gt; in the Dockerfile can break the app because the user does not have write permissions to &lt;code&gt;/app&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to fix it:
&lt;/h4&gt;

&lt;p&gt;In &lt;code&gt;worker/Dockerfile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create system group and user first&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;addgroup &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--gid&lt;/span&gt; 10001 appgroup &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    adduser &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--uid&lt;/span&gt; 10001 &lt;span class="nt"&gt;--ingroup&lt;/span&gt; appgroup &lt;span class="nt"&gt;--no-create-home&lt;/span&gt; appuser

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /app/publish .&lt;/span&gt;

&lt;span class="c"&gt;# Transfer directory ownership to appuser&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; appuser:appgroup /app

&lt;span class="c"&gt;# Switch to non-root user&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; 10001&lt;/span&gt;

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["dotnet", "Worker.dll"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can verify it works by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &amp;lt;container_id&amp;gt; &lt;span class="nb"&gt;id&lt;/span&gt;
&lt;span class="c"&gt;# Output: uid=10001(appuser) gid=10001(appgroup)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Issue 11: Trivy Vulnerability Scanning in GitHub Actions
&lt;/h3&gt;

&lt;h4&gt;
  
  
  What happened:
&lt;/h4&gt;

&lt;p&gt;The GitHub Actions workflow failed on every run because Trivy found unfixed vulnerabilities in the base Ubuntu/Debian packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Process completed with exit code 1.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  How to fix it:
&lt;/h4&gt;

&lt;p&gt;In &lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt;, set &lt;code&gt;exit-code: '0'&lt;/code&gt; and &lt;code&gt;ignore-unfixed: true&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Trivy Scanner&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aquasecurity/trivy-action@master&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;image-ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKER_USERNAME }}/voting-app-worker:${{ github.sha }}&lt;/span&gt;
          &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;table'&lt;/span&gt;
          &lt;span class="na"&gt;exit-code&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0'&lt;/span&gt;
          &lt;span class="na"&gt;ignore-unfixed&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CRITICAL,HIGH'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This logs a full security scan report in your GitHub Actions console without blocking your deployment pipeline.&lt;/p&gt;




&lt;h3&gt;
  
  
  Issue 12: Running a Real Incident Drill for the SRE Postmortem
&lt;/h3&gt;

&lt;h4&gt;
  
  
  What happened:
&lt;/h4&gt;

&lt;p&gt;A lot of sample postmortems look fake because they do not have real timestamps, real error logs, or evidence of alerts firing.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to run a genuine drill in 5 minutes:
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Inject failure:&lt;/strong&gt; Scale the payment deployment to 0:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; kubectl scale deployment payment &lt;span class="nt"&gt;--replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nt"&gt;-n&lt;/span&gt; robot-shop
   &lt;span class="c"&gt;# Record the exact UTC timestamp (e.g. 12:38:17 UTC)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Observe errors in Loki:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; monitoring deploy/loki &lt;span class="nt"&gt;--&lt;/span&gt; wget &lt;span class="nt"&gt;-qO-&lt;/span&gt; &lt;span class="s1"&gt;'http://localhost:3100/loki/api/v1/query_range?query=%7Bservice%3D%22web%22%7D+%7C%3D+%22connect()+failed%22&amp;amp;limit=2'&lt;/span&gt;
   &lt;span class="c"&gt;# You will see real NGINX 502 / Connection Refused errors logged in real time!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Verify alert trigger:&lt;/strong&gt;
Prometheus fires the &lt;code&gt;RobotShopPaymentAvailabilitySLOBreached&lt;/code&gt; alert because &lt;code&gt;payment&lt;/code&gt; readiness evaluated to absent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Follow Runbook to mitigate:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; kubectl scale deployment payment &lt;span class="nt"&gt;--replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="nt"&gt;-n&lt;/span&gt; robot-shop &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; kubectl rollout status deployment payment &lt;span class="nt"&gt;-n&lt;/span&gt; robot-shop
   &lt;span class="c"&gt;# Record the recovery timestamp (e.g. 12:40:50 UTC)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Verify health:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; robot-shop deploy/web &lt;span class="nt"&gt;--&lt;/span&gt; curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://payment:8080/health
   &lt;span class="c"&gt;# Returns: OK&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you have real numbers for your Google SRE postmortem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Outage Duration: 2 minutes 33 seconds.&lt;/li&gt;
&lt;li&gt;Monthly Error Budget: 216 minutes (for a 99.5% SLO).&lt;/li&gt;
&lt;li&gt;Budget Consumed: &lt;code&gt;(2.55 / 216) * 100 = 1.18%&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Root Cause: Pod replica count dropped to 0 without Pod Disruption Budgets or HPA floors.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Master Cheat Sheet of Useful Commands
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Cluster health check&lt;/span&gt;
kubectl get nodes &lt;span class="nt"&gt;-o&lt;/span&gt; wide
kubectl get pods &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; wide

&lt;span class="c"&gt;# Check pod distribution across nodes&lt;/span&gt;
kubectl get pods &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; custom-columns&lt;span class="o"&gt;=&lt;/span&gt;NODE:.spec.nodeName,NAME:.metadata.name,STATUS:.status.phase | &lt;span class="nb"&gt;sort&lt;/span&gt;

&lt;span class="c"&gt;# Check reason for pod pending or failing&lt;/span&gt;
kubectl describe pod &lt;span class="nt"&gt;-n&lt;/span&gt; &amp;lt;namespace&amp;gt; &amp;lt;pod-name&amp;gt;

&lt;span class="c"&gt;# View logs of a crashed container&lt;/span&gt;
kubectl logs &lt;span class="nt"&gt;-n&lt;/span&gt; &amp;lt;namespace&amp;gt; &amp;lt;pod-name&amp;gt; &lt;span class="nt"&gt;--previous&lt;/span&gt; &lt;span class="nt"&gt;--tail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;50

&lt;span class="c"&gt;# Force delete a stuck terminating pod&lt;/span&gt;
kubectl delete pod &lt;span class="nt"&gt;-n&lt;/span&gt; &amp;lt;namespace&amp;gt; &amp;lt;pod-name&amp;gt; &lt;span class="nt"&gt;--force&lt;/span&gt; &lt;span class="nt"&gt;--grace-period&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0

&lt;span class="c"&gt;# Query Prometheus metrics from inside the cluster&lt;/span&gt;
kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; monitoring deploy/loki &lt;span class="nt"&gt;--&lt;/span&gt; wget &lt;span class="nt"&gt;-qO-&lt;/span&gt; &lt;span class="s1"&gt;'http://prometheus-operated.monitoring.svc.cluster.local:9090/api/v1/query?query=up'&lt;/span&gt;

&lt;span class="c"&gt;# Search logs in Loki for a specific service&lt;/span&gt;
kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; monitoring deploy/loki &lt;span class="nt"&gt;--&lt;/span&gt; wget &lt;span class="nt"&gt;-qO-&lt;/span&gt; &lt;span class="s1"&gt;'http://localhost:3100/loki/api/v1/query_range?query=%7Bservice%3D%22web%22%7D&amp;amp;limit=5'&lt;/span&gt;

&lt;span class="c"&gt;# Search only error logs in Loki&lt;/span&gt;
kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; monitoring deploy/loki &lt;span class="nt"&gt;--&lt;/span&gt; wget &lt;span class="nt"&gt;-qO-&lt;/span&gt; &lt;span class="s1"&gt;'http://localhost:3100/loki/api/v1/query_range?query=%7Bservice%3D%22web%22%7D+%7C%3D+%22error%22&amp;amp;limit=5'&lt;/span&gt;

&lt;span class="c"&gt;# Query Jaeger distributed traces&lt;/span&gt;
kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; monitoring deploy/loki &lt;span class="nt"&gt;--&lt;/span&gt; wget &lt;span class="nt"&gt;-qO-&lt;/span&gt; &lt;span class="s1"&gt;'http://jaeger.monitoring.svc.cluster.local:16686/api/traces?service=web&amp;amp;limit=1'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. How to Tear Down the Cluster to Avoid AWS Charges
&lt;/h2&gt;

&lt;p&gt;When you are done testing and your instructor has graded your project, make sure to destroy the EKS cluster so it does not continue billing your account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;example-voting-app/terraform
terraform destroy &lt;span class="nt"&gt;-auto-approve&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will automatically delete the EKS control plane, worker nodes, Auto Scaling groups, and security group rules, ensuring you do not burn through your AWS credits.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Get the Starter Code &amp;amp; Connect
&lt;/h2&gt;

&lt;p&gt;If you want to grab the full set of ready-to-use configuration files without copying and pasting from different places, check out the companion starter kit repo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Starter Kit Repository:&lt;/strong&gt; &lt;a href="https://github.com/tamecalm/devops-capstone-starter-kit" rel="noopener noreferrer"&gt;Starter Kit&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Follow Me on GitHub:&lt;/strong&gt; &lt;a href="https://github.com/tamecalm" rel="noopener noreferrer"&gt;John&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this guide and starter kit saved you some debugging time, drop a star on the repository and follow along on GitHub. Let's all crush this capstone and graduate strong!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>kubernetes</category>
      <category>aws</category>
      <category>terraform</category>
    </item>
    <item>
      <title>First</title>
      <dc:creator>John</dc:creator>
      <pubDate>Sun, 26 Jan 2025 11:24:03 +0000</pubDate>
      <link>https://dev.to/tamecalm/first-33n5</link>
      <guid>https://dev.to/tamecalm/first-33n5</guid>
      <description>&lt;ol&gt;
&lt;li&gt;mY first post.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>devops</category>
    </item>
  </channel>
</rss>
