<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Kubernetes Project]]></title><description><![CDATA[Kubernetes Project]]></description><link>https://kubernetes-project.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Thu, 17 Sep 2026 11:29:26 GMT</lastBuildDate><atom:link href="https://kubernetes-project.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Deploying a Multi-Environment Application with Argo CD🦑and Helm: A Complete GitOps🔄Guide]]></title><description><![CDATA[GitOps has evolved from a niche concept to the cornerstone of modern Kubernetes delivery. This comprehensive guide details the deployment and management of a multi-environment microservice (Dev, QA, Prod) using Argo CD and Helm, with a sample Leaderb...]]></description><link>https://kubernetes-project.hashnode.dev/deploying-a-multi-environment-application-with-argo-cdand-helm-a-complete-gitopsguide</link><guid isPermaLink="true">https://kubernetes-project.hashnode.dev/deploying-a-multi-environment-application-with-argo-cdand-helm-a-complete-gitopsguide</guid><category><![CDATA[ArgoCD]]></category><category><![CDATA[Argo Workflow]]></category><category><![CDATA[Kubernetes]]></category><category><![CDATA[GitOps and ArgoCD Complete Hands-on Project]]></category><category><![CDATA[gitops]]></category><dc:creator><![CDATA[Hardik Arora]]></dc:creator><pubDate>Fri, 21 Nov 2025 18:53:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763751138420/5914d02f-cf6d-4f7b-8d8a-54527e78d4c2.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>GitOps has evolved from a niche concept to the cornerstone of modern Kubernetes delivery. This comprehensive guide details the deployment and management of a multi-environment microservice (Dev, QA, Prod) using Argo CD and Helm, with a sample Leaderboard application as the focal point. It covers the handling of configuration drift, resolution of production issues, and the implementation of the App of Apps pattern for scalable and declarative management.</p>
<hr />
<h2 id="heading-what-this-guide-covers"><strong>📘 What This Guide Covers</strong></h2>
<ul>
<li><p>How Argo CD Applications are created and managed</p>
</li>
<li><p>Using Helm with environment-specific values</p>
</li>
<li><p>Detecting and fixing configuration drift</p>
</li>
<li><p>Understanding and applying the App of Apps pattern</p>
</li>
<li><p>Resolving real-world production issues</p>
</li>
<li><p>Following GitOps best practices end-to-end</p>
</li>
</ul>
<hr />
<h2 id="heading-repository-structure"><strong>📂 Repository Structure</strong></h2>
<h3 id="heading-github-repository">🔗 GitHub Repository</h3>
<p>You can explore the complete implementation of this setup, including Helm charts, environment values, and Argo CD Application manifests here:<br /><strong>GitHub:</strong> <a target="_blank" href="https://github.com/barbaria888/argo-cd-leaderboard-app">https://github.com/barbaria888/argo-cd-leaderboard-app</a></p>
<h3 id="heading-author-achievementhttpsgithubcombarbaria888argo-cd-leaderboard-apputmsourcechatgptcom"><a target="_blank" href="https://github.com/barbaria888/argo-cd-leaderboard-app?utm_source=chatgpt.com">🏅 Author Achievement</a></h3>
<p>This project also contributed to earning Akuity <a target="_blank" href="https://www.credential.net/caf90f89-5cdf-4de6-ae1e-8afc6ba23105#acc.057zAQwr">Understanding Gitops with ArgoCD badge</a></p>
<p><a target="_blank" href="https://www.credential.net/caf90f89-5cdf-4de6-ae1e-8afc6ba23105#acc.057zAQwr"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763750503029/5629777c-1813-4dd3-9626-8b1dd6a4f230.png" alt class="image--center mx-auto" /></a></p>
<p>A clear structure is at the heart of good GitOps. The reposit<a target="_blank" href="https://github.com/barbaria888/argo-cd-leaderboard-app?utm_source=chatgpt.com">ory is organized into Application manifests and Helm charts:</a></p>
<pre><code class="lang-bash">📦 argo-cd-leaderboard-app/
├── apps/
│   ├── leaderboard-dev.yaml
│   ├── leaderboard-qa.yaml
│   └── leaderboard-prod.yaml
│
└── charts/
    └── leaderboard/
        ├── Chart.yaml
        ├── templates/
        ├── values.yaml
        ├── values-dev.yaml
        ├── values-qa.yaml
        └── values-prod.yaml
</code></pre>
<p>Each environment has its own values file, ensuring clean, controlled differences across Dev, QA, and Production.</p>
<hr />
<h2 id="heading-part-1-deploying-the-first-application"><strong>Part 1 — Deploying the First Application</strong></h2>
<h3 id="heading-accessing-the-argo-cd-ui"><strong>Accessing the Argo CD UI</strong></h3>
<p>The user logs into Argo CD, selects <strong>NEW APP</strong>, switches to the YAML editor, and pastes the <code>leaderboard-dev</code> Application manifest.</p>
<p>This manifest declares:</p>
<ul>
<li><p>The repository location</p>
</li>
<li><p>Chart path</p>
</li>
<li><p>Values file (<code>values-dev.yaml</code>)</p>
</li>
<li><p>Destination namespace (<code>dev</code>)</p>
</li>
<li><p>Sync options such as automated namespace creation</p>
</li>
</ul>
<h3 id="heading-first-sync"><strong>First Sync</strong></h3>
<p>After clicking <strong>CREATE</strong>, the Application appears as <strong>Missing</strong> and <strong>OutOfSync</strong>. A manual <strong>SYNC</strong> triggers Kubernetes to create Deployments, ReplicaSets, Pods, Services, and Endpoints. Once the Pod becomes Ready, the entire tree turns green.</p>
<hr />
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763749536271/605e0848-3588-4835-b037-f2b6d8eb22a6.png" alt class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-part-2-detecting-and-understanding-drift"><strong>Part 2 — Detecting and Understanding Drift</strong></h2>
<p>Configuration drift begins the moment Git changes but the cluster does not. When the image tag changes in Git, Argo CD detects the mismatch and marks the Deployment as <strong>OutOfSync</strong>, represented with a yellow icon.</p>
<p>Using the <strong>APP DIFF</strong> feature, differences become crystal clear—especially when filtering with:</p>
<ul>
<li><p><strong>Compact diff</strong></p>
</li>
<li><p><strong>Inline diff</strong></p>
</li>
</ul>
<p>This narrow view highlights only what's changed, such as an updated image tag.</p>
<hr />
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763749556813/f323b13f-7392-4b20-a531-667be751c69d.png" alt class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-part-3-handling-suspended-deployments"><strong>Part 3 — Handling Suspended Deployments</strong></h2>
<p>Sometimes manual interventions happen. For example, imagine someone pauses a Deployment rollout directly in the cluster. Argo CD immediately reflects this by showing the Application as <strong>Suspended</strong>.</p>
<p>To resume:</p>
<ul>
<li><p>Open the Deployment resource</p>
</li>
<li><p>Click the three-dot menu</p>
</li>
<li><p>Select <strong>Resume</strong></p>
</li>
</ul>
<p>Argo CD continues the rollout using the updated image tag.</p>
<p>Verification is simple—check the Pod’s <strong>SUMMARY</strong> tab for the expected image:</p>
<pre><code class="lang-bash">quay.io/akuity/.../leaderboard:0.6.1
</code></pre>
<hr />
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763749576968/0cadd5f2-2286-42dc-a28f-1253090199b3.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763749596274/25266e5c-0fc8-4a00-a3dc-90e8e0af33a7.png" alt class="image--center mx-auto" /></p>
<p><code>pod_image_change_after_Resume</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763749613799/23cf6375-d790-4d2c-8ded-3b46fa870d46.png" alt class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-part-4-scaling-with-the-app-of-apps-pattern"><strong>Part 4 — Scaling with the App of Apps Pattern</strong></h2>
<p>Creating Applications manually through the UI is useful for beginners but doesn’t scale. In production, Applications must be declared in Git.</p>
<p>The <strong>App of Apps</strong> pattern solves this elegantly.</p>
<p>A parent Application points to the <code>/apps</code> directory; Argo CD then automatically creates and manages all child Applications.</p>
<h3 id="heading-key-advantages"><strong>Key Advantages</strong></h3>
<ul>
<li><p>All apps are declared in Git</p>
</li>
<li><p>Adding new applications is as simple as committing a YAML file</p>
</li>
<li><p>Autosync ensures hands-free provisioning</p>
</li>
<li><p>Team onboarding becomes frictionless</p>
</li>
</ul>
<p>Once auto-sync is enabled for the parent app, the entire environment becomes self-managing.</p>
<hr />
<h2 id="heading-part-5-production-troubleshooting-imagepullbackoff"><strong>Part 5 — Production Troubleshooting: ImagePullBackOff</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763749791842/7ce0c4bb-32b0-450d-bcf7-639c94fabf32.png" alt class="image--center mx-auto" /></p>
<p>Production failures are inevitable. One common case:<br />An image tag is incorrectly specified. For example:</p>
<p>Argo CD shows Pods stuck in <strong>ImagePullBackOff</strong>.</p>
<p>A temporary fix may require editing the live manifest:</p>
<ul>
<li><p>Open the Deployment</p>
</li>
<li><p>Switch to <strong>LIVE MANIFEST</strong></p>
</li>
<li><p>Remove the <code>v</code> prefix</p>
</li>
<li><p>Save</p>
<pre><code class="lang-bash">  v0.6.1  ❌   (invalid because of the ‘v’ prefix)
  0.6.1   ✔️   (correct)
</code></pre>
</li>
</ul>
<p>The cluster recovers immediately.<br />But Git still contains the wrong value, so Argo CD will continue to detect drift.</p>
<h3 id="heading-permanent-fix"><strong>Permanent Fix</strong></h3>
<p>Update <code>values-prod.yaml</code>:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">image:</span>
  <span class="hljs-attr">tag:</span> <span class="hljs-string">"0.6.1"</span>
</code></pre>
<hr />
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763749803373/cfb90382-bffa-47be-98e0-5ff1958e825b.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763749862237/7abadd25-6c91-4e49-9f30-a80a6a9f745c.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763749719116/57936c08-fc69-40b4-b836-a14941e85221.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>production back up and running</p>
</blockquote>
<p>Commit → push → Argo CD syncs → drift resolved.</p>
<hr />
<h2 id="heading-key-takeaways"><strong>Key Takeaways</strong></h2>
<h3 id="heading-1-git-is-the-source-of-truth">1. 🧭 Git is the Source of Truth</h3>
<p>All changes must flow through Git — not <code>kubectl</code>.</p>
<h3 id="heading-2-helm-simplifies-multi-environment-delivery">2. 🛠️ Helm Simplifies Multi-Environment Delivery</h3>
<p>Values files keep overrides clean, declarative, and predictable.</p>
<h3 id="heading-3-argo-cd-detects-drift-relentlessly">3. 🔍 Argo CD Detects Drift Relentlessly</h3>
<p>No discrepancy escapes its sync engine.</p>
<h3 id="heading-4-the-app-of-apps-pattern-scales-beautifully">4. 🗂️ The App-of-Apps Pattern Scales Beautifully</h3>
<p>Large fleets of applications become easy to manage.</p>
<h3 id="heading-5-hotfixes-are-temporary">5. ⚡ Hotfixes Are Temporary</h3>
<p>Every manual patch must be followed by the corresponding Git <a target="_blank" href="http://update.Best">update.</a></p>
<p><a target="_blank" href="http://update.Best"><strong>Best</strong></a> <strong>Practices Summary</strong></p>
<ul>
<li><p>Commit every change to Git—no exceptions</p>
</li>
<li><p>Use environment-specific Helm values</p>
</li>
<li><p>Enable auto-sync for stable environments</p>
</li>
<li><p>Follow up hotfixes with commits</p>
</li>
<li><p>Organize manifests cleanly</p>
</li>
<li><p>Keep Applications declarative</p>
</li>
</ul>
<hr />
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>This guide demonstrates a complete, real-world GitOps workflow for managing multi-environment Kubernetes applications with Argo CD and Helm. From initial deployment to drift detection, from suspended rollouts to production fire-fighting, every step reinforces one truth:</p>
<p><strong>GitOps isn’t just a methodology—it’s a discipline.</strong></p>
]]></content:encoded></item><item><title><![CDATA[Kubernetes RBAC in Action: Built a Superhero Admin Group⚔️]]></title><description><![CDATA[A concise, hands-on guide to creating and verifying a custom super-user group in Kubernetes — complete with certificate-based identity and real-world access validation.


🧩 How Default Admin Works




ComponentValue



User (CN)kubernetes-admin

Gro...]]></description><link>https://kubernetes-project.hashnode.dev/kubernetes-rbac-in-action-built-a-superhero-admin-group</link><guid isPermaLink="true">https://kubernetes-project.hashnode.dev/kubernetes-rbac-in-action-built-a-superhero-admin-group</guid><dc:creator><![CDATA[Hardik Arora]]></dc:creator><pubDate>Fri, 21 Nov 2025 18:16:10 GMT</pubDate><content:encoded><![CDATA[<hr />
<blockquote>
<p>A concise, hands-on guide to creating and verifying a <strong>custom super-user group</strong> in Kubernetes — complete with <strong>certificate-based identity</strong> and real-world access validation.</p>
</blockquote>
<hr />
<h2 id="heading-how-default-admin-works">🧩 How Default Admin Works</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Component</strong></td><td><strong>Value</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>User (CN)</strong></td><td><code>kubernetes-admin</code></td></tr>
<tr>
<td><strong>Group (O)</strong></td><td><code>system:masters</code></td></tr>
<tr>
<td><strong>Binding</strong></td><td><code>system:masters → cluster-admin</code></td></tr>
<tr>
<td><strong>Permissions</strong></td><td><code>*</code> verbs on <code>*</code> resources</td></tr>
<tr>
<td><strong>Result</strong></td><td>Full cluster access via certificate</td></tr>
</tbody>
</table>
</div><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763026741009/97b3190b-3768-48f3-b0da-bae0146bb09e.png" alt class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-create-superhero-admin-group">🦸‍♂️ Create Superhero Admin Group</h2>
<h3 id="heading-1-create-a-full-access-clusterrole-and-bind-it-to-the-group">1️⃣ Create a Full-Access ClusterRole, and bind it to the group</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763026836088/7f449903-2248-4ba8-af6e-51996a434261.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-23-test-permissions-with-can-i">2️⃣3️⃣ Test Permissions with <code>can-i</code></h3>
<pre><code class="lang-bash">kubectl auth can-i <span class="hljs-string">'*'</span> <span class="hljs-string">'*'</span> --as=batman --as-group=cluster-superheroes
<span class="hljs-comment"># → yes</span>
</code></pre>
<p>Same goes for <strong>superman</strong>, <strong>wonder-woman</strong>, or any member of <code>cluster-superheroes</code>.</p>
<hr />
<h2 id="heading-prove-it-works-real-user-with-signed-certificate">🔐 Prove It Works — Real User with Signed Certificate</h2>
<h3 id="heading-step-1-generate-key-amp-csr">Step 1: Generate Key &amp; CSR</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763061898056/757b4a1c-7aae-4fbf-b91e-a9e5a6907e6f.png" alt class="image--center mx-auto" /></p>
<pre><code class="lang-bash">openssl req -new -key batman.key -out batman.csr \
  -subj <span class="hljs-string">"/CN=batman/O=cluster-superheroes"</span> -sha256
[Image: cert-sign-request.png]
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763061909990/21ba5f82-2640-45d1-81a8-f747cc838f4b.png" alt class="image--center mx-auto" /></p>
<hr />
<h3 id="heading-step-2-submit-amp-approve-csr">Step 2: Submit &amp; Approve CSR</h3>
<p>Encode the CSR data and create a CSR manifest:</p>
<pre><code class="lang-bash">CSR_DATA=$(base64 batman.csr | tr -d <span class="hljs-string">'\n'</span>)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763061970973/941b384c-e4ae-41e9-b8d7-df56e0befcc8.png" alt class="image--center mx-auto" /></p>
<pre><code class="lang-bash">cat &lt;&lt;EOF &gt; batman-csr.yaml
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: batman
spec:
  request: <span class="hljs-variable">$CSR_DATA</span>
  signerName: kubernetes.io/kube-apiserver-client
  usages:
  - client auth
EOF
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763062008049/94bf06c5-643b-45b4-996b-58d20df770c3.png" alt class="image--center mx-auto" /></p>
<pre><code class="lang-bash">kubectl apply -f batman-csr.yaml
</code></pre>
<p>Now approve the CSR as an admin:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763062387941/262797b2-81f2-4c91-ba77-5f6d259b7322.png" alt class="image--center mx-auto" /></p>
<hr />
<h3 id="heading-step-3-extract-certificate-and-verify">Step 3: Extract Certificate and Verify</h3>
<pre><code class="lang-bash">kubectl get csr batman -o jsonpath=<span class="hljs-string">'{.status.certificate}'</span> | base64 -d &gt; batman.crt
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763062144614/b43ea57a-5e41-489c-ae76-2e1a71cea6d8.png" alt class="image--center mx-auto" /></p>
<pre><code class="lang-bash">openssl x509 -<span class="hljs-keyword">in</span> batman.crt -text -noout | grep -E <span class="hljs-string">"Subject|Issuer"</span>
<span class="hljs-comment"># → CN=batman, O=cluster-superheroes</span>
</code></pre>
<hr />
<h2 id="heading-add-batman-to-kubeconfig">🧭 Add Batman to KubeConfig</h2>
<pre><code class="lang-bash">kubectl config set-credentials batman \
  --client-certificate=batman.crt --client-key=batman.key --embed-certs=<span class="hljs-literal">true</span>

kubectl config set-context batman-context \
  --cluster=$(kubectl config get-clusters | grep -v NAME | head -1) \
  --user=batman

kubectl config use-context batman-context
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763062435535/815e49bd-2876-4468-b6bd-1183cd02f7e2.png" alt class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-real-proof-try-admin-actions">🧠 Real Proof — Try Admin Actions</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763062453208/c4bfadfe-5a11-4e5b-954b-0b658967b1f7.png" alt class="image--center mx-auto" /></p>
<p>If these all succeed — congratulations, <strong>Batman user is added!</strong></p>
<hr />
<h2 id="heading-best-practices">✅ Best Practices</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Do</strong></td><td><strong>Avoid</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Use <strong>groups</strong>, not individual users</td><td>Adding to <code>system:masters</code> in production</td></tr>
<tr>
<td>Rotate certificates <strong>yearly</strong></td><td>Manual CSRs long-term</td></tr>
<tr>
<td><strong>Audit</strong> role bindings regularly</td><td>Overly broad roles</td></tr>
</tbody>
</table>
</div><hr />
<h2 id="heading-summary">📘 Summary</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Step</strong></td><td><strong>Outcome</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Certificate → Group</td><td><code>O=cluster-superheroes</code></td></tr>
<tr>
<td>Group → Binding</td><td><code>cluster-superhero</code></td></tr>
<tr>
<td>Role → Permissions</td><td><code>*</code> on <code>*</code></td></tr>
</tbody>
</table>
</div><hr />
<hr />
]]></content:encoded></item><item><title><![CDATA[How Argo CD Actually Syncs Kubernetes Manifests — Under the Hood💫]]></title><description><![CDATA[Ever clicked “Sync” in Argo CD and wondered what’s really going on behind that magic button?Turns out, it’s not just pushing YAMLs to your cluster — it’s running a smart control loop that keeps your live state perfectly aligned with your Git state.
L...]]></description><link>https://kubernetes-project.hashnode.dev/how-argo-cd-actually-syncs-kubernetes-manifests-under-the-hood</link><guid isPermaLink="true">https://kubernetes-project.hashnode.dev/how-argo-cd-actually-syncs-kubernetes-manifests-under-the-hood</guid><category><![CDATA[ArgoCD]]></category><category><![CDATA[gitops]]></category><category><![CDATA[Kubernetes]]></category><dc:creator><![CDATA[Hardik Arora]]></dc:creator><pubDate>Tue, 11 Nov 2025 12:57:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1762865719000/64790c12-68f5-41fc-943f-ec0b68abd376.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Ever clicked <strong>“Sync”</strong> in Argo CD and wondered what’s <em>really</em> going on behind that magic button?<br />Turns out, it’s not just pushing YAMLs to your cluster — it’s running a smart control loop that keeps your <strong>live</strong> state perfectly aligned with your <strong>Git</strong> state.</p>
<p>Let’s break down how it actually works — piece by piece — from a DevOps learner’s perspective.</p>
<hr />
<h2 id="heading-1-the-argo-cd-application-your-gitops-blueprint">⚙️ 1. The Argo CD Application — Your GitOps Blueprint</h2>
<p>Everything starts with an <strong>Application</strong> — basically Argo CD’s definition of <em>what</em> to deploy and <em>where</em>.</p>
<p>It has three main parts:</p>
<ul>
<li><p><strong>Source</strong> → where the manifests come from (Git repo, Helm chart, etc.)</p>
</li>
<li><p><strong>Destination</strong> → which cluster and namespace to apply them to</p>
</li>
<li><p><strong>Sync Policy</strong> → how updates should happen (manual or automatic)</p>
</li>
</ul>
<p>Once that’s in place, Argo CD watches both your Git repo <em>and</em> your cluster — constantly checking if they’re in sync.</p>
<p>That’s GitOps 101: <strong>Git = truth, cluster = reality</strong>.</p>
<hr />
<h2 id="heading-2-manifest-generation-turning-source-into-deployable-yaml">☸ 2. Manifest Generation — Turning Source into Deployable YAML</h2>
<p>When an Application is created, Argo CD figures out how to turn your source into raw manifests:</p>
<ol>
<li><p><strong>Auto-Detection</strong> → figures out if you’re using Helm, Kustomize, or plain YAML.</p>
</li>
<li><p><strong>Rendering</strong> →</p>
<ul>
<li><p>Helm → <code>helm template</code></p>
</li>
<li><p>Kustomize → <code>kustomize build</code></p>
</li>
<li><p>YAML → just reads it as is</p>
</li>
</ul>
</li>
</ol>
<p>What you get is a clean, ordered list of Kubernetes resources ready for deployment.</p>
<hr />
<h2 id="heading-3-normalization-amp-label-magic">🧾 3. Normalization &amp; Label Magic</h2>
<p>Before applying anything, Argo CD cleans things up a bit:</p>
<ul>
<li><p>Sorts resources in the right order (CRDs first, then CRs).</p>
</li>
<li><p>Adds internal labels like:</p>
<pre><code class="lang-yaml">  <span class="hljs-attr">argocd.argoproj.io/instance:</span> <span class="hljs-string">my-app</span>
</code></pre>
</li>
</ul>
<p>These tiny annotations are how Argo CD tracks which resources belong to which app — and later detects <em>drift</em> or cleans up old ones.</p>
<hr />
<h2 id="heading-4-the-real-sync-comparing-desired-vs-live">🔍 4. The Real “Sync” — Comparing Desired vs. Live</h2>
<p>Here’s where the magic happens.<br />Argo CD compares what’s in Git with what’s actually running in the cluster.</p>
<ul>
<li><p>If something’s off, it highlights the difference.</p>
</li>
<li><p>In <strong>manual mode</strong>, you decide when to fix it.</p>
</li>
<li><p>In <strong>auto mode</strong>, Argo CD just goes ahead and reconciles.</p>
</li>
</ul>
<p>During sync, it:<br />✅ Creates missing resources<br />🩹 Updates the changed ones<br />🧹 Deletes anything removed from Git</p>
<p>It’s basically <code>kubectl apply --prune</code> — but way smarter, safer, and Git-driven.</p>
<hr />
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762865743121/03b60a6d-b49e-4277-8c91-6be800a91da3.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>image courtesy: Akuity</p>
</blockquote>
<h2 id="heading-5-health-checks-because-running-isnt-always-healthy">❤️ 5. Health Checks — Because “Running” Isn’t Always “Healthy”</h2>
<p>After syncing, Argo CD checks how your resources are doing:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Status</td><td>What It Means</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Healthy</strong></td><td>Everything’s good to go</td></tr>
<tr>
<td><strong>Progressing</strong></td><td>Still creating or updating</td></tr>
<tr>
<td><strong>Degraded</strong></td><td>Something’s broken (e.g. pod crash)</td></tr>
<tr>
<td><strong>Missing</strong></td><td>Expected resource isn’t there</td></tr>
<tr>
<td><strong>Suspended</strong></td><td>Waiting for an external event</td></tr>
</tbody>
</table>
</div><p>Your app’s overall health is just the combined status of all these — a simple but super useful view when debugging.</p>
<hr />
<h2 id="heading-6-self-healing-argo-cd-as-your-clusters-immune-system">🧠 6. Self-Healing — Argo CD as Your Cluster’s Immune System</h2>
<p>Enable <code>selfHeal</code> in your <code>syncPolicy</code> and Argo CD instantly becomes your cluster’s watchdog.</p>
<ul>
<li><p>Change something manually with <code>kubectl</code>? Argo CD will roll it back.</p>
</li>
<li><p>Delete a resource from Git? It’ll remove it from the cluster too.</p>
</li>
</ul>
<p>It’s the heart of GitOps — <strong>no manual drift, no hidden state</strong>.<br />Your cluster always reflects what’s in Git, nothing else.</p>
<hr />
<h2 id="heading-7-multi-cluster-power-one-argo-many-worlds">🌐 7. Multi-Cluster Power — One Argo, Many Worlds</h2>
<p>Using the <code>destination</code> field, you can decide exactly <em>where</em> things go:</p>
<p>You can deploy:</p>
<ul>
<li><p>To your local cluster, or</p>
</li>
<li><p>To any other connected cluster via <code>argocd cluster add</code></p>
</li>
</ul>
<p>This makes Argo CD awesome for managing <strong>dev → staging → prod</strong> pipelines or even multi-region setups.</p>
<hr />
<h2 id="heading-8-notifications-amp-feedback-loops">📣 8. Notifications &amp; Feedback Loops</h2>
<p>Every sync, health update, or failure triggers events that can notify you via:</p>
<ul>
<li><p>Slack / MS Teams</p>
</li>
<li><p>Webhooks</p>
</li>
<li><p>CI/CD pipelines like Tekton or Argo Workflows</p>
</li>
</ul>
<p>It’s not just automation — it’s <strong>observability with context</strong>.</p>
<hr />
<h2 id="heading-tldr-the-gitops-control-loop">✨ TL;DR — The GitOps Control Loop</h2>
<blockquote>
<p><strong>Argo CD is the heartbeat of GitOps.</strong><br />It continuously watches, compares, and corrects — keeping your cluster honest, one sync at a time.</p>
</blockquote>
<hr />
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>When I first hit “Sync,” I thought it was just another deploy.<br />Now I know it’s a whole <strong>control loop</strong> — a living system that aligns my cluster with my intent.</p>
<p>That’s what makes Argo CD so addictive.<br />It’s not just deploying YAMLs — it’s watching your ideas come alive, in real time, the GitOps way.  </p>
<blockquote>
<h3 id="heading-big-thanks-to-the-folks-at-akuityhttpsacademyakuityiocoursesgitops-argocd-intro-for-thhttpsakuityioeir-amazing-argo-cd-course-it-helped-me-finally-understand-what-really-happens-after-hitting-sync"><em>Big thanks to the folks at</em> <a target="_blank" href="https://academy.akuity.io/courses/gitops-argocd-intro"><em>Akuity</em></a> <a target="_blank" href="https://akuity.io/"><em>for th</em></a><em>eir amazing Argo CD course — it helped me finally understand what really happens after hitting “Sync.”</em> 🙌☁️</h3>
</blockquote>
<hr />
]]></content:encoded></item><item><title><![CDATA[🧠 Talking to Kubernetes Without kubectl — My First Raw API Experiment]]></title><description><![CDATA[Today I created and deleted a Kubernetes Pod without using kubectl — just curl and the Kubernetes API itself.And that small act completely changed how I see the cluster.

⚙️ Setting the Stage
It started with a question — what really happens when I ru...]]></description><link>https://kubernetes-project.hashnode.dev/talking-to-kubernetes-without-kubectl-my-first-raw-api-experiment</link><guid isPermaLink="true">https://kubernetes-project.hashnode.dev/talking-to-kubernetes-without-kubectl-my-first-raw-api-experiment</guid><category><![CDATA[APIs]]></category><category><![CDATA[#Kubernetes #CRDs #CustomResources #DevOps #CloudNative #K8sAutomation #InfrastructureAsCode]]></category><category><![CDATA[Kubernetes]]></category><dc:creator><![CDATA[Hardik Arora]]></dc:creator><pubDate>Fri, 07 Nov 2025 21:32:48 GMT</pubDate><content:encoded><![CDATA[<p>Today I created and deleted a Kubernetes Pod <strong>without using</strong> <code>kubectl</code> — just <code>curl</code> and the Kubernetes API itself.<br />And that small act completely changed how I see the cluster.</p>
<hr />
<h2 id="heading-setting-the-stage">⚙️ Setting the Stage</h2>
<p>It started with a question — <em>what really happens when I run</em> <code>kubectl run nginx</code>?<br />We all know <code>kubectl</code> talks to the API server, but I wanted to <strong>see it happen</strong>.</p>
<p>So, I decided to reverse-engineer the entire flow.</p>
<hr />
<h2 id="heading-reverse-engineering-kubectl">🔍 Reverse-Engineering kubectl</h2>
<p>Here’s the chain of logic that led to the <code>curl</code> command:</p>
<ol>
<li><p><strong>kubectl is a wrapper around the Kubernetes API.</strong><br /> Every command you run — <code>kubectl get pods</code>, <code>kubectl apply</code>, <code>kubectl run nginx</code> — is just an HTTP call under the hood.</p>
</li>
<li><p><strong>Reveal the real API call using verbosity (</strong><code>-v=6</code>).</p>
<pre><code class="lang-bash"> kubectl run nginx --image=nginx -v=6
</code></pre>
<p> This displays the <em>exact REST request</em> <code>kubectl</code> sends — a <code>POST</code> to</p>
<pre><code class="lang-bash"> https://&lt;apiserver&gt;/api/v1/namespaces/default/pods
</code></pre>
<p> with a JSON body describing the Pod.</p>
</li>
<li><p><strong>Recreate it manually with</strong> <code>kubectl proxy</code>.</p>
<pre><code class="lang-bash"> kubectl proxy &amp;
</code></pre>
<p> This starts a local HTTP proxy at:</p>
<pre><code class="lang-bash"> http://localhost:8001
</code></pre>
<p> It tunnels traffic to the API server using your kubeconfig credentials.</p>
</li>
<li><p><strong>Inspect and export the request using Postman.</strong><br /> Point Postman to:</p>
<pre><code class="lang-bash"> http://localhost:8001/api/v1/namespaces/default/pods
</code></pre>
<p> Set method to <strong>POST</strong>, add headers:</p>
<pre><code class="lang-bash"> Accept: application/json  
 Content-Type: application/json
</code></pre>
<p> and paste your Pod JSON body.<br /> Then click <strong>Code → cURL</strong> — and voilà, you get the real REST call <code>kubectl</code> makes.</p>
</li>
</ol>
<hr />
<h2 id="heading-creating-a-pod-without-kubectl">🚀 Creating a Pod Without kubectl</h2>
<p>Here’s the final command that came out of this reverse-engineering:</p>
<pre><code class="lang-bash">curl -X POST http://localhost:8001/api/v1/namespaces/default/pods?pretty=<span class="hljs-literal">true</span> \
  -H <span class="hljs-string">"Accept: application/json"</span> \
  -H <span class="hljs-string">"Content-Type: application/json"</span> \
  -d <span class="hljs-string">'{
    "apiVersion": "v1",
    "kind": "Pod",
    "metadata": { "name": "nginx" },
    "spec": {
      "containers": [{ "name": "nginx", "image": "nginx:latest" }]
    }
  }'</span>
</code></pre>
<p>The response:</p>
<pre><code class="lang-bash">HTTP/1.1 201 Created
</code></pre>
<p>A Pod was born — no YAML apply, no kubectl magic. Just pure REST.</p>
<hr />
<h2 id="heading-deleting-the-pod">🧹 Deleting the Pod</h2>
<pre><code class="lang-bash">curl -X DELETE http://localhost:8001/api/v1/namespaces/default/pods/nginx
</code></pre>
<p>And Kubernetes instantly acknowledged it.<br />At that moment, I stopped seeing <code>kubectl</code> as a “tool” — it’s simply an API client.</p>
<hr />
<h2 id="heading-what-happens-behind-the-scenes">🧩 What Happens Behind the Scenes</h2>
<p>Every API request to Kubernetes travels through these key steps:</p>
<ol>
<li><p><strong>Request Arrival</strong> → API server receives it (default port <code>6443</code>).</p>
</li>
<li><p><strong>Routing</strong> → <code>/api/v1/namespaces/default/pods</code> maps to the Pod handler.</p>
</li>
<li><p><strong>Authentication</strong> → Verifies <em>who</em> is making the request.</p>
</li>
<li><p><strong>Authorization (RBAC/Webhooks)</strong> → Checks <em>what</em> that identity can do.</p>
</li>
<li><p><strong>Admission Control</strong> → The heart of the control plane:</p>
<ul>
<li><p><strong>Istio</strong> injects the Envoy sidecar via its <em>mutating webhook</em>.</p>
</li>
<li><p><strong>OPA Gatekeeper</strong> enforces policies like disallowing <code>:latest</code> images.</p>
</li>
</ul>
</li>
<li><p><strong>Validation</strong> → Ensures schema correctness.</p>
</li>
<li><p><strong>Persistence</strong> → Writes the object to <strong>etcd</strong> and triggers controllers.</p>
</li>
<li><p><strong>Response</strong> → Returns JSON with a status like <code>201 Created</code> or <code>200 OK</code>.</p>
</li>
</ol>
<p>That’s the full roundtrip your tiny <code>curl</code> just made across the Kubernetes control plane.</p>
<hr />
<p>Every operator, controller, or GitOps tool — from Argo CD to Istio — is just another API client like your <code>curl</code>.<br />Once you understand that, cluster internals start making real sense.</p>
<hr />
]]></content:encoded></item><item><title><![CDATA[Deploying and Scaling a Kubernetes Guestbook Application]]></title><description><![CDATA[This project demonstrates how to build, deploy, and scale a simple Guestbook application on Kubernetes. The workflow covers containerization, autoscaling, and safe rollout practices—core competencies for cloud-native operations.

Step 1: Build and De...]]></description><link>https://kubernetes-project.hashnode.dev/deploying-and-scaling-a-kubernetes-guestbook-application</link><guid isPermaLink="true">https://kubernetes-project.hashnode.dev/deploying-and-scaling-a-kubernetes-guestbook-application</guid><category><![CDATA[Kubernetes]]></category><category><![CDATA[Continuous Learning]]></category><category><![CDATA[Devops]]></category><category><![CDATA[containers]]></category><category><![CDATA[Cloud Computing]]></category><dc:creator><![CDATA[Hardik Arora]]></dc:creator><pubDate>Tue, 19 Aug 2025 16:52:41 GMT</pubDate><content:encoded><![CDATA[<p>This project demonstrates how to build, deploy, and scale a simple Guestbook application on Kubernetes. The workflow covers containerization, autoscaling, and safe rollout practices—core competencies for cloud-native operations.</p>
<hr />
<h2 id="heading-step-1-build-and-deploy-the-guestbook-image">Step 1: Build and Deploy the Guestbook Image</h2>
<p>We begin by building and pushing the application image:</p>
<pre><code class="lang-dockerfile"><span class="hljs-comment"># install dependencies</span>
<span class="hljs-keyword">FROM</span> golang:<span class="hljs-number">1.18</span> AS builder
<span class="hljs-comment">#set working directory</span>
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>
<span class="hljs-comment"># copy script </span>
<span class="hljs-keyword">COPY</span><span class="bash"> main.go .</span>
<span class="hljs-comment"># commands to initialise project</span>
<span class="hljs-keyword">RUN</span><span class="bash"> go mod init guestbook</span>
<span class="hljs-keyword">RUN</span><span class="bash"> go mod tidy</span>
<span class="hljs-keyword">RUN</span><span class="bash"> go build -o main main.go</span>
<span class="hljs-comment"># container image</span>
<span class="hljs-keyword">FROM</span> ubuntu:<span class="hljs-number">18.04</span> 
<span class="hljs-comment">#Copy all files to image</span>
<span class="hljs-keyword">COPY</span><span class="bash"> --from=builder /app/main /app/guestbook</span>
<span class="hljs-keyword">COPY</span><span class="bash"> public/index.html /app/public/index.html</span>
<span class="hljs-keyword">COPY</span><span class="bash"> public/script.js /app/public/script.js</span>
<span class="hljs-keyword">COPY</span><span class="bash"> public/style.css /app/public/style.css</span>
<span class="hljs-keyword">COPY</span><span class="bash"> public/jquery.min.js /app/public/jquery.min.js</span>
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>
<span class="hljs-keyword">CMD</span><span class="bash"> [<span class="hljs-string">"./guestbook"</span>]</span>
<span class="hljs-comment"># expose the container port to listen to requests</span>
<span class="hljs-keyword">EXPOSE</span> <span class="hljs-number">3000</span>
</code></pre>
<blockquote>
<p>deployment.yaml stating all deployment configurations:</p>
</blockquote>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">guestbook</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">1</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">guestbook</span>
  <span class="hljs-attr">strategy:</span>
    <span class="hljs-attr">type:</span> <span class="hljs-string">RollingUpdate</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">app:</span> <span class="hljs-string">guestbook</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">containers:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">guestbook</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">us.icr.io/sn-labs-arorahardik0/guestbook:v1</span>
        <span class="hljs-attr">ports:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">3000</span>
</code></pre>
<p>Docker commands to build image and push to IBM cloud registry:</p>
<pre><code class="lang-bash">docker build -t us.icr.io/<span class="hljs-variable">$MY_NAMESPACE</span>/guestbook:v1 .
docker push us.icr.io/<span class="hljs-variable">$MY_NAMESPACE</span>/guestbook:v1
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755621749031/e963a684-add1-4c7d-9e00-d8e1ab728b84.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-best-practice-validation">Best Practice Validation</h3>
<p>Following a method recommended by Google employees, I validated that the image in the manifest existed and could be pulled:</p>
<pre><code class="lang-bash">kubectl get events -n <span class="hljs-variable">$MY_NAMESPACE</span> --sort-by=.metadata.creationTimestamp
</code></pre>
<pre><code class="lang-bash">kubectl apply -f deployment.yaml
</code></pre>
<p>This prevents invalid or broken deployments caused by incorrect image references.</p>
<hr />
<h2 id="heading-step-2-autoscaling-with-hpa">Step 2: Autoscaling with HPA</h2>
<p>Next, I enabled the Horizontal Pod Autoscaler (HPA) to automatically adjust replicas based on load:</p>
<pre><code class="lang-bash">kubectl autoscale deployment guestbook --cpu-percent=50 --min=1 --max=5
</code></pre>
<p>To simulate load and test scaling behavior:</p>
<pre><code class="lang-bash">kubectl run -i --tty load-generator --rm \
  --image=busybox:1.36.0 --restart=Never -- \
  /bin/sh -c <span class="hljs-string">"while sleep 0.01; do wget -q -O- https://arorahardik0-3000.theiaopenshiftnext-1-labs-prod-theiaopenshift-4-tor01.proxy.cognitiveclass.ai/; 
done"</span>
</code></pre>
<p>I then observed scaling in real time:</p>
<hr />
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755621538227/3053262c-e27b-481f-abae-bed09c41a6b1.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-step-3-rolling-updates-and-rollbacks">Step 3: Rolling Updates and Rollbacks</h2>
<p>I tested deployment resilience by updating the app (v2 image). After pushing the updated image, I changed the <code>deployment.yaml</code> and re-applied:</p>
<pre><code class="lang-bash">kubectl apply -f deployment.yaml
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755621869674/84b1e796-4b75-4ed4-9c86-024b367ffc20.png" alt class="image--center mx-auto" /></p>
<p>Rollout History and Rollback</p>
<p>View rollout history:</p>
<pre><code class="lang-bash">kubectl rollout <span class="hljs-built_in">history</span> deployment/guestbook
</code></pre>
<p>Then getting details for a particular version:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755621644439/ffa98f09-d958-4121-83e2-7e3b2f91ce63.png" alt class="image--center mx-auto" /></p>
<p>If required, rollback safely to a previous version:</p>
<pre><code class="lang-bash">kubectl rollout undo deployment/guestbook --to-revision=1
</code></pre>
<p>This guarantees production safety by allowing quick recovery from failed updates.</p>
<hr />
<h2 id="heading-key-takeaway">Key Takeaway</h2>
<ul>
<li><p><strong>Autoscaling</strong>: HPA ensures applications adapt to varying workloads without manual intervention.</p>
</li>
<li><p><strong>Operational Safety</strong>: Rolling updates and rollbacks safeguard against failures.</p>
</li>
<li><p><strong>Validation</strong>: Using <code>kubectl get events</code> confirms image pullability before rollout.</p>
</li>
</ul>
<hr />
<p>This project shows how even a simple app like Guestbook can highlight enterprise-grade Kubernetes workflows: build → deploy → scale → update.</p>
<hr />
]]></content:encoded></item></channel></rss>