i decided to make a website. a static one. this one. with Hugo.
the main reason i have for needing a website is as a learning project, so i have some stuff to host in a Kubernetes cluster i’m running. the k3s cluster is also a learning project.
k3s running in Raspberry Pi 5 control plane and two Intel NUC8i7 worker nodes.
Cilium as cni. Flux as continuous delivery.
gitlab runs as selfhosted in a separate vm.
my repo structure according to fluxcd best practices.
Parts involved in this solution:
nginx(running from a stock image);
git and bash (running from a stock image)
gitlab running as self-hosted locally in a virtual machine.
Kubernetes:
flux, especially kustomize-controller and helm controller;
cloudflared tunnel to handle external trafffic;
the bitnami/nginx Helm chart;
Getting Started
i built my site by following the straight-forward Getting Started guide in the Hugo documentation.
i did hugo new site uclab and then cd uclab; git init. and then i picked a 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.
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.
Getting Flux’d
to move my web stack into flux, i create a HelmRepository resource for
the bitnami Helm charts:
when i push these to my flux [source repository], the Helm
release rolls out.
Traffic inbound to the cluster is handled by Cloudflare tunnel and direct it to the service.
cloudflare.yaml
---apiVersion:traefik.io/v1alpha1apiVersion:apps/v1kind:Deploymentmetadata:name:cloudflaredspec:selector:matchLabels:app:cloudflaredreplicas:2# You could also consider elastic scaling for this deploymenttemplate:metadata:labels:app:cloudflaredspec:containers:- name:cloudflaredimage:cloudflare/cloudflared:latestargs:- tunnel# Points cloudflared to the config file, which configures what# cloudflared will actually do. This file is created by a ConfigMap# below.- --config- /etc/cloudflared/config/config.yaml- runlivenessProbe:httpGet:# Cloudflared has a /ready endpoint which returns 200 if and only if# it has an active connection to the edge.path:/readyport:2000failureThreshold:1initialDelaySeconds:10periodSeconds:10ports:- containerPort:2000name:http-metricsvolumeMounts:- name:configmountPath:/etc/cloudflared/configreadOnly:true# Each tunnel has an associated "credentials file" which authorizes machines# to run the tunnel. cloudflared will read this file from its local filesystem,# and it'll be stored in a k8s secret.- name:credsmountPath:/etc/cloudflared/credsreadOnly:truevolumes:- name:credssecret:secretName:tunnel-credentials# Create a config.yaml file from the ConfigMap below.- name:configconfigMap:name:cloudflareditems:- key:config.yamlpath:config.yaml---# This ConfigMap is just a way to define the cloudflared config.yaml file in k8s.# It's useful to define it in k8s, rather than as a stand-alone .yaml file, because# this lets you use various k8s templating solutions (e.g. Helm charts) to# parameterize your config, instead of just using string literals.apiVersion:v1kind:ConfigMapmetadata:name:cloudflareddata:config.yaml:| # Name of the tunnel you want to run
tunnel: uclab
credentials-file: /etc/cloudflared/creds/credentials.json
# Serves the metrics server under /metrics and the readiness server under /ready
metrics: 0.0.0.0:2000
no-autoupdate: true
ingress:
- hostname: uclab.dev
service: http://uclab-nginx:80
# This rule sends traffic to the built-in hello-world HTTP server. This can help debug connectivity
# issues. If hello.example.com resolves and tunnel.example.com does not, then the problem is
# in the connection from cloudflared to your local service, not from the internet to cloudflared.
- hostname: hello.example.com
service: hello_world
# This rule matches any traffic which didn't match a previous rule, and responds with HTTP 404.
- service: http_status:404