diff --git a/documentation/.vitepress/config.mts b/documentation/.vitepress/config.mts index 3f1c5021..c14b50ca 100644 --- a/documentation/.vitepress/config.mts +++ b/documentation/.vitepress/config.mts @@ -65,6 +65,10 @@ export default defineConfig({ { text: "Docker 🐋", link: "/docs/install/docker" }, { text: "Proxmox LXC 🐧", link: "/docs/install/proxmox_lxc" }, { text: "Synology NAS ☁️", link: "/docs/install/synology_nas" }, + { + text: "Kubernetes and Kustomize 🌐", + link: "/docs/install/kustomize", + }, { text: "With A Reverse Proxy", diff --git a/documentation/docs/install/kustomize.md b/documentation/docs/install/kustomize.md new file mode 100644 index 00000000..5e9d0744 --- /dev/null +++ b/documentation/docs/install/kustomize.md @@ -0,0 +1,34 @@ +# Kubernetes and Kustomize (k8s) + +_AdventureLog can be run inside a kubernetes cluster using [kustomize](https://kustomize.io/)._ + +## Prerequisites + +A working kubernetes cluster. AdventureLog has been tested on k8s, but any Kustomize-capable flavor should be easy to use. + +## Cluster Routing + +Because the AdventureLog backend must be reachable by **both** the web browser and the AdventureLog frontend, k8s-internal routing mechanisms traditional for standing up other similar applications **cannot** be used. + +In order to host AdventureLog in your cluster, you must therefor configure an internally and externally resolvable ingress that routes to your AdventureLog backend container. + +Once you have made said ingress, set `PUBLIC_SERVER_URL` and `PUBLIC_URL` env variables below to the url of that ingress. + +## Tailscale and Headscale + +Many k8s homelabs choose to use [Tailscale](https://tailscale.com/) or similar projects to remove the need for open ports in your home firewall. + +The [Tailscale k8s Operator](https://tailscale.com/kb/1185/kubernetes/) will set up an externally resolvable service/ingress for your AdventureLog instance, +but it will fail to resolve internally. + +You must [expose tailnet IPs to your cluster](https://tailscale.com/kb/1438/kubernetes-operator-cluster-egress#expose-a-tailnet-https-service-to-your-cluster-workloads) so the AdventureLog pods can resolve them. + +## Getting Started + +Take a look at the [example config](https://github.com/seanmorley15/AdventureLog/blob/main/kustomization.yml) and modify it for your usecase. + +## Environment Variables + +Look at the [environment variable summary](docker.md#configuration) in the docker install section to see available and required configuration options. + +Enjoy AdventureLog! 🎉 diff --git a/kustomization.yml b/kustomization.yml new file mode 100644 index 00000000..da7c6e77 --- /dev/null +++ b/kustomization.yml @@ -0,0 +1,153 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: example-name + labels: + app: adventure +spec: + replicas: 1 + selector: + matchLabels: + app: adventure + template: + metadata: + name: adventure + labels: + app: adventure + spec: + volumes: + - name: adventure-journal + persistentVolumeClaim: + claimName: adventure-journal-pvc + - name: adventure-journal-db + persistentVolumeClaim: + claimName: adventure-journal-db-pvc + containers: + - name: adventure-frontend + image: ghcr.io/seanmorley15/adventurelog-frontend:latest + imagePullPolicy: IfNotPresent + ports: + - containerPort: 3000 + env: + - name: PUBLIC_SERVER_URL + value: "http://internally-and-externally.reachable.io:80" + - name: ORIGIN + value: "http://url-typed-into-browser.io:80" + - name: BODY_SIZE_LIMIT + value: "Infinity" + + - name: adventure-db + image: postgis/postgis:15-3.3 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 5432 + volumeMounts: + - name: adventure-journal-db + mountPath: /var/lib/postgresql/data + env: + - name: POSTGRES_DB + value: database + - name: PGDATA + value: /var/lib/postgresql/data/pgdata/subdir + - name: POSTGRES_USER + value: adventure + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: adventurelog-secret + key: adventure-postgres-password + + - name: adventure-backend + image: ghcr.io/seanmorley15/adventurelog-backend:latest + imagePullPolicy: IfNotPresent + ports: + - containerPort: 80 + - containerPort: 8000 + volumeMounts: + - name: adventure-journal + mountPath: /code/media + env: + - name: PGHOST + value: "adventure-db-svc" + - name: PGDATABASE + value: "database" + - name: PGUSER + value: "adventure" + - name: PGPASSWORD + valueFrom: + secretKeyRef: + name: adventurelog-secret + key: adventure-postgres-password + - name: SECRET_KEY + valueFrom: + secretKeyRef: + name: adventurelog-secret + key: adventure-postgres-password + - name: PUBLIC_URL + value: "http://internally-and-externally.reachable.io:80" # Match the outward port, used for the creation of image urls + - name: FRONTEND_URL + value: "http://url-typed-into-browser.io:80" + - name: CSRF_TRUSTED_ORIGINS + value: "http://url-typed-into-browser.io:80, http://internally-and-externally.reachable.io:80" + - name: DJANGO_ADMIN_USERNAME + value: "admin" + - name: DJANGO_ADMIN_PASSWORD + value: "admin" + - name: DJANGO_ADMIN_EMAIL + value: "admin@example.com" + - name: DEBUG + value: "True" + restartPolicy: Always +--- +apiVersion: v1 +kind: Service +metadata: + name: adventure-db-svc +spec: + selector: + app: adventure + ports: + - name: db + protocol: TCP + port: 5432 + targetPort: 5432 +--- +apiVersion: v1 +kind: Service +metadata: + name: server +spec: + selector: + app: adventure + ports: + - name: http + protocol: TCP + port: 80 + targetPort: 80 + - name: base + protocol: TCP + port: 8000 + targetPort: 8000 +--- +# If you aren't automatically provisioning PVCs (i.e. with Longhorn, you'll need to also create the PV's) +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: adventure-journal-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: adventure-journal-db-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi