<?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.4.1">Jekyll</generator><link href="https://sean.thrailkill.cloud/feed.xml" rel="self" type="application/atom+xml" /><link href="https://sean.thrailkill.cloud/" rel="alternate" type="text/html" hreflang="en" /><updated>2025-12-17T18:05:51-06:00</updated><id>https://sean.thrailkill.cloud/feed.xml</id><title type="html">Good Kid, Mad Lab</title><subtitle>My thoughts and guides concerning my foray into creating my own private cloud/homelab</subtitle><entry><title type="html">You Need To Know About Bootc</title><link href="https://sean.thrailkill.cloud/posts/you-need-to-know-about-bootc/" rel="alternate" type="text/html" title="You Need To Know About Bootc" /><published>2025-01-03T00:00:00-06:00</published><updated>2025-03-25T22:11:34-05:00</updated><id>https://sean.thrailkill.cloud/posts/you-need-to-know-about-bootc</id><content type="html" xml:base="https://sean.thrailkill.cloud/posts/you-need-to-know-about-bootc/"><![CDATA[<p>At the risk of sounding like every YouTuber out there who wants you to click the “Subscribe” button right now, I really believe everyone who cares about Linux should start learning about <a href="https://github.com/containers/bootc">Bootc</a>. When looking at the future of Linux and what that could look like there’s already <a href="https://www.reddit.com/r/linux/comments/1hilxdm/is_immutable_the_future/">a lot of talk</a> of immutable distos like SteamOS, Fedora Silverblue, or NixOS being the future.</p>

<p>Starting off let me be clear, traditional distros aren’t going anywhere. They have their place and people already know and love them. Businesses rely on them. Yet people aren’t using them and we’ve been talking about “Year of the Linux Desktop” for longer than I have been alive. Immutable distros (or “Atomic” as they now like to be called) have been around for a while as well, but are relatively new in the Linux Desktop space and allow end users to be able a Linux desktop like an appliance. They can turn it on and know it will boot properly without having to worry about things users had to worry about in the past like drivers, kernel mods, or new packages breaking things – the ghosts of Linux Desktop Past. If you want to see what can be possible, look at the Steam Deck.</p>

<p>Alright, you get it by now – atomic distros are cool. What makes them even cooler is how you can make your own atomic distro. Enter Bootc! Bootc allows you to make an OS the same way you make an application, using containers! There is no denying that any applications recently developed are shipping today as a Docker container and most developers have some experience with them. Bootc takes this existing developer experience and directly translates it to your OS. Lets look at an example to see how easy it can be:</p>
<div class="language-Containerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="k">FROM</span><span class="s"> quay.io/fedora/fedora-bootc:41</span>
<span class="k">COPY</span><span class="s"> nginx.container /usr/share/containers/systemd</span>
<span class="k">COPY</span><span class="s"> nginx.conf /etc/nginx</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Simple, right? This copies a file to run a Nginx container as a <a href="https://mo8it.com/blog/quadlet/">quadlet</a> and a config into the <code class="language-plaintext highlighter-rouge">/etc</code> folder. Anytime I make a change or on a schedule I set it creates a new image and pushes it to my registry essentially bringing GitOps to my OS. With this whenever my machine starts, whether for the first time or millionth time, its going to be configured to work exactly as expected with no extra work or additional configuration. Day 1/2 configuration with other tools like Ansible or Chef are no longer necessary. Everything together in the same repo.</p>

<p>But that was a simple example for an Nginx server, what about something complex like a Linux desktop with all the software I want like VSCode, OpenRazr, and Nvidia drivers? Look no further than <a href="https://github.com/ublue-os">Universal Blue</a>. This project is the epitome of “I want everything to work out of the box with no extra work”. In fact <a href="https://www.theverge.com/2024/12/30/24329005/bazzite-asus-rog-ally-x-steam-os-editorial">The Verge</a> recently published an article saying that one of the Universal Blue projects, Bazzite, was a better experience than SteamOS. High praise! They put a lot of work into streamlining the user experience so anything you could want is just a simple command away and it always “Just Works” which is exactly the kind of experience normal non-technical users want. Something I can give to my parents and know they’ll have access to all the applications they need and won’t be able to break. Bootc is the first step to be able to achieve that.</p>

<p><strong>Bootc allows OS configuration to shift left and be testable and reproducible using the same tools developers already use for application development.</strong> Bootc isn’t the only piece to the puzzle and I’m just scratching the surface with what Bootc can do. There are more pieces like ComposeFS &amp; Systemd-sysext but Bootc is where I think people should start. If you want to see more examples feel free to check out <a href="https://gitlab.com/SNThrailkill/bootc">my repo on GitLab</a>.</p>

<p><em>Edited 2025-03-25 with some minor changes around feedback from HackerNews post</em></p>]]></content><author><name>Sean Thrailkill</name></author><category term="Bootc" /><category term="Atomic" /><category term="UBlue" /><category term="SteamOS" /><summary type="html"><![CDATA[At the risk of sounding like every YouTuber out there who wants you to click the “Subscribe” button right now, I really believe everyone who cares about Linux should start learning about Bootc. When looking at the future of Linux and what that could look like there’s already a lot of talk of immutable distos like SteamOS, Fedora Silverblue, or NixOS being the future.]]></summary></entry><entry><title type="html">Ubuntu Summit 2024</title><link href="https://sean.thrailkill.cloud/posts/ubuntu-summit-2024/" rel="alternate" type="text/html" title="Ubuntu Summit 2024" /><published>2024-11-02T00:00:00-05:00</published><updated>2024-11-02T00:00:00-05:00</updated><id>https://sean.thrailkill.cloud/posts/ubuntu-summit-2024</id><content type="html" xml:base="https://sean.thrailkill.cloud/posts/ubuntu-summit-2024/"><![CDATA[<p><img src="assets/img/posts/ubuntu-summit-banner.jpg" alt="A flag of the Ubuntu logo outside the venue" /></p>

<p>Last weekend I was able to attend my first Ubuntu Summit in Den Haag, Netherlands! I was kindly invited to represent the <a href="https://latenightlinux.com/">Late Night Linux</a> family of podcasts as one of the hosts of the <a href="https://hybridcloudshow.com/">Hybrid Cloud Show</a>. I was very excited as this means I could finally get to meet one of my co-hosts in person! Canonical paid for my flight and hotel and I was off to meet old friends as well as new ones. This was my first time really participating with the Ubuntu community and it was not quite what I expected.</p>

<p>Canonical has been talking about going public for a while now, at least <a href="https://techcrunch.com/2022/04/21/canonical-now-hopes-to-ipo-in-2023/">since 2018</a> according to my memory. I was positive that as time marches on and we get closer to this seemingly inevitable conclusion that their developer conference would share the same tones. However that was not my experience at all. There were no sponsored talks by partners, the schedule was tight and focused on interesting and innovative subjects, and many of the booths were for the various communities around Ubuntu rather than just companies looking to make a sale.</p>

<p>The booths actually were my favorite activity because it was just time to sit and talk about the various things going on in the community: Linux gaming (!), making better documentation not just for Ubuntu but for all, developing with upstream, and the intricacies of Object Oriented Programming in Rust – just to name a few. To be clear, there were a few companies out there selling things like System76 and Framework but that wasn’t the whole reason they were there. System76 was giving us a sneak peek of their <a href="https://www.phoronix.com/news/COSMIC-Desktop-Alpha-3">latest alpha for Cosmic</a> and discussing the complexity of making a desktop environment (almost) from scratch.</p>

<p>My favorite thing I saw was how a company called DeepComputing wanted to make a RISC-V developer kit. This isn’t as straightforward as many people might think. <a href="https://www.theverge.com/2024/10/18/24273366/qualcomm-cancels-snapdragon-dev-kit-mini-windows-on-arm-pc">Qualcomm found out the hard way</a> with its Windows ARM developer kit. Instead of trying to eat a whole elephant <a href="https://frame.work/blog/introducing-a-new-risc-v-mainboard-from-deepcomputing">they partnered with Framework</a> to make a mainboard that fit their 13-inch model. Framework laptops are famous for their easily repairable and swappable components. Thanks to Framework’s ethos for open sourcing everything, including its hardware specs, it was a perfect match. Now any developer can start trying to develop for RISC-V by just swapping out a piece that the <a href="https://youtu.be/NRJktOqnVr8">Framework CEO showed us takes under 5 minutes to do</a>.</p>

<p>While all that was impressive to me, what really blew me was the people I met. Everyone was <em>extraordinary</em>. No, really, I mean it. Every single person I interacted with outside getting coffee, at the booths, or during the talks was sincerely excited to be there and wanted to share their perspective on things. The Ubuntu community has some very intelligent people, not just the Canonical employees, and they’re all working towards making things better for everyone. A very sincere “Thank you” to the whole community for being very welcoming and open to all my questions about the past, present, and future of Ubuntu.</p>]]></content><author><name></name></author><category term="Ubuntu" /><category term="Summit" /><category term="Hague" /><category term="Netherlands" /><category term="HCS" /><category term="RISC-V" /><category term="Framework" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Using Immich with the Zalando Postgres Operator</title><link href="https://sean.thrailkill.cloud/posts/zalando-immich/" rel="alternate" type="text/html" title="Using Immich with the Zalando Postgres Operator" /><published>2024-03-04T00:00:00-06:00</published><updated>2024-03-04T00:00:00-06:00</updated><id>https://sean.thrailkill.cloud/posts/zalando-immich</id><content type="html" xml:base="https://sean.thrailkill.cloud/posts/zalando-immich/"><![CDATA[<h3 id="the-problem">The Problem</h3>

<p>Immich has been a game changer for my family and has fully replaced Google Photos for over a year. The project has been going strong for a while with no signs of stopping and continues to push out updates full of new features! Something happened though on the update to v1.91, Immich was able to remove a dependency on a service called Typesense which they used for indexing and searching through content by using a new extension called <code class="language-plaintext highlighter-rouge">vectors</code> inside of their existing PostgresQL database. You can read more about the reasoning <a href="https://github.com/immich-app/immich/discussions/5532">here</a>.</p>

<p>The result is that people who were using their own Postgres instance that wasn’t part of the Docker Compose or Helm Chart setup were out of luck with this upgrade and would have to figure out how to move forward. They recommended people switch to a new container image <code class="language-plaintext highlighter-rouge">tensorchord/pgvecto-rs</code> as it is a drop in replacement on top of the normal Postgres image but with the extensions needed included. It’s impressive to see a project this big be able to switch something like their database backend and not cause their users any fuss. However I was using the<a href="https://github.com/zalando/postgres-operator/"> Zalando Postgres Operator</a> which has its own Postgres image called <a href="https://github.com/zalando/spilo">Spilo</a>, so switching away was not so trivial for me.</p>

<p>Zalando is an enterprise-type business and as is customary, can move slowly sometimes. Someone created a <a href="https://github.com/zalando/spilo/issues/894">feature request</a> to get the <code class="language-plaintext highlighter-rouge">vector</code> extension into Spilo back in July 2023. Then in January 2024 <a href="https://github.com/zalando/postgres-operator/issues/2518">someone brought it up</a> at the operator level and someone responded that support has already been baked into Spilo and that the default image for the operator will be updated in the next release. So it seemed like all the pieces were in place to use the <code class="language-plaintext highlighter-rouge">vector</code> extension it was just a matter of time. I also couldn’t switch to <code class="language-plaintext highlighter-rouge">tensorchord/pgvecto-rs</code> as there are some basic issues preventing high availability like <a href="https://github.com/tensorchord/pgvecto.rs/issues/251">stream replication not working</a>.</p>

<p>I saw <a href="https://github.com/immich-app/immich/discussions/5532#discussioncomment-8495714">someone else mention in the comments</a> of the release that they were also looking for ways forward with their Postgres instances and one person even went so far as to <a href="https://github.com/immich-app/immich/discussions/5532#discussioncomment-8357522">build his own custom container image</a> which can be found <a href="https://git.shivering-isles.com/shivering-isles/infrastructure-gitops/-/compare/176fa49766644f921660e7d553dc04c5ee08944e...c7e6b1c7b719ac3cadd3853b0cfd378edd9676aa?from_project_id=233&amp;straight=false">here</a>. This was way too much work for me to try and take on at the time so my Immich instance sat on v1.90.2 for a long time, hoping for someone to come along and provide a path forward. Finally I found some time to tackle the issue and I think my solution is worth sharing.</p>

<h3 id="the-solution">The Solution</h3>

<p>This was actually very simple, if the <code class="language-plaintext highlighter-rouge">vector</code> extension was already supported in the newest Spilo image <code class="language-plaintext highlighter-rouge">ghcr.io/zalando/spilo-15:3.1-p1</code> then all I need to do is switch the one deployment over to the latest version. Like so!</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="rouge-code"><pre>    <span class="na">apiVersion</span><span class="pi">:</span> <span class="s">acid.zalan.do/v1</span>
    <span class="na">kind</span><span class="pi">:</span> <span class="s">postgresql</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">labels</span><span class="pi">:</span>
        <span class="na">team</span><span class="pi">:</span> <span class="s">acid</span>
      <span class="na">name</span><span class="pi">:</span> <span class="s">acid-immich</span>
      <span class="na">namespace</span><span class="pi">:</span> <span class="s">immich</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">dockerImage</span><span class="pi">:</span> <span class="s">ghcr.io/zalando/spilo-15:3.1-p1</span>
      <span class="na">allowedSourceRanges</span><span class="pi">:</span> <span class="pi">[]</span>
      <span class="na">databases</span><span class="pi">:</span>
        <span class="na">immich</span><span class="pi">:</span> <span class="s">immich</span>
      <span class="na">numberOfInstances</span><span class="pi">:</span> <span class="m">2</span>
      <span class="na">postgresql</span><span class="pi">:</span>
        <span class="na">version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">15'</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Once I had ensured that my database was running as normal on the new image, I found that in v1.95.0 they <a href="https://github.com/immich-app/immich/pull/6785">introduced compatibility</a> for the <code class="language-plaintext highlighter-rouge">vector</code> extension as opposed to <code class="language-plaintext highlighter-rouge">vectors</code> that is bundled with <code class="language-plaintext highlighter-rouge">pgvecto-rs</code> by adding this environment variables:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>    DB_VECTOR_EXTENSION=pgvector
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Then I just had to run the following commands against the database according to the migration documentation:</p>
<div class="language-terminal highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="gp">    CREATE EXTENSION vector;</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div></div>
<p>That’s it! My terabyte of photos were migrated in about 2 hours and I’m back to the latest version of Immich. Shout out to the team for this amazing project!</p>]]></content><author><name></name></author><category term="Immich" /><category term="Zalandro" /><category term="Postgres" /><category term="Operator" /><summary type="html"><![CDATA[The Problem]]></summary></entry><entry><title type="html">Using Ingress Controllers to Serve Traffic Outside a Kubernetes Cluster</title><link href="https://sean.thrailkill.cloud/posts/using-ingress-controllers-to-serve-traffic-outside-a-kubernetes-cluster/" rel="alternate" type="text/html" title="Using Ingress Controllers to Serve Traffic Outside a Kubernetes Cluster" /><published>2022-07-24T00:00:00-05:00</published><updated>2022-07-24T00:00:00-05:00</updated><id>https://sean.thrailkill.cloud/posts/using-ingress-controllers-to-serve-traffic-outside-a-kubernetes-cluster</id><content type="html" xml:base="https://sean.thrailkill.cloud/posts/using-ingress-controllers-to-serve-traffic-outside-a-kubernetes-cluster/"><![CDATA[<p>If you’re crazy like me and are running a Kubernetes cluster in your home then chances are you also have other computers running other services. In the past, I would have my storage server run Nginx for any internal services (meaning LAN only) and then used an ingress controller like Traefik or Nginx to serve traffic from inside my cluster to the internet. Recently, I thought that it might be better to also run my internal reverse proxy on something highly available should (God forbid) my storage server go down. So off to the internet I went looking. Turns out its actually surprisingly easy to accomplish this.</p>

<h3 id="setting-up-separated-ingress-controllers-for-internal--external-traffic">Setting Up Separated Ingress Controllers for Internal &amp; External Traffic</h3>

<p>First, update your existing chart/manifests to distinguish that your current ingress controller is going to be used for external traffic only. So change its <code class="language-plaintext highlighter-rouge">ingressClassName</code>, <code class="language-plaintext highlighter-rouge">containerName</code>, and anything else you feel necessary. Secondly, go to your existing ingresses and make sure they’re updated to have the correct <code class="language-plaintext highlighter-rouge">ingressClassName</code> or <code class="language-plaintext highlighter-rouge">kubernetes.io/ingress.class</code> (which is deprecated at time of writing) so that they will all still work correctly.</p>

<h3 id="setting-up-internal-traffic-inside-kubernetes">Setting Up Internal Traffic inside Kubernetes</h3>

<p>Now we have to create another instance of an ingress controller for internal services that I want to run on my cluster. Very simple, just duplicate your helm chart or manifests, do the same changes as the previous step and change the values like <code class="language-plaintext highlighter-rouge">ingressClass</code>, <code class="language-plaintext highlighter-rouge">controller.name</code>, etc … to correctly route for the new internal ingress controller, then add the new LoadBalancer IP to your local DNS. I suggest making a subdomain for all your internal traffic. For example all my internal services are on <code class="language-plaintext highlighter-rouge">local.thrailkill.cloud</code> instead of the base domain. Totally up to you.</p>

<h3 id="setting-up-internal-traffic-outside-kubernetes">Setting Up Internal Traffic outside Kubernetes</h3>

<p>Finally – the reason for the article! The secret here is a Kubernetes resource called <a href="https://www.educba.com/kubernetes-endpoints/">Endpoints</a> and how they relate to a service. A service does not link straight to pods. When Kubernetes sees a service, and if the service selector matches a pod label, Kubernetes will automatically create an Endpoints object with the same name as the service, which stores the pod’s IP address and port. We can create our own endpoints in comboination with a <a href="https://stackoverflow.com/questions/52707840/what-is-a-headless-service-what-does-it-do-accomplish-and-what-are-some-legiti">headless service</a> to forward the traffic outside our cluster. So for example:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="rouge-code"><pre>    <span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
    <span class="na">kind</span><span class="pi">:</span> <span class="s">Endpoints</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">name</span><span class="pi">:</span> <span class="s">my-service</span>
      <span class="na">namespace</span><span class="pi">:</span> <span class="s">my-namespace</span>
    <span class="na">subsets</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">addresses</span><span class="pi">:</span>
          <span class="pi">-</span> <span class="na">ip</span><span class="pi">:</span> <span class="s">10.0.0.100</span>
        <span class="na">ports</span><span class="pi">:</span>
          <span class="pi">-</span> <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Then, to create a headless service you need to set <code class="language-plaintext highlighter-rouge">clusterIP</code> to <code class="language-plaintext highlighter-rouge">None</code>.</p>

<p>NOTE: The endpoint name and the service name must be the same.</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="rouge-code"><pre>    <span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
    <span class="na">kind</span><span class="pi">:</span> <span class="s">Service</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">name</span><span class="pi">:</span> <span class="s">my-service</span>
      <span class="na">namespace</span><span class="pi">:</span> <span class="s">my-namespace</span>
    <span class="na">spec</span><span class="pi">:</span>
      <span class="na">clusterIP</span><span class="pi">:</span> <span class="s">None</span>
      <span class="na">ports</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">port</span><span class="pi">:</span> <span class="m">8080</span>
        <span class="na">protocol</span><span class="pi">:</span> <span class="s">TCP</span>
        <span class="na">targetPort</span><span class="pi">:</span> <span class="m">8080</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>That should be it! Update your local DNS to point a URL to your internal ingress controller and it should work now!</p>

<h3 id="that-was-too-easy">That was too easy…</h3>

<p>Yeah, unfortunatly nothing is ever easy. Somethings like Proxmox or GitLab want to handle SSL termination on their end instead of serving HTTP traffic and letting the SSL termination be handled by a reverse proxy. In that case, you will have to allow <a href="https://kubernetes.github.io/ingress-nginx/user-guide/tls/#ssl-passthrough">SSL Passthrough</a> and that should get you where you want to be.</p>

<p>Also, Endpoints are in the process of being replaced by <a href="https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/">EndpointSlices</a>. I havn’t figured out quite yet how to use this resource but when I do I’ll update this guide.</p>

<p><a href="__GHOST_URL__/contact/">Let me know</a> if you have any questions or want to share your setup!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[If you’re crazy like me and are running a Kubernetes cluster in your home then chances are you also have other computers running other services. In the past, I would have my storage server run Nginx for any internal services (meaning LAN only) and then used an ingress controller like Traefik or Nginx to serve traffic from inside my cluster to the internet. Recently, I thought that it might be better to also run my internal reverse proxy on something highly available should (God forbid) my storage server go down. So off to the internet I went looking. Turns out its actually surprisingly easy to accomplish this.]]></summary></entry><entry><title type="html">Linux Laptops</title><link href="https://sean.thrailkill.cloud/posts/linux-laptops/" rel="alternate" type="text/html" title="Linux Laptops" /><published>2022-04-15T00:00:00-05:00</published><updated>2022-04-15T00:00:00-05:00</updated><id>https://sean.thrailkill.cloud/posts/linux-laptops</id><content type="html" xml:base="https://sean.thrailkill.cloud/posts/linux-laptops/"><![CDATA[<p>For Christmas I bought myself a <a href="https://www.lenovo.com/us/en/p/laptops/yoga/yoga-2-in-1-series/yoga-9-14itl5/88ygc901455">Lenovo Yoga 9i</a>. This lovely 14 inch laptop had the 3 features I was most looking for: touch screen, light weight enough to use as a tablet, and pretty good support for Linux. That last one is tough because unless you’re buying a specific model from a manufacturer like <a href="https://system76.com/laptops">System76</a> that’s not guaranteed. Lenovo has a history of good support for Linux and even offers some flavors of Linux directly, but none of the options are the touch screen/convertible form factor I was looking for.</p>

<h3 id="desktop-environment-vs-operating-system">Desktop Environment vs Operating System</h3>

<p>So when looking at running Linux there are 2 big things you want to look at: the Desktop Environment and the Operating System. The Desktop Environment is how your user interface will look and feel – things like your file browser, application menu, settings, etc. This is usually the most important choice. For a full list of possible Desktop Environments, <a href="https://en.wikipedia.org/wiki/Comparison_of_X_Window_System_desktop_environments#Overview">see this list</a>.</p>

<p>The other big choice is the operating system. This is all about the underpinnings of your computer. How often does it updates? Does it update to the latest and bleeding edge or does it prefer stable packages? If you are a casual user, this shouldn’t mean too much to you however if you are an advanced Linux user, this matters a whole lot. The “which OS (or usually called a Distro) is right for me?” question gets asked almost everyday and everyone has a different opinion. <a href="https://www.reddit.com/r/linux/search/?q=which%20o">I’m not kidding</a>.</p>

<p>In my trials I found KDE Plasma wasn’t quite ready for a touch centered experience. However I have seen some work to that end recently. Hopefully as more Linux devices like the Steam Deck come out that use Plasma we will see some more development time towards that effort.</p>

<h3 id="wayland-vs-x11">Wayland vs X11</h3>

<p>While Gnome worked better I never found a setup that had it all. X11 worked great for some features like auto-rotation and on screen keyboard but lacked features like gesture support on screen and trackpad. Wayland on the other hand supported the gestures and ran smoother but couldn’t auto-rotate and I had to manually open the keyboard when I wanted to type. And if you’re using any kind of software KVM like barrier or synergy or something then forget about support coming for Wayland anytime soon.</p>

<h3 id="so-how-did-it-go">So How Did It Go?</h3>

<p>So for my touch screen laptop, I knew I wanted a Desktop Environment that had good touch support, large icons, and an Operating System that was stable since my laptop isn’t used constantly. For this I tried GNOME Desktop Environment versions of Ubuntu 21.10, OpenSUSE Tumbleweed, Fedora 35, and Pop OS 21.10. Here’s how each went:</p>

<ul>
  <li>Ubuntu 21.10 - Overall was fantastic. Stable, had immediate support for touch screen, partial scaling for resolutions, and I’m already very familiar with Debian based systems.</li>
  <li>openSUSE Tumbleweed - Honestly this is more of a bleeding edge distro but I’m becoming more of a fan of Suse and wanted to give it a try.</li>
  <li>Fedora 35 - Good initial support, had to run some commands to get things working and never could get fingerprint reader to work. Good option.</li>
  <li>Pop OS 20.10 - Worked just as well as Ubuntu (no suprise since it’s based on Ubuntu) and had some extra goodies like Flatpack support right off the bat.</li>
</ul>

<p>Due to Pop OS working just as well as Ubuntu, the extra goodies it had, and the recent trouble with Ubuntu I decided to go with it for now. So far besides an issue with Sleep mode, it has worked really well. Battery life is about 8 hours, all my applications run fast and smooth, and it’s easy to administrate. Let’s hope it stays that way!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[For Christmas I bought myself a Lenovo Yoga 9i. This lovely 14 inch laptop had the 3 features I was most looking for: touch screen, light weight enough to use as a tablet, and pretty good support for Linux. That last one is tough because unless you’re buying a specific model from a manufacturer like System76 that’s not guaranteed. Lenovo has a history of good support for Linux and even offers some flavors of Linux directly, but none of the options are the touch screen/convertible form factor I was looking for.]]></summary></entry><entry><title type="html">Longhorn: Migrating from Cluster Manager to Cluster Explorer</title><link href="https://sean.thrailkill.cloud/posts/when-backups-delete-themselves/" rel="alternate" type="text/html" title="Longhorn: Migrating from Cluster Manager to Cluster Explorer" /><published>2022-01-16T00:00:00-06:00</published><updated>2022-01-16T00:00:00-06:00</updated><id>https://sean.thrailkill.cloud/posts/when-backups-delete-themselves</id><content type="html" xml:base="https://sean.thrailkill.cloud/posts/when-backups-delete-themselves/"><![CDATA[<p><img src="assets/img/posts/longhorn-logo.png" alt="Longhorn logo" /></p>

<h3 id="what-is-longhorn">What is Longhorn?</h3>

<p>So if you have a Kubernetes cluster, chances are you want to use the local storage on your nodes in a pool to run your cluster with. In my opinion, the only real option for this kind of setup is <a href="https://longhorn.io/">Longhorn</a>. I’ve been using Longhorn for about a year and when I first installed it, I did it quickly and easily though the Rancher interface. Between the reliability of the storage and the seamless integration with Rancher, Longhorn has been absolutely amazing.</p>

<h3 id="then-what-is-the-problem">Then what is the problem?</h3>

<p>Rancher has had 2 different interfaces for a while – Cluster Manager (the older one) and Cluster Explorer (the newer one). Cluster Manager was a great start but Rancher realized that its inherent design left it limited for areas like multi-tenancy and so it began working on a new interface that was more suited for bigger and better things. In both UIs, Rancher tries to make things easier for its users and offers “Apps”. Apps are just helm charts but it fills in a lot for you and runs it locally on the cluster so you never have to even drop to terminal.</p>

<p>So a lot of us installed these apps in the older interface and things were great. Then Rancher decided its new interface was ready for use and swapped the default interface to Cluster Explorer. While a welcome change in some ways, it was unwelcome in other ways. Apps that had been installed in the old interface no longer appeared nor were manageable in Cluster Explorer. That means you can’t upgrade, change, or even remove the application unless you go into terminal and run some commands/scripts/crds/etc. This was not ideal for many of us.</p>

<h3 id="how-do-we-fix-this">How do we fix this?</h3>

<p>Long story short, you don’t. The old interface using Helm 2 and the new interface uses Helm 3. There was a migration project called Helm 2to3 but for whatever reason, the way Cluster Manager installed it didn’t play nice with the conversion. There is no way to easily upgrade, but there is <a href="https://www.technowizardry.net/2021/12/upgrading-longhorn-from-helm-2-in-rancher-2-6-the-hard-way/">a hard way</a>.</p>

<p>My recommendation is to backup everything to S3 or NFS storage, <a href="https://longhorn.io/docs/1.2.3/deploy/uninstall/">follow the uninstall instructions</a>, and then reinstall using Cluster Explorer or just using Helm on your own. After which, you can restore your volumes and proceed as normal. While not ideal, I would much rather play it safe with my Kubernetes storage than something that might break down the line because of some hacks I did previously.</p>

<p>P.S. Make sure to follow the uninstall instructions carefully and give the operations time to finish, otherwise you might end up in a bad situation where you’ll have <a href="https://rancher-users.slack.com/archives/CC2UQM49Y/p1641162837159100">your backups delete themselves</a>. Shoutout to @JenTing for helping me through that.</p>

<h3 id="bonus">Bonus!</h3>

<p>After reaching out to the Longhorn team about the incompatability, I received <a href="https://rancher-users.slack.com/archives/CC2UQM49Y/p1642096809038600">this explanation</a> from Vincent:</p>

<blockquote>
  <p>Technically it was still Helm before, but Tiller in Helm 2 had no concept of multi-tenancy and everyone ran it as admin and let anyone with access deploy whatever they wanted through it.
So we added an “Apps” middleman that ran Tiller for you, only on-demand, as the user requesting the operation instead of giving away arbitrary admin.
Helm 3 does the same on its own (while eliminating the daemon  entirely) so our abstraction is no longer needed or useful.  And they have their own problems upgrading from 2-&gt;3 even without involving us. 
You can manage existing old Apps, under Legacy in the left nav when a single Project is selected in the filter at the top of the UI.  They are not “gone”.</p>
</blockquote>]]></content><author><name></name></author><category term="Longhorn" /><category term="SUSE" /><category term="Rancher" /><category term="Kubernetes" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">On the Importance of Logging: Setting up Loki</title><link href="https://sean.thrailkill.cloud/posts/on-the-importance-of-logging/" rel="alternate" type="text/html" title="On the Importance of Logging: Setting up Loki" /><published>2021-07-19T00:00:00-05:00</published><updated>2021-07-19T00:00:00-05:00</updated><id>https://sean.thrailkill.cloud/posts/on-the-importance-of-logging</id><content type="html" xml:base="https://sean.thrailkill.cloud/posts/on-the-importance-of-logging/"><![CDATA[<h3 id="why-loki">Why Loki?</h3>

<p>So the other day I had an application not doing its daily jobs correctly. Unfortunately, because of my docker setup on this particular node, I didn’t have the logs to go back and see what had happened. This left me to wait until the next day to watch the console in real time to diagnose the failure. In an enterprise setting this just isn’t something I have ever had to worry about because logging is so important that it’s almost a reflex anytime I write code. So why would I treat my private cloud any differently?</p>

<p>First off, this is not a comparison of the various logging products out there. There are a lot of other well-written articles out there concerning that. I particularly thought <a href="https://thechief.io/c/editorial/kubernetes-logging-tools-a-comparison/">The Chief I/O article</a> to be insightful and helpful. To me it came down to a solution that would be able to show me a unified interface to search my logs across Kubernetes as well as my other machines like my NAS or Raspberry Pis. The <code class="language-plaintext highlighter-rouge">kube-prometheus-stack</code> helm chart also seemed to give me a bunch of Out Of Memory exceptions that I couldn’t solve. So I needed to find something else.</p>

<p>For me, it came down to <a href="https://www.graylog.org/">Graylog</a> or <a href="https://grafana.com/oss/loki/">Loki</a>. I was already familiar with the Grafana, Prometheus, Alert Manager and the like. Graylog seemed like a really good product that has a lot of backing and adoption however I could not find an official way to bring in Kubernetes logs into Graylog. There are third-party solutions out there for what it’s worth.</p>

<h3 id="the-work">The Work</h3>

<p>I decided to run this on my Kubernetes cluster as a helm chart. <a href="https://grafana.com/docs/loki/latest/installation/">Here are the various ways to run it</a> in case that isn’t for you.</p>

<ol>
  <li>Create a new Loki namespace (always separate workloads by namespace)</li>
  <li>Create your chart values file. Here is what mine looks like:
    <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
</pre></td><td class="rouge-code"><pre> <span class="na">loki</span><span class="pi">:</span>
   <span class="na">enabled</span><span class="pi">:</span> <span class="kc">true</span>
   <span class="na">persistence</span><span class="pi">:</span>
     <span class="na">enabled</span><span class="pi">:</span> <span class="kc">true</span>
     <span class="na">size</span><span class="pi">:</span> <span class="s">5Gi</span>
    
 <span class="na">promtail</span><span class="pi">:</span>
   <span class="na">enabled</span><span class="pi">:</span> <span class="kc">true</span>
    
 <span class="na">grafana</span><span class="pi">:</span>
   <span class="na">enabled</span><span class="pi">:</span> <span class="kc">true</span>
   <span class="na">sidecar</span><span class="pi">:</span>
     <span class="na">datasources</span><span class="pi">:</span>
       <span class="na">enabled</span><span class="pi">:</span> <span class="kc">true</span>
   <span class="na">image</span><span class="pi">:</span>
     <span class="na">tag</span><span class="pi">:</span> <span class="s">7.5.0</span>
    
 <span class="na">prometheus</span><span class="pi">:</span>
   <span class="na">enabled</span><span class="pi">:</span> <span class="kc">true</span>
   <span class="na">alertmanager</span><span class="pi">:</span>
     <span class="na">persistentVolume</span><span class="pi">:</span>
       <span class="na">enabled</span><span class="pi">:</span> <span class="kc">true</span>
   <span class="na">server</span><span class="pi">:</span>
     <span class="na">persistentVolume</span><span class="pi">:</span>
       <span class="na">enabled</span><span class="pi">:</span> <span class="kc">false</span>
</pre></td></tr></tbody></table></code></pre></div>    </div>
  </li>
  <li>Run the helm install command:
    <div class="language-terminal highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="gp">helm install loki grafana/loki-stack --values values.yaml -n &lt;YOUR NAMESPACE&gt;</span><span class="w">
</span></pre></td></tr></tbody></table></code></pre></div>    </div>
  </li>
  <li>
    <p>That’s it! If everything worked correctly you should have logging via Loki and monitoring through Prometheus. To see grafana you can get the <code class="language-plaintext highlighter-rouge">admin</code> user account password by running: <code class="language-plaintext highlighter-rouge">kubectl get secret --namespace loki loki-grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo</code></p>
  </li>
  <li>To see Grafana either do:</li>
</ol>

<ul>
  <li><code class="language-plaintext highlighter-rouge">kubectl edit svc grafana-loki -n &lt;YOUR NAMSPACE&gt;</code> and change the service from ClusterIp to HostPort or LoadBalancer then access from the IP address.</li>
  <li>Or you can temporaily connect by forwarding the port to your localhost by doing: <code class="language-plaintext highlighter-rouge">kubectl port-forward --namespace loki service/loki-grafana 3000:80</code></li>
</ul>

<p>Congrats! Now make sure you familiarize yourself with the <a href="https://grafana.com/docs/loki/latest/logql/">LogQL</a> that Loki uses for queries. It’s a bit of a learning curve but I’ve found it to be intuitive. I might write up a review if I feel strongly one way or another.</p>]]></content><author><name></name></author><category term="Logging" /><category term="Loki" /><category term="Graylog" /><category term="Grafana" /><summary type="html"><![CDATA[Why Loki?]]></summary></entry><entry><title type="html">Private Cloud vs Self Hosted</title><link href="https://sean.thrailkill.cloud/posts/private-cloud-vs-self-hosted/" rel="alternate" type="text/html" title="Private Cloud vs Self Hosted" /><published>2021-06-14T00:00:00-05:00</published><updated>2021-06-14T00:00:00-05:00</updated><id>https://sean.thrailkill.cloud/posts/private-cloud-vs-self-hosted</id><content type="html" xml:base="https://sean.thrailkill.cloud/posts/private-cloud-vs-self-hosted/"><![CDATA[<h3 id="when-you-cant-self-host">When you can’t self host</h3>

<p>For those of us who value our privacy and security of our data, self hosting our apps and services is really the only way to go. In almost no other manner can you guarantee that your data isn’t being spied on, metadata isn’t being collected, or network traffic being watched to name some examples. Sure there are ways to combat that like encrypting your data before sending it, but its easier and safer in our little controlled piece of the internet.</p>

<blockquote>
  <p>“With great power comes great responsibility” - Ben Parker</p>
</blockquote>

<p>With complete control of our network, data, and security it also means that we who self host are also 100% responsible for managing the infrastructure, securing it, and making it available to all users. Some of this can be easy with the right tools. At home I use Terraform to maintain my Kubernetes cluster which then all my services run on Kubernetes. My hosts are all easily managed and updated using Ansible. I can spin up and spin down my whole stack in minutes since I have built them with good tools. However even with these good tools that doesn’t mean that my stuff is secure. For example, I have to set a lot of rules on my router and switches to make sure that other devices can’t snoop or see the traffic and vice versa. Even with these amazing tools, it’s not going to matter if my internet goes out. Like most problems, if you throw enough money at it you can probably solve it. I could use multiple ISPs, or have a 5G failover connection. What about if I lose power? I have to buy a generator? Man, things add up quick and suddenly you have to ask yourself, are all these things worth it?</p>

<p>Honestly, unless you’re self hosting something you probably shouldn’t be, the answer most likely is no. Self-hosting usually means your hosting your stuff. Stuff that you and maybe your family and friends use. They’ll understand if things go down for a bit. They can live without Plex or Nextcloud for a day. But for the things that matter, the things like your APIs or your website where you sell your handmade organic tree based writing surfaces, those might cost your money while they’re down. For these, its time to use a cloud service. Someone else needs to deal with failover connections, backup generators, data replication and backups, all the overhead that comes with hosting important apps and services.</p>

<p>WAIT HOLD ON DON’T CLICK AWAY YET</p>

<p>I know, I know, I want to self-host everything too. However, being a good engineer is knowing when to use the right tool. Unless you have a really good reason, you shouldn’t be reinventing the wheel and adding backup generators to your house… I mean…unless you live in an area with hurricanes or something and already have one…</p>

<h3 id="option-a---private-cloud">Option A - Private Cloud</h3>

<p>SO. If you shouldn’t self host, what are you going to do? Well it depends, if you’re looking at doing things long-term, then I believe it’s best to use a private cloud whether its a Virtual Private Cloud or Virtual Private Server. I wouldn’t go full buy-and-install-your-own-hardware-in-a-datacenter Private Cloud unless you are a larger business and have special needs like regulatory compliance. If you’re already self hosting then the added burdens of managing a virtual private cloud/server should be no problem to you. Doing so gives you the most control you can possibly get without installing hardware in a co-op or something similar. It also allows you to offload some of the management or configuration to the hosting company for when your company takes off and you haven’t hired some additional help yet. Remember that a major reason we’re running a private cloud is because we want as much control over our infrastructure as possible, and we’re most likely going to be paying a price for that compared to a public cloud. <a href="https://www.vmware.com/content/dam/digitalmarketing/vmware/en/pdf/products/vrealize-suite/vmware-paper1-can-private-cloud-be-cheaper-than-public-cloud.pdf">According to a study done by VMware</a> running a private cloud usually is less expensive** in the long run** compared to a public cloud. Win-Win.</p>

<h3 id="option-b---public-cloud">Option B - Public Cloud</h3>

<p>If you are looking at doing something short-term then things can get complicated. It might be worth it to use the public cloud and be defensive about it by using tools to protect your privacy. For networking, technologies like <a href="https://www.wireguard.com/quickstart/">Wireguard</a> or <a href="https://slack.engineering/introducing-nebula-the-open-source-global-overlay-network-from-slack/">Nebula</a> make it easy to quickly add a public cloud instance to appear on your network so it can be treated as part of your private cloud. For storage, encryption is the name of the game. Almost all public cloud providers offer the ability to encrypt your data at rest. The trick is here to not give the provider the encryption key as well. Why would you give a burglar a key to get into your data? Usually it’s called something like “Customer-managed encryption”. If you HAVE to let the provider manage the keys then there are still options you can do like encrypting your data with a tool like <a href="https://cryptomator.org/">Cryptomator</a> before it lands on their hard drives. This is a lot of work to protect your data and privacy but man, public clouds are <a href="https://aws.amazon.com/free/?all-free-tier.sort-by=item.additionalFields.SortRank&amp;all-free-tier.sort-order=asc&amp;awsf.Free%20Tier%20Types=*all&amp;awsf.Free%20Tier%20Categories=*all">literally</a><a href="https://cloud.google.com/free/">giving</a><a href="https://www.oracle.com/cloud/free/">away</a><a href="https://www.ibm.com/cloud/free">free</a><a href="linode.com/ssh">resources</a> just to get you hooked – make it work for you.</p>

<h3 id="option-c---hybrid-cloud">Option C - Hybrid Cloud</h3>

<p>¿Por que no los dos?</p>

<p>For those of you who don’t speak Spanish, that means “Why not both?”. I know that “Hybrid Cloud” is a huge buzzword but I’m going to spell it out so it’s very clear what I am saying here: Self host + Private Cloud. That’s what I mean. It can get complicated between configuring load balancers and duplicating data across local nodes and cloud nodes but it’s a valid option for making sure that something is always available for your users. If you’re going this route I recommend using an orchestrator of some sort. Like if you are using VMware at home, then setting up HCX and VMware Cloud so that way its very easy to vMotion your stuff over if something happens on either location. Kubernetes can also do this natively. If a node disappears because the power goes out at home, it will try to auto heal and move workloads to new nodes within the rules you have set in your cluster.</p>

<h3 id="final-thoughts">Final Thoughts</h3>

<p>Like most things in Software Engineering, there are a lot of ways to go about things. A difference between a good engineer and a great engineer is knowing when to use which tools and how to best configure those tools. Just because I haven’t mentioned it here doesn’t mean its not a viable solution. If you find something that works for you, please comment or message me and let me know!</p>]]></content><author><name></name></author><category term="Private" /><category term="Hybrid" /><category term="Cloud" /><category term="Self-Hosted" /><category term="Architecture" /><summary type="html"><![CDATA[When you can’t self host]]></summary></entry><entry><title type="html">Rancher (Why and Why not)</title><link href="https://sean.thrailkill.cloud/posts/installing-rancher/" rel="alternate" type="text/html" title="Rancher (Why and Why not)" /><published>2021-04-16T00:00:00-05:00</published><updated>2021-04-16T00:00:00-05:00</updated><id>https://sean.thrailkill.cloud/posts/installing-rancher</id><content type="html" xml:base="https://sean.thrailkill.cloud/posts/installing-rancher/"><![CDATA[<p>Howdy! In this month’s post I wanted to talk about <a href="https://rancher.com/docs/rancher/v2.5/en/">Rancher</a>. I know there are already a lot of guides on this so I wanted to explain not only how to install Rancher but also what it is and why you would want to use this and when you wouldn’t.</p>

<h2 id="why">Why?</h2>

<p>If you are just starting to get into Kubernetes and containerization, Rancher can be a great tool for you. It strikes the incredible balance of making a lot of things easy and streamlined for you without hiding the advanced configuration options and details. This makes learning and using Kubernetes really easy with Rancher. It can be a little intimidating to deploy something when you just barely stood up your cluster so using Rancher can help you get stood up quickly. Rancher also doesn’t use and proprietary APIs or libraries when it performs actions. Its all the same tools you would use if you were doing this with CLI. On top of all this, Rancher adds a lot of visualization to what is happening inside your clusters and their health. Rancher even has its own repository of charts that come preconfigured for use with Rancher for things like reverse proxies, monitoring, alerting, distributed storage, and more.</p>

<h2 id="why-not">Why Not?</h2>

<p>Rancher is a fantastic tool to get started like I said, however it makes things so easy that it might become hard in the future to do anything without it. This can come back to hurt you when you look for a guide or are trying to do something in an environment that doesn’t have Rancher. Learning kubectl and the different parts of Kubernetes is absolutely critical even if you use Rancher. **Rancher is meant to add value to your existing tool set. Not replace it. **So if you understand this and want to get started with Rancher, see below!</p>

<h2 id="steps">Steps:</h2>

<p>We’re going to get you started by getting you a certificate to use with Rancher (SSL is always a good idea), then actually install Rancher.</p>

<ol>
  <li>Make sure you have <a href="https://helm.sh/docs/intro/install/">helm installed</a></li>
  <li>Install cert manager:
    <div class="language-terminal highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="go"> https://github.com/jetstack/cert-manager/releases/download/v1.3.1/cert-manager.crds.yaml
 kubectl create namespace cert-manager
 helm repo add jetstack https://charts.jetstack.io
 helm repo update
 helm install cert-manager jetstack/cert-manager \
 --namespace cert-manager \
 --version v1.3.1
</span></pre></td></tr></tbody></table></code></pre></div>    </div>
  </li>
  <li>Install Rancher and expose the deployment so you can get to it. Make sure to set the hostname to whatever you’re going to use to connect to Rancher. For example, <code class="language-plaintext highlighter-rouge">rancher.local</code> or if you’re going to expose this on your domain (which I wouldn’t advise) then something like <code class="language-plaintext highlighter-rouge">rancher.my.domain</code>.
    <div class="language-terminal highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre><span class="go"> helm install rancher rancher-latest/rancher \
   --namespace cattle-system \
   --set hostname=rancher.my.domain
 kubectl -n cattle-system rollout status deploy/rancher
 kubectl expose deployment rancher -n cattle-system --type=LoadBalancer --name=rancher-lb --port=443
</span></pre></td></tr></tbody></table></code></pre></div>    </div>
  </li>
</ol>

<p>That’s it! It is really that simple. Now when you go to the IP of the loadbalancer that was created, you’ll be greeted with a screen to set up a master account. After you set that up, you’ll be able to see you “local” cluster which is your k3s kubernetes cluster! Congrats!</p>

<p>The next step is to install a reverse proxy and then add port forwarding on your router to the static IP for your proxy and start making your deployments accessible to the internet. We’ll cover that next time.</p>]]></content><author><name></name></author><category term="Rancher" /><category term="Kubernetes" /><summary type="html"><![CDATA[Howdy! In this month’s post I wanted to talk about Rancher. I know there are already a lot of guides on this so I wanted to explain not only how to install Rancher but also what it is and why you would want to use this and when you wouldn’t.]]></summary></entry><entry><title type="html">Minimal High Availability Kubernetes Install</title><link href="https://sean.thrailkill.cloud/posts/high-availability-k3s-install/" rel="alternate" type="text/html" title="Minimal High Availability Kubernetes Install" /><published>2021-03-07T00:00:00-06:00</published><updated>2021-03-07T00:00:00-06:00</updated><id>https://sean.thrailkill.cloud/posts/high-availability-k3s-install</id><content type="html" xml:base="https://sean.thrailkill.cloud/posts/high-availability-k3s-install/"><![CDATA[<h2 id="why">Why?</h2>

<p>So for my self hosted setup, my workloads run on Kubernetes using k3s across 3 machines. For the sake of keeping it stupid simple, I’m going to provide some light context and documentation for where to go if you want to really understand whats happening.</p>

<p>If you want high availability (also commonly referred to as HA) then you have 2 options. You can either run an embedded database or an external database. The embedded DB uses etcd and the external DB can use any major DB (see the list <a href="https://rancher.com/docs/k3s/latest/en/installation/datastore/">here</a>). With an external DB you have a minimum of 2 machines and with the embedded you need a minimum of 3 but also you have to have an odd number of machines. See the full list of requirements for <a href="https://rancher.com/docs/k3s/latest/en/installation/ha/">external </a>and <a href="https://rancher.com/docs/k3s/latest/en/installation/ha-embedded/">embedded</a>. For this guide, I used the embedded since I have exactly 3 machines and I didn’t want to have to maintain another DB. Don’t worry, even if you use the embedded there’s still a way to backup your DB (stay tuned for another guide on how to setup that).</p>

<p>One thing that I am going to change here for my installation of k3s is their service load-balancer. Its <a href="https://rancher.com/docs/k3s/latest/en/networking/#service-load-balancer">Klipper</a> and while I’m sure it can work, I’d rather use MetalLB which I’m more comfortable with. Since I’m not using AWS or GCP for this deployment and therefore unable to use the load-balancers they offer, MetalLB will allow me to create and use a bare-metal load-balancer which will be helpful to help make my workloads HA across my different machines. More on that later.</p>

<p>So to keep this as stupid simple as possible, this is what you’re going to get by following this guide:</p>

<ol>
  <li>A load-balancer for your machines (so if one machine goes down, the other machines can still talk to each other)</li>
  <li>High Availbility Kubernetes Cluster (the dream)</li>
  <li>A load-balancer for your workloads (so your workloads can use static IPs which makes configuring workloads WAY easier)</li>
  <li>Sanity</li>
</ol>

<hr />

<h2 id="steps">Steps:</h2>

<ol>
  <li>
    <p>Make sure your machines have static IPs. Kubernetes REALLY doesn’t like it when machines start moving around, even with a load-balancer in front of them.</p>
  </li>
  <li>
    <p>Open up the correct ports on your machines. See <a href="https://rancher.com/docs/rancher/v2.x/en/installation/requirements/ports/#ports-for-rancher-server-nodes-on-k3s">here</a> for a list of what each is for.</p>
  </li>
</ol>

<p>The TCP ports are:</p>

<ul>
  <li>80</li>
  <li>179</li>
  <li>443</li>
  <li>2376</li>
  <li>2379</li>
  <li>2380</li>
  <li>6443</li>
  <li>9099</li>
  <li>9796</li>
  <li>10250</li>
  <li>10254</li>
</ul>

<p>The UDP ports are:</p>

<ul>
  <li>22</li>
  <li>80</li>
  <li>443</li>
  <li>2376</li>
  <li>4789</li>
  <li>6783</li>
  <li>6784</li>
  <li>7946</li>
  <li>8472</li>
</ul>

<ol>
  <li>Set up your machine load-balancer. You need to set up a load-balancer that will act as the single point of reference for your cluster. Then that load-balancer will distribute the requests to the machines as it sees fit. I used NGINX. Simple, light, and powerful. Feel free to use whatever you want. This can be run on docker or bare-metal. Literally thousands of ways and guides on how. Here is the config for NGINX:
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
</pre></td><td class="rouge-code"><pre>#uncomment this next line if you are NOT running nginx in docker
#load_module /usr/lib/nginx/modules/ngx_stream_module.so;
    
events {}
    
stream {
  upstream k3s_servers {
    server server0:6443;
    server server1:6443;
    server server2:6443;
  }
    
  server {
    listen 6443;
    proxy_pass k3s_servers;
    ssl_verify_client optional;
  }
}
</pre></td></tr></tbody></table></code></pre></div>    </div>
  </li>
  <li>Make sure your machines can talk to each other. a simple <code class="language-plaintext highlighter-rouge">nc -vz server:port</code> will tell you if your machines can talk to each other over these ports.</li>
  <li>Choose which computer is going to be the master machine, which are going to be servers, and which are going to be worker nodes. If you don’t know what this means, don’t worry. <a href="https://rancher.com/docs/k3s/latest/en/architecture/">Click here to understand</a>. Got it? Great. We’re going to install a small tool called <a href="https://github.com/alexellis/k3sup">k3sup</a> to help us easily install and manage k3s. Run the installer script like so:
<code class="language-plaintext highlighter-rouge">curl -sLS https://get.k3sup.dev | sh</code>
<code class="language-plaintext highlighter-rouge">sudo install k3sup /usr/local/bin/</code></li>
  <li>Now that you got it installed, here is a quick little bash script I made to make this super easy for you. All you got to do is add your hostnames or IPs and the location of the SSH key for these machines:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
</pre></td><td class="rouge-code"><pre> <span class="c">#!/bin/bash</span>
 <span class="nv">main_server</span><span class="o">=</span>server0
 <span class="nv">servers</span><span class="o">=</span><span class="s1">'server1 server2'</span>
 <span class="nv">agents</span><span class="o">=</span><span class="s1">'agent0 agent1 agent2'</span>
 <span class="nv">ssh_key</span><span class="o">=</span><span class="s1">'~/.ssh/ssh.pem'</span>
    
 <span class="nb">echo</span> <span class="s1">'Installing K3s on main server'</span>
 k3sup <span class="nb">install</span> <span class="se">\</span>
   <span class="nt">--ip</span> <span class="nv">$main_server</span> <span class="se">\</span>
   <span class="nt">--user</span> sean <span class="se">\</span>
   <span class="nt">--cluster</span> <span class="se">\</span>
   <span class="nt">--k3s-extra-args</span> <span class="s1">'--no-deploy traefik --no-deploy servicelb'</span> <span class="se">\</span>
   <span class="nt">--ssh-key</span> <span class="nv">$ssh_key</span>
 <span class="nb">echo</span> <span class="s1">' '</span>
 <span class="nb">echo</span> <span class="s1">'Joining other servers'</span>
 <span class="k">for </span>s <span class="k">in</span> <span class="nv">$servers</span><span class="p">;</span> <span class="k">do
 </span><span class="nb">echo</span> <span class="s1">' '</span>
 <span class="nb">echo</span> <span class="s1">'Joining '</span> <span class="nv">$s</span>
 k3sup <span class="nb">join</span> <span class="se">\</span>
   <span class="nt">--ip</span> <span class="nv">$s</span> <span class="se">\</span>
   <span class="nt">--user</span> sean <span class="se">\</span>
   <span class="nt">--server-user</span> sean <span class="se">\</span>
   <span class="nt">--server-ip</span> <span class="nv">$main_server</span> <span class="se">\</span>
   <span class="nt">--server</span> <span class="se">\</span>
   <span class="nt">--ssh-key</span> <span class="nv">$ssh_key</span>
 <span class="k">done
 </span><span class="nb">echo</span> <span class="s1">' '</span>
 <span class="nb">echo</span> <span class="s1">'Joining other agents'</span>
 <span class="k">for </span>s <span class="k">in</span> <span class="nv">$agents</span><span class="p">;</span> <span class="k">do
 </span><span class="nb">echo</span> <span class="s1">' '</span>
 <span class="nb">echo</span> <span class="s1">'Joining '</span> <span class="nv">$s</span>
 k3sup <span class="nb">join</span> <span class="se">\</span>
   <span class="nt">--ip</span> <span class="nv">$s</span> <span class="se">\</span>
   <span class="nt">--user</span> sean <span class="se">\</span>
   <span class="nt">--server-user</span> sean <span class="se">\</span>
   <span class="nt">--server-ip</span> <span class="nv">$main_server</span> <span class="se">\</span>
   <span class="nt">--ssh-key</span> <span class="nv">$ssh_key</span>
 <span class="k">done
    
 </span><span class="nb">echo</span> <span class="s1">'K3s Install Completed'</span>
</pre></td></tr></tbody></table></code></pre></div>    </div>
  </li>
  <li>Last step, you need to be able to control this cluster. So take the contents of <code class="language-plaintext highlighter-rouge">kubeconfig</code> that was generated for you and put that in the <code class="language-plaintext highlighter-rouge">~/.kube/config</code> folder on whatever machine youre going to use to run <code class="language-plaintext highlighter-rouge">kubectl</code> on.</li>
  <li>Now on said machine, test to see if everything is up and running by doing: <code class="language-plaintext highlighter-rouge">kubectl get nodes</code> and make sure you see all your machines (from now on, they’re called “nodes”).</li>
</ol>

<p>If you did everything right, you should see all your nodes when running</p>
<div class="language-terminal highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="go">kubectl get nodes
</span></pre></td></tr></tbody></table></code></pre></div></div>

<h2 id="what-now">What Now?</h2>

<p>Now you’ve got the dream! Congratulations! Hold up though, before you start applying <code class="language-plaintext highlighter-rouge">kubectl</code> commands all over the place, we wanted to use MetalLB, remember? So lets do that real quick.</p>

<ol>
  <li>Open up a terminal on your master node. Were going to run some <code class="language-plaintext highlighter-rouge">kubectl</code> commands.</li>
  <li>Run <code class="language-plaintext highlighter-rouge">kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.5/manifests/namespace.yaml</code></li>
  <li>Run <code class="language-plaintext highlighter-rouge">kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.5/manifests/metallb.yaml</code></li>
  <li>Run <code class="language-plaintext highlighter-rouge">kubectl create secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)"</code></li>
</ol>

<p>So now we have MetalLB installed but not configured. To do that we need to specify what range of IP addresses MetalLB can assign from. So create a file like this:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="rouge-code"><pre>    <span class="na">apiVersion</span><span class="pi">:</span> <span class="s">v1</span>
    <span class="na">kind</span><span class="pi">:</span> <span class="s">ConfigMap</span>
    <span class="na">metadata</span><span class="pi">:</span>
      <span class="na">namespace</span><span class="pi">:</span> <span class="s">metallb-system</span>
      <span class="na">name</span><span class="pi">:</span> <span class="s">config</span>
    <span class="na">data</span><span class="pi">:</span>
      <span class="na">config</span><span class="pi">:</span> <span class="pi">|</span>
        <span class="s">address-pools:</span>
        <span class="s">- name: default</span>
          <span class="s">protocol: layer2</span>
          <span class="s">addresses:</span>
          <span class="s">- ip.address.range.start-ip.address.range.end</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Hopefully it’s obvious, but replace <code class="language-plaintext highlighter-rouge">ip.address.range.start</code> and <code class="language-plaintext highlighter-rouge">ip.address.range.end</code> with the starting and ending IP address of the pool MetalLB can assign from. Something like <code class="language-plaintext highlighter-rouge">192.168.0.2-192.168.0.100</code>.</p>

<ol>
  <li>Final Step, apply that file you just created: <code class="language-plaintext highlighter-rouge">kubectl apply -f fileName.yaml</code></li>
</ol>

<h2 id="congratulations">Congratulations!</h2>

<p>You’re done. For now. Next step is to deploy some workloads or something. Next post will be about getting Rancher up and running to make deploying workloads way easier for someone not familiar or learning Kubernetes. Peace.</p>

<p><em>Edited 06/15/21 with updated commands to install K3s using k3sup</em></p>]]></content><author><name></name></author><category term="Rancher" /><category term="Kubernetes" /><category term="K3s" /><summary type="html"><![CDATA[Why?]]></summary></entry></feed>