Adding a Helm chart to install in Kubernetes (#1010)

* first pass at a helm chart

* more cleanup

* adding more stuff

* trying to generate the DB password

* more fixes and hardening

* Don't need to bake this in

* switching these back to the defaults

* more cleanup

* bumping the app version to match release

* some fixes

* updating volumeMounts too

* and disabling the PVCs if we aren't using them

* fixing the spacing

---------

Co-authored-by: Aditya Chandel <8075870+adityachandelgit@users.noreply.github.com>
This commit is contained in:
Adam Compton
2025-09-07 08:27:13 -07:00
committed by GitHub
parent 089e7fd781
commit ae24cca0e3
9 changed files with 458 additions and 0 deletions

1
.gitignore vendored
View File

@@ -4,6 +4,7 @@ build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
example-chart/charts/
## MacOS ###
.DS_Store

29
example-chart/Chart.yaml Normal file
View File

@@ -0,0 +1,29 @@
apiVersion: v2
name: booklore
description: A Helm chart for [Booklore](https://github.com/booklore-app/booklore)
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.0
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "0.38.2"
dependencies:
- name: mariadb
version: 22.0.*
repository: oci://registry-1.docker.io/bitnamicharts

View File

@@ -0,0 +1,62 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "booklore.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 "booklore.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 "booklore.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "booklore.labels" -}}
helm.sh/chart: {{ include "booklore.chart" . }}
{{ include "booklore.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "booklore.selectorLabels" -}}
app.kubernetes.io/name: {{ include "booklore.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "booklore.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "booklore.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,141 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "booklore.fullname" . }}
labels:
{{- include "booklore.labels" . | nindent 4 }}
spec:
selector:
matchLabels:
{{- include "booklore.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "booklore.labels" . | nindent 8 }}
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "booklore.serviceAccountName" . }}
{{- with .Values.podSecurityContext }}
securityContext:
{{- toYaml . | nindent 8 }}
{{- end }}
initContainers:
- name: wait-for-database
image: busybox:1.36
command:
- /bin/sh
- -c
- |
echo "Waiting for database on port {{ .Values.mariadb.primary.service.ports.mysql }}..."
until nc -z -v -w5 {{ .Release.Name }}-mariadb {{ .Values.mariadb.primary.service.ports.mysql }}; do
echo "Waiting for database..."
sleep 2
done
echo "Database is available!"
resources:
requests:
cpu: 10m
memory: 16Mi
limits:
cpu: 50m
memory: 32Mi
containers:
- name: {{ .Chart.Name }}
{{- with .Values.securityContext }}
securityContext:
{{- toYaml . | nindent 12 }}
{{- end }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
#protocol: TCP
env:
- name: BOOKLORE_PORT
value: "6060"
- name: DATABASE_URL
value: jdbc:mariadb://{{ .Release.Name }}-mariadb:{{ .Values.mariadb.primary.service.ports.mysql }}/{{ .Values.mariadb.auth.database }}
- name: DATABASE_USERNAME
value: {{ .Values.mariadb.auth.username }}
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
{{- if .Values.mariadb.auth.existingSecret }}
name: {{ .Values.mariadb.auth.existingSecret }}
{{- else }}
name: {{ .Release.Name }}-mariadb
{{- end }}
key: mariadb-password
{{- with .Values.livenessProbe }}
livenessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.readinessProbe }}
readinessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.resources }}
resources:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if (or .Values.persistence.dataVolume.enabled .Values.persistence.booksVolume.enabled .Values.extraVolumeMounts) }}
volumeMounts:
{{- if .Values.persistence.dataVolume.enabled }}
- name: data
mountPath: /app/data
{{- end }}
{{- if .Values.persistence.booksVolume.enabled }}
- name: books
mountPath: /books
{{- end }}
{{- with .Values.extraVolumeMounts }}
{{- toYaml . | nindent 12 }}
{{- end }}
{{- end }}
{{- if (or .Values.persistence.dataVolume.enabled .Values.persistence.booksVolume.enabled .Values.extraVolumes) }}
volumes:
{{- if .Values.persistence.dataVolume.enabled }}
- name: data
persistentVolumeClaim:
claimName: {{ if .Values.persistence.dataVolume.existingClaim }}
{{- .Values.persistence.dataVolume.existingClaim }}
{{ else }}
{{- include "booklore.fullname" . }}-data
{{- end }}
{{- end }}
{{- if .Values.persistence.booksVolume.enabled }}
- name: books
persistentVolumeClaim:
claimName: {{ if .Values.persistence.booksVolume.existingClaim }}
{{- .Values.persistence.booksVolume.existingClaim }}
{{ else }}
{{- include "booklore.fullname" . }}-books
{{- end }}
{{- end }}
{{- with .Values.extraVolumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- end -}}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@@ -0,0 +1,43 @@
{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "booklore.fullname" . }}
labels:
{{- include "booklore.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- with .Values.ingress.className }}
ingressClassName: {{ . }}
{{- end }}
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
{{- with .pathType }}
pathType: {{ . }}
{{- end }}
backend:
service:
name: {{ include "booklore.fullname" $ }}
port:
number: {{ $.Values.service.port }}
{{- end }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,33 @@
{{- if (and .Values.persistence.dataVolume.enabled (not .Values.persistence.dataVolume.existingClaim)) }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
annotations:
helm.sh/resource-policy: keep
labels:
app.kubernetes.io/name: {{ include "booklore.fullname" . }}
name: {{ include "booklore.fullname" . }}-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: {{ .Values.persistence.dataVolume.size }}
{{- end }}
---
{{- if (and .Values.persistence.booksVolume.enabled (not .Values.persistence.booksVolume.existingClaim)) }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
annotations:
helm.sh/resource-policy: keep
labels:
app.kubernetes.io/name: {{ include "booklore.fullname" . }}
name: {{ include "booklore.fullname" . }}-books
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: {{ .Values.persistence.booksVolume.size }}
{{- end }}

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "booklore.fullname" . }}
labels:
{{- include "booklore.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "booklore.selectorLabels" . | nindent 4 }}

View File

@@ -0,0 +1,13 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "booklore.serviceAccountName" . }}
labels:
{{- include "booklore.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
automountServiceAccountToken: {{ .Values.serviceAccount.automount }}
{{- end }}

121
example-chart/values.yaml Normal file
View File

@@ -0,0 +1,121 @@
# Default values for booklore.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
mariadb:
enabled: true
image:
tag: 11.8.3
auth:
database: booklore
username: booklore-user
# This sets the container image more information can be found here: https://kubernetes.io/docs/concepts/containers/images/
image:
repository: booklore/booklore
# This sets the pull policy for images.
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: latest
# This is for the secrets for pulling an image from a private repository more information can be found here: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
imagePullSecrets: []
# This is to override the chart name.
nameOverride: ""
fullnameOverride: ""
# This section builds out the service account more information can be found here: https://kubernetes.io/docs/concepts/security/service-accounts/
serviceAccount:
# Specifies whether a service account should be created
create: true
# Automatically mount a ServiceAccount's API credentials?
automount: true
# Annotations to add to the service account
annotations: {}
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name: ""
# This is for setting Kubernetes Annotations to a Pod.
# For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/
podAnnotations: {}
# This is for setting Kubernetes Labels to a Pod.
# For more information checkout: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
podLabels: {}
podSecurityContext: {}
# fsGroup: 2000
securityContext: {}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
# This is for setting up a service more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/
service:
# This sets the service type more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types
type: ClusterIP
# This sets the ports more information can be found here: https://kubernetes.io/docs/concepts/services-networking/service/#field-spec-ports
port: 6060
# This block is for setting up the ingress for more information can be found here: https://kubernetes.io/docs/concepts/services-networking/ingress/
ingress:
enabled: false
className: ""
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
hosts:
- host: chart-example.local
paths:
- path: /
pathType: ImplementationSpecific
tls: []
# - secretName: chart-example-tls
# hosts:
# - chart-example.local
resources: {}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
# # This is to setup the liveness and readiness probes more information can be found here: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
livenessProbe:
tcpSocket:
port: http
readinessProbe:
tcpSocket:
port: http
# If you want to bring your own persistence (such as a hostPath),
# disable these and do so in extraVolumes/extraVolumeMounts
persistence:
dataVolume:
enabled: true
size: 100Mi
existingClaim: ""
booksVolume:
enabled: true
size: 10Gi
existingClaim: ""
extraVolumes: []
extraVolumeMounts: []
nodeSelector: {}
tolerations: []
affinity: {}