<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.3.4">Jekyll</generator><link href="https://reliccornhusk.github.io/feed.xml" rel="self" type="application/atom+xml"/><link href="https://reliccornhusk.github.io/" rel="alternate" type="text/html" hreflang="en"/><updated>2024-09-17T20:16:15+00:00</updated><id>https://reliccornhusk.github.io/feed.xml</id><title type="html">blank</title><subtitle>Based in Montreal, Canada. This is a space where I share my latest findings, interests and projects related to my work as a DevOps engineer. </subtitle><entry><title type="html">Migrating from Docker to Podman</title><link href="https://reliccornhusk.github.io/blog/2024/podman-migration/" rel="alternate" type="text/html" title="Migrating from Docker to Podman"/><published>2024-08-30T00:00:00+00:00</published><updated>2024-08-30T00:00:00+00:00</updated><id>https://reliccornhusk.github.io/blog/2024/podman-migration</id><content type="html" xml:base="https://reliccornhusk.github.io/blog/2024/podman-migration/"><![CDATA[<figure> <picture> <source class="responsive-img-srcset" srcset="/assets/img/podman-post-480.webp 480w,/assets/img/podman-post-800.webp 800w,/assets/img/podman-post-1400.webp 1400w," sizes="95vw" type="image/webp"/> <img src="/assets/img/podman-post.png" class="img-fluid rounded z-depth-1" width="100%" height="auto" loading="eager" onerror="this.onerror=null; $('.responsive-img-srcset').remove();"/> </picture> </figure> <h2 id="background">Background</h2> <p>For the past few years, I have used Docker and Docker Compose extensively to self-host applications on my “homelab server” – a nice way of calling an old laptop running Debian 24/7 mounted to the underside of my desk. I have been doing this mainly to self host a stack of open-source applications centered around media management. Self-hosting services taught me a lot about Linux systems administration, networking and all things containers. I cannot recommend this enough as a personal project for any DevOps engineer.</p> <p>For my learning purposes, the stack itself is not so interesting as much as <em>how</em> it’s deployed, and I’m constantly tweaking it to test out new tools and automations. I’ve already created Ansible playbooks that deploy it from scratch, Docker Compose files to version the containers and their configuration, connected all the services behind a reverse proxy using Caddy, and in the future I might migrate it to Kubenertes using a single-node cluster with K3S. But before that, I wanted to give Podman a try.</p> <h2 id="why-migrate-to-podman">Why migrate to Podman?</h2> <p>For me, it boils down to its greater compatibility with Kubernetes and its daemonless architecture. To elaborate on the former, <a href="https://kubernetes.io/blog/2020/12/02/dont-panic-kubernetes-and-docker/">Kubernetes has deprecated their Docker runtime support</a> in favour of runtimes compliant with the Container Runtime Interface (CRI), like Podman, containerd or CRI-O. This is understandable, since supoprting Docker meant maintaining Dockershim, an appendage needed to make it work with Kubernetes. To add to that, the Docker project has taken a turn away from its original free open source philosophy in recent years as it started implementing a subscription model and restricting the usability of the tool for free users.</p> <p>For this reason, open-source alternatives like RedHat’s Podman has started gaining a lot of traction. Additionally, if like me your end-goal is working with Kubernetes, Podman allows you to convert your Podman pods to Kubernetes definitions files and to build pods from Kubernetes files with neat commands like <code class="language-plaintext highlighter-rouge">podman kube generate</code> and <code class="language-plaintext highlighter-rouge">podman kube play</code>.</p> <p>Secondly, Podman uses a daemonless architecture to run containers, which means it does not require a service running in the background to spin up and manage the containers. This makes it more secure, since it does not require your containers to interact with a root-owned daemon, but also reduces the overhead required for running containers. Another edge it has over Docker is the ability to run containers as a rootless Linux user, which greatly improves the security of your deployments. In the scenario of a container exploit gaining access to the host, it would access the host from the non-privileged user’s perspective. Running rootless containers greatly improves the security of your deployments, though it should be noted that it also requires some amount of comfort around Linux permissions and has a few limitations, <a href="https://github.com/containers/podman/blob/main/rootless.md">as described in their documentation</a>, when compared to running with root permissions.</p> <p>Finally, if you know how to use Docker, you already know how to use Podman. For a regular user, you can <code class="language-plaintext highlighter-rouge">alias docker=podman</code> in your terminal and the same Docker commands you are used to will also work with Podman due to the effort of the project in keeping the commands compatible across the two. Also, there is Podman Compose for those used to Docker Compose, although power users should probably just stick to Quadlet + kube definition files for versioning their containers, since not all of the features available in a Docker Compose file will work with Podman.</p> <h2 id="the-migration-itself-the-good-the-bad-and-the-ugly">The migration itself: the good, the bad and the ugly</h2> <p>The process of migrating everything was unfortunately not as simple as putting down the Docker containers and bringing them back up with Podman. Maybe it could have been if I had opted to use Podman with a root account, but then it would be too easy! So here is an overview of the steps I took to get the all the same functionalities out of Podman using rootless containers, along with what I learned along the way.</p> <h3 id="setting-up-podman-and-creating-a-separate-user">Setting up Podman and creating a separate user</h3> <p>I began by setting up a new user on the host without sudo access. Using a privileged user, I performed the following steps:</p> <ol> <li><a href="https://podman.io/">Install Podman</a></li> <li><code class="language-plaintext highlighter-rouge">sudo useradd -m -s /usr/bin/zsh podman</code>: -m creates a home folder for the new user and -s sets that user’s shell. If you don’t use ZSH, replace it for /bin/bash.</li> <li><code class="language-plaintext highlighter-rouge">sudo passwd podman</code>: set up a new password for that user.</li> <li>Install <a href="https://github.com/containers/podman-compose">podman-compose</a> if migrating from docker-compose</li> <li><code class="language-plaintext highlighter-rouge">su -- podman</code>: login as the ‘podman’ user.</li> </ol> <p>Following that I thought I would be able to simply spin up all my containers in my Docker Compose files with the new user and call it a day. Nope. After spending a while trying to understand why my containers were not reachable, I started reading their logs before they exited and realized they could no longer access their configuration files. So I tried recursively changing the ownership of all of the required files to the podman user, but that still did not work. I took a step back and really got down to the nitty-gritty of how Podman manages Linux permissions, a topic which had always been a bit daunting for me. Fortunately there are some amazing blog posts by Red Hat on the topic which taugh me a lot (see <a href="##further-reading">Further reading</a>). Let’s hope I don’t lose you, dear reader, with the explanation ahead.</p> <h3 id="linux-permissions-around-containers">Linux permissions around containers</h3> <p>Containers (not just Podman) make use of an ingenious UNIX feature called user namespacing to ensure your container can do essentially the same things as your user would but without needing to use the same identity (User ID and Group ID) as your user. In the case of Podman, when you run any container, rootful or rootless, that container will be running by default under that user’s namespace as the root user of that namespace. Just like a VM that “thinks” it is its own machine but resides in another host, the container’s root user “thinks” it is the root user (UID 0 and GID 0), but in reality it is inside of another user’s namespace. Here is a quick demo to help wrap your head around that:</p> <ol> <li>In a running container, attach a shell and <code class="language-plaintext highlighter-rouge">touch</code> a file on a volume shared with the host (e.g.: <code class="language-plaintext highlighter-rouge">podman exec -it example-container touch /data/foo</code> if /data is a shared volume).</li> <li>Look up that file’s permissions from the container’s perspective (<code class="language-plaintext highlighter-rouge">ls -l</code>) and take note of the owner of the file (UID and GID 0, i.e. root).</li> <li>Now look up that same file’s permissions from the host’s perspective. It will display as if that the file was created by the host user itself.</li> </ol> <p>Why does it do that? Mainly so that an escaped process from the container will not have the same permissions outside the container as it had inside.</p> <p>To add to this complexity, in my case the rootless Podman container was running with a non-root user inside of its namespace. To be more specific, the container’s main process was running under UID:GID 1000:1000. It turns out this <a href="https://www.redhat.com/sysadmin/rootless-podman-makes-sense">is actually best practice</a> if your containers’ main process does not require elevation. So if the root user in the namespace (UID 0) gets mapped to the host user runnning Podman (let’s assume UID/GID 15000), the user performing the processes in ther container was actually getting mapped to UID 16000 on the host (15000 from the host user + 1000 from the namespace UID/GID). So if we were to perform the same demo above, the file from the host’s perspective would be owned not by itself, but by a user with UID/GID 16000.</p> <p>To try to abstract all of this, Podman’s developers created the weirdly-named command <code class="language-plaintext highlighter-rouge">podman unshare</code> to allow you to issue any command from inside Podman’s user namespace without needing to spin a container and <code class="language-plaintext highlighter-rouge">exec</code> into it. This is especially useful for this type of situation, where it allows you to abstract the mapping of UIDs and GIDs and simply issue the command from the container’s perspective. Thus, I was able to run <code class="language-plaintext highlighter-rouge">podman unshare chown -R 1000:1000 /data</code> to give the user with ID 1000 inside the container ownership over the files required by the containers.</p> <h3 id="deploying-and-versioning">Deploying and versioning</h3> <p>After recursively changing the ownership of the files managed by the container, I was able to deploy everything and join the containers into pods as desired. I opted mostly for turning each container into its own pod and joined them all under the same bridge network to facillitate the integration with the reverse proxy. The next step was to generate Kubernetes definition files for these pods to version the deployment itself and make changes declaratively to my services. I did so by running <code class="language-plaintext highlighter-rouge">podman kube generate</code> to each of the pods and later redeployed them with <code class="language-plaintext highlighter-rouge">podman kube play</code> to ensure everything worked as before, and it did.</p> <p>To fully replicate all of the functionalities I had before, I still had to come up with a way for bringing up the containers automatically upon server reboot. With Docker, upon each reboot its daemon would bring up any container with a restart policy of ‘always’ or ‘unless-stopped’. With Podman’s daemonless architecture, this means you are in charge of defining systemd services for your containers/pods. They made this a lot easier with version 4.4 through a tool called Quadlet that allows you to create systemd services through reusable, simplified configuration files. Think of it like a <code class="language-plaintext highlighter-rouge">k8s</code> kubelet that got squashed and became a <em>quad</em>let. It accepts using Kubernetes definition files in their recipe files, which made everything easier.</p> <p>So now, all of my containers became <em>rootless, kubernetes-ready, version-controlled</em> pods and services managed by a kubelet-like tool to manage them as a systemd service. I think I’ve reached container runtime nirvana.</p> <h2 id="further-reading">Further reading</h2> <ul> <li><a href="https://www.redhat.com/sysadmin/quadlet-podman">Make systemd better for Podman with Quadlet</a></li> <li><a href="https://www.redhat.com/sysadmin/rootless-podman-makes-sense">Running rootless Podman as a non-root user</a></li> </ul>]]></content><author><name></name></author><category term="Podman,"/><category term="Docker,"/><category term="Kubernetes"/><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Hello, World: blogging with Jekyll + GitHub Pages</title><link href="https://reliccornhusk.github.io/blog/2024/hello-world/" rel="alternate" type="text/html" title="Hello, World: blogging with Jekyll + GitHub Pages"/><published>2024-08-16T00:00:00+00:00</published><updated>2024-08-16T00:00:00+00:00</updated><id>https://reliccornhusk.github.io/blog/2024/hello-world</id><content type="html" xml:base="https://reliccornhusk.github.io/blog/2024/hello-world/"><![CDATA[<p><code class="language-plaintext highlighter-rouge">print("Hello, World")</code></p> <p>Hey there, you found my blog! This is a space I’ll be using to share a bit of what I do at work and my side projects, as well as what technologies and tools I’ve been learning about.</p> <p>This first post will be a bit meta. I’ll share my experience so far playing with the website generator Jekyll and publishing on GitHub Pages. This approach to blogging makes a lot of sense if you’re already comfortable with writing in Markdown, you have experience with Git and GitHub and enjoy using CLI tools (or would like to gain experience at that). It could even be a nice project for someone just starting out on their developer journey, allowing you to practice your skills with GitHub, Docker (and even a bit of Ruby) while also creating a platform to write about what you’re learning.</p> <h2 id="github-pages"><a href="https://pages.github.com">GitHub Pages</a></h2> <p>GitHub Pages is a way to host a static website for free directly out of a GitHub repository. This means you can simply commit to that repository, and it takes care of deploying the website for you using some predefined GitHub Actions workflows. It even provisions you a TLS certificate through Let’s Encrypt. Free users are allowed one website per account, and it supports custom domains at no extra cost. It checked all the boxes for me in terms of hosting platform, especially since I enjoy working with GitHub Actions. I’ve since learned there are other great options to consider, such as <a href="https://docs.gitlab.com/ee/user/project/pages/">GitLab Pages</a> and <a href="https://pages.cloudflare.com/">CloudFlare Pages</a>.</p> <h2 id="jekyll"><a href="https://jekyllrb.com">Jekyll</a></h2> <p>After that, I needed a tool for generating the blog itself and this is where Jekyll comes in. Jekyll is a blog-aware static website generator built on Ruby. It allows you to blog directly through Markdown files and to manage, build and serve your website through simple CLI commands. This means you don’t have to worry about managing a database as you would with WordPress: it looks for certain YAML and Markdown files to generate the HTML for your website. Simply running <code class="language-plaintext highlighter-rouge">jekyll serve</code> will host your website locally, allowing you to preview on a browser your changes almost in real time.</p> <p>There are other great options for static website generators. <a href="https://gohugo.io">Hugo</a> is an even more performant alternative to Jekyll which is built on Go. I haven’t had the time to try it, but I recommend <a href="https://cloudcannon.com/blog/jekyll-vs-hugo-choosing-the-right-tool-for-the-job/">this post</a> for a comparison between the two. Here is what made me go with Jekyll:</p> <ul> <li> <p><strong>Simple setup</strong>. The Jekyll CLI is intuitive to use and has commands to facilitate the management of your files. So apart from changing my ruby version with rbenv and managing the dependencies with Bundler (nice skills to have picked up), there wasn’t much else I needed to do to get up and running. You could even skip setting up a local environment altogether depending on the Jekyll theme chosen. That is because a lot of themes come with custom GitHub Actions workflows that remove the need for installing Jekyll and any of its plugins locally. With <a href="https://github.com/alshedivat/al-folio">the theme I’m currently using</a>, you can simply push a commit and wait a few minutes while GitHub Actions builds and publishes your website with Jekyll. That works well for a hands-off approach, but I would still recommend setting up Jekyll locally to be able to play with the Jekyll command line and to preview your website before deploying. Many themes come with a Docker image for making that part seamless.</p> </li> <li> <p><strong>Widely adopted by an active community</strong>. This was the most important factor for me. If you browse the options for Jekyll themes, you will find a plethora to choose from built by the community, some free and some paid. There are a lot of amazing paid themes options <a href="https://jekyllthemes.io/">here</a> for fair prices, which come with instructions on how to configure to your liking. If you’re looking for a free theme, simply searching GitHub for “Jekyll theme” and sorting by number of stars will give you great options to choose from.</p> </li> </ul> <h2 id="conclusion">Conclusion</h2> <p>After a week or so playing with Jekyll and GitHub Pages to create this website, I would highly recommend it for the technology oriented of you out there who want to get into blogging or build a portfolio website for free. As a bonus, it taught me a little bit about interacting with Ruby projects and it was my first experience playing with Visual Studio Code’s Dev Containers—interesting on paper but quite laggy, think I’ll just stick to Podman! Most importantly, it saves you the trouble of dealing with HTML or managing a WordPress database.</p> <p>Of course, despite it being free (<em>if you don’t count my fancy domain</em>), it’s clear that there’s a trade-off in terms of the time and skills required with this approach versus simply paying for a service that builds a webpage for you through a user-friendly UI. The best option for you will be determined by how comfortable and willing you are to get ‘hands-on’ with the solution you choose.</p>]]></content><author><name></name></author><category term="GitHub"/><summary type="html"><![CDATA[print("Hello, World")]]></summary></entry></feed>