feat(helm): complete chart normalization with ConfigMap, HPA, and OCI migration
- Added debug mode, structured ConfigMap pattern, and README to all charts - Migrated all flat-structure charts to hierarchical values under chartname key - Fixed templates to use correct hierarchical paths - Renamed helm-rustical key to rustical, added HPA support - Fixed ingress YAML parsing (oxicloud), nested env vars (rustical) - Pushed all normalized charts to OCI registry as 1.0.0+ and updated ArgoCD apps
This commit is contained in:
parent
d0f40c0967
commit
1fd2bfef3d
71 changed files with 2150 additions and 965 deletions
|
|
@ -2,15 +2,9 @@ apiVersion: v2
|
|||
name: chart
|
||||
description: A Helm chart template with standardized structure
|
||||
type: application
|
||||
version: 0.1.0
|
||||
appVersion: "latest"
|
||||
version: 1.0.2
|
||||
appVersion: latest
|
||||
annotations:
|
||||
artifacthub.io/images: |
|
||||
- name: chart
|
||||
image: nginx:latest
|
||||
artifacthub.io/changes: |
|
||||
- kind: added
|
||||
description: Initial chart template
|
||||
artifacthub.io/maintainers: |
|
||||
- name: catalin
|
||||
email: catalin@example.com
|
||||
artifacthub.io/images: "- name: chart\n image: nginx:latest\n"
|
||||
artifacthub.io/changes: "- kind: added\n description: Initial chart template\n"
|
||||
artifacthub.io/maintainers: "- name: catalin\n email: catalin@example.com\n"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ A Helm chart template with standardized structure for self-hosted services.
|
|||
## TL;DR
|
||||
|
||||
```bash
|
||||
helm install chart oci://git.roboces.dev/catalin/fukuops/chart --version 1.0.0 -n apps-roboces
|
||||
helm install chart oci://git.roboces.dev/catalin/fukuops/chart --version 1.0.1 -n apps-roboces
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
|
@ -17,64 +17,74 @@ helm install chart oci://git.roboces.dev/catalin/fukuops/chart --version 1.0.0 -
|
|||
|
||||
## Configuration
|
||||
|
||||
### Basic Configuration
|
||||
All values are nested under the `chart:` key. Example:
|
||||
|
||||
```yaml
|
||||
service:
|
||||
type: LoadBalancer
|
||||
port: 80
|
||||
ingress:
|
||||
enabled: true
|
||||
className: "traefik"
|
||||
hosts:
|
||||
- host: chart.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
```
|
||||
|
||||
### With Persistence
|
||||
|
||||
```yaml
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: "truenas-nfs-csi"
|
||||
accessMode: ReadWriteMany
|
||||
size: 10Gi
|
||||
```
|
||||
|
||||
### With Secrets
|
||||
|
||||
```yaml
|
||||
secret:
|
||||
existingSecretName: chart-secrets
|
||||
chart:
|
||||
replicaCount: 1
|
||||
service:
|
||||
type: LoadBalancer
|
||||
port: 80
|
||||
ingress:
|
||||
enabled: true
|
||||
className: "traefik"
|
||||
hosts:
|
||||
- host: chart.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
config:
|
||||
# Key-value pairs that will be mapped to env vars (uppercased)
|
||||
# Example: `FOO: "bar"` becomes env var FOO=bar
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: "truenas-nfs-csi"
|
||||
accessMode: ReadWriteMany
|
||||
size: 10Gi
|
||||
secret:
|
||||
existingSecretName: chart-secrets
|
||||
```
|
||||
|
||||
## Values Reference
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `replicaCount` | int | `1` | Number of replicas |
|
||||
| `image.repository` | string | `nginx` | Image repository |
|
||||
| `image.pullPolicy` | string | `IfNotPresent` | Image pull policy |
|
||||
| `image.tag` | string | `.Chart.AppVersion` | Image tag |
|
||||
| `service.type` | string | `LoadBalancer` | Service type |
|
||||
| `service.port` | int | `80` | Service port |
|
||||
| `service.targetPort` | int | `80` | Container port |
|
||||
| `ingress.enabled` | bool | `false` | Enable ingress |
|
||||
| `ingress.className` | string | `traefik` | Ingress class |
|
||||
| `persistence.enabled` | bool | `false` | Enable persistence |
|
||||
| `persistence.storageClass` | string | `truenas-nfs-csi` | Storage class |
|
||||
| `persistence.accessMode` | string | `ReadWriteMany` | Access mode |
|
||||
| `persistence.size` | string | `1Gi` | PVC size |
|
||||
| `persistence.name` | string | `chart-data` | PVC name |
|
||||
| `secret.existingSecretName` | string | `""` | Existing secret name |
|
||||
| `resources` | object | `{}` | Container resources |
|
||||
| `livenessProbe` | object | (see values.yaml) | Liveness probe |
|
||||
| `readinessProbe` | object | (see values.yaml) | Readiness probe |
|
||||
| `nodeSelector` | object | `{}` | Node selector |
|
||||
| `tolerations` | array | `[]` | Tolerations |
|
||||
| `affinity` | object | `{}` | Affinity rules |
|
||||
| `chart.replicaCount` | int | `1` | Number of replicas |
|
||||
| `chart.image.repository` | string | `nginx` | Image repository |
|
||||
| `chart.image.pullPolicy` | string | `IfNotPresent` | Image pull policy |
|
||||
| `chart.image.tag` | string | `.Chart.AppVersion` | Image tag |
|
||||
| `chart.service.type` | string | `LoadBalancer` | Service type |
|
||||
| `chart.service.port` | int | `80` | Service port |
|
||||
| `chart.service.targetPort` | int | `80` | Container port |
|
||||
| `chart.ingress.enabled` | bool | `false` | Enable ingress |
|
||||
| `chart.ingress.className` | string | `traefik` | Ingress class |
|
||||
| `chart.config` | object | `{}` | Config key-value pairs mapped to env vars |
|
||||
| `chart.persistence.enabled` | bool | `false` | Enable persistence |
|
||||
| `chart.persistence.storageClass` | string | `truenas-nfs-csi` | Storage class |
|
||||
| `chart.persistence.accessMode` | string | `ReadWriteMany` | Access mode |
|
||||
| `chart.persistence.size` | string | `1Gi` | PVC size |
|
||||
| `chart.persistence.name` | string | `chart-data` | PVC name |
|
||||
| `chart.secret.existingSecretName` | string | `""` | Existing secret name |
|
||||
| `chart.debug.enabled` | bool | `false` | Enable debug mode (adds init container for troubleshooting) |
|
||||
| `chart.resources` | object | `{}` | Container resources |
|
||||
| `chart.livenessProbe` | object | (see values.yaml) | Liveness probe |
|
||||
| `chart.readinessProbe` | object | (see values.yaml) | Readiness probe |
|
||||
| `chart.nodeSelector` | object | `{}` | Node selector |
|
||||
| `chart.tolerations` | array | `[]` | Tolerations |
|
||||
| `chart.affinity` | object | `{}` | Affinity rules |
|
||||
|
||||
## ConfigMap
|
||||
|
||||
The `config:` section creates a ConfigMap that maps keys to environment variables. Keys are uppercased.
|
||||
|
||||
```yaml
|
||||
chart:
|
||||
config:
|
||||
FOO: "bar"
|
||||
BAZ: "qux"
|
||||
```
|
||||
|
||||
This creates env vars `FOO=bar` and `BAZ=qux`.
|
||||
|
||||
## Persistence
|
||||
|
||||
|
|
@ -82,10 +92,28 @@ When persistence is enabled, a PVC named `chart-data` is created. The PVC uses t
|
|||
|
||||
## Secret Management
|
||||
|
||||
The chart looks for an existing `Secret` with the name specified in `secret.existingSecretName`. If not provided, no secret is mounted.
|
||||
The chart looks for an existing `Secret` with the name specified in `chart.secret.existingSecretName`. If not provided, no secret is mounted.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Enable debug mode to troubleshoot issues by adding an init container with a shell:
|
||||
|
||||
```yaml
|
||||
chart:
|
||||
debug:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
This adds an `alpine:3.19` init container with `sleep infinity` that mounts all volumes and inherits env vars. You can exec into it to inspect the environment:
|
||||
|
||||
```bash
|
||||
kubectl exec -it <pod-name> -c debug -- sh
|
||||
```
|
||||
|
||||
When debug mode is enabled, liveness and readiness probes are disabled to prevent restarts.
|
||||
|
||||
### Pod not starting
|
||||
|
||||
```bash
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
{{- if .Values.config }}
|
||||
{{- if .Values.chart.config }}
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
|
|
@ -6,7 +6,8 @@ metadata:
|
|||
labels:
|
||||
{{- include "labels" . | nindent 4 }}
|
||||
data:
|
||||
{{- range $key, $value := .Values.config }}
|
||||
{{ $key }}: {{ $value | quote }}
|
||||
{{- range $key, $value := .Values.chart.config }}
|
||||
{{- $envKey := $key | upper }}
|
||||
{{ $envKey }}: {{ $value | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ metadata:
|
|||
labels:
|
||||
{{- include "labels" . | nindent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
replicas: {{ .Values.chart.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "selectorLabels" . | nindent 6 }}
|
||||
|
|
@ -14,55 +14,90 @@ spec:
|
|||
labels:
|
||||
{{- include "selectorLabels" . | nindent 8 }}
|
||||
spec:
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: {{ .Values.service.targetPort }}
|
||||
protocol: TCP
|
||||
{{- if .Values.config }}
|
||||
{{- with .Values.chart.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.chart.debug.enabled }}
|
||||
initContainers:
|
||||
- name: debug
|
||||
image: alpine:3.19
|
||||
command: ["sleep", "infinity"]
|
||||
envFrom:
|
||||
{{- if .Values.chart.config }}
|
||||
- configMapRef:
|
||||
name: {{ include "fullname" . }}-config
|
||||
{{- end }}
|
||||
{{- if .Values.secret.existingSecretName }}
|
||||
envFrom:
|
||||
{{- end }}
|
||||
{{- if .Values.chart.secret.existingSecretName }}
|
||||
- secretRef:
|
||||
name: {{ .Values.secret.existingSecretName }}
|
||||
{{- end }}
|
||||
{{- if .Values.persistence.enabled }}
|
||||
name: {{ .Values.chart.secret.existingSecretName }}
|
||||
{{- end }}
|
||||
{{- if .Values.chart.persistence.enabled }}
|
||||
volumeMounts:
|
||||
- name: {{ include "pvcName" . }}
|
||||
mountPath: /data
|
||||
{{- end }}
|
||||
{{- with .Values.resources }}
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: false
|
||||
capabilities:
|
||||
drop:
|
||||
- all
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
image: "{{ .Values.chart.image.repository }}:{{ .Values.chart.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.chart.image.pullPolicy }}
|
||||
{{- with .Values.chart.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: {{ .Values.chart.service.targetPort }}
|
||||
protocol: TCP
|
||||
envFrom:
|
||||
{{- if .Values.chart.config }}
|
||||
- configMapRef:
|
||||
name: {{ include "fullname" . }}-config
|
||||
{{- end }}
|
||||
{{- if .Values.chart.secret.existingSecretName }}
|
||||
- secretRef:
|
||||
name: {{ .Values.chart.secret.existingSecretName }}
|
||||
{{- end }}
|
||||
{{- if .Values.chart.persistence.enabled }}
|
||||
volumeMounts:
|
||||
- name: {{ include "pvcName" . }}
|
||||
mountPath: /data
|
||||
{{- end }}
|
||||
{{- with .Values.chart.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.livenessProbe }}
|
||||
{{- if not .Values.chart.debug.enabled }}
|
||||
{{- with .Values.chart.livenessProbe }}
|
||||
livenessProbe:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.readinessProbe }}
|
||||
{{- with .Values.chart.readinessProbe }}
|
||||
readinessProbe:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if not .Values.persistence.enabled }}
|
||||
{{- end }}
|
||||
{{- if not .Values.chart.persistence.enabled }}
|
||||
volumes:
|
||||
- name: {{ include "pvcName" . }}
|
||||
emptyDir: {}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
{{- with .Values.chart.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
{{- with .Values.chart.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
{{- with .Values.chart.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
{{- if .Values.ingress.enabled }}
|
||||
{{- if .Values.chart.ingress.enabled }}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ include "fullname" . }}
|
||||
labels:
|
||||
{{- include "labels" . | nindent 4 }}
|
||||
{{- with .Values.ingress.annotations }}
|
||||
{{- with .Values.chart.ingress.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .Values.ingress.className }}
|
||||
ingressClassName: {{ .Values.ingress.className }}
|
||||
{{- if .Values.chart.ingress.className }}
|
||||
ingressClassName: {{ .Values.chart.ingress.className }}
|
||||
{{- end }}
|
||||
{{- if .Values.ingress.tls }}
|
||||
{{- if .Values.chart.ingress.tls }}
|
||||
tls:
|
||||
{{- range .Values.ingress.tls }}
|
||||
{{- range .Values.chart.ingress.tls }}
|
||||
- hosts:
|
||||
{{- range .hosts }}
|
||||
- {{ . | quote }}
|
||||
|
|
@ -24,7 +24,7 @@ spec:
|
|||
{{- end }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- range .Values.ingress.hosts }}
|
||||
{{- range .Values.chart.ingress.hosts }}
|
||||
- host: {{ .host | quote }}
|
||||
http:
|
||||
paths:
|
||||
|
|
@ -35,7 +35,7 @@ spec:
|
|||
service:
|
||||
name: {{ include "fullname" $ }}
|
||||
port:
|
||||
number: {{ $.Values.service.port }}
|
||||
number: {{ $.Values.chart.service.port }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
|
|||
18
k8s/charts/_template/templates/pdb.yaml
Normal file
18
k8s/charts/_template/templates/pdb.yaml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{{- if .Values.chart.podDisruptionBudget.enabled }}
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: {{ include "fullname" . }}
|
||||
labels:
|
||||
{{- include "labels" . | nindent 4 }}
|
||||
spec:
|
||||
{{- if .Values.chart.podDisruptionBudget.minAvailable }}
|
||||
minAvailable: {{ .Values.chart.podDisruptionBudget.minAvailable }}
|
||||
{{- end }}
|
||||
{{- if .Values.chart.podDisruptionBudget.maxUnavailable }}
|
||||
maxUnavailable: {{ .Values.chart.podDisruptionBudget.maxUnavailable }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "selectorLabels" . | nindent 6 }}
|
||||
{{- end }}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
{{- if .Values.persistence.enabled }}
|
||||
{{- if .Values.chart.persistence.enabled }}
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
|
|
@ -7,11 +7,11 @@ metadata:
|
|||
{{- include "labels" . | nindent 4 }}
|
||||
spec:
|
||||
accessModes:
|
||||
- {{ .Values.persistence.accessMode | default "ReadWriteMany" }}
|
||||
{{- if .Values.persistence.storageClass }}
|
||||
storageClassName: {{ .Values.persistence.storageClass }}
|
||||
- {{ .Values.chart.persistence.accessMode | default "ReadWriteMany" }}
|
||||
{{- if .Values.chart.persistence.storageClass }}
|
||||
storageClassName: {{ .Values.chart.persistence.storageClass }}
|
||||
{{- end }}
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.persistence.size | default "1Gi" }}
|
||||
storage: {{ .Values.chart.persistence.size | default "1Gi" }}
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
{{- if and .Values.secret.enabled (not .Values.secret.existingSecretName) .Values.secret.data }}
|
||||
{{- if and .Values.chart.secret.enabled (not .Values.chart.secret.existingSecretName) .Values.chart.secret.data }}
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
|
|
@ -7,7 +7,7 @@ metadata:
|
|||
{{- include "labels" . | nindent 4 }}
|
||||
type: Opaque
|
||||
data:
|
||||
{{- range $key, $value := .Values.secret.data }}
|
||||
{{- range $key, $value := .Values.chart.secret.data }}
|
||||
{{ $key }}: {{ $value | b64enc | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ metadata:
|
|||
labels:
|
||||
{{- include "labels" . | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
type: {{ .Values.chart.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
- port: {{ .Values.chart.service.port }}
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
|
|
|
|||
|
|
@ -1,60 +1,71 @@
|
|||
replicaCount: 1
|
||||
chart:
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: nginx
|
||||
pullPolicy: IfNotPresent
|
||||
tag: ""
|
||||
image:
|
||||
repository: nginx
|
||||
pullPolicy: IfNotPresent
|
||||
tag: ""
|
||||
|
||||
service:
|
||||
type: LoadBalancer
|
||||
port: 80
|
||||
targetPort: 80
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
className: "traefik"
|
||||
annotations: {}
|
||||
hosts:
|
||||
- host: chart.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls: []
|
||||
|
||||
config: {}
|
||||
|
||||
persistence:
|
||||
enabled: false
|
||||
storageClass: "truenas-nfs-csi"
|
||||
accessMode: ReadWriteMany
|
||||
size: 1Gi
|
||||
name: "chart-data"
|
||||
|
||||
resources: {}
|
||||
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
service:
|
||||
type: LoadBalancer
|
||||
port: 80
|
||||
initialDelaySeconds: 30
|
||||
timeoutSeconds: 15
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
targetPort: 80
|
||||
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 80
|
||||
initialDelaySeconds: 15
|
||||
timeoutSeconds: 2
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
ingress:
|
||||
enabled: false
|
||||
className: "traefik"
|
||||
annotations: {}
|
||||
hosts:
|
||||
- host: chart.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls: []
|
||||
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
||||
config: {}
|
||||
|
||||
secret:
|
||||
enabled: true
|
||||
existingSecretName: ""
|
||||
data: {}
|
||||
persistence:
|
||||
enabled: false
|
||||
storageClass: "truenas-nfs-csi"
|
||||
accessMode: ReadWriteMany
|
||||
size: 1Gi
|
||||
name: "chart-data"
|
||||
|
||||
resources: {}
|
||||
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: 80
|
||||
initialDelaySeconds: 30
|
||||
timeoutSeconds: 15
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 80
|
||||
initialDelaySeconds: 15
|
||||
timeoutSeconds: 2
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
||||
|
||||
secret:
|
||||
enabled: true
|
||||
existingSecretName: ""
|
||||
data: {}
|
||||
|
||||
podSecurityContext: {}
|
||||
securityContext: {}
|
||||
|
||||
podDisruptionBudget:
|
||||
enabled: false
|
||||
minAvailable: 1
|
||||
|
||||
debug:
|
||||
enabled: false
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ apiVersion: v2
|
|||
name: dokuwiki
|
||||
description: A Helm chart for DokuWiki
|
||||
type: application
|
||||
version: 0.1.0
|
||||
version: 1.0.2
|
||||
appVersion: 2024-02-06b
|
||||
annotations:
|
||||
artifacthub.io/images: "- name: dokuwiki\n image: dokuwiki/dokuwiki:2024-02-06b\n"
|
||||
|
|
|
|||
112
k8s/charts/dokuwiki/README.md
Normal file
112
k8s/charts/dokuwiki/README.md
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
# dokuwiki
|
||||
|
||||
A Helm chart for DokuWiki, a simple to use and highly versatile wiki software.
|
||||
|
||||
## TL;DR
|
||||
|
||||
```bash
|
||||
helm install dokuwiki oci://git.roboces.dev/catalin/fukuops/dokuwiki --version 1.0.2 -n apps-roboces
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Kubernetes 1.19+
|
||||
- Helm 3+
|
||||
- A NFS storage class (default: `truenas-nfs-csi`)
|
||||
|
||||
## Configuration
|
||||
|
||||
All values are nested under the `dokuwiki:` key. Example:
|
||||
|
||||
```yaml
|
||||
dokuwiki:
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: "truenas-nfs-csi"
|
||||
accessMode: ReadWriteMany
|
||||
size: 2Gi
|
||||
service:
|
||||
type: LoadBalancer
|
||||
port: 8004
|
||||
ingress:
|
||||
enabled: true
|
||||
hosts:
|
||||
- host: wiki.roboces.dev
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
config:
|
||||
php:
|
||||
timezone: Europe/Madrid
|
||||
memoryLimit: 512M
|
||||
uploadLimit: 128M
|
||||
```
|
||||
|
||||
## Values Reference
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `dokuwiki.replicaCount` | int | `1` | Number of replicas |
|
||||
| `dokuwiki.image.repository` | string | `dokuwiki/dokuwiki` | Image repository |
|
||||
| `dokuwiki.image.pullpolicy` | string | `IfNotPresent` | Image pull policy |
|
||||
| `dokuwiki.image.tag` | string | `.Chart.AppVersion` | Image tag |
|
||||
| `dokuwiki.service.type` | string | `LoadBalancer` | Service type |
|
||||
| `dokuwiki.service.port` | int | `8004` | Service port |
|
||||
| `dokuwiki.service.targetPort` | int | `8080` | Container port |
|
||||
| `dokuwiki.ingress.enabled` | bool | `true` | Enable ingress |
|
||||
| `dokuwiki.ingress.className` | string | `""` | Ingress class |
|
||||
| `dokuwiki.ingress.hosts[0].host` | string | `wiki.roboces.dev` | Ingress hostname |
|
||||
| `dokuwiki.persistence.enabled` | bool | `true` | Enable persistence |
|
||||
| `dokuwiki.persistence.storageClass` | string | `truenas-nfs-csi` | Storage class |
|
||||
| `dokuwiki.persistence.accessMode` | string | `ReadWriteMany` | Access mode |
|
||||
| `dokuwiki.persistence.size` | string | `2Gi` | PVC size |
|
||||
| `dokuwiki.config.php.timezone` | string | `Europe/Madrid` | PHP timezone |
|
||||
| `dokuwiki.config.php.memoryLimit` | string | `512M` | PHP memory limit |
|
||||
| `dokuwiki.config.php.uploadLimit` | string | `128M` | PHP upload limit |
|
||||
| `dokuwiki.debug.enabled` | bool | `false` | Enable debug mode (adds init container for troubleshooting) |
|
||||
| `dokuwiki.resources` | object | `{}` | Container resources |
|
||||
| `dokuwiki.livenessProbe` | object | (see values.yaml) | Liveness probe |
|
||||
| `dokuwiki.readinessProbe` | object | (see values.yaml) | Readiness probe |
|
||||
|
||||
## ConfigMap
|
||||
|
||||
The chart creates a ConfigMap with PHP environment variables:
|
||||
- `PHP_TIMEZONE`
|
||||
- `PHP_MEMORYLIMIT`
|
||||
- `PHP_UPLOADLIMIT`
|
||||
|
||||
## Persistence
|
||||
|
||||
The chart creates a PVC named `dokuwiki-data`. The PVC uses the `truenas-nfs-csi` storage class by default with `ReadWriteMany` access mode.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Enable debug mode to troubleshoot issues by adding an init container with a shell:
|
||||
|
||||
```yaml
|
||||
dokuwiki:
|
||||
debug:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
This adds an `alpine:3.19` init container with `sleep infinity` that mounts all volumes and inherits env vars. You can exec into it to inspect the environment:
|
||||
|
||||
```bash
|
||||
kubectl exec -it <pod-name> -c debug -- sh
|
||||
```
|
||||
|
||||
When debug mode is enabled, liveness and readiness probes are disabled to prevent restarts.
|
||||
|
||||
### Pod not starting
|
||||
|
||||
```bash
|
||||
kubectl get pvc -n apps-roboces -l app.kubernetes.io/name=dokuwiki
|
||||
```
|
||||
|
||||
### Check logs
|
||||
|
||||
```bash
|
||||
kubectl logs dokuwiki-0 -n apps-roboces
|
||||
```
|
||||
10
k8s/charts/dokuwiki/templates/configmap.yaml
Normal file
10
k8s/charts/dokuwiki/templates/configmap.yaml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "dokuwiki.fullname" . }}-config
|
||||
labels:
|
||||
{{- include "dokuwiki.labels" . | nindent 4 }}
|
||||
data:
|
||||
PHP_TIMEZONE: {{ .Values.dokuwiki.config.php.timezone | quote }}
|
||||
PHP_MEMORYLIMIT: {{ .Values.dokuwiki.config.php.memoryLimit | quote }}
|
||||
PHP_UPLOADLIMIT: {{ .Values.dokuwiki.config.php.uploadLimit | quote }}
|
||||
|
|
@ -1,23 +1,23 @@
|
|||
{{- if .Values.ingress.enabled -}}
|
||||
{{- if .Values.dokuwiki.ingress.enabled -}}
|
||||
{{- $fullName := include "dokuwiki.fullname" . -}}
|
||||
{{- $svcPort := .Values.service.port -}}
|
||||
{{- $svcPort := .Values.dokuwiki.service.port -}}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ $fullName }}
|
||||
labels:
|
||||
{{- include "dokuwiki.labels" . | nindent 4 }}
|
||||
{{- with .Values.ingress.annotations }}
|
||||
{{- with .Values.dokuwiki.ingress.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .Values.ingress.className }}
|
||||
ingressClassName: {{ .Values.ingress.className }}
|
||||
{{- if .Values.dokuwiki.ingress.className }}
|
||||
ingressClassName: {{ .Values.dokuwiki.ingress.className }}
|
||||
{{- end }}
|
||||
{{- if .Values.ingress.tls }}
|
||||
{{- if .Values.dokuwiki.ingress.tls }}
|
||||
tls:
|
||||
{{- range .Values.ingress.tls }}
|
||||
{{- range .Values.dokuwiki.ingress.tls }}
|
||||
- hosts:
|
||||
{{- range .hosts }}
|
||||
- {{ . | quote }}
|
||||
|
|
@ -26,7 +26,7 @@ spec:
|
|||
{{- end }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- range .Values.ingress.hosts }}
|
||||
{{- range .Values.dokuwiki.ingress.hosts }}
|
||||
- host: {{ .host | quote }}
|
||||
http:
|
||||
paths:
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ metadata:
|
|||
labels:
|
||||
{{- include "dokuwiki.labels" . | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
type: {{ .Values.dokuwiki.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: {{ .Values.service.targetPort }}
|
||||
- port: {{ .Values.dokuwiki.service.port }}
|
||||
targetPort: {{ .Values.dokuwiki.service.targetPort }}
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
|
|
|
|||
|
|
@ -6,86 +6,84 @@ metadata:
|
|||
{{- include "dokuwiki.labels" . | nindent 4 }}
|
||||
spec:
|
||||
serviceName: {{ include "dokuwiki.fullname" . }}-headless
|
||||
{{- if not .Values.autoscaling.enabled }}
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
{{- if not .Values.dokuwiki.autoscaling.enabled }}
|
||||
replicas: {{ .Values.dokuwiki.replicaCount }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "dokuwiki.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
{{- with .Values.podAnnotations }}
|
||||
{{- with .Values.dokuwiki.podAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "dokuwiki.selectorLabels" . | nindent 8 }}
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
{{- with .Values.dokuwiki.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||||
{{- toYaml .Values.dokuwiki.podSecurityContext | nindent 8 }}
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
env:
|
||||
{{- range $key, $value := .Values.env }}
|
||||
- name: {{ $key }}
|
||||
value: {{ $value | quote }}
|
||||
{{- end }}
|
||||
{{- toYaml .Values.dokuwiki.securityContext | nindent 12 }}
|
||||
image: "{{ .Values.dokuwiki.image.repository }}:{{ .Values.dokuwiki.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.dokuwiki.image.pullPolicy }}
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: {{ include "dokuwiki.fullname" . }}-config
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8080
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
{{- toYaml .Values.livenessProbe | nindent 12 }}
|
||||
{{- toYaml .Values.dokuwiki.livenessProbe | nindent 12 }}
|
||||
readinessProbe:
|
||||
{{- toYaml .Values.readinessProbe | nindent 12 }}
|
||||
{{- toYaml .Values.dokuwiki.readinessProbe | nindent 12 }}
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
{{- toYaml .Values.dokuwiki.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /storage
|
||||
{{- if .Values.persistence.existingClaim }}
|
||||
{{- if .Values.dokuwiki.persistence.existingClaim }}
|
||||
volumes:
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ .Values.persistence.existingClaim }}
|
||||
claimName: {{ .Values.dokuwiki.persistence.existingClaim }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
{{- with .Values.dokuwiki.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
{{- with .Values.dokuwiki.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
{{- with .Values.dokuwiki.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
restartPolicy: Always
|
||||
automountServiceAccountToken: false
|
||||
{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }}
|
||||
restartPolicy: Always
|
||||
automountServiceAccountToken: false
|
||||
{{- if and .Values.dokuwiki.persistence.enabled (not .Values.dokuwiki.persistence.existingClaim) }}
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: data
|
||||
spec:
|
||||
accessModes:
|
||||
- {{ .Values.persistence.accessMode }}
|
||||
{{- if .Values.persistence.storageClass }}
|
||||
{{- if eq "-" .Values.persistence.storageClass }}
|
||||
- {{ .Values.dokuwiki.persistence.accessMode }}
|
||||
{{- if .Values.dokuwiki.persistence.storageClass }}
|
||||
{{- if eq "-" .Values.dokuwiki.persistence.storageClass }}
|
||||
storageClassName: ""
|
||||
{{- else }}
|
||||
storageClassName: {{ .Values.persistence.storageClass | quote }}
|
||||
storageClassName: {{ .Values.dokuwiki.persistence.storageClass | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.persistence.size }}
|
||||
storage: {{ .Values.dokuwiki.persistence.size }}
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -1,66 +1,71 @@
|
|||
replicaCount: 1
|
||||
dokuwiki:
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: dokuwiki/dokuwiki
|
||||
pullpolicy: IfNotPresent
|
||||
tag: ""
|
||||
image:
|
||||
repository: dokuwiki/dokuwiki
|
||||
pullpolicy: IfNotPresent
|
||||
tag: ""
|
||||
|
||||
imagePullSecrets: []
|
||||
podAnnotations: {}
|
||||
podSecurityContext: {}
|
||||
securityContext: {}
|
||||
imagePullSecrets: []
|
||||
podAnnotations: {}
|
||||
podSecurityContext: {}
|
||||
securityContext: {}
|
||||
|
||||
service:
|
||||
type: LoadBalancer
|
||||
port: 8004
|
||||
targetPort: 8080
|
||||
service:
|
||||
type: LoadBalancer
|
||||
port: 8004
|
||||
targetPort: 8080
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
className: ""
|
||||
annotations: {}
|
||||
hosts:
|
||||
- host: wiki.roboces.dev
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls: []
|
||||
ingress:
|
||||
enabled: true
|
||||
className: ""
|
||||
annotations: {}
|
||||
hosts:
|
||||
- host: wiki.roboces.dev
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
tls: []
|
||||
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: "truenas-nfs-csi"
|
||||
accessMode: ReadWriteMany
|
||||
size: 2Gi
|
||||
existingClaim: ""
|
||||
config:
|
||||
php:
|
||||
timezone: Europe/Madrid
|
||||
memoryLimit: 512M
|
||||
uploadLimit: 128M
|
||||
|
||||
autoscaling:
|
||||
enabled: false
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: "truenas-nfs-csi"
|
||||
accessMode: ReadWriteMany
|
||||
size: 2Gi
|
||||
existingClaim: ""
|
||||
|
||||
resources: {}
|
||||
autoscaling:
|
||||
enabled: false
|
||||
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: 8080
|
||||
initialDelaySeconds: 30
|
||||
timeoutSeconds: 15
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
resources: {}
|
||||
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 8080
|
||||
initialDelaySeconds: 15
|
||||
timeoutSeconds: 2
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: 8080
|
||||
initialDelaySeconds: 30
|
||||
timeoutSeconds: 15
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 8080
|
||||
initialDelaySeconds: 15
|
||||
timeoutSeconds: 2
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
|
||||
env:
|
||||
PHP_TIMEZONE: Europe/Madrid
|
||||
PHP_MEMORYLIMIT: 512M
|
||||
PHP_UPLOADLIMIT: 128M
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
||||
|
||||
debug:
|
||||
enabled: false
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ apiVersion: v2
|
|||
name: helm-rustical
|
||||
description: A Helm chart for Rustical WebDAV server
|
||||
type: application
|
||||
version: 0.3.0
|
||||
version: 1.1.1
|
||||
appVersion: 0.14.1
|
||||
annotations:
|
||||
artifacthub.io/images: "- name: rustical\n image: ghcr.io/lennart-k/rustical:0.14.1\n"
|
||||
|
|
|
|||
115
k8s/charts/helm-rustical/README.md
Normal file
115
k8s/charts/helm-rustical/README.md
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
# helm-rustical
|
||||
|
||||
A Helm chart for Rustical WebDAV server.
|
||||
|
||||
## TL;DR
|
||||
|
||||
```bash
|
||||
helm install helm-rustical oci://git.roboces.dev/catalin/fukuops/helm-rustical --version 1.1.0 -n apps-roboces
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Kubernetes 1.19+
|
||||
- Helm 3+
|
||||
- A NFS storage class (default: `truenas-nfs-csi`)
|
||||
- An existing `Secret` containing:
|
||||
- `RUSTICAL_PASSWORD` - Password for WebDAV access
|
||||
|
||||
## Configuration
|
||||
|
||||
All values are nested under the `rustical:` key. Example:
|
||||
|
||||
```yaml
|
||||
rustical:
|
||||
autoscaling:
|
||||
enabled: true
|
||||
minReplicas: 1
|
||||
maxReplicas: 3
|
||||
targetCPUUtilizationPercentage: 80
|
||||
service:
|
||||
type: LoadBalancer
|
||||
port: 8001
|
||||
config:
|
||||
oidc.name: Authentik
|
||||
oidc.issuer: https://auth.example.com/application/o/rustical/
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: "truenas-nfs-csi"
|
||||
size: 50Gi
|
||||
secret:
|
||||
existingSecretName: rustical
|
||||
```
|
||||
|
||||
## Values Reference
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `rustical.replicaCount` | int | `1` | Number of replicas (when autoscaling disabled) |
|
||||
| `rustical.image.repository` | string | `ghcr.io/lennart-k/rustical` | Image repository |
|
||||
| `rustical.image.pullpolicy` | string | `Always` | Image pull policy |
|
||||
| `rustical.image.tag` | string | `.Chart.AppVersion` | Image tag |
|
||||
| `rustical.service.type` | string | `ClusterIP` | Service type |
|
||||
| `rustical.service.port` | int | `4000` | Service port |
|
||||
| `rustical.service.targetPort` | int | `4000` | Container port |
|
||||
| `rustical.ingress.enabled` | bool | `false` | Enable ingress |
|
||||
| `rustical.autoscaling.enabled` | bool | `false` | Enable HPA |
|
||||
| `rustical.autoscaling.minReplicas` | int | `1` | Minimum replicas |
|
||||
| `rustical.autoscaling.maxReplicas` | int | `3` | Maximum replicas |
|
||||
| `rustical.autoscaling.targetCPUUtilizationPercentage` | int | `80` | Target CPU utilization |
|
||||
| `rustical.autoscaling.targetMemoryUtilizationPercentage` | int | `80` | Target memory utilization |
|
||||
| `rustical.persistence.enabled` | bool | `true` | Enable persistence |
|
||||
| `rustical.persistence.storageClass` | string | `truenas-nfs-csi` | Storage class |
|
||||
| `rustical.persistence.accessMode` | string | `ReadWriteMany` | Access mode |
|
||||
| `rustical.persistence.size` | string | `50Gi` | PVC size |
|
||||
| `rustical.secret.existingSecretName` | string | `rustical` | Existing secret name |
|
||||
| `rustical.config` | object | `{}` | Config key-value pairs mapped to env vars |
|
||||
| `rustical.debug.enabled` | bool | `false` | Enable debug mode (adds init container for troubleshooting) |
|
||||
| `rustical.resources` | object | `{}` | Container resources |
|
||||
| `rustical.livenessProbe` | object | (see values.yaml) | Liveness probe |
|
||||
| `rustical.readinessProbe` | object | (see values.yaml) | Readiness probe |
|
||||
|
||||
## ConfigMap
|
||||
|
||||
The `config:` section creates a ConfigMap that maps keys to environment variables. Keys are uppercased.
|
||||
|
||||
## Persistence
|
||||
|
||||
The chart creates a PVC named `rustical-data`. The PVC uses the `truenas-nfs-csi` storage class by default with `ReadWriteMany` access mode.
|
||||
|
||||
## Secret Management
|
||||
|
||||
The chart looks for an existing `Secret` with the name specified in `rustical.secret.existingSecretName`. Required secret keys:
|
||||
- `RUSTICAL_PASSWORD` - Password for WebDAV access
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Enable debug mode to troubleshoot issues by adding an init container with a shell:
|
||||
|
||||
```yaml
|
||||
rustical:
|
||||
debug:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
This adds an `alpine:3.19` init container with `sleep infinity` that mounts all volumes and inherits env vars. You can exec into it to inspect the environment:
|
||||
|
||||
```bash
|
||||
kubectl exec -it <pod-name> -c debug -- sh
|
||||
```
|
||||
|
||||
When debug mode is enabled, liveness and readiness probes are disabled to prevent restarts.
|
||||
|
||||
### Pod not starting
|
||||
|
||||
```bash
|
||||
kubectl get pvc -n apps-roboces -l app.kubernetes.io/name=helm-rustical
|
||||
```
|
||||
|
||||
### Check logs
|
||||
|
||||
```bash
|
||||
kubectl logs helm-rustical-0 -n apps-roboces
|
||||
```
|
||||
13
k8s/charts/helm-rustical/templates/configmap.yaml
Normal file
13
k8s/charts/helm-rustical/templates/configmap.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{{- if .Values.rustical.config }}
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "rustical.fullname" . }}-config
|
||||
labels:
|
||||
{{- include "rustical.labels" . | nindent 4 }}
|
||||
data:
|
||||
{{- range $key, $value := .Values.rustical.config }}
|
||||
{{- $envKey := printf "RUSTICAL_%s" ($key | upper | replace "." "__" | replace "-" "_") }}
|
||||
{{ $envKey }}: {{ $value | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
36
k8s/charts/helm-rustical/templates/hpa.yaml
Normal file
36
k8s/charts/helm-rustical/templates/hpa.yaml
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
{{- if .Values.rustical.autoscaling.enabled }}
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ include "rustical.fullname" . }}
|
||||
labels:
|
||||
{{- include "rustical.labels" . | nindent 4 }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
name: {{ include "rustical.fullname" . }}
|
||||
{{- if .Values.rustical.autoscaling.minReplicas }}
|
||||
minReplicas: {{ .Values.rustical.autoscaling.minReplicas }}
|
||||
{{- end }}
|
||||
{{- if .Values.rustical.autoscaling.maxReplicas }}
|
||||
maxReplicas: {{ .Values.rustical.autoscaling.maxReplicas }}
|
||||
{{- end }}
|
||||
metrics:
|
||||
{{- if .Values.rustical.autoscaling.targetCPUUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: {{ .Values.rustical.autoscaling.targetCPUUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- if .Values.rustical.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: {{ .Values.rustical.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
@ -1,23 +1,23 @@
|
|||
{{- if .Values.ingress.enabled -}}
|
||||
{{- if .Values.rustical.ingress.enabled -}}
|
||||
{{- $fullName := include "rustical.fullname" . -}}
|
||||
{{- $svcPort := .Values.service.port -}}
|
||||
{{- $svcPort := .Values.rustical.service.port -}}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ $fullName }}
|
||||
labels:
|
||||
{{- include "rustical.labels" . | nindent 4 }}
|
||||
{{- with .Values.ingress.annotations }}
|
||||
{{- with .Values.rustical.ingress.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .Values.ingress.className }}
|
||||
ingressClassName: {{ .Values.ingress.className }}
|
||||
{{- if .Values.rustical.ingress.className }}
|
||||
ingressClassName: {{ .Values.rustical.ingress.className }}
|
||||
{{- end }}
|
||||
{{- if .Values.ingress.tls }}
|
||||
{{- if .Values.rustical.ingress.tls }}
|
||||
tls:
|
||||
{{- range .Values.ingress.tls }}
|
||||
{{- range .Values.rustical.ingress.tls }}
|
||||
- hosts:
|
||||
{{- range .hosts }}
|
||||
- {{ . | quote }}
|
||||
|
|
@ -26,7 +26,7 @@ spec:
|
|||
{{- end }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- range .Values.ingress.hosts }}
|
||||
{{- range .Values.rustical.ingress.hosts }}
|
||||
- host: {{ .host | quote }}
|
||||
http:
|
||||
paths:
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ metadata:
|
|||
labels:
|
||||
{{- include "rustical.labels" . | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
type: {{ .Values.rustical.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: {{ .Values.service.targetPort }}
|
||||
- port: {{ .Values.rustical.service.port }}
|
||||
targetPort: {{ .Values.rustical.service.targetPort }}
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
|
|
|
|||
|
|
@ -6,40 +6,35 @@ metadata:
|
|||
{{- include "rustical.labels" . | nindent 4 }}
|
||||
spec:
|
||||
serviceName: {{ include "rustical.fullname" . }}-headless
|
||||
{{- if not .Values.autoscaling.enabled }}
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
{{- if not .Values.rustical.autoscaling.enabled }}
|
||||
replicas: {{ .Values.rustical.replicaCount }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "rustical.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
{{- with .Values.podAnnotations }}
|
||||
{{- with .Values.rustical.podAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "rustical.selectorLabels" . | nindent 8 }}
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
{{- with .Values.rustical.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||||
{{- if .Values.debug.enabled }}
|
||||
{{- toYaml .Values.rustical.podSecurityContext | nindent 8 }}
|
||||
{{- if .Values.rustical.debug.enabled }}
|
||||
initContainers:
|
||||
- name: debug
|
||||
image: alpine:3.19
|
||||
command: ["sleep", "infinity"]
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: {{ .Values.secret.existingSecretName }}
|
||||
env:
|
||||
{{- range $key, $value := .Values.env }}
|
||||
- name: {{ $key }}
|
||||
value: {{ $value | quote }}
|
||||
{{- end }}
|
||||
name: {{ .Values.rustical.secret.existingSecretName }}
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /var/lib/rustical
|
||||
|
|
@ -53,67 +48,64 @@ spec:
|
|||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.image.pullpolicy }}
|
||||
{{- toYaml .Values.rustical.securityContext | nindent 12 }}
|
||||
image: "{{ .Values.rustical.image.repository }}:{{ .Values.rustical.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.rustical.image.pullpolicy }}
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: {{ include "rustical.fullname" . }}-config
|
||||
- secretRef:
|
||||
name: {{ .Values.secret.existingSecretName | default (include "rustical.fullname" .) }}
|
||||
env:
|
||||
{{- range $key, $value := .Values.env }}
|
||||
- name: {{ $key }}
|
||||
value: {{ $value | quote }}
|
||||
{{- end }}
|
||||
name: {{ .Values.rustical.secret.existingSecretName | default (include "rustical.fullname" .) }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 4000
|
||||
protocol: TCP
|
||||
{{- if not .Values.debug.enabled }}
|
||||
{{- if not .Values.rustical.debug.enabled }}
|
||||
livenessProbe:
|
||||
{{- toYaml .Values.livenessProbe | nindent 12 }}
|
||||
{{- toYaml .Values.rustical.livenessProbe | nindent 12 }}
|
||||
readinessProbe:
|
||||
{{- toYaml .Values.readinessProbe | nindent 12 }}
|
||||
{{- toYaml .Values.rustical.readinessProbe | nindent 12 }}
|
||||
{{- end }}
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
{{- toYaml .Values.rustical.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /var/lib/rustical
|
||||
{{- if .Values.persistence.existingClaim }}
|
||||
{{- if .Values.rustical.persistence.existingClaim }}
|
||||
volumes:
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ .Values.persistence.existingClaim }}
|
||||
claimName: {{ .Values.rustical.persistence.existingClaim }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
{{- with .Values.rustical.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
{{- with .Values.rustical.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
{{- with .Values.rustical.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
restartPolicy: Always
|
||||
automountServiceAccountToken: false
|
||||
{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }}
|
||||
{{- if and .Values.rustical.persistence.enabled (not .Values.rustical.persistence.existingClaim) }}
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: data
|
||||
spec:
|
||||
accessModes:
|
||||
- {{ .Values.persistence.accessMode }}
|
||||
{{- if .Values.persistence.storageClass }}
|
||||
{{- if eq "-" .Values.persistence.storageClass }}
|
||||
- {{ .Values.rustical.persistence.accessMode }}
|
||||
{{- if .Values.rustical.persistence.storageClass }}
|
||||
{{- if eq "-" .Values.rustical.persistence.storageClass }}
|
||||
storageClassName: ""
|
||||
{{- else }}
|
||||
storageClassName: {{ .Values.persistence.storageClass | quote }}
|
||||
storageClassName: {{ .Values.rustical.persistence.storageClass | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.persistence.size }}
|
||||
storage: {{ .Values.rustical.persistence.size }}
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -1,63 +1,67 @@
|
|||
# Configuration is managed in k8s/argo-apps/rustical.yaml
|
||||
replicaCount: 1
|
||||
rustical:
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: ghcr.io/lennart-k/rustical
|
||||
pullpolicy: Always
|
||||
tag: ""
|
||||
image:
|
||||
repository: ghcr.io/lennart-k/rustical
|
||||
pullpolicy: Always
|
||||
tag: ""
|
||||
|
||||
imagePullSecrets: []
|
||||
podAnnotations: {}
|
||||
podSecurityContext: {}
|
||||
securityContext: {}
|
||||
imagePullSecrets: []
|
||||
podAnnotations: {}
|
||||
podSecurityContext: {}
|
||||
securityContext: {}
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 4000
|
||||
targetPort: 4000
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: "truenas-nfs-csi"
|
||||
accessMode: ReadWriteMany
|
||||
size: 50Gi
|
||||
existingClaim: ""
|
||||
|
||||
autoscaling:
|
||||
enabled: false
|
||||
|
||||
resources: {}
|
||||
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 4000
|
||||
initialDelaySeconds: 30
|
||||
timeoutSeconds: 15
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
targetPort: 4000
|
||||
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 4000
|
||||
initialDelaySeconds: 15
|
||||
timeoutSeconds: 2
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
ingress:
|
||||
enabled: false
|
||||
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: "truenas-nfs-csi"
|
||||
accessMode: ReadWriteMany
|
||||
size: 50Gi
|
||||
existingClaim: ""
|
||||
|
||||
env: {}
|
||||
autoscaling:
|
||||
enabled: false
|
||||
minReplicas: 1
|
||||
maxReplicas: 3
|
||||
targetCPUUtilizationPercentage: 80
|
||||
targetMemoryUtilizationPercentage: 80
|
||||
|
||||
secret:
|
||||
enabled: true
|
||||
existingSecretName: "rustical"
|
||||
resources: {}
|
||||
|
||||
debug:
|
||||
enabled: false
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: 4000
|
||||
initialDelaySeconds: 30
|
||||
timeoutSeconds: 15
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 4000
|
||||
initialDelaySeconds: 15
|
||||
timeoutSeconds: 2
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
||||
|
||||
config: {}
|
||||
|
||||
secret:
|
||||
enabled: true
|
||||
existingSecretName: "rustical"
|
||||
|
||||
debug:
|
||||
enabled: false
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ apiVersion: v2
|
|||
name: immich
|
||||
description: A Helm chart for Immich photo and video backup solution
|
||||
type: application
|
||||
version: 0.2.3
|
||||
version: 1.0.1
|
||||
appVersion: v3.1.0
|
||||
keywords:
|
||||
- immich
|
||||
|
|
|
|||
192
k8s/charts/immich/README.md
Normal file
192
k8s/charts/immich/README.md
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
# immich
|
||||
|
||||
A Helm chart for Immich, a self-hosted photo and video backup solution.
|
||||
|
||||
## TL;DR
|
||||
|
||||
```bash
|
||||
helm install immich oci://git.roboces.dev/catalin/fukuops/immich --version 1.0.0 -n apps-roboces
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Kubernetes 1.19+
|
||||
- Helm 3+
|
||||
- A NFS storage class (default: `truenas-nfs-csi`)
|
||||
- External PostgreSQL database
|
||||
- External Redis instance
|
||||
|
||||
## Configuration
|
||||
|
||||
All values are nested under the `immich:` key. Example:
|
||||
|
||||
```yaml
|
||||
immich:
|
||||
replicaCount: 1
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 2283
|
||||
config:
|
||||
database:
|
||||
hostname: "192.168.1.3"
|
||||
port: 55432
|
||||
username: ""
|
||||
password: ""
|
||||
name: ""
|
||||
redis:
|
||||
hostname: "192.168.1.3"
|
||||
port: 6379
|
||||
general:
|
||||
timezone: Europe/Madrid
|
||||
persistence:
|
||||
library:
|
||||
enabled: true
|
||||
storageClass: "truenas-nfs-csi"
|
||||
accessMode: ReadWriteMany
|
||||
size: 10Gi
|
||||
machineLearning:
|
||||
enabled: false
|
||||
secret:
|
||||
enabled: false
|
||||
existingSecretName: ""
|
||||
```
|
||||
|
||||
## Values Reference
|
||||
|
||||
### Main Application
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `immich.replicaCount` | int | `1` | Number of replicas |
|
||||
| `immich.image.repository` | string | `ghcr.io/immich-app/immich-server` | Image repository |
|
||||
| `immich.image.pullPolicy` | string | `Always` | Image pull policy |
|
||||
| `immich.image.tag` | string | `.Chart.AppVersion` | Image tag |
|
||||
| `immich.service.type` | string | `ClusterIP` | Service type |
|
||||
| `immich.service.port` | int | `2283` | Service port |
|
||||
| `immich.service.targetPort` | int | `2283` | Container port |
|
||||
| `immich.ingress.enabled` | bool | `false` | Enable ingress |
|
||||
| `immich.debug.enabled` | bool | `false` | Enable debug mode (adds init container for troubleshooting) |
|
||||
| `immich.resources` | object | `{}` | Container resources |
|
||||
| `immich.livenessProbe` | object | (see values.yaml) | Liveness probe |
|
||||
| `immich.readinessProbe` | object | (see values.yaml) | Readiness probe |
|
||||
|
||||
### Database Configuration
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `immich.config.database.hostname` | string | `""` | External database hostname |
|
||||
| `immich.config.database.port` | int | `55432` | Database port |
|
||||
| `immich.config.database.username` | string | `""` | Database username |
|
||||
| `immich.config.database.password` | string | `""` | Database password |
|
||||
| `immich.config.database.name` | string | `""` | Database name |
|
||||
|
||||
### Redis Configuration
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `immich.config.redis.hostname` | string | `""` | External Redis hostname |
|
||||
| `immich.config.redis.port` | int | `6379` | Redis port |
|
||||
| `immich.config.redis.dbIndex` | string | `""` | Redis database index |
|
||||
|
||||
### General Configuration
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `immich.config.general.timezone` | string | `Europe/Madrid` | Timezone |
|
||||
|
||||
### Persistence
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `immich.persistence.library.enabled` | bool | `true` | Enable library persistence |
|
||||
| `immich.persistence.library.storageClass` | string | `truenas-nfs-csi` | Storage class |
|
||||
| `immich.persistence.library.size` | string | `10Gi` | PVC size |
|
||||
| `immich.persistence.encodedVideo.enabled` | bool | `true` | Enable encoded video persistence |
|
||||
| `immich.persistence.thumbs.enabled` | bool | `true` | Enable thumbnails persistence |
|
||||
| `immich.persistence.upload.enabled` | bool | `true` | Enable uploads persistence |
|
||||
| `immich.persistence.profile.enabled` | bool | `true` | Enable profiles persistence |
|
||||
| `immich.persistence.backups.enabled` | bool | `true` | Enable backups persistence |
|
||||
| `immich.persistence.modelCache.enabled` | bool | `true` | Enable model cache persistence |
|
||||
|
||||
### Machine Learning
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `immich.machineLearning.enabled` | bool | `false` | Enable machine learning module |
|
||||
| `immich.machineLearning.image.repository` | string | `ghcr.io/immich-app/immich-machine-learning` | ML image repository |
|
||||
| `immich.machineLearning.replicaCount` | int | `1` | Number of ML replicas |
|
||||
| `immich.machineLearning.service.port` | int | `3003` | ML service port |
|
||||
| `immich.machineLearning.persistence.modelCache.enabled` | bool | `true` | Enable model cache persistence |
|
||||
|
||||
### Secret Management
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `immich.secret.enabled` | bool | `false` | Enable secret management |
|
||||
| `immich.secret.existingSecretName` | string | `""` | Existing secret name |
|
||||
|
||||
## ConfigMap
|
||||
|
||||
The chart creates a ConfigMap with database and Redis environment variables:
|
||||
- `TZ`
|
||||
- `IMMICH_PORT`
|
||||
- `IMMICH_MACHINE_LEARNING_URL`
|
||||
- `DB_HOSTNAME`, `DB_PORT`, `DB_USERNAME`, `DB_DATABASE_NAME`
|
||||
- `REDIS_HOSTNAME`, `REDIS_PORT`, `REDIS_DB`
|
||||
|
||||
## Persistence
|
||||
|
||||
The chart creates multiple PVCs:
|
||||
- `immich-library` - Original photos and videos
|
||||
- `immich-encoded-video` - Transcoded videos
|
||||
- `immich-thumbs` - Generated thumbnails
|
||||
- `immich-upload` - Upload queue
|
||||
- `immich-profile` - User profiles
|
||||
- `immich-backups` - Backup data
|
||||
- `immich-model-cache` - ML model cache
|
||||
|
||||
The library PVC uses `truenas-nfs-csi` storage class by default with `ReadWriteMany` access mode.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Enable debug mode to troubleshoot issues by adding an init container with a shell:
|
||||
|
||||
```yaml
|
||||
immich:
|
||||
debug:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
This adds an `alpine:3.19` init container with `sleep infinity` that mounts all volumes and inherits env vars. You can exec into it to inspect the environment:
|
||||
|
||||
```bash
|
||||
kubectl exec -it <pod-name> -c debug -- sh
|
||||
```
|
||||
|
||||
When debug mode is enabled, liveness and readiness probes are disabled to prevent restarts.
|
||||
|
||||
### Pod not starting
|
||||
|
||||
```bash
|
||||
kubectl get pvc -n apps-roboces -l app.kubernetes.io/name=immich
|
||||
```
|
||||
|
||||
### Check logs
|
||||
|
||||
```bash
|
||||
kubectl logs immich-server-0 -n apps-roboces
|
||||
```
|
||||
|
||||
### Check ML logs (if enabled)
|
||||
|
||||
```bash
|
||||
kubectl logs immich-machine-learning-0 -n apps-roboces
|
||||
```
|
||||
|
||||
### Verify configmap
|
||||
|
||||
```bash
|
||||
kubectl get configmap immich-config -n apps-roboces -o yaml
|
||||
```
|
||||
|
|
@ -1,22 +1,31 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "immich.fullname" . }}
|
||||
name: {{ include "immich.fullname" . }}-config
|
||||
labels:
|
||||
{{- include "immich.labels" . | nindent 4 }}
|
||||
data:
|
||||
TZ: {{ .Values.immich.general.tz | quote }}
|
||||
TZ: {{ .Values.immich.config.general.timezone | quote }}
|
||||
IMMICH_PORT: {{ .Values.immich.service.port | quote }}
|
||||
IMMICH_MACHINE_LEARNING_URL: {{ printf "http://%s-machine-learning:3003" (include "immich.fullname" .) | quote }}
|
||||
{{- range $key, $value := .Values.immich.db }}
|
||||
{{- if and (ne $key "password") $value }}
|
||||
{{ $keyName := printf "DB_%s" (upper $key) }}
|
||||
{{ $keyName }}: {{ $value | quote }}
|
||||
{{- if .Values.immich.config.database.hostname }}
|
||||
DB_HOSTNAME: {{ .Values.immich.config.database.hostname | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.immich.config.database.port }}
|
||||
DB_PORT: {{ .Values.immich.config.database.port | quote }}
|
||||
{{- end }}
|
||||
{{- range $key, $value := .Values.immich.redis }}
|
||||
{{- if and (ne $key "password") (ne $key "username") $value }}
|
||||
{{ $keyName := printf "REDIS_%s" (upper $key) }}
|
||||
{{ $keyName }}: {{ $value | quote }}
|
||||
{{- if .Values.immich.config.database.username }}
|
||||
DB_USERNAME: {{ .Values.immich.config.database.username | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.immich.config.database.name }}
|
||||
DB_DATABASE_NAME: {{ .Values.immich.config.database.name | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.immich.config.redis.hostname }}
|
||||
REDIS_HOSTNAME: {{ .Values.immich.config.redis.hostname | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.immich.config.redis.port }}
|
||||
REDIS_PORT: {{ .Values.immich.config.redis.port | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.immich.config.redis.dbIndex }}
|
||||
REDIS_DB: {{ .Values.immich.config.redis.dbIndex | quote }}
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,50 @@ spec:
|
|||
mountPath: /data/profile
|
||||
- name: backups
|
||||
mountPath: /data/backups
|
||||
{{- if .Values.immich.debug.enabled }}
|
||||
- name: debug
|
||||
image: alpine:3.19
|
||||
command: ["sleep", "infinity"]
|
||||
envFrom:
|
||||
{{- if .Values.immich.secret.existingSecretName }}
|
||||
- secretRef:
|
||||
name: {{ .Values.immich.secret.existingSecretName }}
|
||||
{{- else if .Values.immich.secret.enabled }}
|
||||
- secretRef:
|
||||
name: {{ include "immich.fullname" . }}
|
||||
{{- end }}
|
||||
- configMapRef:
|
||||
name: {{ include "immich.fullname" . }}-config
|
||||
volumeMounts:
|
||||
- name: library
|
||||
mountPath: /data/library
|
||||
{{- if .Values.immich.persistence.encodedVideo.enabled }}
|
||||
- name: encoded-video
|
||||
mountPath: /data/encoded-video
|
||||
{{- end }}
|
||||
{{- if .Values.immich.persistence.thumbs.enabled }}
|
||||
- name: thumbs
|
||||
mountPath: /data/thumbs
|
||||
{{- end }}
|
||||
{{- if .Values.immich.persistence.upload.enabled }}
|
||||
- name: upload
|
||||
mountPath: /data/upload
|
||||
{{- end }}
|
||||
{{- if .Values.immich.persistence.profile.enabled }}
|
||||
- name: profile
|
||||
mountPath: /data/profile
|
||||
{{- end }}
|
||||
{{- if .Values.immich.persistence.backups.enabled }}
|
||||
- name: backups
|
||||
mountPath: /data/backups
|
||||
{{- end }}
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: false
|
||||
capabilities:
|
||||
drop:
|
||||
- all
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}-server
|
||||
securityContext:
|
||||
|
|
@ -69,32 +113,30 @@ spec:
|
|||
image: "{{ .Values.immich.image.repository }}:{{ .Values.immich.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.immich.image.pullPolicy }}
|
||||
envFrom:
|
||||
{{- if .Values.secret.existingSecretName }}
|
||||
{{- if .Values.immich.secret.existingSecretName }}
|
||||
- secretRef:
|
||||
name: {{ .Values.secret.existingSecretName }}
|
||||
{{- else if .Values.secret.enabled }}
|
||||
name: {{ .Values.immich.secret.existingSecretName }}
|
||||
{{- else if .Values.immich.secret.enabled }}
|
||||
- secretRef:
|
||||
name: {{ include "immich.fullname" . }}
|
||||
{{- end }}
|
||||
- configMapRef:
|
||||
name: {{ include "immich.fullname" . }}
|
||||
name: {{ include "immich.fullname" . }}-config
|
||||
env:
|
||||
- name: IMMICH_PORT
|
||||
value: {{ .Values.immich.service.port | quote }}
|
||||
- name: IMMICH_MACHINE_LEARNING_URL
|
||||
value: {{ printf "http://%s-machine-learning:3003" (include "immich.fullname" .) | quote }}
|
||||
{{- range $key, $value := .Values.immich.env }}
|
||||
- name: {{ $key }}
|
||||
value: {{ $value | quote }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 2283
|
||||
protocol: TCP
|
||||
{{- if not .Values.immich.debug.enabled }}
|
||||
livenessProbe:
|
||||
{{- toYaml .Values.immich.livenessProbe | nindent 12 }}
|
||||
readinessProbe:
|
||||
{{- toYaml .Values.immich.readinessProbe | nindent 12 }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 2283
|
||||
protocol: TCP
|
||||
resources:
|
||||
{{- toYaml .Values.immich.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ spec:
|
|||
requests:
|
||||
storage: {{ .Values.immich.persistence.backups.size }}
|
||||
{{- end }}
|
||||
{{- if and .Values.machineLearning.enabled .Values.machineLearning.persistence.modelCache.enabled }}
|
||||
{{- if and .Values.immich.machineLearning.enabled .Values.immich.machineLearning.persistence.modelCache.enabled }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
|
|
@ -116,11 +116,11 @@ metadata:
|
|||
{{- include "immich.labels" . | nindent 4 }}
|
||||
spec:
|
||||
accessModes:
|
||||
- {{ .Values.machineLearning.persistence.modelCache.accessMode }}
|
||||
{{- if .Values.machineLearning.persistence.modelCache.storageClass }}
|
||||
storageClassName: {{ .Values.machineLearning.persistence.modelCache.storageClass | quote }}
|
||||
- {{ .Values.immich.machineLearning.persistence.modelCache.accessMode }}
|
||||
{{- if .Values.immich.machineLearning.persistence.modelCache.storageClass }}
|
||||
storageClassName: {{ .Values.immich.machineLearning.persistence.modelCache.storageClass | quote }}
|
||||
{{- end }}
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.machineLearning.persistence.modelCache.size }}
|
||||
storage: {{ .Values.immich.machineLearning.persistence.modelCache.size }}
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
{{- if and .Values.secret.enabled (not .Values.secret.existingSecretName) -}}
|
||||
{{- if and .Values.immich.secret.enabled (not .Values.immich.secret.existingSecretName) -}}
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
|
|
@ -7,19 +7,19 @@ metadata:
|
|||
{{- include "immich.labels" . | nindent 4 }}
|
||||
type: Opaque
|
||||
stringData:
|
||||
{{- if .Values.immich.db.password }}
|
||||
DB_PASSWORD: {{ .Values.immich.db.password | quote }}
|
||||
{{- if .Values.immich.config.database.password }}
|
||||
DB_PASSWORD: {{ .Values.immich.config.database.password | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.immich.db.username }}
|
||||
DB_USERNAME: {{ .Values.immich.db.username | quote }}
|
||||
{{- if .Values.immich.config.database.username }}
|
||||
DB_USERNAME: {{ .Values.immich.config.database.username | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.immich.redis.password }}
|
||||
REDIS_PASSWORD: {{ .Values.immich.redis.password | quote }}
|
||||
{{- if .Values.immich.config.redis.password }}
|
||||
REDIS_PASSWORD: {{ .Values.immich.config.redis.password | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.immich.redis.username }}
|
||||
REDIS_USERNAME: {{ .Values.immich.redis.username | quote }}
|
||||
{{- if .Values.immich.config.redis.username }}
|
||||
REDIS_USERNAME: {{ .Values.immich.config.redis.username | quote }}
|
||||
{{- end }}
|
||||
{{- range $key, $value := .Values.secret.data }}
|
||||
{{- range $key, $value := .Values.immich.secret.data }}
|
||||
{{ $key }}: {{ $value | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ spec:
|
|||
{{- include "immich.selectorLabels" . | nindent 4 }}
|
||||
app.kubernetes.io/component: server
|
||||
---
|
||||
{{- if .Values.machineLearning.enabled }}
|
||||
{{- if .Values.immich.machineLearning.enabled }}
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
|
|
@ -24,9 +24,9 @@ metadata:
|
|||
{{- include "immich.labels" . | nindent 4 }}
|
||||
app.kubernetes.io/component: machine-learning
|
||||
spec:
|
||||
type: {{ .Values.machineLearning.service.type }}
|
||||
type: {{ .Values.immich.machineLearning.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.machineLearning.service.port }}
|
||||
- port: {{ .Values.immich.machineLearning.service.port }}
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
{{- if .Values.machineLearning.enabled -}}
|
||||
{{- if .Values.immich.machineLearning.enabled -}}
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
|
|
@ -8,8 +8,8 @@ metadata:
|
|||
app.kubernetes.io/component: machine-learning
|
||||
spec:
|
||||
serviceName: {{ include "immich.fullname" . }}-machine-learning-headless
|
||||
{{- if not .Values.machineLearning.autoscaling.enabled }}
|
||||
replicas: {{ .Values.machineLearning.replicaCount }}
|
||||
{{- if not .Values.immich.machineLearning.autoscaling.enabled }}
|
||||
replicas: {{ .Values.immich.machineLearning.replicaCount }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
|
|
@ -17,7 +17,7 @@ spec:
|
|||
app.kubernetes.io/component: machine-learning
|
||||
template:
|
||||
metadata:
|
||||
{{- with .Values.machineLearning.podAnnotations }}
|
||||
{{- with .Values.immich.machineLearning.podAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
|
|
@ -25,41 +25,41 @@ spec:
|
|||
{{- include "immich.selectorLabels" . | nindent 8 }}
|
||||
app.kubernetes.io/component: machine-learning
|
||||
spec:
|
||||
{{- with .Values.machineLearning.imagePullSecrets }}
|
||||
{{- with .Values.immich.machineLearning.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.machineLearning.podSecurityContext | nindent 8 }}
|
||||
{{- toYaml .Values.immich.machineLearning.podSecurityContext | nindent 8 }}
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}-machine-learning
|
||||
securityContext:
|
||||
{{- toYaml .Values.machineLearning.securityContext | nindent 12 }}
|
||||
image: "{{ .Values.machineLearning.image.repository }}:{{ .Values.machineLearning.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.machineLearning.image.pullPolicy }}
|
||||
{{- toYaml .Values.immich.machineLearning.securityContext | nindent 12 }}
|
||||
image: "{{ .Values.immich.machineLearning.image.repository }}:{{ .Values.immich.machineLearning.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.immich.machineLearning.image.pullPolicy }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 3003
|
||||
protocol: TCP
|
||||
env:
|
||||
- name: IMMICH_PORT
|
||||
value: {{ .Values.machineLearning.service.port | quote }}
|
||||
value: {{ .Values.immich.machineLearning.service.port | quote }}
|
||||
- name: IMMICH_MACHINE_LEARNING_PORT
|
||||
value: "3003"
|
||||
resources:
|
||||
{{- toYaml .Values.machineLearning.resources | nindent 12 }}
|
||||
{{- toYaml .Values.immich.machineLearning.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- name: model-cache
|
||||
mountPath: /cache
|
||||
{{- with .Values.machineLearning.nodeSelector }}
|
||||
{{- with .Values.immich.machineLearning.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.machineLearning.affinity }}
|
||||
{{- with .Values.immich.machineLearning.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.machineLearning.tolerations }}
|
||||
{{- with .Values.immich.machineLearning.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -19,22 +19,21 @@ immich:
|
|||
ingress:
|
||||
enabled: false
|
||||
|
||||
db:
|
||||
hostname: "192.168.1.3"
|
||||
port: 55432
|
||||
username: ""
|
||||
password: ""
|
||||
database_name: ""
|
||||
|
||||
redis:
|
||||
hostname: "192.168.1.3"
|
||||
port: 300036
|
||||
username: ""
|
||||
password: ""
|
||||
dbindex: ""
|
||||
|
||||
general:
|
||||
tz: "Europe/Madrid"
|
||||
config:
|
||||
database:
|
||||
hostname: ""
|
||||
port: 55432
|
||||
username: ""
|
||||
password: ""
|
||||
name: ""
|
||||
redis:
|
||||
hostname: ""
|
||||
port: 6379
|
||||
username: ""
|
||||
password: ""
|
||||
dbIndex: ""
|
||||
general:
|
||||
timezone: Europe/Madrid
|
||||
|
||||
persistence:
|
||||
library:
|
||||
|
|
@ -104,66 +103,67 @@ immich:
|
|||
tolerations: []
|
||||
affinity: {}
|
||||
|
||||
env: {}
|
||||
|
||||
machineLearning:
|
||||
enabled: false
|
||||
|
||||
image:
|
||||
repository: ghcr.io/immich-app/immich-machine-learning
|
||||
pullPolicy: Always
|
||||
tag: ""
|
||||
|
||||
imagePullSecrets: []
|
||||
podAnnotations: {}
|
||||
podSecurityContext: {}
|
||||
securityContext: {}
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 3003
|
||||
|
||||
replicaCount: 1
|
||||
|
||||
autoscaling:
|
||||
machineLearning:
|
||||
enabled: false
|
||||
minReplicas: 1
|
||||
maxReplicas: 3
|
||||
targetCPUUtilizationPercentage: 80
|
||||
targetMemoryUtilizationPercentage: 80
|
||||
|
||||
resources: {}
|
||||
image:
|
||||
repository: ghcr.io/immich-app/immich-machine-learning
|
||||
pullPolicy: Always
|
||||
tag: ""
|
||||
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
imagePullSecrets: []
|
||||
podAnnotations: {}
|
||||
podSecurityContext: {}
|
||||
securityContext: {}
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 3003
|
||||
initialDelaySeconds: 30
|
||||
timeoutSeconds: 15
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 3003
|
||||
initialDelaySeconds: 15
|
||||
timeoutSeconds: 2
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
replicaCount: 1
|
||||
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
||||
autoscaling:
|
||||
enabled: false
|
||||
minReplicas: 1
|
||||
maxReplicas: 3
|
||||
targetCPUUtilizationPercentage: 80
|
||||
targetMemoryUtilizationPercentage: 80
|
||||
|
||||
persistence:
|
||||
modelCache:
|
||||
enabled: true
|
||||
storageClass: ""
|
||||
accessMode: ReadWriteMany
|
||||
size: 10Gi
|
||||
resources: {}
|
||||
|
||||
secret:
|
||||
enabled: false
|
||||
existingSecretName: ""
|
||||
data: {}
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: 3003
|
||||
initialDelaySeconds: 30
|
||||
timeoutSeconds: 15
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 3003
|
||||
initialDelaySeconds: 15
|
||||
timeoutSeconds: 2
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
||||
|
||||
persistence:
|
||||
modelCache:
|
||||
enabled: true
|
||||
storageClass: ""
|
||||
accessMode: ReadWriteMany
|
||||
size: 10Gi
|
||||
|
||||
secret:
|
||||
enabled: false
|
||||
existingSecretName: ""
|
||||
data: {}
|
||||
|
||||
debug:
|
||||
enabled: false
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@ apiVersion: v2
|
|||
name: miniflux
|
||||
description: A Helm chart for Miniflux RSS reader
|
||||
type: application
|
||||
version: 0.1.0
|
||||
version: 1.0.2
|
||||
appVersion: 2.3.3
|
||||
annotations:
|
||||
artifacthub.io/images: |
|
||||
- name: miniflux
|
||||
image: miniflux/miniflux:2.3.3
|
||||
artifacthub.io/images: "- name: miniflux\n image: miniflux/miniflux:2.3.3\n"
|
||||
|
|
|
|||
153
k8s/charts/miniflux/README.md
Normal file
153
k8s/charts/miniflux/README.md
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
# miniflux
|
||||
|
||||
A Helm chart for Miniflux, a minimalist and open source RSS reader.
|
||||
|
||||
## TL;DR
|
||||
|
||||
```bash
|
||||
helm install miniflux oci://git.roboces.dev/catalin/fukuops/miniflux --version 1.0.1 -n apps-roboces
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Kubernetes 1.19+
|
||||
- Helm 3+
|
||||
- An existing `Secret` containing:
|
||||
- `DATABASE_URL` - PostgreSQL connection string
|
||||
- `ADMIN_USERNAME` - Admin username (optional if oauth2 is configured)
|
||||
- `ADMIN_PASSWORD` - Admin password (optional if oauth2 is configured)
|
||||
|
||||
## Configuration
|
||||
|
||||
All values are nested under the `miniflux:` key. Example:
|
||||
|
||||
```yaml
|
||||
miniflux:
|
||||
service:
|
||||
type: LoadBalancer
|
||||
config:
|
||||
database:
|
||||
url: "postgres://user:pass@host:5432/miniflux"
|
||||
general:
|
||||
createAdmin: true
|
||||
runMigrations: true
|
||||
workerPoolSize: 1
|
||||
pollingFrequency: 60
|
||||
oauth2:
|
||||
enabled: true
|
||||
provider: oidc
|
||||
redirectUrl: https://feeds.example.com/oauth2/oidc/callback
|
||||
oidcDiscoveryEndpoint: https://auth.example.com/application/o/miniflux/
|
||||
userCreation: true
|
||||
metrics:
|
||||
enabled: true
|
||||
allowedNetworks: 10.0.0.0/8
|
||||
secret:
|
||||
existingSecretName: miniflux
|
||||
```
|
||||
|
||||
## Values Reference
|
||||
|
||||
### Main Settings
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `miniflux.replicaCount` | int | `1` | Number of replicas |
|
||||
| `miniflux.image.repository` | string | `miniflux/miniflux` | Image repository |
|
||||
| `miniflux.image.pullpolicy` | string | `always` | Image pull policy |
|
||||
| `miniflux.image.tag` | string | `.Chart.AppVersion` | Image tag |
|
||||
| `miniflux.service.type` | string | `ClusterIP` | Service type |
|
||||
| `miniflux.service.port` | int | `8888` | Service port |
|
||||
| `miniflux.service.targetPort` | int | `8080` | Container port |
|
||||
| `miniflux.ingress.enabled` | bool | `false` | Enable ingress |
|
||||
| `miniflux.resources` | object | `{}` | Container resources |
|
||||
| `miniflux.livenessProbe` | object | `{}` | Liveness probe |
|
||||
| `miniflux.readinessProbe` | object | `{}` | Readiness probe |
|
||||
| `miniflux.secret.existingSecretName` | string | `""` | Existing secret name |
|
||||
| `miniflux.debug.enabled` | bool | `false` | Enable debug mode (adds init container for troubleshooting) |
|
||||
| `miniflux.nodeSelector` | object | `{}` | Node selector |
|
||||
| `miniflux.tolerations` | array | `[]` | Tolerations |
|
||||
| `miniflux.affinity` | object | `{}` | Affinity rules |
|
||||
| `miniflux.autoscaling.enabled` | bool | `false` | Enable autoscaling |
|
||||
| `miniflux.autoscaling.minReplicas` | int | `1` | Minimum replicas |
|
||||
| `miniflux.autoscaling.maxReplicas` | int | `3` | Maximum replicas |
|
||||
| `miniflux.autoscaling.targetCPUUtilizationPercentage` | int | `80` | Target CPU utilization for HPA |
|
||||
| `miniflux.autoscaling.targetMemoryUtilizationPercentage` | int | `80` | Target memory utilization for HPA |
|
||||
|
||||
### Database Configuration
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `miniflux.config.database.url` | string | `""` | PostgreSQL connection URL |
|
||||
|
||||
### General Configuration
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `miniflux.config.general.createAdmin` | bool | `false` | Create admin user on startup |
|
||||
| `miniflux.config.general.runMigrations` | bool | `false` | Run database migrations |
|
||||
| `miniflux.config.general.workerPoolSize` | int | `1` | Number of worker threads |
|
||||
| `miniflux.config.general.pollingFrequency` | int | `60` | Feed refresh interval (minutes) |
|
||||
| `miniflux.config.general.batchSize` | int | `10` | Number of feeds to refresh per batch |
|
||||
|
||||
### OAuth2 Configuration
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `miniflux.config.oauth2.enabled` | bool | `false` | Enable OAuth2 authentication |
|
||||
| `miniflux.config.oauth2.provider` | string | `oidc` | OAuth2 provider |
|
||||
| `miniflux.config.oauth2.redirectUrl` | string | `""` | OAuth redirect URL |
|
||||
| `miniflux.config.oauth2.oidcDiscoveryEndpoint` | string | `""` | OIDC discovery endpoint URL |
|
||||
| `miniflux.config.oauth2.userCreation` | bool | `false` | Allow OAuth users to create accounts |
|
||||
|
||||
### Metrics Configuration
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `miniflux.config.metrics.enabled` | bool | `false` | Enable metrics endpoint |
|
||||
| `miniflux.config.metrics.allowedNetworks` | string | `""` | Networks allowed to access metrics (CIDR) |
|
||||
|
||||
### YouTube Configuration
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `miniflux.config.youtube.fetchWatchTime` | bool | `false` | Fetch YouTube watch time data |
|
||||
|
||||
## Secret Management
|
||||
|
||||
The chart looks for an existing `Secret` with the name specified in `miniflux.secret.existingSecretName`. Required secret keys:
|
||||
- `DATABASE_URL` - PostgreSQL connection string
|
||||
- `ADMIN_USERNAME` - Admin username (if `config.general.createAdmin=true`)
|
||||
- `ADMIN_PASSWORD` - Admin password (if `config.general.createAdmin=true`)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Enable debug mode to troubleshoot issues by adding an init container with a shell:
|
||||
|
||||
```yaml
|
||||
miniflux:
|
||||
debug:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
This adds an `alpine:3.19` init container with `sleep infinity` that mounts all volumes and inherits env vars. You can exec into it to inspect the environment:
|
||||
|
||||
```bash
|
||||
kubectl exec -it <pod-name> -c debug -- sh
|
||||
```
|
||||
|
||||
When debug mode is enabled, liveness and readiness probes are disabled to prevent restarts.
|
||||
|
||||
### Check logs
|
||||
|
||||
```bash
|
||||
kubectl logs miniflux-0 -n apps-roboces
|
||||
```
|
||||
|
||||
### Verify configmap
|
||||
|
||||
```bash
|
||||
kubectl get configmap miniflux-config -n apps-roboces -o yaml
|
||||
```
|
||||
40
k8s/charts/miniflux/templates/configmap.yaml
Normal file
40
k8s/charts/miniflux/templates/configmap.yaml
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "miniflux.fullname" . }}-config
|
||||
labels:
|
||||
{{- include "miniflux.labels" . | nindent 4 }}
|
||||
data:
|
||||
{{- if .Values.miniflux.config.database.url }}
|
||||
DATABASE_URL: {{ .Values.miniflux.config.database.url | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.miniflux.config.general.runMigrations }}
|
||||
RUN_MIGRATIONS: {{ .Values.miniflux.config.general.runMigrations | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.miniflux.config.general.createAdmin }}
|
||||
CREATE_ADMIN: {{ .Values.miniflux.config.general.createAdmin | quote }}
|
||||
{{- end }}
|
||||
WORKER_POOL_SIZE: {{ .Values.miniflux.config.general.workerPoolSize | quote }}
|
||||
POLLING_FREQUENCY: {{ .Values.miniflux.config.general.pollingFrequency | quote }}
|
||||
BATCH_SIZE: {{ .Values.miniflux.config.general.batchSize | quote }}
|
||||
{{- if .Values.miniflux.config.oauth2.enabled }}
|
||||
OAUTH2_PROVIDER: {{ .Values.miniflux.config.oauth2.provider | quote }}
|
||||
{{- if .Values.miniflux.config.oauth2.redirectUrl }}
|
||||
OAUTH2_REDIRECT_URL: {{ .Values.miniflux.config.oauth2.redirectUrl | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.miniflux.config.oauth2.oidcDiscoveryEndpoint }}
|
||||
OAUTH2_OIDC_DISCOVERY_ENDPOINT: {{ .Values.miniflux.config.oauth2.oidcDiscoveryEndpoint | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.miniflux.config.oauth2.userCreation }}
|
||||
OAUTH2_USER_CREATION: {{ .Values.miniflux.config.oauth2.userCreation | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.miniflux.config.metrics.enabled }}
|
||||
METRICS_COLLECTOR: {{ .Values.miniflux.config.metrics.enabled | quote }}
|
||||
{{- if .Values.miniflux.config.metrics.allowedNetworks }}
|
||||
METRICS_ALLOWED_NETWORKS: {{ .Values.miniflux.config.metrics.allowedNetworks | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.miniflux.config.youtube.fetchWatchTime }}
|
||||
FETCH_YOUTUBE_WATCH_TIME: {{ .Values.miniflux.config.youtube.fetchWatchTime | quote }}
|
||||
{{- end }}
|
||||
|
|
@ -7,8 +7,8 @@ metadata:
|
|||
annotations:
|
||||
kube-score/ignore: pod-networkpolicy,deployment-has-host-podantiaffinity
|
||||
spec:
|
||||
{{- if not .Values.autoscaling.enabled }}
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
{{- if not .Values.miniflux.autoscaling.enabled }}
|
||||
replicas: {{ .Values.miniflux.replicaCount }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
|
|
@ -20,52 +20,49 @@ spec:
|
|||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
{{- with .Values.podAnnotations }}
|
||||
{{- with .Values.miniflux.podAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "miniflux.selectorLabels" . | nindent 8 }}
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
{{- with .Values.miniflux.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||||
{{- toYaml .Values.miniflux.podSecurityContext | nindent 8 }}
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
{{- toYaml .Values.miniflux.securityContext | nindent 12 }}
|
||||
image: "{{ .Values.miniflux.image.repository }}:{{ .Values.miniflux.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.miniflux.image.pullPolicy }}
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: {{ include "miniflux.fullname" . }}-config
|
||||
- secretRef:
|
||||
name: {{ .Values.secret.existingSecretName | default (include "miniflux.fullname" .) }}
|
||||
env:
|
||||
{{- range $key, $value := .Values.env }}
|
||||
- name: {{ $key }}
|
||||
value: {{ $value | quote }}
|
||||
{{- end }}
|
||||
name: {{ .Values.miniflux.secret.existingSecretName | default (include "miniflux.fullname" .) }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8080
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
{{- toYaml .Values.livenessProbe | nindent 12 }}
|
||||
{{- toYaml .Values.miniflux.livenessProbe | nindent 12 }}
|
||||
readinessProbe:
|
||||
{{- toYaml .Values.readinessProbe | nindent 12 }}
|
||||
{{- toYaml .Values.miniflux.readinessProbe | nindent 12 }}
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
{{- toYaml .Values.miniflux.resources | nindent 12 }}
|
||||
{{- with .Values.miniflux.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
{{- with .Values.miniflux.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
{{- with .Values.miniflux.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
|
|
|
|||
33
k8s/charts/miniflux/templates/hpa.yaml
Normal file
33
k8s/charts/miniflux/templates/hpa.yaml
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
{{- if .Values.miniflux.autoscaling.enabled }}
|
||||
---
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ include "miniflux.fullname" . }}
|
||||
labels:
|
||||
{{- include "miniflux.labels" . | nindent 4 }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ include "miniflux.fullname" . }}
|
||||
minReplicas: {{ .Values.miniflux.autoscaling.minReplicas }}
|
||||
maxReplicas: {{ .Values.miniflux.autoscaling.maxReplicas }}
|
||||
metrics:
|
||||
{{- if .Values.miniflux.autoscaling.targetCPUUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: {{ .Values.miniflux.autoscaling.targetCPUUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- if .Values.miniflux.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: {{ .Values.miniflux.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
@ -1,23 +1,23 @@
|
|||
{{- if .Values.ingress.enabled -}}
|
||||
{{- if .Values.miniflux.ingress.enabled -}}
|
||||
{{- $fullName := include "miniflux.fullname" . -}}
|
||||
{{- $svcPort := .Values.service.port -}}
|
||||
{{- $svcPort := .Values.miniflux.service.port -}}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ $fullName }}
|
||||
labels:
|
||||
{{- include "miniflux.labels" . | nindent 4 }}
|
||||
{{- with .Values.ingress.annotations }}
|
||||
{{- with .Values.miniflux.ingress.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .Values.ingress.className }}
|
||||
ingressClassName: {{ .Values.ingress.className }}
|
||||
{{- if .Values.miniflux.ingress.className }}
|
||||
ingressClassName: {{ .Values.miniflux.ingress.className }}
|
||||
{{- end }}
|
||||
{{- if .Values.ingress.tls }}
|
||||
{{- if .Values.miniflux.ingress.tls }}
|
||||
tls:
|
||||
{{- range .Values.ingress.tls }}
|
||||
{{- range .Values.miniflux.ingress.tls }}
|
||||
- hosts:
|
||||
{{- range .hosts }}
|
||||
- {{ . | quote }}
|
||||
|
|
@ -26,7 +26,7 @@ spec:
|
|||
{{- end }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- range .Values.ingress.hosts }}
|
||||
{{- range .Values.miniflux.ingress.hosts }}
|
||||
- host: {{ .host | quote }}
|
||||
http:
|
||||
paths:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
{{- if .Values.podDisruptionBudget.enabled -}}
|
||||
{{- if .Values.miniflux.podDisruptionBudget.enabled -}}
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
|
|
@ -6,11 +6,11 @@ metadata:
|
|||
labels:
|
||||
{{- include "miniflux.labels" . | nindent 4 }}
|
||||
spec:
|
||||
{{- if .Values.podDisruptionBudget.minAvailable }}
|
||||
minAvailable: {{ .Values.podDisruptionBudget.minAvailable }}
|
||||
{{- if .Values.miniflux.podDisruptionBudget.minAvailable }}
|
||||
minAvailable: {{ .Values.miniflux.podDisruptionBudget.minAvailable }}
|
||||
{{- end }}
|
||||
{{- if .Values.podDisruptionBudget.maxUnavailable }}
|
||||
maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }}
|
||||
{{- if .Values.miniflux.podDisruptionBudget.maxUnavailable }}
|
||||
maxUnavailable: {{ .Values.miniflux.podDisruptionBudget.maxUnavailable }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
{{- if and .Values.secret.enabled (not .Values.secret.existingSecretName) -}}
|
||||
{{- if and .Values.miniflux.secret.enabled (not .Values.miniflux.secret.existingSecretName) -}}
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
|
|
@ -7,7 +7,7 @@ metadata:
|
|||
{{- include "miniflux.labels" . | nindent 4 }}
|
||||
type: Opaque
|
||||
stringData:
|
||||
{{- range $key, $value := .Values.secret.data }}
|
||||
{{- range $key, $value := .Values.miniflux.secret.data }}
|
||||
{{ $key }}: {{ $value | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ metadata:
|
|||
labels:
|
||||
{{- include "miniflux.labels" . | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
type: {{ .Values.miniflux.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: {{ .Values.service.targetPort }}
|
||||
- port: {{ .Values.miniflux.service.port }}
|
||||
targetPort: {{ .Values.miniflux.service.targetPort }}
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
|
|
|
|||
|
|
@ -1,42 +1,68 @@
|
|||
# Configuration is managed in k8s/argo-apps/miniflux.yaml
|
||||
replicaCount: 1
|
||||
miniflux:
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: miniflux/miniflux
|
||||
pullpolicy: always
|
||||
tag: ""
|
||||
image:
|
||||
repository: miniflux/miniflux
|
||||
pullpolicy: always
|
||||
tag: ""
|
||||
|
||||
imagePullSecrets: []
|
||||
podAnnotations: {}
|
||||
podSecurityContext: {}
|
||||
securityContext: {}
|
||||
imagePullSecrets: []
|
||||
podAnnotations: {}
|
||||
podSecurityContext: {}
|
||||
securityContext: {}
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 8888
|
||||
targetPort: 8080
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 8888
|
||||
targetPort: 8080
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
ingress:
|
||||
enabled: false
|
||||
|
||||
resources: {}
|
||||
config:
|
||||
database:
|
||||
url: ""
|
||||
general:
|
||||
createAdmin: false
|
||||
runMigrations: false
|
||||
workerPoolSize: 1
|
||||
pollingFrequency: 60
|
||||
batchSize: 10
|
||||
oauth2:
|
||||
enabled: false
|
||||
provider: oidc
|
||||
redirectUrl: ""
|
||||
oidcDiscoveryEndpoint: ""
|
||||
userCreation: false
|
||||
metrics:
|
||||
enabled: false
|
||||
allowedNetworks: ""
|
||||
youtube:
|
||||
fetchWatchTime: false
|
||||
|
||||
livenessProbe: {}
|
||||
readinessProbe: {}
|
||||
resources: {}
|
||||
|
||||
autoscaling:
|
||||
enabled: false
|
||||
livenessProbe: {}
|
||||
readinessProbe: {}
|
||||
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
||||
autoscaling:
|
||||
enabled: false
|
||||
minReplicas: 1
|
||||
maxReplicas: 3
|
||||
targetCPUUtilizationPercentage: 80
|
||||
targetMemoryUtilizationPercentage: 80
|
||||
|
||||
podDisruptionBudget:
|
||||
enabled: false
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
||||
|
||||
env: {}
|
||||
podDisruptionBudget:
|
||||
enabled: false
|
||||
|
||||
secret:
|
||||
enabled: false
|
||||
existingSecretName: ""
|
||||
data: {}
|
||||
secret:
|
||||
enabled: false
|
||||
existingSecretName: ""
|
||||
data: {}
|
||||
|
||||
debug:
|
||||
enabled: false
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ description: 'Ultra-fast, secure & lightweight self-hosted cloud storage — you
|
|||
|
||||
'
|
||||
type: application
|
||||
version: 1.0.0
|
||||
version: 1.0.1
|
||||
appVersion: 0.8.6
|
||||
annotations:
|
||||
artifacthub.io/images: "- name: oxicloud\n image: diocrafts/oxicloud:0.8.6\n"
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ oxicloud:
|
|||
| `oxicloud.persistence.size` | string | `50Gi` | PVC size |
|
||||
| `oxicloud.secret.existingSecretName` | string | `""` | Existing secret name |
|
||||
| `oxicloud.wopi.enabled` | bool | `false` | Enable WOPI/Collabora integration |
|
||||
| `oxicloud.debug.enabled` | bool | `false` | Enable debug mode (adds init container for troubleshooting) |
|
||||
| `oxicloud.ingress.enabled` | bool | `false` | Enable ingress |
|
||||
| `oxicloud.ingress.className` | string | `""` | Ingress class |
|
||||
| `oxicloud.resources` | object | `{}` | Container resources |
|
||||
|
|
@ -115,6 +116,24 @@ Required secret keys depend on configuration:
|
|||
|
||||
## Troubleshooting
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Enable debug mode to troubleshoot issues by adding an init container with a shell:
|
||||
|
||||
```yaml
|
||||
oxicloud:
|
||||
debug:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
This adds an `alpine:3.19` init container with `sleep infinity` that mounts all volumes and inherits env vars. You can exec into it to inspect the environment:
|
||||
|
||||
```bash
|
||||
kubectl exec -it <pod-name> -c debug -- sh
|
||||
```
|
||||
|
||||
When debug mode is enabled, liveness and readiness probes are disabled to prevent restarts.
|
||||
|
||||
### Pod not starting
|
||||
|
||||
Check if the PVC is bound:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
---
|
||||
{{- if .Values.oxicloud.ingress.enabled -}}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
|
|
@ -51,7 +50,6 @@ spec:
|
|||
port:
|
||||
number: {{ .Values.oxicloud.wopi.collabora.service.port }}
|
||||
{{- end }}
|
||||
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
|
|
|
|||
|
|
@ -85,3 +85,6 @@ oxicloud:
|
|||
enabled: true
|
||||
existingSecretName: "oxicloud-secret"
|
||||
data: {}
|
||||
|
||||
debug:
|
||||
enabled: false
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@ apiVersion: v2
|
|||
name: vaultwarden
|
||||
description: A Helm chart for Vaultwarden password manager
|
||||
type: application
|
||||
version: 0.1.0
|
||||
version: 1.0.2
|
||||
appVersion: 1.37.1
|
||||
annotations:
|
||||
artifacthub.io/images: |
|
||||
- name: vaultwarden
|
||||
image: vaultwarden/server:1.37.1-alpine
|
||||
artifacthub.io/images: "- name: vaultwarden\n image: vaultwarden/server:1.37.1-alpine\n"
|
||||
|
|
|
|||
132
k8s/charts/vaultwarden/README.md
Normal file
132
k8s/charts/vaultwarden/README.md
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
# vaultwarden
|
||||
|
||||
A Helm chart for Vaultwarden (formerly Bitwarden RS), a lightweight self-hosted password manager.
|
||||
|
||||
## TL;DR
|
||||
|
||||
```bash
|
||||
helm install vaultwarden oci://git.roboces.dev/catalin/fukuops/vaultwarden --version 1.0.2 -n apps-roboces
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Kubernetes 1.19+
|
||||
- Helm 3+
|
||||
- A NFS storage class (default: `truenas-nfs-csi`)
|
||||
- An existing `Secret` containing:
|
||||
- `DATABASE_URL` (if using external database)
|
||||
- `ADMIN_TOKEN` (optional - for admin panel)
|
||||
- `SMTP_HOST` and related SMTP settings (optional - for email)
|
||||
|
||||
## Configuration
|
||||
|
||||
All values are nested under the `vaultwarden:` key. Example:
|
||||
|
||||
```yaml
|
||||
vaultwarden:
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: "truenas-nfs-csi"
|
||||
accessMode: ReadWriteMany
|
||||
size: 5Gi
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
secret:
|
||||
existingSecretName: vaultwarden-kubernetes-secrets
|
||||
config:
|
||||
database:
|
||||
url: ""
|
||||
enableWal: true
|
||||
connectionRetries: 15
|
||||
webVault:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
## Values Reference
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `vaultwarden.replicaCount` | int | `1` | Number of replicas |
|
||||
| `vaultwarden.image.repository` | string | `vaultwarden/server` | Image repository |
|
||||
| `vaultwarden.image.pullpolicy` | string | `Always` | Image pull policy |
|
||||
| `vaultwarden.image.tag` | string | `.Chart.AppVersion` | Image tag |
|
||||
| `vaultwarden.service.type` | string | `ClusterIP` | Service type |
|
||||
| `vaultwarden.service.port` | int | `80` | Service port |
|
||||
| `vaultwarden.service.targetPort` | int | `80` | Container port |
|
||||
| `vaultwarden.ingress.enabled` | bool | `false` | Enable ingress |
|
||||
| `vaultwarden.config.database.url` | string | `""` | Database connection URL |
|
||||
| `vaultwarden.config.database.enableWal` | bool | `true` | Enable WAL mode |
|
||||
| `vaultwarden.config.database.connectionRetries` | int | `15` | Connection retries |
|
||||
| `vaultwarden.config.database.timeout` | int | `30` | Connection timeout |
|
||||
| `vaultwarden.config.database.idleTimeout` | int | `600` | Idle timeout |
|
||||
| `vaultwarden.config.webVault.enabled` | bool | `true` | Enable web vault |
|
||||
| `vaultwarden.persistence.enabled` | bool | `true` | Enable persistence |
|
||||
| `vaultwarden.persistence.storageClass` | string | `truenas-nfs-csi` | Storage class |
|
||||
| `vaultwarden.persistence.accessMode` | string | `ReadWriteMany` | Access mode |
|
||||
| `vaultwarden.persistence.size` | string | `5Gi` | PVC size |
|
||||
| `vaultwarden.secret.existingSecretName` | string | `vaultwarden-kubernetes-secrets` | Existing secret name |
|
||||
| `vaultwarden.debug.enabled` | bool | `false` | Enable debug mode (adds init container for troubleshooting) |
|
||||
| `vaultwarden.resources` | object | `{}` | Container resources |
|
||||
| `vaultwarden.livenessProbe` | object | (see values.yaml) | Liveness probe |
|
||||
| `vaultwarden.readinessProbe` | object | (see values.yaml) | Readiness probe |
|
||||
|
||||
## ConfigMap
|
||||
|
||||
The chart creates a ConfigMap with database and web vault environment variables:
|
||||
- `DATABASE_URL`
|
||||
- `DB_ENABLE_WAL`
|
||||
- `DB_CONNECTION_RETRIES`
|
||||
- `DB_TIMEOUT`
|
||||
- `DB_IDLE_TIMEOUT`
|
||||
- `WEB_VAULT_ENABLED`
|
||||
- `WEB_VAULT_FOLDER`
|
||||
|
||||
## Persistence
|
||||
|
||||
The chart creates a PVC named `vaultwarden-data`. The PVC uses the `truenas-nfs-csi` storage class by default with `ReadWriteMany` access mode.
|
||||
|
||||
## Secret Management
|
||||
|
||||
The chart looks for an existing `Secret` with the name specified in `vaultwarden.secret.existingSecretName`. Required/optional secret keys depend on configuration:
|
||||
- `DATABASE_URL` - External database connection (if not using built-in SQLite)
|
||||
- `ADMIN_TOKEN` - Admin panel access token
|
||||
- `SMTP_HOST`, `SMTP_PORT`, `SMTP_FROM`, `SMTP_USER`, `SMTP_PASS` - Email configuration
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Enable debug mode to troubleshoot issues by adding an init container with a shell:
|
||||
|
||||
```yaml
|
||||
vaultwarden:
|
||||
debug:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
This adds an `alpine:3.19` init container with `sleep infinity` that mounts all volumes and inherits env vars. You can exec into it to inspect the environment:
|
||||
|
||||
```bash
|
||||
kubectl exec -it <pod-name> -c debug -- sh
|
||||
```
|
||||
|
||||
When debug mode is enabled, liveness and readiness probes are disabled to prevent restarts.
|
||||
|
||||
### Pod not starting
|
||||
|
||||
```bash
|
||||
kubectl get pvc -n apps-roboces -l app.kubernetes.io/name=vaultwarden
|
||||
```
|
||||
|
||||
### Check logs
|
||||
|
||||
```bash
|
||||
kubectl logs vaultwarden-0 -n apps-roboces
|
||||
```
|
||||
|
||||
### Verify configmap
|
||||
|
||||
```bash
|
||||
kubectl get configmap vaultwarden-config -n apps-roboces -o yaml
|
||||
```
|
||||
37
k8s/charts/vaultwarden/templates/configmap.yaml
Normal file
37
k8s/charts/vaultwarden/templates/configmap.yaml
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "vaultwarden.fullname" . }}-config
|
||||
labels:
|
||||
{{- include "vaultwarden.labels" . | nindent 4 }}
|
||||
data:
|
||||
{{- if .Values.vaultwarden.config.database.url }}
|
||||
DATABASE_URL: {{ .Values.vaultwarden.config.database.url | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.vaultwarden.config.database.enableWal }}
|
||||
DB_ENABLE_WAL: {{ .Values.vaultwarden.config.database.enableWal | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.vaultwarden.config.database.connectionRetries }}
|
||||
DB_CONNECTION_RETRIES: {{ .Values.vaultwarden.config.database.connectionRetries | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.vaultwarden.config.database.timeout }}
|
||||
DB_TIMEOUT: {{ .Values.vaultwarden.config.database.timeout | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.vaultwarden.config.database.idleTimeout }}
|
||||
DB_IDLE_TIMEOUT: {{ .Values.vaultwarden.config.database.idleTimeout | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.vaultwarden.config.database.minConns }}
|
||||
DB_MIN_CONNS: {{ .Values.vaultwarden.config.database.minConns | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.vaultwarden.config.database.maxConns }}
|
||||
DB_MAX_CONNS: {{ .Values.vaultwarden.config.database.maxConns | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.vaultwarden.config.database.connInit }}
|
||||
DB_CONN_INIT: {{ .Values.vaultwarden.config.database.connInit | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.vaultwarden.config.webVault.enabled }}
|
||||
WEB_VAULT_ENABLED: {{ .Values.vaultwarden.config.webVault.enabled | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.vaultwarden.config.webVault.folder }}
|
||||
WEB_VAULT_FOLDER: {{ .Values.vaultwarden.config.webVault.folder | quote }}
|
||||
{{- end }}
|
||||
|
|
@ -1,23 +1,23 @@
|
|||
{{- if .Values.ingress.enabled -}}
|
||||
{{- if .Values.vaultwarden.ingress.enabled -}}
|
||||
{{- $fullName := include "vaultwarden.fullname" . -}}
|
||||
{{- $svcPort := .Values.service.port -}}
|
||||
{{- $svcPort := .Values.vaultwarden.service.port -}}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ $fullName }}
|
||||
labels:
|
||||
{{- include "vaultwarden.labels" . | nindent 4 }}
|
||||
{{- with .Values.ingress.annotations }}
|
||||
{{- with .Values.vaultwarden.ingress.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .Values.ingress.className }}
|
||||
ingressClassName: {{ .Values.ingress.className }}
|
||||
{{- if .Values.vaultwarden.ingress.className }}
|
||||
ingressClassName: {{ .Values.vaultwarden.ingress.className }}
|
||||
{{- end }}
|
||||
{{- if .Values.ingress.tls }}
|
||||
{{- if .Values.vaultwarden.ingress.tls }}
|
||||
tls:
|
||||
{{- range .Values.ingress.tls }}
|
||||
{{- range .Values.vaultwarden.ingress.tls }}
|
||||
- hosts:
|
||||
{{- range .hosts }}
|
||||
- {{ . | quote }}
|
||||
|
|
@ -26,7 +26,7 @@ spec:
|
|||
{{- end }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- range .Values.ingress.hosts }}
|
||||
{{- range .Values.vaultwarden.ingress.hosts }}
|
||||
- host: {{ .host | quote }}
|
||||
http:
|
||||
paths:
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ metadata:
|
|||
labels:
|
||||
{{- include "vaultwarden.labels" . | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
type: {{ .Values.vaultwarden.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: {{ .Values.service.targetPort }}
|
||||
- port: {{ .Values.vaultwarden.service.port }}
|
||||
targetPort: {{ .Values.vaultwarden.service.targetPort }}
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
|
|
|
|||
|
|
@ -6,103 +6,86 @@ metadata:
|
|||
{{- include "vaultwarden.labels" . | nindent 4 }}
|
||||
spec:
|
||||
serviceName: {{ include "vaultwarden.fullname" . }}-headless
|
||||
{{- if not .Values.autoscaling.enabled }}
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
{{- if not .Values.vaultwarden.autoscaling.enabled }}
|
||||
replicas: {{ .Values.vaultwarden.replicaCount }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "vaultwarden.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
{{- with .Values.podAnnotations }}
|
||||
{{- with .Values.vaultwarden.podAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "vaultwarden.selectorLabels" . | nindent 8 }}
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
{{- with .Values.vaultwarden.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||||
{{- toYaml .Values.vaultwarden.podSecurityContext | nindent 8 }}
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default (printf "%s-alpine" .Chart.AppVersion) }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
{{- toYaml .Values.vaultwarden.securityContext | nindent 12 }}
|
||||
image: "{{ .Values.vaultwarden.image.repository }}:{{ .Values.vaultwarden.image.tag | default (printf "%s-alpine" .Chart.AppVersion) }}"
|
||||
imagePullPolicy: {{ .Values.vaultwarden.image.pullPolicy }}
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: {{ include "vaultwarden.fullname" . }}-config
|
||||
- secretRef:
|
||||
name: {{ .Values.secret.existingSecretName | default (include "vaultwarden.fullname" .) }}
|
||||
env:
|
||||
{{- range $key, $value := .Values.env }}
|
||||
- name: {{ $key }}
|
||||
value: {{ $value | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.conf.db.database_url }}
|
||||
- name: DATABASE_URL
|
||||
value: {{ .Values.conf.db.database_url | quote }}
|
||||
{{- end }}
|
||||
{{- range $key, $value := .Values.conf.db }}
|
||||
{{- if and (ne $key "database_url") $value }}
|
||||
- name: {{ upper (print "DB_" $key) }}
|
||||
value: {{ $value | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- range $key, $value := .Values.conf.web_vault }}
|
||||
- name: {{ upper (print "WEB_VAULT_" $key) }}
|
||||
value: {{ $value | quote }}
|
||||
{{- end }}
|
||||
name: {{ .Values.vaultwarden.secret.existingSecretName | default (include "vaultwarden.fullname" .) }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
{{- toYaml .Values.livenessProbe | nindent 12 }}
|
||||
{{- toYaml .Values.vaultwarden.livenessProbe | nindent 12 }}
|
||||
readinessProbe:
|
||||
{{- toYaml .Values.readinessProbe | nindent 12 }}
|
||||
{{- toYaml .Values.vaultwarden.readinessProbe | nindent 12 }}
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
{{- toYaml .Values.vaultwarden.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /data
|
||||
{{- if .Values.persistence.existingClaim }}
|
||||
{{- if .Values.vaultwarden.persistence.existingClaim }}
|
||||
volumes:
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ .Values.persistence.existingClaim }}
|
||||
claimName: {{ .Values.vaultwarden.persistence.existingClaim }}
|
||||
{{- end }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
{{- with .Values.vaultwarden.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
{{- with .Values.vaultwarden.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
{{- with .Values.vaultwarden.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
restartPolicy: Always
|
||||
automountServiceAccountToken: false
|
||||
{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }}
|
||||
{{- if and .Values.vaultwarden.persistence.enabled (not .Values.vaultwarden.persistence.existingClaim) }}
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: data
|
||||
spec:
|
||||
accessModes:
|
||||
- {{ .Values.persistence.accessMode }}
|
||||
{{- if .Values.persistence.storageClass }}
|
||||
{{- if eq "-" .Values.persistence.storageClass }}
|
||||
- {{ .Values.vaultwarden.persistence.accessMode }}
|
||||
{{- if .Values.vaultwarden.persistence.storageClass }}
|
||||
{{- if eq "-" .Values.vaultwarden.persistence.storageClass }}
|
||||
storageClassName: ""
|
||||
{{- else }}
|
||||
storageClassName: {{ .Values.persistence.storageClass | quote }}
|
||||
storageClassName: {{ .Values.vaultwarden.persistence.storageClass | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.persistence.size }}
|
||||
storage: {{ .Values.vaultwarden.persistence.size }}
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -1,75 +1,76 @@
|
|||
# Configuration is managed in k8s/argo-apps/vaultwarden.yaml
|
||||
replicaCount: 1
|
||||
vaultwarden:
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: vaultwarden/server
|
||||
pullpolicy: Always
|
||||
tag: ""
|
||||
image:
|
||||
repository: vaultwarden/server
|
||||
pullpolicy: Always
|
||||
tag: ""
|
||||
|
||||
imagePullSecrets: []
|
||||
podAnnotations: {}
|
||||
podSecurityContext: {}
|
||||
securityContext: {}
|
||||
imagePullSecrets: []
|
||||
podAnnotations: {}
|
||||
podSecurityContext: {}
|
||||
securityContext: {}
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
ingress:
|
||||
enabled: false
|
||||
|
||||
conf:
|
||||
db:
|
||||
database_url: ""
|
||||
enable_db_wal: true
|
||||
connection_retries: 15
|
||||
timeout: 30
|
||||
idle_timeout: 600
|
||||
min_conns: 2
|
||||
max_conns: 10
|
||||
conn_init: ""
|
||||
web_vault:
|
||||
config:
|
||||
database:
|
||||
url: ""
|
||||
enableWal: true
|
||||
connectionRetries: 15
|
||||
timeout: 30
|
||||
idleTimeout: 600
|
||||
minConns: 2
|
||||
maxConns: 10
|
||||
connInit: ""
|
||||
webVault:
|
||||
enabled: true
|
||||
folder: "web-vault/"
|
||||
|
||||
persistence:
|
||||
enabled: true
|
||||
folder: "web-vault/"
|
||||
storageClass: "truenas-nfs-csi"
|
||||
accessMode: ReadWriteMany
|
||||
size: 5Gi
|
||||
existingClaim: ""
|
||||
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClass: "truenas-nfs-csi"
|
||||
accessMode: ReadWriteMany
|
||||
size: 5Gi
|
||||
existingClaim: ""
|
||||
autoscaling:
|
||||
enabled: false
|
||||
|
||||
autoscaling:
|
||||
enabled: false
|
||||
resources: {}
|
||||
|
||||
resources: {}
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: 80
|
||||
initialDelaySeconds: 30
|
||||
timeoutSeconds: 15
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: 80
|
||||
initialDelaySeconds: 30
|
||||
timeoutSeconds: 15
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 80
|
||||
initialDelaySeconds: 15
|
||||
timeoutSeconds: 2
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 80
|
||||
initialDelaySeconds: 15
|
||||
timeoutSeconds: 2
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
periodSeconds: 10
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
||||
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
||||
secret:
|
||||
enabled: true
|
||||
existingSecretName: "vaultwarden-kubernetes-secrets"
|
||||
data: {}
|
||||
|
||||
env: {}
|
||||
|
||||
secret:
|
||||
enabled: true
|
||||
existingSecretName: "vaultwarden-kubernetes-secrets"
|
||||
data: {}
|
||||
debug:
|
||||
enabled: false
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue