<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
        <title>BBQ-CLOUD BLOG</title>
        <link>http://www.bbq-cloud.com/</link>
        <description>A Blog about Open Source stuff</description>
        <generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Sat, 10 Feb 2024 21:19:21 &#43;0100</lastBuildDate>
            <atom:link href="http://www.bbq-cloud.com/index.xml" rel="self" type="application/rss+xml" />
        <item>
    <title>My Homelab for 2024 Part 4</title>
    <link>http://www.bbq-cloud.com/homelab-2024-part4/</link>
    <pubDate>Sat, 10 Feb 2024 21:19:21 &#43;0100</pubDate><author>
        <name>Hugo CAMPION</name>
    </author><guid>http://www.bbq-cloud.com/homelab-2024-part4/</guid>
    <description><![CDATA[<h1 id="all-the-other-services" class="headerLink">
    <a href="#all-the-other-services" class="header-mark"></a>All the other services&hellip;</h1><p>And all of this took us to the point where we can list all the services that are not part of the infrastructure, that are truely services&hellip; I use <a href="https://github.com/gethomepage/homepage" target="_blank" rel="noopener noreferrer">Homepage</a> as my main <strong>dashboard</strong>. I configured it with traefik ingress route annotations watcher. If I remove a service, it will be automatically removed from the dashboard, same thing if I add one, it will be auto.</p>
<figure><a href="/images/homelab-2024/homepage-dashboard.png"><img src="/images/homelab-2024/homepage-dashboard.png"
         alt="Homepage dashboard" width="100%" height="100%"/></a>
</figure>

<p>All the Infra and Infrastructure categories are already covered in the series, monitoring too</p>
<h2 id="vaultwarden" class="headerLink">
    <a href="#vaultwarden" class="header-mark"></a>Vaultwarden</h2><p>I think that presenting <a href="https://github.com/dani-garcia/vaultwarden" target="_blank" rel="noopener noreferrer">Vaultwarden</a> is not needed, I put all my <strong>passwords</strong> in here, use it on the computer, the iPhone, the iPad and it is an amazing software. I was waiting for so long that Bitwarden finally commit account switching to the client because I was using the vault of my work with everything in one. When the commit was finally merged recently I deployed my own vault and now use it for my personnal accounts. I have no problem using the compagny one as only you can unlock your vault but you know the urge of SELFHosting <strong>everything</strong>.</p>
<figure><img src="/images/homelab-2024/vaultwarden-webui.png"
         alt="Vaultwarden Login Page" width="50%" height="50%"/>
</figure>

<h2 id="miniflux" class="headerLink">
    <a href="#miniflux" class="header-mark"></a>Miniflux</h2><p>I use <a href="https://github.com/miniflux/v2" target="_blank" rel="noopener noreferrer">miniflux</a> everyday to track my <strong>RSS feeds</strong>, I also track github releases. My choice went for this one because the webui works on mobile, it has postgres support, and OpenID connect.</p>
<figure><img src="/images/homelab-2024/miniflux-webui.png"
         alt="Miniflux Feed Page" width="50%" height="50%&#34;"/>
</figure>

<div class="details admonition tip open">
        <div class="details-summary admonition-title">
            <i class="icon fas fa-lightbulb fa-fw"></i>Track GitHub release with RSS<i class="details-icon fas fa-angle-right fa-fw"></i>
        </div>
        <div class="details-content">
            <div class="admonition-content">Playing with miniflux, I discovered that you can track a project releases by using <a href="https://github.com/go-vikunja/api/releases.atom" target="_blank" rel="noopener noreferrer">https://github.com/go-vikunja/api/releases.atom</a> for the vikunja/api project for example.</div>
        </div>
    </div>
<h2 id="vikunja" class="headerLink">
    <a href="#vikunja" class="header-mark"></a>Vikunja</h2><p>I do all my <strong>TODO and tasks management</strong> with <a href="https://kolaente.dev/vikunja/vikunja" target="_blank" rel="noopener noreferrer">Vikunja</a> that also support postgres and OpenID connect. I found the user experience nice even there is some little bugs but the dev is really reactive when you report them !</p>
<figure><img src="/images/homelab-2024/vikunja-logo.png"
         alt="Vikunja Logo" width="25%" height="25%"/><figcaption>
            <h4>Hands Down one of the best logo on earth...</h4>
        </figcaption>
</figure>

<h2 id="it-tools" class="headerLink">
    <a href="#it-tools" class="header-mark"></a>It-tools</h2><p>This <a href="https://github.com/CorentinTh/it-tools" target="_blank" rel="noopener noreferrer">one</a> is on the list of my daily drivers for sure. A nice collection of <strong>tools</strong> that comes handy when your are dev or sysadmin !</p>
<figure><img src="/images/homelab-2024/ittools-webui.png"
         alt="IT Tools webui" width="100%" height="100%"/>
</figure>

<h2 id="pdf-processing" class="headerLink">
    <a href="#pdf-processing" class="header-mark"></a>Pdf processing</h2><p><a href="https://github.com/Stirling-Tools/Stirling-PDF" target="_blank" rel="noopener noreferrer">Stirling PDF</a> let you Add / Remove / Rotate / Reorganize pages in a PDF, and many more things !</p>
<figure><img src="/images/homelab-2024/pdfstirling.png"
         alt="Stirling PDF Webui" width="100%" height="100%"/>
</figure>

<h2 id="online-copy--paste" class="headerLink">
    <a href="#online-copy--paste" class="header-mark"></a>Online copy / paste</h2><p><a href="https://github.com/PrivateBin/PrivateBin" target="_blank" rel="noopener noreferrer">Private Bin</a> gives me the possibility to <strong>send password or sensitive data</strong> to someone with the &ldquo;Burn after reading&rdquo; option, If the person tell me that he can not guest the pass, I know it is compromised.</p>
<figure><img src="/images/homelab-2024/privatebin.png"
         alt="PrivateBin Webui" width="100%" height="100%"/>
</figure>

<h2 id="document-management-system" class="headerLink">
    <a href="#document-management-system" class="header-mark"></a>Document Management System</h2><p><a href="https://github.com/paperless-ngx/paperless-ngx" target="_blank" rel="noopener noreferrer">Paperless NGX</a> watch a share on my NAS and archive everything that I scan and send to that share via the office scanner. I am really bad keeping papers&hellip; Now I am bad at scanning papers but I made a step forward&hellip;</p>
<figure><img src="/images/homelab-2024/paperless.png"
         alt="Paperless Webui" width="100%" height="100%"/>
</figure>

<h2 id="ambient-sound-to-cool-your-head-off" class="headerLink">
    <a href="#ambient-sound-to-cool-your-head-off" class="header-mark"></a>Ambient sound to cool your head off</h2><p><a href="https://github.com/remvze/moodist" target="_blank" rel="noopener noreferrer">Moodist</a> is an very simple service to deploy and act as a sort of <strong>ambient sound jukebox</strong>, you can set your own audio mood and let it run in background.</p>
<figure><img src="/images/homelab-2024/moodist.png"
         alt="Moodist Webui" width="50%" height="50%"/>
</figure>

<h2 id="but-you-prefer-to-blast-your-eardrums-with-acdc-like-me" class="headerLink">
    <a href="#but-you-prefer-to-blast-your-eardrums-with-acdc-like-me" class="header-mark"></a>But you prefer to blast your eardrums with AC/DC like me?</h2><p><a href="https://gitlab.com/Bockiii/deemix-docker" target="_blank" rel="noopener noreferrer">Deemix</a> is a cool tool to <strong>download music from a Deezer account</strong> and you can play it after in <a href="https://github.com/navidrome/navidrome" target="_blank" rel="noopener noreferrer">Navidrome</a> ( not on the dashboard )</p>
<figure><img src="/images/homelab-2024/deemix.png"
         alt="Deemix Webui" width="100%" height="100%"/>
</figure>

<h2 id="url-shortener" class="headerLink">
    <a href="#url-shortener" class="header-mark"></a>Url shortener</h2><p><a href="https://github.com/shlinkio/shlink" target="_blank" rel="noopener noreferrer">Shlink</a> is my URL shortener, I used it with private bin before noticing a major security flaw&hellip; Now I keep it but do not use it at the moment. It provides a nice GUI to manage your links and some insights.</p>
<figure><img src="/images/homelab-2024/shlink.png"
         alt="Shlink logo" width="25%" height="25%"/>
</figure>

<h2 id="google-alternative" class="headerLink">
    <a href="#google-alternative" class="header-mark"></a>Google alternative</h2><p>This one is the start of something I want to try for a long time : self host as many <strong>privacy oriented alternatives</strong> as I can. I currently looking at Invidious for Youtube, Wikiless for wikipedia, &hellip; you get the idea.</p>
<p><a href="https://github.com/benbusby/whoogle-search" target="_blank" rel="noopener noreferrer">Whoogle</a> is one of them, OK it fetches results from Google but remove a ton of crap before serving them to you&hellip;</p>
<figure><img src="/images/homelab-2024/whoogle.png"
         alt="Whoogle Search" width="100%" height="100%"/>
</figure>

<h2 id="wetransfer-alternative" class="headerLink">
    <a href="#wetransfer-alternative" class="header-mark"></a>Wetransfer alternative</h2><p>I found <a href="https://github.com/stonith404/pingvin-share" target="_blank" rel="noopener noreferrer">Pingvin Share</a> as a self hosted alternative I use it quite often. But I also came across <a href="https://github.com/eikek/sharry" target="_blank" rel="noopener noreferrer">Sharry</a> and I found it more robust, I will make the swap I think.</p>
<figure><img src="/images/homelab-2024/pingvin-share.png"
         alt="Pingvin-share Webui" width="100%" height="100%"/>
</figure>

<h2 id="documentation" class="headerLink">
    <a href="#documentation" class="header-mark"></a>Documentation</h2><p>Before writing this is started to document everything in <a href="https://github.com/requarks/wiki" target="_blank" rel="noopener noreferrer">WikiJS</a> and I need to continue. Very nice UI / User experience, everything is stored in postgres so the pods are stateless and you can scale them juste by increasing the replicas count. Also support OpenID.</p>
<figure><img src="/images/homelab-2024/wikijs-webui.png"
         alt="WikiJS Page" width="100%" height="100%"/>
</figure>

<h2 id="youtube-downloading" class="headerLink">
    <a href="#youtube-downloading" class="header-mark"></a>Youtube downloading</h2><p>If I need to download a song / video / playlist from youtube I go to my own instance of <a href="https://github.com/alexta69/metube" target="_blank" rel="noopener noreferrer">Metube</a>.</p>
<figure><img src="/images/homelab-2024/metube-webui.png"
         alt="Metube webui" width="100%" height="100%"/>
</figure>

<h2 id="file-sharing" class="headerLink">
    <a href="#file-sharing" class="header-mark"></a>File sharing</h2><p>I still use filestation for now but Nextcloud is the Goal to replace it soon.</p>
<h2 id="linux-distrib-isos-sharing" class="headerLink">
    <a href="#linux-distrib-isos-sharing" class="header-mark"></a>Linux Distrib ISOs sharing</h2><p>Still use downloadstation for that, I had a previous VM with Deluge and I think I will try to go with it again, worked quite well in a previous lab</p>
<h2 id="sketching" class="headerLink">
    <a href="#sketching" class="header-mark"></a>Sketching</h2><p>All my schemas are done with my own instance of <a href="https://github.com/excalidraw/excalidraw" target="_blank" rel="noopener noreferrer">Excalidraw</a></p>
<h2 id="so-how-does-it-looks-" class="headerLink">
    <a href="#so-how-does-it-looks-" class="header-mark"></a>So how does it looks ?</h2><figure><img src="/images/homelab-2024/homelab.jpg"
         alt="Metube webui" width="100%" height="100%"/>
</figure>

<p><strong>And that&rsquo;s a wrap for my 2024 Homelab tour !, as I already said reach me on reddit for questions&hellip;</strong></p>
<p><strong>Long Live <a href="https://www.reddit.com/r/homelab/" target="_blank" rel="noopener noreferrer">r/homelab</a> &amp; <a href="https://www.reddit.com/r/selfhosted/" target="_blank" rel="noopener noreferrer">r/selfhosted</a>!!!</strong></p>
]]></description>
</item><item>
    <title>My Homelab for 2024 Part 3</title>
    <link>http://www.bbq-cloud.com/homelab-2024-part3/</link>
    <pubDate>Wed, 07 Feb 2024 20:23:36 &#43;0100</pubDate><author>
        <name>Hugo CAMPION</name>
    </author><guid>http://www.bbq-cloud.com/homelab-2024-part3/</guid>
    <description><![CDATA[<h1 id="infrastructure-supporting-services" class="headerLink">
    <a href="#infrastructure-supporting-services" class="header-mark"></a>Infrastructure supporting services</h1><h2 id="authentication" class="headerLink">
    <a href="#authentication" class="header-mark"></a>Authentication</h2><figure><img src="/images/homelab-2024/authelia-logo.png"
         alt="Authelia Logo" width="50%" height="50%"/>
</figure>

<p>After deploying the first services I wanted to add <strong>authentication</strong> everywhere because some webUI like Longhorn or Thanos do not implement auth. At the beginning I did <strong>basicauth with my traefik</strong> ingress but I quickly searched for a true <strong>Single Sign On</strong> solution. My criteria were :</p>
<ul>
<li>Forward auth compatible to use it with traefik</li>
<li>All in one solution</li>
<li>OpenID provider</li>
<li>FIDO keys support</li>
</ul>
<p>So I tried authentik then found it complicated for nothing, and this <a href="https://github.com/goauthentik/authentik/issues/2023" target="_blank" rel="noopener noreferrer">BUG</a>&hellip;</p>
<p>Then I found <a href="https://github.com/authelia/authelia" target="_blank" rel="noopener noreferrer">Authelia</a> and use it now for everything. It is running in front of the services that needs auth because they do not provide it themselves. Services that are compatible with OpenID use it as a <strong>provider</strong>, that way I can login one time and acces everything. I use <a href="https://www.yubico.com/fr/product/security-key-series/security-key-nfc-by-yubico-black/" target="_blank" rel="noopener noreferrer">Yubikeys</a> as my second factor.</p>
<figure><img src="/images/homelab-2024/authelia-login.png"
         alt="Authelia Login banner on proxmox" width="50%" height="50%"/><figcaption>
            <h4>Here is the proxmox exemple</h4>
        </figcaption>
</figure>

<p>All my authelia users are stored in a LDAP database, I use <a href="https://github.com/lldap/lldap" target="_blank" rel="noopener noreferrer">LLDAP</a> to manage them, it is a very <strong>lightweight ldap</strong> implementation that is perfect for that use case.</p>
<div class="details admonition warning open">
        <div class="details-summary admonition-title">
            <i class="icon fas fa-exclamation-triangle fa-fw"></i>Synology compatibility<i class="details-icon fas fa-angle-right fa-fw"></i>
        </div>
        <div class="details-content">
            <div class="admonition-content">Just be aware that LLDAP is not working with Synology due to <a href="https://github.com/lldap/lldap/issues/340" target="_blank" rel="noopener noreferrer">This</a>. For now, I dit not find anything else that won&rsquo;t work with LLDAP !</div>
        </div>
    </div>
<p>I use the following middleware in traefik for the services that can not do OpenID :</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">traefik.containo.us/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Middleware</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">forwardauth-authelia</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">authelia</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app.kubernetes.io/instance</span><span class="p">:</span><span class="w"> </span><span class="l">authelia</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">app.kubernetes.io/name</span><span class="p">:</span><span class="w"> </span><span class="l">authelia</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">forwardAuth</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">address</span><span class="p">:</span><span class="w"> </span><span class="l">http://authelia.authelia.svc.cluster.local/api/verify?rd=https://whatever.yourdomain.com/</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">authResponseHeaders</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">Remote-User</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">Remote-Name</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">Remote-Email</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">Remote-Groups</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><h2 id="configuration-management" class="headerLink">
    <a href="#configuration-management" class="header-mark"></a>Configuration management</h2><p>I used <a href="https://github.com/ansible/ansible" target="_blank" rel="noopener noreferrer">Ansible</a> several times for work and found it amazing. We also leverage <a href="https://github.com/ansible/awx" target="_blank" rel="noopener noreferrer">AWX</a> to manage / launch our playbooks stored in GIT but for the Homelab I wanted to try to little brother of AWX : <a href="https://github.com/ansible-semaphore/semaphore" target="_blank" rel="noopener noreferrer">Semaphore</a>. Features parity is not here, but for the homelab it will cover my needs.</p>
<figure><img src="/images/homelab-2024/semaphore-webui.png"
         alt="Semaphore Web UI" width="100%" height="100%"/><figcaption>
            <h4>Just managed to launch a playbook with everything stored in my git !</h4>
        </figcaption>
</figure>

<h2 id="the-ci--cd-stack" class="headerLink">
    <a href="#the-ci--cd-stack" class="header-mark"></a>The CI / CD Stack</h2><h3 id="gitops" class="headerLink">
    <a href="#gitops" class="header-mark"></a>GitOps</h3><figure><img src="/images/homelab-2024/git-logo.png"
         alt="Git Logo" width="25%" height="25%"/>
</figure>

<p>As said in Part 1, I have a Gitea instance ( soon to be replace by Forgejo ) that store every single YAML files deployed in my Kubernetes Cluster. I use the GIT as a <strong>single source of truth</strong> for the cluster. I will try to extend that beyond the Kubernetes cluster, with my VM on the proxmox nodes and with Ansible.</p>
<h3 id="ci" class="headerLink">
    <a href="#ci" class="header-mark"></a>CI</h3><p>I use <a href="https://github.com/woodpecker-ci/woodpecker" target="_blank" rel="noopener noreferrer">Woodpecker</a> as my CI tools to build this website for example and some container Images. Nothing fancy here, juste a standard woodpecker install that runs in Kubernetes and launch pods for each steps in the pipeline. I use <a href="https://github.com/GoogleContainerTools/kaniko" target="_blank" rel="noopener noreferrer">Kaniko</a> to build docker images in kubernetes and push them in my private registry.</p>
<figure><a href="/images/homelab-2024/woodpecker-webui.png"><img src="/images/homelab-2024/woodpecker-webui.png"
         alt="Woodpecker WebUI" width="100%" height="100%"/></a>
</figure>

<h3 id="cd" class="headerLink">
    <a href="#cd" class="header-mark"></a>CD</h3><p>ArgoCD is the tool that <strong>sync everything</strong> between my GIT repo and my cluster. I deployed it in <strong>HA mode</strong> with the YAML files of the project. With autosync and autoheal enabled it will fix the cluster if the state drifts from the desired one that is stored in Git. There is an additional tool that you can use : <a href="https://github.com/argoproj-labs/argocd-image-updater/" target="_blank" rel="noopener noreferrer">ArgoCD Image Updater</a>, that will update the image of your app if it finds a  new one in the registry.</p>
<div class="details admonition failure open">
        <div class="details-summary admonition-title">
            <i class="icon fas fa-times-circle fa-fw"></i>Not ideal...<i class="details-icon fas fa-angle-right fa-fw"></i>
        </div>
        <div class="details-content">
            <div class="admonition-content">The image update project last release was one year ago and it doest <strong>not</strong> <a href="https://github.com/argoproj-labs/argocd-image-updater/issues/1" target="_blank" rel="noopener noreferrer">support webhook</a>&hellip; It will polls the registry every 3 minutes so the changes are not instant and if you have a lot of images to watch it will be problem !</div>
        </div>
    </div>
<p>But asides from that Image updating part, ArgoCD is a wonderfull tool that also provide a nice view on all your apps :</p>
<figure><a href="/images/homelab-2024/argocd-app-example.png"><img src="/images/homelab-2024/argocd-app-example.png"
         alt="App deployed with ArgoCD" width="100%" height="100%"/></a>
</figure>

<p>I use it to deploy plain YAML files, Kustomize files and I recently found that you can deploy a Helm chart from a repo with values from another repo so I will migrate all my Helm chart to ArgoCD ! I should be able to get rid of the image updater addon with using Renovate bot&hellip; I also love the fact that your ArgoCD apps objects can be deployed as plain YAML too because they are CRDs, Argo store everything in Kube and do not need a Database or persistent volume :</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">argoproj.io/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Application</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">annotations</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">argocd-image-updater.argoproj.io/allow-tags</span><span class="p">:</span><span class="w"> </span><span class="l">regexp:[1-9]+-[1-9]+</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">argocd-image-updater.argoproj.io/git-branch</span><span class="p">:</span><span class="w"> </span><span class="l">main</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">argocd-image-updater.argoproj.io/image-list</span><span class="p">:</span><span class="w"> </span><span class="l">yourprivateregistry.domain.com/hugo/hugo-blog</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">argocd-image-updater.argoproj.io/update-strategy</span><span class="p">:</span><span class="w"> </span><span class="l">name</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">argocd-image-updater.argoproj.io/write-back-method</span><span class="p">:</span><span class="w"> </span><span class="l">git</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">notifications.argoproj.io/subscribe.on-sync-succeeded.teams</span><span class="p">:</span><span class="w"> </span><span class="l">geco-teams</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">hugo-blog-prod</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="l">...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">destination</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">hugo-blog-app</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">server</span><span class="p">:</span><span class="w"> </span><span class="l">https://kubernetes.default.svc</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">project</span><span class="p">:</span><span class="w"> </span><span class="l">default</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">source</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">hugo-blog/overlays/prod</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">repoURL</span><span class="p">:</span><span class="w"> </span><span class="l">https://yourgitrepo.domain.com/h.campion/hugo-k8s-manifests</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">targetRevision</span><span class="p">:</span><span class="w"> </span><span class="l">HEAD</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">syncPolicy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">automated</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">selfHeal</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>You can also see there the annotations used to config to image updater addon and the teams notifications on succesfull sync.</p>
<h2 id="cloud-native-postgres" class="headerLink">
    <a href="#cloud-native-postgres" class="header-mark"></a>Cloud Native Postgres</h2><figure><img src="/images/homelab-2024/cnpg-logo.svg"
         alt="Cloud Native Postgres Logo" width="50%" height="50%"/>
</figure>

<p>For sure one of my favorite tool of the stack&hellip; And this one was suggested to me by someone on reddit, thanks for this one mate, I do not know your username anymore sorry&hellip;</p>
<p><a href="https://github.com/cloudnative-pg/cloudnative-pg" target="_blank" rel="noopener noreferrer">CNPG</a> is the solution I use to <strong>deploy all my databases</strong>. I went with postgres because of that operator, it just works, and without single failure since the start for me&hellip; And trust me I broke it everyway possible at the start and <strong>never lost</strong> a single bit of data&hellip; CNPG will do all of this for you:</p>
<ul>
<li>Bootstrap a replicated PGSQL cluster with 3 members in my case</li>
<li>Init the DB and the user</li>
<li>Init from a previous backup if you need to</li>
<li>Create a scheduled backup of your DB to a S3 storage</li>
<li>Create the key / certificates combo used to connect to your database</li>
<li>Store them in a regular kube secret to let you use it in your app</li>
<li>Deploy an exporter to let you watch you cluster with prometheus.</li>
<li>Selfheal your cluster if you destroy a member&hellip;</li>
<li>More cool shit that I&rsquo;m not aware of&hellip;</li>
</ul>
<p>It works well with ArgoCD as you can definie your DB cluster as YAML files like so :</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c"># Example of PostgreSQL cluster</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">postgresql.cnpg.io/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Cluster</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">vaultwarden</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">backup</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">barmanObjectStore</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">destinationPath</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;s3://cnpg&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">endpointURL</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;https//your_s3_compatible_endpoint:9000&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">s3Credentials</span><span class="p">:</span><span class="w">             </span><span class="c"># The credentials of your s3 bucket</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">accessKeyId</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">minio-creds</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">ACCESS_KEY_ID</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">secretAccessKey</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">minio-creds</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">ACCESS_SECRET_KEY</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">instances</span><span class="p">:</span><span class="w"> </span><span class="m">3</span><span class="w">                   </span><span class="c"># Number of members you want in that cluster</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">primaryUpdateStrategy</span><span class="p">:</span><span class="w"> </span><span class="l">unsupervised</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">storage</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">size</span><span class="p">:</span><span class="w"> </span><span class="l">10G</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">storageClass</span><span class="p">:</span><span class="w"> </span><span class="l">local-storage </span><span class="w"> </span><span class="c"># As explained before,</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># I use local volumes as CNPG will manage</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c"># HA on its own and I want max IO performance for the database...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">bootstrap</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">initdb</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">database</span><span class="p">:</span><span class="w"> </span><span class="l">vaultwarden</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">owner</span><span class="p">:</span><span class="w"> </span><span class="l">vaultwarden</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">secret</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">vaultwarden-db-secret</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">monitoring</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">enablePodMonitor</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">    </span><span class="c"># Tell cnpg to enable prometheus monitoring</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><h2 id="monitoring-stack" class="headerLink">
    <a href="#monitoring-stack" class="header-mark"></a>Monitoring stack</h2><h3 id="high-available-metrics-gathering" class="headerLink">
    <a href="#high-available-metrics-gathering" class="header-mark"></a>High Available Metrics Gathering</h3><p><figure class="schema-dark"><a href="/images/homelab-2024/prometheus-stack-dark-background.png"><img src="/images/homelab-2024/prometheus-stack-dark-background.png"
         alt="Prometheus Infrastructure Schema" width="100%" height="100%"/></a>
</figure>

<figure class="schema-light"><a href="/images/homelab-2024/prometheus-stack-light-background.png"><img src="/images/homelab-2024/prometheus-stack-light-background.png"
         alt="Prometheus Infrastructure Schema" width="100%" height="100%"/></a>
</figure>

<figure class="schema-black"><a href="/images/homelab-2024/prometheus-stack-black-background.png"><img src="/images/homelab-2024/prometheus-stack-black-background.png"
         alt="Prometheus Infrastructure Schema" width="100%" height="100%"/></a>
</figure>
</p>
<p>I use <strong>Prometheus and Thanos</strong> deployed in Kubernetes with the <a href="https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack" target="_blank" rel="noopener noreferrer">kube-prometheus-stack</a>. It ease the setup of :</p>
<ul>
<li>Prometheus operator that will deploy and configure Prometheus and AlertManagers</li>
<li>Prometheus instances, 3 in my case, with local volume like CNPG</li>
<li>AlertManagers Instances</li>
<li>Sidecars for the prometheus instances that Thanos will need</li>
<li>Kube State Metrics which is a Kubernetes exporter</li>
<li>Node exporter deployed as a DaemonSet on all kube nodes</li>
<li>All the needed CustomRessourceDefinitions</li>
<li>Grafana to view dashboards</li>
</ul>
<p>I found this HelmChart very good, you will need a good amount of time to read the values file and customize it for your needs but the default is killer to get started fast. I added a lot of <strong>exporters</strong> to the defaults ones :</p>
<ul>
<li>Every linux server outside my cluster with the <a href="https://github.com/prometheus/node_exporter" target="_blank" rel="noopener noreferrer">Node exporter</a></li>
<li>Powerdns cluster metrics exported by recursors and authoritative servers + <a href="https://github.com/eko/pihole-exporter" target="_blank" rel="noopener noreferrer">piHole exporter</a></li>
<li><a href="https://github.com/prometheus/blackbox_exporter" target="_blank" rel="noopener noreferrer">Blackbox</a> exporter to monitor HTTP metrics of my services</li>
<li><a href="https://github.com/prometheus/pushgateway" target="_blank" rel="noopener noreferrer">PushGateway</a> that is used to receive asynchronous metrics like K8UP job completions</li>
<li><a href="https://github.com/prometheus/snmp_exporter" target="_blank" rel="noopener noreferrer">SNMP Exporter</a> to poll SNMP on my NAS and Unifi devices, switches&hellip;</li>
<li><a href="https://github.com/billimek/prometheus-speedtest-exporter" target="_blank" rel="noopener noreferrer">Speedtest Exporter</a> for internet bandwidth checks</li>
</ul>
<p>I also try to <strong>gather metrics</strong> from all the services that supports it by leveraging <strong>ServiceMonitors</strong> or <strong>PodMonitors</strong>:</p>
<ul>
<li>Prometheus / Thanos components metrics</li>
<li>Loki components</li>
<li>Grafana</li>
<li>Authelia</li>
<li>Traefik</li>
<li>CNPG clusters</li>
<li>Healthcheckio status</li>
<li>Longhorn</li>
<li>Argo and woodpecker</li>
<li>&hellip;and everything that can expose a /metrics endpoint ^^</li>
</ul>
<p>On top of that I use <strong>thanos</strong> to be able to query my 3 prometheus instances, and <strong>ship the data to S3</strong> compatible minio on my NAS. I keep one week of data on the prometheus instances, in case of NAS failure I have 7 days of metrics to look for if needed. Then I keep one year on the NAS with the compactor downsampling the data to 1 hour after 30days. In my every day usage, I do my queries with the Thanos webUI, and look alerts on it too. My <strong>Grafana</strong> is also connected to the Thanos query frontend.</p>
<figure><a href="/images/homelab-2024/thanos-webui-querry.png"><img src="/images/homelab-2024/thanos-webui-querry.png"
         alt="Thanos query example in the WebUI" width="100%" height="100%"/></a>
</figure>

<p>I used the <a href="https://github.com/bitnami/charts/tree/main/bitnami/thanos" target="_blank" rel="noopener noreferrer">bitnami chart for Thanos</a> to help with the config of :</p>
<ul>
<li>Thanos querier that will fetch metrics from Sidecars and Storage Gateway</li>
<li>Thanos query frontend that enhance the query path with caching and retry</li>
<li>Thanos compactor that will compact / downsample TSDB block in your s3 storage</li>
<li>Thanos storage gateway that make your S3 storage available as a datasource for the querier !</li>
</ul>
<h3 id="visualize-the-data" class="headerLink">
    <a href="#visualize-the-data" class="header-mark"></a>Visualize the data</h3><p>Of course <a href="https://github.com/grafana/grafana" target="_blank" rel="noopener noreferrer">Grafana</a> is used because it is the best Open Source <strong>visualization</strong> product on earth !</p>
<p>In kubernetes your can configure it to be <strong>autoprovisionned</strong> from configMaps, that way the datasources and the Dashboards are kubernetes objects that can be stored in my Git and deployed with ArgoCD. I gathered some dashboards from internet, some comes directly from helm-charts or operator, and some from me. I&rsquo;m currently in the process of migrating my own dashboards to configMaps to make my Grafana &ldquo;stateless&rdquo;.</p>
<p>Here are some example :</p>
<p><figure><a href="/images/homelab-2024/grafana-web-ui.png"><img src="/images/homelab-2024/grafana-web-ui.png"
         alt="Grafana Overview dashboard" width="100%" height="100%"/></a><figcaption>
            <h4>Overview of the kubernetes cluster</h4>
        </figcaption>
</figure>

<figure><a href="/images/homelab-2024/grafana-cnpg-panel.png"><img src="/images/homelab-2024/grafana-cnpg-panel.png"
         alt="Grafana CNPG Dashboard" width="100%" height="100%"/></a><figcaption>
            <h4>Overview of one CPNG cluster</h4>
        </figcaption>
</figure>

<figure><a href="/images/homelab-2024/grafana-blackbox-panel.png"><img src="/images/homelab-2024/grafana-blackbox-panel.png"
         alt="Grafana Blackbox Dashboard" width="100%" height="100%"/></a><figcaption>
            <h4>BlackBox probe metrics panel</h4>
        </figcaption>
</figure>

<div class="details admonition tip open">
        <div class="details-summary admonition-title">
            <i class="icon fas fa-lightbulb fa-fw"></i>Nice kube dash<i class="details-icon fas fa-angle-right fa-fw"></i>
        </div>
        <div class="details-content">
            <div class="admonition-content">Take a look <a href="https://github.com/dotdc/grafana-dashboards-kubernetes" target="_blank" rel="noopener noreferrer">here</a> for nice kube dashboards that are very usefull !</div>
        </div>
    </div></p>
<h3 id="checking-alerts" class="headerLink">
    <a href="#checking-alerts" class="header-mark"></a>Checking alerts</h3><figure><a href="/images/homelab-2024/karma-webui.png"><img src="/images/homelab-2024/karma-webui.png"
         alt="Karma WebUI" width="100%" height="100%"/></a><figcaption>
            <h4>I also have a screen always on with Karma on it which only display alerts</h4>
        </figcaption>
</figure>

<h3 id="healthchecksio" class="headerLink">
    <a href="#healthchecksio" class="header-mark"></a>Healthchecksio</h3><p>To monitor my cron tasks and backups that are not in Kubernetes I deployed <a href="https://github.com/healthchecks/healthchecks" target="_blank" rel="noopener noreferrer">Healthchecksio</a>. In two words : a service that will wait for the process you want to say <strong>Hello !</strong> on a defined schedule and send you alerts when it does <em>not</em>&hellip;</p>
<figure><a href="/images/homelab-2024/healthchecksio.png"><img src="/images/homelab-2024/healthchecksio.png"
         alt="Healthchecksio WebUI" width="100%" height="100%"/></a><figcaption>
            <h4>Here we can see that some of my backup are in progress, in yellow. Green ones are already done</h4>
        </figcaption>
</figure>

<h3 id="website-analytics" class="headerLink">
    <a href="#website-analytics" class="header-mark"></a>Website analytics</h3><p>This website use <a href="https://github.com/umami-software/umami" target="_blank" rel="noopener noreferrer">Umami</a> to do <strong>analytics</strong>. I considered <a href="https://github.com/matomo-org/matomo" target="_blank" rel="noopener noreferrer">Matomo</a> too but the lack of postgres support made me give up the idea. I did not want to deal with two database operators just for Matomo. Umami is also simpler / lighter. Initially I was using a HUGO theme that was not compatible with Umami so I hid the script snippet in the footer but since I switched to a Umami compatible theme I should remove that and use the regular config.toml&hellip;</p>
<figure><a href="/images/homelab-2024/umami-webui.png"><img src="/images/homelab-2024/umami-webui.png"
         alt="Umami site dashboard" width="100%" height="100%"/></a><figcaption>
            <h4>As the site is not online the dash is pretty empty but you get the idea</h4>
        </figcaption>
</figure>

<div class="details admonition tip open">
        <div class="details-summary admonition-title">
            <i class="icon fas fa-lightbulb fa-fw"></i>Configuration<i class="details-icon fas fa-angle-right fa-fw"></i>
        </div>
        <div class="details-content">
            <div class="admonition-content">What I like about Umami is you can tell it to not gather analytics for some domain / from some IP,  so I disable everything for my DEV domain, and everything from my monitoring system</div>
        </div>
    </div>
<h2 id="logging-stack" class="headerLink">
    <a href="#logging-stack" class="header-mark"></a>Logging stack</h2><figure><img src="/images/homelab-2024/loki-logo.png"
         alt="Loki Logo" width="50%" height="50%"/>
</figure>

<p>I&rsquo;m still testing this on my setup because I have issue with storage right now ( space ) that I have to migrate but my previous tests where good. The setup I am evalutating is <a href="https://github.com/grafana/loki" target="_blank" rel="noopener noreferrer">Loki</a> installed in <a href="https://grafana.com/docs/loki/latest/get-started/deployment-modes/#simple-scalable" target="_blank" rel="noopener noreferrer">scalable mode</a> with the <a href="https://github.com/grafana/loki/tree/main/production/helm/loki" target="_blank" rel="noopener noreferrer">official helm chart</a>. The <a href="https://github.com/grafana/agent" target="_blank" rel="noopener noreferrer">grafana agent</a> is installed in Operator mode and use Promtail ( the loki agent ) under the hood to gather logs from my Pods. I managed to gather the acces log of my Traefik ingress controler without problem.</p>
<p>I used the same <em>technique</em> as CNPG and Prometheus for storage, <strong>local persistent volumes</strong> to have maximum performance. Logs are all shipped by Loki to my NAS via S3 protocol.</p>
<p>More on this later&hellip;</p>
<h2 id="pvc-backups" class="headerLink">
    <a href="#pvc-backups" class="header-mark"></a>PVC Backups</h2><figure><img src="/images/homelab-2024/k8up-logo.svg"
         alt="K8UP Logo" width="25%" height="25%"/>
</figure>

<p>I use <a href="https://github.com/k8up-io/" target="_blank" rel="noopener noreferrer">K8up</a> to <strong>backup</strong> all my kubernetes persistent volumes.</p>
<h2 id="oversafe-backup-strategy" class="headerLink">
    <a href="#oversafe-backup-strategy" class="header-mark"></a>Oversafe backup strategy&hellip;</h2><p>Objects that I <strong>backup</strong> :</p>
<ul>
<li>Host themselves with the Proxmox backup client CLI in a cronjob =&gt; to the pbs server</li>
<li>Virtuals machines config and drive with =&gt; to the pbs server</li>
<li>Kubernetes PVC with K8UP =&gt; to my NAS</li>
<li>CNPG Databases =&gt; to my NAS</li>
<li>Longhorn PVC with longhorn backup system =&gt; to my NAS</li>
<li>Configurations =&gt; to my GIT that is a saved vm to the pbs server</li>
</ul>
<p>The nas is snapshoted several times a day as I use <strong>btrfs</strong> and I <strong>sync</strong> it to an <strong>offsite</strong> NAS every evening.</p>
<p>The proxmox backup server is also synced to offsite PBS everyday.</p>
<p>The backups are <strong>watched</strong> by prometheus or healthcheckio and send teams notifications in case of failure. I also see the failure on karma or grafana.</p>
<figure><a href="/images/homelab-2024/grafana-backup-dashboard.png"><img src="/images/homelab-2024/grafana-backup-dashboard.png"
         alt="Grafana Backup Dashboard" width="100%" height="100%"/></a>
</figure>

<p><strong>Let&rsquo;s now talk about other services</strong></p>
]]></description>
</item><item>
    <title>My Homelab for 2024 Part 2</title>
    <link>http://www.bbq-cloud.com/homelab-2024-part2/</link>
    <pubDate>Sun, 04 Feb 2024 19:27:30 &#43;0100</pubDate><author>
        <name>Hugo CAMPION</name>
    </author><guid>http://www.bbq-cloud.com/homelab-2024-part2/</guid>
    <description><![CDATA[<h1 id="the-kubernetes-cluster-where-a-bunch-of-services-lives" class="headerLink">
    <a href="#the-kubernetes-cluster-where-a-bunch-of-services-lives" class="header-mark"></a>The Kubernetes cluster, where a bunch of services lives&hellip;</h1><figure><img src="/images/homelab-2024/kubernetes-logo.png"
         alt="Kubernetes Logo" width="50%" height="50%"/><figcaption>
            <p>1.26 was the best logo ever right&hellip;</p>
        </figcaption>
</figure>

<h2 id="the-fundation" class="headerLink">
    <a href="#the-fundation" class="header-mark"></a>The fundation</h2><p>The cluster consists of 6 <strong>Virtual Machines</strong> running on the Proxmox Cluster:</p>
<ul>
<li>3 Control Plane node with 2 Cores and 6G of RAM</li>
<li>3 Worker node with 6 Cores and 20G of RAM</li>
</ul>
<p>At the time I&rsquo;m writing this, they are running Debian 11 as OS and Kubernetes 1.28. I will update them to Debian 12 as I already started migrating some machines to 12 and the Hypervisor are already on 12. But for Kubernetes I like to stay <strong>one version behind</strong>, as they support the 3 last version, there is no problem doing this.</p>
<p>The cluster is provisionned and updated via the <a href="https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/" target="_blank" rel="noopener noreferrer">Kubeadm tool</a>, I wanted to learn the Kubernetes &ldquo;Vanilla&rdquo; tool, then I found it quite simple so I ditched to idea to switch to K3S&hellip; even this product is quite cool too !</p>
<p>It is in <strong>HA Mode</strong> with <strong>Etcd as backend</strong>, <strong>CoreDNS</strong> for the cluster DNS and I use <a href="https://kube-vip.io/" target="_blank" rel="noopener noreferrer">kube-vip</a> in ARP mode to share a <strong>Virtual IP</strong> between my control plane nodes. I can always access the API is one node still up !</p>
<p>I use <a href="https://github.com/projectcalico/calico" target="_blank" rel="noopener noreferrer">Calico</a> as my <strong>CNI</strong> ( Networking ) plugin, deployed with the <strong>Calico Operator</strong>, but I want to try <a href="https://cilium.io/" target="_blank" rel="noopener noreferrer">Cillium</a></p>
<p>Each proxmox host one worker and one control plane node.</p>
<h2 id="storage" class="headerLink">
    <a href="#storage" class="header-mark"></a>Storage</h2><h3 id="longhorn" class="headerLink">
    <a href="#longhorn" class="header-mark"></a>Longhorn</h3><figure><img src="/images/homelab-2024/longhorn-logo.png"
         alt="Rancher Longhorn Logo" width="25%" height="25%"/>
</figure>

<p>I wanted to have <strong>shared storage</strong> between my nodes, but not by using my NAS as a single point of failure. So I use my NAS to access data on it via NFS Mounts for Musics and Media that are not used by <strong>critical</strong> workloads, but for things that I do not want off when my NAS is in maintenance, I use <a href="https://longhorn.io/" target="_blank" rel="noopener noreferrer">Rancher Longhorn</a>. As my workers are connected through their own bridge with <strong>10G</strong>, longhorn is taking advantage of the faster network.</p>
<figure><a href="/images/homelab-2024/longhorn-webui.png"><img src="/images/homelab-2024/longhorn-webui.png"
         alt="Rancher Longhorn WebUI" width="100%" height="100%"/></a>
</figure>

<p>Longhorn data are on a <strong>dedicated disk</strong> on the VM, this disk is on the <strong>nvme</strong> storage of the proxmox. I can case longhorn fill up the disk, the workload using longhorn will crash but the kube node will not&hellip;</p>
<h3 id="local-volumes" class="headerLink">
    <a href="#local-volumes" class="header-mark"></a>Local volumes</h3><p>I also use <strong>local volumes</strong> for some workloads when I prefer to manage the availability directly with the product&hellip; The most common use case I have for that is <strong>Postgres Databases</strong>. I do not need to have replicated storage as the replication is managed by the database itself&hellip; I also use this for <strong>Prometheus</strong> metrics for example because my Prometheus is in HA Mode too, more on this later !</p>
<p>This is the storage class I use for that :</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">storage.k8s.io/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">StorageClass</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">local-storage</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">provisioner</span><span class="p">:</span><span class="w"> </span><span class="l">kubernetes.io/no-provisioner</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">volumeBindingMode</span><span class="p">:</span><span class="w"> </span><span class="l">WaitForFirstConsumer</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>And if I want a volume :</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">PersistentVolume</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">postgres-local-worker3-privatebin</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">capacity</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">storage</span><span class="p">:</span><span class="w"> </span><span class="l">10G</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">accessModes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">ReadWriteOnce</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">persistentVolumeReclaimPolicy</span><span class="p">:</span><span class="w"> </span><span class="l">Retain</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">storageClassName</span><span class="p">:</span><span class="w"> </span><span class="l">local-storage</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">local</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">/mnt/postgres-volume/privatebin</span><span class="w"> </span><span class="c"># Path on the host you want to use</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">nodeAffinity</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">required</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">nodeSelectorTerms</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">matchExpressions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">kubernetes.io/hostname</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">operator</span><span class="p">:</span><span class="w"> </span><span class="l">In</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">values</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span>- <span class="l">k8s-worker3</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p><div class="details admonition tip open">
        <div class="details-summary admonition-title">
            <i class="icon fas fa-lightbulb fa-fw"></i>Scheduling<i class="details-icon fas fa-angle-right fa-fw"></i>
        </div>
        <div class="details-content">
            <div class="admonition-content">Notice how I make sure that the pod which ask for that volume will be scheduled on the right host, I use nodeAffinity for that. If you do not do that, the pod could be started on a different node each time, with data that is obviously not synced&hellip;</div>
        </div>
    </div>
<div class="details admonition danger open">
        <div class="details-summary admonition-title">
            <i class="icon fas fa-skull-crossbones fa-fw"></i>Do not use on NON-HA wordloads<i class="details-icon fas fa-angle-right fa-fw"></i>
        </div>
        <div class="details-content">
            <div class="admonition-content">Due to the fact that with this pattern the pod will be tied to a particular node, in case that node is down the pod is down too because unschedulable. So you have to make sure that you can loose the pods that are using this volume. That is my case with the workloads I choose to run like this !</div>
        </div>
    </div></p>
<h2 id="loadbalancer" class="headerLink">
    <a href="#loadbalancer" class="header-mark"></a>LoadBalancer</h2><figure><a href="/images/homelab-2024/metallb-logo.png"><img src="/images/homelab-2024/metallb-logo.png"
         alt="Metallb Logo" width="25%" height="25%"/></a>
</figure>

<p>To deploy <strong>Loadbalancer</strong> type service I use <a href="https://metallb.universe.tf/" target="_blank" rel="noopener noreferrer">Metallb</a> in <strong>ARP</strong> mode. My services are tied to a <strong>virtual IP</strong> and Metallb will manage that VIP to make it follow the service when the pods are moving. The downside of this mode is that all traffic is routed to one node, so it can not be considered as <strong>true</strong> loadbalancing, but for <strong>failover</strong> it works like a charm !
<div class="details admonition tip open">
        <div class="details-summary admonition-title">
            <i class="icon fas fa-lightbulb fa-fw"></i>In case you need true load balancing<i class="details-icon fas fa-angle-right fa-fw"></i>
        </div>
        <div class="details-content">
            <div class="admonition-content">Metallb could also run in BGP mode where you will be able to split the traffic across nodes, I might try that with BIRD on my Linux router&hellip;</div>
        </div>
    </div></p>
<h2 id="ingress-controller" class="headerLink">
    <a href="#ingress-controller" class="header-mark"></a>Ingress Controller</h2><figure><a href="/images/homelab-2024/traefik-logo.png"><img src="/images/homelab-2024/traefik-logo.png"
         alt="Traefik Logo" width="25%" height="25%"/></a>
</figure>

<p>The <strong>Ingress controller</strong> of that cluster is <a href="https://github.com/traefik/traefik" target="_blank" rel="noopener noreferrer">Traefik</a> because when I started to learn docker and swarm I found it perfect, so I tried it on kube and found the same level of happyness with it ^^. I use the Custom Ressources Definitions from Traefik but the &ldquo;<em>regular</em>&rdquo; ingress api object is also enabled so that other software can leverage traefik like helm, operators or cert-manager.</p>
<p>I also use it as my reverse proxy for <strong>non-kubernetes</strong> workload. My NAS, Hypervisor WebUI, PBS WebUI are also <em>served</em> by traefik. To do this I use <strong>external service</strong> defined &ldquo;by hand&rdquo;.</p>
<p>Here is the proxmox webUI example which use 3 servers for backend :</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Service</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">external-pve</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">traefik</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">clusterIP</span><span class="p">:</span><span class="w"> </span><span class="l">None  </span><span class="w"> </span><span class="c"># We set clusterIP to none as the service is OUTSIDE</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">protocol</span><span class="p">:</span><span class="w"> </span><span class="l">TCP</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="m">8006</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Endpoints</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">external-pve</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">traefik</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">subsets</span><span class="p">:</span><span class="w">            </span><span class="c"># Then we set the endpoints &#34;by hand&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="nt">addresses</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">ip</span><span class="p">:</span><span class="w"> </span><span class="l">xxx.1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">ip</span><span class="p">:</span><span class="w"> </span><span class="l">xxx.2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">ip</span><span class="p">:</span><span class="w"> </span><span class="l">xxx.3</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="m">8006</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>Then the &ldquo;normal&rdquo; Traefik IngressRoute to use it :</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">traefik.containo.us/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">IngressRoute</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">external-pve</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">traefik</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">entryPoints</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">websecure</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">routes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">match</span><span class="p">:</span><span class="w"> </span><span class="l">Host(`xxx.domain.com`)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Rule</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">external-pve</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="m">8006</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">scheme</span><span class="p">:</span><span class="w"> </span><span class="l">https        </span><span class="w"> </span><span class="c"># We use https because the proxmox webui listen on TCP 8006 with TLS</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">sticky</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">cookie</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">traefik-sticky</span><span class="w"> </span><span class="c"># And we set stickyness to make the NoVNC console work !</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">tls</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">secretName</span><span class="p">:</span><span class="w"> </span><span class="l">xxx.domain.com</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><div class="details admonition tip open">
        <div class="details-summary admonition-title">
            <i class="icon fas fa-lightbulb fa-fw"></i>Scheduling<i class="details-icon fas fa-angle-right fa-fw"></i>
        </div>
        <div class="details-content">
            <div class="admonition-content">For monitoring purpose, log collection and metrics are enabled on the traefik</div>
        </div>
    </div>
<h2 id="addons" class="headerLink">
    <a href="#addons" class="header-mark"></a>Addons</h2><p>There is some Addons that I think are really usefull in a Kube cluster.</p>
<h3 id="kube-metric-server" class="headerLink">
    <a href="#kube-metric-server" class="header-mark"></a>Kube metric server</h3><p>The <a href="https://github.com/kubernetes-sigs/metrics-server" target="_blank" rel="noopener noreferrer">Kube Metrics Server</a> is here to give you basic <strong>metrics</strong> on your cluster, it is needed for the <em>top</em> commands to works like so :</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">kubectl top nodes
</span></span><span class="line"><span class="cl">NAME       CPU<span class="o">(</span>cores<span class="o">)</span>   CPU%   MEMORY<span class="o">(</span>bytes<span class="o">)</span>   MEMORY%
</span></span><span class="line"><span class="cl">manager1   176m         8%     3566Mi          61%
</span></span><span class="line"><span class="cl">manager2   170m         8%     3828Mi          65%
</span></span><span class="line"><span class="cl">manager3   249m         12%    3548Mi          60%
</span></span><span class="line"><span class="cl">worker1    978m         16%    14335Mi         71%
</span></span><span class="line"><span class="cl">worker2    1247m        20%    15032Mi         75%
</span></span><span class="line"><span class="cl">worker3    3362m        56%    11942Mi         59%
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">kubectl top pods
</span></span><span class="line"><span class="cl">NAME                       CPU<span class="o">(</span>cores<span class="o">)</span>   MEMORY<span class="o">(</span>bytes<span class="o">)</span>
</span></span><span class="line"><span class="cl">traefik-78d8f67fc8-scpv8   6m           115Mi
</span></span><span class="line"><span class="cl">traefik-78d8f67fc8-sxb4z   13m          220Mi
</span></span></code></pre></td></tr></table>
</div>
</div><p>But this is also what you need for Horizontal and Verticial Pod Autoscaler to work ! This is less true now because I&rsquo;m pretty sure that I saw you can connecter thoses autoscaler to custom metrics providers.</p>
<h3 id="sealed-secrets" class="headerLink">
    <a href="#sealed-secrets" class="header-mark"></a>Sealed Secrets</h3><p><a href="https://github.com/bitnami-labs/sealed-secrets" target="_blank" rel="noopener noreferrer">SealedSecrets</a> is <strong>mandatory</strong> if you want to go full <strong>GitOps</strong> like me, it will allow you to <strong>push secrets</strong> to a git repo, even a public one, because your <strong>secrets become encrypted</strong> and only your kubernetes cluster can decrypt them ! It works like this :</p>
<ul>
<li>You create a &ldquo;regular&rdquo; kubernetes secret in <em>mysecret-unsealed.yaml</em></li>
<li>You encrypt that secret with <code>kubeseal -f mysecret-unsealed.yaml -w mysecret.yaml</code></li>
<li>It will output a file to <em>mysecret.yaml</em></li>
<li><code>kubectl apply -f mysecret.yaml</code></li>
<li>The kubeseal controller will see the <strong>sealedsecrets</strong> object in your cluster and will <strong>unencrypt</strong> it to a <strong>regular secret</strong> that you can use like any other k8s secret !</li>
<li>You can ditch the unsealed file if you want, in any case that file should <strong>NOT</strong> be commit to a git repo ! The <strong>sealed</strong> one is made for that !</li>
</ul>
<p>I use it for every secrets, all my secrets are commited to a git repo, I also use a .gitignore with a pattern in case I forgot unsealed files somewhere&hellip;</p>
<div class="details admonition tip open">
        <div class="details-summary admonition-title">
            <i class="icon fas fa-lightbulb fa-fw"></i>Scheduling<i class="details-icon fas fa-angle-right fa-fw"></i>
        </div>
        <div class="details-content">
            <div class="admonition-content">You will need the kubeseal <strong>binary</strong> on your machine to be able to encrypt secrets, but it will manage and rotate key for you, and the install is very simple</div>
        </div>
    </div>
<h3 id="kured" class="headerLink">
    <a href="#kured" class="header-mark"></a>Kured</h3><p><a href="https://github.com/kubereboot/kured" target="_blank" rel="noopener noreferrer">Kured</a> is as Good as simple&hellip; It <strong>watches your nodes</strong> ( deployed as a <strong>DaemonSet</strong> ) for a specific file and <strong>reboot</strong> them when that file is present. So as I have debian with <strong>automatic upgrades</strong> configured, it will reboot the node when new kernels are installed for exemple. You can configure a lot of things like :</p>
<ul>
<li><strong>Planning</strong>, when nodes are allowed or not to reboot</li>
<li>Where to send <strong>notification</strong> on reboot, I use teams</li>
<li>Only drain pods that <strong>match a label</strong></li>
<li>A Prometheus instance to abort reboot if alerts are found</li>
<li>Max nodes to reboot at the sametime
&hellip;</li>
</ul>
<h3 id="defrag-cron-jobs" class="headerLink">
    <a href="#defrag-cron-jobs" class="header-mark"></a>Defrag Cron Jobs</h3><p>I use to have regular warnings of my Etcd Database being fragmented so I created a cronjob to take care of this and never had anymore alerts :</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">batch/v1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">CronJob</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">etcd-defrag-cronjob</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">kube-system</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">schedule</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;00 16 * * *&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">successfulJobsHistoryLimit</span><span class="p">:</span><span class="w"> </span><span class="m">5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">jobTemplate</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">template</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">containers</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">etcd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">k8s.gcr.io/etcd:3.5.3-0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">securityContext</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="nt">seccompProfile</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                  </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">RuntimeDefault</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">args</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span>- <span class="l">/bin/sh</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span>- -<span class="l">c</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span>- <span class="l">etcdctl=&#39;etcdctl&#39;;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                  </span><span class="l">export ETCDCTL_API=3;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                  </span><span class="l">etcdctl --endpoints=&#34;https://IP_OF_A_MANAGER_NODE:2379&#34; --cacert=&#34;/etc/kubernetes/pki/etcd/ca.crt&#34; --cert=&#34;/etc/kubernetes/pki/etcd/peer.crt&#34; --key=&#34;/etc/kubernetes/pki/etcd/peer.key&#34; defrag --cluster;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">volumeMounts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span>- <span class="nt">mountPath</span><span class="p">:</span><span class="w"> </span><span class="l">/etc/kubernetes/pki/etcd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">etcd-certs</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">nodeSelector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="nt">node-role.kubernetes.io/control-plane</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;&#39;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">hostPath</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">/etc/kubernetes/pki/etcd</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">DirectoryOrCreate</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">etcd-certs</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">restartPolicy</span><span class="p">:</span><span class="w"> </span><span class="l">OnFailure</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">tolerations</span><span class="p">:</span><span class="w">     </span><span class="c"># Tell kube that this pod can run on managers !</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;node-role.kubernetes.io/master&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">operator</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Equal&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">effect</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;NoSchedule&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span>- <span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;node-role.kubernetes.io/control-plane&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">operator</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;Equal&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">value</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">              </span><span class="nt">effect</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;NoSchedule&#34;</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><h3 id="cert-manager" class="headerLink">
    <a href="#cert-manager" class="header-mark"></a>Cert Manager</h3><figure><img src="/images/homelab-2024/certmanager-logo.png"
         alt="Cert-manager Logo" width="25%" height="25%"/>
</figure>

<p><a href="https://github.com/cert-manager/cert-manager" target="_blank" rel="noopener noreferrer">Cert-manager</a> allows you to <strong>request / issue certificates automatically</strong>. Several projects use it so I guess everyone needs it in a cluster at some point. Unless you manage certs yourself. I also use it to request HTTPS Certs for Traefik&hellip; I use the <strong>HTTP challenge</strong> with <a href="https://letsencrypt.org/" target="_blank" rel="noopener noreferrer">Let's Encrypt</a>.</p>
<h3 id="compliance-checker" class="headerLink">
    <a href="#compliance-checker" class="header-mark"></a>Compliance checker</h3><figure><img src="/images/homelab-2024/popeye-webui.png"
         alt="Popeye Web UI" width="100%" height="100%"/>
</figure>

<p>I found this little tool called <a href="https://github.com/derailed/popeye" target="_blank" rel="noopener noreferrer">Popeye</a> than can <strong>scan a cluster</strong> and tell you where you made some <strong>not so best practices things</strong>. There is an option to output in HTML format&hellip; So I use it directly in cluster, as a CronJob, and expose the result with a Caddy Web server pod. This way I can check the <strong>compliance</strong> of my cluster with a web browser.</p>
<h3 id="diun-image-checker" class="headerLink">
    <a href="#diun-image-checker" class="header-mark"></a>Diun Image checker</h3><figure><img src="/images/homelab-2024/diun-logo.png"
         alt="Diun Logo" width="25%" height="25%"/>
</figure>

<p><a href="https://github.com/crazy-max/diun" target="_blank" rel="noopener noreferrer">Diun</a> will <strong>watch your cluster</strong> and <strong>send alerts when new version of Images</strong> you are using got a <strong>update</strong>. As I&rsquo;m running full GitOps I will switch this to <a href="https://github.com/renovatebot/renovate" target="_blank" rel="noopener noreferrer">Renovate</a> but this little cool tool is worth mentionning !</p>
<p><strong>Next part will be the supporting services running on that cluster</strong></p>
]]></description>
</item><item>
    <title>My Homelab for 2024 Part 1</title>
    <link>http://www.bbq-cloud.com/homelab-2024-part1/</link>
    <pubDate>Mon, 29 Jan 2024 15:46:15 &#43;0100</pubDate><author>
        <name>Hugo CAMPION</name>
    </author><guid>http://www.bbq-cloud.com/homelab-2024-part1/</guid>
    <description><![CDATA[<p>I scratched my head thinking about how I can describe my Homelab, I started with wikijs, but seeing all the information I put in here like the serial number of the machines, IP addresses, vlans, etc&hellip; I started to feel less comfortable showing this to the internet. I was thinking about making two versions of the documentation in my wiki, one private, one public, then it seemed complicated to maintain&hellip; So here we are, it will be 2 or 3 articles on this blog.</p>
<div class="details admonition warning open">
        <div class="details-summary admonition-title">
            <i class="icon fas fa-exclamation-triangle fa-fw"></i>Comments<i class="details-icon fas fa-angle-right fa-fw"></i>
        </div>
        <div class="details-content">
            <div class="admonition-content">Commenting is disabled because I want it to happen on reddit, reddit is for discussion and in the first place I should have published that on the r/homelab subreddit&hellip; You will land here from a Reddit post&hellip;</div>
        </div>
    </div>
<div class="details admonition question open">
        <div class="details-summary admonition-title">
            <i class="icon fas fa-question-circle fa-fw"></i>Questions<i class="details-icon fas fa-angle-right fa-fw"></i>
        </div>
        <div class="details-content">
            <div class="admonition-content">Feel free to ask me if you are interested in a particular article !</div>
        </div>
    </div>
<h1 id="lets-start-with-the-physical-part" class="headerLink">
    <a href="#lets-start-with-the-physical-part" class="header-mark"></a>Let&rsquo;s start with the physical part</h1><h2 id="hypervisors" class="headerLink">
    <a href="#hypervisors" class="header-mark"></a>Hypervisors</h2><figure><img src="/images/homelab-2024/hp-g800.avif"
         alt="HP EliteDesk 800 G5" width="25%" height="25%"/>
</figure>

<table>
<thead>
<tr>
<th style="text-align:center">Make &amp; model</th>
<th style="text-align:center">HP EliteDesk 800 G5</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><i class="fas fa-microchip"></i></td>
<td style="text-align:center">6 x Intel(R) Core(TM) i5-9500(vc1&amp;2)/8500(vc3)  CPU @ 3.00GHz (1 Socket)</td>
</tr>
<tr>
<td style="text-align:center"><i class="fas fa-memory"></i></td>
<td style="text-align:center">32Go</td>
</tr>
<tr>
<td style="text-align:center"><i class="fas fa-hdd"></i></td>
<td style="text-align:center">2xSamsung 980 Nvme 500Go RAID1</td>
</tr>
<tr>
<td style="text-align:center"><i class="fas fa-hdd"></i></td>
<td style="text-align:center">1xShit SSD 500Go alone</td>
</tr>
<tr>
<td style="text-align:center"><i class="fas fa-network-wired"></i></td>
<td style="text-align:center">1x Gigabit Intel I219-LM ( embedded )</td>
</tr>
<tr>
<td style="text-align:center"><i class="fas fa-network-wired"></i></td>
<td style="text-align:center">2x Gigabit Intel I350</td>
</tr>
<tr>
<td style="text-align:center"><i class="fas fa-network-wired"></i></td>
<td style="text-align:center">2x 10Gigabit Intel 82599ES</td>
</tr>
<tr>
<td style="text-align:center"><i class="fab fa-windows"></i></td>
<td style="text-align:center">pve/8.1.3/b46aac3b42da5d15</td>
</tr>
</tbody>
</table>
<p>I run 3 HP SFF Desktop machines as <a href="https://www.proxmox.com/en/proxmox-virtual-environment/overview" target="_blank" rel="noopener noreferrer">Proxmox</a> node, I bought them used. Original lab was done with little MFF Dell optiplex, 3050 with core i5 and 32Gb ram each, performance was good with very low power consuption but they were running hot&hellip; The nvme especially, so I decided to abort it and went with bigger one to have better temperatures and 10G networking !</p>
<figure><a href="/images/homelab-2024/proxmox-webui.png"><img src="/images/homelab-2024/proxmox-webui.png"
         alt="Proxmox Web UI" width="100%" height="100%"/></a>
</figure>

<p>So there is a <strong>3 nodes cluster</strong>, storage is local with lvm thin on a RAID1 of 2 nvme, no HA, I manage availabily at service level.</p>
<p>I use them to run QEMU Virtual Machines and LXC Containers but I&rsquo;m migrating anything that left on LXC to QEMU. I use <a href="https://www.debian.org/" target="_blank" rel="noopener noreferrer">Debian</a> OS everywhere I can !</p>
<p>I use two separate bridges for networking, one with the two gigabit cards in LACP, with every VLAN tagged on it. All VMs / LXCs use it, backup go through this one, the WebUI too. The Two 10G cards are on a separate bridge, no LACP. only Kubernetes workers use this one and I made a loop with the main switch. The switch use the spanning tree to avoid making a mess and I can loose one cable or one host and still be able to reach every node. I use dell Direct Attach Copper cables for this loop, some Reddit research showed me that DAC are less expensive and run cooler !</p>
<h2 id="backup-server" class="headerLink">
    <a href="#backup-server" class="header-mark"></a>Backup server</h2><figure><img src="/images/homelab-2024/hp-microserver-g8.png"
         alt="Hp Microserver Gen 8" width="25%" height="25%"/>
</figure>

<table>
<thead>
<tr>
<th style="text-align:center">Make &amp; model</th>
<th style="text-align:center">HP Microserver G8</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><i class="fas fa-microchip"></i></td>
<td style="text-align:center">2 x Intel(R) Celeron(R) CPU G1610T @ 2.30GHz (1 Socket)</td>
</tr>
<tr>
<td style="text-align:center"><i class="fas fa-memory"></i></td>
<td style="text-align:center">16Go</td>
</tr>
<tr>
<td style="text-align:center"><i class="fas fa-hdd"></i></td>
<td style="text-align:center">4xIron Wolf 8To RAID10</td>
</tr>
<tr>
<td style="text-align:center"><i class="fas fa-hdd"></i></td>
<td style="text-align:center">1xCrucial SSD 120Go alone</td>
</tr>
<tr>
<td style="text-align:center"><i class="fas fa-network-wired"></i></td>
<td style="text-align:center">2x Gigabit Broadcom  BCM5720 ( embedded )</td>
</tr>
<tr>
<td style="text-align:center"><i class="fas fa-network-wired"></i></td>
<td style="text-align:center">1x Gigabit Intel I210</td>
</tr>
<tr>
<td style="text-align:center"><i class="fab fa-windows"></i></td>
<td style="text-align:center">pbs/2.4.1-1</td>
</tr>
</tbody>
</table>
<p>The oldest of the stack, nerver failed me&hellip;It runs <a href="https://www.proxmox.com/en/proxmox-backup-server/overview" target="_blank" rel="noopener noreferrer">Proxmox Backup Server</a> and all vzdumps from Proxmox hosts are stored here !</p>
<figure><a href="/images/homelab-2024/pbs-webui.png"><img src="/images/homelab-2024/pbs-webui.png"
         alt="PBS WebUI" width="100%" height="100%"/></a>
</figure>

<p>Due to the fact that the HP BIOS won&rsquo;t let you boot on the CDROM SATA port, where the SSD is connected, I installed GRUB on a small USB stick that seats inside the server, I boot from this USB, everyting else is on the SSD ( OS ) , ZFS RAID10 Storage ( backups ) on the Spinning drives.</p>
<h2 id="nas" class="headerLink">
    <a href="#nas" class="header-mark"></a>Nas</h2><figure><img src="/images/homelab-2024/synology-ds923plus.png"
         alt="Synology DS923&#43;" width="25%" height="25%"/>
</figure>

<table>
<thead>
<tr>
<th style="text-align:center">Make &amp; model</th>
<th style="text-align:center">Synology DS923+</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><i class="fas fa-microchip"></i></td>
<td style="text-align:center">2 x AMD Ryzen R1600 @ 2.6Ghz</td>
</tr>
<tr>
<td style="text-align:center"><i class="fas fa-memory"></i></td>
<td style="text-align:center">4Go</td>
</tr>
<tr>
<td style="text-align:center"><i class="fas fa-hdd"></i></td>
<td style="text-align:center">4x Western Red NAS nonpro 4To RAID 5</td>
</tr>
<tr>
<td style="text-align:center"><i class="fas fa-hdd"></i></td>
<td style="text-align:center">2 Toshiba NVME 512Go for cache</td>
</tr>
<tr>
<td style="text-align:center"><i class="fas fa-network-wired"></i></td>
<td style="text-align:center">Bond with 2 Gigabit NICs</td>
</tr>
<tr>
<td style="text-align:center"><i class="fab fa-windows"></i></td>
<td style="text-align:center">DSM 7.2</td>
</tr>
</tbody>
</table>
<p>This is my <strong>main storage</strong> for personal documents, media, backups&hellip; The only thing that run on this is DSM obviously and a minio docker container because I need S3 compatible storage. And of course NFS / SMB for the shares. My kubernetes cluster use NFS to mount volumes.</p>
<h2 id="networking" class="headerLink">
    <a href="#networking" class="header-mark"></a>Networking</h2><p><figure class="schema-dark"><a href="/images/homelab-2024/home-networking-physical-dark-background.png"><img src="/images/homelab-2024/home-networking-physical-dark-background.png"
         alt="HomeNetwork" width="100%" height="100%"/></a>
</figure>

<figure class="schema-light"><a href="/images/homelab-2024/home-networking-physical-light-background.png"><img src="/images/homelab-2024/home-networking-physical-light-background.png"
         alt="HomeNetwork" width="100%" height="100%"/></a>
</figure>

<figure class="schema-black"><a href="/images/homelab-2024/home-networking-physical-black-background.png"><img src="/images/homelab-2024/home-networking-physical-black-background.png"
         alt="HomeNetwork" width="100%" height="100%"/></a>
</figure>
</p>
<h3 id="isp-wan-piece-of-shit" class="headerLink">
    <a href="#isp-wan-piece-of-shit" class="header-mark"></a>ISP WAN Piece of Shit</h3><figure><img src="/images/homelab-2024/livebox5.png"
         alt="Livebox 5" width="25%" height="25%"/>
</figure>

<div class="details admonition question open">
        <div class="details-summary admonition-title">
            <i class="icon fas fa-question-circle fa-fw"></i>Comments<i class="details-icon fas fa-angle-right fa-fw"></i>
        </div>
        <div class="details-content">
            <div class="admonition-content">I am forced to use a livebox 5 from Sagemcom and do not want to talk about it</div>
        </div>
    </div>
<p>Getting rid of this is on the roadmap, I will try a ubiquiti GPON&hellip; In the meantime it is providing net to my main router and all ports are forwarded to it&hellip; All filtering is disabled, no DHCP, DNS or WIFI !</p>
<h3 id="main-router" class="headerLink">
    <a href="#main-router" class="header-mark"></a>Main Router</h3><figure><img src="/images/homelab-2024/apu2c4.png"
         alt="AlixBoard APU2C4" width="25%" height="25%"/>
</figure>

<table>
<thead>
<tr>
<th style="text-align:center">Make &amp; model</th>
<th style="text-align:center">AlixBoard APU2D2</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><i class="fas fa-microchip"></i></td>
<td style="text-align:center">4 x AMD GX-412TC @ 1Ghz</td>
</tr>
<tr>
<td style="text-align:center"><i class="fas fa-memory"></i></td>
<td style="text-align:center">2Go</td>
</tr>
<tr>
<td style="text-align:center"><i class="fas fa-hdd"></i></td>
<td style="text-align:center">16Go</td>
</tr>
<tr>
<td style="text-align:center"><i class="fas fa-network-wired"></i></td>
<td style="text-align:center">3x Gigabit Intel I211</td>
</tr>
<tr>
<td style="text-align:center"><i class="fab fa-windows"></i></td>
<td style="text-align:center">Debian 11 with Nftable</td>
</tr>
</tbody>
</table>
<p>More information of this amazing board <a href="https://www.pcengines.ch/apu2c4.htm" target="_blank" rel="noopener noreferrer">Here</a>.
This is my main <strong>firewall</strong>, only purpose of this box is <strong>filtering</strong>, <strong>routing</strong>, <strong>NAT</strong> and <strong>VPN</strong>. By default everything is rejected both ways IN and OUT, I try to be as precise as I can to just open what is needed.
There is two VPN on it, one <strong>wireguard</strong> for me when I&rsquo;m not at home and need access to something that is not exposed. And a LAN to LAN <strong>openvpn</strong> with the society I work for ! This way I can work from home, send my backups off site, and we can send the society backup in my Homelab, <strong>win/win for everybody</strong> !
Futur project is to have two off these sharing virtual IP to do HA.</p>
<h3 id="switching" class="headerLink">
    <a href="#switching" class="header-mark"></a>Switching</h3><h4 id="sw1-core-switch--mikrotik-crs326-24g-2s" class="headerLink">
    <a href="#sw1-core-switch--mikrotik-crs326-24g-2s" class="header-mark"></a>SW1 Core switch : Mikrotik CRS326-24G-2S+</h4><div class="details admonition tip open">
        <div class="details-summary admonition-title">
            <i class="icon fas fa-lightbulb fa-fw"></i>BootMode<i class="details-icon fas fa-angle-right fa-fw"></i>
        </div>
        <div class="details-content">
            <div class="admonition-content">It can be booted as a switch or a router but the hardware is definitely selected for switching so I boot SWOS from Mikrotik.</div>
        </div>
    </div>
<figure><img src="/images/homelab-2024/mikrotik-crs326-24g-2s.png"
         alt="Mikrotik crs326-24g-2s" width="25%" height="25%"/>
</figure>

<h4 id="other-switches" class="headerLink">
    <a href="#other-switches" class="header-mark"></a>Other switches</h4><h5 id="sw2-unifi-flex-mini" class="headerLink">
    <a href="#sw2-unifi-flex-mini" class="header-mark"></a>SW2 Unifi Flex Mini</h5><figure><img src="/images/homelab-2024/unifi-flex-mini.png"
         alt="Unifi Flex Mini" width="25%" height="25%"/>
</figure>

<p>This little gut is <strong>Amazing</strong>, for 26€ you get a 5 ports Gigabit swith that have VLANs, what else ?</p>
<div class="details admonition question open">
        <div class="details-summary admonition-title">
            <i class="icon fas fa-question-circle fa-fw"></i>But not everything<i class="details-icon fas fa-angle-right fa-fw"></i>
        </div>
        <div class="details-content">
            <div class="admonition-content">Just be aware that the flex mini won&rsquo;t do LACP, SNMP and SSH.</div>
        </div>
    </div>
<h5 id="sw3-unifi-sw8" class="headerLink">
    <a href="#sw3-unifi-sw8" class="header-mark"></a>SW3 Unifi SW8</h5><figure><img src="/images/homelab-2024/unifi-sw8.png"
         alt="Unifi SW8" width="25%" height="25%"/>
</figure>

<p>Last switch that I use close to the TV, it connects to WIFI AP and some media equipments to the rest of the house. This one is fully functional but more expensive&hellip;</p>
<h4 id="unifi-ap-pro" class="headerLink">
    <a href="#unifi-ap-pro" class="header-mark"></a>Unifi AP Pro</h4><figure><img src="/images/homelab-2024/uap.png"
         alt="Unifi UAP Pro" width="25%" height="25%"/>
</figure>

<p>Only one Access Point with 3 SSID, one for the LAN, one for the GUESTS clients and the last for IOT devices. I live in a flat so only 1 is more than enough !</p>
<h3 id="vlans" class="headerLink">
    <a href="#vlans" class="header-mark"></a>VLANS</h3><p>The network is segmented with VLANs:</p>
<ul>
<li>LAN : There is everyting in here, this is the <strong>main</strong> one.</li>
<li>IOT : The <strong>shit</strong> vlan, smart tv, smart light bulbs, things that I do not trust !</li>
<li>GUEST : This one is for the GUEST Wifi SSID, it gives only internet.</li>
<li>STORAGE : PVE and PBS are connected in this one for the backups.</li>
<li>ADMIN : Switches, Access point, PBS &amp; PVE Webui / SSH</li>
<li>CLUSTER: This one is for corosync, the Proxmox cluster communication protocol</li>
</ul>
<h1 id="base-services" class="headerLink">
    <a href="#base-services" class="header-mark"></a>Base services</h1><div class="details admonition tip open">
        <div class="details-summary admonition-title">
            <i class="icon fas fa-lightbulb fa-fw"></i>...<i class="details-icon fas fa-angle-right fa-fw"></i>
        </div>
        <div class="details-content">
            <div class="admonition-content">Thoses services are Virtual Machines or LXC Containers but I do not want them to be on kubernetes.</div>
        </div>
    </div>
<h2 id="dhcp-cluster" class="headerLink">
    <a href="#dhcp-cluster" class="header-mark"></a>DHCP Cluster</h2><p><figure><img src="/images/homelab-2024/kea-dhcp-logo.png"
         alt="Kea Logo" width="25%" height="25%"/>
</figure>

I am in the process of migrating to <a href="https://www.isc.org/kea/" target="_blank" rel="noopener noreferrer">Kea DHCP</a> , my first solution was ISC-DHCP in cluster with two LXCs, it was working great but I am moving to KEA because the future is now&hellip; The KEA cluster is working but the final goal is to have the Kea Agent connected to the Stork management UI. I already have the agent working but I miss the Stork part&hellip; Part of the roadmap !</p>
<h2 id="my-overkill-dns-architecture" class="headerLink">
    <a href="#my-overkill-dns-architecture" class="header-mark"></a>My overkill DNS Architecture&hellip;</h2><p>This one could be an article for itself. I use two <a href="https://pi-hole.net/" target="_blank" rel="noopener noreferrer">PiHoles</a> for DNS filtering / blacklisting, two <a href="https://doc.powerdns.com/recursor/" target="_blank" rel="noopener noreferrer">PowerDNS Recursors</a> and two <a href="https://doc.powerdns.com/authoritative/" target="_blank" rel="noopener noreferrer">PowerDNS Authoritative Servers</a> connected to a master/slave MariaDB database !</p>
<p><figure class="schema-dark"><a href="/images/homelab-2024/home-dns-infra-dark-background.png"><img src="/images/homelab-2024/home-dns-infra-dark-background.png"
         alt="DNS Infrastructure Schema" width="100%" height="100%"/></a>
</figure>

<figure class="schema-light"><a href="/images/homelab-2024/home-dns-infra-light-background.png"><img src="/images/homelab-2024/home-dns-infra-light-background.png"
         alt="DNS Infrastructure Schema" width="100%" height="100%"/></a>
</figure>

<figure class="schema-black"><a href="/images/homelab-2024/home-dns-infra-black-background.png"><img src="/images/homelab-2024/home-dns-infra-black-background.png"
         alt="DNS Infrastructure Schema" width="100%" height="100%"/></a>
</figure>
</p>
<p>My DHCP cluster pushes the two piHoles to my clients, servers are using the recursors directly. I have 2 000 000+ domains on blacklist, with the time I&rsquo;ve got to the point that a good amount of crap is blocked and I&rsquo;m not bothered anymore with false positives&hellip;
The PowerDNS solution is very robust, the cluster keeps my DNS service running even if one of the two vm is down for maintenance. I can route my queries where I want, this is really important for my work, and I can gather some metrics for my monitoring ! I also installed <a href="https://github.com/PowerDNS-Admin/PowerDNS-Admin" target="_blank" rel="noopener noreferrer">PowerDNS Admin</a> webui, nice way to manage my records, I quite like it to be honnest.</p>
<h2 id="unifi-controller" class="headerLink">
    <a href="#unifi-controller" class="header-mark"></a>Unifi controller</h2><figure><a href="/images/homelab-2024/unifi-controller.png"><img src="/images/homelab-2024/unifi-controller.png"
         alt="Unifi Controller" width="100%" height="100%"/></a>
</figure>

<p>Used to manage Unifi devices, push updates, checks connected clients&hellip;</p>
<h2 id="git" class="headerLink">
    <a href="#git" class="header-mark"></a>Git</h2><figure><a href="/images/homelab-2024/git-webui.png"><img src="/images/homelab-2024/git-webui.png"
         alt="Gitea Webui" width="100%" height="100%"/></a>
</figure>

<p>This is a Critical One, I use it to store :</p>
<ul>
<li>This website source</li>
<li>GitOps for all the kubernetes Yaml / Kustomize manifests</li>
<li>Ansible Playbooks</li>
<li>Sometimes a project I need to fork to make a PR</li>
</ul>
<p>It is a Gitea instance for now and it will be switched to <a href="https://forgejo.org/" target="_blank" rel="noopener noreferrer">Forgejo</a> !</p>
<h2 id="registry" class="headerLink">
    <a href="#registry" class="header-mark"></a>Registry</h2><figure><a href="/images/homelab-2024/harbor-webui.png"><img src="/images/homelab-2024/harbor-webui.png"
         alt="Harbor Webui" width="100%" height="100%"/></a>
</figure>

<p>I use <a href="https://github.com/goharbor/harbor" target="_blank" rel="noopener noreferrer">Harbor</a> to store container images. CVE Scanning is enabled and we can see that the image for that blog have warning&hellip; I&rsquo;ll take care of that !</p>
<h2 id="docker-cluster-for-tests" class="headerLink">
    <a href="#docker-cluster-for-tests" class="header-mark"></a>Docker cluster for tests</h2><p>I have a 3 nodes <a href="https://docs.docker.com/engine/swarm/" target="_blank" rel="noopener noreferrer">Docker Swarm</a> cluster running with Portainer installed.</p>
<figure><a href="/images/homelab-2024/portainer-webui.png"><img src="/images/homelab-2024/portainer-webui.png"
         alt="Portainer Webui" width="100%" height="100%"/></a>
</figure>

<p>This is used as a testing environment for me, at work we still use Swarm a lot and if I want to quickly test something it is easier on this. There is one manager ( No HA needed ) and two workers.</p>
<h2 id="minecraft-server" class="headerLink">
    <a href="#minecraft-server" class="header-mark"></a>Minecraft server</h2><figure><img src="/images/homelab-2024/minecraft-logo.png"
         alt="Minecraft Logo" width="50%" height="50%"/>
</figure>

<p>I still have an Old Minecraft server that I used to play on, still here because I do not want to destroy the map on this one. I do not know anymore how this thing is working but I remember that it had a management interface in CLI to launch command directly on the server&hellip;</p>
<h2 id="ca" class="headerLink">
    <a href="#ca" class="header-mark"></a>CA</h2><p>This one is a little special, I needed a <strong>PKI</strong> to manage my certs / CA, and at work we use our PfSense for that&hellip; I found this dead simple and great so I installed a PfSense VM, stripped every service I can and use it as a PKI with a WebUI&hellip; For example, ldap server certs, openvpn certs, &hellip; It use less than 256M of RAM&hellip;</p>
<h2 id="honney0gain-worker" class="headerLink">
    <a href="#honney0gain-worker" class="header-mark"></a>Honney0gain worker</h2><p>I have a honneygain worker, it basically use your internet connection to make money, they give you 0.0000000001% of what they earn and they possibly doing shaddy things of my back&hellip; So this is the perfect exemple for the <strong>IOT VLAN</strong>&hellip; I will throw this shit away but they promised me 20 bucks so I have to reach the goal before&hellip;</p>
<div class="details admonition warning open">
        <div class="details-summary admonition-title">
            <i class="icon fas fa-exclamation-triangle fa-fw"></i>Do not do this<i class="details-icon fas fa-angle-right fa-fw"></i>
        </div>
        <div class="details-content">
            <div class="admonition-content">For real, I tried but this is really not something I would recommand, now if you want it anyway, lock down this in his own network and just give it internet acces and nothing else !</div>
        </div>
    </div>
<p><strong>And that is the End of the Base / Physical part, let&rsquo;s talk about the kubernetes cluster now !</strong></p>
]]></description>
</item></channel>
</rss>
