From 5489da4490e83f5801da088e7f2179d4ee1d9c69 Mon Sep 17 00:00:00 2001
From: Benjamin Waldher <benjamin-git@waldher.us>
Date: Sun, 9 Apr 2023 20:19:26 +0000
Subject: [PATCH] Add Kubernetes configuration

---
 README.md                     |   1 +
 kubernetes/README.md          |  62 ++++++++++++++++++++
 kubernetes/deployment.yaml    | 107 ++++++++++++++++++++++++++++++++++
 kubernetes/ingress.yaml       |  32 ++++++++++
 kubernetes/kustomization.yaml |  37 ++++++++++++
 kubernetes/namespace.yaml     |   4 ++
 kubernetes/postgres.yaml      |  58 ++++++++++++++++++
 kubernetes/secret.yaml        |  12 ++++
 kubernetes/service.yaml       |  27 +++++++++
 kubernetes/solr.yaml          |  63 ++++++++++++++++++++
 10 files changed, 403 insertions(+)
 create mode 100644 kubernetes/README.md
 create mode 100644 kubernetes/deployment.yaml
 create mode 100644 kubernetes/ingress.yaml
 create mode 100644 kubernetes/kustomization.yaml
 create mode 100644 kubernetes/namespace.yaml
 create mode 100644 kubernetes/postgres.yaml
 create mode 100644 kubernetes/secret.yaml
 create mode 100644 kubernetes/service.yaml
 create mode 100644 kubernetes/solr.yaml

diff --git a/README.md b/README.md
index 9fce319e..e9817b66 100644
--- a/README.md
+++ b/README.md
@@ -77,6 +77,7 @@ Other ways are documented
 - Using the [nix](https://nixos.org/nix) package manager as [described
   here](https://docspell.org/docs/install/installing/#nix). A NixOS
   module is available, too.
+- Use the [Kubernetes](./kubernetes/README.md) configuration.
 
 
 ## Documentation
diff --git a/kubernetes/README.md b/kubernetes/README.md
new file mode 100644
index 00000000..9632673b
--- /dev/null
+++ b/kubernetes/README.md
@@ -0,0 +1,62 @@
+# Docspell Kubernetes Setup
+
+This folder contains the necessary Kubernetes manifests, as well as a [Kustomization](https://kustomize.io/), to deploy docspell to a Kubernetes cluster.
+
+## Using Kustomize
+
+To deploy a basic installation using Kustomize, you can use the following command:
+
+``` shell
+kubectl apply -k https://raw.githubusercontent.com/eikek/docspell/master/kubernetes
+```
+
+For a more advanced and production ready setup, create your own kustomization.yaml, changing the secret value and replica counts as necessary:
+
+``` yaml
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+resources:
+- github.com/eikek/docspell.git//kubernetes?timeout=90s&ref=master
+patches:
+- target:
+    kind: Deployment
+    name: restserver
+  patch: |-
+    - op: replace
+      path: /spec/replicas
+      value: 2
+    - op: replace
+      path: /spec/template/spec/containers/0/image
+      value: docspell/restserver:v0.40.0
+- target:
+    kind: Deployment
+    name: joex
+  patch: |-
+    - op: replace
+      path: /spec/template/spec/containers/0/image
+      value: docspell/joex:v0.40.0
+- target:
+    kind: Secret
+    name: restserver-secrets
+  patch: |-
+    - op: replace
+      path: /data/DOCSPELL_SERVER_BACKEND_JDBC_PASSWORD
+      value: ZGJwYXNzMg== # dbpass2
+    - op: replace
+      path: /data/DOCSPELL_JOEX_JDBC_PASSWORD
+      value: ZGJwYXNzMg== # dbpass2
+```
+
+And apply your kustomization:
+
+``` shell
+kubectl apply -k .
+```
+
+## Using Kubernetes manifests
+
+To deploy a basic installation using the Kubernetes manifests, you can use the following command:
+
+``` shell
+kubectl apply -f https://raw.githubusercontent.com/eikek/docspell/master/kubernetes
+```
\ No newline at end of file
diff --git a/kubernetes/deployment.yaml b/kubernetes/deployment.yaml
new file mode 100644
index 00000000..47cc52a9
--- /dev/null
+++ b/kubernetes/deployment.yaml
@@ -0,0 +1,107 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: restserver
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: restserver
+  template:
+    metadata:
+      labels:
+        app: restserver
+    spec:
+      containers:
+        - name: restserver
+          image: docspell/restserver
+          ports:
+            - containerPort: 7880
+          envFrom:
+            - configMapRef:
+                name: restserver-config
+            - secretRef:
+                name: restserver-secrets
+          env:
+            - name: DOCSPELL_SERVER_INTERNAL__URL
+              value: "http://restserver:7880"
+            - name: DOCSPELL_SERVER_BACKEND_JDBC_URL
+              value: "jdbc:postgresql://postgres:5432/dbname"
+            - name: DOCSPELL_SERVER_BIND_ADDRESS
+              value: "0.0.0.0"
+            - name: DOCSPELL_SERVER_FULL__TEXT__SEARCH_ENABLED
+              value: "true"
+            - name: DOCSPELL_SERVER_FULL__TEXT__SEARCH_SOLR_URL
+              value: "http://solr:8983/solr/docspell"
+            - name: DOCSPELL_SERVER_INTEGRATION__ENDPOINT_ENABLED
+              value: "true"
+            - name: DOCSPELL_SERVER_INTEGRATION__ENDPOINT_HTTP__HEADER_ENABLED
+              value: "true"
+            - name: DOCSPELL_SERVER_BACKEND_SIGNUP_MODE
+              value: "open"
+            - name: DOCSPELL_SERVER_BACKEND_ADDONS_ENABLED
+              value: "false"
+          resources:
+            limits:
+              cpu: '1'
+              memory: 1Gi
+            requests:
+              cpu: '0.5'
+              memory: 512Mi
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: joex
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: joex
+  template:
+    metadata:
+      labels:
+        app: joex
+    spec:
+      containers:
+        - name: joex
+          image: docspell/joex
+          ports:
+            - containerPort: 7878
+          envFrom:
+            - configMapRef:
+                name: joex-config
+            - secretRef:
+                name: joex-secrets
+          env:
+            - name: DOCSPELL_JOEX_APP__ID
+              value: "joex1"
+            - name: DOCSPELL_JOEX_PERIODIC__SCHEDULER_NAME
+              value: "joex1"
+            - name: DOCSPELL_JOEX_SCHEDULER_NAME
+              value: "joex1"
+            - name: DOCSPELL_JOEX_BASE__URL
+              value: "http://joex:7878"
+            - name: DOCSPELL_JOEX_BIND_ADDRESS
+              value: "0.0.0.0"
+            - name: DOCSPELL_JOEX_FULL__TEXT__SEARCH_ENABLED
+              value: "true"
+            - name: DOCSPELL_JOEX_FULL__TEXT__SEARCH_SOLR_URL
+              value: "http://solr:8983/solr/docspell"
+            - name: DOCSPELL_JOEX_JDBC_URL
+              value: "jdbc:postgresql://postgres:5432/dbname"
+            - name: DOCSPELL_JOEX_JDBC_USER
+              value: "dbuser"
+            - name: DOCSPELL_JOEX_ADDONS_EXECUTOR__CONFIG_RUNNER
+              value: "docker,trivial"
+            - name: DOCSPELL_JOEX_CONVERT_HTML__CONVERTER
+              value: "weasyprint"
+          args:
+            - "-J-Xmx3G"
+          resources:
+            limits:
+              cpu: '1'
+              memory: 3Gi
+            requests:
+              cpu: '0.5'
+              memory: 1.5Gi
\ No newline at end of file
diff --git a/kubernetes/ingress.yaml b/kubernetes/ingress.yaml
new file mode 100644
index 00000000..8798bb54
--- /dev/null
+++ b/kubernetes/ingress.yaml
@@ -0,0 +1,32 @@
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  name: docspell-ingress
+  annotations:
+    nginx.ingress.kubernetes.io/rewrite-target: /$1
+spec:
+  rules:
+    - host: docspell.example.com
+      http:
+        paths:
+          - path: /restserver(/|$)(.*)
+            pathType: Prefix
+            backend:
+              service:
+                name: docspell-restserver
+                port:
+                  name: http
+          - path: /joex(/|$)(.*)
+            pathType: Prefix
+            backend:
+              service:
+                name: docspell-joex
+                port:
+                  name: http
+          - path: /solr(/|$)(.*)
+            pathType: Prefix
+            backend:
+              service:
+                name: docspell-solr
+                port:
+                  name: solr
diff --git a/kubernetes/kustomization.yaml b/kubernetes/kustomization.yaml
new file mode 100644
index 00000000..82bdc4ea
--- /dev/null
+++ b/kubernetes/kustomization.yaml
@@ -0,0 +1,37 @@
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+metadata:
+  name: docspell
+namespace: docspell
+resources:
+- namespace.yaml
+- deployment.yaml
+- service.yaml
+- ingress.yaml
+- postgres.yaml
+- solr.yaml
+configMapGenerator:
+- literals:
+  - DOCSPELL_SERVER_BACKEND_JDBC_USER=dbuser
+  name: restserver-config
+- literals:
+  - DOCSPELL_JOEX_JDBC_USER=dbuser
+  name: joex-config
+secretGenerator:
+- literals:
+  - DOCSPELL_SERVER_BACKEND_JDBC_PASSWORD=dbpass
+  - DOCSPELL_SERVER_ADMIN__ENDPOINT_SECRET=admin123
+  - DOCSPELL_SERVER_AUTH_SERVER__SECRET=mysecret
+  - DOCSPELL_SERVER_INTEGRATION__ENDPOINT_HTTP__HEADER_HEADER__VALUE=integration-password123
+  - DOCSPELL_SERVER_BACKEND_SIGNUP_NEW__INVITE__PASSWORD=myinvitepass
+  name: restserver-secrets
+- literals:
+  - DOCSPELL_JOEX_JDBC_PASSWORD=dbpass
+  name: joex-secrets
+images:
+  - name: docspell/restserver
+    newTag: latest
+  - name: docspell/joex
+    newTag: latest
+  - name: docspell/dsc
+    newTag: latest
diff --git a/kubernetes/namespace.yaml b/kubernetes/namespace.yaml
new file mode 100644
index 00000000..c7bda5e9
--- /dev/null
+++ b/kubernetes/namespace.yaml
@@ -0,0 +1,4 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: docspell
\ No newline at end of file
diff --git a/kubernetes/postgres.yaml b/kubernetes/postgres.yaml
new file mode 100644
index 00000000..13d78e72
--- /dev/null
+++ b/kubernetes/postgres.yaml
@@ -0,0 +1,58 @@
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+  name: postgres
+spec:
+  replicas: 1
+  serviceName: postgres
+  selector:
+    matchLabels:
+      app: postgres
+  template:
+    metadata:
+      labels:
+        app: postgres
+    spec:
+      containers:
+        - name: postgres
+          image: postgres:15.2
+          env:
+            - name: POSTGRES_USER
+              valueFrom:
+                configMapKeyRef:
+                  name: restserver-config
+                  key: DOCSPELL_SERVER_BACKEND_JDBC_USER
+            - name: POSTGRES_PASSWORD
+              valueFrom:
+                secretKeyRef:
+                  name: restserver-secrets
+                  key: DOCSPELL_SERVER_BACKEND_JDBC_PASSWORD
+            - name: POSTGRES_DB
+              value: dbname
+          ports:
+            - name: postgres
+              containerPort: 5432
+          volumeMounts:
+            - name: postgres-data
+              mountPath: /var/lib/postgresql/data
+  volumeClaimTemplates:
+    - metadata:
+        name: postgres-data
+      spec:
+        accessModes: [ "ReadWriteOnce" ]
+        resources:
+          requests:
+            storage: 1Gi
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: postgres
+spec:
+  selector:
+    app: postgres
+  ports:
+    - name: postgres
+      port: 5432
+      targetPort: postgres
+  clusterIP: None
\ No newline at end of file
diff --git a/kubernetes/secret.yaml b/kubernetes/secret.yaml
new file mode 100644
index 00000000..205884c5
--- /dev/null
+++ b/kubernetes/secret.yaml
@@ -0,0 +1,12 @@
+apiVersion: v1
+kind: Secret
+metadata:
+  name: docspell-secrets
+type: Opaque
+stringData:
+  DOCSPELL_SERVER_ADMIN__ENDPOINT_SECRET: admin123
+  DOCSPELL_SERVER_AUTH_SERVER__SECRET: mysecret
+  DOCSPELL_SERVER_BACKEND_JDBC_PASSWORD: dbpass
+  DOCSPELL_SERVER_BACKEND_JDBC_USER: dbuser
+  DOCSPELL_SERVER_BACKEND_SIGNUP_NEW__INVITE__PASSWORD: myinvitepass
+  DOCSPELL_SERVER_INTEGRATION__ENDPOINT_HTTP__HEADER_HEADER__VALUE: integration-password123
diff --git a/kubernetes/service.yaml b/kubernetes/service.yaml
new file mode 100644
index 00000000..5efb775e
--- /dev/null
+++ b/kubernetes/service.yaml
@@ -0,0 +1,27 @@
+apiVersion: v1
+kind: Service
+metadata:
+  name: restserver
+spec:
+  selector:
+    app: restserver
+  ports:
+    - name: http
+      protocol: TCP
+      port: 7880
+      targetPort: 7880
+  type: ClusterIP
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: joex
+spec:
+  selector:
+    app: joex
+  ports:
+    - name: http
+      protocol: TCP
+      port: 7878
+      targetPort: 7878
+  type: ClusterIP
diff --git a/kubernetes/solr.yaml b/kubernetes/solr.yaml
new file mode 100644
index 00000000..0deef8ac
--- /dev/null
+++ b/kubernetes/solr.yaml
@@ -0,0 +1,63 @@
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+  name: solr
+  labels:
+    app: solr
+spec:
+  replicas: 1
+  serviceName: solr
+  selector:
+    matchLabels:
+      app: solr
+  template:
+    metadata:
+      labels:
+        app: solr
+    spec:
+      containers:
+        - name: solr
+          image: solr:9
+          ports:
+            - containerPort: 8983
+          command:
+            - docker-entrypoint.sh
+            - solr-precreate
+            - docspell
+          livenessProbe: # Check for a 302 response
+            httpGet:
+              path: /solr/admin/info/system
+              port: 8983
+            initialDelaySeconds: 60
+            periodSeconds: 10
+            timeoutSeconds: 5
+          readinessProbe:
+            httpGet:
+              path: /solr/admin/info/system
+              port: 8983
+            initialDelaySeconds: 10
+            periodSeconds: 5
+            timeoutSeconds: 1
+          volumeMounts:
+            - name: solr-data
+              mountPath: /var/solr
+  volumeClaimTemplates:
+    - metadata:
+        name: solr-data
+      spec:
+        accessModes: ["ReadWriteOnce"]
+        resources:
+          requests:
+            storage: 1Gi
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: solr
+spec:
+  selector:
+    app: solr
+  ports:
+  - name: solr
+    port: 8983
+    targetPort: 8983