<?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[Basanta's Blog]]></title><description><![CDATA[Basanta's Blog]]></description><link>https://basantakharel.com</link><generator>RSS for Node</generator><lastBuildDate>Mon, 14 Sep 2026 06:10:26 GMT</lastBuildDate><atom:link href="https://basantakharel.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[The META Framework: Building AI Applications That Manage, Enhance, and Augment]]></title><description><![CDATA[As we enter a historic period in technology, the race is on to define the next era of software development. The goal is no longer just to build tools for humans, but to design systems where artificial intelligence operates as a true partner.
As Andre...]]></description><link>https://basantakharel.com/the-meta-framework-building-ai-applications-that-manage-enhance-and-augment</link><guid isPermaLink="true">https://basantakharel.com/the-meta-framework-building-ai-applications-that-manage-enhance-and-augment</guid><category><![CDATA[#ai-tools]]></category><category><![CDATA[software development]]></category><dc:creator><![CDATA[Basanta Kharel]]></dc:creator><pubDate>Fri, 23 Jan 2026 23:42:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/otr1BY4mKrU/upload/d8daef8fee10acf11ba9cdc031c36761.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As we enter a historic period in technology, the race is on to define the next era of software development. The goal is no longer just to build tools for humans, but to design systems where artificial intelligence operates as a true partner.</p>
<p>As <strong>Andrej Karpathy</strong>, former Director of AI at Tesla, observes: "The hottest new programming language is English" - highlighting how natural language is becoming the primary interface for software creation.</p>
<p>To create an application that stands out in this new era, developers must focus on a new design principle: the <strong>META framework</strong> - a unified experience that is both powerful and intuitive.</p>
<p>META stands for:</p>
<p>- <strong>Manager</strong>: AI as an intelligent orchestrator of tools</p>
<p>- <strong>Enhancer</strong>: AI as a creative collaborator</p>
<p>- <strong>Transparency</strong>: Inspectable AI actions and reasoning</p>
<p>- <strong>Augmentation</strong>: Amplifying human capability, not replacing it</p>
<h2 id="heading-manager-moving-beyond-static-tools"><strong>Manager: Moving Beyond Static Tools</strong></h2>
<p>The next wave of AI applications will not require users to explicitly select every tool for every task. Instead, the application should serve as a high-level manager, capable of interpreting intent.</p>
<p><strong>Karpathy</strong> further elaborates on this shift: "We're moving from a world where you tell the computer exactly what to do, to a world where you tell it what you want and it figures out how to do it." This is the essence of AI as Manager. For coding analogy we are moving away from imperative to declarative paradigm or moving from traditional LAMP stack management to Kubernetes orchestration.</p>
<p><strong>Claude Code</strong> exemplifies this perfectly. When you ask it to "fix the authentication bug," it doesn't just write code - it autonomously searches the codebase with Grep, reads relevant files, analyzes the issue, writes the fix, runs tests, and even creates a git commit. The user never needs to specify which tools to use; Claude Code interprets intent and orchestrates the right sequence of actions.</p>
<p>Similarly, tools like <strong>Cursor</strong> and <strong>GitHub Copilot Workspace</strong> act as intelligent managers, integrating capabilities such as code search, editing, terminal access, and version control into a unified natural language interface. This removes the friction of navigating complex menus, allowing the user to focus on the outcome rather than the process.</p>
<h2 id="heading-enhancer-elevating-the-creative-process"><strong>Enhancer: Elevating the Creative Process</strong></h2>
<p>A key feature of these next-gen apps is their ability to assist with ideation. Rather than just executing commands, the AI should brainstorm, generate concepts, and refine ideas in a collaborative back-and-forth loop.</p>
<p><strong>Greg Brockman</strong>, co-founder of OpenAI, describes this as "extending human creativity" - where AI becomes a thought partner that helps explore possibilities faster than ever before. <strong>Emad Mostaque</strong>, founder of Stability AI, adds: "AI tools should amplify human creativity, not constrain it to predefined templates."</p>
<p><strong>v0 by Vercel</strong> demonstrates this enhancement beautifully. Designers can describe a UI component in natural language, and v0 generates multiple React component variations instantly. The designer iterates by refining prompts, transforming from a lone coder into a creative director with AI handling the implementation details.</p>
<p><strong>ChatGPT Canvas</strong> and <strong>Claude Artifacts</strong> similarly enable this creative partnership, providing dedicated workspaces where users and AI collaboratively draft documents, code, and designs. The AI suggests improvements, restructures content, and visualizes ideas - making the creative process faster and more exploratory.</p>
<h2 id="heading-transparency-trust-through-inspectable-actions"><strong>Transparency: Trust Through Inspectable Actions</strong></h2>
<p>For users to fully trust these advanced systems, the AI's actions must be inspectable. It is not enough for the system to work like magic; users need the ability to look "under the hood" to see how a result was generated.</p>
<p><strong>Dario Amodei</strong>, CEO of Anthropic, emphasizes this principle: "The more powerful AI systems become, the more important interpretability and transparency become. Users should be able to understand and verify what AI is doing." <strong>Yann LeCun</strong>, Chief AI Scientist at Meta, similarly advocates for "observable reasoning chains" in AI systems.</p>
<p><strong>Claude Code</strong> excels here by showing every tool it invokes - whether it's reading a file, running a bash command, or editing code. Users can see the exact grep searches performed, the specific lines of code modified, and the reasoning behind each action. This "human-in-the-loop" approach allows users to verify accuracy and provide necessary feedback.</p>
<p><strong>Devin</strong>, the AI software engineer, similarly provides a real-time activity stream showing its planning, web searches, code changes, and terminal commands. <strong>Replit Agent</strong> displays its thought process and the sequence of files it creates or modifies. This transparency ensures that the human remains in control, able to intervene when needed.</p>
<h2 id="heading-augmentation-amplifying-human-capability"><strong>Augmentation: Amplifying Human Capability</strong></h2>
<p>Ultimately, the purpose of these applications is not to replace human effort but to augment it. By automating tedious details and bridging the gap between intent and execution, AI empowers users to achieve more than they could alone.</p>
<p><strong>Doug Engelbart</strong>, the pioneer of human-computer interaction, envisioned this decades ago with his concept of "augmenting human intellect."</p>
<p><strong>Windsurf</strong> by Codeium demonstrates this by combining autonomous "Flows" with human-guided "Cascade" mode. Developers maintain creative control while the AI handles boilerplate code generation, test writing, and documentation. The result is a workflow that is not only more efficient but also deeply gratifying, as developers see their ideas come to life with unprecedented speed and fidelity.</p>
<p><strong>Notion AI</strong> similarly augments writing and knowledge work by drafting content, summarizing documents, and extracting insights - letting users focus on strategic thinking rather than mechanical tasks.</p>
<p>The <strong>META framework</strong> represents a shift from static software to dynamic partnerships, where the "machine" is no longer just a calculator, but a collaborator capable of understanding and executing complex visions. The tools mentioned above - Claude Code, Cursor, v0, ChatGPT Canvas, Devin, Windsurf, and others - are pioneering this new paradigm, setting the standard for what next-generation AI applications should deliver.</p>
<h2 id="heading-references-amp-further-reading"><strong>References &amp; Further Reading</strong></h2>
<p><strong>Thought Leaders:</strong></p>
<p>- [Andrej Karpathy](<a target="_blank" href="https://twitter.com/karpathy">https://twitter.com/karpathy</a>) - AI researcher, former Tesla AI Director</p>
<p>- [Greg Brockman](<a target="_blank" href="https://twitter.com/gdb">https://twitter.com/gdb</a>) - Co-founder and President of OpenAI</p>
<p>- [Dario Amodei](<a target="_blank" href="https://www.anthropic.com/research">https://www.anthropic.com/research</a>) - CEO of Anthropic</p>
<p>- [Yann LeCun](<a target="_blank" href="https://twitter.com/ylecun">https://twitter.com/ylecun</a>) - Chief AI Scientist at Meta</p>
<p>- [Emad Mostaque](<a target="_blank" href="https://twitter.com/EMostaque">https://twitter.com/EMostaque</a>) - Founder of Stability AI</p>
<p><strong>Tools &amp; Platforms:</strong></p>
<p>- [Claude Code](<a target="_blank" href="https://docs.anthropic.com/en/docs/claude-code">https://docs.anthropic.com/en/docs/claude-code</a>) - AI coding assistant</p>
<p>- [Cursor](<a target="_blank" href="https://cursor.sh">https://cursor.sh</a>) - AI-powered code editor</p>
<p>- [GitHub Copilot Workspace](<a target="_blank" href="https://githubnext.com/projects/copilot-workspace">https://githubnext.com/projects/copilot-workspace</a>)</p>
<p>- [v0 by Vercel](<a target="_blank" href="https://v0.dev">https://v0.dev</a>) - AI UI generation</p>
<p>- [ChatGPT Canvas](<a target="_blank" href="https://openai.com/index/introducing-canvas/">https://openai.com/index/introducing-canvas/</a>)</p>
<p>- [Claude Artifacts](<a target="_blank" href="https://support.anthropic.com/en/articles/9487310-what-are-artifacts-and-how-do-i-use-them">https://support.anthropic.com/en/articles/9487310-what-are-artifacts-and-how-do-i-use-them</a>)</p>
<p>- [Devin](<a target="_blank" href="https://www.cognition-labs.com/introducing-devin">https://www.cognition-labs.com/introducing-devin</a>) - AI software engineer</p>
<p>- [Replit Agent](<a target="_blank" href="https://replit.com/ai">https://replit.com/ai</a>) - AI coding agent</p>
<p>- [Windsurf](<a target="_blank" href="https://codeium.com/windsurf">https://codeium.com/windsurf</a>) - AI development environment</p>
<p>- [Notion AI](<a target="_blank" href="https://www.notion.so/product/ai">https://www.notion.so/product/ai</a>) - AI-powered workspace</p>
]]></content:encoded></item><item><title><![CDATA[Mastering Memory Management in AKS 1.29]]></title><description><![CDATA[Memory management plays a crucial role in ensuring optimal performance and resource efficiency in Azure Kubernetes Service (AKS). With the release of AKS 1.29, significant changes have been introduced to improve memory allocation and pod stability. I...]]></description><link>https://basantakharel.com/mastering-memory-management-in-aks-129</link><guid isPermaLink="true">https://basantakharel.com/mastering-memory-management-in-aks-129</guid><category><![CDATA[Kubernetes]]></category><category><![CDATA[aks]]></category><category><![CDATA[k8s 1.29]]></category><dc:creator><![CDATA[Basanta Kharel]]></dc:creator><pubDate>Wed, 03 Jan 2024 23:03:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/6Vg8N8u61aI/upload/1c41a9e4a644df473e8a2c7af1027f67.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Memory management plays a crucial role in ensuring optimal performance and resource efficiency in Azure Kubernetes Service (AKS). With the release of AKS 1.29, significant changes have been introduced to improve memory allocation and pod stability. In this blog, we will explore these changes and provide practical insights on how to optimize memory management in AKS 1.29. But first lets go through the basics of resource management in k8s.</p>
<p>In Kubernetes you can define CPU and memory (RAM) requirements for each container in a Pod. <strong>Resource Requests</strong> indicate the minimum amount of a resource that a container needs to run properly. The kube-scheduler uses this information to assign Pods to suitable nodes. <strong>Resource Limits</strong>, set by users, these limits prevent containers from consuming more resources than allocated. They are enforced to maintain fairness and resource efficiency within the cluster. Read more in <a target="_blank" href="https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/">kubernetes docs</a></p>
<p>If a node has surplus resources, a container might use more than its requested amount, subject to its defined limit. Consider a container with a 4GiB memory limit. If the container attempts to exceed this limit, the kubelet intervenes. This can result in process terminations within the container, often manifested as Out of Memory (OOM) errors.</p>
<p>However Kubernetes also includes eviction policies managed by the kubelet. These policies trigger Pod eviction if the node's available resources fall below specified thresholds. For instance, if the node's available memory dips below a defined threshold, the kubelet may evict Pods to reclaim resources, even though they are within their resource limit which brings us to the topic of this post.</p>
<h3 id="heading-challenges-of-memory-management-pre-aks-129">Challenges of Memory Management Pre-AKS 1.29:</h3>
<p>In previous versions of AKS, memory management posed challenges due to a higher eviction threshold and a complex reservation formula. These factors often led to inefficient resource allocation and pod instability.</p>
<ul>
<li><p><strong>Higher Eviction Thresholds</strong>: Nodes would start evicting pods when the available memory dipped below 750 MiB, a mechanism to prevent system instability due to low memory.</p>
</li>
<li><p><strong>Complex Memory Reservation</strong>: A regressive formula determined the amount of memory reserved for system processes and Kubernetes components, which impacted the allocatable memory for pods.</p>
</li>
</ul>
<h3 id="heading-exploring-the-changes-in-aks-129">Exploring the Changes in AKS 1.29:</h3>
<p>AKS 1.29 introduced significant changes in memory management, optimizing how resources are allocated and used.</p>
<ul>
<li><p><strong>Reduced Eviction Threshold</strong>: The new threshold is set at <code>memory.available&lt;100Mi</code>, allowing for more efficient resource allocation and better pod stability.</p>
</li>
<li><p><strong>Simplified Memory Reservation</strong>: The new formula for <code>kube-reserved</code> memory is the lesser of <code>20MB * Max Pods + 50MB</code> or <code>25% of the total system memory</code>. This change aims to simplify and potentially increase the allocatable memory.</p>
</li>
</ul>
<h3 id="heading-example-for-an-8gb-vm-with-30-max-pods">Example for an 8GB VM with 30 Max Pods:</h3>
<h4 id="heading-previous-aks-versions">Previous AKS versions</h4>
<ul>
<li><p><strong>Eviction Threshold</strong>: <code>memory.available&lt;750Mi</code></p>
</li>
<li><p>The regressive formula typically reserved:</p>
<ul>
<li><p>25% of the first 4GB = 1GB (1024 MiB)</p>
</li>
<li><p>20% of the next 4GB (4GB to 8GB) = 0.8GB (819.2 MiB)</p>
</li>
</ul>
</li>
<li><p><strong>Total Reserved</strong>: 1024 MiB + 819.2 MiB = 1843.2 MiB</p>
</li>
<li><p><strong>Allocatable Memory</strong>: <code>8GB - 1.8432GB - 0.75GB ≈ 5.41GB</code></p>
</li>
</ul>
<h4 id="heading-aks-129">AKS 1.29</h4>
<ul>
<li><p><strong>Eviction Threshold</strong>: <code>memory.available&lt;100Mi</code></p>
</li>
<li><p><strong>Memory Reservation</strong>: <code>20MB * 30 + 50MB = 650MB</code> (approximately 0.65GB)</p>
</li>
<li><p><strong>Allocatable Memory</strong>: <code>8GB - 0.65GB - 0.1GB = 7.25GB</code></p>
</li>
</ul>
<h3 id="heading-example-for-a-4gb-vm-with-70-max-pods">Example for a 4GB VM with 70 Max Pods:</h3>
<h4 id="heading-previous-aks-versions-1">Previous AKS versions</h4>
<ul>
<li><p><strong>Eviction Threshold</strong>: <code>memory.available&lt;750Mi</code></p>
</li>
<li><p>The regressive formula for a 4GB node typically reserved:</p>
<ul>
<li>25% of the first 4GB = 1GB (1024 MiB)</li>
</ul>
</li>
<li><p><strong>Total Reserved</strong>: 1024 MiB</p>
</li>
<li><p><strong>Allocatable Memory</strong>: <code>4GB - 1GB - 0.75GB = 2.25GB</code></p>
</li>
</ul>
<h4 id="heading-aks-129-1">AKS 1.29</h4>
<ul>
<li><p><strong>Eviction Threshold</strong>: <code>memory.available&lt;100Mi</code></p>
</li>
<li><p><strong>Memory Reservation</strong>: Capped at <code>25% * 4GB = 1000MB</code> (1GB), as it's lesser than <code>20MB * 70 + 50MB = 1450MB</code>.</p>
</li>
<li><p><strong>Allocatable Memory</strong>: <code>4GB - 1GB - 0.1GB = 2.9GB</code></p>
</li>
</ul>
<p>For a detailed understanding of resource reservations in AKS, refer to <a target="_blank" href="https://learn.microsoft.com/en-us/azure/aks/concepts-clusters-workloads#resource-reservations">Microsoft's official documentation</a>.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>The transition from the regressive formula in previous versions to the new calculation method in AKS 1.29 can result in different amounts of memory being reserved for kubelet and system processes. In AKS 1.29, the changes generally lead to a more predictable and potentially higher percentage of allocatable memory, particularly for larger VM sizes. The reduced eviction threshold in AKS 1.29 also signifies a more proactive approach to managing memory availability, enhancing node stability.</p>
<p>To effectively manage memory in AKS 1.29, stay updated with new changes, apply best practices, and continually adjust to your application needs. Efficient memory management is an ongoing journey that enhances your AKS cluster's performance.</p>
]]></content:encoded></item><item><title><![CDATA[DNS in Kubernetes: More Than Just Name Resolution]]></title><description><![CDATA[When you think of DNS, you might think of it as a simple system for translating domain names to IP addresses. But in the world of Kubernetes, DNS plays a much more pivotal role. Let's dive into the intricacies of DNS in Kubernetes and understand why ...]]></description><link>https://basantakharel.com/dns-in-kubernetes-more-than-just-name-resolution</link><guid isPermaLink="true">https://basantakharel.com/dns-in-kubernetes-more-than-just-name-resolution</guid><category><![CDATA[Kubernetes]]></category><category><![CDATA[Headless Services ]]></category><category><![CDATA[SRV Records ]]></category><category><![CDATA[DNS Service Discovery ]]></category><dc:creator><![CDATA[Basanta Kharel]]></dc:creator><pubDate>Sat, 28 Oct 2023 13:52:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/bHlZX1D4I8g/upload/df3e82ab86776cdb4e67e489d0fad277.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>When you think of DNS, you might think of it as a simple system for translating domain names to IP addresses. But in the world of Kubernetes, DNS plays a much more pivotal role. Let's dive into the intricacies of DNS in Kubernetes and understand why it's the backbone of service discovery.</em></p>
<h2 id="heading-introduction-the-significance-of-dns-in-kubernetes"><strong>Introduction: The Significance of DNS in Kubernetes</strong></h2>
<p>In Kubernetes, DNS isn't just about resolving domain names. It's the heart of service discovery. When you create a service in Kubernetes, it's automatically given a DNS name, allowing other pods to discover and communicate with it without needing to know its IP address.</p>
<h2 id="heading-the-mechanics-of-kubernetes-dns"><strong>The Mechanics of Kubernetes DNS</strong></h2>
<ul>
<li><p><strong>Service and Pod DNS Records:</strong> Every service and pod in Kubernetes is automatically assigned a DNS record. This ensures consistent communication paths. For example, a service named "my-service" in the "default" namespace would have a DNS name like <code>my-service.default.svc.cluster-domain.example</code>. <a target="_blank" href="https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/">Learn more from the official Kubernetes documentation</a>.</p>
</li>
<li><p><strong>Namespaces and DNS:</strong> The namespace of a pod can influence DNS query results. A pod in the "test" namespace querying for "data" in the "prod" namespace would use <a target="_blank" href="http://data.prod"><code>data.prod</code></a>.</p>
</li>
<li><p><strong>Headless Services:</strong> These are special services without a cluster IP. Their DNS name resolves to the IPs of all selected pods, rather than a single IP.</p>
<p>  <strong>Example</strong>: Imagine you have a headless service named "database-service" in the "backend" namespace, and it selects three pods with IPs <code>10.0.1.1</code>, <code>10.0.1.2</code>, and <code>10.0.1.3</code>. When another pod tries to resolve the DNS name <code>database-service.backend.svc.cluster-domain.example</code>, it will get back a list of all three IPs (<code>10.0.1.1</code>, <code>10.0.1.2</code>, <code>10.0.1.3</code>) instead of a single service IP. This is particularly useful for applications that need to discover their peers like in distributed databases. <a target="_blank" href="https://kubernetes.io/docs/concepts/services-networking/service/#headless-services">Read more about headless services</a>.</p>
</li>
</ul>
<ul>
<li><p><strong>SRV Records:</strong> SRV records are a type of DNS record used in Kubernetes and other systems to describe services offered by a domain. In Kubernetes, for services with named ports, SRV records are created to aid in service discovery across varying ports.</p>
<ul>
<li><p><strong>Structure</strong>: An SRV record typically has the format: <code>_service._protocol.name</code>. The <code>_service</code> and <code>_protocol</code> are prefixed with underscores, and the <code>name</code> is the domain name where the service is offered.</p>
</li>
<li><p><strong>Example</strong>: If you have a service named "web-app" in the "default" namespace with a named port "http" at port number 8080, Kubernetes would create an SRV record like <code>_http._tcp.web-app.default.svc.cluster-domain.example</code>. This SRV record points to the port 8080 of the pods selected by the "web-app" service.</p>
</li>
<li><p><strong>Usage</strong>: SRV records are particularly useful when you need to discover not just the IP address of a service but also the port number on which the service is running. This is common in systems where services might be running on non-standard ports.</p>
</li>
<li><p><strong>Benefits in Kubernetes</strong>: In dynamic environments like Kubernetes, where pods can come and go and might be exposed on different ports, SRV records provide a way to dynamically discover services without hardcoding IP addresses and port numbers.</p>
</li>
</ul>
</li>
<li><p><strong>Pod's DNS Policy:</strong> This determines how DNS queries from the pod are handled. The "ClusterFirst" policy, for instance, ensures non-cluster queries are forwarded to an upstream nameserver.</p>
</li>
<li><p><strong>Custom DNS Configurations:</strong> The <code>dnsConfig</code> field in a pod spec allows users to customize DNS settings, offering flexibility in service discovery.</p>
</li>
<li><p><strong>Windows Nodes and DNS:</strong> DNS resolution on Windows nodes has its quirks. For instance, all names with a dot (<code>.</code>) are treated as FQDNs. The <code>Resolve-DNSName</code> PowerShell cmdlet is recommended for DNS resolutions on Windows.</p>
</li>
</ul>
<h2 id="heading-advanced-dns-configurations-in-kubernetes"><strong>Advanced DNS Configurations in Kubernetes</strong></h2>
<ul>
<li><p><strong>Pod's DNS Config:</strong> Provides granular control over DNS settings for a Pod. It allows specifying custom nameservers, search domains, and other DNS settings.</p>
</li>
<li><p><strong>DNS Resolution on Windows Nodes:</strong> Windows treats all names with a dot (<code>.</code>) as FQDN and skips FQDN resolution. It's recommended to use the <code>Resolve-DNSName</code> PowerShell cmdlet for DNS resolutions on Windows.</p>
</li>
<li><p><strong>DNS Search Domain List Limits:</strong> Kubernetes allows up to 32 search domains. The total length of all search domains should not exceed 2048 characters.</p>
</li>
<li><p><strong>Pod's setHostnameAsFQDN Field:</strong> When set to true, the kubelet writes the Pod's FQDN into the hostname for that Pod's namespace. This means both <code>hostname</code> and <code>hostname --fqdn</code> commands return the Pod's FQDN.</p>
<p>  <strong>Example</strong>: Suppose you have a Pod with the name "web-app" in the "production" namespace, and its FQDN is <code>web-app.production.svc.cluster-domain.example</code>. If <code>setHostnameAsFQDN</code> is set to true for this Pod, running the <code>hostname</code> command inside the Pod would return <code>web-app.production.svc.cluster-domain.example</code> instead of just <code>web-app</code>. Similarly, the <code>hostname --fqdn</code> command would also return <code>web-app.production.svc.cluster-domain.example</code>.</p>
</li>
</ul>
<h2 id="heading-why-does-this-matter"><strong>Why Does This Matter?</strong></h2>
<p>Understanding DNS in Kubernetes is crucial because it simplifies service discovery. Without DNS, pods would need to track the IP addresses of other services, a challenge given the ephemeral nature of pods. With DNS, services are easily discoverable, regardless of their current IP address.</p>
<h2 id="heading-key-takeaways"><strong>Key Takeaways</strong></h2>
<ul>
<li><p>DNS in Kubernetes is central to service discovery.</p>
</li>
<li><p>Kubernetes offers flexible DNS configurations to suit various needs.</p>
</li>
<li><p>As applications grow and span multiple clusters, mastering Kubernetes DNS becomes essential.</p>
</li>
</ul>
<h2 id="heading-further-reading"><strong>Further Reading</strong></h2>
<p>For those keen on diving deeper, the <a target="_blank" href="https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/">official Kubernetes documentation</a> provides comprehensive insights into DNS configurations and behaviors.</p>
<hr />
<p><em>By understanding the nuances of DNS in Kubernetes, developers and administrators can build more resilient, scalable, and efficient applications. It's not just about name resolution; it's about seamless communication in a dynamic environment.</em></p>
]]></content:encoded></item><item><title><![CDATA[Setting up Kafka Connect with Debezium Connectors: Importance of Database History Parameters]]></title><description><![CDATA[If you're working with databases, you've probably heard of Kafka, Kafka Connect, and connectors. In a nutshell, Kafka is a distributed streaming platform that allows you to publish and subscribe to streams of records, while Kafka Connect is a framewo...]]></description><link>https://basantakharel.com/setting-up-kafka-connect-with-debezium-connectors-importance-of-database-history-parameters</link><guid isPermaLink="true">https://basantakharel.com/setting-up-kafka-connect-with-debezium-connectors-importance-of-database-history-parameters</guid><category><![CDATA[kafka connect]]></category><category><![CDATA[debezium]]></category><category><![CDATA[Confluent Kafka]]></category><dc:creator><![CDATA[Basanta Kharel]]></dc:creator><pubDate>Tue, 09 May 2023 16:48:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/GCFuprAvC6A/upload/4186074f734e04166ada67cde9c0770f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you're working with databases, you've probably heard of Kafka, Kafka Connect, and connectors. In a nutshell, Kafka is a distributed streaming platform that allows you to publish and subscribe to streams of records, while Kafka Connect is a framework that simplifies the process of getting data into and out of Kafka. Connectors are plugins that let you connect to various data sources and sync them with Kafka.</p>
<p>One popular set of connectors for Kafka are the Debezium connectors. These connectors are specifically designed for change data capture (CDC) scenarios, which involve capturing changes in a database and publishing them to Kafka topics. The Debezium connectors are available for various databases, including MySQL, PostgreSQL, Oracle, and SQL Server.</p>
<p>However, when setting up the Debezium connectors, there's an important consideration to keep in mind: the configuration of the Kafka topic used for storing the history of database changes. This topic is set using the <mark>database.history.kafka.topic</mark> parameter, and it's critical that this topic has infinite retention and a single partition. <a target="_blank" href="https://debezium.io/documentation/reference/1.9/install.html#_configuring_debezium_topics">https://debezium.io/documentation/reference/1.9/install.html#_configuring_debezium_topics</a><br />Starting 2.0 debezium changed the paramater name to <mark>schema.history.internal.kafka.topic</mark> so if you are reading this wondering why you don't have that parameter you might be implementing the 2.x version of the connector</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">kafka.strimzi.io/v1beta2</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">KafkaConnector</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">sql-connector-001</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">strimzi.io/cluster:</span> <span class="hljs-string">debezium-connect-cluster</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">class:</span> <span class="hljs-string">io.debezium.connector.sqlserver.SqlServerConnector</span>
  <span class="hljs-attr">tasksMax:</span> <span class="hljs-number">1</span>
  <span class="hljs-attr">config:</span>
    <span class="hljs-attr">database.history.kafka.topic:</span> <span class="hljs-string">schema-changes-topic</span>
    <span class="hljs-string">...</span>
</code></pre>
<p>It's worth noting that Confluent Kafka, the most popular distribution of Kafka, sets its topics with only one week of retention by default. This means that if you're using Confluent Kafka, you'll need to override this default behavior for the history topic.</p>
<p>If you don't set the retention policy for the history topic correctly, you may run into issues with the Debezium connector not working properly. One common error message is "The db history topic or its content is fully or partially missing. Please check the database history topic configuration and re-execute the snapshot."</p>
<p>If you encounter this error, you'll need to take corrective action, which may involve nuking the topics, setting proper configurations, and starting from scratch. Alternatively, you could rename the connector to another name, which is a temporary fix that will only last another week.</p>
<p>In summary, when setting up Kafka Connect with Debezium Connectors, it's important to pay close attention to the configuration of the history topic. Make sure that it has infinite retention and a single partition to avoid issues with the Debezium connector not working properly. Open source no-code tools like Kafka Connect make it easier than ever to build a scalable, reliable, and distributed data pipeline. However, it's essential to be careful about configuring our connectors, which can often go wrong without us realizing it. By setting the right configuration parameters for the database history topic, we can ensure that our Kafka Connect pipeline runs smoothly and without any hiccups. I hope this blog post helps you avoid any potential issues and that you are reading this before you release your connectors to production.</p>
]]></content:encoded></item><item><title><![CDATA[Building a Scalable Streaming Architecture with Kafka and Debezium for Multiple SQL Databases]]></title><description><![CDATA[In a recent project at work, we needed to monitor changes to data stored in multiple databases and services in real-time. To achieve this goal, we opted for Kafka as our distributed streaming platform. We chose Confluent Cloud, the hosted service pro...]]></description><link>https://basantakharel.com/building-a-scalable-streaming-architecture-with-kafka-and-debezium-for-multiple-sql-databases</link><guid isPermaLink="true">https://basantakharel.com/building-a-scalable-streaming-architecture-with-kafka-and-debezium-for-multiple-sql-databases</guid><category><![CDATA[kafka connect]]></category><category><![CDATA[debezium]]></category><category><![CDATA[change data capture]]></category><dc:creator><![CDATA[Basanta Kharel]]></dc:creator><pubDate>Thu, 06 Apr 2023 20:21:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/xOLhD-qfoRI/upload/b80f8be56d769bae134d26f4bfede43f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In a recent project at work, we needed to monitor changes to data stored in multiple databases and services in real-time. To achieve this goal, we opted for Kafka as our distributed streaming platform. We chose Confluent Cloud, the hosted service provided by the creators of Kafka, for our Kafka Cluster as it offers a fully managed Kafka Service, eliminating the need for us to handle infrastructure management, scaling, or maintenance tasks. However, when it came to implementing Change Data Capture (CDC) to capture changes from our databases, we faced a challenge. Our databases follow a single tenancy isolation strategy, which means that each tenant has their own isolated database instance. We have hundreds of tenants, each with their own database, which would require hundreds of Debezium connectors in a typical setup.</p>
<p>Confluent Cloud offers a managed solution for CDC through its connectors, but the pricing model would not be favorable for our setup with hundreds of connectors. So, we had to come up with a solution that would work with our unique architecture and budget constraints. We ended up deploying self-managed Kafka Connect Instances in Azure Kubernetes Cluster</p>
<p>Here is the overall streaming architecture:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680803672020/eeec648d-d101-4f69-a8f7-2740c620f9bb.png" alt class="image--center mx-auto" /></p>
<ol>
<li><p>Some service updates the SQL database</p>
</li>
<li><p>The debezium connector, registered in Kafka Connect Instance deployed in Kubernetes environment, captures the changes</p>
</li>
<li><p>Debezium worker sends the events to confluent Kafka raw topic</p>
</li>
<li><p>The event processor processes the event and pushes that to another final topic for consumption</p>
</li>
</ol>
<h2 id="heading-why-kafka">Why Kafka?</h2>
<p>Kafka is a powerful distributed streaming platform that enables users to build scalable and reliable data pipelines and applications. It is based on the publish-subscribe messaging model and allows producers to publish messages to a Kafka topic, while consumers subscribe to the topic to receive messages. With its high throughput, low latency, and fault-tolerant architecture, Kafka is widely used for building real-time data streaming pipelines.</p>
<p>One popular use case for Kafka is Change Data Capture (CDC), which is the process of detecting and capturing changes to data sources and making those changes available in real-time to other systems or applications. This is where Debezium comes in. Debezium is an open source distributed platform that provides change data capture capabilities for Kafka. With Debezium connectors, Kafka can capture data changes from various data sources such as MySQL, PostgreSQL, MongoDB, and Oracle, among others (MSSQL in our case).</p>
<h2 id="heading-deploying-debezium-connectors">Deploying Debezium Connectors</h2>
<p>Deploying Debezium connectors on a self-managed Kubernetes cluster was not an easy endeavor. Deploying a self-managed service does require more effort in terms of setting up and maintaining the cluster. Specially for someone who had not deployed any Kubernetes service before! But open source came to the rescue.</p>
<p>To deploy the Debezium connector on a self-managed AKS cluster we used the open-source <a target="_blank" href="https://strimzi.io/">Strimzi operator</a>. Strimzi provides an easy and scalable way to manage Kafka and Kafka Connect clusters on Kubernetes. It allows for the deployment of Kafka brokers, ZooKeeper nodes, Kafka Connect workers, and Debezium connectors as Kubernetes resources. Strimzi also enables the configuration and scaling of Kafka and Kafka Connect resources using Kubernetes Custom Resource Definitions (CRDs). For our use case, we only needed to deploy Kafka Connect and Debezium Connectors</p>
<h3 id="heading-kafka-connect-yaml">Kafka Connect Yaml</h3>
<p>To deploy a Kubernetes service, you need to provide a YAML file. For production use cases I would not recommend to kubectl directly but use something like Helm charts (we use helm charts for all our kubernetes deployment). With that out of the way, lets dig into Kafka Connect</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">kafka.strimzi.io/v1beta2</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">KafkaConnect</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">debezium-connect-cluster</span>
  <span class="hljs-attr">annotations:</span>
    <span class="hljs-attr">strimzi.io/use-connector-resources:</span> <span class="hljs-string">"true"</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">image:</span> {{ <span class="hljs-string">custom-image/kafka-connect-with-x-connector</span> }}
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">2</span>
  <span class="hljs-attr">bootstrapServers:</span> {{ <span class="hljs-string">kafka-cluster-kafka-bootstrap:9092</span> }}
  <span class="hljs-attr">config:</span>
    <span class="hljs-attr">group.id:</span> <span class="hljs-string">connect-cluster</span>
    <span class="hljs-attr">offset.storage.topic:</span> <span class="hljs-string">connect-cluster-offsets</span>
    <span class="hljs-attr">config.storage.topic:</span> <span class="hljs-string">connect-cluster-configs</span>
    <span class="hljs-attr">status.storage.topic:</span> <span class="hljs-string">connect-cluster-status</span>
    <span class="hljs-comment"># -1 means it will use the default replication factor configured in the broker</span>
    <span class="hljs-attr">config.storage.replication.factor:</span> <span class="hljs-number">-1</span>
    <span class="hljs-attr">offset.storage.replication.factor:</span> <span class="hljs-number">-1</span>
    <span class="hljs-attr">status.storage.replication.factor:</span> <span class="hljs-number">-1</span>
    <span class="hljs-comment">#secret config</span>
    <span class="hljs-attr">config.providers:</span> <span class="hljs-string">secrets</span>
    <span class="hljs-attr">config.providers.secrets.class:</span> <span class="hljs-string">io.strimzi.kafka.KubernetesSecretConfigProvider</span>
</code></pre>
<p>The ones with {{ }} double braces need to be updated</p>
<p>First is the image, for KafkaConnect. By default base Kafka Connect image doesnot include any connectors. You need to add each one you need to /opt/kafka/plugins/ directory. Please follow <a target="_blank" href="https://strimzi.io/blog/2020/01/27/deploying-debezium-with-kafkaconnector-resource/">deploying debezium with kafka connector</a> for more details. It is quite old but is full of riches. I might write a blog on just the building image part later. The second variable is the bootstrapServers which is where the kafka cluster is. In our case it is the confluent but for local testing, it is {{ your kafka cluster name}}-bootstrap-server:9092. So if you kafka cluster is named "kafka-cluster" it would be kafka-cluster-kafka-bootstrap:9092</p>
<p>To scale all you have to do is increase the replicas value, in the example is 2. Since KafkaConnect is a custom type, provided by Strimzi CRDs, strimzi operator takes care of the rest of the deployment for you. In background, strimzi operator creates one Deployment with RollingUpdate Strategy with x replicas and Kafka Connect environment variables</p>
<h3 id="heading-installing-kafka-connect">Installing Kafka Connect</h3>
<p>Assuming you have a Kubernetes environment and helm you can apply the yaml file to a specific namespace like 'kafka'. Prerequisite for setting your local environment:</p>
<ol>
<li><p><a target="_blank" href="https://minikube.sigs.k8s.io/docs/start/">Minikube</a> : Minikube is local Kubernetes, focusing on making it easy to learn and develop for Kubernetes</p>
</li>
<li><p><a target="_blank" href="https://helm.sh/">Helm</a> : Helm helps you manage Kubernetes applications</p>
</li>
<li><p><a target="_blank" href="https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/quick-start/enable-hyper-v">Hyper-V</a> : Enable Hyper-V on your windows machine, if you are using windows</p>
</li>
</ol>
<pre><code class="lang-bash"><span class="hljs-comment"># create kafka namespace</span>
kubectl create ns kafka
<span class="hljs-comment"># add strimzi repo to helm chart </span>
helm repo add strimzi https://strimzi.io/charts/
<span class="hljs-comment"># install strimzi operator</span>
helm install strimzi strimzi/strimzi-kafka-operator --namespace kafka
<span class="hljs-comment"># deploy kafka connect</span>
kubectl apply -f kafka-connect.yaml --namespace kafka <span class="hljs-comment"># yaml file shown above</span>
</code></pre>
<h3 id="heading-deploying-debezium-kafka-connector">Deploying Debezium Kafka Connector</h3>
<p>Next is to deploy the debezium connector. And here is the sample yaml file</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">kafka.strimzi.io/v1beta2</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">KafkaConnector</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">sql-connector-001</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">strimzi.io/cluster:</span> <span class="hljs-string">debezium-connect-cluster</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">class:</span> <span class="hljs-string">io.debezium.connector.sqlserver.SqlServerConnector</span>
  <span class="hljs-attr">tasksMax:</span> <span class="hljs-number">1</span>
  <span class="hljs-attr">config:</span>
    <span class="hljs-attr">database.server.name:</span> {{ <span class="hljs-string">server-name</span> }}
    <span class="hljs-attr">database.hostname:</span> {{ <span class="hljs-string">host-name</span> }}
    <span class="hljs-attr">database.dbname:</span> {{ <span class="hljs-string">$dbname</span> <span class="hljs-string">|</span> <span class="hljs-string">lower</span> <span class="hljs-string">|</span> <span class="hljs-string">quote</span> }}
    <span class="hljs-attr">database.user:</span> <span class="hljs-string">"${secrets:kafka/dbsecret:username}"</span>
    <span class="hljs-attr">database.password:</span> <span class="hljs-string">"${secrets:kafka/dbsecret:password}"</span>
    <span class="hljs-comment">#history events for database changes</span>
    <span class="hljs-attr">database.history.kafka.bootstrap.servers:</span> {{ <span class="hljs-string">kafka-cluster-kafka-bootstrap:9092</span>}}
    <span class="hljs-attr">database.history.kafka.topic:</span> <span class="hljs-string">schema-changes-topic</span>
    <span class="hljs-comment">#included tables</span>
    <span class="hljs-attr">table.include.list:</span> <span class="hljs-string">"dbo.table_1"</span>
    <span class="hljs-attr">column.include.list:</span> <span class="hljs-string">"dbo.table_1.id, dbo.table_1.name"</span>
</code></pre>
<pre><code class="lang-yaml"><span class="hljs-comment"># secret file</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Secret</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">dbsecret</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">kafka</span>
<span class="hljs-attr">type:</span> <span class="hljs-string">Opaque</span>
<span class="hljs-attr">data:</span>
  <span class="hljs-attr">username:</span> {{ <span class="hljs-string">b64encoded</span> <span class="hljs-string">username</span>}}
  <span class="hljs-attr">password:</span> {{ <span class="hljs-string">b64encoded</span> <span class="hljs-string">password</span>}}
</code></pre>
<pre><code class="lang-bash"><span class="hljs-comment"># applying secret</span>
kubectl apply -f dbsecret.yaml 
<span class="hljs-comment"># apply connector</span>
kubectl apply -f sql-connector-001.yaml -n kafka
</code></pre>
<p>We are using KubernetesSecretConfigProvider in the KafkaConnect chart above. This allows to dynamically load db secrets in kafka connector above with properties:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">database.user:</span> <span class="hljs-string">"${secrets:kafka/dbsecret:username}"</span>
<span class="hljs-attr">database.password:</span> <span class="hljs-string">"${secrets:kafka/dbsecret:password}"</span>
</code></pre>
<p>This allows to add secrets or update them without restarting the Kafka Connect Instance as Debezium connectors are barely configurations attached to KafkaConnect</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In conclusion, deploying Kafka Connect and Debezium Kafka Connector can be a great solution for capturing database changes and streaming them to Kafka. With the help of Kubernetes and Helm, deploying and scaling the solution is made much easier. While there are many ways to deploy Kafka Connect and Debezium Kafka Connector, using Strimzi can simplify the process and provide additional features like custom resource definitions for Kafka Connect and Kafka Connector. Additionally, using a fully managed Kafka service like Confluent Cloud can reduce the operational burden and allow you to focus on building your streaming application. With the right setup, deploying Kafka Connect and Debezium Kafka Connector can help you build real-time data pipelines and unlock valuable insights from your data.</p>
<p>I only scratched the surface of deploying Kafka connect and connectors. Please leave comments if you want to learn more about something I failed to mention</p>
]]></content:encoded></item><item><title><![CDATA[How do I call another kubernetes service inside the same cluster]]></title><description><![CDATA[Let's say you have a nodejs backend deployed in Kubernetes, and wanted to call another service inside your Kubernetes cluster. How do you do it? (Skip to answer by scrolling down to the bottom) Read more about DNS in kubernetes here: https://basantak...]]></description><link>https://basantakharel.com/how-do-i-call-another-kubernetes-service-inside-the-same-cluster</link><guid isPermaLink="true">https://basantakharel.com/how-do-i-call-another-kubernetes-service-inside-the-same-cluster</guid><category><![CDATA[Kubernetes]]></category><category><![CDATA[Microservices]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[Kubernetes DNS ]]></category><dc:creator><![CDATA[Basanta Kharel]]></dc:creator><pubDate>Thu, 19 Jan 2023 23:30:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/98Lw0q5LvJA/upload/e5c5ac20fb191354b4c0a14607250584.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Let's say you have a nodejs backend deployed in Kubernetes, and wanted to call another service inside your Kubernetes cluster. How do you do it? (Skip to answer by scrolling down to the bottom) Read more about DNS in kubernetes here: <a target="_blank" href="https://basantakharel.com/dns-in-kubernetes-more-than-just-name-resolution">https://basantakharel.com/dns-in-kubernetes-more-than-just-name-resolution</a></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> axios = <span class="hljs-built_in">require</span>(<span class="hljs-string">'axios'</span>);
<span class="hljs-comment">//DONOT use in production without error handling</span>
<span class="hljs-keyword">const</span> {data} = <span class="hljs-keyword">await</span> axios.get(<span class="hljs-string">'some-service'</span>);
</code></pre>
<p>Assuming you have some knowledge of Kubernetes(K8s), you know that you need a deployment and a service</p>
<p>To give basic info here is some description of deployment and service</p>
<h2 id="heading-deployment">Deployment</h2>
<blockquote>
<p>A Deployment provides declarative updates for Pods and ReplicaSets.</p>
<p>You describe a desired state in a Deployment, and the Deployment Controller changes the actual state to the desired state at a controlled rate. You can define Deployments to create new ReplicaSets or to remove existing Deployments and adopt all their resources with new Deployments.</p>
</blockquote>
<p>You don't want to be creating pods manually or even replica sets but use deployment which handles rollout/rollback of your pods(not useful if you have only one replica as in the example below but good practice nonetheless)</p>
<p><em>Check out the</em> <a target="_blank" href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/"><em>K8s deployment official documentation</em></a> <em>for more info</em></p>
<p>And here is a sample YAML file for a deployment</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">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">some-service-dpl</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">some-service-dpl</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">some-service-dpl</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">some-service-dpl</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">yourdockerregistry/some-service-dpl-image</span>
        <span class="hljs-attr">ports:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">8080</span>
</code></pre>
<h2 id="heading-service">Service</h2>
<blockquote>
<p>A Kubernetes Service is an abstraction that defines a logical set of Pods running somewhere in your cluster, that all provide the same functionality. When created, each Service is assigned a unique <strong>IP address</strong> (also called clusterIP). This address is tied to the lifespan of the Service, and <strong>will not change while the Service is alive</strong>.</p>
</blockquote>
<p>Service by default is of type ClusterIP which exposes the service on cluster-internal IP which will not change while the service is alive. But it will if it is killed and a new one spins up so an IP address is not a full proof way to talk to a service</p>
<p><em>Check out the</em> <a target="_blank" href="https://kubernetes.io/docs/concepts/services-networking/service/"><em>K8s service official documentation</em></a> <em>for more info</em></p>
<p>Here is YAML file for a service that is exposing the previous deployment</p>
<pre><code class="lang-yaml"><span class="hljs-attr">kind:</span> <span class="hljs-string">Service</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">some-service-svc</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">some-service-svc</span>
  <span class="hljs-attr">ports:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">some-service-api</span>
      <span class="hljs-attr">targetPort:</span> <span class="hljs-number">8080</span>
      <span class="hljs-attr">port:</span> <span class="hljs-number">8080</span>
      <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span>
</code></pre>
<p>So how do we talk to this service backend? The whole point of creating the service was to get a stable IP for the pods managed by replica sets managed by some-service-dpl. So we don't want to talk to the pods directly</p>
<p>We also don't want to talk to the service directly using an IP address which can be found by this simple kubectl command. Especially not if we are deploying both services at the same time and there is no way to know the IP address beforehand</p>
<pre><code class="lang-bash">kubectl get svc some-service-svc
NAME               TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)     AGE
some-service-svc   ClusterIP   10.0.15.149   &lt;none&gt;        8080/TCP    21s
</code></pre>
<p>What we do want to use is the name of the service itself and that's it!</p>
<p>This works because K8s uses DNS for services and pods and internally resolves http://some-service-svc:8080 to http://10.0.15.149:8080. Below you can see the fully qualified DNS name for a service</p>
<pre><code class="lang-yaml"> {{<span class="hljs-string">service_name</span>}}<span class="hljs-string">.{{namespace}}.svc.cluster.local</span>
</code></pre>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> axios = <span class="hljs-built_in">require</span>(<span class="hljs-string">'axios'</span>);
<span class="hljs-comment">//DONOT use in production without error handling</span>
<span class="hljs-keyword">const</span> {data} = <span class="hljs-keyword">await</span> axios.get(<span class="hljs-string">'http://some-service-svc:8080'</span>);
<span class="hljs-comment">//if the service was in some other namespace </span>
<span class="hljs-keyword">const</span> {data} = <span class="hljs-keyword">await</span> axios.get(<span class="hljs-string">'http://some-service-svc.otherNamespace.svc.cluster.local:8080'</span>);
</code></pre>
<p><em>Just make sure that you keep track of the name of your service or add the service and port exposed by the service in your environment from config variables (from Helm values if you use Helm for example)</em></p>
]]></content:encoded></item><item><title><![CDATA[JAAS config entry not terminated by semi-colon]]></title><description><![CDATA[This is a blog regarding an error I encountered while trying connecting to Confluent Kafka Bootstrap server from self-manged KafkaConnect in my local minikube server in windows environment. If you are getting this error and are not using windows envi...]]></description><link>https://basantakharel.com/jaas-config-entry-not-terminated-by-semi-colon</link><guid isPermaLink="true">https://basantakharel.com/jaas-config-entry-not-terminated-by-semi-colon</guid><category><![CDATA[Strimzi]]></category><category><![CDATA[Apache Kafka]]></category><category><![CDATA[Kubernetes]]></category><dc:creator><![CDATA[Basanta Kharel]]></dc:creator><pubDate>Tue, 15 Nov 2022 22:01:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/TtjipokhTbY/upload/v1668549689804/kVpXA3KLK.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is a blog regarding an error I encountered while trying connecting to Confluent Kafka Bootstrap server from self-manged KafkaConnect in my local minikube server in windows environment. If you are getting this error and are not using windows environment, this article might not be useful for you. </p>
<p>JAAS stands for Java Authentication and Authorization Service. And per <a target="_blank" href="https://docs.oracle.com/en/java/javase/11/security/java-authentication-and-authorization-service-jaas-reference-guide.html#GUID-2A935F5E-0803-411D-B6BC-F8C64D01A25C">Oracle JAAS reference</a> guide it can be used for two purposes:</p>
<ol>
<li>for authentication of users, to reliably and securely determine who is currently executing Java code, regardless of whether the code is running as an application, an applet, a bean, or a servlet; and</li>
<li>for authorization of users to ensure they have the access control rights (permissions) required to do the actions performed.</li>
</ol>
<p>JAAS is the Java implementation of Pluggable Authentication Module (PAM) framework.
And now where is JIM you may ask, which is the reason for this post, and I am getting to it, I promise
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1668543488741/jYm82fwRB.png" alt="image.png" /></p>
<p>Now lets talk about the elephant in the room, 'JAAS config entry not terminated by semi-colon', we will get to JIM later I promise. Here is the <a target="_blank" href="https://github.com/a0x8o/kafka/blob/master/clients/src/main/java/org/apache/kafka/common/security/JaasConfig.java">link</a> to the full source code of the JaasConfig class. For the lazy ones here is the snippet that we are concerned about:</p>
<pre><code><span class="hljs-keyword">if</span> (tokenizer.ttype != <span class="hljs-string">';'</span>)
  <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"JAAS config entry not terminated by semi-colon"</span>);
</code></pre><p>From the snippet we can figure out that it will throw an IllegalArgumentException if the config doesn't end with a semi-colon. My first thought after finding this out was okay I need to add a semi-colon then. But where exactly? And how do I add a semi-colon in a yaml file?
Here is the yaml snippet, authentication part is what sets the JAAS config</p>
<pre><code>apiVersion: kafka.strimzi.io/v1beta2
<span class="hljs-attr">kind</span>: KafkaConnect
<span class="hljs-attr">metadata</span>:
  name: kafka-connect-cluster
  <span class="hljs-attr">annotations</span>:
    strimzi.io/use-connector-resources: <span class="hljs-string">"true"</span>
<span class="hljs-attr">spec</span>:
  replicas: <span class="hljs-number">1</span>
  <span class="hljs-attr">tls</span>:
    trustedCertificates: []
  <span class="hljs-attr">authentication</span>: 
    type: plain
    <span class="hljs-attr">username</span>: XXXXXXXXXXX
    <span class="hljs-attr">passwordSecret</span>:
      secretName: confluent-secret
      <span class="hljs-attr">password</span>: password
  ...
</code></pre><p>So how do you stick a semi-colon here? You don't. 
But that's what the error is saying: the error is wrong
The error is wrong you say: yes</p>
<p>Some might say that the error is right, but I am not going to argue on the validity of it. My concern is to not get that error anymore, but the message is not helping. The issue was not that it was missing a semi-colon. If you look carefully at the yaml above, you can see the passwordSecret under spec-&gt;authentication
Thats how you store secret in kubernetes world. Here password value is not the actual password but the label to the field in confluent-secret Secret that stores the password. Let's say you have the password in MY-PASSWORD.txt file 
What you do is first create a Secret in k8s</p>
<pre><code>kubectl create secret generic confluent-secret --<span class="hljs-keyword">from</span>-file=password=./MY-PASSWORD.txt -n Kafka (here Kafka is my k8s namespace)
</code></pre><p>Which creates this Secret object in k8s </p>
<pre><code>apiVersion: v1
<span class="hljs-attr">data</span>:
  password: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=
kind: Secret
<span class="hljs-attr">metadata</span>:
  name: confluent-secret
  <span class="hljs-attr">namespace</span>: kafka
  ...
type: Opaque
....
</code></pre><p>It is an object of kind Secret with name confluent-secret and it stores the password from MY-PASSWORD.txt to under data-&gt; password. But you don't just store password in plain text in k8s world, technically it is still plain text but you encode it first. What kubectl does is base64 encodes the password before creating this Secret. And that my friend is the climax of the story</p>
<p>Now before revealing it, I have a promise to fulfill. There is no JIM in this story here only PAM, sorry folks I lied. But if there was ever any JIM, it would be any kafka service that tries to connect to a Kafka bootstrap server like a Kafka Connect instance or a MirroMaker instance. I actually figured out the solution to fix the 'not terminated by semi-colon' error by digging down a closed github bug in strimzi kafka operator which you can find here: <a target="_blank" href="https://github.com/strimzi/strimzi-kafka-operator/issues/3949">not quite the semi-colon bug</a></p>
<p>Getting back to the climax, base64 encoding is everything that is wrong that causes this error. Now I can imagine Dwights of the world that are windows developer laughing at this saying I knew it all along. Wait dwight, and windows what's going on?</p>
<p><img src="https://25.media.tumblr.com/tumblr_md0db9917B1r81219o1_500.gif" alt="Dwight laughing" /></p>
<blockquote>
<p>kubectl create secret doesn't work in windows environment</p>
</blockquote>
<p>Let that sink in. There I said it, kubectl create secret is broken in windows.
It is not encoding the password right. And poor JaasConfig trips up and returns the only error it can think of 'JAAS config entry not terminated by semi-colon'</p>
<p>The fix is simple, just don't use windows and use linux environment instead 
Or if you have to stick with windows here is the trick that I did to fix this issue</p>
<ol>
<li>Open up a linux terminal, (you can use install <a target="_blank" href="https://learn.microsoft.com/en-us/windows/wsl/install">WSL in windows</a> </li>
<li>base64 encode your password  <pre><code>echo XXXXXX | base64
</code></pre></li>
<li>Edit the Secret in windows:<pre><code>kubectl edit secret confluent-secret -n kafka
</code></pre></li>
<li>replace the password value with the encoded string from step 2</li>
</ol>
<p>And live happily ever after</p>
]]></content:encoded></item><item><title><![CDATA[Efficiently transfer c# objects to from Azure blob storage]]></title><description><![CDATA[Azure Blob Storage is primarily designed to store files and the client API is intuitively geared towards working with files and streams
Recently I had to deal with reading/writing in-memory objects using blob storage. There was not much literature re...]]></description><link>https://basantakharel.com/efficiently-transfer-c-objects-to-from-azure-blob-storage</link><guid isPermaLink="true">https://basantakharel.com/efficiently-transfer-c-objects-to-from-azure-blob-storage</guid><category><![CDATA[C#]]></category><category><![CDATA[Azure]]></category><category><![CDATA[azure-blobstorage]]></category><dc:creator><![CDATA[Basanta Kharel]]></dc:creator><pubDate>Thu, 23 Sep 2021 21:07:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/K4io-aDZF78/upload/v1668550177169/pSkyZcFjua.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Azure Blob Storage is primarily designed to store files and the client API is intuitively geared towards working with files and streams</p>
<p>Recently I had to deal with reading/writing in-memory objects using blob storage. There was not much literature regarding doing this efficiently using the API. So I began the journey to find one, or rather stumbled into one</p>
<p>Here was the first pass at reading from blob storage:</p>
<pre><code>using Newtonsoft.Json;

public <span class="hljs-keyword">async</span> Task&lt;T&gt; ReadDataAsync (string blobId, CancellationToken c)
{
    <span class="hljs-keyword">var</span> client = containerClient.GetBlobClient(blobId);
    using <span class="hljs-keyword">var</span> stream = <span class="hljs-keyword">await</span> client.OpenReadAsync(<span class="hljs-literal">null</span>, c);
    using <span class="hljs-keyword">var</span> streamReader = <span class="hljs-keyword">new</span> StreamReader(stream);
    using <span class="hljs-keyword">var</span> json = <span class="hljs-keyword">new</span> JsonTextReader(streamReader);
    <span class="hljs-keyword">return</span> JsonSerializer.CreateDefault().Deserialize&lt;T&gt;(json);
}
</code></pre><p>This is okay performance wise, StreamReader and JsonTextReader should optimize memory usage but the code is too verbose. We can do better.</p>
<p>Here we go with the next pass on reading blob storage</p>
<p>using System.Text.Json;</p>
<pre><code>public <span class="hljs-keyword">async</span> Task&lt;T&gt; ReadDataAsync (string blobId, CancellationToken c)
{
    <span class="hljs-keyword">var</span> client = containerClient.GetBlobClient(blobId);
    using <span class="hljs-keyword">var</span> stream = <span class="hljs-keyword">await</span> client.OpenReadAsync(<span class="hljs-literal">null</span>, c);
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> JsonSerializer.DeserializeAsync&lt;T&gt;(stream, <span class="hljs-literal">null</span>, c);
}
</code></pre><p>This is much better, now we are using the new module System.Text.Json which helps tidy up the code a little better, reducing 3 lines from the first pass to just 1 line. Performance wise it is the same as before</p>
<p>Now lets see the first pass at writing to blob storage</p>
<pre><code>public <span class="hljs-keyword">async</span> Task WriteAsync (SomeData data, string blobId)
{
    <span class="hljs-keyword">var</span> client = containerClient.GetBlobClient(blobId);
    <span class="hljs-keyword">await</span> using <span class="hljs-keyword">var</span> ms = <span class="hljs-keyword">new</span> MemoryStream();
    <span class="hljs-keyword">var</span> json = JsonConvert.SerializeObject(data);
    <span class="hljs-keyword">var</span> writer = <span class="hljs-keyword">new</span> StreamWriter(ms);
    <span class="hljs-keyword">await</span> writer.WriteAsync(json);
    <span class="hljs-keyword">await</span> writer.FlushAsync();
    ms.Position = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">await</span> client.UploadAsync(ms);
}
</code></pre><p>This is sub par to say the least as put in a PR comment. That's exactly what happened to me. I wrote this code and the PR comment recommended me to optimize it! Here is why this aprroach is not so good, first the data is serialized to json, next the serialized json is written into stream. And finally after resetting the stream position to 0 it is uploaded to blob storage. This is 3X the memory of the original data. We can definitely do better</p>
<p>Now the second pass at writing to blob storage</p>
<pre><code>using System.Text.Json;

public <span class="hljs-keyword">async</span> Task WriteAsync&lt;T&gt; (T data, string blobId)
{
    <span class="hljs-keyword">var</span> client = containerClient.GetBlobClient(blobId);
    <span class="hljs-keyword">await</span> using <span class="hljs-keyword">var</span> ms = <span class="hljs-keyword">new</span> MemoryStream();
    <span class="hljs-keyword">await</span> JsonSerializer.SerializeAsync(ms, data);
    ms.Position = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">await</span> client.UploadAsync(ms);
}
</code></pre><p>I would say some improvement compared to the first pass, instead of 3x memory now it is down to 2x memory, but we can do better. With that PR review encouragement here is the final pass on writing to blob storage. Big thanks to this stackoverflow answer that mad use of Azure.Storage.Blobs.Specialized https://stackoverflow.com/questions/62279770/how-to-upload-stream-with-azure-storage-blobs</p>
<pre><code>using System.Text.Json;
using Azure.Storage.Blobs.Specialized;

public <span class="hljs-keyword">async</span> Task WriteAsync&lt;T&gt; (T data, string blobId)
{
    <span class="hljs-keyword">var</span> client = containerClient.GetBlockBlobClient(blobId);
    <span class="hljs-keyword">var</span> ms = <span class="hljs-keyword">await</span> client.OpenWriteAsync(<span class="hljs-literal">true</span>);
    <span class="hljs-keyword">await</span> JsonSerializer.SerializeAsync(ms, data);
}
</code></pre><p>The only difference between this and the previous code is that we are using a new api of GetBlockBlobClient instead of GetBlobClient from pass 2. This api gives us stream to write data directly into. And voila just 1x memory and it is uploaded to blob storage</p>
<p>With that we are finally at the most performant code that I can think of ! Let me know if there is something even better than this</p>
<p>Here is the final code for both read and write</p>
<pre><code>using System.Text.Json;
using Azure.Storage.Blobs.Specialized;

public <span class="hljs-keyword">async</span> Task&lt;T&gt; ReadDataAsync (string blobId, CancellationToken c)
{
    <span class="hljs-keyword">var</span> client = containerClient.GetBlobClient(blobId);
    using <span class="hljs-keyword">var</span> stream = <span class="hljs-keyword">await</span> client.OpenReadAsync(<span class="hljs-literal">null</span>, c);
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> JsonSerializer.DeserializeAsync&lt;T&gt;(stream, <span class="hljs-literal">null</span>, c);
}

public <span class="hljs-keyword">async</span> Task WriteAsync&lt;T&gt; (T data, string blobId)
{
    <span class="hljs-keyword">var</span> client = containerClient.GetBlockBlobClient(blobId);
    <span class="hljs-keyword">var</span> ms = <span class="hljs-keyword">await</span> client.OpenWriteAsync(<span class="hljs-literal">true</span>);
    <span class="hljs-keyword">await</span> JsonSerializer.SerializeAsync(ms, data);
}
</code></pre>]]></content:encoded></item></channel></rss>