diff --git a/.d2/flux-seq.d2 b/.d2/flux-seq.d2 new file mode 100644 index 0000000..d0d63b8 --- /dev/null +++ b/.d2/flux-seq.d2 @@ -0,0 +1,18 @@ +flux deployment: { + shape: sequence_diagram + + laptop: My Laptop + gitlab: Git Repo + k8s: Kubernetes + flux: Flux Kustomize Controller + fluxhelm: Flux Helm Controller + + laptop -> gitlab: git push + gitlab <- flux: git pull + k8s <- flux: apply HelmRelease + + k8s."HelmRelease" + fluxhelm -> k8s: poll for HelmRelease resources + fluxhelm -> k8s: `helm upgrade` + k8s."NGINX Deployment, etc..." +} \ No newline at end of file diff --git a/.d2/hugo-gitlab-pages.d2 b/.d2/hugo-gitlab-pages.d2 new file mode 100644 index 0000000..c35dfdb --- /dev/null +++ b/.d2/hugo-gitlab-pages.d2 @@ -0,0 +1,38 @@ +title: |md + # deploy w/ GitHub pages & actions +| { + shape: text + near: top-center +} + +my laptop { + icon: https://icons.terrastruct.com/tech%2Flaptop.svg + near: center-left +} + +my laptop -> GitHub.repo: git push + +GitHub { + icon: https://icons.terrastruct.com/social%2F039-github.svg + + repo <- runner.build job: pull + + runner { + build job + deploy job + } + + events api <- runner: long poll + + runner.build job -> artifact storage.pages artifact: uses + runner.deploy job -> artifact storage.pages artifact: uses + + runner.deploy job -> pages.Hugo site: deploys + artifact storage { + pages artifact + } + + pages { + Hugo site + } +} diff --git a/content/posts/hugo-on-k8s-nginx.md b/content/posts/hugo-on-k8s-nginx.md index 935392d..79e17a2 100644 --- a/content/posts/hugo-on-k8s-nginx.md +++ b/content/posts/hugo-on-k8s-nginx.md @@ -3,29 +3,34 @@ title = 'Hugo on Kubernetes & NGINX' date = 2024-02-28T15:35:46-08:00 draft = true series = ['wtf'] -categories = ['Tech'] -tags = ['meta', 'k8s'] +categories = ['Tutorial'] +tags = ['meta', 'k8s', 'flux', 'hugo'] +toc = true +++ -i decided to make a website. a static one. this one. with [Hugo][hugo]. this -is basically as a vanity project so i have some stuff to host in a -[Kubernetes][k8s] cluster i'm running. the k8s cluster is also as a vanity -project. +i decided to make a website. a static one. this one. with [Hugo][hugo]. the +main reason i have for this is as a vanity project so i have some stuff to +host in a [Kubernetes][k8s] cluster i'm running. the k8s cluster is also as +a vanity project. -because i don't like software, i wanted a way to deploy my site that -doesn't involve much of it. this post is about that. +because i don't like software, i wanted a way to deploy my site that doesn't +involve much of it. this post is about that. ## Getting Started -i built my site by following the straight-forward _[Getting Started][hugo-started]_ -guide in the Hugo documentation. +i built my site by following the straight-forward +_[Getting Started][hugo-started]_ guide in the Hugo documentation. -i did `hugo new site estradiol.cloud`. and then `cd estradiol.cloud; git init`. and -then i picked a ridiculous theme ["inspired by terminal ricing aesthetics"][risotto], -installing it like `git submodule add https://github.com/joeroe/risotto.git themes/risotto; echo "theme = 'risotto'" >> hugo.toml`. i appreciate the culinary naming choice. +i did `hugo new site estradiol.cloud`. and then `cd estradiol.cloud; git init`. +and then i picked a ridiculous theme +["inspired by terminal ricing aesthetics"][risotto], installing it like `git +submodule add https://github.com/joeroe/risotto.git themes/risotto; echo "theme += 'risotto'" >> hugo.toml`.[^1] -at this point, my website is basically finished (i also changed the title in `hugo.toml`). -i probably won't be putting anything on it, so there's no point fiddling with other +[^1]: i appreciate the culinary theme. + +at this point, my website is basically finished (i also changed the title in +`hugo.toml`). i probably won't be putting anything on it, so there's no point fiddling with other details. about deployment, the guide's _[Basic Usage][hugo-deploy]_ page has this to offer: @@ -42,12 +47,14 @@ importantly, you can't make a post about deploying this way. _everyone_ deploys this way. if _i_ deploy this way, this site will have no content. it also involves some system somewhere that can run Hugo to build the site and push -it to some remote system where my cluster can reach the `public/` output. i definitely -already need Hugo installed on my workstation if i'm going to post anything here -(unlikely), so now i'm running Hugo in two places. there's surely going to be -other complex nonsense like webhooks involved. +it to some remote system where my cluster can reach the compiled site. i definitely +already need Hugo installed on my workstation if i'm going to post anything.[^2] +so now i'm running Hugo in two places. there's surely going to be other complex +nonsense like webhooks involved. - +[^2]: unlikely. + +![diagram: deploy w/ GitHub pages & actions](images/hugo-github-pages.svg) ---- @@ -61,37 +68,109 @@ _actual content_ into version control? couldn't be me. ## Getting Static -what if instead i pushed my site to a git repository exactly as i intend to serve it? -then i could shell into my web server, pull the site, and _nifty-galifty!_ isn't this -the way it has [always been done][worm]? +suppose i instead checked my content into git exactly as i intend to serve it? +then i could shell into my server box, pull the site, and _nifty-galifty!_ isn't +this the way it has [always been done][worm-love]? -one problem is that i don't have a web server, i have a _container orchestration -system_. there are several upsides to this (few of which are relevant for my project) -but it also means that _somehow_ my content needs to end up in a container, and i -don't want that container to need to retain state across restarts or replicas. -i _could_ run a little pipeline that builds a container wrapping my static site, +my problem is that i don't have a server box. i have a _container orchestration +system_. there are several upsides to this[^3] but it means that _somehow_ my +generated content needs to end up in a container. because [Pods][k8s-pods] are +ephemeral and i'd like to run my site with horizontal scalability[^4], i don't +want my container to need to retain runtime state across restarts or replicas. + +[^3]: few of which could be considered relevant for my project. +[^4]: i absolutely will not need this + +i _could_ run a little pipeline that builds a container wrapping my content and pushes it to a registry somewhere my deployments can pull it. all ready to go. -but now i've got _software_ again: build stages and webhooks and to make matters +but now i've got _software_ again: build stages and webhooks and, to make matters worse, now i'm hosting and versioning container images. - +![diagram: deploy w/ container build](/images/hugo-container-build.svg) -i don't want any of this. +i don't want any of this. i just want to put some HTML and static assets behind a +web server. --- -instead, i'd like to deploy a popular stock container from a public registry and -deliver my content to it continuously. +instead, i'd like to deploy a popular container image from a public registry +and deliver my content to it continuously. -as a minimal version of this, i could do: +a minimal setup to achieve this might look like: -```yaml + - a `Pod` with: + - an `nginx` container to serve the content; + - a `git-pull` sidecar that loops, pulling the git content; + - an `initContainer` to do the initial checkout; + - an `emptyDir` volume to share between the containers. + - a `ConfigMap` to store the nginx config. + + +![diagram: minimal pod/configmap setup](/images/hugo-minimal-pod-setup.svg) + +i use `git sparse-checkout` to avoid pulling repository contents i don't want +to serve out: + +```bash +# git-clone command +git clone https://code.estradiol.cloud/tamsin/estradiol.cloud.git --no-checkout --branch trunk /tmp/www; +cd /tmp/www; +git sparse-checkout init --cone; +git sparse-checkout set public; +git checkout; +shopt -s dotglob +mv /tmp/www/* /www +``` + +script up my `git pull` loop: + +```bash +# git-pull command +while true; do + cd /www && git -c safe.directory=/www pull origin trunk + sleep 60 +done +``` + +and configure `nginx` to use `public/` as root: + +```txt +# ConfigMap; data: default.conf +server { + listen 80; + location / { + root /www/public; + index index.html; + } +} +``` + +the rest of this is pretty much boilerplate: + +{{< code-details summary="`kubectl apply -f estradiol-cloud.yaml`" lang="yaml" details=` +# estradiol-cloud.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/instance: estradiol-cloud + app.kubernetes.io/name: nginx + name: nginx-server-block +data: + default.conf: |- + server { + listen 80; + location / { + root /www/public; + index index.html; + } + } +--- apiVersion: v1 kind: Pod metadata: name: nginx - namespace: ec labels: app.kubernetes.io/instance: estradiol-cloud app.kubernetes.io/name: nginx @@ -102,8 +181,10 @@ spec: ports: - containerPort: 80 volumeMounts: - - mountPath: /app - name: staticsite + - mountPath: /www + name: www + - mountPath: /etc/nginx/conf.d + name: nginx-server-block - name: git-pull image: bitnami/git command: @@ -111,142 +192,66 @@ spec: - -ec - | while true; do - cd /app && git -c safe.directory=/app pull origin trunk + cd /www && git -c safe.directory=/www pull origin trunk sleep 60 done volumeMounts: - - mountPath: /app - name: staticsite + - mountPath: /www + name: www initContainers: - name: git-clone image: bitnami/git command: - /bin/bash - - -ec + - -c - | - git clone https://code.estradiol.cloud/tamsin/estradiol.cloud.git --no-checkout --branch trunk /tmp/app - cd /tmp/app && git sparse-checkout init --cone - git sparse-checkout set public && git checkout - rm -rf /app && mv /tmp/app /app + shopt -s dotglob + git clone https://code.estradiol.cloud/tamsin/estradiol.cloud.git --no-checkout --branch trunk /tmp/www; + cd /tmp/www; +p git sparse-checkout init --cone; + git sparse-checkout set public; + git checkout; + mv /tmp/www/* /www volumeMounts: - - mountPath: /app - name: staticsite + - mountPath: /www + name: www volumes: - - emptyDir: {} - name: staticsite + - name: www + emptyDir: {} + - name: nginx-server-block + configMap: + name: nginx-server-block +` >}} --- -apiVersion: v1 -kind: ConfigMap -metadata: - labels: - app.kubernetes.io/instance: estradiol-cloud - app.kubernetes.io/name: nginx - name: nginx-server-block - namespace: ec -data: - server-block.conf: |- - server { - listen 8080; - root /app/public; - index index.html; - } -``` +my Hugo workflow now looks like: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 1. make changes to source; + 1. run `hugo --gc --minify`;[^7] + 1. commit & push. + +the only active process from this point is my little control loop running `git pull`. + +[^7]: i added `disableHTML = true` to `[minify]` configuration in `hugo.toml` +to keep HTML diffs readable. + +## Getting Web + +my Pod is running. everything is great. if i want to browse to my website i +just need to setup a [port-forward][k8s-port] + +TK: YAML counts as software. + +conveniently, [Bitnami][bitnami] maintains a [Helm][helm] Chart that + +![diagram: helm setup](/images/hugo-helm-setup.svg) ## Getting Flux'd -```yaml -apiVersion: source.toolkit.fluxcd.io/v1beta2 -kind: HelmRepository -metadata: - name: bitnami - namespace: default -spec: - url: https://charts.bitnami.com/bitnami -``` +by this point i'm pretty `git push`-pilled and i'm thinking i don't much like +having this `helm` client software installed on my laptop. + ```yaml apiVersion: source.toolkit.fluxcd.io/v1beta2 @@ -258,12 +263,92 @@ spec: url: https://charts.bitnami.com/bitnami ``` +{{< code-details summary="`release.yaml`" lang="yaml" details=` +apiVersion: helm.toolkit.fluxcd.io/v2beta1 +kind: HelmRelease +metadata: + name: web + namespace: estradiol-cloud +spec: + interval: 5m + chart: + spec: + chart: nginx + version: '15.12.2' + sourceRef: + kind: HelmRepository + name: bitnami + namespace: default + interval: 1m + values: + cloneStaticSiteFromGit: + enabled: true + repository: "https://code.estradiol.cloud/tamsin/estradiol.cloud.git" + branch: trunk + gitClone: + command: + - /bin/bash + - -ec + - | + [[ -f "/opt/bitnami/scripts/git/entrypoint.sh" ]] && source "/opt/bitnami/scripts/git/entrypoint.sh" + git clone {{ .Values.cloneStaticSiteFromGit.repository }} --no-checkout --branch {{ .Values.cloneStaticSiteFromGit.branch }} /tmp/app + [[ "$?" -eq 0 ]] && cd /tmp/app && git sparse-checkout init --cone && git sparse-checkout set public && git checkout && shopt -s dotglob && rm -rf /app/* && mv /tmp/app/* /app/ + ingress: + enabled: true + hostname: estradiol.cloud + ingressClassName: nginx + tls: true + annotations: { + cert-manager.io/cluster-issuer: letsencrypt-prod + } + serverBlock: |- + server { + listen 8080; + root /app/public; + index index.html; + } + service: + type: ClusterIP +`>}} +![Scenario 1: Across columns](images/flux-seq.svg) + +## A Note About Software + +at this point i'm forced to admit there's still a lot of software involved in this. +setting aside the stuff that provisions and scales my cluster nodes, i have: + + - `nginx` (running from a stock image); + - `git` & `bash` (running from a stock image); + - a remote git server (i'm running `gitea`[^8], but github dot com is fine here); + - Kubernetes (oops!); + - `fluxcd`; + - especially `kustomize-controller` and `helm-controller`; + - `nginx-ingress` controller; + - the `bitnami/nginx` Helm chart; + +[^8]: because i'm running `gitea` in my cluster and i want to avoid a circular +dependency for my `flux` source repository, i also depend on GitLab dot com. + +i get to maintain my two `bash` scripts for `git-clone` and `git-pull`, my +NGINX config, and a couple of blobs of YAML. + +at least there are no webhooks. + +--- + +_fin_ + + +[bitnami]: https://bitnami.com/ +[helm]: https://helm.sh [hugo]: https://gohugo.io [hugo-deploy]: https://gohugo.io/getting-started/usage/#deploy-your-site [hugo-started]: https://gohugo.io/getting-started [k8s]: https://kubernetes.io [k8s-init]: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ +[k8s-pods]: https://kubernetes.io/docs/concepts/workloads/pods/ +[k8s-port]: https://kubernetes.io/docs/tasks/access-application-cluster/port-forward-access-application-cluster/ [k8s-pv]: https://kubernetes.io/docs/concepts/storage/persistent-volumes/ [risotto]: https://github.com/joeroe/risotto -[worm]: https://www.mikecurato.com/worm-loves-worm +[worm-love]: https://www.mikecurato.com/worm-loves-worm diff --git a/content/posts/hugo-on-k8s-nginx/images/flux-seq.svg b/content/posts/hugo-on-k8s-nginx/images/flux-seq.svg new file mode 100644 index 0000000..f8ca08a --- /dev/null +++ b/content/posts/hugo-on-k8s-nginx/images/flux-seq.svg @@ -0,0 +1,122 @@ + + + + + + + + +flux deploymentMy LaptopGit RepoKubernetesFlux Kustomize ControllerFlux Helm Controller git push git pull apply HelmRelease poll for HelmRelease resources `helm upgrade`HelmReleaseNGINX Deployment, etc... + + + + + + + + + + + + + + + diff --git a/content/posts/hugo-on-k8s-nginx/images/hugo-github-pages.svg b/content/posts/hugo-on-k8s-nginx/images/hugo-github-pages.svg new file mode 100644 index 0000000..f68d957 --- /dev/null +++ b/content/posts/hugo-on-k8s-nginx/images/hugo-github-pages.svg @@ -0,0 +1,875 @@ + + + + + + + + +

deploy w/ GitHub pages & actions

+
my laptopGitHubreporunnerevents apiartifact storagepagesbuild jobdeploy jobpages artifactHugo site git push pull long poll uses uses deploys + + + + + + + + + + + + + + + + + + + +
diff --git a/content/posts/hugo-on-k8s-nginx/index.md b/content/posts/hugo-on-k8s-nginx/index.md new file mode 100644 index 0000000..e50d502 --- /dev/null +++ b/content/posts/hugo-on-k8s-nginx/index.md @@ -0,0 +1,356 @@ ++++ +title = 'Hugo on Kubernetes & NGINX' +date = 2024-02-28T15:35:46-08:00 +draft = true +series = ['wtf'] +categories = ['Tutorial'] +tags = ['meta', 'k8s', 'flux', 'hugo'] +toc = true ++++ + +i decided to make a website. a static one. this one. with [Hugo][hugo]. the +main reason i have for this is as a vanity project so i have some stuff to +host in a [Kubernetes][k8s] cluster i'm running. the k8s cluster is also as +a vanity project. + +because i don't like software, i wanted a way to deploy my site that doesn't +involve much of it. this post is about that. + +## Getting Started + +i built my site by following the straight-forward +_[Getting Started][hugo-started]_ guide in the Hugo documentation. + +i did `hugo new site estradiol.cloud`. and then `cd estradiol.cloud; git init`. +and then i picked a ridiculous theme +["inspired by terminal ricing aesthetics"][risotto], installing it like `git +submodule add https://github.com/joeroe/risotto.git themes/risotto; echo "theme += 'risotto'" >> hugo.toml`.[^1] + +[^1]: i appreciate the culinary theme. + +at this point, my website is basically finished (i also changed the title in +`hugo.toml`). i probably won't be putting anything on it, so there's no point fiddling with other +details. + +about deployment, the guide's _[Basic Usage][hugo-deploy]_ page has this to offer: + +> Most of our users deploy their sites using a CI/CD workflow, where a push{{< sup "1" >}} +> to their GitHub or GitLab repository triggers a build and deployment. Popular +> providers include AWS Amplify, CloudCannon, Cloudflare Pages, GitHub Pages, +> GitLab Pages, and Netlify. +> +> 1. The Git repository contains the entire project directory, typically excluding the +> public directory because the site is built _after_ the push. + +importantly, you can't make a post about deploying this way. _everyone_ deploys +this way. if _i_ deploy this way, this site will have no content. + +it also involves some system somewhere that can run Hugo to build the site and push +it to some remote system where my cluster can reach the compiled site. i definitely +already need Hugo installed on my workstation if i'm going to post anything.[^2] +so now i'm running Hugo in two places. there's surely going to be other complex +nonsense like webhooks involved. + +[^2]: unlikely. + +![diagram: deploy w/ GitHub pages & actions](/images/hugo-github-pages.svg) + +---- + +and hang on. let's look at this again: + +> 1. The Git repository contains the entire project directory, typically excluding the +> public directory because the site is built _after_ the push. + +you're telling me i'm going to build a nice static site and not check the +_actual content_ into version control? couldn't be me. + +## Getting Static + +suppose i instead checked my content into git exactly as i intend to serve it? +then i could shell into my server box, pull the site, and _nifty-galifty!_ isn't +this the way it has [always been done][worm-love]? + + +my problem is that i don't have a server box. i have a _container orchestration +system_. there are several upsides to this[^3] but it means that _somehow_ my +generated content needs to end up in a container. because [Pods][k8s-pods] are +ephemeral and i'd like to run my site with horizontal scalability[^4], i don't +want my container to need to retain runtime state across restarts or replicas. + +[^3]: few of which could be considered relevant for my project. +[^4]: i absolutely will not need this + +i _could_ run a little pipeline that builds a container wrapping my content and +pushes it to a registry somewhere my deployments can pull it. all ready to go. +but now i've got _software_ again: build stages and webhooks and, to make matters +worse, now i'm hosting and versioning container images. + +![diagram: deploy w/ container build](/images/hugo-container-build.svg) + +i don't want any of this. i just want to put some HTML and static assets behind a +web server. + +--- + +instead, i'd like to deploy a popular container image from a public registry +and deliver my content to it continuously. + +a minimal setup to achieve this might look like: + + - a `Pod` with: + - an `nginx` container to serve the content; + - a `git-pull` sidecar that loops, pulling the git content; + - an `initContainer` to do the initial checkout; + - an `emptyDir` volume to share between the containers. + - a `ConfigMap` to store the nginx config. + + +![diagram: minimal pod/configmap setup](/images/hugo-minimal-pod-setup.svg) + +i use `git sparse-checkout` to avoid pulling repository contents i don't want +to serve out: + +```bash +# git-clone command +git clone https://code.estradiol.cloud/tamsin/estradiol.cloud.git --no-checkout --branch trunk /tmp/www; +cd /tmp/www; +git sparse-checkout init --cone; +git sparse-checkout set public; +git checkout; +shopt -s dotglob +mv /tmp/www/* /www +``` + +script up my `git pull` loop: + +```bash +# git-pull command +while true; do + cd /www && git -c safe.directory=/www pull origin trunk + sleep 60 +done +``` + +and configure `nginx` to use `public/` as root: + +```txt +# ConfigMap; data: default.conf +server { + listen 80; + location / { + root /www/public; + index index.html; + } +} +``` + +the rest of this is pretty much boilerplate: + +{{< code-details summary="`kubectl apply -f estradiol-cloud.yaml`" lang="yaml" details=` +# estradiol-cloud.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/instance: estradiol-cloud + app.kubernetes.io/name: nginx + name: nginx-server-block +data: + default.conf: |- + server { + listen 80; + location / { + root /www/public; + index index.html; + } + } +--- +apiVersion: v1 +kind: Pod +metadata: + name: nginx + labels: + app.kubernetes.io/instance: estradiol-cloud + app.kubernetes.io/name: nginx +spec: + containers: + - name: nginx + image: nginx:1.25.4 + ports: + - containerPort: 80 + volumeMounts: + - mountPath: /www + name: www + - mountPath: /etc/nginx/conf.d + name: nginx-server-block + - name: git-pull + image: bitnami/git + command: + - /bin/bash + - -ec + - | + while true; do + cd /www && git -c safe.directory=/www pull origin trunk + sleep 60 + done + volumeMounts: + - mountPath: /www + name: www + initContainers: + - name: git-clone + image: bitnami/git + command: + - /bin/bash + - -c + - | + shopt -s dotglob + git clone https://code.estradiol.cloud/tamsin/estradiol.cloud.git --no-checkout --branch trunk /tmp/www; + cd /tmp/www; +p git sparse-checkout init --cone; + git sparse-checkout set public; + git checkout; + mv /tmp/www/* /www + volumeMounts: + - mountPath: /www + name: www + volumes: + - name: www + emptyDir: {} + - name: nginx-server-block + configMap: + name: nginx-server-block +` >}} + +--- + +my Hugo workflow now looks like: + + 1. make changes to source; + 1. run `hugo --gc --minify`;[^7] + 1. commit & push. + +the only active process from this point is my little control loop running `git pull`. + +[^7]: i added `disableHTML = true` to `[minify]` configuration in `hugo.toml` +to keep HTML diffs readable. + +## Getting Web + +my Pod is running. everything is great. if i want to browse to my website i +just need to setup a [port-forward][k8s-port] + +TK: YAML counts as software. + +conveniently, [Bitnami][bitnami] maintains a [Helm][helm] Chart that + +![diagram: helm setup](/images/hugo-helm-setup.svg) + +## Getting Flux'd + +by this point i'm pretty `git push`-pilled and i'm thinking i don't much like +having this `helm` client software installed on my laptop. + + +```yaml +apiVersion: source.toolkit.fluxcd.io/v1beta2 +kind: HelmRepository +metadata: + name: bitnami + namespace: default +spec: + url: https://charts.bitnami.com/bitnami +``` + +{{< code-details summary="`release.yaml`" lang="yaml" details=` +apiVersion: helm.toolkit.fluxcd.io/v2beta1 +kind: HelmRelease +metadata: + name: web + namespace: estradiol-cloud +spec: + interval: 5m + chart: + spec: + chart: nginx + version: '15.12.2' + sourceRef: + kind: HelmRepository + name: bitnami + namespace: default + interval: 1m + values: + cloneStaticSiteFromGit: + enabled: true + repository: "https://code.estradiol.cloud/tamsin/estradiol.cloud.git" + branch: trunk + gitClone: + command: + - /bin/bash + - -ec + - | + [[ -f "/opt/bitnami/scripts/git/entrypoint.sh" ]] && source "/opt/bitnami/scripts/git/entrypoint.sh" + git clone {{ .Values.cloneStaticSiteFromGit.repository }} --no-checkout --branch {{ .Values.cloneStaticSiteFromGit.branch }} /tmp/app + [[ "$?" -eq 0 ]] && cd /tmp/app && git sparse-checkout init --cone && git sparse-checkout set public && git checkout && shopt -s dotglob && rm -rf /app/* && mv /tmp/app/* /app/ + ingress: + enabled: true + hostname: estradiol.cloud + ingressClassName: nginx + tls: true + annotations: { + cert-manager.io/cluster-issuer: letsencrypt-prod + } + serverBlock: |- + server { + listen 8080; + root /app/public; + index index.html; + } + service: + type: ClusterIP +`>}} + +{{ with .Resources.GetMatch "hugo-flux-seq.svg" }} + +{{ end }} + +## A Note About Software + +at this point i'm forced to admit there's still a lot of software involved in this. +setting aside the stuff that provisions and scales my cluster nodes, i have: + + - `nginx` (running from a stock image); + - `git` & `bash` (running from a stock image); + - a remote git server (i'm running `gitea`[^8], but github dot com is fine here); + - Kubernetes (oops!); + - `fluxcd`; + - especially `kustomize-controller` and `helm-controller`; + - `nginx-ingress` controller; + - the `bitnami/nginx` Helm chart; + +[^8]: because i'm running `gitea` in my cluster and i want to avoid a circular +dependency for my `flux` source repository, i also depend on GitLab dot com. + +i get to maintain my two `bash` scripts for `git-clone` and `git-pull`, my +NGINX config, and a couple of blobs of YAML. + +at least there are no webhooks. + +--- + +_fin_ + + +[bitnami]: https://bitnami.com/ +[helm]: https://helm.sh +[hugo]: https://gohugo.io +[hugo-deploy]: https://gohugo.io/getting-started/usage/#deploy-your-site +[hugo-started]: https://gohugo.io/getting-started +[k8s]: https://kubernetes.io +[k8s-init]: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ +[k8s-pods]: https://kubernetes.io/docs/concepts/workloads/pods/ +[k8s-port]: https://kubernetes.io/docs/tasks/access-application-cluster/port-forward-access-application-cluster/ +[k8s-pv]: https://kubernetes.io/docs/concepts/storage/persistent-volumes/ +[risotto]: https://github.com/joeroe/risotto +[worm-love]: https://www.mikecurato.com/worm-loves-worm diff --git a/hugo.toml b/hugo.toml index d1e7f45..579067b 100644 --- a/hugo.toml +++ b/hugo.toml @@ -7,7 +7,7 @@ paginate = 3 [params] [params.about] title = "" -description = "Making vanity projects easy since 2024" +description = "" [[params.socialLinks]] icon = "fa-brands fa-mastodon" @@ -27,6 +27,14 @@ url = "https://github.com/no-reply" [params.theme] palette = "material" +[markup] + # Table of contents + # Add toc = true to content front matter to enable + [markup.tableOfContents] + startLevel = 2 + endLevel = 3 + ordered = true + [menus] [[menus.main]] name = 'Posts' diff --git a/layouts/_default/single.html b/layouts/_default/single.html new file mode 100644 index 0000000..d63a21e --- /dev/null +++ b/layouts/_default/single.html @@ -0,0 +1,25 @@ +{{ define "main" }} +
+

{{ .Title | markdownify }}

+
+
+ {{ .Content }} +
+ +{{ end }} + +{{define "aside" }} + {{ if .Params.description }}

{{ .Params.description }}

{{ end }} + {{ if or (.Params.author) (.Params.date) }} +

+ {{ if .Params.author }}By {{ .Params.author }}{{ if .Date }}, {{ end }}{{ end }} + {{ if .Date }}{{ .Date.Format "2006-01-02" }}{{ end }} +

+ {{ end }} + + {{ if and (.Params.toc) (.TableOfContents) }} +
+ {{ .Title }}: + {{ .TableOfContents }} + {{ end }} +{{ end }} diff --git a/layouts/shortcodes/code-details.html b/layouts/shortcodes/code-details.html new file mode 100644 index 0000000..a454ef6 --- /dev/null +++ b/layouts/shortcodes/code-details.html @@ -0,0 +1,4 @@ +
+{{ .Get "summary" | markdownify }} +{{ printf "```%s%s```" (.Get "lang") (.Get "details") | markdownify}} +
diff --git a/layouts/shortcodes/details.html b/layouts/shortcodes/details.html new file mode 100644 index 0000000..9ca63cc --- /dev/null +++ b/layouts/shortcodes/details.html @@ -0,0 +1,4 @@ +
+{{ .Get "summary" | markdownify }} +{{ .Get "details" | markdownify }} +
diff --git a/public/categories/index.html b/public/categories/index.html index 65a374b..76ba652 100644 --- a/public/categories/index.html +++ b/public/categories/index.html @@ -2,7 +2,7 @@ Categories – estradiol.cloud - + @@ -60,7 +60,7 @@

-

Making vanity projects easy since 2024

+ diff --git a/public/categories/tech/index.html b/public/categories/tech/index.html deleted file mode 100644 index 15e4f16..0000000 --- a/public/categories/tech/index.html +++ /dev/null @@ -1,98 +0,0 @@ - - - - Tech – estradiol.cloud - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
-

Tech

- - - - - - -
- -
-
-
- - -

-

Making vanity projects easy since 2024

-
- - - -
-
-
- - -
-
- - - -
- - - diff --git a/public/categories/tech/index.xml b/public/categories/tech/index.xml deleted file mode 100644 index b552851..0000000 --- a/public/categories/tech/index.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - Tech on estradiol.cloud - http://localhost:1313/categories/tech/ - Recent content in Tech on estradiol.cloud - Hugo -- gohugo.io - en-us - Wed, 28 Feb 2024 15:35:46 -0800 - - - Hugo on Kubernetes & NGINX - http://localhost:1313/posts/hugo-on-k8s-nginx/ - Wed, 28 Feb 2024 15:35:46 -0800 - http://localhost:1313/posts/hugo-on-k8s-nginx/ - i decided to make a website. a static one. this one. with Hugo. this is basically as a vanity project so i have some stuff to host in a Kubernetes cluster i&rsquo;m running. the k8s cluster is also as a vanity project. because i don&rsquo;t like software, i wanted a way to deploy my site that doesn&rsquo;t involve much of it. this post is about that. Getting Started i built my site by following the straight-forward Getting Started guide in the Hugo documentation. - - - diff --git a/public/index.html b/public/index.html index 52c82eb..932ff43 100644 --- a/public/index.html +++ b/public/index.html @@ -3,7 +3,7 @@ estradiol.cloud – estradiol.cloud - + @@ -54,7 +54,7 @@

-

Making vanity projects easy since 2024

+ diff --git a/public/posts/hugo-on-k8s-nginx/index.html b/public/posts/hugo-on-k8s-nginx/index.html deleted file mode 100644 index af7d4c4..0000000 --- a/public/posts/hugo-on-k8s-nginx/index.html +++ /dev/null @@ -1,325 +0,0 @@ - - - - Hugo on Kubernetes & NGINX – estradiol.cloud - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
-
-

Hugo on Kubernetes & NGINX

-
-
-

i decided to make a website. a static one. this one. with Hugo. this -is basically as a vanity project so i have some stuff to host in a -Kubernetes cluster i’m running. the k8s cluster is also as a vanity -project.

-

because i don’t like software, i wanted a way to deploy my site that -doesn’t involve much of it. this post is about that.

-

Getting Started

-

i built my site by following the straight-forward Getting Started -guide in the Hugo documentation.

-

i did hugo new site estradiol.cloud. and then cd estradiol.cloud; git init. and -then i picked a ridiculous theme “inspired by terminal ricing aesthetics”, -installing it like git submodule add https://github.com/joeroe/risotto.git themes/risotto; echo "theme = 'risotto'" >> hugo.toml. i appreciate the culinary naming choice.

-

at this point, my website is basically finished (i also changed the title in hugo.toml). -i probably won’t be putting anything on it, so there’s no point fiddling with other -details.

-

about deployment, the guide’s Basic Usage page has this to offer:

-
-

Most of our users deploy their sites using a CI/CD workflow, where a push1 - -to their GitHub or GitLab repository triggers a build and deployment. Popular -providers include AWS Amplify, CloudCannon, Cloudflare Pages, GitHub Pages, -GitLab Pages, and Netlify.

-
    -
  1. The Git repository contains the entire project directory, typically excluding the -public directory because the site is built after the push.
  2. -
-
-

importantly, you can’t make a post about deploying this way. everyone deploys -this way. if i deploy this way, this site will have no content.

-

it also involves some system somewhere that can run Hugo to build the site and push -it to some remote system where my cluster can reach the public/ output. i definitely -already need Hugo installed on my workstation if i’m going to post anything here -(unlikely), so now i’m running Hugo in two places. there’s surely going to be -other complex nonsense like webhooks involved.

- -
-

and hang on. let’s look at this again:

-
-
    -
  1. The Git repository contains the entire project directory, typically excluding the -public directory because the site is built after the push.
  2. -
-
-

you’re telling me i’m going to build a nice static site and not check the -actual content into version control? couldn’t be me.

-

Getting Static

-

what if instead i pushed my site to a git repository exactly as i intend to serve it? -then i could shell into my web server, pull the site, and nifty-galifty! isn’t this -the way it has always been done?

-

one problem is that i don’t have a web server, i have a container orchestration -system. there are several upsides to this (few of which are relevant for my project) -but it also means that somehow my content needs to end up in a container, and i -don’t want that container to need to retain state across restarts or replicas.

-

i could run a little pipeline that builds a container wrapping my static site, -pushes it to a registry somewhere my deployments can pull it. all ready to go. -but now i’ve got software again: build stages and webhooks and to make matters -worse, now i’m hosting and versioning container images.

- -

i don’t want any of this.

-
-

instead, i’d like to deploy a popular stock container from a public registry and -deliver my content to it continuously.

-

as a minimal version of this, i could do:

-
apiVersion: v1
-kind: Pod
-metadata:
-  name: nginx
-  namespace: ec
-  labels:
-    app.kubernetes.io/instance: estradiol-cloud
-    app.kubernetes.io/name: nginx
-spec:
-  containers:
-  - name: nginx
-    image: nginx:1.25.4
-    ports:
-    - containerPort: 80
-    volumeMounts:
-    - mountPath: /app
-      name: staticsite
-  - name: git-pull
-    image: bitnami/git
-    command:
-    - /bin/bash
-    - -ec
-    - |
-      while true; do
-        cd /app && git -c safe.directory=/app pull origin trunk
-        sleep 60
-      done      
-    volumeMounts:
-    - mountPath: /app
-      name: staticsite
-  initContainers:
-  - name: git-clone
-    image: bitnami/git
-    command:
-    - /bin/bash
-    - -ec
-    - |
-      git clone https://code.estradiol.cloud/tamsin/estradiol.cloud.git --no-checkout --branch trunk /tmp/app
-      cd /tmp/app && git sparse-checkout init --cone
-      git sparse-checkout set public && git checkout
-      rm -rf /app && mv /tmp/app /app      
-    volumeMounts:
-    - mountPath: /app
-      name: staticsite
-  volumes:
-  - emptyDir: {}
-    name: staticsite
-
----
-apiVersion: v1
-kind: ConfigMap
-metadata:
-  labels:
-    app.kubernetes.io/instance: estradiol-cloud
-    app.kubernetes.io/name: nginx
-  name: nginx-server-block
-  namespace: ec
-data:
-  server-block.conf: |-
-    server {
-      listen 8080;
-      root /app/public;
-      index index.html;
-    }    
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Getting Flux’d

-
apiVersion: source.toolkit.fluxcd.io/v1beta2
-kind: HelmRepository
-metadata:
-  name: bitnami
-  namespace: default
-spec:
-  url: https://charts.bitnami.com/bitnami
-
apiVersion: source.toolkit.fluxcd.io/v1beta2
-kind: HelmRepository
-metadata:
-  name: bitnami
-  namespace: default
-spec:
-  url: https://charts.bitnami.com/bitnami
-
-
-
- -
- -
-
-
- - -

-

Making vanity projects easy since 2024

-
- - - -
-
-
- - -

- - 2024-02-28 -

- - - - -
-
- - - -
- - - diff --git a/public/posts/index.html b/public/posts/index.html index 3111c51..a809670 100644 --- a/public/posts/index.html +++ b/public/posts/index.html @@ -2,7 +2,7 @@ Posts – estradiol.cloud - + @@ -60,7 +60,7 @@

-

Making vanity projects easy since 2024

+ diff --git a/public/tags/index.html b/public/tags/index.html index cb7a52a..f77d76b 100644 --- a/public/tags/index.html +++ b/public/tags/index.html @@ -2,7 +2,7 @@ Tags – estradiol.cloud - + @@ -60,7 +60,7 @@

-

Making vanity projects easy since 2024

+ diff --git a/public/tags/k8s/index.html b/public/tags/k8s/index.html deleted file mode 100644 index ae7c14e..0000000 --- a/public/tags/k8s/index.html +++ /dev/null @@ -1,98 +0,0 @@ - - - - K8s – estradiol.cloud - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
-

K8s

- - - - - - -
- -
-
-
- - -

-

Making vanity projects easy since 2024

-
- - - -
-
-
- - -
-
- - - -
- - - diff --git a/public/tags/k8s/index.xml b/public/tags/k8s/index.xml deleted file mode 100644 index dca5b65..0000000 --- a/public/tags/k8s/index.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - K8s on estradiol.cloud - http://localhost:1313/tags/k8s/ - Recent content in K8s on estradiol.cloud - Hugo -- gohugo.io - en-us - Wed, 28 Feb 2024 15:35:46 -0800 - - - Hugo on Kubernetes & NGINX - http://localhost:1313/posts/hugo-on-k8s-nginx/ - Wed, 28 Feb 2024 15:35:46 -0800 - http://localhost:1313/posts/hugo-on-k8s-nginx/ - i decided to make a website. a static one. this one. with Hugo. this is basically as a vanity project so i have some stuff to host in a Kubernetes cluster i&rsquo;m running. the k8s cluster is also as a vanity project. because i don&rsquo;t like software, i wanted a way to deploy my site that doesn&rsquo;t involve much. this post is about that. Getting Started i built my site by following the straight-forward Getting Started guide in the Hugo documentation. - - - diff --git a/public/tags/meta/index.html b/public/tags/meta/index.html deleted file mode 100644 index 3e5b71f..0000000 --- a/public/tags/meta/index.html +++ /dev/null @@ -1,98 +0,0 @@ - - - - Meta – estradiol.cloud - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
-

Meta

- - - - - - -
- -
-
-
- - -

-

Making vanity projects easy since 2024

-
- - - -
-
-
- - -
-
- - - -
- - - diff --git a/public/tags/meta/index.xml b/public/tags/meta/index.xml deleted file mode 100644 index 762e14d..0000000 --- a/public/tags/meta/index.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - Meta on estradiol.cloud - http://localhost:1313/tags/meta/ - Recent content in Meta on estradiol.cloud - Hugo -- gohugo.io - en-us - Wed, 28 Feb 2024 15:35:46 -0800 - - - Hugo on Kubernetes & NGINX - http://localhost:1313/posts/hugo-on-k8s-nginx/ - Wed, 28 Feb 2024 15:35:46 -0800 - http://localhost:1313/posts/hugo-on-k8s-nginx/ - i decided to make a website. a static one. this one. with Hugo. this is basically as a vanity project so i have some stuff to host in a Kubernetes cluster i&rsquo;m running. the k8s cluster is also as a vanity project. because i don&rsquo;t like software, i wanted a way to deploy my site that doesn&rsquo;t involve much. this post is about that. Getting Started i built my site by following the straight-forward Getting Started guide in the Hugo documentation. - - - diff --git a/public/tags/tech/index.html b/public/tags/tech/index.html deleted file mode 100644 index 15e4f16..0000000 --- a/public/tags/tech/index.html +++ /dev/null @@ -1,98 +0,0 @@ - - - - Tech – estradiol.cloud - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
-

Tech

- - - - - - -
- -
-
-
- - -

-

Making vanity projects easy since 2024

-
- - - -
-
-
- - -
-
- - - -
- - - diff --git a/public/tags/tech/index.xml b/public/tags/tech/index.xml deleted file mode 100644 index 84cac81..0000000 --- a/public/tags/tech/index.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - Tech on estradiol.cloud - http://localhost:1313/tags/tech/ - Recent content in Tech on estradiol.cloud - Hugo -- gohugo.io - en-us - Wed, 28 Feb 2024 15:35:46 -0800 - - - Hugo on Kubernetes & NGINX - http://localhost:1313/posts/hugo-on-k8s-nginx/ - Wed, 28 Feb 2024 15:35:46 -0800 - http://localhost:1313/posts/hugo-on-k8s-nginx/ - i decided to make a website. a static one. this one. with Hugo. this is basically as a vanity project so i have some stuff to host in a Kubernetes cluster i&rsquo;m running. the k8s cluster is also as a vanity project. because i don&rsquo;t like software, i wanted a way to deploy my site that doesn&rsquo;t involve much. this post is about that. Getting Started i built my site by following the straight-forward Getting Started guide in the Hugo documentation. - - -