<?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[Amir Kolahi]]></title><description><![CDATA[Amir Kolahi]]></description><link>https://amirkolahi.ir</link><generator>RSS for Node</generator><lastBuildDate>Thu, 09 Apr 2026 19:33:29 GMT</lastBuildDate><atom:link href="https://amirkolahi.ir/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Installing Kafka Clusters with Helm Charts: A Step-by-Step Guide]]></title><description><![CDATA[Apache Kafka is the backbone of modern data streaming, and deploying it on Kubernetes ensures scalability and resilience. In this tutorial, we will set up a Kafka cluster in KRaft mode (without Zookeeper) using a custom Helm Chart.
By the end of this...]]></description><link>https://amirkolahi.ir/installing-kafka-clusters-with-helm-charts-a-step-by-step-guide</link><guid isPermaLink="true">https://amirkolahi.ir/installing-kafka-clusters-with-helm-charts-a-step-by-step-guide</guid><category><![CDATA[kafka]]></category><category><![CDATA[Helm]]></category><category><![CDATA[k8s]]></category><category><![CDATA[cluster]]></category><dc:creator><![CDATA[Amir Kolahi]]></dc:creator><pubDate>Thu, 18 Dec 2025 06:48:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766040432594/00b60c0a-caed-4dd2-8d93-a04e8f4753b4.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Apache Kafka is the backbone of modern data streaming, and deploying it on Kubernetes ensures scalability and resilience. In this tutorial, we will set up a Kafka cluster in KRaft mode (without Zookeeper) using a custom Helm Chart.</p>
<p>By the end of this guide, you will have a running Kafka cluster defined as code, ready to handle your streaming data.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>Before we dive in, make sure you have the following tools installed and configured:</p>
<ol>
<li><p>Kubernetes Cluster: A running cluster (Minikube, Kind, or a cloud provider like GKE/EKS).</p>
</li>
<li><p>kubectl: The Kubernetes command-line tool.</p>
</li>
<li><p>Helm: The package manager for Kubernetes.</p>
</li>
</ol>
<h3 id="heading-step-1-installing-helm">Step 1: Installing Helm</h3>
<p>If you haven't installed Helm yet, here is how you can do it on Linux/macOS.</p>
<p>For macOS (using Homebrew):</p>
<pre><code class="lang-bash">brew install helm
</code></pre>
<p>For Linux (using Script):</p>
<pre><code class="lang-bash">curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
</code></pre>
<p>Verify the installation:</p>
<pre><code class="lang-bash">helm version
</code></pre>
<h3 id="heading-step-2-initialize-the-helm-chart">Step 2: Initialize the Helm Chart</h3>
<p>Let's create the directory structure for our chart. Run the following command to generate a boilerplate chart:</p>
<pre><code class="lang-bash">helm create kafka-chart
</code></pre>
<p>This creates a folder named <code>kafka-chart</code>. Since we want to build our own logic, clean up the default templates:</p>
<pre><code class="lang-bash">rm -rf kafka-chart/templates/*
rm kafka-chart/values.yaml
</code></pre>
<p>Now we have a clean slate to add our configuration files.</p>
<h3 id="heading-step-3-configuration-files">Step 3: Configuration Files</h3>
<p>We need to define our Chart metadata and default values.</p>
<ol>
<li>Chart Definition (<code>Chart.yaml</code>)</li>
</ol>
<p>Open <code>kafka-chart/Chart.yaml</code> and replace its content with the following to define our application info:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v2</span>
<span class="hljs-attr">name:</span> <span class="hljs-string">kafka-chart</span>
<span class="hljs-attr">description:</span> <span class="hljs-string">A</span> <span class="hljs-string">Helm</span> <span class="hljs-string">chart</span> <span class="hljs-string">for</span> <span class="hljs-string">deploying</span> <span class="hljs-string">Kafka</span> <span class="hljs-string">with</span> <span class="hljs-string">KRaft</span> <span class="hljs-string">mode</span>
<span class="hljs-attr">type:</span> <span class="hljs-string">application</span>
<span class="hljs-attr">version:</span> <span class="hljs-number">0.1</span><span class="hljs-number">.0</span>
<span class="hljs-attr">appVersion:</span> <span class="hljs-string">"1.0"</span>
</code></pre>
<ol start="2">
<li>Default Values (<code>values.yaml</code>)</li>
</ol>
<p>Create a new <code>kafka-chart/values.yaml</code>. This file serves as the single source of truth for our configuration (replicas, image, storage, etc.).</p>
<pre><code class="lang-yaml"><span class="hljs-attr">replicaCount:</span> <span class="hljs-number">3</span>

<span class="hljs-attr">service:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">kafka-svc</span>
  <span class="hljs-attr">port:</span> <span class="hljs-number">9092</span>

<span class="hljs-attr">image:</span>
  <span class="hljs-attr">repository:</span> <span class="hljs-string">doughgle/kafka-kraft</span>
  <span class="hljs-attr">tag:</span> <span class="hljs-string">latest</span>
  <span class="hljs-attr">pullPolicy:</span> <span class="hljs-string">IfNotPresent</span>

<span class="hljs-attr">pdb:</span>
  <span class="hljs-attr">minAvailable:</span> <span class="hljs-number">2</span>

<span class="hljs-attr">storage:</span>
  <span class="hljs-attr">size:</span> <span class="hljs-string">1Gi</span>

<span class="hljs-attr">kafka:</span>
  <span class="hljs-attr">clusterId:</span> <span class="hljs-string">"oh-sxaDRTcyAr6pFRbXyzA"</span>
  <span class="hljs-attr">replicationFactor:</span> <span class="hljs-number">3</span>
  <span class="hljs-attr">minInSyncReplicas:</span> <span class="hljs-number">2</span>
  <span class="hljs-attr">shareDir:</span> <span class="hljs-string">/mnt/kafka</span>

<span class="hljs-attr">namespace:</span> <span class="hljs-string">default</span>
</code></pre>
<h3 id="heading-step-4-creating-kubernetes-templates">Step 4: Creating Kubernetes Templates</h3>
<p>Now, let's create the actual Kubernetes resources inside the <code>kafka-chart/templates/ directory</code>.</p>
<ol>
<li>Headless Service (<code>templates/services.yaml</code>)</li>
</ol>
<p>We use a Headless Service (<code>clusterIP: None</code>) because Kafka brokers need stable network identities.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Service</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> {{ <span class="hljs-string">.Values.service.name</span> }}
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">kafka-app</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">clusterIP:</span> <span class="hljs-string">None</span>
  <span class="hljs-attr">ports:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">'9092'</span>
      <span class="hljs-attr">port:</span> {{ <span class="hljs-string">.Values.service.port</span> }}
      <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span>
      <span class="hljs-attr">targetPort:</span> {{ <span class="hljs-string">.Values.service.port</span> }}
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">kafka-app</span>
</code></pre>
<ol start="2">
<li>Pod Disruption Budget (<code>templates/pdb.yaml</code>)</li>
</ol>
<p>To ensure high availability during voluntary disruptions (like node upgrades), we define a PDB.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">policy/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">PodDisruptionBudget</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">kafka-pdb</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">minAvailable:</span> {{ <span class="hljs-string">.Values.pdb.minAvailable</span> }}
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">kafka-app</span>
</code></pre>
<ol start="3">
<li>StatefulSet (<code>templates/statefulset.yaml</code>)</li>
</ol>
<p>The StatefulSet manages the deployment and scaling of the Kafka pods. It handles the storage volume claims and passes necessary environment variables for the KRaft mode.</p>
<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">StatefulSet</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">kafka</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">kafka-app</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">serviceName:</span> {{ <span class="hljs-string">.Values.service.name</span> }}
  <span class="hljs-attr">replicas:</span> {{ <span class="hljs-string">.Values.replicaCount</span> }}
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">kafka-app</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">kafka-app</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">kafka-container</span>
          <span class="hljs-attr">image:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ .Values.image.repository }}</span>:<span class="hljs-template-variable">{{ .Values.image.tag }}</span>"</span>
          <span class="hljs-attr">imagePullPolicy:</span> {{ <span class="hljs-string">.Values.image.pullPolicy</span> }}
          <span class="hljs-attr">ports:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">9092</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">9093</span>
          <span class="hljs-attr">env:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">REPLICAS</span>
              <span class="hljs-attr">value:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ .Values.replicaCount }}</span>"</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">SERVICE</span>
              <span class="hljs-attr">value:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ .Values.service.name }}</span>"</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">NAMESPACE</span>
              <span class="hljs-attr">value:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ .Values.namespace }}</span>"</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">SHARE_DIR</span>
              <span class="hljs-attr">value:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ .Values.kafka.shareDir }}</span>"</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">CLUSTER_ID</span>
              <span class="hljs-attr">value:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ .Values.kafka.clusterId }}</span>"</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">DEFAULT_REPLICATION_FACTOR</span>
              <span class="hljs-attr">value:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ .Values.kafka.replicationFactor }}</span>"</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">DEFAULT_MIN_INSYNC_REPLICAS</span>
              <span class="hljs-attr">value:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ .Values.kafka.minInSyncReplicas }}</span>"</span>
          <span class="hljs-attr">volumeMounts:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">data</span>
              <span class="hljs-attr">mountPath:</span> {{ <span class="hljs-string">.Values.kafka.shareDir</span> }}
  <span class="hljs-attr">volumeClaimTemplates:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">metadata:</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">data</span>
      <span class="hljs-attr">spec:</span>
        <span class="hljs-attr">accessModes:</span> [<span class="hljs-string">"ReadWriteOnce"</span>]
        <span class="hljs-attr">resources:</span>
          <span class="hljs-attr">requests:</span>
            <span class="hljs-attr">storage:</span> {{ <span class="hljs-string">.Values.storage.size</span> }}
</code></pre>
<h3 id="heading-step-5-deploying-the-chart">Step 5: Deploying the Chart</h3>
<p>With all files in place, we can now install our Kafka cluster.</p>
<ol>
<li><p>Dry Run (Optional):</p>
<p> It's good practice to verify what will be generated before applying it.</p>
<pre><code class="lang-bash"> helm install kafka-release ./kafka-chart --dry-run --debug
</code></pre>
</li>
<li><p>Install the Chart:</p>
<p> Run the following command to deploy:</p>
<pre><code class="lang-bash"> helm install kafka-release ./kafka-chart
</code></pre>
</li>
</ol>
<h3 id="heading-step-6-verification">Step 6: Verification</h3>
<p>Once installed, check the status of your pods:</p>
<pre><code class="lang-bash">kubectl get pods -w
</code></pre>
<p>You should see 3 pods (<code>kafka-0, kafka-1, kafka-2</code>) transitioning to the Running state.</p>
<p>To verify the service:</p>
<pre><code class="lang-bash">kubectl get svc
</code></pre>
<p>You have now successfully deployed a Kafka cluster using Helm! This setup uses the KRaft mode, removing the dependency on Zookeeper and simplifying the architecture.</p>
<p>Happy Coding! 🚀</p>
]]></content:encoded></item><item><title><![CDATA[Guide to Installing and Setting Up Krew and Starship Prompt]]></title><description><![CDATA[This playbook establishes a modern Kubernetes CLI workflow. You'll install Krew and the ctx/ns plugins for quick, hassle-free context and namespace switching, and configure the Starship prompt to display ⎈ <context> <namespace> directly in Zsh. An op...]]></description><link>https://amirkolahi.ir/guide-to-installing-and-setting-up-krew-and-starship-prompt</link><guid isPermaLink="true">https://amirkolahi.ir/guide-to-installing-and-setting-up-krew-and-starship-prompt</guid><category><![CDATA[Kubernetes]]></category><category><![CDATA[Prompt]]></category><dc:creator><![CDATA[Amir Kolahi]]></dc:creator><pubDate>Sat, 18 Oct 2025 08:47:58 GMT</pubDate><content:encoded><![CDATA[<h3 id="heading-this-playbook-establishes-a-modern-kubernetes-cli-workflow-youll-install-krew-and-the-ctxns-plugins-for-quick-hassle-free-context-and-namespace-switching-and-configure-the-starship-prompt-to-display-directly-in-zsh-an-optional-fzf-step-adds-fuzzy-selection-for-interactive-pickers-all-steps-are-aware-of-the-operating-system-and-architecture-linuxmacos-intelarm-and-are-safe-to-run-multiple-times">This playbook establishes a modern Kubernetes CLI workflow. You'll install Krew and the <code>ctx/ns</code> plugins for quick, hassle-free context and namespace switching, and configure the Starship prompt to display <code>⎈ &lt;context&gt; &lt;namespace&gt;</code> directly in Zsh. An optional fzf step adds fuzzy selection for interactive pickers. All steps are aware of the operating system and architecture (Linux/macOS, Intel/ARM) and are safe to run multiple times.</h3>
<h3 id="heading-0prerequisites"><strong>0.Prerequisites</strong></h3>
<p>Ensure <code>kubectl</code> is installed and in your <code>PATH</code>.</p>
<p>Install <code>ohmyzsh</code> in your terminal</p>
<h3 id="heading-1-install-krew-and-the-ctxns-plugins"><strong>1. Install Krew and the ctx/ns plugins:</strong></h3>
<pre><code class="lang-bash">( <span class="hljs-built_in">set</span> -euxo pipefail
  <span class="hljs-built_in">cd</span> <span class="hljs-string">"<span class="hljs-subst">$(mktemp -d)</span>"</span>
  OS=<span class="hljs-string">"<span class="hljs-subst">$(uname | tr '[:upper:]' '[:lower:]')</span>"</span>
  ARCH=<span class="hljs-string">"<span class="hljs-subst">$(uname -m | sed -e 's/x86_64/amd64/' -e 's/armv[0-9]*/arm/' -e 's/aarch64$/arm64/')</span>"</span>
  KREW=<span class="hljs-string">"krew-<span class="hljs-variable">${OS}</span>_<span class="hljs-variable">${ARCH}</span>"</span>
  curl -fsSLO <span class="hljs-string">"https://github.com/kubernetes-sigs/krew/releases/latest/download/<span class="hljs-variable">${KREW}</span>.tar.gz"</span>
  tar zxvf <span class="hljs-string">"<span class="hljs-variable">${KREW}</span>.tar.gz"</span>
  ./<span class="hljs-string">"<span class="hljs-variable">${KREW}</span>"</span> install krew
)
</code></pre>
<p>Add Krew to <code>PATH</code> (Zsh):</p>
<pre><code class="lang-bash">grep -q <span class="hljs-string">'\.krew.*/bin'</span> ~/.zshrc || <span class="hljs-built_in">echo</span> <span class="hljs-string">'export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"'</span> &gt;&gt; ~/.zshrc
<span class="hljs-built_in">source</span> ~/.zshrc
</code></pre>
<p>Install plugins:</p>
<pre><code class="lang-bash">kubectl krew install ctx ns
</code></pre>
<p>Then install fzf:</p>
<pre><code class="lang-bash">sudo apt update &amp;&amp; sudo apt install -y fzf
</code></pre>
<p>Usege:</p>
<pre><code class="lang-bash">kubectl ctx                 <span class="hljs-comment"># list &amp; interactively switch contexts</span>
kubectl ctx &lt;context&gt;       <span class="hljs-comment"># switch directly</span>
kubectl ctx -               <span class="hljs-comment"># toggle to previous context</span>
kubectl ns                  <span class="hljs-comment"># list &amp; switch namespaces</span>
kubectl ns &lt;namespace&gt;      <span class="hljs-comment"># switch directly</span>
</code></pre>
<p>(Optional short aliases in ~/.zshrc: <code>alias kctx='kubectl ctx</code> and <code>alias kns='kubectl ns</code>)</p>
<h3 id="heading-2install-and-enable-starship">2.Install and enable Starship</h3>
<p>Install Starship:</p>
<pre><code class="lang-bash">curl -sS https://starship.rs/install.sh | sh
</code></pre>
<p>Enable Starship for Zsh (must be the last line in <code>~/.zshrc</code>):</p>
<pre><code class="lang-bash">grep -q <span class="hljs-string">'starship init zsh'</span> ~/.zshrc || <span class="hljs-built_in">echo</span> <span class="hljs-string">'eval "$(starship init zsh)"'</span> &gt;&gt; ~/.zshrc
<span class="hljs-built_in">source</span> ~/.zshrc
</code></pre>
<h3 id="heading-3configure-starship-to-show-kubernetes-contextnamespace">3.Configure Starship to show Kubernetes context/namespace</h3>
<p>Create config file ~/.config/starship.toml:</p>
<pre><code class="lang-bash">mkdir -p ~/.config
cat &gt; ~/.config/starship.toml &lt;&lt;<span class="hljs-string">'TOML'</span>
format = <span class="hljs-string">"<span class="hljs-variable">$kubernetes</span><span class="hljs-variable">$directory</span><span class="hljs-variable">$git_branch</span><span class="hljs-variable">$git_status</span><span class="hljs-variable">$python</span><span class="hljs-variable">$character</span>"</span>

[kubernetes]
disabled = <span class="hljs-literal">false</span>
symbol = <span class="hljs-string">"⎈ "</span>
style = <span class="hljs-string">"bold blue"</span>
format = <span class="hljs-string">'[$symbol$context( \($namespace\))]($style) '</span>

detect_files = []
detect_extensions = []
detect_folders = []

contexts = [
  { context_pattern = <span class="hljs-string">"kubernetes-super-admin@cluster.local"</span>, context_alias = <span class="hljs-string">"DemoCluster"</span> },
  { context_pattern = <span class="hljs-string">"kind-kind"</span>, context_alias = <span class="hljs-string">"kind"</span> }
]

[directory]
truncation_length = 3

[git_branch]
format = <span class="hljs-string">" on [<span class="hljs-variable">$symbol</span><span class="hljs-variable">$branch</span>](<span class="hljs-variable">$style</span>) "</span>

[git_status]
format = <span class="hljs-string">"([<span class="hljs-variable">$all_status</span>](<span class="hljs-variable">$style</span>)) "</span>

[python]
disabled = <span class="hljs-literal">true</span>

[character]
success_symbol = <span class="hljs-string">" ➜ "</span>     
error_symbol   = <span class="hljs-string">" ✗ "</span>         
vimcmd_symbol  = <span class="hljs-string">" ❮ "</span>
</code></pre>
<p>You should see something like:</p>
<pre><code class="lang-bash">⎈ DemoCluster (default) ~  ➜
</code></pre>
]]></content:encoded></item></channel></rss>