{"id":2985,"date":"2026-07-08T10:47:42","date_gmt":"2026-07-08T10:47:42","guid":{"rendered":"https:\/\/codeinsightacademy.com\/blog\/?p=2985"},"modified":"2026-07-08T11:59:27","modified_gmt":"2026-07-08T11:59:27","slug":"kubernetes","status":"publish","type":"post","link":"https:\/\/codeinsightacademy.com\/blog\/devops\/kubernetes\/","title":{"rendered":"Kubernetes"},"content":{"rendered":"\n<h1>Kubernetes for Express Developers: A Beginner-Friendly Guide<\/h1>\n\n\n\n<p>Are you a Node.js + Express developer who understands Docker and Linux, but feels overwhelmed by Kubernetes? This guide is for you. We are going to transition from standard Docker to Kubernetes step-by-step.<\/p>\n\n\n\n<h2>The Application<\/h2>\n\n\n\n<p>We are deploying a very simple Node.js + Express application packaged into a Docker image called <code>myapp:v1<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const express = require(\"express\");\nconst app = express();\n\napp.get(\"\/hi\", (req, res) =&gt; {\n    res.send(\"Hi from Express\");\n});\n\napp.listen(3000, () =&gt; console.log(\"Server running\"));<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h2>Part 1: Without Kubernetes<\/h2>\n\n\n\n<p>Traditionally, you would run Docker containers directly on Linux servers with a Load Balancer in front. graph TD LB[Load Balancer] &#8211;&gt; S1[Server 1\\nmyapp:v1] LB &#8211;&gt; S2[Server 2\\nmyapp:v1]<\/p>\n\n\n\n<p><strong>The Problem:<\/strong><br>If Server 2 crashes, the container dies. Manual intervention is required to provision a new server, install Docker, run the container, and update the load balancer to point to the new IP.<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h2>Part 2: Introducing Kubernetes<\/h2>\n\n\n\n<p>Kubernetes is an orchestration tool that automates the deployment, scaling, and management of containers.<\/p>\n\n\n\n<ul><li><strong>Control Plane:<\/strong> The &#8220;brain&#8221; making global decisions.<\/li><li><strong>Worker Nodes:<\/strong> Linux servers running your apps.<\/li><li><strong>Pod:<\/strong> A wrapper around your container (smallest unit in K8s).<\/li><li><strong>Deployment:<\/strong> Instructions (e.g., &#8220;Keep 2 copies running&#8221;).<\/li><li><strong>Service:<\/strong> Internal load balancer for your Pods.<\/li><\/ul>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h2>Part 3 &amp; 4: Deploying &amp; Request Flow<\/h2>\n\n\n\n<p>When deployed on Kubernetes with <code>replicas: 2<\/code>, traffic flows from the browser, hits the K8s Service, and is load-balanced to the Pods across Worker Nodes. graph TD B[Browser] &#8211;&gt;|GET \/hi| S{Kubernetes Service} S &#8211;&gt; P1[Worker 1\\nPod-1] S &#8211;&gt; P2[Worker 2\\nPod-2]<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h2>Part 5: When a Node Crashes<\/h2>\n\n\n\n<p>If Worker 2 crashes, Kubernetes automatically fixes it without manual intervention.<\/p>\n\n\n\n<ol><li><strong>Desired State:<\/strong> 2 Pods<\/li><li><strong>Current State:<\/strong> 1 Pod (Node crashed)<\/li><li><strong>Reconciliation Loop:<\/strong> Control plane notices the mismatch.<\/li><li><strong>Scheduler:<\/strong> Automatically creates a new Pod on a healthy Node to reach the desired state.<\/li><\/ol>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h2>Part 6: Scaling<\/h2>\n\n\n\n<p>Scaling is just changing a number in your Deployment manifest. Set <code>replicas: 5<\/code> and the Scheduler spreads pods out: graph TD subgraph Worker 1 P1[Pod-1] P3[Pod-3] P5[Pod-5] end subgraph Worker 2 P2[Pod-2] P4[Pod-4] end<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h2>Part 7: Physical vs Logical Relationships<\/h2>\n\n\n\n<p>Understanding how the hardware maps to Kubernetes concepts: graph TD PS[Physical Server] &#8211;&gt; WN[Worker Node\\nK8s Managed] WN &#8211;&gt; P1[Pod-1\\nLogical Wrapper] P1 &#8211;&gt; C1[Container\\nmyapp:v1] WN &#8211;&gt; P2[Pod-2] P2 &#8211;&gt; C2[Container\\nmyapp:v1]<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h2>Part 8: Real-World Analogies<\/h2>\n\n\n\n<ul><li><strong>Docker Container:<\/strong> A Shipping Container (holds your cargo).<\/li><li><strong>Kubernetes Pod:<\/strong> A Truck Trailer (holds the shipping container).<\/li><li><strong>Deployment:<\/strong> A Fleet Manager (&#8220;I need 5 trucks running!&#8221;).<\/li><li><strong>Service:<\/strong> A Company Switchboard (Routes calls to available operators).<\/li><\/ul>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h2>Part 9: Docker vs Kubernetes<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Concept<\/th><th>Docker<\/th><th>Kubernetes<\/th><\/tr><\/thead><tbody><tr><td><strong>Build Image<\/strong><\/td><td><code>docker build<\/code><\/td><td>Still <code>docker build<\/code> (or similar)<\/td><\/tr><tr><td><strong>Run App<\/strong><\/td><td><code>docker run<\/code><\/td><td><strong>Deployment<\/strong> (Asks cluster to run Pods)<\/td><\/tr><tr><td><strong>Smallest Unit<\/strong><\/td><td>Container<\/td><td><strong>Pod<\/strong> (Contains the container)<\/td><\/tr><tr><td><strong>Infrastructure<\/strong><\/td><td>Docker Host<\/td><td><strong>Worker Node<\/strong> (Part of a Cluster)<\/td><\/tr><tr><td><strong>Orchestration<\/strong><\/td><td>Docker Compose<\/td><td><strong>Kubernetes<\/strong> (Multi-machine cluster)<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h2>Part 10: Complete Request Lifecycle<\/h2>\n\n\n\n<p>Tracing a request from the user to your Express code: flowchart TD A[Browser] &#8211;&gt;|URL| B{Service} B &#8211;&gt;|Load balances| C[Pod\\nNode-2, Port 3000] C &#8211;&gt;|Forwards| D[Container] D &#8211;&gt;|app.get| E[Express Route] E &#8211;&gt;|Response| A<\/p>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h2>Part 11: Autoscaling (HPA)<\/h2>\n\n\n\n<p>Kubernetes can automatically scale your application up and down based on traffic using a <strong>Horizontal Pod Autoscaler (HPA)<\/strong>.<\/p>\n\n\n\n<p>To make autoscaling work, you need three things:<\/p>\n\n\n\n<ol><li><strong>Metrics Server:<\/strong> A cluster add-on that actively monitors pod CPU and memory usage.<\/li><li><strong>Resource Requests:<\/strong> Your <code>deployment.yaml<\/code> must define baseline CPU\/Memory requests so Kubernetes knows what &#8220;100%&#8221; utilization means.<\/li><li><strong>HPA Manifest:<\/strong> A configuration telling Kubernetes the minimum and maximum pods allowed (e.g., min: 2, max: 10).<\/li><\/ol>\n\n\n\n<blockquote class=\"wp-block-quote\"><p><strong>Pro Tip:<\/strong> By keeping the HPA in a separate file (e.g., <code>k8s\/hpa\/hpa.yaml<\/code>), you make autoscaling modular. You can deploy the base app locally without it, and selectively apply the HPA only in Production!<\/p><\/blockquote>\n\n\n\n<hr class=\"wp-block-separator\"\/>\n\n\n\n<h2>Bonus: Running This Locally<\/h2>\n\n\n\n<p>If you have Docker and Minikube installed, you can try this exact setup yourself!<\/p>\n\n\n\n<p><strong>1. Start the cluster and build the image:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>minikube start\neval $(minikube docker-env)\ndocker build -t myapp:v1 .<\/code><\/pre>\n\n\n\n<p><strong>2. Deploy to Kubernetes:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl apply -f k8s\/deployment.yaml\nkubectl apply -f k8s\/service.yaml<\/code><\/pre>\n\n\n\n<p><strong>3. Test the Load Balancer (v2)<\/strong><br>If you update your app to return the pod name using <code>os.hostname()<\/code>, you can see Kubernetes load balancing in action!<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker build -t myapp:v2 .\nkubectl set image deployment\/myapp-deployment express-container=myapp:v2<\/code><\/pre>\n\n\n\n<h3>Triggering the Autoscaler (Load Testing)<\/h3>\n\n\n\n<p>Want to see the autoscaler in action? Open three terminal windows to watch the magic happen:<\/p>\n\n\n\n<ul><li><strong>Terminal 1 (Watch Pods):<\/strong> <code>kubectl get pods -w<\/code><\/li><li><strong>Terminal 2 (Watch HPA):<\/strong> <code>kubectl get hpa -w<\/code><\/li><li><strong>Terminal 3 (Generate Load):<\/strong><\/li><\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl run -i --tty load-generator --rm --image=busybox:1.28 --restart=Never -- \\\n\/bin\/sh -c \"while sleep 0.01; do wget -q -O- http:\/\/myapp-service\/hi; echo ''; done\"<\/code><\/pre>\n\n\n\n<blockquote class=\"wp-block-quote\"><p><strong>The Cooldown Period:<\/strong> Node.js is incredibly fast. To force a scaling event, you might need to temporarily lower your HPA target to <code>10%<\/code>. Once scaled up, if you stop the load generator, Kubernetes will wait for a <strong>5-minute cooldown period<\/strong> before terminating the extra pods to prevent rapid &#8220;flapping&#8221;.<\/p><\/blockquote>\n\n\n\n<h3>Stopping &amp; Deleting the Cluster<\/h3>\n\n\n\n<p>If you are familiar with Docker Compose, Minikube commands will feel very similar:<\/p>\n\n\n\n<ul><li><strong><code>minikube stop<\/code><\/strong> (Like <code>docker compose stop<\/code>): Safely shuts down the virtual machine, but saves all your data, deployments, and services. You can run <code>minikube start<\/code> tomorrow and pick up exactly where you left off.<\/li><li><strong><code>minikube delete<\/code><\/strong> (Like <code>docker compose down -v<\/code>): Completely destroys the cluster and wipes the database. You will get a 100% clean slate the next time you start it, and you will need to run your <code>kubectl apply<\/code> commands again.<\/li><\/ul>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Kubernetes for Express Developers: A Beginner-Friendly Guide Are you a Node.js + Express developer who understands Docker and Linux, but feels overwhelmed by Kubernetes? This guide is for you. We are going to transition from standard Docker to Kubernetes step-by-step. The Application We are deploying a very simple Node.js + Express application packaged into a [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[20],"tags":[],"_links":{"self":[{"href":"https:\/\/codeinsightacademy.com\/blog\/wp-json\/wp\/v2\/posts\/2985"}],"collection":[{"href":"https:\/\/codeinsightacademy.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/codeinsightacademy.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/codeinsightacademy.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/codeinsightacademy.com\/blog\/wp-json\/wp\/v2\/comments?post=2985"}],"version-history":[{"count":4,"href":"https:\/\/codeinsightacademy.com\/blog\/wp-json\/wp\/v2\/posts\/2985\/revisions"}],"predecessor-version":[{"id":2989,"href":"https:\/\/codeinsightacademy.com\/blog\/wp-json\/wp\/v2\/posts\/2985\/revisions\/2989"}],"wp:attachment":[{"href":"https:\/\/codeinsightacademy.com\/blog\/wp-json\/wp\/v2\/media?parent=2985"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codeinsightacademy.com\/blog\/wp-json\/wp\/v2\/categories?post=2985"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codeinsightacademy.com\/blog\/wp-json\/wp\/v2\/tags?post=2985"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}