estradiol.cloud/content/posts/hugo-on-k8s-nginx.md
2024-02-28 22:49:15 -08:00

270 lines
8.8 KiB
Markdown

+++
title = 'Hugo on Kubernetes & NGINX'
date = 2024-02-28T15:35:46-08:00
draft = true
series = ['wtf']
categories = ['Tech']
tags = ['meta', 'k8s']
+++
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.
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`. 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][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 `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.
<!-- diagram ?? -->
----
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
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]?
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.
<!-- diagram ?? -->
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:
```yaml
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;
}
```
<!-- ```yaml -->
<!-- apiVersion: apps/v1 -->
<!-- kind: Deployment -->
<!-- metadata: -->
<!-- labels: -->
<!-- app.kubernetes.io/instance: estradiol-cloud -->
<!-- app.kubernetes.io/name: nginx -->
<!-- name: web-nginx -->
<!-- namespace: estradiol-cloud -->
<!-- spec: -->
<!-- replicas: 1 -->
<!-- selector: -->
<!-- matchLabels: -->
<!-- app.kubernetes.io/instance: estradiol-cloud -->
<!-- app.kubernetes.io/name: nginx -->
<!-- template: -->
<!-- metadata: -->
<!-- labels: -->
<!-- app.kubernetes.io/instance: estradiol-cloud -->
<!-- app.kubernetes.io/name: nginx -->
<!-- spec: -->
<!-- containers: -->
<!-- - name: git-repo-syncer -->
<!-- image: docker.io/bitnami/git:2.43.2-debian-12-r2 -->
<!-- imagePullPolicy: IfNotPresent -->
<!-- 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 -->
<!-- - name: nginx -->
<!-- image: docker.io/bitnami/nginx:1.25.4-debian-12-r2 -->
<!-- imagePullPolicy: IfNotPresent -->
<!-- env: -->
<!-- - name: NGINX_HTTP_PORT_NUMBER -->
<!-- value: "8080" -->
<!-- livenessProbe: -->
<!-- tcpSocket: -->
<!-- port: http -->
<!-- readinessProbe: -->
<!-- tcpSocket: -->
<!-- port: http -->
<!-- ports: -->
<!-- - containerPort: 8080 -->
<!-- name: http -->
<!-- protocol: TCP -->
<!-- volumeMounts: -->
<!-- - mountPath: /opt/bitnami/nginx/conf/server_blocks -->
<!-- name: nginx-server-block -->
<!-- - mountPath: /app -->
<!-- name: staticsite -->
<!-- initContainers: -->
<!-- - name: git-clone-repository -->
<!-- image: docker.io/bitnami/git:2.43.2-debian-12-r2 -->
<!-- imagePullPolicy: IfNotPresent -->
<!-- command: -->
<!-- - /bin/bash -->
<!-- - -ec -->
<!-- - | -->
<!-- [[ -f "/opt/bitnami/scripts/git/entrypoint.sh" ]] && source "/opt/bitnami/scripts/git/entrypoint.sh" -->
<!-- git clone https://code.estradiol.cloud/tamsin/estradiol.cloud.git --no-checkout --branch trunk /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/ -->
<!-- volumeMounts: -->
<!-- - mountPath: /app -->
<!-- name: staticsite -->
<!-- restartPolicy: Always -->
<!-- terminationGracePeriodSeconds: 30 -->
<!-- volumes: -->
<!-- - configMap: -->
<!-- defaultMode: 420 -->
<!-- name: web-nginx-server-block -->
<!-- name: nginx-server-block -->
<!-- - emptyDir: {} -->
<!-- name: staticsite -->
<!-- ``` -->
## Getting Flux'd
```yaml
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bitnami
namespace: default
spec:
url: https://charts.bitnami.com/bitnami
```
```yaml
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bitnami
namespace: default
spec:
url: https://charts.bitnami.com/bitnami
```
[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-pv]: https://kubernetes.io/docs/concepts/storage/persistent-volumes/
[risotto]: https://github.com/joeroe/risotto
[worm]: https://www.mikecurato.com/worm-loves-worm