<?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" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Blog]]></title><description><![CDATA[Tech, Science and Everything Else]]></description><link>https://blog.suraj-mittal.dev/</link><image><url>https://blog.suraj-mittal.dev/favicon.png</url><title>Blog</title><link>https://blog.suraj-mittal.dev/</link></image><generator>Ghost 5.76</generator><lastBuildDate>Mon, 06 Apr 2026 02:47:48 GMT</lastBuildDate><atom:link href="https://blog.suraj-mittal.dev/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Fixing timeout errors on code running on AWS]]></title><description><![CDATA[Fixing connection hanging on code running on instances behind AWS NAT Gateway]]></description><link>https://blog.suraj-mittal.dev/connection-hangs-in-aws-nat-gateway/</link><guid isPermaLink="false">6596dbcad5fdf3363956265e</guid><category><![CDATA[aws]]></category><category><![CDATA[api]]></category><category><![CDATA[cloud]]></category><category><![CDATA[docker]]></category><category><![CDATA[linux]]></category><dc:creator><![CDATA[Suraj Mittal]]></dc:creator><pubDate>Thu, 04 Jan 2024 16:51:38 GMT</pubDate><content:encoded><![CDATA[<p>The other day, i was running my code in an AWS EKS cluster which was behind a NAT gateway and the code which was running locally perfectly started exhibited weird behavior. </p><p>To give an overview of what the code was doing, it was making an API Post request which took more than 5-6 mins in some cases to return a response. So, as a sane thing to do, we added a request timeout of 10 mins. This is how the sample code looked.</p><pre><code class="language-python">from httpx import AsyncClient
async with AsyncClient(base_url=&quot;https://the-called-service&quot;, timeout=600) as client:
	_ = await client.post(&quot;/a_long_open_connection&quot;)</code></pre><p>Assuming the connection returns in 4-5 mins, we should be seeing successes. But on the cluster, the requests were getting timed out after 600 seconds. </p><p>Now this was weird, as AWS has a pretty reliable network and considering that this was running quite nicely on my shitty home internet, something was wrong.</p><p>After a bit of searching i found this on <a href="https://docs.aws.amazon.com/vpc/latest/userguide/nat-gateway-troubleshooting.html?ref=blog.suraj-mittal.dev">AWS page</a>. </p><blockquote><strong>Internet</strong> <strong>Connection</strong> <strong>Drops</strong> <strong>after</strong> <strong>350 seconds</strong><br><br>Problem<br>Your instances can access the internet, but the connection drops after 350 seconds.<br><br>Cause<br>If a connection that&apos;s using a NAT gateway is idle for 350 seconds or more, the connection times out.<br>When a connection times out, a NAT gateway returns an RST packet to any resources behind the NAT gateway that attempt to continue the connection (it does not send a FIN packet).<br><br>Solution<br>To prevent the connection from being dropped, you can initiate more traffic over the connection. Alternatively, you can enable TCP keepalive on the instance with a value less than 350 seconds.</blockquote><p>By default linux has a keep alive timeout of 2 hours and this is well less than that. As to why AWS did it, i am not sure, and considering this, the code is running on EKS cluster which i have no control over. We needed a way to fix this. </p><p>So, how do we keep alive this connection. Well, lets just send periodic calls to some other endpoint which returns instantaneously.</p><p>So, we write a periodic async wrapper like so. &#xA0;</p><pre><code class="language-python">from __future__ import annotations

import asyncio
import logging
from asyncio import ensure_future
from functools import wraps
from traceback import format_exception
from typing import Any, Callable, Coroutine, Union

from starlette.concurrency import run_in_threadpool

NoArgsNoReturnFuncT = Callable[[], None]
NoArgsNoReturnAsyncFuncT = Callable[[], Coroutine[Any, Any, None]]
NoArgsNoReturnDecorator = Callable[[Union[NoArgsNoReturnFuncT, NoArgsNoReturnAsyncFuncT]], NoArgsNoReturnAsyncFuncT]


def repeat_every(
        *,
        seconds: float,
        wait_first: bool = False,
        logger: logging.Logger | None = None,
        raise_exceptions: bool = False,
        max_repetitions: int | None = None,
) -&gt; NoArgsNoReturnDecorator:
    &quot;&quot;&quot;
    This function returns a decorator that modifies a function, so it is periodically re-executed after its first call.

    The function it decorates should accept no arguments and return nothing. If necessary, this can be accomplished
    by using `functools.partial` or otherwise wrapping the target function prior to decoration.

    Parameters
    ----------
    seconds: float
        The number of seconds to wait between repeated calls
    wait_first: bool (default False)
        If True, the function will wait for a single period before the first call
    logger: Optional[logging.Logger] (default None)
        The logger to use to log any exceptions raised by calls to the decorated function.
        If not provided, exceptions will not be logged by this function (though they may be handled by the event loop).
    raise_exceptions: bool (default False)
        If True, errors raised by the decorated function will be raised to the event loop&apos;s exception handler.
        Note that if an error is raised, the repeated execution will stop.
        Otherwise, exceptions are just logged and the execution continues to repeat.
        See https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.set_exception_handler for more info.
    max_repetitions: Optional[int] (default None)
        The maximum number of times to call the repeated function. If `None`, the function is repeated forever.
    &quot;&quot;&quot;

    def decorator(func: NoArgsNoReturnAsyncFuncT | NoArgsNoReturnFuncT) -&gt; NoArgsNoReturnAsyncFuncT:
        &quot;&quot;&quot;
        Converts the decorated function into a repeated, periodically-called version of itself.
        &quot;&quot;&quot;
        is_coroutine = asyncio.iscoroutinefunction(func)

        @wraps(func)
        async def wrapped() -&gt; None:
            repetitions = 0

            async def loop() -&gt; None:
                nonlocal repetitions
                if wait_first:
                    await asyncio.sleep(seconds)
                while max_repetitions is None or repetitions &lt; max_repetitions:
                    try:
                        if is_coroutine:
                            await func()  # type: ignore
                        else:
                            await run_in_threadpool(func)
                        repetitions += 1
                    except Exception as exc:
                        if logger is not None:
                            formatted_exception = &quot;&quot;.join(format_exception(type(exc), exc, exc.__traceback__))
                            logger.error(formatted_exception)
                        if raise_exceptions:
                            raise exc
                    await asyncio.sleep(seconds)

            ensure_future(loop())

        return wrapped

    return decorator</code></pre><p>Thanks to the awesome package called fastapi-utils where this code is borrowed from. You can check the project out <a href="https://github.com/dmontagu/fastapi-utils?ref=blog.suraj-mittal.dev">here</a>.</p><p>And then we modify our initial code like so.</p><pre><code class="language-python">from httpx import AsyncClient
import asyncio

async with AsyncClient(base_url=&quot;https://the-called-service&quot;, timeout=600) as client:
	@repeat_every(seconds=60)
	async def continuous_poll():
    		_ = await client.get(&quot;/healthcheck&quot;)	
	
_ = asyncio.create_task(continuous_poll())
_ = await client.post(&quot;/a_long_open_connection&quot;)
    </code></pre><p>And viola it now works. </p><p>Thats for all. Thanks for reading. </p>]]></content:encoded></item><item><title><![CDATA[Kibana Best Practices]]></title><description><![CDATA[Description of some of the best practices to setup Kibana clusters]]></description><link>https://blog.suraj-mittal.dev/kibana-best-practices/</link><guid isPermaLink="false">638b38b3a82d211dae7b44c5</guid><category><![CDATA[cloud]]></category><category><![CDATA[aws]]></category><category><![CDATA[kibana]]></category><category><![CDATA[search]]></category><category><![CDATA[Import 2022-12-03 11:53]]></category><dc:creator><![CDATA[Suraj Mittal]]></dc:creator><pubDate>Fri, 18 Mar 2022 11:17:21 GMT</pubDate><content:encoded><![CDATA[<ol><li>Your cluster should have greater than 20% of available storage space, or greater than 20 GB of storage space, otherwise basic write operations like adding documents and creating indexes can start to fail. This can be checked by <code>GET _cat/allocation?v</code> in the Kibana Dev Tools</li><li>A single shard should ideally be between 10-50GB.</li><li>Large shards can make it difficult to recover from failure, but because each shard uses some amount of CPU and memory, having too many small shards can cause performance issues and out of memory errors. In other words, shards should be small enough that the underlying instance can handle them, but not so small that they place needless strain on the hardware.<br>You can check the indices and their shard counts by running &#xA0;<code>GET _cat/indices?v</code> in the Kibana Dev Tools</li><li>When JVM memory pressure is high, garbage collection takes place more frequently and this is a CPU intensive process. The high CPU utilisation also causes search rejections as the cluster was under high strain.</li></ol><h3 id="potential-solutions">Potential Solutions</h3><ol><li>Shard count can be reduced by deleting or closing indices or by re-indexing into bigger indices, refer <a href="https://opster.com/guides/elasticsearch/operations/elasticsearch-oversharding/?ref=blog.suraj-mittal.dev">link</a> and <a href="https://docs.aws.amazon.com/opensearch-service/latest/developerguide/sizing-domains.html?ref=blog.suraj-mittal.dev#bp-sharding">link</a></li><li>Finally we can also delete old/unnecessary indices to free up space and increase performance and then update the sharding strategy</li></ol>]]></content:encoded></item><item><title><![CDATA[Moving the WSL2 vhdx]]></title><description><![CDATA[WSL2 takes up a lot of space. Here is show how to move it to a different directory or path]]></description><link>https://blog.suraj-mittal.dev/moving-the-wsl2-vhdx/</link><guid isPermaLink="false">638b38b3a82d211dae7b44c1</guid><category><![CDATA[wsl2]]></category><category><![CDATA[windows]]></category><category><![CDATA[linx]]></category><category><![CDATA[docker]]></category><category><![CDATA[Import 2022-12-03 11:53]]></category><dc:creator><![CDATA[Suraj Mittal]]></dc:creator><pubDate>Mon, 20 Jul 2020 07:27:57 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>There are a lot of chances that your WSL2 environment size is going to blow up. It is very easy for it to reach 100GB in size. In these cases it is better to move the vhdx file from the system drive to an external ssd or some other non system drive.</p>
<p>Although there are commands provided by WSL2 for these purposes with the flags <code>--import</code> and <code>--export</code>, these are not exactly stable.</p>
<p>The <code>--import</code> command has a bug wherein it needs ram, the size of vhdx to be imported correctly. Now obviously most of us dont have huge amounts of ram lying around.</p>
<p>Im going to show how you can do this an easy way without spending time doing import/export and waiting for those operations to blow up.</p>
<ol>
<li>
<p>Shutdown wsl using <code>wsl --shutdown</code> command.</p>
</li>
<li>
<p>Find the path to your vhdx location. Refer <a href="https://blog.suraj-mittal.dev/reduce-wsl2-disk-filesize/">Here</a></p>
</li>
<li>
<p>Now copy this vhdx file to the place where you want to migrate to.</p>
</li>
<li>
<p>Create a file with extension <code>.reg</code> and add these contents <pre>Windows Registry Editor Version 5.00<br>
[HKEY_USERS\<b>SID</b>\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss\{<b>UUID</b>}]<br>
&quot;State&quot;=dword:00000001<br>
&quot;DistributionName&quot;=&quot;<b>distribution name</b>&quot;<br>
&quot;Version&quot;=dword:00000002<br>
&quot;BasePath&quot;=&quot;<b>vhdx folder path</b>&quot;<br>
&quot;Flags&quot;=dword:0000000f<br>
&quot;DefaultUid&quot;=dword:000003e8</pre></p>
<ul>
<li>the <em>vhdx folder path</em> should be in the following format <code>\\\\?\\E:\\WSL\\ubuntu-20</code></li>
<li><em>SID</em> can be found by typing the following in command prompt <code>whoami /USER</code></li>
<li><em>UUID</em> can be generated randomly using anything. i used this <a href="https://www.uuidgenerator.net/?ref=blog.suraj-mittal.dev">website</a></li>
<li><em>distribution name</em> can be any string identifier.</li>
</ul>
</li>
<li>
<p>Double click the file and let it add content to your registry.</p>
</li>
<li>
<p>Run <code>wsl --list</code> and you should be able to see the new wsl entry created.</p>
</li>
<li>
<p>Clear the older entry by using <code>wsl --unregister</code> command.</p>
</li>
</ol>
<blockquote>
<p>NOTE:</p>
<ol>
<li>If you get <em>permission denied error</em>, right click the <code>.vhdx</code> file, go the security and transfer the ownership of that file to the current user or the SID displayed above.</li>
<li>If the path in above format does not work, you can try the folowing format <code>\\?\E:\WSL\ubuntu-20\</code></li>
</ol>
</blockquote>
<p>Thanks for reading and i hope this helped some of you guys facing this problem.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[VSCode in Google Colab]]></title><description><![CDATA[How to use VSCode in Google Colab.]]></description><link>https://blog.suraj-mittal.dev/vscode-colab/</link><guid isPermaLink="false">638b38b3a82d211dae7b44c0</guid><category><![CDATA[cloud]]></category><category><![CDATA[datascience]]></category><category><![CDATA[linux]]></category><category><![CDATA[Import 2022-12-03 11:53]]></category><dc:creator><![CDATA[Suraj Mittal]]></dc:creator><pubDate>Fri, 19 Jun 2020 09:28:32 GMT</pubDate><content:encoded><![CDATA[<p>VSCode is an amazing editor and complementing its functionality with free python runtime service provided by google colab can enable a lot of students who do not have high performance laptops to use the best of both worlds. </p><p>To use colab with vscode, first run the below code inside a colab notebook</p><figure class="kg-card kg-code-card"><pre><code class="language-python">import random, string
password = &apos;&apos;.join(random.choice(string.ascii_letters + string.digits) for i in range(20))

#Download ngrok
! wget -q -c -nc https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip
! unzip -qq -n ngrok-stable-linux-amd64.zip
#Setup sshd
! apt-get install -qq -o=Dpkg::Use-Pty=0 openssh-server pwgen &gt; /dev/null
#Set root password
! echo root:$password | chpasswd
! mkdir -p /var/run/sshd
! echo &quot;PermitRootLogin yes&quot; &gt;&gt; /etc/ssh/sshd_config
! echo &quot;PasswordAuthentication yes&quot; &gt;&gt; /etc/ssh/sshd_config
! echo &quot;LD_LIBRARY_PATH=/usr/lib64-nvidia&quot; &gt;&gt; /root/.bashrc
! echo &quot;export LD_LIBRARY_PATH&quot; &gt;&gt; /root/.bashrc

#Run sshd
get_ipython().system_raw(&apos;/usr/sbin/sshd -D &amp;&apos;)

#Ask token
print(&quot;Copy authtoken from https://dashboard.ngrok.com/auth&quot;)
import getpass
authtoken = getpass.getpass()

#Create tunnel
get_ipython().system_raw(&apos;./ngrok authtoken $authtoken &amp;&amp; ./ngrok tcp 22 &amp;&apos;)
#Print root password
print(&quot;Root password: {}&quot;.format(password))
#Get public address
! curl -s http://localhost:4040/api/tunnels | python3 -c \
    &quot;import sys, json; print(json.load(sys.stdin)[&apos;tunnels&apos;][0][&apos;public_url&apos;])&quot;</code></pre><figcaption>cell.py</figcaption></figure><p>What we are essentially doing here is, using ngrok to enable ssh connections to this instance.</p><p>Now just go to your ngrok page and get the ip and port. the username is root and the password is displayed when the above code is run.</p><p>Now, you can just use these and connect via VSCode</p><blockquote>Disclaimer: The source code used above was from the following <a href="http://www.javaear.com/question/59508225.html?ref=blog.suraj-mittal.dev">source</a></blockquote>]]></content:encoded></item><item><title><![CDATA[Unbrick router via TFTP]]></title><description><![CDATA[<p>In this post, I am going to show how to unbrick your router which might be bricked due to a failed firmware upgrade or flashing another firmware like OpenWRT.</p><p>Most of the new generation routers ship with a restore mechanism via TFTP protocol and since TFTP is generally a part</p>]]></description><link>https://blog.suraj-mittal.dev/unbrick-router-via-tftp/</link><guid isPermaLink="false">638b38b3a82d211dae7b44bf</guid><category><![CDATA[networking]]></category><category><![CDATA[routers]]></category><category><![CDATA[windows]]></category><category><![CDATA[Import 2022-12-03 11:53]]></category><dc:creator><![CDATA[Suraj Mittal]]></dc:creator><pubDate>Sun, 17 May 2020 10:23:59 GMT</pubDate><content:encoded><![CDATA[<p>In this post, I am going to show how to unbrick your router which might be bricked due to a failed firmware upgrade or flashing another firmware like OpenWRT.</p><p>Most of the new generation routers ship with a restore mechanism via TFTP protocol and since TFTP is generally a part of the bootloader, it is almost always possible to restore a router using this method.</p><p>The idea behind TFTP restore is a simple one, </p><ol><li>The router needs to be started in TFTP client mode</li><li>Routers asks a TFTP server running at &lt;SOME_IP&gt; to send it a file named &#xA0;&lt;FILENAME.bin&gt;</li><li>If the server sends this file, router then uses this file to flash itself.</li></ol><p>So now that we have this cleared, lets get started. And yes, you will need to connect to the router via an ethernet on the router LAN port because wifi and other stuffs won&apos;t work.</p><h3 id="start-the-router-in-tftp-client-mode">Start the router in TFTP client mode</h3><p>Assuming that the router is connected to a computer via LAN cable, to start the router in this mode, we need to press a combination of hardware buttons on the router. In most of the router, this is holding the reset button and then pressing the power button. </p><p>You will see the networking adapter turn to unidentified network from unplugged cable mode.</p><h3 id="identifying-the-tftp-server-ip-and-filename">Identifying the TFTP server IP and Filename</h3><p>Now that we know how to start the router in TFTP mode, we need to know what IP and filename is the router looking at.</p><ol><li>To do this, we first download a wonderful network monitoring tool called <a href="https://www.wireshark.org/?ref=blog.suraj-mittal.dev">Wireshark</a>.<br>Download and install this tool, and then start monitoring the ethernet network adapter for your computer</li><li>Now restart the router in TFTP mode and you should start seeing some packets in Wireshark. What we are looking for are packets like this <code>Who has 192.168.0.100? Tell 192.168.0.66</code><br>Now what this packet says is that the router is looking for ip <code>192.168.0.100</code> as the TFTP server and that the ip of the router itself is <code>192.168.0.66</code><br>Bingo, we got out TFTP server IP.</li><li>Now set the static ip of your computer to <code>192.168.0.100</code> and netmask as <code>255.255.255.0</code> and restart the router in TFTP mode again. Dont turn off wireshark yet, since we need the filename and keep on monitoring the network adapter.</li><li>This time on wireshark, you should see something like <code>Read Request, File: recovery.bin</code><br>What this means is that router is asking the server to send it this file over TFTP if it has it.<br>Bingo, we got our filename too.</li></ol><h3 id="flashing-the-firmware">Flashing the firmware</h3><ol><li>Download and run TFTP server software. I personally use the portable edition of this <a href="https://tftpd32.jounin.net/tftpd32_download.html?ref=blog.suraj-mittal.dev">TFTP server</a>.</li><li>Download your router firmware file from the official website and rename it to the filename identified above and put it in a folder.</li><li>In the TFTP server software, point the directory to the directory where your renamed file is present, and for the server interface, select your LAN adapter with and make sure the IP is the one which you identified above.</li><li>Restart the router again in TFTP mode, and if everything goes right, the file transfer should start and once that is complete, give the router some time to flash and restore itself.</li></ol><p>Voila, you have restored your router via TFTP and unbricked itself.</p><p>Thanks for reading. I hope this guide was helpful<br></p>]]></content:encoded></item><item><title><![CDATA[Reduce WSL2 disk size]]></title><description><![CDATA[Windows 10 Pro/Enterprise 
optimize-vhd -Path <PATH-TO-VHDX> -Mode full
Windows 10 Home
select vdisk file="<PATH-TO-VHDX>"
attach vdisk readonly
compact vdisk
detach vdisk


]]></description><link>https://blog.suraj-mittal.dev/reduce-wsl2-disk-filesize/</link><guid isPermaLink="false">638b38b3a82d211dae7b44bb</guid><category><![CDATA[wsl2]]></category><category><![CDATA[windows]]></category><category><![CDATA[linux]]></category><category><![CDATA[Import 2022-12-03 11:53]]></category><dc:creator><![CDATA[Suraj Mittal]]></dc:creator><pubDate>Sat, 21 Mar 2020 10:18:47 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>WSL was good but it was not perfect for a linux dev running Windows 10. One reason was it was not fully compatible with linux. In the upcoming Windows 10 feature update, a new version of WSL is coming and it is called WSL2. It is optional and users can upgrade their exising WSL installations to WSL2.</p>
<p>Behind the scenes, WSL2 actually runs a lightweight VM. It boots up in seconds compared to what it would have taken for a normal VM to start.<br>
Running graphical applications is also simple by using an XServer like MobaXTerm.</p>
<p>Due to it running inside a VM, the file access methods have changed, and the Distro installation/files is actually inside of a virtual hard drive and to access the full benefits and speeds of linux file systems, we need to copy our files inside the virtual hard drive.</p>
<p>Now, this might seem okay, but the problem is that the size of the virtual hard drive keeps on increasing automatically and if you delete a file from the linux file system, the space occupied by the VHD is still the same.</p>
<p>We need a way to manually free up this space from Windows 10.</p>
<h3 id="windows10proenterprisemethod">Windows 10 Pro/Enterprise Method</h3>
<p>if you have Pro or enterprise, you can do the following</p>
<ul>
<li>Find the path of the VHD: To do this, enter the following in powershell</li>
</ul>
<pre><code>wsl --shutdown
Get-AppxPackage -Name &quot;*Distro Name*&quot; | Select PackageFamilyName

---
PackageFamilyName
-----------------
CanonicalGroupLimited.Ubuntu18.04onWindows_79rhkp1fndgsc

</code></pre>
<ul>
<li>The VHD path would now be</li>
</ul>
<pre><code>%USERPROFILE%\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu18.04onWindows_79rhkp1fndgsc\LocalState\ext4.vhdx
</code></pre>
<ul>
<li>Now run the powershell command</li>
</ul>
<pre><code>optimize-vhd -Path &lt;PATH-TO-VHDX&gt; -Mode full
</code></pre>
<h3 id="windows10home">Windows 10 Home</h3>
<p>Home version of windows 10 doesn&apos;t expose Hyper-V tooling kit. Therefore we need to use the old method of diskpart to reudce the size.</p>
<ul>
<li>Find the .vhdx file path</li>
<li>Run <code>diskpart</code> from command line</li>
<li>Run the following commands</li>
</ul>
<pre><code>select vdisk file=&quot;&lt;PATH-TO-VHDX&gt;&quot;
attach vdisk readonly
compact vdisk
detach vdisk
</code></pre>
<blockquote>
<h4 id="note">NOTE</h4>
<ul>
<li>You can run the command <code>sudo fstrim / </code> inside the linux distro before shutting it down to improve the saving</li>
<li>If reducing did not help you too much and you would like to move the data to a different drive, please refer the guide <a href="https://blog.suraj-mittal.dev/moving-the-wsl2-vhdx/">here</a></li>
</ul>
</blockquote>
<p>Thanks for reading</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Non Blocking Scheduled tasks in python]]></title><description><![CDATA[Code snippet for non blocking python scheduled task]]></description><link>https://blog.suraj-mittal.dev/non-blocking-scheduled-tasks-in-python/</link><guid isPermaLink="false">638b38b3a82d211dae7b44ba</guid><category><![CDATA[python]]></category><category><![CDATA[Import 2022-12-03 11:53]]></category><dc:creator><![CDATA[Suraj Mittal]]></dc:creator><pubDate>Fri, 20 Mar 2020 07:26:16 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>A lot of times we need to run some kind of automatic update scripts in our python api webserver.</p>
<p>One example of it might be automatically fetching and updating your machine learning models if they have been updated in a remote location with least disruption.</p>
<p>Also the automatic update task needs to be non blocking in nature.</p>
<p>Based on the above requirements, we can either use a threading or multiprocessing module to make it non blocking in nature</p>
<p>We use the threading module, because we require a access to shared variables of other object. Machine Learning model in this case.</p>
<p>Below is a scheduled task class which uses the power of Threading and Event api&apos;s to perform it.</p>
<pre><code class="language-python">import threading
import typing
from datetime import timedelta


class ScheduledTask(threading.Thread):
    def __init__(self, time_interval_seconds: int, scheduled_fn: typing.Callable, *args, **kwargs):
        threading.Thread.__init__(self)
        self.daemon = True
        self.stopped = threading.Event()
        self.time_interval_seconds = timedelta(seconds=time_interval_seconds)
        self.scheduled_fn = scheduled_fn
        self.args = args
        self.kwargs = kwargs

    def run(self):
        while not self.stopped.wait(self.time_interval_seconds.total_seconds()):
            self.scheduled_fn(*self.args, **self.kwargs)


class MachineLearningModel:
    def __init__(self):
        self.model = self._fetch_model()
        self.automatic_update_task = ScheduledTask(100, self._automatic_update_model)
        self.automatic_update_task.start()
    def _fetch_model(self):
        &quot;&quot;&quot;Code to fetch model from remote repo&quot;&quot;&quot;
        pass
    def _automatic_update_model(self):
        self.model = self._fetch_model()
    def predict(self,input):
        &quot;&quot;&quot;code to perform prediction&quot;&quot;&quot;
        pass


model_object = MachineLearningModel()
# model.predict()

</code></pre>
<p>Thats it.</p>
<p>Thanks for reading.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[MongoDB authentication]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>In this post, i try to show one of the ways of using authentication on standalone versions and replica set of MongoDb</p>
<h2 id="standaloneinstance">STANDALONE INSTANCE</h2>
<p>Connect to mongo shell on the mongodb instance and run.</p>
<pre><code class="language-javascript">use admin;
db.createUser(
    { 
        user: &quot;adminUser&quot;, 
        pwd: &quot;adminPassword&quot;, 
        roles: [
            { role: &quot;userAdminAnyDatabase&</code></pre>]]></description><link>https://blog.suraj-mittal.dev/mongodb-authentication/</link><guid isPermaLink="false">638b38b3a82d211dae7b44b7</guid><category><![CDATA[mongodb]]></category><category><![CDATA[docker]]></category><category><![CDATA[Import 2022-12-03 11:53]]></category><dc:creator><![CDATA[Suraj Mittal]]></dc:creator><pubDate>Sat, 15 Feb 2020 07:23:09 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>In this post, i try to show one of the ways of using authentication on standalone versions and replica set of MongoDb</p>
<h2 id="standaloneinstance">STANDALONE INSTANCE</h2>
<p>Connect to mongo shell on the mongodb instance and run.</p>
<pre><code class="language-javascript">use admin;
db.createUser(
    { 
        user: &quot;adminUser&quot;, 
        pwd: &quot;adminPassword&quot;, 
        roles: [
            { role: &quot;userAdminAnyDatabase&quot;, db: &quot;admin&quot; },
            &quot;readWriteAnyDatabase&quot;
        ] 
    }
);
</code></pre>
<h2 id="replicaset">REPLICA SET</h2>
<p>On primary node, connect to mongo shell and run the command.</p>
<pre><code class="language-javascript">use admin;
db.createUser(
    { 
        user: &quot;adminUser&quot;, 
        pwd: &quot;adminPassword&quot;, 
        roles: [{ 
            role: &quot;userAdminAnyDatabase&quot;, db: &quot;admin&quot; }, 
            { role: &quot;dbAdminAnyDatabase&quot;, db: &quot;admin&quot; }, 
            { role: &quot;readWriteAnyDatabase&quot;, db: &quot;admin&quot; }, 
            { role: &quot;clusterAdmin&quot;, db: &quot;admin&quot; }
        ] 
    }
);
</code></pre>
<p>Generate a keyfile for replicaset communication. and copy this to all instances of mongodb</p>
<pre><code class="language-sh">openssl rand -base64 756 &gt; mongodb.key
chmod 400 mongodb.key
</code></pre>
<blockquote>
<p>NOTE:</p>
<ul>
<li>More roles are required for replica set compared to standalone deployments.</li>
</ul>
</blockquote>
<p>Restart the mongod instance</p>
<ol>
<li>If using docker container, then run <code>docker run -v source:/data/db -p 27017:27017 -d mongo:latest --auth</code> after stopping the container.</li>
<li>If running standalone mongod instances, modify the config file and add these lines to it.</li>
</ol>
<pre><code class="language-yaml">security:
    authorization: &quot;enabled&quot;
    keyFile: &lt;path_to&gt;/mongodb.key 
</code></pre>
<blockquote>
<p>NOTE:</p>
<ul>
<li>If running on docker, exec into the container and run the mongo shell.</li>
<li>For production, you should secure the deployments. You can refer for that <a href="https://medium.com/@rajanmaharjan/secure-your-mongodb-connections-ssl-tls-92e2addb3c89?ref=blog.suraj-mittal.dev">here</a>.</li>
</ul>
</blockquote>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Almost free location API]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>In this blog post i try to get around pricey User IP location services using a cloud function and with the help of tracked data by our friends at Google.</p>
<p>Below is a nodejs script to deploy on google cloud functions.</p>
<pre><code class="language-js">const cors = require(&apos;cors&apos;)
const cityTimezones = require(</code></pre>]]></description><link>https://blog.suraj-mittal.dev/almost-free-location-api/</link><guid isPermaLink="false">638b38b3a82d211dae7b44b4</guid><category><![CDATA[programming]]></category><category><![CDATA[location]]></category><category><![CDATA[cloud]]></category><category><![CDATA[Import 2022-12-03 11:53]]></category><dc:creator><![CDATA[Suraj Mittal]]></dc:creator><pubDate>Sun, 08 Dec 2019 19:09:33 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>In this blog post i try to get around pricey User IP location services using a cloud function and with the help of tracked data by our friends at Google.</p>
<p>Below is a nodejs script to deploy on google cloud functions.</p>
<pre><code class="language-js">const cors = require(&apos;cors&apos;)
const cityTimezones = require(&apos;city-timezones&apos;);

// Set `useWhitelist` to `false` if you want to accept all requests.
const config = {
  useWhitelist: false
}

// Define from which origins requests are allowed.
const whitelist = [
  &apos;https://YOURWEBSITE.com&apos;
];

// Parse the whitelist and decide if the request is allowed.
const corsOptionsWhitelist = function (req, callback) {
  var corsOptions;

  if (whitelist.indexOf(req.header(&apos;Origin&apos;)) !== -1) {
    corsOptions = { origin: true }
  } else {
    corsOptions = { origin: false }
  }

  callback(null, corsOptions);
}

// Options when not using the whitelist.
const corsOptions = {
  origin: true
}

// Handle the response within this function. It can be extended to include more data.
function _geolocation(req, res) {
  // res.header(&apos;Cache-Control&apos;,&apos;no-cache&apos;);

  const data = {
    country: req.headers[&quot;x-appengine-country&quot;],
    region: req.headers[&quot;x-appengine-region&quot;],
    city: req.headers[&quot;x-appengine-city&quot;],
    cityLatLong: req.headers[&quot;x-appengine-citylatlong&quot;],
    userIP: req.headers[&quot;x-appengine-user-ip&quot;],
    cityData: cityTimezones.lookupViaCity(req.headers[&quot;x-appengine-city&quot;])
  }

  res.json(data)
};

// Export the cloud function.
exports.geolocation = (req, res) =&gt; {
  const corsHandler = config.useWhitelist ? cors(corsOptionsWhitelist) : cors(corsOptions);

  return corsHandler(req, res, function() {
    return _geolocation(req, res);
  });
};
</code></pre>
<p>This is the package.json contents</p>
<pre><code class="language-js">{
  &quot;name&quot;: &quot;gfc-geolocation&quot;,
  &quot;version&quot;: &quot;0.0.1&quot;,
  &quot;dependencies&quot;: {
    &quot;cors&quot;: &quot;^2.8.4&quot;,
    &quot;city-timezones&quot;: &quot;^1.0.5&quot;
    }
}
</code></pre>
<blockquote>
<p>Google provides 2 million cloud functions invocation free per month. So effectively you are getting user location tracking at fraction of a price.</p>
</blockquote>
<p><strong>Disclaimer: This wonderful hack was contributed by <a href="https://www.linkedin.com/in/amit-kumar-0206/?ref=blog.suraj-mittal.dev">Amit Kumar</a></strong></p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Setting up Drone CI/CD]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>CI/CD pipelines is a must for rapid prototyping and development of applications. Be it frontend or backend.</p>
<p>One of the open source tool for CI/CD is the one called <a href="https://drone.io/?ref=blog.suraj-mittal.dev">Drone</a></p>
<p>Setup of this tool is extremely simple.</p>
<h2 id="requirements">Requirements</h2>
<ul>
<li><code>docker</code> installation on your system</li>
</ul>
<p>Just ssh into your server</p>]]></description><link>https://blog.suraj-mittal.dev/setting-up-drone-ci-cd/</link><guid isPermaLink="false">638b38b3a82d211dae7b44b1</guid><category><![CDATA[docker]]></category><category><![CDATA[ci/cd]]></category><category><![CDATA[drone]]></category><category><![CDATA[Import 2022-12-03 11:53]]></category><dc:creator><![CDATA[Suraj Mittal]]></dc:creator><pubDate>Sun, 03 Nov 2019 08:10:54 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>CI/CD pipelines is a must for rapid prototyping and development of applications. Be it frontend or backend.</p>
<p>One of the open source tool for CI/CD is the one called <a href="https://drone.io/?ref=blog.suraj-mittal.dev">Drone</a></p>
<p>Setup of this tool is extremely simple.</p>
<h2 id="requirements">Requirements</h2>
<ul>
<li><code>docker</code> installation on your system</li>
</ul>
<p>Just ssh into your server and run the following command</p>
<pre><code class="language-bash">docker run \
  --volume=/var/run/docker.sock:/var/run/docker.sock \
  --volume=/var/lib/drone:/data \
  --env=DRONE_RUNNER_CAPACITY=10 \
  --env=DRONE_SERVER_HOST=drone.example.com \
  --env=DRONE_SERVER_PROTO=https \
  --env=DRONE_TLS_AUTOCERT=true \
  --publish=80:80 \
  --publish=443:443 \
  --restart=always \
  --detach=true \
  --name=drone \
  --env=DRONE_BITBUCKET_CLIENT_ID=${BITBUCKET_KEY} \
  --env=DRONE_BITBUCKET_CLIENT_SECRET=${DRONE_BITBUCKET_CLIENT_SECRET} \
  --env=DRONE_USER_FILTER=user1,user2,user3 \
  --env=DRONE_USER_CREATE=username:user1,admin:true \
  drone/drone:1
</code></pre>
<blockquote>
<p><strong>NOTES</strong></p>
<ul>
<li><code>--volume=/var/lib/drone:/data</code> : to persist to data to our local filesystem</li>
<li><code>--env=DRONESERVERHOST=drone.example.com --env=DRONESERVERPROTO=https env=DRONETLSAUTOCERT=true   --publish=80:80 --publish=443:443</code>: required for exposing it to the internet and issuing https certificates. You can ignore all these options if you don&apos;t need the functionality.</li>
<li><code>--env=DRONEBITBUCKETCLIENTID=${BITBUCKETKEY} --env=DRONEBITBUCKETCLIENTSECRET=${DRONEBITBUCKETCLIENTSECRET}</code>: To link with bitbucket installation</li>
<li><code>--env=DRONEUSERFILTER=user1,user2,user3 --env=DRONEUSERCREATE=username:user1,admin:true</code>: Default user creation and filtering since drone installation is open to everyone by default</li>
</ul>
</blockquote>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Cron Jobs in Kubernetes]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>A lot of time, we need to run periodic jobs using Cron. Kubernetes is an amazing tool for it since it simplifies resource management by spinning up nodes in case of non availability of resources and releasing them in case of excess capacity.</p>
<p>Cron jobs can be deployed by referring</p>]]></description><link>https://blog.suraj-mittal.dev/cron-jobs-in-kubernetes/</link><guid isPermaLink="false">638b38b3a82d211dae7b44b0</guid><category><![CDATA[kubernetes]]></category><category><![CDATA[Import 2022-12-03 11:53]]></category><dc:creator><![CDATA[Suraj Mittal]]></dc:creator><pubDate>Wed, 30 Oct 2019 11:44:36 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>A lot of time, we need to run periodic jobs using Cron. Kubernetes is an amazing tool for it since it simplifies resource management by spinning up nodes in case of non availability of resources and releasing them in case of excess capacity.</p>
<p>Cron jobs can be deployed by referring to the below sample yaml and running <code>kubectl apply -f</code></p>
<pre><code class="language-yaml">apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: cron-job
  namespace: &quot;${namespace}&quot;
spec:
  schedule: &quot;0/30 * * * *&quot;
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: cron-jobs
            image: &quot;${repo}:${DRONE_BUILD_NUMBER}&quot;
            env:
              - name: run_environment
                value: ${run_environment}
          restartPolicy: OnFailure
</code></pre>
<p>This runs the cron job every 30 minutes</p>
<blockquote>
<p><strong>Notes</strong></p>
<ul>
<li>Use <code>envsubst</code> to replace env variables in <code>.yml</code> file</li>
<li>Install it in debian using command <code>apt-get install gettext -y</code></li>
<li>Run the deploy in CI/CD using the command <code>cat deploy.yml|envsubst|kubectl apply -f -</code></li>
<li>We set <code>restartPolicy: OnFailure</code> so that, it doesn&apos;t keep on restarting the job on completion of it.</li>
</ul>
</blockquote>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Finding means of multi-modal Gaussian distribution]]></title><description><![CDATA[<!--kg-card-begin: markdown--><h2 id="needtofindthemeansofthemultimodalnormaldistribution">Need to find the means of the multi-modal normal distribution</h2>
<ul>
<li>In our day to day lives, we encounter many situations where data is generated with multiple peaks(modes).</li>
<li>One such problem would be the identification of peak hour times in a public transport systems like metro or buses.</li>
<li>We need</li></ul>]]></description><link>https://blog.suraj-mittal.dev/finding-means-of-multimodal-gaussian-distribution/</link><guid isPermaLink="false">638b38b3a82d211dae7b44ae</guid><category><![CDATA[statistics]]></category><category><![CDATA[normal distribution]]></category><category><![CDATA[Import 2022-12-03 11:53]]></category><dc:creator><![CDATA[Suraj Mittal]]></dc:creator><pubDate>Sat, 26 Oct 2019 16:36:10 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h2 id="needtofindthemeansofthemultimodalnormaldistribution">Need to find the means of the multi-modal normal distribution</h2>
<ul>
<li>In our day to day lives, we encounter many situations where data is generated with multiple peaks(modes).</li>
<li>One such problem would be the identification of peak hour times in a public transport systems like metro or buses.</li>
<li>We need to identify these peaks so that we can target increasing the frequencies of the buses/trains during the peak hours.</li>
</ul>
<h2 id="generatingrandomdatawithmultiplepeaks">Generating random data with multiple peaks</h2>
<p>Use the below code to generate multi-modal gaussian distributions</p>
<pre><code class="language-python">%matplotlib inline

import numpy as np
import pandas as pd

# Generating multiple gaussians 
distribution1 = np.random.normal(loc=0,scale=1.0,size=(300))
distribution2 = np.random.normal(loc=5,scale=1.0,size=(300))
distribution3 = np.random.normal(loc=10,scale=1.0,size=(300))
distribution4 = np.random.normal(loc=15,scale=1.0,size=(150))
distribution5 = np.random.normal(loc=-10,scale=1.0,size=(10))

combined_distribution = np.concatenate([distribution1,distribution2,distribution3,distribution4,distribution5])

combined_data_dataframe = pd.DataFrame(combined_distribution)
combined_data_dataframe.plot(kind=&apos;kde&apos;)
</code></pre>
<p><img src="https://blog.suraj-mittal.dev/content/images/2019/10/gaussian-1.png" alt="gaussian-1" loading="lazy"></p>
<p>As you can see, we have created a random gaussian data with multiple means of <code>0,5,10,15 and -10</code></p>
<h2 id="whathappensifwejustcalculatethemean">What happens if we,just calculate the mean?</h2>
<pre><code class="language-python">print(combined_distribution.mean())
&gt;&gt;&gt; 6.314271309260518
</code></pre>
<p>As you can see, the mean does not represent the peak due to multi-modality of the data.</p>
<p>Hence, to find multiple peaks programatically, we use one of the mixture models available in the <code>scikit-learn</code> package called <code>GaussianMixtureModel</code></p>
<h2 id="whyamixturemodel">Why a mixture model?</h2>
<ul>
<li>These models are based on the assumption that, there is a presence of another subpopulation within the main population.</li>
<li>Can approximate the subpopulations while being not computationally heavy for mid sized data (100&apos;s of thousands).</li>
<li>In case of a huge data, approximations about data can be made by sampling a small sample of data randomly and fitting the model on these samples. [See <a href="http://mathworld.wolfram.com/CentralLimitTheorem.html?ref=blog.suraj-mittal.dev">Central Limit Theorem</a>]</li>
</ul>
<p>Now, the code for it</p>
<pre><code class="language-python">from sklearn.mixture import GaussianMixture
mixture_model = GaussianMixture(n_components=5)
mixture_model.fit(combined_distribution.reshape(-1,1))
print(mixture_model.means_.astype(np.int32).reshape(-1))
print(mixture_model.weights_.reshape(-1))
&gt;&gt;&gt; [14  5 10 -9  0]
&gt;&gt;&gt; [0.1417197  0.28217055 0.2822368  0.00943396 0.28443898]
</code></pre>
<blockquote>
<ul>
<li>We can combine use these weights as a representation of the density of the data at the peak.</li>
<li>In order to automatically determine the peaks, we can use a variation of gaussian mixture called <code>BayesianGaussianMixture</code> along with the <code>means_</code> and <code>degrees_of_freedom_</code> attribute to select the proper peaks</li>
<li>Alternative we can use the <code>scipy.stats.gaussian_kde</code> to find the density, but it smooths out the lower density values more than necessary.</li>
</ul>
</blockquote>
<hr>
<p>Thank you for reading</p>
<!--kg-card-end: markdown--><p></p>]]></content:encoded></item><item><title><![CDATA[Setting Up Nginx Ingress, Letsencrypt in Kubernetes without LoadBalancers]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Deploying services using Docker containers are all in the rage nowadays and Kubernetes provides a good way to manage them. Also, majority of the cloud providers have kubernetes as a service. So there&apos;s that too.</p>
<h2 id="beforewebegin">Before we begin</h2>
<p>I assume that you have setup Kubernetes and have Kubectl</p>]]></description><link>https://blog.suraj-mittal.dev/setting-up-ngnix-letsencrypt-in-kubernetes/</link><guid isPermaLink="false">638b38b3a82d211dae7b44ad</guid><category><![CDATA[kubernetes]]></category><category><![CDATA[letsencrypt]]></category><category><![CDATA[nginx]]></category><category><![CDATA[ingress]]></category><category><![CDATA[Import 2022-12-03 11:53]]></category><dc:creator><![CDATA[Suraj Mittal]]></dc:creator><pubDate>Sat, 26 Oct 2019 12:25:52 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Deploying services using Docker containers are all in the rage nowadays and Kubernetes provides a good way to manage them. Also, majority of the cloud providers have kubernetes as a service. So there&apos;s that too.</p>
<h2 id="beforewebegin">Before we begin</h2>
<p>I assume that you have setup Kubernetes and have Kubectl configured on your local machine.<br>
If not, i suggest you yo go and set those up before starting.<br>
Now lets get started :D</p>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><div style="width:100%;height:0;padding-bottom:83%;position:relative;"><iframe src="https://giphy.com/embed/gHtvqib1yFvNbsjwCs" width="100%" height="100%" style="position:absolute" frameborder="0" class="giphy-embed" allowfullscreen></iframe></div><p><a href="https://giphy.com/gifs/Friends-season-6-episode-606-the-one-on-last-night-gHtvqib1yFvNbsjwCs?ref=blog.suraj-mittal.dev">via GIPHY</a></p><!--kg-card-end: html--><!--kg-card-begin: markdown--><h2 id="installinghelm">Installing Helm</h2>
<p><strong>What is helm and why do i need it?</strong></p>
<ul>
<li>Helm is a package manager for kubernetes.</li>
<li>Since we are going to install some services and their dependencies into the cluster, we need helm to make sure we can install them with simple set of commands.</li>
<li>Alternatively, you can install these packages without helm. Just go their respective github repos and find the yaml files and install them using <code>kubectl apply</code></li>
</ul>
<p>Spin up your terminal and fire up the following commands to install helm.</p>
<pre><code class="language-sh">kubectl --namespace kube-system create sa tiller
# create a cluster role binding for tiller
kubectl create clusterrolebinding tiller \
   --clusterrole cluster-admin \
   --serviceaccount=kube-system:tiller
echo &quot;initialize helm&quot;
# initialized helm within the tiller service account
helm init --service-account tiller
# updates the repos for Helm repo integration
helm repo update
echo &quot;verify helm&quot;
# verify that helm is installed in the cluster
kubectl get deploy,svc tiller-deploy -n kube-system
</code></pre>
<p>Basically what the above commands does is as follows</p>
<ul>
<li>Creates a service account called <code>tiller</code> in the namespace <code>kube-system</code> which is the system namespace that kubernetes creates after the cluster creation</li>
<li>Assigns the <code>cluster-admin</code> role to the namespace so that we can install other charts via helm without any problems</li>
<li>Install <code>helm</code> to the cluster.</li>
</ul>
<h2 id="installingnginxingresscontroller">Installing nginx ingress controller</h2>
<p>Now that we&apos;ve got helm setup and going, we need to install <em>nginx ingress controller</em> in the cluster using helm<br>
To do this, we run</p>
<pre><code class="language-sh">helm install stable/nginx-ingress \
    --namespace=kube-system \
    --name nginx-ingress \
    --set controller.kind=DaemonSet \
    --set controller.daemonset.useHostPort=true \
    --set controller.service.enabled=false
</code></pre>
<p>Basically what we are doing here is installing nginx with some specific settings so that it doesn&apos;t spin up loadbalancers during the actual creation of ingress.</p>
<blockquote>
<ul>
<li>You can read more about DaemonSets <a href="https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/?ref=blog.suraj-mittal.dev">here</a></li>
<li><code>useHostPort</code> basically tells the controller to use host network of the cluster nodes and allows us to access 80 and 443 ports from outside the cluster.</li>
</ul>
</blockquote>
<h2 id="installingcertmanager">Installing certmanager</h2>
<p>We need a way to issue certificates to the cluster and this is done by using certmanager.<br>
Install it using helm by</p>
<pre><code class="language-bash">helm install --name cert-manager --version v0.5.2 \
    --namespace kube-system stable/cert-manager
</code></pre>
<p>Now that we have installed certmanger, we need someone to issue the certificate for us, lets use LetsEncrypt for it.</p>
<h2 id="installingletencryptascertificateissuer">Installing Letencrypt as certificate issuer</h2>
<p>Open a text editor and enter the below</p>
<pre><code class="language-yaml">apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    email: &quot;&lt;YOUR_EMAIL&gt;&quot;
    privateKeySecretRef:
      name: letsencrypt-staging
    http01: {}
---
apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: &quot;&lt;YOUR_EMAIL&gt;&quot;
    privateKeySecretRef:
      name: letsencrypt-prod
    http01: {}
</code></pre>
<p>save the above file as <code>lets_encrpyt.yml</code> and then run <code>kubectl apply -f lets_encrypt.yml</code></p>
<h2 id="runaningress">Run an ingress</h2>
<p>Now that we have setup letsencrypt as cluster issuer, we need to setup an ingress to handle the incoming connections.<br>
Open another editor and enter the following</p>
<pre><code class="language-yaml">apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    ingress.kubernetes.io/ssl-redirect: &quot;true&quot;
    certmanager.k8s.io/cluster-issuer: letsencrypt-prod
    certmanager.k8s.io/acme-http01-edit-in-place: &quot;true&quot;
    kubernetes.io/ingress.class: &quot;nginx&quot;
  name: custom-ingress-nginx
spec:
  rules:
    - host: &lt;YOUR-HOSTNAME&gt;
      http:
        paths:
          - path: /
            backend:
              serviceName: &quot;&lt;SERVICE-NAME&gt;&quot;
              servicePort: &lt;SERVICE-PORT&gt;
  tls:
    - secretName: test-eng-secret
      hosts:
        - &lt;YOUR-HOSTNAME&gt;
</code></pre>
<ul>
<li>we specify the cluster issuer name in the annotation to issue us certificates. Use <code>letsencrypt-staging</code> to see if your setup is working and then use the production one.</li>
<li>And we specify the ingress class as <code>nginx</code> to use the nginx ingress controller.</li>
<li>the <code>&lt;SERVICE-NAME&gt;</code> and <code>&lt;SERVICE-PORT&gt;</code> are the names and ports in the <code>NodePort</code> services exposed above.</li>
</ul>
<hr>
<p>Thanks for reading and look forward to my future struggles.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>