<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Avil Page (Posts about devops)</title><link>https://avilpage.com/</link><description></description><atom:link href="https://avilpage.com/tags/devops.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><lastBuildDate>Fri, 25 Sep 2026 10:35:56 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Build/Deploy Full Stack Web Apps with only Config</title><link>https://avilpage.com/2026/03/config-first-tools.html</link><dc:creator>Anand Reddy Pandikunta</dc:creator><description>&lt;blockquote&gt;
&lt;p&gt;The best code is the code you don't have to write&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;Config First Tools&lt;/h4&gt;
&lt;p&gt;Any tool that provides first class support for configuration files can be considered a Config First Tool. 
These tools allow developers to setup/build/deploy applications by writing configuration files instead of writing code.&lt;/p&gt;
&lt;p&gt;In DevOps world, tools like Terraform, Ansible, and Kubernetes are examples of Config First Tools. 
These kinds of tools are not that popular in the web development world.&lt;/p&gt;
&lt;p&gt;In this article, lets see how we can build a full stack web application using only configuration files.&lt;/p&gt;
&lt;h4&gt;Todo - Web App&lt;/h4&gt;
&lt;p&gt;A simple backend API that allows users to manage todo lists.
A simple frontend that uses the backend API.
A simple script to deploy the backend and frontend applications.&lt;/p&gt;
&lt;h4&gt;Backend API&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://pocketbase.io/"&gt;PocketBase&lt;/a&gt; is a backend API that provides a simple way to create and manage databases, authentication, and file storage.
It is a great tool for building simple backend APIs without writing any code.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;brew&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;pocketbase
$&lt;span class="w"&gt; &lt;/span&gt;pocketbase&lt;span class="w"&gt; &lt;/span&gt;serve
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can create a collection called &lt;code&gt;Todo&lt;/code&gt; with required fields and pocketbase will automatically generate the API for us.&lt;/p&gt;
&lt;p&gt;&lt;img alt="config-first-tools" src="https://avilpage.com/images/config-first-tools-backend.png"&gt;&lt;/p&gt;
&lt;h4&gt;Frontend&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://lowdefy.com/"&gt;Lowdefy&lt;/a&gt; is a config first tool that allows us to build web applications using yaml config.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;npm&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-g&lt;span class="w"&gt; &lt;/span&gt;lowdefy
$&lt;span class="w"&gt; &lt;/span&gt;lowdefy&lt;span class="w"&gt; &lt;/span&gt;init
$&lt;span class="w"&gt; &lt;/span&gt;lowdefy&lt;span class="w"&gt; &lt;/span&gt;dev
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For basic crud apps, instead of us writing the boilerplate config, we can ask an AI agent to write the config for us.&lt;/p&gt;
&lt;h4&gt;Deployment&lt;/h4&gt;
&lt;p&gt;For deployment, we can use Ansible to deploy both the backend and frontend applications. Here also we can just AI agent to write the playbook for us.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;ansible
$&lt;span class="w"&gt; &lt;/span&gt;ansible-playbook&lt;span class="w"&gt; &lt;/span&gt;deploy.yml
&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;OliveTin&lt;/h4&gt;
&lt;p&gt;&lt;img alt="config-first-tools" src="https://avilpage.com/images/config-first-tools-devops.png"&gt;&lt;/p&gt;
&lt;p&gt;Even though we can use ansible to deploy the applications, it is not that convenient to run ansible playbooks on mobile.
&lt;a href="https://www.olivetin.app"&gt;OliveTin&lt;/a&gt; is a config first tool that allows us to create web UIs for our scripts.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Deploy&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;ToDo&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;App"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;popupOnStart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;execution-dialog&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;shell&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ansible-playbook deploy.yml&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;6000&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"🤖"&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can create a simple web UI for our deployment script with just 5 lines.&lt;/p&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;For quick prototyping and simple applications, config first tools can be a great choice as they allow us to build applications without writing any code.
They also allow us to focus on the application logic rather than the implementation details.&lt;/p&gt;
&lt;p&gt;With the rise of AI, it is much easy to maintain 500 lines of config than 5000 lines of code.&lt;/p&gt;</description><category>AI</category><category>devops</category><category>web development</category><guid>https://avilpage.com/2026/03/config-first-tools.html</guid><pubDate>Sat, 28 Mar 2026 06:17:29 GMT</pubDate></item><item><title>In Praise of OliveTin</title><link>https://avilpage.com/2026/01/in-praise-of-olivetin.html</link><dc:creator>Anand Reddy Pandikunta</dc:creator><description>&lt;p&gt;&lt;img alt="olivetin" src="https://avilpage.com/images/olivetin.png"&gt;&lt;/p&gt;
&lt;p&gt;In a world of complex internal tools, &lt;a href="https://www.olivetin.app"&gt;OliveTin&lt;/a&gt; bridges the gap between raw CLI and usable web UI with just yaml config.&lt;/p&gt;
&lt;h4&gt;Server Admin Panel&lt;/h4&gt;
&lt;p&gt;I used admin panels like &lt;a href="https://avilpage.com/2024/12/install-cockpit-on-remote-linux-vm.html"&gt;CockPit&lt;/a&gt;, Ajenti, etc to provide simple web UI 
for non-developers to provide access to services. Developing custom widgets is time consuming and requires programming skills as well.&lt;/p&gt;
&lt;p&gt;With OliveTin, I can provide web UI directly with just yaml config. This saves a lot of time and effort.
It also provides a clean UI as it starts from scratch.&lt;/p&gt;
&lt;h4&gt;Backend Driven UI&lt;/h4&gt;
&lt;p&gt;For existing python scripts, I often use tools like Streamlit, NiceGUI, etc to create web UIs.
Organizing the UI components for multiple scripts is time consuming.&lt;/p&gt;
&lt;p&gt;OliveTin can provide web UI directly python scripts and they can be organized in groups with just yaml config.&lt;/p&gt;
&lt;h4&gt;Mobile Friendly&lt;/h4&gt;
&lt;p&gt;To do a simple deployment on mobile, I need to open an app that supports ssh, ssh into the server, navigate to a directory, 
run a script to deploy.&lt;/p&gt;
&lt;p&gt;OliveTin provides a "Deploy" button on mobile browser which is way convenient.&lt;/p&gt;
&lt;h4&gt;Authentication&lt;/h4&gt;
&lt;p&gt;&lt;img alt="olivetin-oauth2" src="https://avilpage.com/images/olivetin-oauth2.png"&gt;&lt;/p&gt;
&lt;p&gt;OliveTin provides authentication(local users, OAuth2, JWT, etc), authorization(ACLs) &amp;amp; accounting(logs) out of the box.
With just yaml config, we can secure the web UI.&lt;/p&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;There are hundreds of other features provided by OliveTin like scheduling, file uploads, webhooks, etc.&lt;/p&gt;
&lt;p&gt;If you ever want to provide a simple web UI for scripts with low code or no code, give OliveTin a try!&lt;/p&gt;</description><category>automation</category><category>cross-platform</category><category>devops</category><guid>https://avilpage.com/2026/01/in-praise-of-olivetin.html</guid><pubDate>Fri, 30 Jan 2026 20:13:11 GMT</pubDate></item><item><title>Remote Access to k3d/k3s Kubernetes cluster</title><link>https://avilpage.com/2025/12/remote-access-to-k3d-k3s-cluster.html</link><dc:creator>Anand Reddy Pandikunta</dc:creator><description>&lt;h4&gt;Introduction&lt;/h4&gt;
&lt;p&gt;We learnt how to &lt;a href="https://avilpage.com/2023/03/setup-k8s-anywhere-k3d.html"&gt;deploy kubernetes cluster anywhere with a single k3d command&lt;/a&gt;. 
By default, k3d cluster is accessible only from the host machine.&lt;/p&gt;
&lt;h4&gt;Remote Access&lt;/h4&gt;
&lt;p&gt;&lt;img alt="k3d-remote-access" src="https://avilpage.com/images/k3s-remote-access.png"&gt;&lt;/p&gt;
&lt;p&gt;Create new cluster with&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;k3d&lt;span class="w"&gt; &lt;/span&gt;cluster&lt;span class="w"&gt; &lt;/span&gt;create&lt;span class="w"&gt; &lt;/span&gt;cloud-k8s&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--api-port&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6443&lt;/span&gt;:6443@loadbalancer&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--k3s-arg&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"--tls-san=&amp;lt;remote-ip&amp;gt;@server:*"&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;--api-port 6443:6443@loadbalancer&lt;/code&gt; maps port 6443 on the host machine to the cluster's API server load balancer. 
This can be changed (e.g., 8080:6443) if 6443 is taken on the host.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;--k3s-arg "--tls-san=..."&lt;/code&gt;  adds the host's public IP to the certificate, preventing SSL errors later.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Ensure remote server firewall allows incoming traffic on the chosen port (e.g., 6443 or 8080). 
To verify, run &lt;code&gt;telnet &amp;lt;remote-ip&amp;gt; 6443&lt;/code&gt; from local machine.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On the remote server, get kubeconfig:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;k3d&lt;span class="w"&gt; &lt;/span&gt;kubeconfig&lt;span class="w"&gt; &lt;/span&gt;get&lt;span class="w"&gt; &lt;/span&gt;cloud-k8s
---
apiVersion:&lt;span class="w"&gt; &lt;/span&gt;v1
clusters:
-&lt;span class="w"&gt; &lt;/span&gt;cluster:
&lt;span class="w"&gt;    &lt;/span&gt;server:&lt;span class="w"&gt; &lt;/span&gt;https://0.0.0.0:6443
&lt;span class="w"&gt;  &lt;/span&gt;name:&lt;span class="w"&gt; &lt;/span&gt;k3d-rk
&lt;span class="w"&gt;   &lt;/span&gt;...&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# truncated for brevity&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Copy this kubeconfig, replace &lt;code&gt;0.0.0.0&lt;/code&gt; with the remote server's public IP.&lt;/p&gt;
&lt;p&gt;Open &lt;a href="https://github.com/freelensapp/freelens"&gt;Free Lens&lt;/a&gt;, paste this config and connect to the remote k3d cluster.&lt;/p&gt;
&lt;p&gt;&lt;img alt="k3d-remote-access" src="https://avilpage.com/images/k3s-remote-access-freelens.png"&gt;&lt;/p&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;You can use your favorite Kubernetes tools (kubectl, FreeLens, k9s, etc.) to manage the remote k8s cluster.&lt;/p&gt;</description><category>devops</category><category>kubernetes</category><category>linux</category><guid>https://avilpage.com/2025/12/remote-access-to-k3d-k3s-cluster.html</guid><pubDate>Wed, 17 Dec 2025 03:10:39 GMT</pubDate></item><item><title>Migrating from Nginx to Caddy</title><link>https://avilpage.com/2025/05/migrating-from-nginx-to-caddy.html</link><dc:creator>Anand Reddy Pandikunta</dc:creator><description>&lt;p&gt;When deploying web apps in production, a web server is essential for serving static files, handling reverse proxying, 
and managing SSL/TLS certificates. &lt;/p&gt;
&lt;p&gt;I have been using Nginx for a long time, but I have recently switched to Caddy as it provides automatic HTTPS by default.
In addition, Caddy has a simpler configuration syntax and is easier to set up for basic use cases.&lt;/p&gt;
&lt;h4&gt;Nginx vs Caddy&lt;/h4&gt;
&lt;p&gt;To illustrate the differences, here are some examples of how to configure a simple web server in both Nginx and Caddy.&lt;/p&gt;
&lt;h5&gt;Nginx Configuration&lt;/h5&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;listen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;server_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;avilpage.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/var/www/html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;index.htm&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kn"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;/api&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kn"&gt;proxy_pass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With Nginx, you need to manually handle SSL/TLS certificates, which can be cumbersome. 
You typically use tools like Certbot to obtain and renew certificates.&lt;/p&gt;
&lt;h5&gt;Caddy Configuration&lt;/h5&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;avilpage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;file_server&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;reverse_proxy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;localhost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Caddy automatically manages SSL/TLS certificates for you, so you don't need to worry about obtaining or renewing them.
It also provides default redirection from HTTP to HTTPS, making it easier to secure your site.&lt;/p&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;Caddy's automatic HTTPS and simpler configuration syntax make it a compelling choice for web servers, 
especially for those who want to get started quickly without dealing with the complexities of SSL/TLS management.&lt;/p&gt;</description><category>automation</category><category>devops</category><guid>https://avilpage.com/2025/05/migrating-from-nginx-to-caddy.html</guid><pubDate>Fri, 30 May 2025 15:51:21 GMT</pubDate></item><item><title>Free DockerHub Alternative - ECR Public Gallery</title><link>https://avilpage.com/2025/02/free-dockerhub-alternative-ecr-gallery.html</link><dc:creator>Anand Reddy Pandikunta</dc:creator><description>&lt;p&gt;&lt;img alt="docker-rate-limits" src="https://avilpage.com/images/docker-rate-limits.png"&gt;&lt;/p&gt;
&lt;p&gt;DockerHub started rate limiting&lt;sup id="fnref:rate"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2025/02/free-dockerhub-alternative-ecr-gallery.html#fn:rate"&gt;1&lt;/a&gt;&lt;/sup&gt; anonymous docker pulls. When testing out a new CI/CD setup, I hit the rate limit and had to wait for an hour to pull the image. This was a good time to look for alternatives.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://gallery.ecr.aws/"&gt;AWS ECR Public Gallery&lt;/a&gt;&lt;sup id="fnref:ecr"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2025/02/free-dockerhub-alternative-ecr-gallery.html#fn:ecr"&gt;2&lt;/a&gt;&lt;/sup&gt; is a good alternative to DockerHub as of today(2025 Feb). It is free and does not have rate limits even for anonymous users. &lt;/p&gt;
&lt;p&gt;&lt;img alt="public-ecr-gallery" src="https://avilpage.com/images/public-ecr-gallery.png"&gt;&lt;/p&gt;
&lt;p&gt;Once we find the required image from the gallery, we can simply change the image name in the &lt;code&gt;docker pull&lt;/code&gt; command to pull the image from ECR Gallery.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;docker&lt;span class="w"&gt; &lt;/span&gt;pull&lt;span class="w"&gt; &lt;/span&gt;public.ecr.aws/ubuntu/ubuntu
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In &lt;code&gt;Dockerfile&lt;/code&gt;, we can use the image from ECR Gallery as follows:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;public.ecr.aws/ubuntu/ubuntu&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That is a quick way to avoid DockerHub rate limits.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:rate"&gt;
&lt;p&gt;&lt;a href="https://docs.docker.com/docker-hub/usage/"&gt;DockerHub Limits&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2025/02/free-dockerhub-alternative-ecr-gallery.html#fnref:rate" title="Jump back to footnote 1 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:ecr"&gt;
&lt;p&gt;&lt;a href="https://gallery.ecr.aws"&gt;AWS ECR Public Gallery&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2025/02/free-dockerhub-alternative-ecr-gallery.html#fnref:ecr" title="Jump back to footnote 2 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>devops</category><category>docker</category><guid>https://avilpage.com/2025/02/free-dockerhub-alternative-ecr-gallery.html</guid><pubDate>Sun, 09 Feb 2025 16:08:34 GMT</pubDate></item><item><title>How (and when) to use systemd timer instead of cronjob</title><link>https://avilpage.com/2024/08/guide-systemd-timer-cronjob.html</link><dc:creator>Anand Reddy Pandikunta</dc:creator><description>&lt;h4&gt;Introduction&lt;/h4&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;*&lt;span class="w"&gt; &lt;/span&gt;*&lt;span class="w"&gt; &lt;/span&gt;*&lt;span class="w"&gt; &lt;/span&gt;*&lt;span class="w"&gt; &lt;/span&gt;*&lt;span class="w"&gt; &lt;/span&gt;bash&lt;span class="w"&gt; &lt;/span&gt;demo.sh
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Just a single line of code is sufficient to schedule a cron job. However, there are some scenarios where I find systemd timer more useful than cronjob.&lt;/p&gt;
&lt;h4&gt;How to use systemd timer&lt;/h4&gt;
&lt;p&gt;We need to create a service file(contains the script to be run) and a timer(contains the schedule).&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# demo.service&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;Unit&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Demo&lt;span class="w"&gt; &lt;/span&gt;service

&lt;span class="o"&gt;[&lt;/span&gt;Service&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bash&lt;span class="w"&gt; &lt;/span&gt;demo.sh
&lt;/pre&gt;&lt;/div&gt;

&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# demo.timer&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;Unit&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Run&lt;span class="w"&gt; &lt;/span&gt;myscript.service&lt;span class="w"&gt; &lt;/span&gt;every&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;minutes

&lt;span class="o"&gt;[&lt;/span&gt;Timer&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;OnBootSec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1min
&lt;span class="nv"&gt;OnUnitActiveSec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1min

&lt;span class="o"&gt;[&lt;/span&gt;Install&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;multi-user.target
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can copy these files to &lt;code&gt;/etc/systemd/system/&lt;/code&gt; and enable the timer.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;cp&lt;span class="w"&gt; &lt;/span&gt;demo.service&lt;span class="w"&gt; &lt;/span&gt;demo.timer&lt;span class="w"&gt; &lt;/span&gt;/etc/systemd/system/

$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;daemon-reload

$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;enable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--now&lt;span class="w"&gt; &lt;/span&gt;demo.timer
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can use &lt;code&gt;systemctl&lt;/code&gt; to see when the task is executed last and when it will be executed next.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;list-timers&lt;span class="w"&gt; &lt;/span&gt;--all
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src="https://avilpage.com/images/systemd-timer-cronjob.png" alt="systemd timer"&gt;&lt;/p&gt;
&lt;h4&gt;Use Cases&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Singleton - In the above example, lets say &lt;code&gt;demo.sh&lt;/code&gt; takes ~10 minutes to run. With cron job, in ten minutes we will have 10 instances of &lt;code&gt;demo.sh&lt;/code&gt; running. This is not ideal. With systemd timer, it will ensure only one instance of &lt;code&gt;demo.sh&lt;/code&gt; is running at a time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On demand runs - If we want to test out the script/job, systemd allows us to immediately run it with usual &lt;code&gt;systemctl start demo&lt;/code&gt; without needing to run the script manually.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Timer - With cron, we can run tasks upto a minute precision. Timer can run tasks till &lt;code&gt;second&lt;/code&gt; level precision. &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="o"&gt;[&lt;/span&gt;Timer&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;OnCalendar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;*-*-*&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;15&lt;/span&gt;:30:15
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In addition to that, we can run tasks based on system events. For example, we can run a script 15 minutes from reboot.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="o"&gt;[&lt;/span&gt;Timer&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;OnBootSec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;15min
&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;Systemd timer is a powerful tool that can replace cronjob in many scenarios. It provides more control and flexibility over cronjob. However, cronjob is still a good choice for simple scheduling tasks.&lt;/p&gt;</description><category>automation</category><category>devops</category><guid>https://avilpage.com/2024/08/guide-systemd-timer-cronjob.html</guid><pubDate>Mon, 05 Aug 2024 07:37:50 GMT</pubDate></item><item><title>Mastering Kraken2 - Part 2 - Performance Optimisation</title><link>https://avilpage.com/2024/07/mastering-kraken2-performance-optimisation.html</link><dc:creator>Anand Reddy Pandikunta</dc:creator><description>&lt;h4&gt;Mastering Kraken2&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://avilpage.com/2024/07/mastering-kraken2-initial-runs.html"&gt;Part 1 - Initial Runs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://avilpage.com/2024/07/mastering-kraken2-performance-optimisation.html"&gt;Part 2 - Classification Performance Optimisation&lt;/a&gt; (this post)&lt;/p&gt;
&lt;p&gt;&lt;a href="https://avilpage.com/2024/07/mastering-kraken2-build-custom-db.html"&gt;Part 3 - Build custom database indices&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://avilpage.com/2024/08/mastering-kraken2-fda-argos-index.html"&gt;Part 4 - Build FDA-ARGOS index&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;Part 5 - Regular vs Fast Builds (upcoming)&lt;/p&gt;
&lt;p&gt;Part 6 - Benchmarking (upcoming)&lt;/p&gt;
&lt;h4&gt;Introduction&lt;/h4&gt;
&lt;p&gt;In the previous post, we learned how to set up kraken2&lt;sup id="fnref:k2"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2024/07/mastering-kraken2-performance-optimisation.html#fn:k2"&gt;1&lt;/a&gt;&lt;/sup&gt;, download pre-built indices, and run kraken2. In this post, we will learn various ways to speed up the classification process.&lt;/p&gt;
&lt;h4&gt;Increasing RAM&lt;/h4&gt;
&lt;p&gt;Kraken2 standard database is ~80GB in size. It is recommended to have at least db size RAM to run kraken2 efficiently&lt;sup id="fnref:ksr"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2024/07/mastering-kraken2-performance-optimisation.html#fn:ksr"&gt;2&lt;/a&gt;&lt;/sup&gt;. Let's use 128GB RAM machine and run kraken2 with ERR10359977&lt;sup id="fnref:err"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2024/07/mastering-kraken2-performance-optimisation.html#fn:err"&gt;3&lt;/a&gt;&lt;/sup&gt; sample.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kraken2&lt;span class="w"&gt; &lt;/span&gt;--db&lt;span class="w"&gt; &lt;/span&gt;k2_standard&lt;span class="w"&gt; &lt;/span&gt;--report&lt;span class="w"&gt; &lt;/span&gt;report.txt&lt;span class="w"&gt; &lt;/span&gt;ERR10359977.fastq.gz&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;output.txt
Loading&lt;span class="w"&gt; &lt;/span&gt;database&lt;span class="w"&gt; &lt;/span&gt;information...&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;done&lt;/span&gt;.
&lt;span class="m"&gt;95064&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sequences&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;14&lt;/span&gt;.35&lt;span class="w"&gt; &lt;/span&gt;Mbp&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;processed&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.142s&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2662&lt;/span&gt;.9&lt;span class="w"&gt; &lt;/span&gt;Kseq/m,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;402&lt;/span&gt;.02&lt;span class="w"&gt; &lt;/span&gt;Mbp/m&lt;span class="o"&gt;)&lt;/span&gt;.
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;94816&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sequences&lt;span class="w"&gt; &lt;/span&gt;classified&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;99&lt;/span&gt;.74%&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;248&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sequences&lt;span class="w"&gt; &lt;/span&gt;unclassified&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.26%&lt;span class="o"&gt;)&lt;/span&gt;
kraken2&lt;span class="w"&gt; &lt;/span&gt;--db&lt;span class="w"&gt; &lt;/span&gt;k2_standard&lt;span class="w"&gt; &lt;/span&gt;--report&lt;span class="w"&gt; &lt;/span&gt;report.txt&lt;span class="w"&gt; &lt;/span&gt;ERR10359977.fastq.gz&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;.68s&lt;span class="w"&gt; &lt;/span&gt;user&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;152&lt;/span&gt;.19s&lt;span class="w"&gt; &lt;/span&gt;system&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;35&lt;/span&gt;%&lt;span class="w"&gt; &lt;/span&gt;cpu&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;:17.55&lt;span class="w"&gt; &lt;/span&gt;total
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now the time taken has come down from 40 minutes to 7 minutes. The classification speed has also increased from 0.19 Mbp/m to 402.02 Mbp/m.&lt;/p&gt;
&lt;p&gt;The previous sample had only a few reads, and the speed is not a good indicator. Let's run kraken2 with a larger sample.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kraken2&lt;span class="w"&gt; &lt;/span&gt;--db&lt;span class="w"&gt; &lt;/span&gt;k2_standard&lt;span class="w"&gt; &lt;/span&gt;--report&lt;span class="w"&gt; &lt;/span&gt;report.txt&lt;span class="w"&gt; &lt;/span&gt;--paired&lt;span class="w"&gt; &lt;/span&gt;SRR6915097_1.fastq.gz&lt;span class="w"&gt; &lt;/span&gt;SRR6915097_2.fastq.gz&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;output.txt
Loading&lt;span class="w"&gt; &lt;/span&gt;database&lt;span class="w"&gt; &lt;/span&gt;information...&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;done&lt;/span&gt;.
Processed&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;14980000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sequences&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2972330207&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bp&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;...
&lt;span class="m"&gt;17121245&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sequences&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3397&lt;/span&gt;.15&lt;span class="w"&gt; &lt;/span&gt;Mbp&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;processed&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;797&lt;/span&gt;.424s&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1288&lt;/span&gt;.2&lt;span class="w"&gt; &lt;/span&gt;Kseq/m,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;.61&lt;span class="w"&gt; &lt;/span&gt;Mbp/m&lt;span class="o"&gt;)&lt;/span&gt;.
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;9826671&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sequences&lt;span class="w"&gt; &lt;/span&gt;classified&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;57&lt;/span&gt;.39%&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;7294574&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sequences&lt;span class="w"&gt; &lt;/span&gt;unclassified&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;42&lt;/span&gt;.61%&lt;span class="o"&gt;)&lt;/span&gt;
kraken2&lt;span class="w"&gt; &lt;/span&gt;--db&lt;span class="w"&gt; &lt;/span&gt;k2_standard&lt;span class="w"&gt; &lt;/span&gt;--report&lt;span class="w"&gt; &lt;/span&gt;report.txt&lt;span class="w"&gt; &lt;/span&gt;--paired&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;output.txt&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;526&lt;/span&gt;.39s&lt;span class="w"&gt; &lt;/span&gt;user&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;308&lt;/span&gt;.24s&lt;span class="w"&gt; &lt;/span&gt;system&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;68&lt;/span&gt;%&lt;span class="w"&gt; &lt;/span&gt;cpu&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;:23.86&lt;span class="w"&gt; &lt;/span&gt;total
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This took almost 20 minutes to classify ~3 Gbp of data. Out of 20 minutes, 13 minutes was spent in classification. The remaining time in loading the db into memory.&lt;/p&gt;
&lt;p&gt;Let's use k2_plusPF&lt;sup id="fnref:k2p"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2024/07/mastering-kraken2-performance-optimisation.html#fn:k2p"&gt;4&lt;/a&gt;&lt;/sup&gt; db, which is twice the size of k2_standard and run kraken2.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kraken2&lt;span class="w"&gt; &lt;/span&gt;--db&lt;span class="w"&gt; &lt;/span&gt;k2_plusfp&lt;span class="w"&gt; &lt;/span&gt;--report&lt;span class="w"&gt; &lt;/span&gt;report.txt&lt;span class="w"&gt; &lt;/span&gt;--paired&lt;span class="w"&gt; &lt;/span&gt;SRR6915097_1.fastq.gz&lt;span class="w"&gt; &lt;/span&gt;SRR6915097_2.fastq.gz&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;output.txt
Loading&lt;span class="w"&gt; &lt;/span&gt;database&lt;span class="w"&gt; &lt;/span&gt;information...done.
&lt;span class="m"&gt;17121245&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sequences&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3397&lt;/span&gt;.15&lt;span class="w"&gt; &lt;/span&gt;Mbp&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;processed&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;755&lt;/span&gt;.290s&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1360&lt;/span&gt;.1&lt;span class="w"&gt; &lt;/span&gt;Kseq/m,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;269&lt;/span&gt;.87&lt;span class="w"&gt; &lt;/span&gt;Mbp/m&lt;span class="o"&gt;)&lt;/span&gt;.
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;9903824&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sequences&lt;span class="w"&gt; &lt;/span&gt;classified&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;57&lt;/span&gt;.85%&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;7217421&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sequences&lt;span class="w"&gt; &lt;/span&gt;unclassified&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;42&lt;/span&gt;.15%&lt;span class="o"&gt;)&lt;/span&gt;
kraken2&lt;span class="w"&gt; &lt;/span&gt;--db&lt;span class="w"&gt; &lt;/span&gt;k2_plusfp/&lt;span class="w"&gt; &lt;/span&gt;--report&lt;span class="w"&gt; &lt;/span&gt;report.txt&lt;span class="w"&gt; &lt;/span&gt;--paired&lt;span class="w"&gt; &lt;/span&gt;SRR6915097_1.fastq.gz&lt;span class="w"&gt;  &lt;/span&gt;&amp;gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="m"&gt;509&lt;/span&gt;.71s&lt;span class="w"&gt; &lt;/span&gt;user&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;509&lt;/span&gt;.51s&lt;span class="w"&gt; &lt;/span&gt;system&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;55&lt;/span&gt;%&lt;span class="w"&gt; &lt;/span&gt;cpu&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;30&lt;/span&gt;:35.49&lt;span class="w"&gt; &lt;/span&gt;total
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This took ~30 minutes to complete, but the classification took only 13 minutes similar to k2_standard. The remaining time was spent in loading the db into memory.&lt;/p&gt;
&lt;h4&gt;Preloading db into RAM&lt;/h4&gt;
&lt;p&gt;We can use vmtouch&lt;sup id="fnref:vmt"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2024/07/mastering-kraken2-performance-optimisation.html#fn:vmt"&gt;5&lt;/a&gt;&lt;/sup&gt; to preload db into RAM. kraken2 provides &lt;code&gt;--memory-mapping&lt;/code&gt; option to use preloaded db. &lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;vmtouch&lt;span class="w"&gt; &lt;/span&gt;-vt&lt;span class="w"&gt; &lt;/span&gt;k2_standard/hash.k2d&lt;span class="w"&gt; &lt;/span&gt;k2_standard/opts.k2d&lt;span class="w"&gt; &lt;/span&gt;k2_standard/taxo.k2d
&lt;span class="w"&gt;           &lt;/span&gt;Files:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;Directories:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;Touched&lt;span class="w"&gt; &lt;/span&gt;Pages:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;20382075&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;77G&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;Elapsed:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;434&lt;/span&gt;.77&lt;span class="w"&gt; &lt;/span&gt;seconds
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When Linux requires RAM, it will incrementally evict the db from memory. To prevent this, we can copy the db to shared memory (/dev/shm) and then use vmtouch to preload the db.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;cp&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;k2_standard&lt;span class="w"&gt; &lt;/span&gt;/dev/shm

$&lt;span class="w"&gt; &lt;/span&gt;vmtouch&lt;span class="w"&gt; &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;/dev/shm/*.k2d
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, let's run kraken2 with &lt;code&gt;--memory-mapping&lt;/code&gt; option.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kraken2&lt;span class="w"&gt; &lt;/span&gt;--db&lt;span class="w"&gt; &lt;/span&gt;k2_standard&lt;span class="w"&gt; &lt;/span&gt;--report&lt;span class="w"&gt; &lt;/span&gt;report.txt&lt;span class="w"&gt; &lt;/span&gt;--memory-mapping&lt;span class="w"&gt; &lt;/span&gt;--paired&lt;span class="w"&gt; &lt;/span&gt;SRR6915097_1.fastq.gz&lt;span class="w"&gt; &lt;/span&gt;SRR6915097_2.fastq.gz&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;output.txt
Loading&lt;span class="w"&gt; &lt;/span&gt;database&lt;span class="w"&gt; &lt;/span&gt;information...&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;done&lt;/span&gt;.
&lt;span class="m"&gt;17121245&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sequences&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3397&lt;/span&gt;.15&lt;span class="w"&gt; &lt;/span&gt;Mbp&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;processed&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;532&lt;/span&gt;.486s&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1929&lt;/span&gt;.2&lt;span class="w"&gt; &lt;/span&gt;Kseq/m,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;382&lt;/span&gt;.79&lt;span class="w"&gt; &lt;/span&gt;Mbp/m&lt;span class="o"&gt;)&lt;/span&gt;.
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;9826671&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sequences&lt;span class="w"&gt; &lt;/span&gt;classified&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;57&lt;/span&gt;.39%&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;7294574&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sequences&lt;span class="w"&gt; &lt;/span&gt;unclassified&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;42&lt;/span&gt;.61%&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;kraken2&lt;span class="w"&gt; &lt;/span&gt;--db&lt;span class="w"&gt; &lt;/span&gt;k2_standard&lt;span class="w"&gt; &lt;/span&gt;--report&lt;span class="w"&gt; &lt;/span&gt;report.txt&lt;span class="w"&gt; &lt;/span&gt;--paired&lt;span class="w"&gt; &lt;/span&gt;SRR6915097_1.fastq.gz&lt;span class="w"&gt;   &lt;/span&gt;&amp;gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;424&lt;/span&gt;.20s&lt;span class="w"&gt; &lt;/span&gt;user&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;11&lt;/span&gt;.76s&lt;span class="w"&gt; &lt;/span&gt;system&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;81&lt;/span&gt;%&lt;span class="w"&gt; &lt;/span&gt;cpu&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;:54.98&lt;span class="w"&gt; &lt;/span&gt;total
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now the classification took only ~10 minutes.&lt;/p&gt;
&lt;h4&gt;Multi threading&lt;/h4&gt;
&lt;p&gt;kraken2 supports multiple threads. I am using a machine with 40 threads.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kraken2&lt;span class="w"&gt; &lt;/span&gt;--db&lt;span class="w"&gt; &lt;/span&gt;k2_standard&lt;span class="w"&gt; &lt;/span&gt;--report&lt;span class="w"&gt; &lt;/span&gt;report.txt&lt;span class="w"&gt; &lt;/span&gt;--paired&lt;span class="w"&gt; &lt;/span&gt;SRR6915097_1.fastq.gz&lt;span class="w"&gt; &lt;/span&gt;SRR6915097_2.fastq.gz&lt;span class="w"&gt; &lt;/span&gt;--memory-mapping&lt;span class="w"&gt; &lt;/span&gt;--threads&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;output.txt
Loading&lt;span class="w"&gt; &lt;/span&gt;database&lt;span class="w"&gt; &lt;/span&gt;information...&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;done&lt;/span&gt;.
&lt;span class="m"&gt;17121245&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sequences&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3397&lt;/span&gt;.15&lt;span class="w"&gt; &lt;/span&gt;Mbp&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;processed&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;71&lt;/span&gt;.675s&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;14332&lt;/span&gt;.5&lt;span class="w"&gt; &lt;/span&gt;Kseq/m,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2843&lt;/span&gt;.81&lt;span class="w"&gt; &lt;/span&gt;Mbp/m&lt;span class="o"&gt;)&lt;/span&gt;.
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;9826671&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sequences&lt;span class="w"&gt; &lt;/span&gt;classified&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;57&lt;/span&gt;.39%&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;7294574&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sequences&lt;span class="w"&gt; &lt;/span&gt;unclassified&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;42&lt;/span&gt;.61%&lt;span class="o"&gt;)&lt;/span&gt;
kraken2&lt;span class="w"&gt; &lt;/span&gt;--db&lt;span class="w"&gt; &lt;/span&gt;k2_standard&lt;span class="w"&gt; &lt;/span&gt;--report&lt;span class="w"&gt; &lt;/span&gt;report.txt&lt;span class="w"&gt; &lt;/span&gt;--paired&lt;span class="w"&gt; &lt;/span&gt;SRR6915097_1.fastq.gz&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="m"&gt;556&lt;/span&gt;.58s&lt;span class="w"&gt; &lt;/span&gt;user&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;22&lt;/span&gt;.85s&lt;span class="w"&gt; &lt;/span&gt;system&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;762&lt;/span&gt;%&lt;span class="w"&gt; &lt;/span&gt;cpu&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;:16.02&lt;span class="w"&gt; &lt;/span&gt;total
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With 32 threads, the classification took only 1 minute. Beyond 32 threads, the classification time did not decrease significantly.&lt;/p&gt;
&lt;h4&gt;Optimising input files&lt;/h4&gt;
&lt;p&gt;So far we have used gzipped input files. Let's use unzipped input files and run kraken2.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;gunzip&lt;span class="w"&gt; &lt;/span&gt;SRR6915097_1.fastq.gz
$&lt;span class="w"&gt; &lt;/span&gt;gunzip&lt;span class="w"&gt; &lt;/span&gt;SRR6915097_2.fastq.gz

$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kraken2&lt;span class="w"&gt; &lt;/span&gt;--db&lt;span class="w"&gt; &lt;/span&gt;k2_standard&lt;span class="w"&gt; &lt;/span&gt;--report&lt;span class="w"&gt; &lt;/span&gt;report.txt&lt;span class="w"&gt; &lt;/span&gt;--paired&lt;span class="w"&gt; &lt;/span&gt;SRR6915097_1.fastq&lt;span class="w"&gt; &lt;/span&gt;SRR6915097_2.fastq&lt;span class="w"&gt; &lt;/span&gt;--memory-mapping&lt;span class="w"&gt; &lt;/span&gt;--threads&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;output.txt
Loading&lt;span class="w"&gt; &lt;/span&gt;database&lt;span class="w"&gt; &lt;/span&gt;information...&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;done&lt;/span&gt;.
&lt;span class="m"&gt;17121245&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sequences&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3397&lt;/span&gt;.15&lt;span class="w"&gt; &lt;/span&gt;Mbp&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;processed&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;34&lt;/span&gt;.809s&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;29512&lt;/span&gt;.0&lt;span class="w"&gt; &lt;/span&gt;Kseq/m,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;5855&lt;/span&gt;.68&lt;span class="w"&gt; &lt;/span&gt;Mbp/m&lt;span class="o"&gt;)&lt;/span&gt;.
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;9826671&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sequences&lt;span class="w"&gt; &lt;/span&gt;classified&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;57&lt;/span&gt;.39%&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;7294574&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sequences&lt;span class="w"&gt; &lt;/span&gt;unclassified&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;42&lt;/span&gt;.61%&lt;span class="o"&gt;)&lt;/span&gt;
kraken2&lt;span class="w"&gt; &lt;/span&gt;--db&lt;span class="w"&gt; &lt;/span&gt;k2_standard&lt;span class="w"&gt; &lt;/span&gt;--report&lt;span class="w"&gt; &lt;/span&gt;report.txt&lt;span class="w"&gt; &lt;/span&gt;--paired&lt;span class="w"&gt; &lt;/span&gt;SRR6915097_1.fastq&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="m"&gt;565&lt;/span&gt;.03s&lt;span class="w"&gt; &lt;/span&gt;user&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;17&lt;/span&gt;.12s&lt;span class="w"&gt; &lt;/span&gt;system&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1530&lt;/span&gt;%&lt;span class="w"&gt; &lt;/span&gt;cpu&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;38&lt;/span&gt;.047&lt;span class="w"&gt; &lt;/span&gt;total
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now the classification time has come down to 40 seconds.&lt;/p&gt;
&lt;p&gt;Since the input fastq files are paired, interleaving the files also takes time. Lets interleave the files and run kraken2.&lt;/p&gt;
&lt;p&gt;To interleave the files, lets use &lt;code&gt;seqfu&lt;/code&gt; tool.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;conda&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-y&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;conda-forge&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;bioconda&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"seqfu&amp;gt;1.10"&lt;/span&gt;

$&lt;span class="w"&gt; &lt;/span&gt;seqfu&lt;span class="w"&gt; &lt;/span&gt;interleave&lt;span class="w"&gt; &lt;/span&gt;-1&lt;span class="w"&gt; &lt;/span&gt;SRR6915097_1.fastq.gz&lt;span class="w"&gt; &lt;/span&gt;-2&lt;span class="w"&gt; &lt;/span&gt;SRR6915097_2.fastq.gz&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;SRR6915097.fastq

$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;kraken2&lt;span class="w"&gt; &lt;/span&gt;--db&lt;span class="w"&gt; &lt;/span&gt;k2_standard&lt;span class="w"&gt; &lt;/span&gt;--report&lt;span class="w"&gt; &lt;/span&gt;report.txt&lt;span class="w"&gt; &lt;/span&gt;--memory-mapping&lt;span class="w"&gt; &lt;/span&gt;SRR6915097.fq&lt;span class="w"&gt; &lt;/span&gt;--threads&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;output.txt
Loading&lt;span class="w"&gt; &lt;/span&gt;database&lt;span class="w"&gt; &lt;/span&gt;information...&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;done&lt;/span&gt;.
&lt;span class="m"&gt;34242490&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sequences&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3397&lt;/span&gt;.15&lt;span class="w"&gt; &lt;/span&gt;Mbp&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;processed&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;.199s&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;101714&lt;/span&gt;.1&lt;span class="w"&gt; &lt;/span&gt;Kseq/m,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10090&lt;/span&gt;.91&lt;span class="w"&gt; &lt;/span&gt;Mbp/m&lt;span class="o"&gt;)&lt;/span&gt;.
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;17983321&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sequences&lt;span class="w"&gt; &lt;/span&gt;classified&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;52&lt;/span&gt;.52%&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;16259169&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sequences&lt;span class="w"&gt; &lt;/span&gt;unclassified&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;47&lt;/span&gt;.48%&lt;span class="o"&gt;)&lt;/span&gt;
kraken2&lt;span class="w"&gt; &lt;/span&gt;--db&lt;span class="w"&gt; &lt;/span&gt;k2_standard&lt;span class="w"&gt; &lt;/span&gt;--report&lt;span class="w"&gt; &lt;/span&gt;report.txt&lt;span class="w"&gt; &lt;/span&gt;--memory-mapping&lt;span class="w"&gt; &lt;/span&gt;SRR6915097.fq&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;32&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;618&lt;/span&gt;.96s&lt;span class="w"&gt; &lt;/span&gt;user&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;18&lt;/span&gt;.24s&lt;span class="w"&gt; &lt;/span&gt;system&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2653&lt;/span&gt;%&lt;span class="w"&gt; &lt;/span&gt;cpu&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;24&lt;/span&gt;.013&lt;span class="w"&gt; &lt;/span&gt;total
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now the classification time has come down to 24 seconds. &lt;/p&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;In terms of classification speed, we have come a long way from 0.1 Mbp/m to 1200 Mbp/m. In the next post, we will learn how to optimise the creation of custom indices.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:k2"&gt;
&lt;p&gt;&lt;a href="https://ccb.jhu.edu/software/kraken2/"&gt;Kraken2&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2024/07/mastering-kraken2-performance-optimisation.html#fnref:k2" title="Jump back to footnote 1 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:ksr"&gt;
&lt;p&gt;&lt;a href="https://github.com/DerrickWood/kraken2/blob/master/docs/MANUAL.markdown#system-requirements"&gt;Kraken System Requirements&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2024/07/mastering-kraken2-performance-optimisation.html#fnref:ksr" title="Jump back to footnote 2 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:err"&gt;
&lt;p&gt;&lt;a href="ftp://ftp.sra.ebi.ac.uk/vol1/fastq/ERR103/077/ERR10359977/ERR10359977.fastq.gz"&gt;ERR10359977.fastq.gz&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2024/07/mastering-kraken2-performance-optimisation.html#fnref:err" title="Jump back to footnote 3 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:k2p"&gt;
&lt;p&gt;&lt;a href="https://benlangmead.github.io/aws-indexes/k2"&gt;Genomic Index Zone - k2&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2024/07/mastering-kraken2-performance-optimisation.html#fnref:k2p" title="Jump back to footnote 4 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:vmt"&gt;
&lt;p&gt;&lt;a href="https://hoytech.com/vmtouch/"&gt;https://hoytech.com/vmtouch/&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2024/07/mastering-kraken2-performance-optimisation.html#fnref:vmt" title="Jump back to footnote 5 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>bioinformatics</category><category>devops</category><category>kraken2</category><category>metagenomics</category><guid>https://avilpage.com/2024/07/mastering-kraken2-performance-optimisation.html</guid><pubDate>Sun, 28 Jul 2024 05:21:30 GMT</pubDate></item><item><title>Headlamp - k8s Lens open source alternative</title><link>https://avilpage.com/2024/06/headlamp-k8s-lens-open-source-alternative.html</link><dc:creator>Anand Reddy Pandikunta</dc:creator><description>&lt;p&gt;&lt;img alt="headlamp - Open source Kubernetes Lens alternator" src="https://avilpage.com/images/headlamp-k8s-lens-open-source-alternative.png"&gt;&lt;/p&gt;
&lt;p&gt;Since Lens is not open source, I tried out monokle, octant, k9s, and headlamp&lt;sup id="fnref:headlamp"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2024/06/headlamp-k8s-lens-open-source-alternative.html#fn:headlamp"&gt;1&lt;/a&gt;&lt;/sup&gt;. Among them, headlamp UI &amp;amp; features are closest to Lens. &lt;/p&gt;
&lt;h4&gt;Headlamp&lt;/h4&gt;
&lt;p&gt;Headlamp is CNCF sandbox project that provides cross-platform desktop application to manage Kubernetes clusters. It auto-detects clusters and provides cluster wide resource usage by default. &lt;/p&gt;
&lt;p&gt;It can also be installed inside the cluster and can be accessed using a web browser. This is useful when we want to access the cluster from a mobile device.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;helm&lt;span class="w"&gt; &lt;/span&gt;repo&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;headlamp&lt;span class="w"&gt; &lt;/span&gt;https://headlamp-k8s.github.io/headlamp/

$&lt;span class="w"&gt; &lt;/span&gt;helm&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;headlamp&lt;span class="w"&gt; &lt;/span&gt;headlamp/headlamp
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Lets port-forward the service &amp;amp; copy the token to access it.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;create&lt;span class="w"&gt; &lt;/span&gt;token&lt;span class="w"&gt; &lt;/span&gt;headlamp

&lt;span class="c1"&gt;# we can do this via headlamp UI as well&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;port-forward&lt;span class="w"&gt; &lt;/span&gt;service/headlamp&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;8080&lt;/span&gt;:80
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, we can access the headlamp UI at &lt;a href="http://"&gt;http://localhost:8080&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="headlamp - Open source Kubernetes Lens alternator" src="https://avilpage.com/images/headlamp-k8s-lens-open-source-alternative2.png"&gt;&lt;/p&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;If you are looking for an open source alternative to Lens, headlamp is a good choice. It provides a similar UI &amp;amp; features as Lens, and it is accessible via mobile devices as well. &lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:headlamp"&gt;
&lt;p&gt;&lt;a href="https://headlamp.dev/"&gt;https://headlamp.dev/&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2024/06/headlamp-k8s-lens-open-source-alternative.html#fnref:headlamp" title="Jump back to footnote 1 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>devops</category><category>kubernetes</category><guid>https://avilpage.com/2024/06/headlamp-k8s-lens-open-source-alternative.html</guid><pubDate>Sun, 23 Jun 2024 20:18:02 GMT</pubDate></item><item><title>macOS - Log &amp; track historical CPU, RAM usage</title><link>https://avilpage.com/2024/06/macos-log-track-cpu-ram-usage.html</link><dc:creator>Anand Reddy Pandikunta</dc:creator><description>&lt;p&gt;&lt;img alt="macOS - Log CPU &amp;amp; RAM history" src="https://avilpage.com/images/mac-log-cpu-ram-grafana.png"&gt;&lt;/p&gt;
&lt;p&gt;In macOS, we can use inbuilt &lt;code&gt;Activity Monitor&lt;/code&gt; or third party apps like &lt;code&gt;Stats&lt;/code&gt; to check the live CPU/RAM usage. But, we can't track the historical CPU &amp;amp; memory usage. &lt;code&gt;sar&lt;/code&gt;, &lt;code&gt;atop&lt;/code&gt; can track the historical CPU &amp;amp; memory usage. But, they are not available for macOS.&lt;/p&gt;
&lt;h4&gt;Netdata&lt;/h4&gt;
&lt;p&gt;Netdata&lt;sup id="fnref:Netdata"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2024/06/macos-log-track-cpu-ram-usage.html#fn:Netdata"&gt;1&lt;/a&gt;&lt;/sup&gt; is an open source observability tool that can monitor CPU, RAM, network, disk usage. It can also track the historical data. &lt;/p&gt;
&lt;p&gt;Unfortunately, it is not stable on macOS. I tried installing it on multiple macbooks, but it didn't work. I raised an issue&lt;sup id="fnref:netdata_issue"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2024/06/macos-log-track-cpu-ram-usage.html#fn:netdata_issue"&gt;2&lt;/a&gt;&lt;/sup&gt; on their GitHub repository and the team mentioned that macOS is a low priority for them.&lt;/p&gt;
&lt;h4&gt;Glances&lt;/h4&gt;
&lt;p&gt;Glances&lt;sup id="fnref:Glances"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2024/06/macos-log-track-cpu-ram-usage.html#fn:Glances"&gt;3&lt;/a&gt;&lt;/sup&gt; is a cross-platform monitoring tool that can monitor CPU, RAM, network, disk usage. It can also track the historical data.&lt;/p&gt;
&lt;p&gt;We can install it using Brew or pip.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;brew&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;glances

$&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;glances
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once it is installed, we can monitor the resource usage using the below command.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;glances
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="macOS - Log CPU &amp;amp; RAM history" src="https://avilpage.com/images/mac-log-cpu-ram-glances.png"&gt;&lt;/p&gt;
&lt;p&gt;Glances can log historical data to a file using the below command.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;glances&lt;span class="w"&gt; &lt;/span&gt;--export-csv&lt;span class="w"&gt; &lt;/span&gt;/tmp/glances.csv
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In addition to that, it can log data to services like influxdb, prometheus, etc.&lt;/p&gt;
&lt;p&gt;Let's install influxdb and export stats to it.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;brew&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;influxdb
$&lt;span class="w"&gt; &lt;/span&gt;brew&lt;span class="w"&gt; &lt;/span&gt;services&lt;span class="w"&gt; &lt;/span&gt;start&lt;span class="w"&gt; &lt;/span&gt;influxdb
$&lt;span class="w"&gt; &lt;/span&gt;influx&lt;span class="w"&gt; &lt;/span&gt;setup

$&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;influxdb-client

$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;glances.conf
&lt;span class="o"&gt;[&lt;/span&gt;influxdb&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;localhost
&lt;span class="nv"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;8086&lt;/span&gt;
&lt;span class="nv"&gt;protocol&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http
&lt;span class="nv"&gt;org&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;avilpage
&lt;span class="nv"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;glances
&lt;span class="nv"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secret_token

$&lt;span class="w"&gt; &lt;/span&gt;glances&lt;span class="w"&gt; &lt;/span&gt;--export-influxdb&lt;span class="w"&gt; &lt;/span&gt;-C&lt;span class="w"&gt; &lt;/span&gt;glances.conf
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can view stats in the influxdb from Data Explorer web UI at &lt;a href="http://localhost:8086"&gt;http://localhost:8086&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="macOS - Log CPU &amp;amp; RAM history" src="https://avilpage.com/images/mac-log-cpu-ram-influxdb.png"&gt;&lt;/p&gt;
&lt;p&gt;Glances provides a prebuilt Grafana dashboard&lt;sup id="fnref:grafana_dashboard"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2024/06/macos-log-track-cpu-ram-usage.html#fn:grafana_dashboard"&gt;4&lt;/a&gt;&lt;/sup&gt; that we can import to visualize the stats. &lt;/p&gt;
&lt;p&gt;From Grafana -&amp;gt; Dashboard -&amp;gt; Import, we can import the dashboard using the above URL.&lt;/p&gt;
&lt;p&gt;&lt;img alt="macOS - Log CPU &amp;amp; RAM history" src="https://avilpage.com/images/mac-log-cpu-ram-grafana.png"&gt;&lt;/p&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;In addition to InfluxDB, Glances can export data to ~20 services. So far, it is the best tool to log, track and view historical CPU, RAM, network and disk usage in macOS. The same method works for Linux and Windows as well.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:Netdata"&gt;
&lt;p&gt;&lt;a href="https://github.com/netdata/netdata"&gt;https://github.com/netdata/netdata&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2024/06/macos-log-track-cpu-ram-usage.html#fnref:Netdata" title="Jump back to footnote 1 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:netdata_issue"&gt;
&lt;p&gt;&lt;a href="https://github.com/netdata/netdata/issues/16696"&gt;https://github.com/netdata/netdata/issues/16696&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2024/06/macos-log-track-cpu-ram-usage.html#fnref:netdata_issue" title="Jump back to footnote 2 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:Glances"&gt;
&lt;p&gt;&lt;a href="https://github.com/nicolargo/glances"&gt;https://github.com/niolargo/glances&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2024/06/macos-log-track-cpu-ram-usage.html#fnref:Glances" title="Jump back to footnote 3 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:grafana_dashboard"&gt;
&lt;p&gt;&lt;a href="https://glances.readthedocs.io/en/latest/gw/influxdb.html#grafana"&gt;https://glances.readthedocs.io/en/latest/gw/influxdb.html#grafana&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2024/06/macos-log-track-cpu-ram-usage.html#fnref:grafana_dashboard" title="Jump back to footnote 4 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>devops</category><category>macbook</category><category>python</category><guid>https://avilpage.com/2024/06/macos-log-track-cpu-ram-usage.html</guid><pubDate>Fri, 31 May 2024 20:18:02 GMT</pubDate></item><item><title>Timestamp to Relative Time - Kibana Scripted fields</title><link>https://avilpage.com/2024/04/timestamp-to-relative-time-kibana-scripted-fields.html</link><dc:creator>Anand Reddy Pandikunta</dc:creator><description>&lt;p&gt;When browsing logs in Kibana, there will be a timestamp stamp field on the left for all the docs. It is difficult to read &amp;amp; comprehend the timestamp in the logs. It would be better if we can convert the timestamp to a human-readable relative time like &lt;code&gt;5 minutes ago&lt;/code&gt;, &lt;code&gt;1 hour ago&lt;/code&gt;, etc.&lt;/p&gt;
&lt;h4&gt;Kibana Scripted Fields&lt;/h4&gt;
&lt;p&gt;Kibana provides a feature called scripted fields to create new fields in the index pattern. We can use this feature to convert the timestamp to a relative time.&lt;/p&gt;
&lt;p&gt;&lt;img alt="kibana-relative-time" src="https://avilpage.com/images/kibana-relative-time1.png"&gt;&lt;/p&gt;
&lt;p&gt;Go to &lt;code&gt;Stack Management&lt;/code&gt; -&amp;gt; &lt;code&gt;Index Patterns&lt;/code&gt; -&amp;gt; &lt;code&gt;Create index pattern&lt;/code&gt; -&amp;gt; Select the index pattern -&amp;gt; &lt;code&gt;Scripted fields&lt;/code&gt;, click on &lt;code&gt;Add scripted field&lt;/code&gt;, add the below script.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;timestamp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;'@timestamp'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toInstant&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;toEpochMilli&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;long&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7200000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3600000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;" hours ago"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3600000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3600000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;" hour ago"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;" minutes ago"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;" minute ago"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;" seconds ago"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once the field is saved, we can go back to &lt;code&gt;Discover&lt;/code&gt; and see the new field in the logs. We can toggle the visibility of the &lt;code&gt;Relative Time&lt;/code&gt; field to see the relative time.&lt;/p&gt;
&lt;p&gt;&lt;img alt="kibana-relative-time" src="https://avilpage.com/images/kibana-relative-time2.png"&gt;&lt;/p&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;Instead of looking at the timestamp and calculating the relative time in our head, we can use relative time in Kibana . This will make it easier to read &amp;amp; comprehend the logs.&lt;/p&gt;</description><category>devops</category><category>kibana</category><category>monitoring</category><guid>https://avilpage.com/2024/04/timestamp-to-relative-time-kibana-scripted-fields.html</guid><pubDate>Wed, 10 Apr 2024 08:45:33 GMT</pubDate></item><item><title>Remotely Access Raspberry Pi or Any Device</title><link>https://avilpage.com/2023/09/tailscale-remote-ssh-raspberry-pi.html</link><dc:creator>Anand Reddy Pandikunta</dc:creator><description>&lt;p&gt;I recently started using Raspberry Pi and I wanted to access it when I am outside of home as well. After trying out few solutions, I stumbled upon Tailscale&lt;sup id="fnref:tailscale"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2023/09/tailscale-remote-ssh-raspberry-pi.html#fn:tailscale"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Tailscale is a mesh VPN that makes it easy to connect out devices, wherever they are. It is free for personal use and supports all major platforms like Linux, Windows, Mac, Android, iOS, etc.&lt;/p&gt;
&lt;h4&gt;Installation&lt;/h4&gt;
&lt;p&gt;I installed tailscale on Raspberry Pi using the following command.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-fsSL&lt;span class="w"&gt; &lt;/span&gt;https://tailscale.com/install.sh&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sh
&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Setup&lt;/h4&gt;
&lt;p&gt;Once the installation is done, I run &lt;code&gt;tailscale up&lt;/code&gt; to start the daemon. This opened a browser window and asked me to log in with email address. After I logged in, I can see all the devices in the tailscale dashboard.&lt;/p&gt;
&lt;p&gt;&lt;img alt="tailscale dashboard" src="https://avilpage.com/images/tailscale-pi.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;tailscale&lt;/code&gt; has CLI tool as well and status can be viewed with the following command.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;tailscale&lt;span class="w"&gt; &lt;/span&gt;status
&lt;span class="m"&gt;100&lt;/span&gt;.81.13.75&lt;span class="w"&gt;   &lt;/span&gt;m1&lt;span class="w"&gt;                    &lt;/span&gt;avilpage@&lt;span class="w"&gt;  &lt;/span&gt;macOS&lt;span class="w"&gt;   &lt;/span&gt;-
&lt;span class="m"&gt;100&lt;/span&gt;.12.12.92&lt;span class="w"&gt;   &lt;/span&gt;rpi1.tailscale.ts.net&lt;span class="w"&gt; &lt;/span&gt;avilpage@&lt;span class="w"&gt;  &lt;/span&gt;linux&lt;span class="w"&gt;   &lt;/span&gt;offline
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I also set up a cron job to start tailscale daemon on boot.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;crontab&lt;span class="w"&gt; &lt;/span&gt;-e
@reboot&lt;span class="w"&gt; &lt;/span&gt;tailscale&lt;span class="w"&gt; &lt;/span&gt;up
&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Access&lt;/h4&gt;
&lt;p&gt;Now I can access the device from anywhere using the tailscale IP address. For example, if the IP address is &lt;code&gt;100.34.2.23&lt;/code&gt;. I can ssh into the device using the following command.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;ssh&lt;span class="w"&gt; &lt;/span&gt;pi@100.81.12.92
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It also provides DNS names for each device. For example, I can ssh into the device using the following command as well.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;ssh&lt;span class="w"&gt; &lt;/span&gt;pi@raspberry3.tailscale.net
&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;Tailscale is a great tool to access devices remotely. It is easy to set up and works well with Raspberry Pi, Mac &amp;amp; Linux as well.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:tailscale"&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Tailscale"&gt;https://en.wikipedia.org/wiki/Tailscale&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2023/09/tailscale-remote-ssh-raspberry-pi.html#fnref:tailscale" title="Jump back to footnote 1 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>devops</category><category>raspberry_pi</category><category>tailscale</category><guid>https://avilpage.com/2023/09/tailscale-remote-ssh-raspberry-pi.html</guid><pubDate>Mon, 25 Sep 2023 01:49:54 GMT</pubDate></item><item><title>Record Resource Usage of Single Process</title><link>https://avilpage.com/2023/04/record-resource-usage-per-process.html</link><dc:creator>Anand Reddy Pandikunta</dc:creator><description>&lt;h4&gt;Introduction&lt;/h4&gt;
&lt;p&gt;On Linux &amp;amp; Mac, we can use an inbuilt &lt;code&gt;top&lt;/code&gt; command line tool to monitor the resource usage of a single process in real time. &lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# On Linux, for a given pid&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;top&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1234&lt;/span&gt;

&lt;span class="c1"&gt;# On Mac, for a given pid&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;top&lt;span class="w"&gt; &lt;/span&gt;-pid&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1234&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In this article, we will see how to record and plot resource usage of a single process using &lt;code&gt;top&lt;/code&gt; and a Python package called psrecord&lt;sup id="fnref:psrecord"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2023/04/record-resource-usage-per-process.html#fn:psrecord"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h4&gt;Record Resource Usage&lt;/h4&gt;
&lt;p&gt;In some cases, we need to record the resource usage of a process to use it later. For example, we can use this data to find out the peak resource usage of a process. For this, we can use &lt;code&gt;top&lt;/code&gt; to log resource usage into a text file. &lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# On Linux, for a given pid&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;top&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1234&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-b&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;top.log

&lt;span class="c1"&gt;# On Mac, for a given pid&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;top&lt;span class="w"&gt; &lt;/span&gt;-l&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-pid&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;32515&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;awk&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'NR%13==0; fflush(stdout)'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;top.log
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once we have the log file, we can view the raw data or we can plot the resource usage by using tools like &lt;code&gt;gnuplot&lt;/code&gt; or &lt;code&gt;matplotlib&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Instead of using &lt;code&gt;top&lt;/code&gt; command, we can use &lt;code&gt;psrecord&lt;/code&gt; to record the resource usage of a process. &lt;code&gt;psrecord&lt;/code&gt; is a Python package that can be installed all using &lt;code&gt;pip&lt;/code&gt;. &lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;psrecord
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once installed, we can use &lt;code&gt;psrecord&lt;/code&gt; to record the resource usage of a process. &lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# record resource usage of a process with pid 1234&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;psrecord&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1234&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--log&lt;span class="w"&gt; &lt;/span&gt;top.log

&lt;span class="c1"&gt;# start and record resource usage of a process&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;psrecord&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;script.py&lt;span class="w"&gt; &lt;/span&gt;--plot&lt;span class="w"&gt; &lt;/span&gt;graph.png
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can view the raw data in the log file.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# view raw data&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;head&lt;span class="w"&gt; &lt;/span&gt;top.log
$&lt;span class="w"&gt; &lt;/span&gt;head&lt;span class="w"&gt; &lt;/span&gt;a.txt
&lt;span class="c1"&gt;# Elapsed time   CPU (%)     Real (MB)   Virtual (MB)&lt;/span&gt;
&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.000&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.000&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;.000&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="m"&gt;399461&lt;/span&gt;.438
&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.000&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="m"&gt;93&lt;/span&gt;.700&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;.000&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="m"&gt;399461&lt;/span&gt;.438
&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.000&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="m"&gt;96&lt;/span&gt;.300&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;.000&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="m"&gt;399461&lt;/span&gt;.438
&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.000&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="m"&gt;91&lt;/span&gt;.900&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;.000&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="m"&gt;399461&lt;/span&gt;.438
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Here is the generated graph.&lt;/p&gt;
&lt;p align="center"&gt;
&lt;img src="https://avilpage.com/images/single-proc-resource.png" alt="single-proc-resource"&gt;
&lt;/p&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;In this article, we have seen how to record and plot resource usage of a single process using top(inbuilt tool), psrecord(3rd party package).&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:psrecord"&gt;
&lt;p&gt;&lt;a href="https://pypi.org/project/psrecord/"&gt;https://pypi.org/project/psrecord/&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2023/04/record-resource-usage-per-process.html#fnref:psrecord" title="Jump back to footnote 1 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>devops</category><category>linux</category><category>macbook</category><guid>https://avilpage.com/2023/04/record-resource-usage-per-process.html</guid><pubDate>Fri, 14 Apr 2023 00:48:37 GMT</pubDate></item><item><title>Setup Kubernetes Anywhere with Single Command</title><link>https://avilpage.com/2023/03/setup-k8s-anywhere-k3d.html</link><dc:creator>Anand Reddy Pandikunta</dc:creator><description>&lt;div class="embed-responsive embed-responsive-16by9"&gt;
&lt;iframe class="embed-responsive-item" src="https://www.youtube.com/embed/Vo0mAsXe-hI" allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h4&gt;Introduction&lt;/h4&gt;
&lt;p&gt;In an earlier article, we have seen how to set up &lt;a href="https://avilpage.com/2022/10/local-kubernetes-with-k3s-on-mac.html"&gt;Kubernetes on M1 Mac&lt;/a&gt;. That involved spinning up a VM and installing Kubernetes&lt;sup id="fnref:kubernetes"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2023/03/setup-k8s-anywhere-k3d.html#fn:kubernetes"&gt;1&lt;/a&gt;&lt;/sup&gt; on it. In this article, we will see how to set up Kubernetes directly on Docker so that we can use the same set-up on any operating system.&lt;/p&gt;
&lt;h4&gt;Prerequisites&lt;/h4&gt;
&lt;p&gt;Ensure you have Docker installed on your system. If you are on a Mac or Windows, you can install Docker Desktop&lt;sup id="fnref:docker-desktop"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2023/03/setup-k8s-anywhere-k3d.html#fn:docker-desktop"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h4&gt;k3s/k3d&lt;/h4&gt;
&lt;p&gt;k3s&lt;sup id="fnref:k3s"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2023/03/setup-k8s-anywhere-k3d.html#fn:k3s"&gt;3&lt;/a&gt;&lt;/sup&gt; is a lightweight Kubernetes distribution by Rancher. It is a single binary that can be run on any Linux machine. But it doesn't work on Mac or Windows.&lt;/p&gt;
&lt;p&gt;k3d&lt;sup id="fnref:k3d"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2023/03/setup-k8s-anywhere-k3d.html#fn:k3d"&gt;4&lt;/a&gt;&lt;/sup&gt; is a wrapper around k3s that allows you to run k3s on Docker. It is a great option for running Kubernetes on your local machine.&lt;/p&gt;
&lt;h4&gt;Installation&lt;/h4&gt;
&lt;p&gt;k3d can be installed using the following command:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;brew&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;k3d&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# mac&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;chocolatey&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;k3d&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# windows&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bash&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# linux&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once it is installed, we can create a cluster using the following command:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;k3d&lt;span class="w"&gt; &lt;/span&gt;cluster&lt;span class="w"&gt; &lt;/span&gt;create&lt;span class="w"&gt; &lt;/span&gt;demo
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will launch a cluster with a single node. We can also setup a multi-node cluster using the following command:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;k3d&lt;span class="w"&gt; &lt;/span&gt;cluster&lt;span class="w"&gt; &lt;/span&gt;create&lt;span class="w"&gt; &lt;/span&gt;demo&lt;span class="w"&gt; &lt;/span&gt;--servers&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--agents&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can verify the cluster is up and running using the following command:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;get&lt;span class="w"&gt; &lt;/span&gt;nodes
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can also use GUI tools like Lens to manage and navigate the cluster. In the above video we have used Lens to create a Jenkins deployment as well.&lt;/p&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;In this article, we have seen how to set up Kubernetes on Docker. This is a great option for running Kubernetes on your local machine. We can also use this to run production setup for small applications.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:kubernetes"&gt;
&lt;p&gt;&lt;a href="https://kubernetes.io/"&gt;https://kubernetes.io/&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2023/03/setup-k8s-anywhere-k3d.html#fnref:kubernetes" title="Jump back to footnote 1 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:docker-desktop"&gt;
&lt;p&gt;&lt;a href="https://www.docker.com/products/docker-desktop/"&gt;https://www.docker.com/products/docker-desktop/&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2023/03/setup-k8s-anywhere-k3d.html#fnref:docker-desktop" title="Jump back to footnote 2 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:k3s"&gt;
&lt;p&gt;&lt;a href="https://k3s.io/"&gt;https://k3s.io/&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2023/03/setup-k8s-anywhere-k3d.html#fnref:k3s" title="Jump back to footnote 3 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:k3d"&gt;
&lt;p&gt;&lt;a href="https://k3d.io/"&gt;https://k3d.io/&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2023/03/setup-k8s-anywhere-k3d.html#fnref:k3d" title="Jump back to footnote 4 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>devops</category><category>docker</category><category>kubernetes</category><guid>https://avilpage.com/2023/03/setup-k8s-anywhere-k3d.html</guid><pubDate>Fri, 03 Mar 2023 21:25:27 GMT</pubDate></item><item><title>Speed Up AMD64(Intel) VMs on ARM(M1 Mac) Host</title><link>https://avilpage.com/2022/10/speedup-amd64-containers-on-arm.html</link><dc:creator>Anand Reddy Pandikunta</dc:creator><description>&lt;h4&gt;Introduction&lt;/h4&gt;
&lt;p&gt;From 2020, Apple has transitioned from Intel to ARM based Apple Silicon M1. If we run &lt;code&gt;uname -mp&lt;/code&gt; on these devices, we can see the CPU architecture details.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;uname&lt;span class="w"&gt; &lt;/span&gt;-mp
arm64&lt;span class="w"&gt; &lt;/span&gt;arm
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let's run the same command on a device using Intel x86 processor.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;uname&lt;span class="w"&gt; &lt;/span&gt;-mp
x86_64&lt;span class="w"&gt; &lt;/span&gt;x86_64
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Many popular docker images&lt;sup id="fnref:dhub"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2022/10/speedup-amd64-containers-on-arm.html#fn:dhub"&gt;1&lt;/a&gt;&lt;/sup&gt; doesn't have ARM64 support yet. When setting up a dev environment in M1 Mac, there are high chances that we stumble on these containers if we are using plain docker or ARM64 VM. So, there is a need to spin up x86_64 VMs.&lt;/p&gt;
&lt;p&gt;In this article, lets see how the performance affects when running a cross architecture containers and how to speed it up.&lt;/p&gt;
&lt;h4&gt;Setup&lt;/h4&gt;
&lt;p&gt;Lima&lt;sup id="fnref:lima"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2022/10/speedup-amd64-containers-on-arm.html#fn:lima"&gt;2&lt;/a&gt;&lt;/sup&gt; can run foreign architecture(x6_64) VMs on Mac. Let's install lima, start a AMD64 VM &amp;amp; ARM64 VM and install k3s[^k3s] in them. k3s will run multiple process in the background and let's see how resource consumption varies in these VMs.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;brew&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;lima

$&lt;span class="w"&gt; &lt;/span&gt;limactl&lt;span class="w"&gt; &lt;/span&gt;start&lt;span class="w"&gt; &lt;/span&gt;linux_arm64
$&lt;span class="w"&gt; &lt;/span&gt;limactl&lt;span class="w"&gt; &lt;/span&gt;start&lt;span class="w"&gt; &lt;/span&gt;linux_amd64
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When starting a VM, we can edit &lt;code&gt;arch&lt;/code&gt; parameter in the configuration. Once VM starts, we can see the details by running &lt;code&gt;limactl list&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;limactl&lt;span class="w"&gt; &lt;/span&gt;list
NAME&lt;span class="w"&gt;                &lt;/span&gt;ARCH
linux_amd64&lt;span class="w"&gt;         &lt;/span&gt;x86_64
linux_arm64&lt;span class="w"&gt;         &lt;/span&gt;aarch64
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Lets login to each VM &amp;amp; install k3s.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;limactl&lt;span class="w"&gt; &lt;/span&gt;shell&lt;span class="w"&gt; &lt;/span&gt;linux_arm64

$&lt;span class="w"&gt; &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-sfL&lt;span class="w"&gt; &lt;/span&gt;https://get.k3s.io&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sh&lt;span class="w"&gt; &lt;/span&gt;-
&lt;/pre&gt;&lt;/div&gt;

&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;limactl&lt;span class="w"&gt; &lt;/span&gt;shell&lt;span class="w"&gt; &lt;/span&gt;linux_amd64

$&lt;span class="w"&gt; &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-sfL&lt;span class="w"&gt; &lt;/span&gt;https://get.k3s.io&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sh&lt;span class="w"&gt; &lt;/span&gt;-
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If we look at resource consumption on the host machine, x86_84 VM is using way more resources than ARM64 VM. This is because of the emulation layer that is running on top of the VM.&lt;/p&gt;
&lt;p align="center"&gt;
&lt;img src="https://avilpage.com/images/arch-arm-docker.png"&gt;
&lt;/p&gt;

&lt;p&gt;We can login to individual VMs, run &lt;code&gt;top&lt;/code&gt; to see the load average as well.&lt;/p&gt;
&lt;h4&gt;Fast Mode&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;lima&lt;/code&gt; provides &lt;code&gt;fast-mode&lt;/code&gt; option for cross architecture VMs which will speed up the performance.&lt;/p&gt;
&lt;p&gt;For that, we need to log in to VMs and install emulators.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;start&lt;span class="w"&gt; &lt;/span&gt;containerd
$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;nerdctl&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;--privileged&lt;span class="w"&gt; &lt;/span&gt;--rm&lt;span class="w"&gt; &lt;/span&gt;tonistiigi/binfmt&lt;span class="w"&gt; &lt;/span&gt;--install&lt;span class="w"&gt; &lt;/span&gt;all
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;After that we can restart the VMs and monitor the resource consumption. On an average, we can see that the resource consumption is reduced by 50%.&lt;/p&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;In this article, we saw how to run cross architecture VMs on M1 Mac and how to speed up the performance. We can use this technique to run cross-architecture containers on Linux as well.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:dhub"&gt;
&lt;p&gt;&lt;a href="https://hub.docker.com/search?q=&amp;amp;page=10"&gt;https://hub.docker.com/search?q=&amp;amp;page=10&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2022/10/speedup-amd64-containers-on-arm.html#fnref:dhub" title="Jump back to footnote 1 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:lima"&gt;
&lt;p&gt;&lt;a href="https://github.com/lima-vm/lima"&gt;https://github.com/lima-vm/lima&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2022/10/speedup-amd64-containers-on-arm.html#fnref:lima" title="Jump back to footnote 2 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>devops</category><category>linux</category><category>macbook</category><guid>https://avilpage.com/2022/10/speedup-amd64-containers-on-arm.html</guid><pubDate>Thu, 20 Oct 2022 17:08:56 GMT</pubDate></item><item><title>Local Kubernetes Cluster with K3s on MacBook M1</title><link>https://avilpage.com/2022/10/local-kubernetes-with-k3s-on-mac.html</link><dc:creator>Anand Reddy Pandikunta</dc:creator><description>&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; This article is outdated. I wrote an updated article on &lt;a href="https://avilpage.com/2023/03/setup-k8s-anywhere-k3d.html"&gt;setting up k8s anywhere with single command&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;div class="embed-responsive embed-responsive-16by9"&gt;
&lt;iframe class="embed-responsive-item" src="https://www.youtube.com/embed/vi5eRgBMs90" allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;

&lt;h4&gt;Introduction&lt;/h4&gt;
&lt;p&gt;Kubernetes(k8s)&lt;sup id="fnref:k8s"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2022/10/local-kubernetes-with-k3s-on-mac.html#fn:k8s"&gt;1&lt;/a&gt;&lt;/sup&gt; is an open-source system for managing large scale containerized applications. K3s&lt;sup id="fnref:k3s"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2022/10/local-kubernetes-with-k3s-on-mac.html#fn:k3s"&gt;2&lt;/a&gt;&lt;/sup&gt; is lightweight K8s in a single binary file. However, K3s won't work directly on Macbook as it needs systemd/OpenRC.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;curl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sfL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;k3s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sh&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ERROR&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;Can&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;find&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;systemd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;openrc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;supervisor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;k3s&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To setup k8s/k3s on Mac, we need to setup a Linux layer on top of Mac. An easy way to spin up Linux VMs on Macbook M1(Apple Silicon) is to use multipass&lt;sup id="fnref:multipass"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2022/10/local-kubernetes-with-k3s-on-mac.html#fn:multipass"&gt;3&lt;/a&gt;&lt;/sup&gt;. In this article, lets see how to setup K3s on Mac using multipass&lt;/p&gt;
&lt;h4&gt;K3s Setup&lt;/h4&gt;
&lt;p&gt;Install multipass with brew by running the following command.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;brew&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;--cask&lt;span class="w"&gt; &lt;/span&gt;multipass
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once it is installed, spin up a new VM by specifying memory and disk space.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;multipass&lt;span class="w"&gt; &lt;/span&gt;launch&lt;span class="w"&gt; &lt;/span&gt;--name&lt;span class="w"&gt; &lt;/span&gt;k3s&lt;span class="w"&gt; &lt;/span&gt;--mem&lt;span class="w"&gt; &lt;/span&gt;4G&lt;span class="w"&gt; &lt;/span&gt;--disk&lt;span class="w"&gt; &lt;/span&gt;40G
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once VM is launched, we can see VM details.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;multipass&lt;span class="w"&gt; &lt;/span&gt;info&lt;span class="w"&gt; &lt;/span&gt;k3s
Name:&lt;span class="w"&gt;           &lt;/span&gt;k3s
State:&lt;span class="w"&gt;          &lt;/span&gt;Running
IPv4:&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="m"&gt;192&lt;/span&gt;.168.64.4
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;.42.0.0
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;.42.0.1
Release:&lt;span class="w"&gt;        &lt;/span&gt;Ubuntu&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;22&lt;/span&gt;.04.1&lt;span class="w"&gt; &lt;/span&gt;LTS
Image&lt;span class="w"&gt; &lt;/span&gt;hash:&lt;span class="w"&gt;     &lt;/span&gt;78b5ca0da456&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;Ubuntu&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;22&lt;/span&gt;.04&lt;span class="w"&gt; &lt;/span&gt;LTS&lt;span class="o"&gt;)&lt;/span&gt;
Load:&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;.34&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.10&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;.70
Disk&lt;span class="w"&gt; &lt;/span&gt;usage:&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.7G&lt;span class="w"&gt; &lt;/span&gt;out&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;38&lt;/span&gt;.6G
Memory&lt;span class="w"&gt; &lt;/span&gt;usage:&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;.2G&lt;span class="w"&gt; &lt;/span&gt;out&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.8G
Mounts:&lt;span class="w"&gt;         &lt;/span&gt;/Users/chillaranand/test/k8s&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;~/k8s
&lt;span class="w"&gt;                    &lt;/span&gt;UID&lt;span class="w"&gt; &lt;/span&gt;map:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;503&lt;/span&gt;:default
&lt;span class="w"&gt;                    &lt;/span&gt;GID&lt;span class="w"&gt; &lt;/span&gt;map:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;:default
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can even mount Mac directories on the VM.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;multipass&lt;span class="w"&gt; &lt;/span&gt;mount&lt;span class="w"&gt; &lt;/span&gt;~/test/k8s&lt;span class="w"&gt; &lt;/span&gt;k3s:~/k8s
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will be useful when we are making changes on host directories and want to apply changes on the cluster which is inside VM.&lt;/p&gt;
&lt;p&gt;Now, we can install k3s by running the install script inside the VM.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;multipass&lt;span class="w"&gt; &lt;/span&gt;shell&lt;span class="w"&gt; &lt;/span&gt;k3s

ubuntu@k3s:~$&lt;span class="w"&gt; &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-sfL&lt;span class="w"&gt; &lt;/span&gt;https://get.k3s.io&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sh&lt;span class="w"&gt; &lt;/span&gt;-
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will setup a k3s cluster on the VM. We can use kubectl and deploy applications on this cluster.&lt;/p&gt;
&lt;p&gt;By default, k3s config file will be located at &lt;code&gt;/etc/rancher/k3s/k3s.yaml&lt;/code&gt;. With this config file, we can use Lens&lt;sup id="fnref:lens"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2022/10/local-kubernetes-with-k3s-on-mac.html#fn:lens"&gt;4&lt;/a&gt;&lt;/sup&gt; to manage k8s cluster.&lt;/p&gt;
&lt;p align="center"&gt;
&lt;img src="https://avilpage.com/images/k8s-mac-m1.jpg"&gt;
&lt;/p&gt;

&lt;p&gt;Lets find out IP of the VM &amp;amp; k8s token so that we can spin up a new VM and add it to this cluster.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# get token &amp;amp; ip of k3s&lt;/span&gt;
&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;multipass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;k3s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;rancher&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;k3s&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;
&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;multipass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;k3s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;grep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ip&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;multipass&lt;span class="w"&gt; &lt;/span&gt;launch&lt;span class="w"&gt; &lt;/span&gt;--name&lt;span class="w"&gt; &lt;/span&gt;k3s-worker&lt;span class="w"&gt; &lt;/span&gt;--mem&lt;span class="w"&gt; &lt;/span&gt;2G&lt;span class="w"&gt; &lt;/span&gt;--disk&lt;span class="w"&gt; &lt;/span&gt;20G

$&lt;span class="w"&gt; &lt;/span&gt;multipass&lt;span class="w"&gt; &lt;/span&gt;shell&lt;span class="w"&gt; &lt;/span&gt;k3s-worker

ubuntu@k3s-worker:~$&lt;span class="w"&gt; &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-sfL&lt;span class="w"&gt; &lt;/span&gt;https://get.k3s.io&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;K3S_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://192.168.64.4:6443&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;K3S_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"hs48af...947fh4::server:3tfkwjd...4jed73"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sh&lt;span class="w"&gt; &lt;/span&gt;-
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can verify if the node is added correctly from k3s VM.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;ubuntu&lt;/span&gt;&lt;span class="nv"&gt;@k3s&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;kubectl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;
&lt;span class="n"&gt;NAME&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="n"&gt;STATUS&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;ROLES&lt;/span&gt;&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="n"&gt;AGE&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;VERSION&lt;/span&gt;
&lt;span class="n"&gt;k3s&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="n"&gt;Ready&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;control&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;plane&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="mf"&gt;.24.6&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;k3s1&lt;/span&gt;
&lt;span class="n"&gt;k3s&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;Ready&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;none&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="n"&gt;m15s&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="mf"&gt;.24.6&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;k3s1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once we are done with experimenting k3s, we can delete the VMs.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;multipass&lt;span class="w"&gt; &lt;/span&gt;delete&lt;span class="w"&gt; &lt;/span&gt;k3s&lt;span class="w"&gt; &lt;/span&gt;k3s-worker
$&lt;span class="w"&gt; &lt;/span&gt;multipass&lt;span class="w"&gt; &lt;/span&gt;purge
&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;multipass is a great tool to spin up Linux VMs on Mac with single command. K3s is better tool to setup k8s cluster locally for development and testing.&lt;/p&gt;
&lt;p&gt;Even though we have mentioned this tutorial is meant for Mac M1, it should work fine on any Linux distribution as well.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:k8s"&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Kubernetes"&gt;https://en.wikipedia.org/wiki/Kubernetes&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2022/10/local-kubernetes-with-k3s-on-mac.html#fnref:k8s" title="Jump back to footnote 1 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:k3s"&gt;
&lt;p&gt;&lt;a href="https://k3s.io/"&gt;https://k3s.io/&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2022/10/local-kubernetes-with-k3s-on-mac.html#fnref:k3s" title="Jump back to footnote 2 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:multipass"&gt;
&lt;p&gt;&lt;a href="https://github.com/canonical/multipass"&gt;https://github.com/canonical/multipass&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2022/10/local-kubernetes-with-k3s-on-mac.html#fnref:multipass" title="Jump back to footnote 3 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:lens"&gt;
&lt;p&gt;&lt;a href="https://k8slens.dev/"&gt;https://k8slens.dev/&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2022/10/local-kubernetes-with-k3s-on-mac.html#fnref:lens" title="Jump back to footnote 4 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>devops</category><category>kubernetes</category><category>macbook</category><guid>https://avilpage.com/2022/10/local-kubernetes-with-k3s-on-mac.html</guid><pubDate>Tue, 11 Oct 2022 02:38:36 GMT</pubDate></item><item><title>Make Python Docker Builds Slim &amp; Fast</title><link>https://avilpage.com/2020/10/python-docker-build-slim-fast.html</link><dc:creator>Anand Reddy Pandikunta</dc:creator><description>&lt;h4&gt;Introduction&lt;/h4&gt;
&lt;p&gt;When using Docker, if the build is taking time or the build image is huge, it will waste system resources as well as our time. In this article, let's see how to reduce build time as well as image size when using Docker for Python projects.&lt;/p&gt;
&lt;h4&gt;Project&lt;/h4&gt;
&lt;p&gt;Let us take a hello world application written in flask.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;flask&lt;/span&gt;


&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vm"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;home&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'hello world - v1.0.0'&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let's create a requirements.txt file to list out python packages required for the project.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;flask==1.1.2
pandas==1.1.2
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Pandas binary wheel size is ~10MB. It is included in requirements to see how python packages affect docker image size.&lt;/p&gt;
&lt;p&gt;Here is our Dockerfile to run the flask application.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;FROM python:3.7

ADD . /app

WORKDIR /app

RUN python -m pip install -r requirements.txt

EXPOSE 5000

ENTRYPOINT [ "python" ]

CMD [ "-m" "flask" "run" ]
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let's use the following commands to measure the image size &amp;amp; build time with/without cache.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;docker&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;flask:0.0&lt;span class="w"&gt; &lt;/span&gt;--pull&lt;span class="w"&gt; &lt;/span&gt;--no-cache
&lt;span class="o"&gt;[&lt;/span&gt;+&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Building&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;45&lt;/span&gt;.3s&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;9&lt;/span&gt;/9&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;FINISHED

$&lt;span class="w"&gt; &lt;/span&gt;touch&lt;span class="w"&gt; &lt;/span&gt;app.py&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# modify app.py file&lt;/span&gt;

$&lt;span class="w"&gt; &lt;/span&gt;docker&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;flask:0.1
&lt;span class="o"&gt;[&lt;/span&gt;+&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Building&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;15&lt;/span&gt;.3s&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;9&lt;/span&gt;/9&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;FINISHED

$&lt;span class="w"&gt; &lt;/span&gt;docker&lt;span class="w"&gt; &lt;/span&gt;images&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;flask
flask&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.1&lt;span class="w"&gt;     &lt;/span&gt;06d3e985f12e&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;.01GB
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;With the current docker, here are the results.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://avilpage.com/images/docker0.png" style="vertical-align:middle"&gt;&lt;/p&gt;
&lt;h4&gt;1. Install requirements first&lt;/h4&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;FROM python:3.7

WORKDIR /app

ADD ./requirements.txt /app/requirements.txt

RUN python -m pip install -r requirements.txt

ADD . /app

EXPOSE 5000

ENTRYPOINT [ "python" ]

CMD [ "-m" "flask" "run" ]
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let us modify the docker file to install requirements first and then add code to the docker image.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://avilpage.com/images/docker1.png" style="vertical-align:middle"&gt;&lt;/p&gt;
&lt;p&gt;Now, build without cache took almost the same time. With cache, the build is completed in a second. Since docker caches step by step, it has cached python package installation step and thereby reducing the build time.&lt;/p&gt;
&lt;h4&gt;2. Disable Cache&lt;/h4&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;FROM python:3.7

WORKDIR /app

ADD ./requirements.txt /app/requirements.txt

RUN python -m pip install -r requirements.txt --no-cache

ADD . /app

EXPOSE 5000

ENTRYPOINT [ "python" ]

CMD [ "-m" "flask" "run" ]
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By default, pip will cache the downloaded packages. Since we don't need a cache inside docker, let's disable pip cache by passing &lt;code&gt;--no-cache&lt;/code&gt; argument.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://avilpage.com/images/docker2.png" style="vertical-align:middle"&gt;&lt;/p&gt;
&lt;p&gt;This reduced the docker image size by ~20MB. In real-world projects, where there are a good number of dependencies, the overall image size will be reduced a lot.&lt;/p&gt;
&lt;h4&gt;3. Use slim variant&lt;/h4&gt;
&lt;p&gt;Till now, we have been using defacto Python variant. It has a large number of common debian packages. There is a slim variant that doesn't contain all these common packages&lt;sup id="fnref:dpy"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2020/10/python-docker-build-slim-fast.html#fn:dpy"&gt;4&lt;/a&gt;&lt;/sup&gt;. Since we don't need all these debian packages, let's use slim variant.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;FROM python:3.7-slim

...
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src="https://avilpage.com/images/docker3.png" style="vertical-align:middle"&gt;&lt;/p&gt;
&lt;p&gt;This reduced the docker image size by ~750 MB without affecting the build time.&lt;/p&gt;
&lt;h4&gt;4. Build from source&lt;/h4&gt;
&lt;p&gt;Python packages can be installed via wheels (.whl files) for a faster and smoother installation. We can also install them via source code. If we look at Pandas project files on PyPi&lt;sup id="fnref:pandas"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2020/10/python-docker-build-slim-fast.html#fn:pandas"&gt;1&lt;/a&gt;&lt;/sup&gt;, it provides both wheels as well as tar zip source files. Pip will prefer wheels over source code the installation process will be much smoother.&lt;/p&gt;
&lt;p&gt;To reduce Docker image size, we can build from source instead of using the wheel. This will increase build time as the python package will take some time to compile while building.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src="https://avilpage.com/images/docker4.png" style="vertical-align:middle"&gt;&lt;/p&gt;
&lt;p&gt;Here build size is reduced by ~20MB but the build has increased to 15 minutes.&lt;/p&gt;
&lt;h4&gt;5. Use Alpine&lt;/h4&gt;
&lt;p&gt;Earlier we have used, python slim variant the base image. However, there is Alpine variant which is much smaller than slim. One caveat with using alpine is Python wheels won't work with this image&lt;sup id="fnref:aw"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2020/10/python-docker-build-slim-fast.html#fn:aw"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;We have to build all packages from source. For example, packages like TensorFlow provide only wheels for installation. To install this on Alpine, we have to install from the source which will take additional effort to figure out dependencies and install.&lt;/p&gt;
&lt;p&gt;Using Alpine will reduce the image size by ~70 MB but it is not recomended to use Alpine as wheels won't work with this image.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://avilpage.com/images/docker5.png" style="vertical-align:middle"&gt;&lt;/p&gt;
&lt;p&gt;All the docker files used in the article are available on github&lt;sup id="fnref:gh"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2020/10/python-docker-build-slim-fast.html#fn:gh"&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;We have started with a docker build of 1.01 GB and reduced it to 0.13 GB. We have also optimized build times using the docker caching mechanism. We can use appropriate steps to optimize build for size or speed or both.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:pandas"&gt;
&lt;p&gt;&lt;a href="https://pypi.org/project/pandas/#files"&gt;https://pypi.org/project/pandas/#files&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2020/10/python-docker-build-slim-fast.html#fnref:pandas" title="Jump back to footnote 1 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:aw"&gt;
&lt;p&gt;&lt;a href="https://github.com/docker-library/docs/issues/904"&gt;https://github.com/docker-library/docs/issues/904&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2020/10/python-docker-build-slim-fast.html#fnref:aw" title="Jump back to footnote 2 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:gh"&gt;
&lt;p&gt;&lt;a href="https://github.com/ChillarAnand/library-docker"&gt;https://github.com/ChillarAnand/library-docker&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2020/10/python-docker-build-slim-fast.html#fnref:gh" title="Jump back to footnote 3 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:dpy"&gt;
&lt;p&gt;&lt;a href="https://hub.docker.com/_/python"&gt;https://hub.docker.com/_/python&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2020/10/python-docker-build-slim-fast.html#fnref:dpy" title="Jump back to footnote 4 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>devops</category><category>featured</category><category>python</category><guid>https://avilpage.com/2020/10/python-docker-build-slim-fast.html</guid><pubDate>Sat, 31 Oct 2020 12:53:13 GMT</pubDate></item><item><title>How To Deploy Mirth Connect To Kubernetes</title><link>https://avilpage.com/2020/07/deploy-mirth-to-kubernetes.html</link><dc:creator>Anand Reddy Pandikunta</dc:creator><description>&lt;h4&gt;Introduction&lt;/h4&gt;
&lt;p&gt;NextGen Connect(previously Mirth Connect) is widely used integration engine for information exchange in health-care domain. In this article, let us see how to deploy Mirth Connect to a Kubernetes cluster.&lt;/p&gt;
&lt;h4&gt;Deployment To k8s&lt;/h4&gt;
&lt;p&gt;From version 3.8, NextGen has started providing official docker images for Connect&lt;sup id="fnref:nc"&gt;&lt;a class="footnote-ref" href="https://avilpage.com/2020/07/deploy-mirth-to-kubernetes.html#fn:nc"&gt;1&lt;/a&gt;&lt;/sup&gt;. By default, Connect docker exposes 8080, 8443 ports. We can start a Connect instance locally, by running the following command.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nv"&gt;$docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;8080&lt;/span&gt;:8080&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;8443&lt;/span&gt;:8443&lt;span class="w"&gt; &lt;/span&gt;nextgenhealthcare/connect
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can use this docker image and create a k8s deployment to start a container.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: mirth-connect
  namespace: default
spec:
  template:
    spec:
      containers:
      &lt;span class="k"&gt;-&lt;/span&gt; name: mirth-connect
        image: docker.io/nextgenhealthcare/connect
        ports:
        &lt;span class="k"&gt;-&lt;/span&gt; name: http
          containerPort: 8080
        &lt;span class="k"&gt;-&lt;/span&gt; name: https
          containerPort: 8443
        &lt;span class="k"&gt;-&lt;/span&gt; name: hl7-test
          containerPort: 9001
        env:
          &lt;span class="k"&gt;-&lt;/span&gt; name: DATABASE
            value: postgres
          &lt;span class="k"&gt;-&lt;/span&gt; name: DATABASE_URL
            value: jdbc:postgresql://avilpage.com:5432/mirth_db
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This deployment file can be applied on a cluster using &lt;code&gt;kubectl&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;apply&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;connect-deployment.yaml
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To access this container, we can create a service to expose this deployment to public.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="o"&gt;---&lt;/span&gt;
&lt;span class="n"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v1&lt;/span&gt;
&lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;
&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mirth&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;annotations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;beta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kubernetes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;load&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;balancer&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;cert&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;acm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ap&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;south&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;beta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kubernetes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;load&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;balancer&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"443"&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;external&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;dns&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kubernetes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;avilpage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;
&lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LoadBalancer&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mirth&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;targetPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TCP&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;443&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;targetPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8443&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TCP&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hl7&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9001&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;targetPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9001&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TCP&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will create a load balancer in AWS through which we can access mirth connect instance. If an ingress controller is present in the cluster, we can use it directly instead of using a seperate load balancer for this service.&lt;/p&gt;
&lt;p&gt;Once Mirth Connect is up &amp;amp; running, we might have to create HL7 channels running on various ports. In the above configuration files, we have exposed 9001 HL7 port for testing of channel. Once we configure Mirth Channels, we need to expose appropriate ports in deployment as well as service similiar to this.&lt;/p&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;Earlier, there were no official docker images for Mirth Connect and it was bit diffucult to dockerize Mirth Connect and deploy it. With the release of official Docker images, deploying Mirth Connect to k8s or any other container orchestration platform has become much easier.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:nc"&gt;
&lt;p&gt;&lt;a href="https://hub.docker.com/r/nextgenhealthcare/connect/"&gt;https://hub.docker.com/r/nextgenhealthcare/connect/&lt;/a&gt; &lt;a class="footnote-backref" href="https://avilpage.com/2020/07/deploy-mirth-to-kubernetes.html#fnref:nc" title="Jump back to footnote 1 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>aws</category><category>devops</category><category>HealthIT</category><category>kubernetes</category><guid>https://avilpage.com/2020/07/deploy-mirth-to-kubernetes.html</guid><pubDate>Wed, 30 Sep 2020 17:55:46 GMT</pubDate></item><item><title>Tips On Improving kubectl Productivity</title><link>https://avilpage.com/2020/05/tips-on-improving-kubectl-productivity.html</link><dc:creator>Anand Reddy Pandikunta</dc:creator><description>&lt;p&gt;&lt;code&gt;kubectl&lt;/code&gt; is CLI tool to control Kubernetes clusters. As we start using kubectl to interact with mutliple clusters, we end up running lengthy commands and even running multiple commands for simple tasks like running a shell in a container.&lt;/p&gt;
&lt;p&gt;In this article, lets learn few tips to improve our productivity when using kubectl.&lt;/p&gt;
&lt;h4&gt;Aliases&lt;/h4&gt;
&lt;p&gt;Aliases in general improve the productivity when using a shell.&lt;/p&gt;
&lt;p&gt;kubectl provides shortcuts for commands. For example,&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# instead of running full command&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;get&lt;span class="w"&gt; &lt;/span&gt;services

&lt;span class="c1"&gt;# we can use short hand version&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;get&lt;span class="w"&gt; &lt;/span&gt;svc
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It also provides completion for commands.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# enable completion for zsh&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;completion&lt;span class="w"&gt; &lt;/span&gt;zsh&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# type `kubectl ` and hit `&amp;lt;TAB&amp;gt;` will show possible options&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;kubectl
annotate&lt;span class="w"&gt;       &lt;/span&gt;attach&lt;span class="w"&gt;         &lt;/span&gt;cluster-info
api-resources&lt;span class="w"&gt;  &lt;/span&gt;auth&lt;span class="w"&gt;           &lt;/span&gt;completion
api-versions&lt;span class="w"&gt;   &lt;/span&gt;autoscale&lt;span class="w"&gt;      &lt;/span&gt;config
apply&lt;span class="w"&gt;          &lt;/span&gt;certificate&lt;span class="w"&gt;    &lt;/span&gt;convert

&lt;span class="c1"&gt;# type `kubectl g`, and hit `&amp;lt;TAB&amp;gt;` will show possible options&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;get
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Even though completions are helpful, setting up up aliases for most commanly used commands will save a lot of time.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'kubectl'&lt;/span&gt;

&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;kdp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'kubectl describe pod'&lt;/span&gt;
&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;kgp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'kubectl get pods'&lt;/span&gt;
&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;kgpa&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'kubectl get pods --all-namespaces'&lt;/span&gt;
&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'kubectl exec -it'&lt;/span&gt;
&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;wkgp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'watch -n1 kubectl getp pods'&lt;/span&gt;

&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;kga&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'kubectl get all'&lt;/span&gt;
&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;kgaa&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'kubectl get all --all-namespaces'&lt;/span&gt;

&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;kaf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'kubectl apply -f'&lt;/span&gt;

&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;kcgc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'kubectl config get-contexts'&lt;/span&gt;
&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;kccc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'kubectl config current-context'&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you don't write your own aliases, there is &lt;a href="https://github.com/ahmetb/kubectl-aliases"&gt;kubectl-aliases&lt;/a&gt; which provides exhuastive list of aliases. We can source this file in rc file and start using them.&lt;/p&gt;
&lt;h4&gt;Use Functions&lt;/h4&gt;
&lt;p&gt;Even though aliases help us to run lengthy commands with an alias, there are times where we have to run multiple commands to get things done for a single task.&lt;/p&gt;
&lt;p&gt;For example, to view kubenetes dashboard, we have to get the token, start proxy server and then open the url in browser. We can write a simple function as shown below to do all of that.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;kp&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;kubernetes-dashboard&lt;span class="w"&gt; &lt;/span&gt;describe&lt;span class="w"&gt; &lt;/span&gt;secret&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;kubernetes-dashboard&lt;span class="w"&gt; &lt;/span&gt;get&lt;span class="w"&gt; &lt;/span&gt;secret&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;admin-user&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;awk&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'{print $1}'&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'token:'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;awk&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'{print $2}'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;pbcopy
&lt;span class="w"&gt;    &lt;/span&gt;open&lt;span class="w"&gt; &lt;/span&gt;http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/
&lt;span class="w"&gt;    &lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;proxy
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now from the shell, when we run &lt;code&gt;kp&lt;/code&gt;, it function will copy the token to clipboard, open kubernetes dashboard in browser and will start the proxy server.&lt;/p&gt;
&lt;h4&gt;Use Labels&lt;/h4&gt;
&lt;p&gt;To describe a pod or tail logs from a pod, we can use pod names.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;get&lt;span class="w"&gt; &lt;/span&gt;pods
NAME&lt;span class="w"&gt;                             &lt;/span&gt;READY&lt;span class="w"&gt;   &lt;/span&gt;STATUS
hello-world-79d794c659-tpfv2&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;/1&lt;span class="w"&gt;     &lt;/span&gt;Running


$&lt;span class="w"&gt; &lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;describe&lt;span class="w"&gt; &lt;/span&gt;pod&lt;span class="w"&gt; &lt;/span&gt;hello-world-79d794c659-tpfv2

$&lt;span class="w"&gt; &lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;logs&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;pod/hello-world-79d794c659-tpfv2
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;When the app gets updated, the name of pod also updates. If your shell has auto completion feature, it will autocomplete to previous name.&lt;/p&gt;
&lt;p&gt;So, instead of using pod name, we can use pod labels as mentioned below.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;describe&lt;span class="w"&gt; &lt;/span&gt;pod&lt;span class="w"&gt; &lt;/span&gt;-l&lt;span class="o"&gt;=&lt;/span&gt;hello-world

$&lt;span class="w"&gt; &lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;logs&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;-l&lt;span class="o"&gt;=&lt;/span&gt;pod/hello-world
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We run the command once and next time shell will show autocomplete and we can use that directly.&lt;/p&gt;
&lt;h4&gt;Kubectl Tools&lt;/h4&gt;
&lt;p&gt;k8s has a good ecosystem and the following packages are aimed to make certain k8s tasks easier.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/aylei/kubectl-debug"&gt;kubectl-debug&lt;/a&gt; - Debug pod by a new container with all troubleshooting tools pre-installed.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/pixel-point/kube-forwarder"&gt;kube-forwarder&lt;/a&gt; - Easy to use port forwarding manager.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/wercker/stern"&gt;stern&lt;/a&gt; - Multi pod and container log tailing.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/ahmetb/kubectx"&gt;kubectx&lt;/a&gt; - Quick way to switch between clusters and namespaces.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/astefanutti/kubebox"&gt;kubebox&lt;/a&gt; - Terminal and Web console for Kubernetes.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/derailed/k9s"&gt;k9s&lt;/a&gt; - Interactive terminal UI.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/IBM/kui"&gt;kui&lt;/a&gt; - Hybrid CLI/UI tool for k8s.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/databricks/click"&gt;click&lt;/a&gt; - Interactive controller for k8s.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/lensapp/lens"&gt;lens&lt;/a&gt; - Stand alone corss platform k8s IDE.&lt;/p&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;In this article we have seen some useful methods as well as some tools to improve productivity with kubectl. If you spend a lot of time interacting with kubernetes cluster, it is important to notice your workflows and find better tools or ways to improve productivity.&lt;/p&gt;</description><category>devops</category><category>kubernetes</category><guid>https://avilpage.com/2020/05/tips-on-improving-kubectl-productivity.html</guid><pubDate>Sun, 31 May 2020 13:12:00 GMT</pubDate></item><item><title>Continuous Deployment To Kubernetes With Skaffold</title><link>https://avilpage.com/2020/04/setup-continuous-deployment-with-kubernetes.html</link><dc:creator>Anand Reddy Pandikunta</dc:creator><description>&lt;p&gt;In this article, let us see how to set up a continuous deployment pipeline to Kubernetes in CircleCI using Skaffold.&lt;/p&gt;
&lt;h4&gt;Prerequisites&lt;/h4&gt;
&lt;p&gt;You should have a kubernetes cluster in a cloud environment or in your local machine. Check your cluster status with the following commands.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;cluster-info
$&lt;span class="w"&gt; &lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;config&lt;span class="w"&gt; &lt;/span&gt;get-contexts
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You should know how to manually deploy your application to kubernetes.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# push latest docker image to container registry&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;docker&lt;span class="w"&gt; &lt;/span&gt;push&lt;span class="w"&gt; &lt;/span&gt;chillaranand/library

&lt;span class="c1"&gt;# deploy latest image to k8s&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;apply&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;app/deployment.yaml
$&lt;span class="w"&gt; &lt;/span&gt;kubectl&lt;span class="w"&gt; &lt;/span&gt;apply&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;app/service.yaml
&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Skaffold&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://github.com/GoogleContainerTools/skaffold"&gt;Skaffold&lt;/a&gt; is a CLI tool to facilitate continuous development and deployment workflows for Kubernetes applications.&lt;/p&gt;
&lt;p&gt;Skaffold binaries are available for all platforms. Download the binary file for your OS and move it to bin folder.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-Lo&lt;span class="w"&gt; &lt;/span&gt;skaffold&lt;span class="w"&gt; &lt;/span&gt;https://storage.googleapis.com/skaffold/releases/latest/skaffold-darwin-amd64
$&lt;span class="w"&gt; &lt;/span&gt;chmod&lt;span class="w"&gt; &lt;/span&gt;+x&lt;span class="w"&gt; &lt;/span&gt;skaffold
$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;mv&lt;span class="w"&gt; &lt;/span&gt;skaffold&lt;span class="w"&gt; &lt;/span&gt;/usr/local/bin
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Inside your project root, run &lt;code&gt;init&lt;/code&gt; command to generate a config file. If your project has k8s manifests, it will detect them and include it in the configuration file.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;skaffold&lt;span class="w"&gt; &lt;/span&gt;init
Configuration&lt;span class="w"&gt; &lt;/span&gt;skaffold.yaml&lt;span class="w"&gt; &lt;/span&gt;was&lt;span class="w"&gt; &lt;/span&gt;written

$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;skaffold.yaml
apiVersion:&lt;span class="w"&gt; &lt;/span&gt;skaffold/v2beta1
kind:&lt;span class="w"&gt; &lt;/span&gt;Config
metadata:
&lt;span class="w"&gt;  &lt;/span&gt;name:&lt;span class="w"&gt; &lt;/span&gt;library
build:
&lt;span class="w"&gt;  &lt;/span&gt;artifacts:
&lt;span class="w"&gt;  &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;image:&lt;span class="w"&gt; &lt;/span&gt;docker.io/chillaranand/library
deploy:
&lt;span class="w"&gt;  &lt;/span&gt;kubectl:
&lt;span class="w"&gt;    &lt;/span&gt;manifests:
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;kubernetes/deployment.yaml
&lt;span class="w"&gt;    &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;kubernetes/service.yaml
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To deploy latest changes to your cluster, run&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;skaffold&lt;span class="w"&gt; &lt;/span&gt;run
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will build the docker image, push to registry and will apply the manifests in the clusters. Now, k8s will pull the latest image from the registry and create a new deployment.&lt;/p&gt;
&lt;h4&gt;CircleCI Workflow&lt;/h4&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;version:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;.1

orbs:
&lt;span class="w"&gt;  &lt;/span&gt;aws-cli:&lt;span class="w"&gt; &lt;/span&gt;circleci/aws-cli@0.1.19
&lt;span class="w"&gt;  &lt;/span&gt;kubernetes:&lt;span class="w"&gt; &lt;/span&gt;circleci/kubernetes@0.11.0

commands:
&lt;span class="w"&gt;  &lt;/span&gt;kubernetes-deploy:

&lt;span class="w"&gt;    &lt;/span&gt;steps:
&lt;span class="w"&gt;      &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;setup_remote_docker

&lt;span class="w"&gt;      &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;aws-cli/setup:
&lt;span class="w"&gt;          &lt;/span&gt;profile-name:&lt;span class="w"&gt; &lt;/span&gt;default

&lt;span class="w"&gt;      &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;kubernetes/install-kubectl:
&lt;span class="w"&gt;          &lt;/span&gt;kubectl-version:&lt;span class="w"&gt; &lt;/span&gt;v1.15.10

&lt;span class="w"&gt;      &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;checkout

&lt;span class="w"&gt;      &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;run:
&lt;span class="w"&gt;          &lt;/span&gt;name:&lt;span class="w"&gt; &lt;/span&gt;container&lt;span class="w"&gt; &lt;/span&gt;registry&lt;span class="w"&gt; &lt;/span&gt;log&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;command:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;aws&lt;span class="w"&gt; &lt;/span&gt;ecr&lt;span class="w"&gt; &lt;/span&gt;get-login&lt;span class="w"&gt; &lt;/span&gt;--region&lt;span class="w"&gt; &lt;/span&gt;ap-south-1&lt;span class="w"&gt; &lt;/span&gt;--no-include-email&lt;span class="k"&gt;)&lt;/span&gt;

&lt;span class="w"&gt;      &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;run:
&lt;span class="w"&gt;          &lt;/span&gt;name:&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;skaffold
&lt;span class="w"&gt;          &lt;/span&gt;command:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-Lo&lt;span class="w"&gt; &lt;/span&gt;skaffold&lt;span class="w"&gt; &lt;/span&gt;https://storage.googleapis.com/skaffold/releases/latest/skaffold-linux-amd64
&lt;span class="w"&gt;            &lt;/span&gt;chmod&lt;span class="w"&gt; &lt;/span&gt;+x&lt;span class="w"&gt; &lt;/span&gt;skaffold
&lt;span class="w"&gt;            &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;mv&lt;span class="w"&gt; &lt;/span&gt;skaffold&lt;span class="w"&gt; &lt;/span&gt;/usr/local/bin

&lt;span class="w"&gt;      &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;run:
&lt;span class="w"&gt;          &lt;/span&gt;name:&lt;span class="w"&gt; &lt;/span&gt;update&lt;span class="w"&gt; &lt;/span&gt;kube&lt;span class="w"&gt; &lt;/span&gt;config&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;connect&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;required&lt;span class="w"&gt; &lt;/span&gt;cluster
&lt;span class="w"&gt;          &lt;/span&gt;command:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;aws&lt;span class="w"&gt; &lt;/span&gt;eks&lt;span class="w"&gt; &lt;/span&gt;--region&lt;span class="w"&gt; &lt;/span&gt;ap-south-1&lt;span class="w"&gt; &lt;/span&gt;update-kubeconfig&lt;span class="w"&gt; &lt;/span&gt;--name&lt;span class="w"&gt; &lt;/span&gt;demo-cluster

&lt;span class="w"&gt;      &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;run:
&lt;span class="w"&gt;          &lt;/span&gt;name:&lt;span class="w"&gt; &lt;/span&gt;deploy&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;k8s
&lt;span class="w"&gt;          &lt;/span&gt;command:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;skaffold&lt;span class="w"&gt; &lt;/span&gt;run
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;CircleCI orbs are shareable packages to speed up CI setup. Here we are using aws-cli, kubernetes orbs to easily install/setup them inside the CI environment.&lt;/p&gt;
&lt;p&gt;Since CircleCI builds run in a docker container, to run docker commands inside container, we have to specify &lt;code&gt;setup_remote_docker&lt;/code&gt; key so that a separate environment is created for it.&lt;/p&gt;
&lt;p&gt;Remaining steps are self explainatory.&lt;/p&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;Here we have seen how to set up CD to kubernetes in CircleCI. If we want to set up this another CI like Jenkins or Travis, instead of using orbs, we have to use system package mangers like apt-get to install them. All others steps will remain same.&lt;/p&gt;</description><category>aws</category><category>devops</category><category>kubernetes</category><guid>https://avilpage.com/2020/04/setup-continuous-deployment-with-kubernetes.html</guid><pubDate>Thu, 30 Apr 2020 11:15:36 GMT</pubDate></item><item><title>Versioning &amp; Retrieving Files From AWS S3 With Boto</title><link>https://avilpage.com/2019/07/aws-s3-bucket-objects-versions.html</link><dc:creator>Anand Reddy Pandikunta</dc:creator><description>&lt;h4&gt;Introduction&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://aws.amazon.com/s3/"&gt;Amazon S3&lt;/a&gt; (Amazon Simple Storage Service) is an object storage service offered by Amazon Web Services. For S3 buckets, if versioning is enabled, users can preserve, retrieve, and restore every version of the object stored in the bucket.&lt;/p&gt;
&lt;p&gt;In this article, we will understand how to enable versioning for a bucket and retrieve all versions of an object from AWS web interface as well as &lt;a href="https://pypi.org/project/boto3/"&gt;Python boto&lt;/a&gt; library.&lt;/p&gt;
&lt;h4&gt;Versioning of Bucket&lt;/h4&gt;
&lt;p&gt;Bucket versioning can be changed with a toggle button from the AWS web console in the bucket properties.&lt;/p&gt;
&lt;p align="center"&gt;
&lt;img src="https://avilpage.com/images/aws-s3-bucket-version1.png"&gt;
&lt;/p&gt;

&lt;p&gt;We can do the same with Python boto3 library.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;boto3&lt;/span&gt;


&lt;span class="n"&gt;bucket_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'avilpage'&lt;/span&gt;

&lt;span class="n"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'s3'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;versioning&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BucketVersioning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# check status&lt;/span&gt;
&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;versioning&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# enable versioning&lt;/span&gt;
&lt;span class="n"&gt;versioning&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# disable versioning&lt;/span&gt;
&lt;span class="n"&gt;versioning&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;suspend&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Retrieving Objects&lt;/h4&gt;
&lt;p&gt;Once versioning is enabled, we can store multiple versions of an object by uploading an object multiple times with the same key.&lt;/p&gt;
&lt;p&gt;We can write a simple script to generate a text file with a random text and upload it to S3.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;random&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;string&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;boto3&lt;/span&gt;

&lt;span class="n"&gt;file_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'test.txt'&lt;/span&gt;
&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;file_name&lt;/span&gt;
&lt;span class="n"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'s3'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'w'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;fh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ascii_letters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;fh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;upload_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If this script is executed multiple times, the same file gets overridden with a different version id with the same key in the bucket.&lt;/p&gt;
&lt;p&gt;We can see all the versions of the file from the bucket by selecting the file and then clicking drop-down at &lt;code&gt;Latest version&lt;/code&gt;.&lt;/p&gt;
&lt;p align="center"&gt;
&lt;img src="https://avilpage.com/images/aws-s3-bucket-version2.png"&gt;
&lt;/p&gt;

&lt;p&gt;We can write a script to retrieve and show contents of all the versions of the test.txt file with the following script.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;boto3&lt;/span&gt;


&lt;span class="n"&gt;bucket_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'avilpage'&lt;/span&gt;
&lt;span class="n"&gt;s3_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'s3'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;versions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3_client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list_object_versions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;versions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;version_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;versions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Versions'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'VersionId'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;file_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;versions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Versions'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'Key'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;file_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;VersionId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;version_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Body'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;Object versioning is useful to protect data from unintended overwrites. In this article, we learnt how to change bucket versioning, upload multiple versions of same file and retrieving all versions of the file using AWS web console as well as boto3.&lt;/p&gt;</description><category>aws</category><category>devops</category><category>python</category><guid>https://avilpage.com/2019/07/aws-s3-bucket-objects-versions.html</guid><pubDate>Wed, 24 Jul 2019 15:51:21 GMT</pubDate></item><item><title>How To Install Private Python Packages With Pip</title><link>https://avilpage.com/2018/11/how-to-install-private-python-packages-with-pip.html</link><dc:creator>Anand Reddy Pandikunta</dc:creator><description>&lt;h4&gt;Introduction&lt;/h4&gt;
&lt;p&gt;To distribute python code, we need to package it and host it somewhere, so that users can install and use it. If the code is public, it can be published to &lt;a href="https://pypi.org"&gt;PyPi&lt;/a&gt; or any public repository, so that anyone can access it. If the code is private, we need to provide proper authentication mechanism before allowing users to access it.&lt;/p&gt;
&lt;p&gt;In this article, we will see how to use &lt;a href="https://packaging.python.org/key_projects/#pip"&gt;pip&lt;/a&gt; to install Python packages hosted on GitLab, GitHub, Bitbucket or any other services.&lt;/p&gt;
&lt;h4&gt;Packaging&lt;/h4&gt;
&lt;p&gt;To package python project, we need to create &lt;code&gt;setup.py&lt;/code&gt; file which is build script for setuptools. Below is a sample setup file to create a package named &lt;code&gt;library&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;setuptools&lt;/span&gt;


&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"README.md"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"r"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;fh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;long_description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;setuptools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"library"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"chillaranand"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;author_email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"foo@avilpage.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"A simple python package"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;long_description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;long_description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/chillaranand/library"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;setuptools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;find_packages&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;classifiers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"Programming Language :: Python :: 3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"License :: OSI Approved :: MIT License"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;"Operating System :: OS Independent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Python provides &lt;a href="https://packaging.python.org/tutorials/packaging-projects/"&gt;detailed packaging documentation&lt;/a&gt; on structuring and building the package.&lt;/p&gt;
&lt;h4&gt;Installation&lt;/h4&gt;
&lt;p&gt;Once module(s) is packaged and pushed to hosting service, it can be installed with pip.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# using https&lt;/span&gt;
$&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;git+https://github.com/chillaranand/library.git

&lt;span class="c1"&gt;# using ssh&lt;/span&gt;
pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;git+ssh://git@github.com/chillaranand/library.git
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This usually requires authentication with usersname/password or ssh key. This setup works for developement machines. To use it in CI/CD pipelines or as a dependency, we can use tokens to simplify installation.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;foobar

$&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;git+https://&lt;span class="nv"&gt;$GITHUB_TOKEN&lt;/span&gt;@github.com/chillaranand/library.git
&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;In this article, we have seen how to package python code and install private packages with pip. This makes it easy to manage dependencies or install packages on multiple machines.&lt;/p&gt;</description><category>devops</category><category>python</category><guid>https://avilpage.com/2018/11/how-to-install-private-python-packages-with-pip.html</guid><pubDate>Thu, 31 Jan 2019 06:42:12 GMT</pubDate></item><item><title>Setup Continous Deployment For Python Chalice</title><link>https://avilpage.com/2018/10/cd-python-chalice-aws.html</link><dc:creator>Anand Reddy Pandikunta</dc:creator><description>&lt;h4&gt;Outline&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://pypi.org/project/chalice/"&gt;Chalice&lt;/a&gt; is a microframework developed by Amazon for quickly creating and deploying serverless applications in Python.&lt;/p&gt;
&lt;p&gt;In this article, we will see how to setup continous deployment with &lt;a href="https://github.com"&gt;GitHub&lt;/a&gt; and &lt;a href="https://docs.aws.amazon.com/codepipeline/latest/userguide/welcome.html"&gt;AWS CodePipeline&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;CD Setup&lt;/h4&gt;
&lt;p&gt;Chalice provides cli command &lt;code&gt;deploy&lt;/code&gt; to deploy from local system.&lt;/p&gt;
&lt;p&gt;Chalice also provides cli command &lt;code&gt;generate-pipeline&lt;/code&gt; command to generate CloudFormation template. This template is useful to automatically generate several resources required for AWS pipeline.&lt;/p&gt;
&lt;p align="center"&gt;
&lt;img src="https://avilpage.com/images/aws-python-pipeline.png" height="300px" width="600"&gt;
&lt;/p&gt;

&lt;p&gt;This by default uses CodeCommit repository for hosting code. We can use GitHub repo as a source instead of CodeCommit.&lt;/p&gt;
&lt;p&gt;Chalice by default provides a build file to package code and push it to S3. In the deploy step, it uses this artifact to deploy the code.&lt;/p&gt;
&lt;p&gt;We can use a custom buildpsec file to directly deploy the code from build step.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;

&lt;span class="n"&gt;phases&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;install&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Entering&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;phase&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Installing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dependencies&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="k"&gt;upgrade&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;awscli&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;chalice&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sudo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;requirements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;entered&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;phase&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;started&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n n-Quoted"&gt;`date`&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;chalice&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;deploy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;staging&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This buildspec file install requirements and deploys chalice app to staging. We can add one more build step to deploy it production after manual intervention.&lt;/p&gt;
&lt;h4&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;We have seen how to setup continous deployment for chalice application with GitHub and AWS CodePipeline.&lt;/p&gt;</description><category>aws</category><category>devops</category><category>python</category><guid>https://avilpage.com/2018/10/cd-python-chalice-aws.html</guid><pubDate>Tue, 30 Oct 2018 15:51:21 GMT</pubDate></item><item><title>How To Deploy Django Channels To Production</title><link>https://avilpage.com/2018/05/deploying-scaling-django-channels.html</link><dc:creator>Anand Reddy Pandikunta</dc:creator><description>&lt;p&gt;In this article, we will see how to deploy &lt;a href="https://pypi.org/project/channels/"&gt;django channels&lt;/a&gt; to production and how we can scale it to handle more load. We will be using nginx as proxy server, &lt;a href="https://pypi.org/project/daphne/"&gt;daphne&lt;/a&gt; as ASGI server, gunicorn as WSGI server and redis for channel back-end.&lt;/p&gt;
&lt;p&gt;Daphne can serve HTTP requests as well as WebSocket requests. For stability and performance, we will use uwsgi/gunicorn to serve HTTP requests and daphne to serve websocket requests.&lt;/p&gt;
&lt;p&gt;We will be using systemd to create and manage processes instead of depending on third party process managers like supervisor or circus. We will be using ansible for managing deployments. If you don't want to use ansible, you can just replace template variables in the following files with actual values.&lt;/p&gt;
&lt;h4&gt;Nginx Setup&lt;/h4&gt;
&lt;p&gt;Nginx will be routing requests to WSGI server and ASGI server based on URL. Here is nginx configuration for server.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;listen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;server_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}}:&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;server_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;server_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;server_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}};&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;301&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;avilpage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;request_uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;listen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;server_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}}:&lt;/span&gt;&lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;server_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;server_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;server_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}};&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;ssl_certificate&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;certs&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;avilpage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;crt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;ssl_certificate_key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;certs&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;avilpage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;access_log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;avilpage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;access&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;error_log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;nginx&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;avilpage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;proxy_pass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;proxy_set_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Forwarded&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;For&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;proxy_set_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;http_host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;proxy_set_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Real&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;IP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;proxy_redirect&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;proxy_pass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;9000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;proxy_http_version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;proxy_read_timeout&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;proxy_redirect&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;proxy_set_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Upgrade&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;http_upgrade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;proxy_set_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Connection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"upgrade"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;proxy_set_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;proxy_set_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Real&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;IP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;proxy_set_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Forwarded&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;For&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;proxy_set_header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Forwarded&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;server_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;project_root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;favicon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ico&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;project_root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;favicon&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ico&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;robots&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;alias&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;project_root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;robots&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;WSGI Server Setup&lt;/h4&gt;
&lt;p&gt;We will use gunicorn for wsgi server. We can run gunicorn with&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;gunicorn&lt;span class="w"&gt; &lt;/span&gt;avilpage.wsgi&lt;span class="w"&gt; &lt;/span&gt;--bind&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.0.0.0:8000&lt;span class="w"&gt; &lt;/span&gt;--log-level&lt;span class="w"&gt; &lt;/span&gt;error&lt;span class="w"&gt; &lt;/span&gt;--log-file&lt;span class="o"&gt;=&lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;--settings&lt;span class="w"&gt; &lt;/span&gt;avilpage.production_settings
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can create a systemd unit file to make it as a service.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;gunicorn&lt;/span&gt;
&lt;span class="na"&gt;After&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network.target&lt;/span&gt;


&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;span class="na"&gt;PIDFile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/run/gunicorn/pid&lt;/span&gt;
&lt;span class="na"&gt;User&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;root&lt;/span&gt;
&lt;span class="na"&gt;Group&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;root&lt;/span&gt;
&lt;span class="na"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;{{ project_root }}&lt;/span&gt;
&lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"DJANGO_SETTINGS_MODULE={{ project_name }}.production_settings"&lt;/span&gt;
&lt;span class="na"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;{{ venv_bin }}/gunicorn {{ project_name}}.wsgi --bind 0.0.0.0:8000 --log-level error --log-file=- --workers 5 --preload&lt;/span&gt;


&lt;span class="na"&gt;ExecReload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/bin/kill -s HUP $MAINPID&lt;/span&gt;
&lt;span class="na"&gt;ExecStop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/bin/kill -s TERM $MAINPID&lt;/span&gt;
&lt;span class="na"&gt;Restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;on-abort&lt;/span&gt;
&lt;span class="na"&gt;PrivateTmp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;


&lt;span class="k"&gt;[Install]&lt;/span&gt;
&lt;span class="na"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Whenever server restarts, systemd will automatically start gunicorn service. We can also restart gunicorn manually with&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;service&lt;span class="w"&gt; &lt;/span&gt;gunicorn&lt;span class="w"&gt; &lt;/span&gt;restart
&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;ASGI Server Setup&lt;/h4&gt;
&lt;p&gt;We will use daphne for ASGI server and it can be started with&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;daphne&lt;span class="w"&gt; &lt;/span&gt;avilpage.asgi:application&lt;span class="w"&gt; &lt;/span&gt;--bind&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.0.0.0&lt;span class="w"&gt; &lt;/span&gt;--port&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;9000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--verbosity&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We can create a systemd unit file like the previous one to create a service.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;daphne daemon&lt;/span&gt;
&lt;span class="na"&gt;After&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network.target&lt;/span&gt;


&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;span class="na"&gt;PIDFile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/run/daphne/pid&lt;/span&gt;
&lt;span class="na"&gt;User&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;root&lt;/span&gt;
&lt;span class="na"&gt;Group&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;root&lt;/span&gt;
&lt;span class="na"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;{{ project_root }}&lt;/span&gt;
&lt;span class="na"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"DJANGO_SETTINGS_MODULE={{ project_name }}.production_settings"&lt;/span&gt;
&lt;span class="na"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;{{ venv_bin }}/daphne --bind 0.0.0.0 --port 9000 --verbosity 0 {{project_name}}.asgi:application&lt;/span&gt;
&lt;span class="na"&gt;ExecReload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/bin/kill -s HUP $MAINPID&lt;/span&gt;
&lt;span class="na"&gt;ExecStop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/bin/kill -s TERM $MAINPID&lt;/span&gt;
&lt;span class="na"&gt;Restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;on-abort&lt;/span&gt;
&lt;span class="na"&gt;PrivateTmp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;


&lt;span class="k"&gt;[Install]&lt;/span&gt;
&lt;span class="na"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Deployment&lt;/h4&gt;
&lt;p&gt;Here is &lt;a href="https://github.com/ChillarAnand/eddie/blob/master/ubuntu/config/playbooks/django_setup.yml"&gt;an ansible playbook&lt;/a&gt; which is used to deploy these config files to our server. To run the playbook on server &lt;code&gt;avilpage.com&lt;/code&gt;, execute&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;ansible-playbook&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;avilpage.com,&lt;span class="w"&gt; &lt;/span&gt;django_setup.yml
&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Scaling&lt;/h4&gt;
&lt;p&gt;Now that we have deployed channels to production, we can do performance test to see how our server performs under load.&lt;/p&gt;
&lt;p&gt;For WebSockets, we can use &lt;a href="https://www.npmjs.com/package/thor"&gt;Thor&lt;/a&gt; to run performance test.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;thor&lt;span class="w"&gt; &lt;/span&gt;-C&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-A&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;wss://avilpage.com/ws/books/
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Our server is able to handle &lt;code&gt;100 requests per second&lt;/code&gt; with a &lt;code&gt;latency of 800ms&lt;/code&gt;. This is good enough for low traffic website.&lt;/p&gt;
&lt;p&gt;To improve performance, we can use unix sockets instead of rip/port for gunicorn and daphne. Also, daphne has support for multiprocessing using &lt;a href="https://avilpage.com/2018/05/deploying-scaling-django-channels.html"&gt;shared file descriptors&lt;/a&gt;. Unfortunately, it doesn't work as expected. As &lt;a href="https://github.com/django/daphne/issues/182#issuecomment-387507887"&gt;mentioned here&lt;/a&gt;, we can use systemd templates and spawn multiple daphne process.&lt;/p&gt;
&lt;p&gt;An alternate way is to use &lt;a href="https://pypi.org/project/uvicorn/"&gt;uvicorn&lt;/a&gt; to start multiple workers. Install uvicorn using pip&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;pip&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;uvicorn
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Start uvicorn ASGI server with&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;$&lt;span class="w"&gt; &lt;/span&gt;uvicorn&lt;span class="w"&gt; &lt;/span&gt;avilpage.asgi&lt;span class="w"&gt; &lt;/span&gt;--log-level&lt;span class="w"&gt; &lt;/span&gt;critical&lt;span class="w"&gt; &lt;/span&gt;--workers&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will spin up 4 workers which should be able to handle more load. If this performance is not sufficient, we have to setup a load balancer and spin up multiple servers(just like scaling any other web application).&lt;/p&gt;</description><category>devops</category><category>django</category><category>featured</category><category>python</category><guid>https://avilpage.com/2018/05/deploying-scaling-django-channels.html</guid><pubDate>Fri, 18 May 2018 15:51:21 GMT</pubDate></item></channel></rss>