diff --git a/charts/keydb/Chart.yaml b/charts/keydb/Chart.yaml new file mode 100644 index 0000000..451418f --- /dev/null +++ b/charts/keydb/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v2 +name: keydb +description: A Helm chart for KeyDB multimaster setup +type: application +version: 0.0.34 diff --git a/charts/keydb/README.md b/charts/keydb/README.md new file mode 100644 index 0000000..8fbc2cc --- /dev/null +++ b/charts/keydb/README.md @@ -0,0 +1,134 @@ +# KeyDB + +[KeyDB](https://keydb.dev) clocks in at 5X faster than Redis (node vs node). KeyDB is a popular drop in Redis alternative that people flock to because it enables you to consolidate a lot of the complexities associated with Redis. KeyDB is multithreaded with the ability to use several storage mediums natively and scale vertically. The superior architecture is enabling KeyDB to become the bridge between cache layer and traditional databases offering performance and durability. + +## Introduction + +This chart bootstraps a [KeyDB](https://keydb.dev) highly available multi-master statefulset in a [Kubernetes](http://kubernetes.io) cluster using the Helm package manager. + +forked from https://github.com/Enapter/charts + +### Config Example: + +``` +configExtraArgs: + - client-output-buffer-limit: ["normal", "0", "0", "0"] + - client-output-buffer-limit: ["replica", "268435456", "67108864", "60"] + - client-output-buffer-limit: ["pubsub", "33554432", "8388608", "60"] + - save: ~ + - tcp-backlog "1024" +``` + +### Resulting File: + +``` +... + +exec keydb-server /etc/keydb/redis.conf \ + + ... + + --client-output-buffer-limit "normal" "0" "0" "0" \ + --client-output-buffer-limit "replica" "268435456" "67108864" "60" \ + --client-output-buffer-limit "pubsub" "33554432" "8388608" "60" \ + --save \ + --tcp-backlog "1024" \ + + ... +``` + +## Prerequisites + +- PV provisioner support in the underlying infrastructure if you want to enable persistence + +## Configuration + +The following table lists the configurable parameters of the KeyDB chart and their default values. + +| Parameter | Description | Default | +|:--------------------------------|:---------------------------------------------------|:------------------------------------------| +| `imageRepository` | KeyDB docker image | `eqalpha/keydb` | +| `imageTag` | KeyDB docker image tag | `x86_64_v6.3.2` | +| `imagePullPolicy` | K8s imagePullPolicy | `IfNotPresent` | +| `imagePullSecrets` | KeyDB Pod imagePullSecrets | `[]` | +| `nodes` | Number of KeyDB master pods | `3` | +| `password` | If enabled KeyDB servers are password-protected | `""` | +| `existingSecret` | If enabled password is taken from secret | `""` | +| `existingSecretPasswordKey` | Secret key name. | `"password"` | +| `port` | KeyDB service port clients connect to | `6379` | +| `portName` | KeyDB service port name in the Service spec | `server` | +| `threads` | KeyDB server-threads per node | `2` | +| `multiMaster` | KeyDB multi-master setup | `yes` | +| `activeReplicas` | KeyDB active replication setup | `yes` | +| `protectedMode` | KeyDB protection mode | `no` | +| `appendonly` | KeyDB appendonly setting | `no` | +| `configExtraArgs` | Additional configuration arguments for KeyDB | `[]` | +| `annotations` | KeyDB StatefulSet annotations | `{}` | +| `podAnnotations` | KeyDB pods annotations | `{}` | +| `tolerations` | KeyDB tolerations setting | `{}` | +| `nodeSelector` | KeyDB nodeSelector setting | `{}` | +| `topologySpreadConstraints` | KeyDB topologySpreadConstraints setting | `[]` | +| `affinity` | StatefulSet Affinity rules | Look values.yaml | +| `extraInitContainers` | Additional init containers for StatefulSet | `[]` | +| `extraContainers` | Additional sidecar containers for StatefulSet | `[]` | +| `extraVolumes` | Additional volumes for init and sidecar containers | `[]` | +| `livenessProbe.custom` | Custom LivenessProbe for KeyDB pods | `{}` | +| `readinessProbe.custom` | Custom ReadinessProbe for KeyDB pods | `{}` | +| `readinessProbeRandomUuid` | Random UUIDv4 for readiness GET probe | `90f717dd-0e68-43b8-9363-fddaad00d6c9` | +| `startupProbe.custom` | Custom StartupProbe for KeyDB pods | `{}` | +| `persistentVolume.enabled` | Should PVC be created via volumeClaimTemplates | `true` | +| `persistentVolume.accessModes` | Volume access modes | `[ReadWriteOnce]` | +| `persistentVolume.selector` | PVC selector. (In order to match existing PVs) | `{}` | +| `persistentVolume.size` | Size of the volume | `1Gi` | +| `persistentVolume.storageClass` | StorageClassName for volume | `` | +| `podDisruptionBudget` | podDisruptionBudget for KeyDB pods | Look values.yaml | +| `resources` | Resources for KeyDB containers | `{}` | +| `scripts.enabled` | Turn on health util scripts | `false` | +| `scripts.cleanupCoredumps` | Coredumps cleanup scripts | Look values.yaml | +| `scripts.cleanupTempfiles` | Tempfiles cleanup scripts | Look values.yaml | +| `scripts.securityContext` | SecurityContext for scripts container | `{}` | +| `keydb.securityContext` | SecurityContext for KeyDB container | `{}` | +| `securityContext` | SecurityContext for KeyDB pods | `{}` | +| `service.annotations` | Service annotations | `{}` | +| `service.appProtocol.enabled` | Turn on appProtocol fields in port specs | `false` | +| `loadBalancer.enabled` | Create LoadBalancer service | `false` | +| `loadBalancer.annotations` | Annotations for LB | `{}` | +| `loadBalancer.extraSpec` | Additional spec for LB | `{}` | +| `serviceAccount.enabled` | Use a dedicated ServiceAccount (SA) | `false` | +| `serviceAccount.create` | Create the SA (rather than use an existing one) | `true` | +| `serviceAccount.name` | Set the name of an existing SA or override created | `` | +| `serviceAccount.extraSpec` | Additional spec for the created SA | `{}` | +| `serviceMonitor.enabled` | Prometheus operator ServiceMonitor | `false` | +| `serviceMonitor.labels` | Additional labels for ServiceMonitor | `{}` | +| `serviceMonitor.annotations` | Additional annotations for ServiceMonitor | `{}` | +| `serviceMonitor.interval` | ServiceMonitor scrape interval | `30s` | +| `serviceMonitor.scrapeTimeout` | ServiceMonitor scrape timeout | `nil` | +| `exporter.enabled` | Prometheus Exporter sidecar contaner | `false` | +| `exporter.imageRepository` | Exporter Image | `oliver006/redis_exporter` | +| `exporter.imageTag` | Exporter Image Tag | `v1.48.0-alpine` | +| `exporter.pullPolicy` | Exporter imagePullPolicy | `IfNotPresent` | +| `exporter.port` | `prometheus.io/port` | `9121` | +| `exporter.portName` | Exporter service port name in the Service spec | `redis-exporter` | +| `exporter.scrapePath` | `prometheus.io/path` | `/metrics` | +| `exporter.livenessProbe` | LivenessProbe for sidecar Prometheus exporter | Look values.yaml | +| `exporter.readinessProbe` | ReadinessProbe for sidecar Prometheus exporter | Look values.yaml | +| `exporter.startupProbe` | StartupProbe for sidecar Prometheus exporter | Look values.yaml | +| `exporter.resources` | Resources for sidecar Prometheus container | `{}` | +| `exporter.securityContext` | SecurityContext for Prometheus exporter container | `{}` | +| `exporter.extraArgs` | Additional arguments for exporter | `[]` | + +## Using existingSecret + +When definining existingSecret (by default is "") password value is ignored. Password is taken from that secret, instead of being provided as plain text under values.yaml file. \ +Secret key must be `existingSecretPasswordKey` (*password* by default). \ +Example of of such secret: +```bash +kubectl create secret generic keydb-password --from-literal=password=KEYDB_PASSWORD +``` +Definition of existingSecret in that case: +```yaml +password: "" +existingSecret: keydb-password +existingSecretPasswordKey: password-key-in-secret-file +``` +It is important to use only one way of providing passwords: via plain text under values.yaml or using already existing secret. diff --git a/charts/keydb/templates/_helpers.tpl b/charts/keydb/templates/_helpers.tpl new file mode 100644 index 0000000..5952169 --- /dev/null +++ b/charts/keydb/templates/_helpers.tpl @@ -0,0 +1,68 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "keydb.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "keydb.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "keydb.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Common labels +*/}} +{{- define "keydb.labels" -}} +helm.sh/chart: {{ include "keydb.chart" . }} +{{ include "keydb.selectorLabels" . }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{/* +Selector labels +*/}} +{{- define "keydb.selectorLabels" -}} +app.kubernetes.io/name: {{ include "keydb.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "keydb.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "keydb.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{- define "common.tplvalues.render" -}} + {{- if typeIs "string" .value }} + {{- tpl .value .context }} + {{- else }} + {{- tpl (.value | toYaml) .context }} + {{- end }} +{{- end -}} \ No newline at end of file diff --git a/charts/keydb/templates/cm-health.yaml b/charts/keydb/templates/cm-health.yaml new file mode 100644 index 0000000..7034cfd --- /dev/null +++ b/charts/keydb/templates/cm-health.yaml @@ -0,0 +1,80 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "keydb.fullname" . }}-health + labels: + {{- include "keydb.labels" . | nindent 4 }} +data: + ping_readiness_local.sh: |- + #!/bin/bash + + set -e + + loading_response="LOADING KeyDB is loading the dataset in memory" + + [[ -n "${REDIS_PASSWORD}" ]] && export REDISCLI_AUTH="${REDIS_PASSWORD}" + response="$( + timeout -s 15 "${1}" \ + keydb-cli \ + -h localhost \ + -p "${REDIS_PORT}" \ + GET {{ .Values.readinessProbeRandomUuid }} + )" + if [ "${response}" = "${loading_response}" ]; then + echo "${response}" + exit 1 + fi + + ping_liveness_local.sh: |- + #!/bin/bash + + set -e + + [[ -n "${REDIS_PASSWORD}" ]] && export REDISCLI_AUTH="${REDIS_PASSWORD}" + response="$( + timeout -s 15 "${1}" \ + keydb-cli \ + -h localhost \ + -p "${REDIS_PORT}" \ + PING + )" + if [ "${response}" != "PONG" ]; then + echo "${response}" + exit 1 + fi + +{{- if .Values.scripts.enabled }} + + scripts_local.sh: |- + #!/bin/bash + + set -e + + script_dir="$(dirname "$0")" + while true; do +{{- if .Values.scripts.cleanupCoredumps.enabled }} + "${script_dir}/cleanup_coredumps.sh" +{{- end }} +{{- if .Values.scripts.cleanupTempfiles.enabled }} + "${script_dir}/cleanup_tempfiles.sh" +{{- end }} + sleep 60 + done +{{- end }} + +{{- if .Values.scripts.cleanupCoredumps.enabled }} + cleanup_coredumps.sh: |- + #!/bin/bash + + set -e + + find /data/ -type f -name "core.*" -mmin +{{ .Values.scripts.cleanupCoredumps.minutes }} -delete +{{- end }} +{{- if .Values.scripts.cleanupTempfiles.enabled }} + cleanup_tempfiles.sh: |- + #!/bin/bash + + set -e + + find /data/ -type f \( -name "temp-*.aof" -o -name "temp-*.rdb" \) -mmin +{{ .Values.scripts.cleanupTempfiles.minutes }} -delete +{{- end }} diff --git a/charts/keydb/templates/pdb.yaml b/charts/keydb/templates/pdb.yaml new file mode 100644 index 0000000..6af8774 --- /dev/null +++ b/charts/keydb/templates/pdb.yaml @@ -0,0 +1,18 @@ +{{- if .Values.podDisruptionBudget.enabled }} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "keydb.fullname" . }} + labels: + {{- include "keydb.labels" . | nindent 4 }} +spec: +{{- if .Values.podDisruptionBudget.maxUnavailable }} + maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }} +{{- end }} +{{- if .Values.podDisruptionBudget.minAvailable }} + minAvailable: {{ .Values.podDisruptionBudget.minAvailable }} +{{- end }} + selector: + matchLabels: + {{- include "keydb.selectorLabels" . | nindent 6 }} +{{- end }} diff --git a/charts/keydb/templates/sa.yaml b/charts/keydb/templates/sa.yaml new file mode 100644 index 0000000..db7569b --- /dev/null +++ b/charts/keydb/templates/sa.yaml @@ -0,0 +1,11 @@ +{{- if .Values.serviceAccount.enabled | and .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "keydb.serviceAccountName" . | quote }} + labels: + {{- include "keydb.labels" . | nindent 4 }} +{{- with .Values.serviceAccount.extraSpec }} +{{ toYaml . }} +{{- end }} +{{- end }} diff --git a/charts/keydb/templates/secret-utils.yaml b/charts/keydb/templates/secret-utils.yaml new file mode 100644 index 0000000..ec9b8fb --- /dev/null +++ b/charts/keydb/templates/secret-utils.yaml @@ -0,0 +1,46 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "keydb.fullname" . }}-utils + labels: + {{- include "keydb.labels" . | nindent 4 }} +type: Opaque +stringData: + server.sh: | + #!/bin/bash + set -euxo pipefail + + host="$(hostname)" + replicas=() + for node in {0..{{ (sub (.Values.nodes | int) 1) }}}; do + if [ "${host}" != "{{ include "keydb.fullname" . }}-${node}" ]; then + replicas+=("--replicaof {{ include "keydb.fullname" . }}-${node}.{{ include "keydb.fullname" . }}-headless {{ .Values.port }}") + fi + done + exec keydb-server /etc/keydb/redis.conf \ + --active-replica {{ .Values.activeReplicas | quote }} \ + --multi-master {{ .Values.multiMaster | quote }} \ + --appendonly {{ .Values.appendonly | quote }} \ + --bind "0.0.0.0" \ + --port "{{ .Values.internalPort }}" \ + --protected-mode {{ .Values.protectedMode | quote }} \ + --server-threads {{ .Values.threads | quote }} \ + {{- if .Values.existingSecret }} + --masterauth "${REDIS_PASSWORD}" \ + --requirepass "${REDIS_PASSWORD}" \ + {{- else if .Values.password }} + --masterauth {{ .Values.password | quote }} \ + --requirepass {{ .Values.password | quote }} \ + {{- end }} + {{- range $item := .Values.configExtraArgs }} + {{- range $key, $value := $item }} + {{- if kindIs "invalid" $value }} + --{{ $key }} \ + {{- else if kindIs "slice" $value }} + --{{ $key }}{{ range $value }} {{ . | quote }}{{ end }} \ + {{- else }} + --{{ $key }} {{ $value | quote }} \ + {{- end }} + {{- end }} + {{- end }} + "${replicas[@]}" diff --git a/charts/keydb/templates/sm.yaml b/charts/keydb/templates/sm.yaml new file mode 100644 index 0000000..8f94b4a --- /dev/null +++ b/charts/keydb/templates/sm.yaml @@ -0,0 +1,31 @@ +{{- if and .Values.exporter.enabled .Values.serviceMonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "keydb.fullname" . }} + labels: + {{- include "keydb.labels" . | nindent 4 }} + {{- if .Values.serviceMonitor.labels }} + {{- toYaml .Values.serviceMonitor.labels | nindent 4 }} + {{- end }} + {{- if .Values.serviceMonitor.annotations }} + annotations: + {{- toYaml .Values.serviceMonitor.annotations | nindent 4 }} + {{- end }} +spec: + selector: + matchLabels: + {{- include "keydb.labels" . | nindent 6 }} + namespaceSelector: + matchNames: + - {{.Release.Namespace }} + endpoints: + - port: redis-exporter + path: {{ .Values.exporter.scrapePath }} + {{- if .Values.serviceMonitor.interval }} + interval: {{ .Values.serviceMonitor.interval }} + {{- end }} + {{- if .Values.serviceMonitor.scrapeTimeout }} + scrapeTimeout: {{ .Values.serviceMonitor.scrapeTimeout }} + {{- end }} +{{- end }} diff --git a/charts/keydb/templates/sts.yaml b/charts/keydb/templates/sts.yaml new file mode 100644 index 0000000..7dd9927 --- /dev/null +++ b/charts/keydb/templates/sts.yaml @@ -0,0 +1,311 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "keydb.fullname" . }} + {{- if .Values.annotations }} + annotations: + {{- toYaml .Values.annotations | nindent 4 }} + {{- end }} + labels: + {{- include "keydb.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.nodes }} + serviceName: {{ include "keydb.fullname" . }}-headless + selector: + matchLabels: + {{- include "keydb.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + checksum/secret-utils: {{ include (print $.Template.BasePath "/secret-utils.yaml") . | sha256sum }} + {{- if .Values.exporter.enabled }} + prometheus.io/scrape: "true" + prometheus.io/path: "{{ .Values.exporter.scrapePath }}" + prometheus.io/port: "{{ .Values.exporter.port }}" + {{- end }} + {{- if .Values.podAnnotations }} + {{- toYaml .Values.podAnnotations | nindent 8 }} + {{- end }} + labels: + {{- include "keydb.labels" . | nindent 8 }} + spec: + affinity: + {{- include "common.tplvalues.render" (dict "value" .Values.affinity "context" $) | nindent 8 }} + {{- if .Values.extraInitContainers }} + initContainers: + {{- toYaml .Values.extraInitContainers | nindent 6 }} + {{- end }} + containers: + - name: keydb + {{- if .Values.image }} + image: {{ .Values.image }} + {{- else }} + image: {{ .Values.imageRepository }}:{{ .Values.imageTag }} + {{- end }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + command: + - /utils/server.sh + env: + - name: REDIS_PORT + value: {{ .Values.internalPort | quote }} + {{- if .Values.existingSecret }} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.existingSecret }} + key: {{ .Values.existingSecretPasswordKey }} + {{- else if .Values.password }} + - name: REDIS_PASSWORD + value: "{{ .Values.password }}" + {{- end }} + ports: + - name: {{ .Values.internalPortName }} + containerPort: {{ .Values.internalPort | int }} + protocol: TCP + {{- if .Values.livenessProbe.enabled }} + livenessProbe: + {{- if .Values.livenessProbe.custom }} + {{- toYaml .Values.livenessProbe.custom | nindent 10 }} + {{- else }} + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.livenessProbe.periodSeconds }} + # One second longer than command timeout should prevent generation of zombie processes. + timeoutSeconds: {{ add1 .Values.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.livenessProbe.failureThreshold }} + exec: + command: + - sh + - -c + - /health/ping_liveness_local.sh {{ .Values.livenessProbe.timeoutSeconds }} + {{- end }} + {{- end }} + {{- if .Values.readinessProbe.enabled }} + readinessProbe: + {{- if .Values.readinessProbe.custom }} + {{- toYaml .Values.readinessProbe.custom | nindent 10 }} + {{- else }} + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + # One second longer than command timeout should prevent generation of zombie processes. + timeoutSeconds: {{ add1 .Values.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + exec: + command: + - sh + - -c + - /health/ping_readiness_local.sh {{ .Values.readinessProbe.timeoutSeconds }} + {{- end }} + {{- end }} + {{- if .Values.startupProbe.enabled }} + startupProbe: + {{- if .Values.startupProbe.custom }} + {{- toYaml .Values.startupProbe.custom | nindent 10 }} + {{- else }} + periodSeconds: {{ .Values.startupProbe.periodSeconds }} + # One second longer than command timeout should prevent generation of zombie processes. + timeoutSeconds: {{ add1 .Values.startupProbe.timeoutSeconds }} + failureThreshold: {{ .Values.startupProbe.failureThreshold }} + exec: + command: + - sh + - -c + - /health/ping_readiness_local.sh {{ .Values.startupProbe.timeoutSeconds }} + {{- end }} + {{- end }} + {{- if .Values.lifecycle }} + lifecycle: + {{- toYaml .Values.lifecycle | nindent 10 }} + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 10 }} + securityContext: + {{- toYaml .Values.keydb.securityContext | nindent 10 }} + volumeMounts: + - name: health + mountPath: /health + - name: keydb-data + mountPath: /data + - name: utils + mountPath: /utils + readOnly: true + {{- if .Values.exporter.enabled }} + - name: redis-exporter + {{- if .Values.exporter.image }} + image: {{ .Values.exporter.image }} + {{- else }} + image: {{ .Values.exporter.imageRepository }}:{{ .Values.exporter.imageTag }} + {{- end }} + imagePullPolicy: {{ .Values.exporter.pullPolicy }} + args: + {{- range $item := .Values.exporter.extraArgs }} + {{- range $key, $value := $item }} + {{- if kindIs "invalid" $value }} + - --{{ $key }} + {{- else if kindIs "slice" $value }} + - --{{ $key }} + {{- range $value }} + - {{ . | quote }} + {{- end }} + {{- else }} + - --{{ $key }} + - {{ $value | quote }} + {{- end }} + {{- end }} + {{- end }} + env: + - name: REDIS_EXPORTER_WEB_LISTEN_ADDRESS + value: "0.0.0.0:{{ .Values.exporter.port }}" + - name: REDIS_EXPORTER_WEB_TELEMETRY_PATH + value: {{ .Values.exporter.scrapePath | quote }} + - name: REDIS_ADDR + value: "redis://localhost:{{ .Values.internalPort }}" + {{- if .Values.existingSecret }} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.existingSecret }} + key: {{ .Values.existingSecretPasswordKey }} + {{- else if .Values.password }} + - name: REDIS_PASSWORD + value: "{{ .Values.password }}" + {{- end }} + {{- if .Values.exporter.livenessProbe }} + livenessProbe: + {{- toYaml .Values.exporter.livenessProbe | nindent 10 }} + {{- end }} + {{- if .Values.exporter.readinessProbe }} + readinessProbe: + {{- toYaml .Values.exporter.readinessProbe | nindent 10 }} + {{- end }} + {{- if .Values.exporter.startupProbe }} + startupProbe: + {{- toYaml .Values.exporter.startupProbe | nindent 10 }} + {{- end }} + resources: + {{- toYaml .Values.exporter.resources | nindent 10 }} + securityContext: + {{- toYaml .Values.exporter.securityContext | nindent 10 }} + ports: + - name: {{ .Values.exporter.portName | quote }} + containerPort: {{ .Values.exporter.port }} + protocol: TCP + {{- end }} + {{- if .Values.scripts.enabled }} + - name: scripts + {{- if .Values.image }} + image: {{ .Values.image }} + {{- else }} + image: {{ .Values.imageRepository }}:{{ .Values.imageTag }} + {{- end }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + command: + - /health/scripts_local.sh + env: + - name: REDIS_PORT + value: {{ .Values.internalPort | quote }} + {{- if .Values.existingSecret }} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.existingSecret }} + key: {{ .Values.existingSecretPasswordKey }} + {{- else if .Values.password }} + - name: REDIS_PASSWORD + value: "{{ .Values.password }}" + {{- end }} + resources: + {{- toYaml .Values.scripts.resources | nindent 10 }} + securityContext: + {{- toYaml .Values.scripts.securityContext | nindent 10 }} + volumeMounts: + - name: health + mountPath: /health + - name: keydb-data + mountPath: /data + {{- end }} + {{- if .Values.extraContainers }} + {{- toYaml .Values.extraContainers | nindent 6 }} + {{- end }} + imagePullSecrets: + {{- toYaml .Values.imagePullSecrets | nindent 8 }} + securityContext: + {{- toYaml .Values.securityContext | nindent 8 }} + {{- if .Values.serviceAccount.enabled }} + serviceAccountName: {{ include "keydb.serviceAccountName" . | quote }} + {{- end }} + {{- if .Values.tolerations }} + tolerations: + {{- toYaml .Values.tolerations | nindent 8 }} + {{- end }} + {{- if .Values.nodeSelector }} + nodeSelector: + {{- toYaml .Values.nodeSelector | nindent 8 }} + {{- end }} + {{- if .Values.topologySpreadConstraints }} + topologySpreadConstraints: + {{- range .Values.topologySpreadConstraints }} + - labelSelector: + matchLabels: + {{- include "keydb.selectorLabels" $ | nindent 14 }} + topologyKey: {{ default "topology.kubernetes.io/zone" .topologyKey }} + maxSkew: {{ .maxSkew }} + {{- if .minDomains }} + minDomains: {{ .minDomains }} + {{- end }} + whenUnsatisfiable: {{ default "DoNotSchedule" .whenUnsatisfiable }} + {{- if .nodeAffinityPolicy }} + nodeAffinityPolicy: {{ .nodeAffinityPolicy }} + {{- end }} + {{- if .nodeTaintsPolicy }} + nodeTaintsPolicy: {{ .nodeTaintsPolicy }} + {{- end }} + {{- end }} + {{- end }} + volumes: + - name: health + configMap: + name: {{ include "keydb.fullname" . }}-health + defaultMode: 0755 + - name: utils + secret: + secretName: {{ include "keydb.fullname" . }}-utils + defaultMode: 0755 + items: + - key: server.sh + path: server.sh + {{- if not .Values.persistentVolume.enabled }} + - name: keydb-data + emptyDir: {{- toYaml .Values.persistentVolume.emptyDir | nindent 10 }} + {{- end }} + {{- if .Values.extraVolumes }} + {{- toYaml .Values.extraVolumes | nindent 6 }} + {{- end }} + {{- if .Values.persistentVolume.enabled }} + volumeClaimTemplates: + - metadata: + name: keydb-data + annotations: + {{- if .Values.persistentVolume.annotations }} + {{- toYaml .Values.persistentVolume.annotations | nindent 8 }} + {{- end }} + labels: + spec: + accessModes: + {{- toYaml .Values.persistentVolume.accessModes | nindent 8 }} + resources: + requests: + storage: {{ .Values.persistentVolume.size }} + {{- if .Values.persistentVolume.storageClass }} + {{- if (eq "-" .Values.persistentVolume.storageClass) }} + storageClassName: "" + {{ else }} + storageClassName: {{ .Values.persistentVolume.storageClass }} + {{- end }} + {{- end }} + {{- if .Values.persistentVolume.selector }} + selector: + {{- toYaml .Values.persistentVolume.selector | nindent 8 }} + {{- end }} + {{- end }} diff --git a/charts/keydb/templates/svc-headless.yaml b/charts/keydb/templates/svc-headless.yaml new file mode 100644 index 0000000..ea4298e --- /dev/null +++ b/charts/keydb/templates/svc-headless.yaml @@ -0,0 +1,22 @@ +# Headless service +apiVersion: v1 +kind: Service +metadata: + name: {{ include "keydb.fullname" . }}-headless + labels: + {{- include "keydb.labels" . | nindent 4 }} + annotations: + {{- toYaml .Values.service.annotations | nindent 4 }} +spec: + type: ClusterIP + clusterIP: None + ports: + - name: {{ .Values.portName | quote }} + port: {{ .Values.port | int }} + protocol: TCP + targetPort: {{ .Values.internalPortName | quote }} + {{- if .Values.service.appProtocol.enabled }} + appProtocol: redis + {{- end }} + selector: + {{- include "keydb.selectorLabels" . | nindent 4 }} diff --git a/charts/keydb/templates/svc-lb.yaml b/charts/keydb/templates/svc-lb.yaml new file mode 100644 index 0000000..4370915 --- /dev/null +++ b/charts/keydb/templates/svc-lb.yaml @@ -0,0 +1,26 @@ +{{- if .Values.loadBalancer.enabled }} +# Load balancer service +apiVersion: v1 +kind: Service +metadata: + name: {{ include "keydb.fullname" . }}-lb + labels: + {{- include "keydb.labels" . | nindent 4 }} + annotations: + {{- toYaml .Values.loadBalancer.annotations | nindent 4 }} +spec: + type: LoadBalancer + {{- if .Values.loadBalancer.extraSpec }} + {{- toYaml .Values.loadBalancer.extraSpec | nindent 2 }} + {{- end }} + ports: + - name: {{ .Values.portName | quote }} + port: {{ .Values.port | int }} + protocol: TCP + targetPort: {{ .Values.internalPortName | quote }} + {{- if .Values.service.appProtocol.enabled }} + appProtocol: redis + {{- end }} + selector: + {{- include "keydb.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/charts/keydb/templates/svc.yaml b/charts/keydb/templates/svc.yaml new file mode 100644 index 0000000..1d1ef1d --- /dev/null +++ b/charts/keydb/templates/svc.yaml @@ -0,0 +1,28 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "keydb.fullname" . }} + labels: + {{- include "keydb.labels" . | nindent 4 }} + annotations: + {{- toYaml .Values.service.annotations | nindent 4 }} +spec: + type: ClusterIP + ports: + - name: {{ .Values.portName | quote }} + port: {{ .Values.port | int }} + protocol: TCP + targetPort: {{ .Values.internalPortName | quote }} + {{- if .Values.service.appProtocol.enabled }} + appProtocol: redis + {{- end }} + - name: {{ .Values.exporter.portName | quote }} + port: {{ .Values.exporter.port | int }} + protocol: TCP + targetPort: {{ .Values.exporter.portName | quote }} + {{- if .Values.service.appProtocol.enabled }} + appProtocol: http + {{- end }} + selector: + {{- include "keydb.selectorLabels" . | nindent 4 }} + sessionAffinity: ClientIP diff --git a/charts/keydb/values.yaml b/charts/keydb/values.yaml new file mode 100644 index 0000000..fe03d53 --- /dev/null +++ b/charts/keydb/values.yaml @@ -0,0 +1,279 @@ +nameOverride: "" +fullnameOverride: "" + +imageRepository: eqalpha/keydb +imageTag: x86_64_v6.3.4 +imagePullPolicy: IfNotPresent +imagePullSecrets: [] + +nodes: 3 + +password: "" +existingSecret: "" +existingSecretPasswordKey: "password" +port: 6379 +portName: server +internalPort: 6379 +internalPortName: keydb + +threads: 2 + +multiMaster: "yes" +activeReplicas: "yes" +protectedMode: "no" +appendonly: "no" + +annotations: {} + +configExtraArgs: [] + # - somesimple: "argument" + # - client-output-buffer-limit: ["normal", "0", "0", "0"] + # - client-output-buffer-limit: ["replica", "268435456", "67108864", "60"] + # - client-output-buffer-limit: ["pubsub", "33554432", "8388608", "60"] + +podAnnotations: {} + +tolerations: {} + # - effect: NoSchedule + # key: key + # operator: Equal + # value: value + +nodeSelector: {} + # topology.kubernetes.io/region: some-region + +topologySpreadConstraints: [] +# - maxSkew: 1 +# ## Optional keys +# # whenUnsatisfiable: DoNotSchedule +# # topologyKey: "topology.kubernetes.io/zone" +# # minDomains: 1 +# # nodeAffinityPolicy: Honor +# # nodeTaintsPolicy: Honor + +affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: app.kubernetes.io/name + operator: In + values: + - '{{ include "keydb.name" . }}' + - key: app.kubernetes.io/instance + operator: In + values: + - '{{ .Release.Name }}' + topologyKey: "kubernetes.io/hostname" + +additionalAffinities: {} + # nodeAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # nodeSelectorTerms: + # - matchExpressions: + # - key: node_pool + # operator: In + # values: somenodepool + +podDisruptionBudget: + enabled: true + maxUnavailable: 1 + +# Additional init containers +extraInitContainers: [] + +# Additional sidecar containers +extraContainers: [] +# - name: backup +# image: minio/mc:latest + +# Volumes that can be used in init and sidecar containers +extraVolumes: [] +# - name: volume-from-secret +# secret: +# secretName: secret-to-mount +# - name: empty-dir-volume +# emptyDir: {} + +# Liveness Probe +livenessProbe: + enabled: true + custom: {} + # tcpSocket: + # port: keydb + # initialDelaySeconds: 30 + initialDelaySeconds: 20 + periodSeconds: 5 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + +# Readiness Probe +readinessProbe: + enabled: true + custom: {} + # tcpSocket: + # port: keydb + # initialDelaySeconds: 30 + initialDelaySeconds: 20 + periodSeconds: 5 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 5 + +# Random UUID for readiness GET probe +readinessProbeRandomUuid: "90f717dd-0e68-43b8-9363-fddaad00d6c9" + +# Startup Probe +startupProbe: + enabled: true + custom: {} + # tcpSocket: + # port: keydb + periodSeconds: 5 + timeoutSeconds: 1 + failureThreshold: 24 + +# Lifecycle Hooks +lifecycle: {} + # preStop: + # exec: + # command: + # - sh + # - -c + # - "sleep 15; kill 1" + +persistentVolume: + enabled: true + accessModes: + - ReadWriteOnce + selector: {} + # matchLabels: + # release: "stable" + # matchExpressions: + # - {key: environment, operator: In, values: [dev]} + size: 1Gi + + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + + # If persistentVolume is disable, use this to configure the empty dir + emptyDir: {} + +resources: {} + +# Please read https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/#enabling-unsafe-sysctls +# before sysctls setup +securityContext: {} + # sysctls: + # - name: net.core.somaxconn + # value: "512" + # - name: vm.overcommit_memory + # value: "1" + +keydb: + # Container security context + securityContext: {} + +service: + annotations: {} + appProtocol: + enabled: false + +serviceAccount: + enabled: false + create: true + name: "" + + # extraSpec: + # automountServiceAccountToken: false + # imagePullSecrets: + # - name: pull-secret + extraSpec: {} + +loadBalancer: + enabled: false + + # annotations: + # service.beta.kubernetes.io/aws-load-balancer-type: nlb + annotations: {} + + # extraSpec: + # loadBalancerIP: "1.2.3.4" + # loadBalancerSourceRanges: + # - 1.2.3.4/32 + extraSpec: {} + +# Prometheus-operator ServiceMonitor +serviceMonitor: + # Redis exporter must also be enabled + enabled: false + labels: + annotations: + interval: 30s + # scrapeTimeout: 20s + +# Redis exporter +exporter: + enabled: false + imageRepository: oliver006/redis_exporter + imageTag: v1.48.0-alpine + pullPolicy: IfNotPresent + + # Prometheus port & scrape path + port: 9121 + portName: redis-exporter + scrapePath: /metrics + + # Liveness Probe + livenessProbe: + httpGet: + path: /health + port: redis-exporter + + # Readiness Probe + readinessProbe: + httpGet: + path: /health + port: redis-exporter + + # Startup Probe + startupProbe: + httpGet: + path: /health + port: redis-exporter + failureThreshold: 24 + periodSeconds: 5 + + # CPU/Memory resource limits/requests + resources: {} + + # Container security context + securityContext: {} + + # Additional args for redis exporter + extraArgs: [] + # - somesimple: "argument" + # - client-output-buffer-limit: ["normal", "0", "0", "0"] + # - client-output-buffer-limit: ["replica", "268435456", "67108864", "60"] + # - client-output-buffer-limit: ["pubsub", "33554432", "8388608", "60"] + +scripts: + enabled: false + # CPU/Memory resource limits/requests + resources: {} + # Container security context + securityContext: {} + cleanupCoredumps: + enabled: false + minutes: 1440 + cleanupTempfiles: + enabled: true + minutes: 60