<?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[Technobabble]]></title><description><![CDATA[Generalizing Specialist - that's just hyperbole for "jack of all, master of some". Dev for 20+ years with curiosity still intact.]]></description><link>https://blog.omkarpatil.dev</link><generator>RSS for Node</generator><lastBuildDate>Mon, 07 Sep 2026 05:42:59 GMT</lastBuildDate><atom:link href="https://blog.omkarpatil.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Azure AKS cluster from scratch using Terraform]]></title><description><![CDATA[While working on creating training material on Azure IaC using Terraform, I created a small demo project to create an Azure AKS cluster from scratch and integrated it with Terraform Cloud. It came out quite nicely and therefore I thought I would shar...]]></description><link>https://blog.omkarpatil.dev/azure-aks-cluster-from-scratch-using-terraform</link><guid isPermaLink="true">https://blog.omkarpatil.dev/azure-aks-cluster-from-scratch-using-terraform</guid><category><![CDATA[Azure]]></category><category><![CDATA[Kubernetes]]></category><category><![CDATA[Terraform]]></category><dc:creator><![CDATA[Omkar Patil]]></dc:creator><pubDate>Thu, 12 May 2022 21:53:04 GMT</pubDate><content:encoded><![CDATA[<p>While working on creating training material on Azure IaC using Terraform, I created a small demo project to create an Azure AKS cluster from scratch and integrated it with <a target="_blank" href="https://cloud.hashicorp.com/products/terraform">Terraform Cloud</a>. It came out quite nicely and therefore I thought I would share it with larger audience 🙂.</p>
<p>Detailed instructions can be found on the repo README. You'll need your own Azure account and a subscription. The AKS cluster code is a local module and the created cluster will have the following features:</p>
<ul>
<li>Nodes with Ubuntu OS</li>
<li>Azure CNI</li>
<li>Separate vnet and subnet</li>
<li>Single nodepool with autoscaling enabled</li>
<li>AKS managed Azure AD integration</li>
<li>System-assigned managed Identity</li>
<li>Cluster auto-upgrade enabled</li>
</ul>
<p>Quick note about Terraform Cloud - if you haven't tried it yet, you should. It has a free plan for smaller teams up to 5 and provides a way to manage state remotely and securely. </p>
<p>Happy coding 🤘.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/ospatil/k8s-azure-devops">https://github.com/ospatil/k8s-azure-devops</a></div>
]]></content:encoded></item><item><title><![CDATA[Folds in TypeScript]]></title><description><![CDATA[For past few days, folds were stuck in my head for some reason and needed some unfolding 😃. I did so and below is the summary of my understanding for the benefit of my future self.
Why
Consider the scenario where we have an array of numbers and we w...]]></description><link>https://blog.omkarpatil.dev/folds-in-typescript</link><guid isPermaLink="true">https://blog.omkarpatil.dev/folds-in-typescript</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Functional Programming]]></category><dc:creator><![CDATA[Omkar Patil]]></dc:creator><pubDate>Tue, 08 Feb 2022 12:31:29 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1644241084280/8EDmibTDN.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>For past few days, folds were stuck in my head for some reason and needed some unfolding 😃. I did so and below is the summary of my understanding for the benefit of my future self.</p>
<h2 id="heading-why">Why</h2>
<p>Consider the scenario where we have an array of numbers and we would like to add them together <em>without using a loop</em>. No loops, no problem, we can use recursion.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> sum = ([h, ...t]: <span class="hljs-built_in">number</span>[]): <span class="hljs-function"><span class="hljs-params">number</span> =&gt;</span> h === <span class="hljs-literal">undefined</span> ? <span class="hljs-number">0</span> : h + sum(t);

assert.equal(sum([<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]), <span class="hljs-number">6</span>);
assert.equal(sum([<span class="hljs-number">5</span>]), <span class="hljs-number">5</span>); <span class="hljs-comment">// array with 1 element</span>
assert.equal(sum([]), <span class="hljs-number">0</span>); <span class="hljs-comment">// empty array</span>
</code></pre>
<p>The function <code>sum</code>:</p>
<ul>
<li>accepts an array of numbers.</li>
<li>destructures it into head <code>h</code> and tail <code>t</code>: <code>[h, ...t]</code>.</li>
<li>returns <code>0</code> if the head is <code>undefined</code>. This serves as a base case for the recursion.</li>
<li>else carries on the <code>sum</code> operation with the tail: <code>h + sum(t)</code>.</li>
</ul>
<p>Now, let's define a function to multiply the numbers in an array:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> product = ([h, ...t]: <span class="hljs-built_in">number</span>[]): <span class="hljs-function"><span class="hljs-params">number</span> =&gt;</span> h === <span class="hljs-literal">undefined</span> ? <span class="hljs-number">1</span> : h * product(t);

assert.equal(product([<span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]), <span class="hljs-number">12</span>);
</code></pre>
<p>As we can see, both look almost same. The only bits that vary are:</p>
<ol>
<li>Base case value: what to return when we get down to empty array i.e. the base case of recursion.</li>
<li>The operation: <code>sum</code> in one case and <code>product</code> in the other.</li>
</ol>
<p>This is where folds come in. They generalize the traversing the array and carrying out some operation with combines the array elements in some way.</p>
<h2 id="heading-folds">Folds</h2>
<p>We can traverse an array in one of the two ways: <em>from the right</em> or <em>the left</em>.</p>
<h3 id="heading-right-fold">Right Fold</h3>
<p>Let's define right fold <code>foldr</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> foldr = &lt;A, B&gt;(f: <span class="hljs-function">(<span class="hljs-params">x: A, acc: B</span>) =&gt;</span> B, acc: B, [h, ...t]: A[]): <span class="hljs-function"><span class="hljs-params">B</span> =&gt;</span> h === <span class="hljs-literal">undefined</span> ? acc : f(h, foldr(f, acc, t));
</code></pre>
<p>There is quite a bit that's going on there. Let's go over it step by step.</p>
<p>Arguments:</p>
<ol>
<li>The <em>combiner</em> function <code>f: (x: A, acc: B) =&gt; B</code>:  It accepts the current element of the array and existing accumulator, <em>combines</em> them in some fashion and produces new value of accumulator.</li>
<li>accumulator <code>acc: B</code>: Initial value and the one that should be returned for the base case of the recursion.</li>
<li>array <code>[h, ...t]: A[]</code>: that we need to traverse and combine in some fashion.</li>
</ol>
<p>Coming to the generics types <code>&lt;A, B&gt;(f: (x: A, acc: B) =&gt; B, acc: B, [h, ...t]: A[]): B</code>, it could be surprising to see two separate types being used:  <code>A</code> for the array elements and and <code>B</code> for the accumulator. The final return type of <code>foldr</code> is also <code>B</code> i.e. the generic type of the accumulator.</p>
<p>Why not only <code>A</code>, which is the type of array elements, when all we are doing is traversing the array and producing final result by combining the elements in some fashion. </p>
<p>It turns out it's very much possible to combine the array elements into a different type and the generic type <code>B</code> covers that usage. In some cases, <code>A</code> and <code>B</code> will be same, in some cases, not. We'll see an example later where it's not.</p>
<p>Now, let's see <code>foldr</code> in action. Let's define our <code>sum</code> and <code>product</code> functions in terms of <code>foldr</code>:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> sumFoldr = <span class="hljs-function">(<span class="hljs-params">xs: <span class="hljs-built_in">number</span>[]</span>) =&gt;</span> foldr(<span class="hljs-function">(<span class="hljs-params">x, acc</span>) =&gt;</span> x + acc, <span class="hljs-number">0</span>, xs);
assert.equal(sumFoldr([<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]), <span class="hljs-number">6</span>);

<span class="hljs-keyword">const</span> productFoldr = <span class="hljs-function">(<span class="hljs-params">xs: <span class="hljs-built_in">number</span>[]</span>) =&gt;</span> foldr(<span class="hljs-function">(<span class="hljs-params">x, acc</span>) =&gt;</span> x * acc, <span class="hljs-number">1</span>, xs);
assert.equal(productFoldr([<span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]), <span class="hljs-number">12</span>);
</code></pre>
<p>As we can see, we get expected results.</p>
<p>I found John Whitington's <a target="_blank" href="http://ocaml-book.com/more-ocaml-algorithms-methods-diversions/">More OCAML</a> book has one of the most straight-forward and to-the-point illustrations of <a target="_blank" href="https://static1.squarespace.com/static/51224b9fe4b0dce195c74e5d/t/53fca40ae4b0770690337575/1409065994086/fold.pdf">folds</a> execution.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644241084280/8EDmibTDN.png" alt="foldr.png" /></p>
<p>The call trace makes one thing obvious: <code>foldr</code> is not tail-recursive. The call stack grows till we reach to the end of array before the <em>combine</em> operation starts and stack unwinds.</p>
<h3 id="heading-left-fold">Left Fold</h3>
<p>Let's define left fold <code>foldl</code>:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> foldl = &lt;A, B&gt;(f: <span class="hljs-function">(<span class="hljs-params">x: A, acc: B</span>) =&gt;</span> B, acc: B, [h, ...t]: A[]): <span class="hljs-function"><span class="hljs-params">B</span> =&gt;</span> h === <span class="hljs-literal">undefined</span> ? acc : foldl(f, f(h, acc), t);
</code></pre>
<p>The function signature is same as <code>foldr</code>, the difference being how the <em>combiner</em> function is applied: <code>foldl(f, f(h, acc), t)</code>. We start with initial value of accumulator, apply the <em>combiner</em> function to produce new value for accumulator and use the new value to continue recursing over the remaining array.</p>
<p>Here is how the execution trace looks like:
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1644245800658/9ZYEaLLQk.png" alt="foldl.png" /></p>
<p>Now, let's see <code>foldl</code> in action. Let's define our <code>sum</code> and <code>product</code> functions in terms of <code>foldl</code>:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> sumFoldl = <span class="hljs-function">(<span class="hljs-params">xs: <span class="hljs-built_in">number</span>[]</span>) =&gt;</span> foldl(<span class="hljs-function">(<span class="hljs-params">x, acc</span>) =&gt;</span> x + acc, <span class="hljs-number">0</span>, xs);
assert.equal(sumFoldl([<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]), <span class="hljs-number">6</span>);

<span class="hljs-keyword">const</span> productFoldl = <span class="hljs-function">(<span class="hljs-params">xs: <span class="hljs-built_in">number</span>[]</span>) =&gt;</span> foldl(<span class="hljs-function">(<span class="hljs-params">x, acc</span>) =&gt;</span> x * acc, <span class="hljs-number">1</span>, xs);
assert.equal(productFoldl([<span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]), <span class="hljs-number">12</span>);
</code></pre>
<p>And expected results.</p>
<h3 id="heading-map-and-reduce">Map and Reduce</h3>
<p>Now that we have the fold implementation in place, lets implement two common functions, <code>map</code> and <code>reduce</code> in terms of fold. These are defined as Array instance methods in the standard JavaScript API, but we'll implement these as functions.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> map = &lt;A, B&gt;(xs: A[], cb: <span class="hljs-function">(<span class="hljs-params">x: A</span>) =&gt;</span> B): B[] =&gt; foldl(<span class="hljs-function">(<span class="hljs-params">x, acc</span>) =&gt;</span> {
    acc.push(cb(x));
    <span class="hljs-keyword">return</span> acc;
}, [] <span class="hljs-keyword">as</span> B[], xs);

assert.deepEqual(map([<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>], <span class="hljs-function"><span class="hljs-params">x</span> =&gt;</span> x * <span class="hljs-number">2</span>), [<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">6</span>]);
<span class="hljs-comment">// to demonstrate usage of return array containing different type</span>
assert.deepEqual(map([<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>], <span class="hljs-function"><span class="hljs-params">_x</span> =&gt;</span> <span class="hljs-string">'ho'</span>), [<span class="hljs-string">'ho'</span>, <span class="hljs-string">'ho'</span>, <span class="hljs-string">'ho'</span>]);

<span class="hljs-comment">// reduce</span>
<span class="hljs-keyword">const</span> reduce = &lt;A&gt;([h, ...t]: A[], cb: <span class="hljs-function">(<span class="hljs-params">pre: A, cur: A</span>) =&gt;</span> A) =&gt; foldl(<span class="hljs-function">(<span class="hljs-params">x, acc</span>) =&gt;</span> cb(x, acc), h, t);

assert.deepEqual(reduce([<span class="hljs-number">7</span>, <span class="hljs-number">3</span>, <span class="hljs-number">8</span>], <span class="hljs-function">(<span class="hljs-params">pre, cur</span>) =&gt;</span> pre + cur), <span class="hljs-number">18</span>);
</code></pre>
<p>The <code>map</code> example demonstrates the use of different type for accumulator. It's a rather contrived example, but demonstrates the point well.</p>
<h2 id="heading-folding-over-functions">Folding over functions</h2>
<p>We went over folding over primitive values in the last section. Folding over functions is also quite common and useful operation. Function <em>piping</em> and <em>composition</em> are the two use cases where we can use folding over functions to create a new one.</p>
<h3 id="heading-pipe">Pipe</h3>
<p>A <code>pipe</code> function of functions <code>f1</code>, <code>f2</code> and <code>f3</code> can be defined as: <code>pipe([f1, f2, f3])(x) = f3(f2((f1(x))))</code>.</p>
<p>We give input <code>x</code> to first function <code>f1</code>, take the result and pipe it as input to <code>f2</code>,  get the result and pipe it as input to <code>f3</code> to get the final result.</p>
<p>Let's create pipe creator function called <code>plumber</code> that takes two functions and returns their pipe function.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> plumber = &lt;A&gt;<span class="hljs-function">(<span class="hljs-params">fn1: IdType&lt;A&gt;, fn2: IdType&lt;A&gt;</span>) =&gt;</span> (x: A) =&gt; fn2(fn1(x));
</code></pre>
<p>What's this <code>IdType&lt;A&gt;</code> type of the functions and why it's needed? </p>
<p>If we have an array of functions and would like to create a pipe function using <code>plumber</code> function, we have a problem with kickstarting the process with the first function.</p>
<p><code>plumber</code> expects 2 arguments and we have just one. That's where <em>Identity</em> function comes in. It's a function that simply returns the argument it gets. </p>
<p>We use the <em>identity</em> function as initial value with the first function in the array to kickstart the pipe formation.</p>
<p>Let's create a pipe function in imperative fashion first to understand it better.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">type</span> IdType&lt;A&gt; = <span class="hljs-function">(<span class="hljs-params">x: A</span>) =&gt;</span> A;

<span class="hljs-keyword">const</span> double = <span class="hljs-function">(<span class="hljs-params">i: <span class="hljs-built_in">number</span></span>) =&gt;</span> i * <span class="hljs-number">2</span>;
<span class="hljs-keyword">const</span> triple = <span class="hljs-function">(<span class="hljs-params">i: <span class="hljs-built_in">number</span></span>) =&gt;</span> i * <span class="hljs-number">3</span>;
<span class="hljs-keyword">const</span> quadruple = <span class="hljs-function">(<span class="hljs-params">i: <span class="hljs-built_in">number</span></span>) =&gt;</span> i * <span class="hljs-number">4</span>;

<span class="hljs-keyword">const</span> fns = [double, triple, quadruple];

<span class="hljs-keyword">const</span> plumber = &lt;A&gt;<span class="hljs-function">(<span class="hljs-params">fn1: IdType&lt;A&gt;, fn2: IdType&lt;A&gt;</span>) =&gt;</span> (x: A) =&gt; fn2(fn1(x));

<span class="hljs-comment">// since plumber needs two functions to form the pipeline, we need something to start with the</span>
<span class="hljs-comment">// first function in the array and that something is the id function.</span>
<span class="hljs-keyword">const</span> idNumber: IdType&lt;<span class="hljs-built_in">number</span>&gt; = <span class="hljs-function"><span class="hljs-params">x</span> =&gt;</span> x; <span class="hljs-comment">// id function for number type</span>

<span class="hljs-keyword">let</span> acc = idNumber;

<span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> fn <span class="hljs-keyword">of</span> fns) {
    acc = plumber(acc, fn);
}

assert.equal(acc(<span class="hljs-number">1</span>), <span class="hljs-number">24</span>); <span class="hljs-comment">// acc is the final pipe function</span>
</code></pre>
<p>As we can see, we are traversing the array from left to right, assigning the composed pipe function up to that point to the accumulator and the final value of the accumulator is the final pipe function. As such, this is a perfect fit for <code>foldl</code> and below is the implementation based on <code>foldl</code>.</p>
<pre><code class="lang-ts"><span class="hljs-comment">// pipe([f1, f2, f3])(x) = f3(f2((f1(x))))</span>
<span class="hljs-keyword">const</span> pipe = &lt;A&gt;<span class="hljs-function">(<span class="hljs-params">fns: <span class="hljs-built_in">Array</span>&lt;IdType&lt;A&gt;&gt;</span>) =&gt;</span> foldl(<span class="hljs-function">(<span class="hljs-params">fn, acc</span>) =&gt;</span> x =&gt; acc(fn(x)), <span class="hljs-function">(<span class="hljs-params">x: A</span>) =&gt;</span> x, fns);

<span class="hljs-keyword">const</span> half = <span class="hljs-function">(<span class="hljs-params">x: <span class="hljs-built_in">number</span></span>) =&gt;</span> x / <span class="hljs-number">2</span>;
<span class="hljs-keyword">const</span> third = <span class="hljs-function">(<span class="hljs-params">x: <span class="hljs-built_in">number</span></span>) =&gt;</span> x / <span class="hljs-number">3</span>;
<span class="hljs-keyword">const</span> tenTimes = <span class="hljs-function">(<span class="hljs-params">x: <span class="hljs-built_in">number</span></span>) =&gt;</span> x * <span class="hljs-number">10</span>;

<span class="hljs-keyword">const</span> pipeline = pipe([half, third, tenTimes]);
<span class="hljs-comment">// this is equivalent to tenTimes(third(half(24))) === 40</span>
assert.equal(pipeline(<span class="hljs-number">24</span>), tenTimes(third(half(<span class="hljs-number">24</span>))));
</code></pre>
<h3 id="heading-compose">Compose</h3>
<p>A <code>compose</code> function of functions <code>f1</code>, <code>f2</code> and <code>f3</code> can be defined as: <code>compose([f1, f2, f3])(x) = f1(f2((f3(x))))</code>.</p>
<p>We start traversing the array from right, give input <code>x</code> to function <code>f3</code>, take the result and provide it as input to <code>f2</code>,  get the result and provide it as input to <code>f1</code> to get the final result. It's a perfect fit for <code>foldr</code> and here is the implementation.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> compose = &lt;A&gt;<span class="hljs-function">(<span class="hljs-params">fns: <span class="hljs-built_in">Array</span>&lt;IdType&lt;A&gt;&gt;</span>) =&gt;</span> foldr(<span class="hljs-function">(<span class="hljs-params">fn, acc</span>) =&gt;</span> x =&gt; fn(acc(x)), <span class="hljs-function">(<span class="hljs-params">x: A</span>) =&gt;</span> x, fns);

<span class="hljs-keyword">const</span> plusOne: IdType&lt;<span class="hljs-built_in">number</span>&gt; = <span class="hljs-function"><span class="hljs-params">x</span> =&gt;</span> x + <span class="hljs-number">1</span>;
<span class="hljs-comment">// or add type to the parameter to conform to IdType&lt;number&gt;</span>
<span class="hljs-keyword">const</span> fiveTimes = <span class="hljs-function">(<span class="hljs-params">x: <span class="hljs-built_in">number</span></span>) =&gt;</span> x * <span class="hljs-number">5</span>;

<span class="hljs-keyword">const</span> composition = compose([plusOne, fiveTimes]);
<span class="hljs-comment">// this is equivalent to plusOne(fiveTimes(10)) === 51</span>
assert.equal(composition(<span class="hljs-number">10</span>), plusOne(fiveTimes(<span class="hljs-number">10</span>)));
</code></pre>
<p>Here is the <a target="_blank" href="https://github.com/ospatil/dsa/blob/ts/src/functional/folds.ts">complete code listing</a> for quick reference.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> assert <span class="hljs-keyword">from</span> <span class="hljs-string">'node:assert/strict'</span>;

<span class="hljs-comment">// recursive addition of elements of an array</span>
<span class="hljs-keyword">const</span> sum = ([h, ...t]: <span class="hljs-built_in">number</span>[]): <span class="hljs-function"><span class="hljs-params">number</span> =&gt;</span> h === <span class="hljs-literal">undefined</span> ? <span class="hljs-number">0</span> : h + sum(t);

assert.equal(sum([<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]), <span class="hljs-number">6</span>);
assert.equal(sum([<span class="hljs-number">5</span>]), <span class="hljs-number">5</span>); <span class="hljs-comment">// array with 1 element</span>
assert.equal(sum([]), <span class="hljs-number">0</span>); <span class="hljs-comment">// empty array</span>

<span class="hljs-comment">// recursive multiplication of lements of an array</span>
<span class="hljs-keyword">const</span> product = ([h, ...t]: <span class="hljs-built_in">number</span>[]): <span class="hljs-function"><span class="hljs-params">number</span> =&gt;</span> h === <span class="hljs-literal">undefined</span> ? <span class="hljs-number">1</span> : h * product(t);

assert.equal(product([<span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]), <span class="hljs-number">12</span>);
assert.equal(product([<span class="hljs-number">5</span>]), <span class="hljs-number">5</span>);
assert.equal(product([]), <span class="hljs-number">1</span>);

<span class="hljs-comment">/* as we can see sum and product are almost same. The things that vary is the base case value -
 * (0 for sum and 1 for product) and the operation. Let's generalize it.
 */</span>
<span class="hljs-keyword">const</span> foldr = &lt;A, B&gt;(f: <span class="hljs-function">(<span class="hljs-params">x: A, acc: B</span>) =&gt;</span> B, acc: B, [h, ...t]: A[]): <span class="hljs-function"><span class="hljs-params">B</span> =&gt;</span> h === <span class="hljs-literal">undefined</span> ? acc : f(h, foldr(f, acc, t));

<span class="hljs-keyword">const</span> sumFoldr = <span class="hljs-function">(<span class="hljs-params">xs: <span class="hljs-built_in">number</span>[]</span>) =&gt;</span> foldr(<span class="hljs-function">(<span class="hljs-params">x, acc</span>) =&gt;</span> x + acc, <span class="hljs-number">0</span>, xs);
assert.equal(sumFoldr([<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]), <span class="hljs-number">6</span>);

<span class="hljs-keyword">const</span> productFoldr = <span class="hljs-function">(<span class="hljs-params">xs: <span class="hljs-built_in">number</span>[]</span>) =&gt;</span> foldr(<span class="hljs-function">(<span class="hljs-params">x, acc</span>) =&gt;</span> x * acc, <span class="hljs-number">1</span>, xs);
assert.equal(productFoldr([<span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]), <span class="hljs-number">12</span>);

<span class="hljs-comment">/* now let's look at foldl */</span>
<span class="hljs-keyword">const</span> foldl = &lt;A, B&gt;(f: <span class="hljs-function">(<span class="hljs-params">x: A, acc: B</span>) =&gt;</span> B, acc: B, [h, ...t]: A[]): <span class="hljs-function"><span class="hljs-params">B</span> =&gt;</span> h === <span class="hljs-literal">undefined</span> ? acc : foldl(f, f(h, acc), t);

<span class="hljs-keyword">const</span> sumFoldl = <span class="hljs-function">(<span class="hljs-params">xs: <span class="hljs-built_in">number</span>[]</span>) =&gt;</span> foldl(<span class="hljs-function">(<span class="hljs-params">x, acc</span>) =&gt;</span> x + acc, <span class="hljs-number">0</span>, xs);
assert.equal(sumFoldl([<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]), <span class="hljs-number">6</span>);

<span class="hljs-keyword">const</span> productFoldl = <span class="hljs-function">(<span class="hljs-params">xs: <span class="hljs-built_in">number</span>[]</span>) =&gt;</span> foldl(<span class="hljs-function">(<span class="hljs-params">x, acc</span>) =&gt;</span> x * acc, <span class="hljs-number">1</span>, xs);
assert.equal(productFoldl([<span class="hljs-number">2</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]), <span class="hljs-number">12</span>);

<span class="hljs-comment">/* let's implement a couple of JavaScript standard apis using folds: map, reduce, not exact but close enough. */</span>
<span class="hljs-comment">// map - the reason for two type parameters is the returned array can be of any type.</span>
<span class="hljs-keyword">const</span> map = &lt;A, B&gt;(xs: A[], cb: <span class="hljs-function">(<span class="hljs-params">x: A</span>) =&gt;</span> B): B[] =&gt; foldl(<span class="hljs-function">(<span class="hljs-params">x, acc</span>) =&gt;</span> {
    acc.push(cb(x));
    <span class="hljs-keyword">return</span> acc;
}, [] <span class="hljs-keyword">as</span> B[], xs);

assert.deepEqual(map([<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>], <span class="hljs-function"><span class="hljs-params">x</span> =&gt;</span> x * <span class="hljs-number">2</span>), [<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">6</span>]);
<span class="hljs-comment">// to demonstrate usage of return array containing different type</span>
assert.deepEqual(map([<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>], <span class="hljs-function"><span class="hljs-params">_x</span> =&gt;</span> <span class="hljs-string">'ho'</span>), [<span class="hljs-string">'ho'</span>, <span class="hljs-string">'ho'</span>, <span class="hljs-string">'ho'</span>]);

<span class="hljs-comment">// reduce</span>
<span class="hljs-keyword">const</span> reduce = &lt;A&gt;([h, ...t]: A[], cb: <span class="hljs-function">(<span class="hljs-params">pre: A, cur: A</span>) =&gt;</span> A) =&gt; foldl(<span class="hljs-function">(<span class="hljs-params">x, acc</span>) =&gt;</span> cb(x, acc), h, t);

assert.deepEqual(reduce([<span class="hljs-number">7</span>, <span class="hljs-number">3</span>, <span class="hljs-number">8</span>], <span class="hljs-function">(<span class="hljs-params">pre, cur</span>) =&gt;</span> pre + cur), <span class="hljs-number">18</span>);

<span class="hljs-comment">/* pipe and compose */</span>
<span class="hljs-comment">/* define type for identity */</span>
<span class="hljs-keyword">type</span> IdType&lt;A&gt; = <span class="hljs-function">(<span class="hljs-params">x: A</span>) =&gt;</span> A;

<span class="hljs-keyword">const</span> double = <span class="hljs-function">(<span class="hljs-params">i: <span class="hljs-built_in">number</span></span>) =&gt;</span> i * <span class="hljs-number">2</span>;
<span class="hljs-keyword">const</span> triple = <span class="hljs-function">(<span class="hljs-params">i: <span class="hljs-built_in">number</span></span>) =&gt;</span> i * <span class="hljs-number">3</span>;
<span class="hljs-keyword">const</span> quadruple = <span class="hljs-function">(<span class="hljs-params">i: <span class="hljs-built_in">number</span></span>) =&gt;</span> i * <span class="hljs-number">4</span>;

<span class="hljs-keyword">const</span> fns = [double, triple, quadruple];

<span class="hljs-keyword">const</span> plumber = &lt;A&gt;<span class="hljs-function">(<span class="hljs-params">fn1: IdType&lt;A&gt;, fn2: IdType&lt;A&gt;</span>) =&gt;</span> (x: A) =&gt; fn2(fn1(x));

<span class="hljs-comment">// since plumber needs two functions to form the pipeline, we need something to start with the</span>
<span class="hljs-comment">// first function in the array and that something is the id function.</span>
<span class="hljs-keyword">const</span> idNumber: IdType&lt;<span class="hljs-built_in">number</span>&gt; = <span class="hljs-function"><span class="hljs-params">x</span> =&gt;</span> x; <span class="hljs-comment">// id function for number type</span>

<span class="hljs-keyword">let</span> acc = idNumber;

<span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> fn <span class="hljs-keyword">of</span> fns) {
    acc = plumber(acc, fn);
}

assert.equal(acc(<span class="hljs-number">1</span>), <span class="hljs-number">24</span>); <span class="hljs-comment">// acc is the final pipe function</span>

<span class="hljs-comment">// pipe([f1, f2, f3])(x) = f3(f2((f1(x))))</span>
<span class="hljs-keyword">const</span> pipe = &lt;A&gt;<span class="hljs-function">(<span class="hljs-params">fns: <span class="hljs-built_in">Array</span>&lt;IdType&lt;A&gt;&gt;</span>) =&gt;</span> foldl(<span class="hljs-function">(<span class="hljs-params">fn, acc</span>) =&gt;</span> x =&gt; acc(fn(x)), <span class="hljs-function">(<span class="hljs-params">x: A</span>) =&gt;</span> x, fns);

<span class="hljs-keyword">const</span> half = <span class="hljs-function">(<span class="hljs-params">x: <span class="hljs-built_in">number</span></span>) =&gt;</span> x / <span class="hljs-number">2</span>;
<span class="hljs-keyword">const</span> third = <span class="hljs-function">(<span class="hljs-params">x: <span class="hljs-built_in">number</span></span>) =&gt;</span> x / <span class="hljs-number">3</span>;
<span class="hljs-keyword">const</span> tenTimes = <span class="hljs-function">(<span class="hljs-params">x: <span class="hljs-built_in">number</span></span>) =&gt;</span> x * <span class="hljs-number">10</span>;

<span class="hljs-keyword">const</span> pipeline = pipe([half, third, tenTimes]);
<span class="hljs-comment">// this is equivalent to tenTimes(third(half(24))) === 40</span>
assert.equal(pipeline(<span class="hljs-number">24</span>), tenTimes(third(half(<span class="hljs-number">24</span>))));

<span class="hljs-comment">/* compose: compose([f1, f2, f3])(x) = f1(f2((f3(x)))) */</span>
<span class="hljs-keyword">const</span> compose = &lt;A&gt;<span class="hljs-function">(<span class="hljs-params">fns: <span class="hljs-built_in">Array</span>&lt;IdType&lt;A&gt;&gt;</span>) =&gt;</span> foldr(<span class="hljs-function">(<span class="hljs-params">fn, acc</span>) =&gt;</span> x =&gt; fn(acc(x)), <span class="hljs-function">(<span class="hljs-params">x: A</span>) =&gt;</span> x, fns);

<span class="hljs-keyword">const</span> plusOne: IdType&lt;<span class="hljs-built_in">number</span>&gt; = <span class="hljs-function"><span class="hljs-params">x</span> =&gt;</span> x + <span class="hljs-number">1</span>;
<span class="hljs-comment">// or add type to the parameter to conform to IdType&lt;number&gt;</span>
<span class="hljs-keyword">const</span> fiveTimes = <span class="hljs-function">(<span class="hljs-params">x: <span class="hljs-built_in">number</span></span>) =&gt;</span> x * <span class="hljs-number">5</span>;

<span class="hljs-keyword">const</span> composition = compose([plusOne, fiveTimes]);
<span class="hljs-comment">// this is equivalent to plusOne(fiveTimes(10)) === 51</span>
assert.equal(composition(<span class="hljs-number">10</span>), plusOne(fiveTimes(<span class="hljs-number">10</span>)));
</code></pre>
<p>That's it for today. Happy coding 💻!</p>
]]></content:encoded></item><item><title><![CDATA[Kubernetes services and Azure load balancers]]></title><description><![CDATA[Azure Kubernetes Service (AKS)  makes use of many core Azure resources to provide the necessary functionality. In this post, we'll take a look at how Kubernetes LoadBalancer services and Ingress are mapped to an Azure Load Balancer.
Cluster creation
...]]></description><link>https://blog.omkarpatil.dev/kubernetes-services-and-azure-load-balancers</link><guid isPermaLink="true">https://blog.omkarpatil.dev/kubernetes-services-and-azure-load-balancers</guid><category><![CDATA[Azure]]></category><category><![CDATA[Kubernetes]]></category><dc:creator><![CDATA[Omkar Patil]]></dc:creator><pubDate>Sun, 05 Sep 2021 15:51:06 GMT</pubDate><content:encoded><![CDATA[<p> <a target="_blank" href="https://azure.microsoft.com/en-us/services/kubernetes-service/">Azure Kubernetes Service (AKS)</a>  makes use of many core Azure resources to provide the necessary functionality. In this post, we'll take a look at how Kubernetes LoadBalancer services and Ingress are mapped to an <a target="_blank" href="https://docs.microsoft.com/en-us/azure/load-balancer/load-balancer-overview">Azure Load Balancer</a>.</p>
<h3 id="cluster-creation">Cluster creation</h3>
<p>During the AKS cluster creation process, a public load balancer is created in the infrastructure resource group of the cluster.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630850763847/jYdOTX2E1.png" alt="01-create-cluster.png" /></p>
<p>You can see the load balancer and its public IP address in the resources list of the infrastructure resource group.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630851618972/u7kRh3rHE.png" alt="02-infrastructure-rg.png" /></p>
<p>If we look at the IP address details, we can see it is being used to provide outbound connectivity from the cluster.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630851737177/07D30SaL0.png" alt="04-lb-1ip-rule.png" /></p>
<h3 id="kubernetes-loadbalancer-services">Kubernetes LoadBalancer services</h3>
<p>Now let's create a simple Kubernetes LoadBalancer service.</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">public-svc</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">type:</span> <span class="hljs-string">LoadBalancer</span>
  <span class="hljs-attr">ports:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">port:</span> <span class="hljs-number">80</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">public-app</span>
</code></pre>
<p>The service gets a new public IP address.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630852308578/a45sWsZ6s.png" alt="05-lb-service.png" /></p>
<p>And the new address is associated with the existing load balancer.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630852784854/-34kEEEwS.png" alt="06-lb-2ip.png" /></p>
<p>A look at the rule associated with this address tells us that it manages the inbound traffic to the service.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630853042669/rjZcNghzl.png" alt="07-lb-2ip-rule.png" /></p>
<h3 id="ingress-controllers">Ingress controllers</h3>
<p>Now let's see how an Ingress controller is associated with a load balancer.</p>
<p>I created a separate public IP address (<code>20.81.68.54</code>) in the infrastructure resource group to be used for the Ingress controller. </p>
<p>Let's deploy a NginX Ingress controller using Helm.</p>
<pre><code class="lang-bash">helm install nginx-ingress ingress-nginx/ingress-nginx \
  --create-namespace \
  --namespace ingress-ns \
  --<span class="hljs-built_in">set</span> controller.replicaCount=2 \
  --<span class="hljs-built_in">set</span> controller.ingressClass=nginx \
  --<span class="hljs-built_in">set</span> controller.nodeSelector.<span class="hljs-string">"beta\.kubernetes\.io/os"</span>=linux \
  --<span class="hljs-built_in">set</span> defaultBackend.nodeSelector.<span class="hljs-string">"beta\.kubernetes\.io/os"</span>=linux \
  --<span class="hljs-built_in">set</span> controller.admissionWebhooks.patch.nodeSelector.<span class="hljs-string">"beta\.kubernetes\.io/os"</span>=linux \
  --<span class="hljs-built_in">set</span> controller.service.loadBalancerIP=<span class="hljs-string">"20.81.68.54"</span>
</code></pre>
<p>We can verify the public IP address has been associated with the Ingress controller.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630853880740/zh4XXjHU6.png" alt="09-nginx-ingress.png" /></p>
<p>Now if we check the load balancer again, we can see it manages the traffic for the Ingress IP address too.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630854002195/6HsxueCT9.png" alt="10-lb-3ip.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630854071461/ogvhOL0E-.png" alt="11-lb-ip-list.png" /></p>
<p>The rules associated with this IP address show that it manages the inbound <em>http</em> and <em>https</em> traffic to the ingress controller.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630854211133/Tr60crt5o.png" alt="12-lb-ingress-ip-rules.png" /></p>
<h3 id="using-internal-load-balancer">Using internal load balancer</h3>
<p>So far, we have been making services available publicly. To restrict access to services to the same virtual network as the AKS cluster, we can make use of an internal load balancer.</p>
<p>Let's create a new Ingress controller which will use an internal load balancer.</p>
<pre><code class="lang-sh">helm install nginx-ingress ingress-nginx/ingress-nginx \
  --create-namespace \
  --namespace ingress-ns-internal \
  --<span class="hljs-built_in">set</span> controller.replicaCount=2 \
  --<span class="hljs-built_in">set</span> controller.ingressClass=nginx-internal \
  --<span class="hljs-built_in">set</span> controller.nodeSelector.<span class="hljs-string">"beta\.kubernetes\.io/os"</span>=linux \
  --<span class="hljs-built_in">set</span> defaultBackend.nodeSelector.<span class="hljs-string">"beta\.kubernetes\.io/os"</span>=linux \
  --<span class="hljs-built_in">set</span> controller.admissionWebhooks.patch.nodeSelector.<span class="hljs-string">"beta\.kubernetes\.io/os"</span>=linux \
  --set-string controller.service.annotations.<span class="hljs-string">"service\.beta\.kubernetes\.io/azure-load-balancer-internal"</span>=<span class="hljs-string">"true"</span>
</code></pre>
<p>The annotation <code>service.beta.kubernetes.io/azure-load-balancer-internal"="true"</code> will result in creation of an internal load balancer in the infrastructure resource group of the cluster.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630854875156/A7FaNlUcH.png" alt="13-lb-internal.png" /></p>
<p>The internal load balancer gets a dynamic IP from the cluster subnet.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630855124380/GxTEFQbfR.png" alt="14-lb-internal-rules.png" /></p>
<h3 id="conclusion">Conclusion</h3>
<p>We get one public Azure load balancer for an AKS cluster and all the traffic on the public IP addresses associated with the Kubernetes LoadBalancer services and Ingress controllers is managed by it.</p>
<p>We can additionally create an internal load balancer to restrict traffic to the same virtual network as the AKS cluster.</p>
]]></content:encoded></item><item><title><![CDATA[Exploring linux underpinnings of containers]]></title><description><![CDATA[Some time ago, I played around linux primitives that power containers and documented my learnings on Github. Hope it proves useful to someone trying to do the same.
https://github.com/shikshan/containers]]></description><link>https://blog.omkarpatil.dev/exploring-linux-underpinnings-of-containers</link><guid isPermaLink="true">https://blog.omkarpatil.dev/exploring-linux-underpinnings-of-containers</guid><category><![CDATA[Linux]]></category><category><![CDATA[Docker]]></category><category><![CDATA[containers]]></category><dc:creator><![CDATA[Omkar Patil]]></dc:creator><pubDate>Tue, 31 Aug 2021 22:02:25 GMT</pubDate><content:encoded><![CDATA[<p>Some time ago, I played around linux primitives that power containers and documented my learnings on Github. Hope it proves useful to someone trying to do the same.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/shikshan/containers">https://github.com/shikshan/containers</a></div>
]]></content:encoded></item><item><title><![CDATA[Two node Kafka development cluster using docker]]></title><description><![CDATA[Here is a docker-compose yaml file to start a two Kafka cluster and kafdrop ui for a quick local development setup.
# STARTING UP: docker-compose up --remove-orphans -d
# STOP: docker-compose down
# CHECK LOGS: docker-compose logs -f -t
version: '3'
...]]></description><link>https://blog.omkarpatil.dev/two-node-kafka-development-cluster-using-docker</link><guid isPermaLink="true">https://blog.omkarpatil.dev/two-node-kafka-development-cluster-using-docker</guid><category><![CDATA[kafka]]></category><category><![CDATA[Docker]]></category><dc:creator><![CDATA[Omkar Patil]]></dc:creator><pubDate>Tue, 31 Aug 2021 01:30:10 GMT</pubDate><content:encoded><![CDATA[<p>Here is a docker-compose yaml file to start a two Kafka cluster and <a target="_blank" href="https://github.com/obsidiandynamics/kafdrop">kafdrop ui</a> for a quick local development setup.</p>
<pre><code class="lang-yml"><span class="hljs-comment"># STARTING UP: docker-compose up --remove-orphans -d</span>
<span class="hljs-comment"># STOP: docker-compose down</span>
<span class="hljs-comment"># CHECK LOGS: docker-compose logs -f -t</span>
<span class="hljs-attr">version:</span> <span class="hljs-string">'3'</span>

<span class="hljs-attr">networks:</span>
  <span class="hljs-attr">kafka-net:</span>
    <span class="hljs-attr">driver:</span> <span class="hljs-string">bridge</span>

<span class="hljs-attr">services:</span>
  <span class="hljs-attr">zookeeper:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">bitnami/zookeeper</span>
    <span class="hljs-attr">networks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">kafka-net</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'2181:2181'</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">ALLOW_ANONYMOUS_LOGIN=yes</span>
    <span class="hljs-comment"># volumes:</span>
    <span class="hljs-comment">#   - ./data/zookeeper/data:/data</span>
    <span class="hljs-comment">#   - ./data/zookeeper/datalog:/datalog</span>
  <span class="hljs-attr">kafka1:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">bitnami/kafka</span>
    <span class="hljs-attr">networks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">kafka-net</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'9091:9091'</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KAFKA_CFG_BROKER_ID=1</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">ALLOW_PLAINTEXT_LISTENER=yes</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KAFKA_CFG_LISTENERS=INTERNAL://:19091,EXTERNAL://:9091</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KAFKA_CFG_ADVERTISED_LISTENERS=INTERNAL://kafka1:19091,EXTERNAL://localhost:9091</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KAFKA_CFG_INTER_BROKER_LISTENER_NAME=INTERNAL</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true</span>
    <span class="hljs-comment"># volumes:</span>
    <span class="hljs-comment">#   - ./data/kafka1/data:/var/lib/kafka/data</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">zookeeper</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">always</span>
  <span class="hljs-attr">kafka2:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">bitnami/kafka</span>
    <span class="hljs-attr">networks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">kafka-net</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'9092:9092'</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KAFKA_CFG_BROKER_ID=2</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">ALLOW_PLAINTEXT_LISTENER=yes</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KAFKA_CFG_LISTENERS=INTERNAL://:19092,EXTERNAL://:9092</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KAFKA_CFG_ADVERTISED_LISTENERS=INTERNAL://kafka2:19092,EXTERNAL://localhost:9092</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KAFKA_CFG_INTER_BROKER_LISTENER_NAME=INTERNAL</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true</span>
    <span class="hljs-comment"># volumes:</span>
    <span class="hljs-comment">#   - ./data/kafka2/data:/var/lib/kafka/data</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">zookeeper</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">always</span>
  <span class="hljs-attr">kafdrop:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">obsidiandynamics/kafdrop</span>
    <span class="hljs-attr">networks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">kafka-net</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">'no'</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'9000:9000'</span>
    <span class="hljs-attr">environment:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">KAFKA_BROKERCONNECT=kafka1:19091,kafka2:19092</span>
    <span class="hljs-attr">depends_on:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">kafka1</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">kafka2</span>
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Setting up a Kubernetes cluster in Azure]]></title><description><![CDATA[All the major cloud providers provide managed Kubernetes services these days that are an apt choice for production environments. I was curious about the mechanics of cluster setup and therefore created a tiny two node cluster in Azure using  Kubeadm ...]]></description><link>https://blog.omkarpatil.dev/setting-up-a-kubernetes-cluster-in-azure</link><guid isPermaLink="true">https://blog.omkarpatil.dev/setting-up-a-kubernetes-cluster-in-azure</guid><category><![CDATA[Kubernetes]]></category><category><![CDATA[Azure]]></category><dc:creator><![CDATA[Omkar Patil]]></dc:creator><pubDate>Fri, 27 Aug 2021 23:47:20 GMT</pubDate><content:encoded><![CDATA[<p>All the major cloud providers provide managed Kubernetes services these days that are an apt choice for production environments. I was curious about the mechanics of cluster setup and therefore created a tiny two node cluster in Azure using  <a target="_blank" href="https://kubernetes.io/docs/reference/setup-tools/kubeadm/">Kubeadm</a> tool just for learning purpose. While the authoritative source of information is of course <a target="_blank" href="https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/">Kubernetes documentation</a>, here are some quick notes:</p>
<h2 id="creating-vms-in-azure">Creating VMs in Azure</h2>
<ul>
<li>It's a good idea to create all Azure resources under one <em>Resource Group</em>. You can then delete all of those in one go once you are done by deleting the RG.</li>
<li><p>Creating a SSH key in Azure and using it to log into different VMs makes life so much easier. Here is how you can do it. Create it with a name of your choice (<em>azureuser</em> in this post) as shown in the screenshot below:<br />  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630101985235/Oi7-9-D7ud.png" alt="ssh-key.png" /></p>
<p>  After creation, Azure will prompt for saving the generated private key file. Download it and keep it at a known location on your machine, e.g. <code>~/.ssh/azureuser.pem</code>. Change the permissions - <code>chmod 400 azureuser.pem</code>. </p>
</li>
<li>Create a virtual network with required address range, for example: <code>10.100.0.0/24</code> (<code>10.100.0.0</code> - <code>10.100.0.255</code>). <a target="_blank" href="https://test53.com/">IPv4 CIDR Calculator</a> is a handy tool to calculate CIDR IP range.</li>
<li>Adjust the <code>default</code> subnet IP range under the virtual network to <code>10.100.0.0/25</code> (<code>10.100.0.0 - 10.100.0.127</code>). We'll use this subnet for the Kubernetes cluster.</li>
<li>Create a <em>Network Security Group</em> and associate it with the <em>default</em> subnet. The inbound and outbound rules provided out-of-the-box are good enough.</li>
<li>I always use bastion service to avoid exposing VMs to internet. You can either associate an existing bastion or create a new one while creating a VNET. In Azure portal, a new bastion can be created while creating a VNET from the <em>Security</em> tab as shown in the screenshot below:
  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630079555504/cqhalXlu6.png" alt="bastion.png" /><ul>
<li>Give the bastion a name of your choice.</li>
<li>Use <code>10.100.0.128/27</code> <code>(10.100.0.128 - 10.100.0.159)</code> as <code>AzureBastionSubnet</code> address space. Azure requires the exact name <code>AzureBastionSubnet</code> for the subnet to be used for the bastion.</li>
<li>Select <em>Create New</em> for the <em>Public IP address</em> field and give a name to it.</li>
</ul>
</li>
<li>Create two virtual machines: one to be used as Kubernetes master node and the other as a worker node.<ul>
<li>Use the latest Ubuntu Server LTS image.</li>
<li><em>Standard_B2ms</em> size with 2 vcpus, 8 GiB memory and 30 GiB disks will suffice for our learner's cluster.</li>
<li>Use <em>SSH public key authentication</em> option with <em>azureuser</em> as username and use the SSH key created earlier as value for <em>Use existing key stored in Azure</em> option.</li>
<li>Select <em>None</em> for Public inbound ports.</li>
<li>Use the <em>virtual network</em> and <em>default</em> subnet created earlier in the networking options. Set <em>Public IP</em> to <em>None</em>. Select <em>None</em> for NIC network security group.</li>
</ul>
</li>
<li>Start the VMs and connect using <em>Bastion</em> option. Use <em>azureuser</em> as username, <em>SSH Private Key from Local File</em> as <em>Authentication Type</em> and select the previously saved <em>pem</em> file through <em>Local File</em> option.</li>
</ul>
<h2 id="setting-up-kubernetes-cluster">Setting up Kubernetes cluster</h2>
<h3 id="installations">Installations</h3>
<p>Install the required software on both <em>master</em> and <em>worker</em> node VMs.</p>
<p>Install container runtime. We'll use <em>CRI-O</em>.</p>
<pre><code class="lang-bash">cat &lt;&lt;EOF | sudo tee /etc/modules-load.d/containerd.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

<span class="hljs-comment"># Setup required sysctl params, these persist across reboots.</span>
cat &lt;&lt;EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf
net.bridge.bridge-nf-call-iptables  = 1
net.ipv4.ip_forward                 = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF

<span class="hljs-comment"># Apply sysctl params without reboot</span>
sudo sysctl --system

<span class="hljs-comment"># Install CRI-O</span>
OS=xUbuntu_20.04
VERSION=1.22
cat &lt;&lt;EOF | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/<span class="hljs-variable">$OS</span>/ /
EOF
cat &lt;&lt;EOF | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable:cri-o:<span class="hljs-variable">$VERSION</span>.list
deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable:/cri-o:/<span class="hljs-variable">$VERSION</span>/<span class="hljs-variable">$OS</span>/ /
EOF

curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/<span class="hljs-variable">$OS</span>/Release.key | sudo apt-key --keyring /etc/apt/trusted.gpg.d/libcontainers.gpg add -
curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/<span class="hljs-variable">$OS</span>/Release.key | sudo apt-key --keyring /etc/apt/trusted.gpg.d/libcontainers.gpg add -

sudo apt-get update
sudo apt-get install cri-o cri-o-runc

<span class="hljs-comment"># Start CRI-O</span>
sudo systemctl daemon-reload
sudo systemctl <span class="hljs-built_in">enable</span> crio --now
</code></pre>
<p>Install Kubernetes packages.</p>
<pre><code class="lang-bash">sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl
<span class="hljs-comment"># Download the Google Cloud public signing key</span>
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg

<span class="hljs-comment"># Add the Kubernetes apt repository</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main"</span> | sudo tee /etc/apt/sources.list.d/kubernetes.list

sudo apt-get update
<span class="hljs-comment"># install kubeadm, kubelet, and kubectl</span>
sudo apt-get install -y kubelet kubeadm kubectl
<span class="hljs-comment"># Pin the installed packages at their installed versions</span>
sudo apt-mark hold kubelet kubeadm kubectl
</code></pre>
<h3 id="create-a-cluster">Create a cluster</h3>
<p>Run the following steps on <strong>master node VM</strong>.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Make sure that your Pod network does not overlap with any of the host networks</span>
sudo kubeadm init --pod-network-cidr 192.168.0.0/16

<span class="hljs-comment"># Copy the join command printed in the output. We'll need it later on worker.</span>

mkdir -p <span class="hljs-variable">$HOME</span>/.kube
sudo cp -i /etc/kubernetes/admin.conf <span class="hljs-variable">$HOME</span>/.kube/config
sudo chown $(id -u):$(id -g) <span class="hljs-variable">$HOME</span>/.kube/config

<span class="hljs-comment"># Use Calico networking plugin</span>
kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml
</code></pre>
<p>Confirm the master node is running: <code>kubectl get node</code>.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630097884260/ohtRrdCiR.png" alt="master-ready.png" /></p>
<p>Run the following commands on <strong>worker node VM</strong>.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Run the join command coped from master</span>
sudo kubeadm join 10.100.0.4:6443 --token w5ukck.qhuw0s86gd7dsxv5 --discovery-token-ca-cert-hash sha256:31401ee3712a958829d846cf9d1417325f9c1508a8113549ef1a41a7ce2eee7d
</code></pre>
<blockquote>
<p>If you forget to copy the join command, it can be regenerated on the <strong>master node</strong> using: <code>kubeadm token create --print-join-command</code>.</p>
</blockquote>
<p>Verify that the worker has joined the cluster by running <code>kubectl get nodes</code> again on <strong>master</strong>.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1630098684251/2YSLdNIW_B.png" alt="master-worker.png" /></p>
<p>To stop the cluster, stop the worker node first followed by master node and other way round while starting up.</p>
<p>That's all for today. Happy coding! À bientôt 🙋‍♂️! </p>
]]></content:encoded></item><item><title><![CDATA[Protocol translation in NestJS microservices]]></title><description><![CDATA[Making async microservices talk to clients that use "standard" web-friendly API protocols (think mobile and web applications) requires protocol translation. Here is a very simple idea, inspired by nats proxy project, on how to achieve it in NestJS wi...]]></description><link>https://blog.omkarpatil.dev/protocol-translation-in-nestjs-microservices</link><guid isPermaLink="true">https://blog.omkarpatil.dev/protocol-translation-in-nestjs-microservices</guid><category><![CDATA[Node.js]]></category><category><![CDATA[Microservices]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[nest]]></category><dc:creator><![CDATA[Omkar Patil]]></dc:creator><pubDate>Thu, 19 Aug 2021 20:31:06 GMT</pubDate><content:encoded><![CDATA[<p>Making async microservices talk to clients that use "standard" web-friendly API protocols (think mobile and web applications) requires <a target="_blank" href="https://microservices.io/patterns/apigateway.html">protocol translation</a>. Here is a very simple idea, inspired by <a target="_blank" href="https://nats.io/blog/natsproxy_project/">nats proxy project</a>, on how to achieve it in <a target="_blank" href="https://nestjs.com/">NestJS</a> with <a target="_blank" href="https://nats.io/">NATS</a> microservice transport.</p>
<p>The complete source code for this article is available on <a target="_blank" href="https://github.com/ospatil/protocol-translator.git">Github</a>. The README provides the setup and usage instructions as well as main files to look at. It's a <a target="_blank" href="https://nx.dev/">Nx Monorepo</a> that contains the following two applications:</p>
<ol>
<li><strong>Gateway</strong>: This application exposes REST interface to clients, receives HTTP requests, dynamically forms the NATS subject name to send message to based on incoming request verb and path, sends the message to the subject, waits for NATS response from microservice and once received, sends out HTTP response back to client.</li>
<li><strong>Microservice</strong>: This application listens on NATS subject and responds with a string message when invoked.</li>
</ol>
<p>The overall interaction looks like this:
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629402269532/thQa21ZjB.png" alt="protocol-translator.png" /></p>
<p>That's it for today. Happy coding! À bientôt 🙋‍♂️! </p>
]]></content:encoded></item></channel></rss>