From b10ee995caa4c0b4235f11f070c31c7b44e925c1 Mon Sep 17 00:00:00 2001 From: tamsin woo Date: Sat, 9 Mar 2024 01:42:20 -0800 Subject: [PATCH] more WIP on the meta post --- .d2/hugo-container-build.d2 | 35 + .d2/hugo-helm-setup.d2 | 30 + .d2/hugo-minimal-pod-setup.d2 | 22 + .../images/hugo-container-build.svg | 875 ++++++++++++++++++ .../images/hugo-helm-setup.svg | 875 ++++++++++++++++++ .../images/hugo-minimal-pod-setup.svg | 872 +++++++++++++++++ content/posts/hugo-on-k8s-nginx/index.md | 182 +++- content/posts/hugo-on-k8s-nginx/site.yaml | 90 ++ layouts/shortcodes/code-details.html | 2 + public/categories/index.html | 2 + public/categories/tutorial/index.html | 2 + public/index.html | 2 + public/posts/index.html | 2 + public/posts/miniflux-rss/index.html | 4 + public/tags/index.html | 2 + public/tags/k8s/index.html | 2 + public/tags/meta/index.html | 2 + public/tags/miniflux/index.html | 2 + public/tags/rss/index.html | 2 + 19 files changed, 2969 insertions(+), 36 deletions(-) create mode 100644 .d2/hugo-helm-setup.d2 create mode 100644 .d2/hugo-minimal-pod-setup.d2 create mode 100644 content/posts/hugo-on-k8s-nginx/images/hugo-container-build.svg create mode 100644 content/posts/hugo-on-k8s-nginx/images/hugo-helm-setup.svg create mode 100644 content/posts/hugo-on-k8s-nginx/images/hugo-minimal-pod-setup.svg create mode 100644 content/posts/hugo-on-k8s-nginx/site.yaml diff --git a/.d2/hugo-container-build.d2 b/.d2/hugo-container-build.d2 index 8225f63..122af30 100644 --- a/.d2/hugo-container-build.d2 +++ b/.d2/hugo-container-build.d2 @@ -4,3 +4,38 @@ title: |md shape: text near: top-center } + +my laptop -> GitHub.repo: git push + +my laptop { + icon: https://icons.terrastruct.com/tech%2Flaptop.svg + near: center-left +} + +GitHub { + icon: https://icons.terrastruct.com/social%2F039-github.svg + + runner { + build job + deploy job + } + + repo <- runner.build job: pull + events api <- runner: long poll + + image registry { + "estradiol-cloud:latest" + } + + runner.build job -> "image registry"."estradiol-cloud:latest": push +} + +GitHub.runner.deploy job -> "kubernetes": kubectl apply + +kubernetes { + icon: https://icons.terrastruct.com/azure%2F_Companies%2FKubernetes.svg + + nginx pod +} + +kubernetes.nginx pod -> GitHub."image registry"."estradiol-cloud:latest": pull diff --git a/.d2/hugo-helm-setup.d2 b/.d2/hugo-helm-setup.d2 new file mode 100644 index 0000000..ca5e162 --- /dev/null +++ b/.d2/hugo-helm-setup.d2 @@ -0,0 +1,30 @@ +title: |md + # deploy w/ helm & bitnami/nginx +| { + shape: text + near: top-center +} + +my laptop -> repo: git push +my laptop -> kubernetes: `kubectl apply` + + +kubernetes { + icon: https://icons.terrastruct.com/azure%2F_Companies%2FKubernetes.svg + ingress -> service + service -> pod(s) + + pod(s) { + git-pull -> emptyDir: mounts + nginx -> emptyDir: mounts + emptyDir { icon: https://icons.terrastruct.com/tech%2Fdiskette.svg } + } + pod(s).nginx -> configMap: mounts +} + +kubernetes.pod(s).git-pull -> repo: pull + +web { + icon: https://icons.terrastruct.com/essentials%2F140-internet.svg +} +web -> kubernetes.ingress diff --git a/.d2/hugo-minimal-pod-setup.d2 b/.d2/hugo-minimal-pod-setup.d2 new file mode 100644 index 0000000..41355d1 --- /dev/null +++ b/.d2/hugo-minimal-pod-setup.d2 @@ -0,0 +1,22 @@ +title: |md + # deploy w/ kubectl apply +| { + shape: text + near: top-center +} + +my laptop -> repo: git push +my laptop -> kubernetes: `kubectl apply` + +kubernetes { + icon: https://icons.terrastruct.com/azure%2F_Companies%2FKubernetes.svg + + pod { + git-pull -> emptyDir: mounts + nginx -> emptyDir: mounts + emptyDir { icon: https://icons.terrastruct.com/tech%2Fdiskette.svg } + } + pod.nginx -> configMap: mounts +} + +kubernetes.pod.git-pull -> repo: pull diff --git a/content/posts/hugo-on-k8s-nginx/images/hugo-container-build.svg b/content/posts/hugo-on-k8s-nginx/images/hugo-container-build.svg new file mode 100644 index 0000000..957b257 --- /dev/null +++ b/content/posts/hugo-on-k8s-nginx/images/hugo-container-build.svg @@ -0,0 +1,875 @@ + + + + + + + + +

deploy w/ container build & actions

+
my laptopGitHubkubernetesreporunnerevents apiimage registrynginx podbuild jobdeploy jobestradiol-cloud:latest git push pull long poll push kubectl apply pull + + + + + + + + + + + + + + + + + + + +
diff --git a/content/posts/hugo-on-k8s-nginx/images/hugo-helm-setup.svg b/content/posts/hugo-on-k8s-nginx/images/hugo-helm-setup.svg new file mode 100644 index 0000000..42ad254 --- /dev/null +++ b/content/posts/hugo-on-k8s-nginx/images/hugo-helm-setup.svg @@ -0,0 +1,875 @@ + + + + + + + + +

deploy w/ helm & bitnami/nginx

+
my laptoprepokuberneteswebingressservicepod(s)configMapgit-pullemptyDirnginx git push `kubectl apply` mounts mounts mounts pull + + + + + + + + + + + + + + + + + + + +
diff --git a/content/posts/hugo-on-k8s-nginx/images/hugo-minimal-pod-setup.svg b/content/posts/hugo-on-k8s-nginx/images/hugo-minimal-pod-setup.svg new file mode 100644 index 0000000..e2afe31 --- /dev/null +++ b/content/posts/hugo-on-k8s-nginx/images/hugo-minimal-pod-setup.svg @@ -0,0 +1,872 @@ + + + + + + + + +

deploy w/ kubectl apply

+
my laptoprepokubernetespodconfigMapgit-pullemptyDirnginx git push `kubectl apply` mounts mounts mounts pull + + + + + + + + + + + + + + + + +
diff --git a/content/posts/hugo-on-k8s-nginx/index.md b/content/posts/hugo-on-k8s-nginx/index.md index e50d502..c7ef7d5 100644 --- a/content/posts/hugo-on-k8s-nginx/index.md +++ b/content/posts/hugo-on-k8s-nginx/index.md @@ -9,9 +9,9 @@ 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. +main reason i have for needing a website is as a vanity project, because i need +some stuff to host in a [Kubernetes][k8s] cluster i'm running. the k8s cluster +is also 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. @@ -27,13 +27,14 @@ and then i picked a ridiculous theme submodule add https://github.com/joeroe/risotto.git themes/risotto; echo "theme = 'risotto'" >> hugo.toml`.[^1] -[^1]: i appreciate the culinary theme. +[^1]: i appreciate the culinary branding. 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. +`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: +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 @@ -46,15 +47,15 @@ about deployment, the guide's _[Basic Usage][hugo-deploy]_ page has this to offe 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. +this approach also involves a build system somewhere that can run Hugo to +build and push the compiled code and assets onto my cluster. 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) +![diagram: deploy w/ GitHub Pages & Actions](images/hugo-github-pages.svg) ---- @@ -68,11 +69,10 @@ _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? +suppose i instead check 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 @@ -87,7 +87,7 @@ 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) +![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. @@ -106,10 +106,11 @@ a minimal setup to achieve this might look like: - 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) -![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 +when a new pod comes up, the `initContainer` mounts the +[`emptyDir`][k8s-emptydir] and clones the repository into `/www`. i use +`git sparse-checkout` to avoid pulling repository contents i don't want to serve out: ```bash @@ -123,7 +124,7 @@ shopt -s dotglob mv /tmp/www/* /www ``` -script up my `git pull` loop: +for the sidecar, i script up my `git pull` loop: ```bash # git-pull command @@ -133,7 +134,9 @@ while true; do done ``` -and configure `nginx` to use `public/` as root: +and i create a [ConfigMap][k8s-configmap] with a server block to configure +`nginx` to use Hugo's `public/` as root: + ```txt # ConfigMap; data: default.conf @@ -208,7 +211,7 @@ spec: 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 init --cone; git sparse-checkout set public; git checkout; mv /tmp/www/* /www @@ -231,26 +234,132 @@ my Hugo workflow now looks like: 1. run `hugo --gc --minify`;[^7] 1. commit & push. -the only active process from this point is my little control loop running `git pull`. +my `git pull` control loop takes things over from here and i'm on easy street. + +[^7]: i added `disableHTML = true` and `disableXML = true` to `[minify]` +configuration in `hugo.toml` to keep HTML and RSS diffs readable. -[^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] +this is going great! my Pod is running. it's serving out my code. i get +continuous deployment™ for the low price of 11 lines `bash`. i mean... +no one can actually browse to my website[^8] but that will be an easy fix, +right? -TK: YAML counts as software. +[^8]: i can check that its working, at least, with a [port-forward][k8s-port]. -conveniently, [Bitnami][bitnami] maintains a [Helm][helm] Chart that +first, i need a [`Service`][k8s-svc]. this gives me a proxy with service +discovery. TK: what is this really? -![diagram: helm setup](/images/hugo-helm-setup.svg) +{{< code-details summary="kubectl apply -f service.yaml" lang="yaml" details=` +# service.yaml +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/instance: estradiol-cloud + app.kubernetes.io/name: nginx + name: nginx +spec: + type: ClusterIP + selector: + app.kubernetes.io/instance: estradiol-cloud + app.kubernetes.io/name: nginx + ports: + - name: http + port: 80 + protocol: TCP + targetPort: http +` >}} + + +and i need an [`Ingress`][k8s-ingress] to handle traffic inbound to the cluster +and direct it to the `Service`: + +{{< code-details summary="kubectl apply -f ingress.yaml" lang="yaml" details=` +# ingress.yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + labels: + app.kubernetes.io/instance: estradiol-cloud + app.kubernetes.io/name: nginx + name: nginx +spec: + rules: + - host: estradiol.cloud + http: + paths: + - backend: + service: + name: nginx + port: + name: http + path: / + pathType: Prefix +` >}} + +TK: wtf? Ingress controller + +--- + +as this has come together, i've gotten increasinly anxious about how much +YAML i've had to write. this is a problem because YAML is software and, as +we've established, i'm hoping not to have much of that. it's also annoying +that most of this YAML really is boilerplate. + +conveniently, [Bitnami][bitnami] maintains a [Helm][helm] Chart that hides all +the boilerplate and does exactly what we've just been doing manually.[^9] + +[^9]: what incredible luck! (obviously, until now i've been working backward from this chart) + +TK: install helm +TK: pull bitnami chart +TK: helm values + +{{< code-details summary="`helm upgrade --install --create-namespace --namespace estradiol-cloud -f values.yaml`" lang="yaml" details=` +# values.yaml +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 +`>}} + +![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. +having this `helm` client software installed on my laptop. plus, i still have +some YAML and it's not really great that i'm storing it in flat files and +pushing it to my cluster manually. i love automation. i might love automation +more than i disdain software. i feel prepared to get some software if it +will get this yaml out of my shell history and into a git repo. ```yaml @@ -311,9 +420,7 @@ spec: type: ClusterIP `>}} -{{ with .Resources.GetMatch "hugo-flux-seq.svg" }} - -{{ end }} +![diagram: flux git push/deploy sequence](images/flux-seq.svg) ## A Note About Software @@ -324,8 +431,7 @@ setting aside the stuff that provisions and scales my cluster nodes, i have: - `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`; + - `fluxcd`, especially `kustomize-controller` and `helm-controller`; - `nginx-ingress` controller; - the `bitnami/nginx` Helm chart; @@ -343,14 +449,18 @@ _fin_ [bitnami]: https://bitnami.com/ +[cert-mgr]: https://cert-manager.io/docs/tutorials/acme/nginx-ingress/ [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-emptydir]: https://kubernetes.io/docs/concepts/storage/volumes/#emptydir [k8s-init]: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ +[k8s-ingress]: https://kubernetes.io/docs/concepts/services-networking/ingress/ [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/ +[k8s-svc]: https://kubernetes.io/docs/concepts/services-networking/service/ [risotto]: https://github.com/joeroe/risotto [worm-love]: https://www.mikecurato.com/worm-loves-worm diff --git a/content/posts/hugo-on-k8s-nginx/site.yaml b/content/posts/hugo-on-k8s-nginx/site.yaml new file mode 100644 index 0000000..0f05234 --- /dev/null +++ b/content/posts/hugo-on-k8s-nginx/site.yaml @@ -0,0 +1,90 @@ +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 + name: http + 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; + 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 +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/instance: estradiol-cloud + app.kubernetes.io/name: nginx + name: nginx +spec: + type: ClusterIP + selector: + app.kubernetes.io/instance: estradiol-cloud + app.kubernetes.io/name: nginx + ports: + - name: http + port: 80 + protocol: TCP + targetPort: http diff --git a/layouts/shortcodes/code-details.html b/layouts/shortcodes/code-details.html index a454ef6..3d3fe1a 100644 --- a/layouts/shortcodes/code-details.html +++ b/layouts/shortcodes/code-details.html @@ -1,4 +1,6 @@ +

{{ .Get "summary" | markdownify }} {{ printf "```%s%s```" (.Get "lang") (.Get "details") | markdownify}}
+

diff --git a/public/categories/index.html b/public/categories/index.html index f42d302..f5039a6 100644 --- a/public/categories/index.html +++ b/public/categories/index.html @@ -19,6 +19,8 @@ + + diff --git a/public/categories/tutorial/index.html b/public/categories/tutorial/index.html index da89e54..eed1af4 100644 --- a/public/categories/tutorial/index.html +++ b/public/categories/tutorial/index.html @@ -19,6 +19,8 @@ + + diff --git a/public/index.html b/public/index.html index 409db23..49d6588 100644 --- a/public/index.html +++ b/public/index.html @@ -20,6 +20,8 @@ + + diff --git a/public/posts/index.html b/public/posts/index.html index 3d70617..08287a1 100644 --- a/public/posts/index.html +++ b/public/posts/index.html @@ -19,6 +19,8 @@ + + diff --git a/public/posts/miniflux-rss/index.html b/public/posts/miniflux-rss/index.html index faa1ebd..5a44c9b 100644 --- a/public/posts/miniflux-rss/index.html +++ b/public/posts/miniflux-rss/index.html @@ -19,6 +19,8 @@ + + @@ -69,6 +71,7 @@ bitnami chart), but using it in the existing chart required: +

values.yaml
ingress:
@@ -97,6 +100,7 @@ bitnami chart), but using it in the existing chart required:
      size: 10Gi
 
+

diff --git a/public/tags/index.html b/public/tags/index.html index 15cc30d..9840aad 100644 --- a/public/tags/index.html +++ b/public/tags/index.html @@ -19,6 +19,8 @@ + + diff --git a/public/tags/k8s/index.html b/public/tags/k8s/index.html index 2ab48e3..ff63654 100644 --- a/public/tags/k8s/index.html +++ b/public/tags/k8s/index.html @@ -19,6 +19,8 @@ + + diff --git a/public/tags/meta/index.html b/public/tags/meta/index.html index eecbc48..bee583a 100644 --- a/public/tags/meta/index.html +++ b/public/tags/meta/index.html @@ -19,6 +19,8 @@ + + diff --git a/public/tags/miniflux/index.html b/public/tags/miniflux/index.html index 0435799..ff595ae 100644 --- a/public/tags/miniflux/index.html +++ b/public/tags/miniflux/index.html @@ -19,6 +19,8 @@ + + diff --git a/public/tags/rss/index.html b/public/tags/rss/index.html index 0c1fcd4..73cd42a 100644 --- a/public/tags/rss/index.html +++ b/public/tags/rss/index.html @@ -19,6 +19,8 @@ + +