<?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: Nishant Bhardwaj</title>
    <description>The latest articles on DEV Community by Nishant Bhardwaj (@nishant_bhardwaj_9135ba51).</description>
    <link>https://dev.to/nishant_bhardwaj_9135ba51</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%2F4044251%2F7c8a3361-5cdc-48e5-89e7-799b63f30ceb.jpg</url>
      <title>DEV Community: Nishant Bhardwaj</title>
      <link>https://dev.to/nishant_bhardwaj_9135ba51</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nishant_bhardwaj_9135ba51"/>
    <language>en</language>
    <item>
      <title>Observability Stack: Prometheus, Node Exporter &amp; Grafana</title>
      <dc:creator>Nishant Bhardwaj</dc:creator>
      <pubDate>Wed, 26 Aug 2026 12:46:03 +0000</pubDate>
      <link>https://dev.to/nishant_bhardwaj_9135ba51/observability-stack-prometheus-node-exporter-grafana-2j3o</link>
      <guid>https://dev.to/nishant_bhardwaj_9135ba51/observability-stack-prometheus-node-exporter-grafana-2j3o</guid>
      <description>&lt;p&gt;A solid observability setup usually comes down to three pieces working together: something that &lt;strong&gt;collects&lt;/strong&gt; metrics, something that &lt;strong&gt;exposes&lt;/strong&gt; system-level metrics, and something that &lt;strong&gt;visualizes&lt;/strong&gt; it all. Here's what each one does and how to install them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Theory: How This All Fits Together
&lt;/h2&gt;

&lt;p&gt;Before installing anything, it helps to understand the model, because it's a bit different from how logging or alerting tools usually work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pull, not push.&lt;/strong&gt; Most people's first instinct is "the app should send its metrics somewhere." Prometheus flips that around — it &lt;strong&gt;pulls&lt;/strong&gt; metrics on a timer instead. Every target (a machine, a service, an app) exposes a simple HTTP endpoint, usually &lt;code&gt;/metrics&lt;/code&gt;, that just returns plain text numbers. Prometheus visits that endpoint every N seconds (the "scrape interval") and saves whatever it finds, with a timestamp attached. Nothing gets pushed to Prometheus — Prometheus goes and asks.&lt;/p&gt;

&lt;p&gt;This means for anything to show up in Prometheus, it has to satisfy one requirement: &lt;strong&gt;something has to expose a &lt;code&gt;/metrics&lt;/code&gt; endpoint Prometheus can reach.&lt;/strong&gt; That's the whole game. Everything else in this stack exists to satisfy that one requirement or to make the data useful afterward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Node Exporter exists.&lt;/strong&gt; Your operating system doesn't naturally speak Prometheus's language — it doesn't expose CPU/memory/disk stats as a &lt;code&gt;/metrics&lt;/code&gt; endpoint by default. Node Exporter's only job is to read stats the OS already tracks (via &lt;code&gt;/proc&lt;/code&gt; and &lt;code&gt;/sys&lt;/code&gt; on Linux) and republish them in the text format Prometheus expects, on port 9100. It's a translator, not a monitoring tool by itself — it collects nothing, decides nothing, alerts on nothing. It just answers "what does this machine look like right now?" whenever asked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Prometheus itself is separate.&lt;/strong&gt; Prometheus doesn't know anything about CPUs or memory — it has no idea what it's scraping. It just knows: "go hit this list of URLs on a schedule, and remember what comes back." The intelligence is in the &lt;em&gt;config&lt;/em&gt; (which targets to scrape, how often) and in &lt;em&gt;queries&lt;/em&gt; (PromQL) you write later to make sense of the numbers. This separation is deliberate — the same Prometheus can scrape a server, a database, a Kubernetes pod, or your own app, as long as each one exposes metrics in the right format.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Grafana is a separate tool entirely.&lt;/strong&gt; Prometheus stores numbers and can run queries, but its built-in UI is bare-bones — good for debugging, bad for a dashboard someone glances at every morning. Grafana's only job is to ask Prometheus questions (via those same PromQL queries) and draw the answers as graphs. Grafana stores no metrics itself — if Prometheus goes down, Grafana has nothing to show. They're two separate concerns: Prometheus is the database, Grafana is the window into it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Putting it together&lt;/strong&gt;, the flow for a single metric is always:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OS stat → Node Exporter formats it → Prometheus scrapes it on a timer → Grafana queries Prometheus → you see a graph
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that clicks, installing the three pieces is mostly mechanical — you're just standing up each link in that chain.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Prometheus
&lt;/h2&gt;

&lt;p&gt;Prometheus is a time-series database and monitoring system. It periodically scrapes metrics from configured targets, stores them, and lets you query them with PromQL.&lt;/p&gt;

&lt;p&gt;Once it's installed and running, it's also a scrape target for itself — Prometheus exposes its own internal metrics on port 9090, which is a handy sanity check: if you can query Prometheus about its own scrape performance, you know the whole pull mechanism is working.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Official download:&lt;/strong&gt; &lt;a href="https://prometheus.io/download/" rel="noopener noreferrer"&gt;https://prometheus.io/download/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install (Linux binary)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget &amp;lt;paste-official-tarball-link-here&amp;gt;
&lt;span class="nb"&gt;tar &lt;/span&gt;xvfz prometheus-&lt;span class="k"&gt;*&lt;/span&gt;.tar.gz
&lt;span class="nb"&gt;cd &lt;/span&gt;prometheus-&lt;span class="k"&gt;*&lt;/span&gt;/
&lt;span class="nb"&gt;sudo mv &lt;/span&gt;prometheus promtool /usr/local/bin/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Run&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;prometheus &lt;span class="nt"&gt;--config&lt;/span&gt;.file&lt;span class="o"&gt;=&lt;/span&gt;prometheus.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Node Exporter
&lt;/h2&gt;

&lt;p&gt;Node Exporter runs on a machine and exposes hardware and OS-level metrics — CPU, memory, disk, network — in a format Prometheus can scrape.&lt;/p&gt;

&lt;p&gt;It doesn't talk to Prometheus, doesn't know Prometheus exists, and doesn't store any history — it just sits there and answers HTTP requests on port 9100 with whatever the machine's stats are &lt;em&gt;right now&lt;/em&gt;. You install one Node Exporter per machine you want visibility into. If you have five servers, that's five Node Exporters, each scraped independently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Official download:&lt;/strong&gt; &lt;a href="https://prometheus.io/download/" rel="noopener noreferrer"&gt;https://prometheus.io/download/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install (Linux binary)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget &amp;lt;paste-official-tarball-link-here&amp;gt;
&lt;span class="nb"&gt;tar &lt;/span&gt;xvfz node_exporter-&lt;span class="k"&gt;*&lt;/span&gt;.tar.gz
&lt;span class="nb"&gt;sudo mv &lt;/span&gt;node_exporter-&lt;span class="k"&gt;*&lt;/span&gt;/node_exporter /usr/local/bin/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Run&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node_exporter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Grafana
&lt;/h2&gt;

&lt;p&gt;Grafana connects to Prometheus (or other data sources) and turns the raw metrics into dashboards, graphs, and alerts.&lt;/p&gt;

&lt;p&gt;Grafana is completely independent of the other two — you could uninstall it entirely and Prometheus would keep scraping and storing data without noticing. Its only job is to be a nicer way to look at what's already there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Official download:&lt;/strong&gt; &lt;a href="https://grafana.com/docs/grafana/latest/setup-grafana/installation/" rel="noopener noreferrer"&gt;https://grafana.com/docs/grafana/latest/setup-grafana/installation/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install (Debian/Ubuntu)&lt;/strong&gt;&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;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; apt-transport-https software-properties-common
&lt;span class="nb"&gt;sudo &lt;/span&gt;add-apt-repository &lt;span class="s2"&gt;"deb https://packages.grafana.com/oss/deb stable main"&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;grafana
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; grafana-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Connecting Everything: exporter-config.yaml
&lt;/h2&gt;

&lt;p&gt;This is Prometheus's own config file — it tells Prometheus what to scrape. Save as &lt;code&gt;prometheus.yml&lt;/code&gt; (or &lt;code&gt;exporter-config.yaml&lt;/code&gt;) and point Prometheus at it with &lt;code&gt;--config.file&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A quick walkthrough of the fields, since the names aren't obvious the first time you see them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;scrape_interval&lt;/code&gt;&lt;/strong&gt; — how often Prometheus visits every target. 15s is a common default; lower means more granular data but more storage and load.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;job_name&lt;/code&gt;&lt;/strong&gt; — just a label you choose, so when you later query the data you can tell "this metric came from the node_exporter job" vs "this one came from the prometheus job." It has no special meaning to Prometheus beyond that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;static_configs&lt;/code&gt; / &lt;code&gt;targets&lt;/code&gt;&lt;/strong&gt; — the actual list of &lt;code&gt;host:port&lt;/code&gt; addresses to scrape. "Static" just means you're typing the addresses by hand here, as opposed to Prometheus discovering them automatically (which is how it works in more dynamic environments like Kubernetes).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add one &lt;code&gt;job_name&lt;/code&gt; block per thing you want to monitor — one machine's Node Exporter, another machine's Node Exporter, your own app once it exposes metrics, and so on.&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;global&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;scrape_interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;15s&lt;/span&gt;

&lt;span class="na"&gt;scrape_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Prometheus scraping itself&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;prometheus'&lt;/span&gt;
    &lt;span class="na"&gt;static_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;localhost:9090'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="c1"&gt;# Node Exporter — system metrics&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;node_exporter'&lt;/span&gt;
    &lt;span class="na"&gt;static_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;localhost:9100'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Grafana Setup
&lt;/h2&gt;

&lt;p&gt;Once Grafana is running, open it in a browser and add Prometheus as a data source. This step is the one thing that trips people up as a junior — you're not "installing" the connection, you're just telling Grafana one URL to send its queries to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Connections → Data sources → Add data source&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;Prometheus&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Set the URL to &lt;code&gt;http://localhost:9090&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save &amp;amp; Test&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;From there, import a dashboard (e.g. the community &lt;strong&gt;Node Exporter Full&lt;/strong&gt; dashboard) or build your own panels. A dashboard is really just a saved collection of PromQL queries with a chart type attached to each one — nothing magic. Importing one from the community just saves you from writing those queries yourself on day one; you can always open any panel later and see (and edit) the exact query behind it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Default Ports
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Default Port&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Prometheus&lt;/td&gt;
&lt;td&gt;&lt;code&gt;9090&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node Exporter&lt;/td&gt;
&lt;td&gt;&lt;code&gt;9100&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grafana&lt;/td&gt;
&lt;td&gt;&lt;code&gt;3000&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Prometheus's web UI and API live on 9090, Node Exporter exposes its &lt;code&gt;/metrics&lt;/code&gt; endpoint on 9100 (not a UI — just raw metrics text), and Grafana's login and dashboards live on 3000.&lt;/p&gt;




&lt;h2&gt;
  
  
  Observability Workflow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    OS / Machine Stats
                           │
                           ▼
                     Node Exporter
                (exposes :9100/metrics)
                           │
                           ▼
                      Prometheus
                (scrapes targets on a timer,
                    stores as time series)
                           │
                           ▼
                    PromQL Queries
                           │
                           ▼
                        Grafana
                (renders dashboards &amp;amp; graphs)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Node Exporter turns raw machine stats into something Prometheus can read, Prometheus pulls and stores that data on a schedule, and Grafana queries Prometheus to turn the numbers into dashboards. Each layer only does one job — that's what makes the pieces easy to swap or reason about individually.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Learnings
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Prometheus &lt;strong&gt;pulls&lt;/strong&gt; metrics on a schedule — nothing pushes data to it.&lt;/li&gt;
&lt;li&gt;Node Exporter doesn't monitor anything itself; it only translates OS stats into a format Prometheus can scrape.&lt;/li&gt;
&lt;li&gt;Prometheus has no idea what CPU or memory even are — all the meaning comes from config and PromQL queries.&lt;/li&gt;
&lt;li&gt;Grafana stores no data of its own; it's just a window into whatever Prometheus already has.&lt;/li&gt;
&lt;li&gt;Default ports to remember: Prometheus &lt;code&gt;9090&lt;/code&gt;, Node Exporter &lt;code&gt;9100&lt;/code&gt;, Grafana &lt;code&gt;3000&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Prometheus, Node Exporter, and Grafana each do exactly one job, and understanding that separation is most of the battle — once it's clear that Node Exporter just exposes data, Prometheus just pulls and stores it, and Grafana just visualizes it, setting up the stack becomes a mechanical checklist rather than a mystery. From here, natural next steps are adding more scrape targets, writing custom PromQL queries, and eventually setting up Alertmanager to get notified when something crosses a threshold.&lt;/p&gt;

</description>
      <category>observability</category>
      <category>devops</category>
      <category>prometheus</category>
      <category>grafana</category>
    </item>
    <item>
      <title>Kubernetes HPA: Guide with Apache on KIND</title>
      <dc:creator>Nishant Bhardwaj</dc:creator>
      <pubDate>Thu, 23 Jul 2026 17:28:28 +0000</pubDate>
      <link>https://dev.to/nishant_bhardwaj_9135ba51/kubernetes-hpa-guide-with-apache-on-kind-131o</link>
      <guid>https://dev.to/nishant_bhardwaj_9135ba51/kubernetes-hpa-guide-with-apache-on-kind-131o</guid>
      <description>&lt;p&gt;Horizontal Pod Autoscaler (HPA) automatically increases or decreases the number of Pods based on CPU or memory utilization. In this project, I configured HPA on a local KIND cluster and verified autoscaling using CPU-based metrics.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Namespace
&lt;/h2&gt;

&lt;p&gt;A Namespace logically separates Kubernetes resources, making it easier to organize and manage applications independently.&lt;/p&gt;

&lt;h3&gt;
  
  
  namespace.yml
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Namespace&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&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;apache&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Deployment
&lt;/h2&gt;

&lt;p&gt;The Deployment manages the desired number of application Pods. I configured CPU requests and limits because HPA uses these values to calculate resource utilization and make scaling decisions.&lt;/p&gt;

&lt;h3&gt;
  
  
  deployment.yml
&lt;/h3&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;apache-deployment&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;apache&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;3&lt;/span&gt;

  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apache-app&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;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apache-app&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;apache&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;httpd:latest&lt;/span&gt;

          &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Always&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="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;

          &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;100m"&lt;/span&gt;
              &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;100Mi"&lt;/span&gt;

            &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;200m"&lt;/span&gt;
              &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;250Mi"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. Service
&lt;/h2&gt;

&lt;p&gt;A Service provides a stable endpoint for accessing the application and distributes incoming traffic across all available Pods.&lt;/p&gt;

&lt;h3&gt;
  
  
  service.yml
&lt;/h3&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;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;Service&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;apache-sv&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;apache&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;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apache-app&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="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
      &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. Metrics Server
&lt;/h2&gt;

&lt;p&gt;Metrics Server collects CPU and memory usage from each Pod and exposes these metrics to Kubernetes. Without Metrics Server, HPA cannot monitor resource utilization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verify Metrics Server
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;@root-IdeaPad-Gaming-3-15IHU6:~/Code/K8s/django_hpa&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl top nodes &lt;span class="nt"&gt;-n&lt;/span&gt; apache
NAME                              CPU&lt;span class="o"&gt;(&lt;/span&gt;cores&lt;span class="o"&gt;)&lt;/span&gt;   CPU&lt;span class="o"&gt;(&lt;/span&gt;%&lt;span class="o"&gt;)&lt;/span&gt;   MEMORY&lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;   MEMORY&lt;span class="o"&gt;(&lt;/span&gt;%&lt;span class="o"&gt;)&lt;/span&gt;   
demo-kind-cluster-control-plane   126m         1%       612Mi           7%          
demo-kind-cluster-worker          29m          0%       487Mi           6%          
demo-kind-cluster-worker2         27m          0%       471Mi           6%          
demo-kind-cluster-worker3         34m          0%       520Mi           6%          
@root-IdeaPad-Gaming-3-15IHU6:~/Code/K8s/django_hpa&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl top pods &lt;span class="nt"&gt;-n&lt;/span&gt; apache
NAME                                 CPU&lt;span class="o"&gt;(&lt;/span&gt;cores&lt;span class="o"&gt;)&lt;/span&gt;   MEMORY&lt;span class="o"&gt;(&lt;/span&gt;bytes&lt;span class="o"&gt;)&lt;/span&gt;   
apache-deployment-67856f954c-b4n2x   1m           13Mi            
apache-deployment-67856f954c-xkmwb   1m           12Mi            
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If CPU and memory metrics are displayed, the Metrics Server is working correctly.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Horizontal Pod Autoscaler (HPA)
&lt;/h2&gt;

&lt;p&gt;The Horizontal Pod Autoscaler continuously monitors CPU utilization and automatically increases or decreases the number of Pod replicas based on the configured target utilization.&lt;/p&gt;

&lt;h3&gt;
  
  
  hpa.yml
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Horizontal Pod Autoscaler for Django Todo Application&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;autoscaling/v2&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;HorizontalPodAutoscaler&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;apache-hpa&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;apache&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Target the Apache deployment&lt;/span&gt;
  &lt;span class="na"&gt;scaleTargetRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apache-deployment&lt;/span&gt;  

  &lt;span class="c1"&gt;# Scaling limits&lt;/span&gt;
  &lt;span class="na"&gt;minReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;maxReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;

  &lt;span class="c1"&gt;# Metrics to scale on&lt;/span&gt;
  &lt;span class="na"&gt;metrics&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# CPU-based scaling&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Resource&lt;/span&gt;
    &lt;span class="na"&gt;resource&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;cpu&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Utilization&lt;/span&gt;
        &lt;span class="na"&gt;averageUtilization&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;50&lt;/span&gt;  

  &lt;span class="c1"&gt;# Memory-based scaling&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Resource&lt;/span&gt;
    &lt;span class="na"&gt;resource&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;memory&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Utilization&lt;/span&gt;
        &lt;span class="na"&gt;averageUtilization&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;70&lt;/span&gt;  

  &lt;span class="c1"&gt;# Scaling behavior configuration&lt;/span&gt;
  &lt;span class="na"&gt;behavior&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Scale down policies&lt;/span&gt;
    &lt;span class="na"&gt;scaleDown&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;stabilizationWindowSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30&lt;/span&gt; 
      &lt;span class="na"&gt;policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Percent&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;50&lt;/span&gt; 
        &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pods&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2&lt;/span&gt;  
        &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt;
      &lt;span class="na"&gt;selectPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Min&lt;/span&gt;  

    &lt;span class="c1"&gt;# Scale up policies&lt;/span&gt;
    &lt;span class="na"&gt;scaleUp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;stabilizationWindowSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0&lt;/span&gt;  
      &lt;span class="na"&gt;policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Percent&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;100&lt;/span&gt;  
        &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pods&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;4&lt;/span&gt;  
        &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt;
      &lt;span class="na"&gt;selectPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Max&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. Generate Load
&lt;/h2&gt;

&lt;p&gt;Generate continuous HTTP requests to simulate client traffic and trigger autoscaling.&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="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; http://localhost:8000 &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  7. Monitor Autoscaling
&lt;/h2&gt;

&lt;p&gt;Watch the HPA status in real time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get hpa &lt;span class="nt"&gt;-w&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Watch Pods being created or terminated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-w&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check CPU utilization of each Pod.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl top pods
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  HPA Workflow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 Client Requests
                        │
                        ▼
                    Kubernetes Service
                        │
                        ▼
                    Deployment
                        │
                        ▼
                Application Pods
                        │
                        ▼
                 Metrics Server
                        │
                        ▼
        Horizontal Pod Autoscaler
                        │
                        ▼
        Scale Pods Up or Scale Pods Down
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Key Learnings
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;HPA automatically scales Pods based on CPU or Memory utilization.&lt;/li&gt;
&lt;li&gt;Metrics Server is mandatory for CPU-based autoscaling.&lt;/li&gt;
&lt;li&gt;CPU requests must be defined in the Deployment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubectl top pods&lt;/code&gt; helps monitor real-time resource usage.&lt;/li&gt;
&lt;li&gt;HPA improves application scalability without manual intervention.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Horizontal Pod Autoscaler makes Kubernetes applications more resilient by automatically adjusting the number of running Pods according to workload demand. This hands-on implementation on a KIND cluster helped me understand the complete autoscaling workflow—from collecting metrics to dynamically scaling application replicas.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>devops</category>
      <category>kubernetes</category>
      <category>hpa</category>
    </item>
  </channel>
</rss>
