feat: Automatically use Deployment instead of StatefulSet (#59)

This commit is contained in:
Philipp Kolberg
2023-12-09 06:49:31 +01:00
committed by GitHub
parent 05c1a351dc
commit ba5ff95484
13 changed files with 405 additions and 137 deletions

View File

@@ -11,6 +11,10 @@ This [Helm](https://helm.sh/docs/) chart is used to deploy `vaultwarden` with a
The `vaultwarden` project can be found [here](https://github.com/dani-garcia/vaultwarden). To learn more about Vaultwarden, please visit the [wiki](https://github.com/dani-garcia/vaultwarden/wiki).
### Change of Resource Type in Versions >= 0.18.0
Starting from version 0.18.0, when a stateless configuration is detected that utilizes an external database and persistent storage, a `Deployment` is automatically used in favor of the current `StatefulSet`. This enables running multiple pods simultaneously, thereby enhancing the processes of updates, rollbacks, and scalability for load balancing. This automatic detection can be overridden by manually specifying a `resourceType`.
## Prerequisites
- Kubernetes >= 1.12

View File

@@ -8,10 +8,10 @@ keywords:
sources:
- https://github.com/guerzon/vaultwarden
- https://github.com/dani-garcia/vaultwarden
appVersion: 1.29.2
appVersion: 1.30.1
maintainers:
- name: guerzon
email: guerzon@proton.me
url: https://github.com/guerzon
version: 0.17.0
version: 0.18.0
kubeVersion: ">=1.12.0-0"

View File

@@ -66,3 +66,29 @@ Return the database string
{{- $var := print .Values.database.type "://" .Values.database.username ":" .Values.database.password "@" .Values.database.host (include "dbPort" . ) "/" .Values.database.dbName }}
{{- printf "%s" $var }}
{{- end -}}
{{/*
Return the appropriate apiVersion for podDisruptionBudget.
*/}}
{{- define "podDisruptionBudget.apiVersion" -}}
{{- if semverCompare ">=1.21-0" .Capabilities.KubeVersion.Version -}}
{{- print "policy/v1" -}}
{{- else -}}
{{- print "policy/v1beta1" -}}
{{- end -}}
{{- end -}}
{{/*
Determine whether to use deployment or statefulset
*/}}
{{- define "vaultwarden.resourceType" -}}
{{- if .Values.resourceType }}
{{- .Values.resourceType }}
{{- else }}
{{- if (and .Values.data (ne .Values.database.type "default")) }}
{{- "Deployment" }}
{{- else }}
{{- "StatefulSet" }}
{{- end }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,128 @@
{{- define "vaultwarden.podSpec" }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.initContainers }}
initContainers:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- image: {{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
name: vaultwarden
envFrom:
- configMapRef:
name: {{ include "vaultwarden.fullname" . }}
env:
{{- if or (.Values.smtp.username.value) (.Values.smtp.username.existingSecretKey )}}
- name: SMTP_USERNAME
valueFrom:
secretKeyRef:
name: {{ default (include "vaultwarden.fullname" .) .Values.smtp.existingSecret }}
key: {{ default "SMTP_USERNAME" .Values.smtp.username.existingSecretKey }}
{{- end }}
{{- if or (.Values.smtp.password.value) (.Values.smtp.password.existingSecretKey )}}
- name: SMTP_PASSWORD
valueFrom:
secretKeyRef:
name: {{ default (include "vaultwarden.fullname" .) .Values.smtp.existingSecret }}
key: {{ default "SMTP_PASSWORD" .Values.smtp.password.existingSecretKey }}
{{- end }}
{{- if .Values.adminToken }}
- name: ADMIN_TOKEN
valueFrom:
secretKeyRef:
name: {{ default (include "vaultwarden.fullname" .) .Values.adminToken.existingSecret }}
key: {{ default "ADMIN_TOKEN" .Values.adminToken.existingSecretKey }}
{{- else }}
- name: DISABLE_ADMIN_TOKEN
value: "true"
{{- end }}
{{- if ne "default" .Values.database.type }}
- name: DATABASE_URL
{{- if .Values.database.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.database.existingSecret }}
key: {{ .Values.database.existingSecretKey }}
{{- else }}
{{- if .Values.database.uriOverride }}
value: {{ .Values.database.uriOverride }}
{{- else }}
value: {{ include "dbString" . | quote }}
{{- end }}
{{- end }}
{{- end }}
ports:
- containerPort: 8080
name: http
protocol: TCP
- containerPort: {{ .Values.websocket.port }}
name: websocket
protocol: TCP
{{- if or (.Values.data) (.Values.attachments) }}
volumeMounts:
{{- with .Values.data }}
- name: {{ .name }}
mountPath: {{ default "/data" .path }}
{{- end }}
{{- with .Values.attachments }}
- name: {{ .name }}
mountPath: {{ default "/data/attachments" .path }}
{{- end }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if .Values.livenessProbe.enabled }}
livenessProbe:
httpGet:
path: /alive
port: http
initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.livenessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }}
successThreshold: {{ .Values.livenessProbe.successThreshold }}
failureThreshold: {{ .Values.livenessProbe.failureThreshold }}
{{- end }}
{{- if .Values.readinessProbe.enabled }}
readinessProbe:
httpGet:
path: /alive
port: http
initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.readinessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }}
successThreshold: {{ .Values.readinessProbe.successThreshold }}
failureThreshold: {{ .Values.readinessProbe.failureThreshold }}
{{- end }}
{{- if .Values.startupProbe.enabled }}
startupProbe:
httpGet:
path: /alive
port: http
initialDelaySeconds: {{ .Values.startupProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.startupProbe.periodSeconds }}
timeoutSeconds: {{ .Values.startupProbe.timeoutSeconds }}
successThreshold: {{ .Values.startupProbe.successThreshold }}
failureThreshold: {{ .Values.startupProbe.failureThreshold }}
{{- end }}
{{- with .Values.sidecars }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if .Values.serviceAccount.create }}
serviceAccountName: {{ .Values.serviceAccount.name }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,38 @@
{{- define "vaultwarden.pvcSpec" }}
{{- if (or .Values.data .Values.attachments) -}}
volumeClaimTemplates:
{{- with .Values.data }}
- metadata:
name: {{ .name }}
labels:
{{- include "vaultwarden.labels" $ | nindent 10 }}
annotations:
meta.helm.sh/release-name: {{ $.Release.Name | quote }}
meta.helm.sh/release-namespace: {{ $.Release.Namespace | quote }}
spec:
accessModes:
- "ReadWriteOnce"
resources:
requests:
storage: {{ .size }}
{{- with .class }}
storageClassName: {{ . | quote }}
{{- end }}
{{- end }}
{{- with .Values.attachments }}
- metadata:
name: {{ .name }}
labels:
{{- include "vaultwarden.labels" $ | nindent 10 }}
spec:
accessModes:
- "ReadWriteOnce"
resources:
requests:
storage: {{ .size }}
{{- with .class }}
storageClassName: {{ . | quote }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}

View File

@@ -6,6 +6,10 @@ metadata:
labels:
app.kubernetes.io/component: vaultwarden
{{- include "vaultwarden.labels" . | nindent 4 }}
{{- with .Values.configMapAnnotations }}
annotations:
{{- . | toYaml | nindent 4 }}
{{- end }}
data:
DOMAIN: {{ .Values.domain | quote }}
{{- if and .Values.smtp.host .Values.smtp.from }}
@@ -32,6 +36,7 @@ data:
{{- if .Values.attachments }}
ATTACHMENTS_FOLDER: {{ default "/data/attachments" .Values.attachments.path | quote }}
{{- end }}
ROCKET_ADDRESS: {{ .Values.rocket.address | quote }}
ROCKET_PORT: {{ .Values.rocket.port | quote }}
ROCKET_WORKERS: {{ .Values.rocket.workers | quote }}
SHOW_PASSWORD_HINT: {{ .Values.showPassHint | quote }}

View File

@@ -0,0 +1,50 @@
{{- if eq (include "vaultwarden.resourceType" .) "Deployment" }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "vaultwarden.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
app.kubernetes.io/component: vaultwarden
{{- include "vaultwarden.labels" . | nindent 4 }}
{{- range $key, $value := .Values.commonLabels }}
{{- printf "%s: %s" $key (tpl $value $ | quote) | nindent 4 }}
{{- end }}
{{- with .Values.commonAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/component: vaultwarden
{{- include "vaultwarden.selectorLabels" . | nindent 6 }}
{{- with .Values.strategy }}
strategy:
{{- . | toYaml | nindent 8 }}
{{- end }}
template:
metadata:
labels:
app.kubernetes.io/component: vaultwarden
{{- include "vaultwarden.selectorLabels" . | nindent 8 }}
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha1sum }}
checksum/secret: {{ include (print $.Template.BasePath "/secrets.yaml") . | sha1sum }}
{{- with .Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- include "vaultwarden.podSpec" . | nindent 6 }}
volumes:
{{- range $pvc := (fromYaml (include "vaultwarden.pvcSpec" .)).volumeClaimTemplates }}
{{- $newName := printf "%s-%s-0" $pvc.metadata.name $.Release.Name }}
- name: {{ $pvc.metadata.name }}
persistentVolumeClaim:
claimName: {{ $newName }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,22 @@
{{- if .Values.podDisruptionBudget.enabled }}
{{- $component := .Values.podDisruptionBudget }}
apiVersion: {{ include "podDisruptionBudget.apiVersion" . }}
kind: PodDisruptionBudget
metadata:
name: vaultwarden
namespace: {{ .Release.Namespace }}
labels:
k8s-app: vaultwarden
app.kubernetes.io/name: vaultwarden
app.kubernetes.io/part-of: vaultwarden
spec:
{{- with $component.maxUnavailable }}
maxUnavailable: {{ . }}
{{- end }}
{{- with $component.minAvailable }}
minAvailable: {{ . }}
{{- end }}
selector:
matchLabels:
k8s-app: vaultwarden
{{- end }}

View File

@@ -0,0 +1,10 @@
{{- if eq (include "vaultwarden.resourceType" .) "Deployment" }}
{{- range $pvc := (fromYaml (include "vaultwarden.pvcSpec" .)).volumeClaimTemplates }}
---
apiVersion: v1
kind: PersistentVolumeClaim
{{- $newName := printf "%s-%s-0" $pvc.metadata.name $.Release.Name }}
{{- $newPvc := merge (dict "metadata" (dict "name" $newName)) $pvc }}
{{ $newPvc | toYaml }}
{{- end }}
{{- end }}

View File

@@ -13,7 +13,7 @@ data:
SMTP_PASSWORD: {{ .Values.smtp.password.value | b64enc | quote }}
SMTP_USERNAME: {{ .Values.smtp.username.value | b64enc | quote }}
{{- end }}
{{- if ( .Values.adminToken ) }}
{{- if not ( .Values.adminToken.existingSecret ) }}
ADMIN_TOKEN: {{ .Values.adminToken.value | b64enc | quote }}
{{- end }}
{{ end }}

View File

@@ -28,3 +28,6 @@ spec:
protocol: TCP
targetPort: {{ .Values.websocket.port }}
{{- end }}
{{- if .Values.service.ipFamilyPolicy }}
ipFamilyPolicy: {{ .Values.service.ipFamilyPolicy }}
{{- end }}

View File

@@ -1,3 +1,4 @@
{{- if eq (include "vaultwarden.resourceType" .) "StatefulSet" }}
apiVersion: apps/v1
kind: StatefulSet
metadata:
@@ -6,10 +7,10 @@ metadata:
labels:
app.kubernetes.io/component: vaultwarden
{{- include "vaultwarden.labels" . | nindent 4 }}
{{- range $key, $value := .Values.statefulsetlabels }}
{{- range $key, $value := .Values.commonLabels }}
{{- printf "%s: %s" $key (tpl $value $ | quote) | nindent 4 }}
{{- end }}
{{- with .Values.statefulsetAnnotations }}
{{- with .Values.commonAnnotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
@@ -20,6 +21,10 @@ spec:
matchLabels:
app.kubernetes.io/component: vaultwarden
{{- include "vaultwarden.selectorLabels" . | nindent 6 }}
{{- with .Values.strategy }}
updateStrategy:
{{- . | toYaml | nindent 8 }}
{{- end }}
template:
metadata:
labels:
@@ -35,133 +40,9 @@ spec:
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- if .Values.nodeSelector }}
nodeSelector:
{{- toYaml .Values.nodeSelector | nindent 8 }}
{{- end }}
{{- if .Values.affinity }}
affinity:
{{- toYaml .Values.affinity | nindent 8 }}
{{- end }}
{{- if .Values.tolerations }}
tolerations:
{{- toYaml .Values.tolerations | nindent 8 }}
{{- end }}
{{- if .Values.initContainers }}
initContainers:
{{- toYaml .Values.initContainers | nindent 8 }}
{{- end }}
containers:
- image: {{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
name: vaultwarden
envFrom:
- configMapRef:
name: {{ include "vaultwarden.fullname" . }}
env:
{{- if or (.Values.smtp.username.value) (.Values.smtp.username.existingSecretKey )}}
- name: SMTP_USERNAME
valueFrom:
secretKeyRef:
name: {{ default (include "vaultwarden.fullname" .) .Values.smtp.existingSecret }}
key: {{ default "SMTP_USERNAME" .Values.smtp.username.existingSecretKey }}
{{- end }}
{{- if or (.Values.smtp.password.value) (.Values.smtp.password.existingSecretKey )}}
- name: SMTP_PASSWORD
valueFrom:
secretKeyRef:
name: {{ default (include "vaultwarden.fullname" .) .Values.smtp.existingSecret }}
key: {{ default "SMTP_PASSWORD" .Values.smtp.password.existingSecretKey }}
{{- end }}
{{- if .Values.adminToken }}
- name: ADMIN_TOKEN
valueFrom:
secretKeyRef:
name: {{ default (include "vaultwarden.fullname" .) .Values.adminToken.existingSecret }}
key: {{ default "ADMIN_TOKEN" .Values.adminToken.existingSecretKey }}
{{- else }}
- name: DISABLE_ADMIN_TOKEN
value: "true"
{{- end }}
{{- if ne "default" .Values.database.type }}
- name: DATABASE_URL
{{- if .Values.database.existingSecret }}
valueFrom:
secretKeyRef:
name: {{ .Values.database.existingSecret }}
key: {{ .Values.database.existingSecretKey }}
{{- else }}
{{- if .Values.database.uriOverride }}
value: {{ .Values.database.uriOverride }}
{{- else }}
value: {{ include "dbString" . | quote }}
{{- end }}
{{- end }}
{{- end }}
ports:
- containerPort: 8080
name: http
protocol: TCP
- containerPort: {{ .Values.websocket.port }}
name: websocket
protocol: TCP
readinessProbe:
httpGet:
path: /alive
port: 8080
initialDelaySeconds: 5
{{- if or (.Values.data) (.Values.attachments) }}
volumeMounts:
{{- if .Values.data }}
- name: {{ .Values.data.name }}
mountPath: {{ default "/data" .Values.data.path }}
{{- end }}
{{- if .Values.attachments }}
- name: {{ .Values.attachments.name }}
mountPath: {{ default "/data/attachments" .Values.attachments.path }}
{{- end }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- if .Values.sidecars }}
{{- toYaml .Values.sidecars | nindent 8 }}
{{- end }}
{{- if .Values.serviceAccount.create }}
serviceAccountName: {{ .Values.serviceAccount.name }}
{{- end }}
{{- if (or .Values.data .Values.attachments) }}
{{- include "vaultwarden.podSpec" . | nindent 6 }}
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain
whenScaled: Retain
volumeClaimTemplates:
{{- if .Values.data }}
- metadata:
name: {{ .Values.data.name }}
labels:
{{- include "vaultwarden.labels" . | nindent 10 }}
spec:
accessModes:
- "ReadWriteOnce"
resources:
requests:
storage: {{ .Values.data.size }}
{{- if .Values.data.class }}
storageClassName: {{ .Values.data.class | quote }}
{{- end }}
{{- end }}
{{- if .Values.attachments }}
- metadata:
name: {{ .Values.attachments.name }}
labels:
{{- include "vaultwarden.labels" . | nindent 10 }}
spec:
accessModes:
- "ReadWriteOnce"
resources:
requests:
storage: {{ .Values.attachments.size }}
{{- if .Values.attachments.class }}
storageClassName: {{ .Values.attachments.class | quote }}
{{- end }}
{{- end }}
{{- end }}
{{- include "vaultwarden.pvcSpec" . | nindent 2 }}
{{- end }}

View File

@@ -11,7 +11,7 @@ image:
## @param image.tag Vaultwarden image tag
## Ref: https://hub.docker.com/r/vaultwarden/server/tags
##
tag: "1.29.2-alpine"
tag: "1.30.1-alpine"
## @param image.pullPolicy Vaultwarden image pull policy
## ref: https://kubernetes.io/docs/user-guide/images/#pre-pulling-images
##
@@ -39,12 +39,22 @@ websocket:
## @param rocket.workers Rocket number of workers
##
rocket:
address: "0.0.0.0"
port: "8080"
workers: "10"
## @param webVaultEnabled Enable Web Vault
##
webVaultEnabled: "true"
## @section Overwrite automatic resource type detection
## Can be either Deployment or StatefulSet
##
resourceType: ""
## @param configMapAnnotations Add extra annotations to the configmap
##
configMapAnnotations: {}
## @section Pod configuration
##
## @param podAnnotations Add extra annotations to the pod
@@ -173,6 +183,78 @@ ingress:
## - Support for multiple TLS hostnames.
##
## @section Probe Parameters
##
## Liveness probe configuration
##
livenessProbe:
## @param livenessProbe.enabled Enable liveness probe
##
enabled: true
## @param livenessProbe.initialDelaySeconds Delay before liveness probe is initiated
##
initialDelaySeconds: 200
## @param livenessProbe.timeoutSeconds How long to wait for the probe to succeed
##
timeoutSeconds: 1
## @param livenessProbe.periodSeconds How often to perform the probe
##
periodSeconds: 10
## @param livenessProbe.successThreshold Minimum consecutive successes for the probe to be considered successful
##
successThreshold: 1
## @param livenessProbe.failureThreshold Minimum consecutive failures for the probe to be considered failed
##
failureThreshold: 10
## Readiness probe configuration
##
readinessProbe:
## @param readinessProbe.enabled Enable readiness probe
##
enabled: true
## @param readinessProbe.initialDelaySeconds Delay before readiness probe is initiated
##
initialDelaySeconds: 5
## @param readinessProbe.timeoutSeconds How long to wait for the probe to succeed
##
timeoutSeconds: 1
## @param readinessProbe.periodSeconds How often to perform the probe
##
periodSeconds: 10
## @param readinessProbe.successThreshold Minimum consecutive successes for the probe to be considered successful
##
successThreshold: 1
## @param readinessProbe.failureThreshold Minimum consecutive failures for the probe to be considered failed
##
failureThreshold: 3
## Startup probe configuration
##
startupProbe:
## @param startupProbe.enabled Enable startup probe
##
enabled: false
## @param startupProbe.initialDelaySeconds Delay before startup probe is initiated
##
initialDelaySeconds: 60
## @param startupProbe.timeoutSeconds How long to wait for the probe to succeed
##
timeoutSeconds: 1
## @param startupProbe.periodSeconds How often to perform the probe
##
periodSeconds: 10
## @param startupProbe.successThreshold Minimum consecutive successes for the probe to be considered successful
##
successThreshold: 1
## @param startupProbe.failureThreshold Minimum consecutive failures for the probe to be considered failed
##
failureThreshold: 10
securityContext: {}
## Service configuration
service:
## @param service.type Service type
@@ -184,6 +266,8 @@ service:
## @param service.labels Additional labels for the service
##
labels: {}
## @param service.ipFamilyPolicy IP family policy for the service
ipFamilyPolicy: "SingleStack"
## @section Database Configuration
##
@@ -345,13 +429,13 @@ affinity: {}
##
tolerations: []
## @param statefulsetlabels Additional labels for the statefulset
## @param commonLabels Additional labels for the deployment or statefulset
##
statefulsetlabels: {}
commonLabels: {}
## @param statefulsetAnnotations Annotations for the statefulset
## @param commonAnnotations Annotations for the deployment or statefulset
##
statefulsetAnnotations: {}
commonAnnotations: {}
## @param pushNotifications Enable mobile push notifications
## Supported since 1.29.0.
@@ -374,3 +458,20 @@ resources: {}
# requests:
# cpu: 50m
# memory: 256Mi
strategy: {}
# type: RollingUpdate
# rollingUpdate:
# maxSurge: 1
# maxUnavailable: 0
# PodDisruptionBudget settings
podDisruptionBudget:
# -- enable PodDisruptionBudget
# ref: https://kubernetes.io/docs/concepts/workloads/pods/disruptions/
enabled: false
# -- Minimum number/percentage of pods that should remain scheduled.
# When it's set, maxUnavailable must be disabled by `maxUnavailable: null`
minAvailable: 1
# -- Maximum number/percentage of pods that may be made unavailable
maxUnavailable: null