<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>GitOps on JJGadgets</title><link>https://6f8f7e98.jjgadgets-tech.pages.dev/categories/gitops/</link><description>Recent content in GitOps on JJGadgets</description><generator>Hugo -- gohugo.io</generator><language>en</language><copyright>JJGadgets</copyright><lastBuildDate>Wed, 03 May 2023 00:00:00 +0000</lastBuildDate><atom:link href="https://6f8f7e98.jjgadgets-tech.pages.dev/categories/gitops/index.xml" rel="self" type="application/rss+xml"/><item><title>Flux Repo Structure</title><link>https://6f8f7e98.jjgadgets-tech.pages.dev/2024/flux-repo-structure/</link><pubDate>Wed, 03 May 2023 00:00:00 +0000</pubDate><guid>https://6f8f7e98.jjgadgets-tech.pages.dev/2024/flux-repo-structure/</guid><description>&lt;h1 id="introduction">Introduction&lt;/h1>
&lt;p>In this post, I go over the repository structure that I use for my Kubernetes homelab, powered with GitOps by FluxCD.&lt;/p>
&lt;p>Remember that &lt;em>there is no &amp;ldquo;right way&amp;rdquo; or &amp;ldquo;one size fits all&amp;rdquo; to repo structuring&lt;/em>, define and be clear on your goals before you structure your repo.&lt;/p>
&lt;p>&lt;strong>NOTE:&lt;/strong> From here on out, all &lt;em>files&lt;/em> will be in bold and is prefixed by &lt;strong>./&lt;/strong> while all &lt;em>folders&lt;/em> will be in bold and suffixed by &lt;strong>/&lt;/strong> e.g. &lt;strong>folder/&lt;/strong> and &lt;strong>./file.yaml&lt;/strong>&lt;/p>
&lt;h1 id="goals">Goals&lt;/h1>
&lt;ul>
&lt;li>
&lt;p>Define desired configuration of apps and services deployed in Kubernetes cluster.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Version control all configuration changes with Git.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Automate all the things! (as much as possible)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Establish high security baseline of both Kubernetes cluster&amp;rsquo;s components, and GitOps components.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Allow for basic multi-cluster environments (production and non-production) while using monorepo&lt;/p>
&lt;ul>
&lt;li>Cluster-specific configurations using variable substitution in deploy manifests, and cluster-specific secrets.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Allow for opting-in which apps/services/components to deploy per cluster, to optimize resource allocation (CPU, memory, storage, network etc).&lt;/p>
&lt;ul>
&lt;li>I &lt;em>don&amp;rsquo;t really need&lt;/em> a Minecraft Java server on both prod and staging if I don&amp;rsquo;t change any of its configuration, do I?&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Minimize human errors or forgetfulness by keeping duplicated code to an absolute minimum.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h1 id="repo-structure-kube">Repo Structure (&lt;strong>kube/&lt;/strong>)&lt;/h1>
&lt;ul>
&lt;li>
&lt;h2 id="bootstrap">&lt;strong>bootstrap/&lt;/strong>&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>flux/&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>./kustomization.yaml&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Installs Flux from the kustomization.yaml located in ./manifests/install of fluxcd/flux2 remote GitHub repo.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;h2 id="clusters">&lt;strong>clusters/&lt;/strong>&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>cluster-name/&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Cluster specific folder&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Any name is fine, e.g. &lt;strong>prod/&lt;/strong> and &lt;strong>dev/&lt;/strong>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>distro/&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Cluster distribution&amp;rsquo;s configuration&lt;/p>
&lt;/li>
&lt;li>
&lt;p>e.g. Talos via talhelper (&lt;strong>talos/&lt;/strong>)&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>config/&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>All Flux manifests for cluster-specific configuration state goes here&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>./flux-install.yaml&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Flux SourceRepository pointed to Flux&amp;rsquo;s manifests OCI repo, scoped to version branch&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Fluxtomization to deploy and control Flux&amp;rsquo;s components (takes over control of Flux components from Flux bootstrap)&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>./flux-repo.yaml&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Flux SourceRepository pointed to user&amp;rsquo;s repo (e.g. JJGadgets/Biohazard, onedr0p/home-ops, 0dragosh/homelab etc), use SSH key to clone (and optionally push if configured)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&amp;ldquo;Master&amp;rdquo; Fluxtomization to deploy and control cluster-specific configuration (path points to cluster folder) and all other deployments (via cluster folder&amp;rsquo;s ./kustomization.yaml)&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Includes configuration and patches for variable substitution ( &lt;code>${VARIABLE}&lt;/code> ) and SOPS secret decryption of all other deployments&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Includes patches for DRYing configs&lt;/p>
&lt;ul>
&lt;li>For the truly lazy, patching a Fluxtomization with patches for other resources controlled by said Fluxtomization (e.g. HelmRelease) is possible&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>./kustomization.yaml&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Native Kubernetes Kustomization to control what resources are deployed, either by Fluxtomization or &lt;code>kubectl apply -k&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Used here for opt-in deployment of all cluster-specific configuration manifests in cluster folder.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Used here for opt-in deployment of which apps folders to deploy to cluster (from &lt;strong>kube/deploy/&lt;/strong>).&lt;/p>
&lt;ul>
&lt;li>
&lt;p>References the apps folders to deploy like so: &lt;code>../../../deploy/apps/jellyfin&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Each app folder will then have its own &lt;strong>kustomization.yaml&lt;/strong>, which opts-in deployment of the namespaces needed as well as the app&amp;rsquo;s Fluxtomization (&lt;strong>ks.yaml&lt;/strong>).&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Indirectly the &amp;ldquo;master&amp;rdquo; Fluxtomization &lt;em>also controls the namespaces deployed&lt;/em> (since there&amp;rsquo;s no Fluxtomizations in between the chain of &lt;strong>kustomization.yaml&lt;/strong>s).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>This allows the app&amp;rsquo;s Fluxtomization to add &amp;ldquo;master&amp;rdquo; Fluxtomization as dependency (via dependsOn).&lt;/p>
&lt;ul>
&lt;li>Ensures that namespaces are always deployed before the resources are deployed within the namespaces.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;em>NOTE:&lt;/em> (about namespacing)&lt;/p>
&lt;ul>
&lt;li>
&lt;p>I choose to group apps in their own namespace, unless an app requires either multiple containers (e.g. server and web client, or microservices architecture), or 2 different apps must be in the same namespace to share Kubernetes resources or other reasons.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>My structure is a janky ghetto way to implement &amp;ldquo;multi-cluster&amp;rdquo; with a very specific purpose of having production and non-production (dev/test/staging/UAT, whichever is appropriate) clusters, where the differences in the clusters mostly come down to cluster-specific variables/secrets and control of which apps are deployed, allowing the non-prod cluster(s) to be scaled smaller than the prod cluster (e.g. I &lt;em>don&amp;rsquo;t really need&lt;/em> Jellyfin on both prod and staging if I don&amp;rsquo;t change any of its configuration)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Other folks like onedr0p, bjw-s, 0dragosh etc will have the &amp;ldquo;master&amp;rdquo; Fluxtomization deploy a separate &amp;ldquo;apps&amp;rdquo; Fluxtomization that points to &lt;strong>kubernetes/apps&lt;/strong> which has &lt;strong>kubernetes/apps/namespace/kustomization.yaml&lt;/strong> control both namespace and app Fluxtomizations (&lt;strong>kubernetes/apps/namespace/app/ks.yaml&lt;/strong>) to deploy, &lt;em>which isn&amp;rsquo;t a wrong way to structure for their needs&lt;/em>, however this means that there is no easy way to control which cluster should deploy which apps which I desire&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>./secrets.yaml&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Cluster-specific native Kubernetes secrets, encrypted-at-rest by SOPS (Flux supports decrypting SOPS-encrypted files).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Can be used via variable substitution at deploy-time.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>external-secrets is preferred over storing secrets in here to easily sync and/or consume namespace-specific secrets.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>One hacky way to have cluster-specific yet namespace-specific native Kubernetes secrets (you can&amp;rsquo;t reference a secret stored in &lt;code>flux-system&lt;/code> namespace from e.g. &lt;code>minecraft&lt;/code> namespace) is to variable substitute the secret data into a &lt;code>kind: Secret&lt;/code> manifest in &lt;strong>kube/deploy/&lt;/strong>.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>./vars.yaml&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Cluster-specific variables.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Can be used via variable substitution at deploy-time.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;h2 id="deploy">&lt;strong>deploy/&lt;/strong>&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>Apps, services and components to be deployed to clusters.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Contains baseline common manifests that are written to work across all clusters.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Cluster-specific configuration is achieved via variable substitution and optionally patches.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>core/&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Backend(-related) components that are &amp;ldquo;core&amp;rdquo; to Kubernetes clusters for them to operate, such as CNI, storage solution, Ingress Controller, and more.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>apps/&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>User-facing apps and services.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>app-name/&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Replace &lt;strong>app-name/&lt;/strong> with app name, such as &lt;strong>minecraft/&lt;/strong>, &lt;strong>authentik/&lt;/strong>, or &lt;strong>immich/&lt;/strong>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>./ns.yaml&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Namespace definition for app, or group of apps.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>./ks.yaml&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>All app-specific Fluxtomizations that deploy and control the app&amp;rsquo;s resources.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Points to either &lt;strong>app/&lt;/strong> and/or &lt;strong>component-name/&lt;/strong> folders which are explained below.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Use dependsOn to ensure that dependencies (such as storage solution and Ingress Controller) are deployed and configured before app&amp;rsquo;s resources are deployed and configured.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>./repo.yaml&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Only needed if deployment method relies on Flux SourceRepository, such as HelmRepository or OCIRepository.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>./kustomization.yaml&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Native Kubernetes Kustomization to control what resources are deployed, either by Fluxtomization or &lt;code>kubectl apply -k&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Used here for opting-in all YAML files but not folders, since folders are controlled by app-specific Fluxtomization which itself is controlled by &amp;ldquo;master&amp;rdquo; Fluxtomization via this and cluster&amp;rsquo;s &lt;strong>kustomization.yaml&lt;/strong>.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>app/&lt;/strong> OR &lt;strong>component-name/&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>All manifests used to deploy Kubernetes resources needed for app are placed here.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Usually if both &lt;strong>app/&lt;/strong> and another folder such as &lt;strong>config/&lt;/strong> or &lt;strong>certs/&lt;/strong> are present, &lt;strong>app/&lt;/strong> is used to deploy components which are a dependency for the other folders&amp;rsquo; resources to be deployed.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>./netpol.yaml&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>
&lt;p>CiliumNetworkPolicy or Kubernetes native Network Policy&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&amp;ldquo;&lt;em>Principle of Least Privilege&lt;/em>&amp;rdquo; and &amp;ldquo;&lt;em>Default Deny&lt;/em>&amp;rdquo; practices are followed, only allow necessary connections&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Commonly allowed include Ingress Controller, intra-namespace traffic, communication with other apps&amp;rsquo; Kubernetes services &lt;em>as needed&lt;/em>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Not all Kubernetes components need to be network accessible by every application, such as APIServer, storage solution pods/services, or Flux. Assign only if such traffic is necessary for app to function.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>./hr.yaml&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>HelmRelease deployment method.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul></description></item></channel></rss>