diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json index 313d0e6..32edbd6 100644 --- a/.obsidian/workspace.json +++ b/.obsidian/workspace.json @@ -13,12 +13,12 @@ "state": { "type": "markdown", "state": { - "file": "notes/index.md", + "file": "notes/0000-how-this-was-built.md", "mode": "source", "source": false }, "icon": "lucide-file", - "title": "index" + "title": "0000-how-this-was-built" } } ] @@ -170,8 +170,8 @@ }, "active": "a7a84c356653d530", "lastOpenFiles": [ - "here.md", "notes/index.md", + "here.md", "notes/0000-how-this-was-built.md" ] } \ No newline at end of file diff --git a/notes/0000-how-this-was-built.md b/notes/0000-how-this-was-built.md index 0ced051..f615561 100644 --- a/notes/0000-how-this-was-built.md +++ b/notes/0000-how-this-was-built.md @@ -3,4 +3,214 @@ title: Building this digital garden tags: - how-to - cicd + - quartz + - github-actions +draft: true --- +### Why? + +Well, in my work I deal with networks, systems, servers, services, tools, etc... every time I got stuck with an issue or a broken config or anything really, a quick Google search led me to some random person's web page which either gave me a solution or at least pointed me in the right direction. +So as a thank you to all those who spend their time and energy sharing their experience and knowledge, I decided to do the same and set up a place where I can write about what I do, learn, discover, break (and hopefully fix) and deal with. + +### How? + +I found out recently about this class of tools called [static site generators (SSGs)](https://en.wikipedia.org/wiki/Static_site_generator) - they can take text written in various formats and use it to generate static websites. Since I wanted for some time to have a place to share my notes online, I felt SSGs were just the thing I needed so I started looking around. There are many great options but out of all of them [Quartz](https://quartz.jzhao.xyz/) felt like the one - built with Typescript, super lightweight and comes with a clean and simple interface. + +At the same time, I've been messing with K8s in my home lab for a few years and I also had to get familiar with Github Actions for a work project (I've mostly used Gitlab CICD and custom scripts for CI stuff) so it all combined nicely into an opportunity to learn something new! + +Anyway, let's get into it - the plan was to serve the site with Nginx, running as a pod in K8s. The content would be written in Markdown (edited with Obsidian - more on that later) and pushed to my Gitea local instance where a Github Actions workflow would build the site files with Quartz and upload them to the Nginx pod's PVC. + +Summarised, I had to: +- [ ] Set up a Nginx pod (PVC, cert, service, ingress, configmap and deployment) +- [ ] Create a notes repo and the Github Actions workflow for auto deployment +- [ ] Troubleshoot the whole things until it works (always the fun bit) + +##### Nginx pod setup + +Nothing fancy, manifests below and explanations after. + +PVC: +``` +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: +    namespace: my-stuff +    name: digital-garden-data-pvc +spec: +    storageClassName: nfs-client +    accessModes: +        - ReadWriteOnce +    resources: +        requests: +            storage: 10Gi +``` + +Certificate: +``` +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: digital-garden-prod-tls + namespace: my-stuff +spec: + secretName: digital-garden-prod-tls + # 90d + duration: 2160h + # 15d + renewBefore: 360h + subject: + organizations: + - RPI + commonName: vlads-notes.jumpingcrab.com + isCA: false + privateKey: + algorithm: RSA + encoding: PKCS1 + size: 2048 + usages: + - server auth + - client auth + dnsNames: + - vlads-notes.jumpingcrab.com + issuerRef: + name: letsencrypt-prod + kind: ClusterIssuer + group: cert-manager.ioService: +``` +Service: +``` +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: digital-garden + name: digital-garden-svc + namespace: my-stuff +spec: + type: ClusterIP + ports: + - port: 443 + protocol: TCP + targetPort: 80 + selector: + app: digital-garden +``` + +Ingress: +``` +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + namespace: my-stuff + name: digital-garden-ingress + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + nginx.ingress.kubernetes.io/proxy-body-size: 512m +spec: + ingressClassName: nginx + tls: + - hosts: + - vlads-notes.jumpingcrab.com + # Name of the certifciate (see kubectl get certificate -A) + secretName: digital-garden-prod-tls + rules: + - host: vlads-notes.jumpingcrab.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + # Mapping to the service (see kubectl get services -n nextcloud) + name: digital-garden-svc + port: + number: 443``` +``` +ConfigMap: +``` +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: digital-garden-nginx-conf + namespace: my-stuff +data: + nginx.conf: | + user nginx; + worker_processes 3; + error_log /var/log/nginx/error.log; + events { + worker_connections 10240; + } + http { + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + access_log /var/log/nginx/access.log main; + server { + listen 80; + server_name vlads-notes.jumpingcrab.com; + root /www/data; + index index.html; + error_page 404 /404.html; + + include mime.types; + + location / { + try_files $uri $uri.html $uri/ =404; + } + } + } +``` +Deployment: +``` +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: digital-garden + namespace: my-stuff + labels: + app: digital-garden +spec: + replicas: 1 + selector: + matchLabels: + app: digital-garden + template: + metadata: + labels: + app: digital-garden + name: digital-garden + spec: + containers: + - name: digital-garden + image: nginx:1.28.0 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 80 + volumeMounts: + - name: digital-garden-volume + mountPath: /www/data + - name: nginx-config + mountPath: /etc/nginx/nginx.conf + subPath: nginx.conf + resources: {} + volumes: + - name: digital-garden-volume + persistentVolumeClaim: + claimName: digital-garden-data-pvc + - name: nginx-config + configMap: + name: digital-garden-nginx-conf +``` + +I needed the PVC to persists the data if the pod or the node crashes - it's hosted on an SSD attached to an NFS server that is exposed via a StorageClass to the cluster. The certificate is managed via [CertManager](https://cert-manager.io/) and is issued by Let'sEncrypt - always good to use TLS! The service simply ties the pod to the ingress, not much to say here. The ingress uses the Nginx admission controller and is configured with the Let'sEncrypt cert to enable TLS. The config map has a minimal Nginx config file that is mounted to the pod under "/etc/nginx/nginx.conf". Lastly, the deployment which ties it all together - not much to say, it's just one Nginx replica. Good practice says that I should add some resource limits and requests, but I'll leave that for later with the rest of the tech debt... + +#### Notes repo and Github Actions workflow setup + +TBC \ No newline at end of file