drop duplicate post
This commit is contained in:
parent
459dfcf6aa
commit
700a8dbdf6
@ -1,354 +0,0 @@
|
||||
+++
|
||||
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
|
||||
`>}}
|
||||
|
||||
![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-love]: https://www.mikecurato.com/worm-loves-worm
|
Loading…
Reference in New Issue
Block a user