feat(immich): add initial chart

This commit is contained in:
secustor 2025-01-17 22:49:13 +01:00
parent 2687c2c901
commit 7aa6d236f0
No known key found for this signature in database
20 changed files with 803 additions and 1 deletions

56
.github/workflows/release.yaml vendored Normal file
View file

@ -0,0 +1,56 @@
name: Release Charts
on:
push:
branches:
- main
permissions: {}
jobs:
release:
permissions:
contents: write # to push chart release and create a release (helm/chart-releaser-action)
packages: write # needed for ghcr access
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4.2.2
with:
fetch-depth: 0
- name: Configure Git
run: |
git config user.name "$GITHUB_ACTOR"
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
- name: Set up Helm
uses: azure/setup-helm@v4.2.0
with:
version: v3.12.0
- name: Run chart-releaser
uses: helm/chart-releaser-action@v1.5.0
with:
charts_dir: charts
config: cr.yaml
env:
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
CR_SKIP_EXISTING: "true"
- name: Login to GHCR
uses: docker/login-action@v3.0.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push charts to GHCR
run: |
shopt -s nullglob
for pkg in .cr-release-packages/*.tgz; do
if [ -z "${pkg:-}" ]; then
break
fi
helm push "${pkg}" "oci://ghcr.io/${GITHUB_REPOSITORY}"
done

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
# Chart dependencies
**/charts/*.tgz
output

View file

@ -1,2 +1,4 @@
# helm-charts # helm-charts
Contains Helm charts maintained by myself Contains Helm charts maintained by myself.
See the README.md in each chart directory for more information.

23
charts/immich/.helmignore Normal file
View file

@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

7
charts/immich/Chart.yaml Normal file
View file

@ -0,0 +1,7 @@
apiVersion: v2
name: immich
description: A Immich Helm chart targeted at advanced users
type: application
version: 0.1.0
appVersion: "v1.124.2"

9
charts/immich/README.md Normal file
View file

@ -0,0 +1,9 @@
# Immich
> [!WARNING]
> This chart is in alpha state and should be used with care.
> It is under heavy development.
This chart deploys [Immich](https://immich.app/) and
is targeted at an audience which wants to have some lower level control over the deployment.

View file

View file

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

View file

@ -0,0 +1,10 @@
{{/*
Secret names
*/}}
{{- define "postgres.secretName" -}}
{{ include "immich.fullname" . }}-postgres
{{- end }}
{{- define "redis.secretName" -}}
{{ include "immich.fullname" . }}-redis
{{- end }}

View file

@ -0,0 +1,17 @@
{{- if .Values.httpRoute.enabled -}}
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: {{ include "immich-server.name" . }}
labels:
{{- include "immich-server.labels" . | nindent 4 }}
spec:
parentRefs:
{{- .Values.httpRoute.parentRefs | toYaml | nindent 4 }}
hostnames:
{{- .Values.httpRoute.hostnames | toYaml | nindent 4 }}
rules:
- backendRefs:
- name: {{ include "immich-server.name" . }}
port: {{ .Values.server.service.port }}
{{- end -}}

View file

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

View file

@ -0,0 +1,88 @@
{{- if .Values.machineLearning.enabled -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "immich-machine-learning.name" . }}
labels:
{{- include "immich-machine-learning.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.machineLearning.replicaCount }}
selector:
matchLabels:
{{- include "immich-machine-learning.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.machineLearning.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "immich-machine-learning.labels" . | nindent 8 }}
{{- with .Values.machineLearning.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "immich.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.machineLearning.podSecurityContext | nindent 8 }}
containers:
- name: immich-machine-learning
image: "{{ .Values.machineLearning.image.repository }}:{{ .Values.machineLearning.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.machineLearning.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.machineLearning.service.port }}
protocol: TCP
env:
{{- if .Values.machineLearning.cache.enabled }}
- name: TRANSFORMERS_CACHE_DIR
value: /cache
{{- end }}
{{- if .Values.machineLearning.env }}
{{- toYaml .Values.machineLearning.env | nindent 12 }}
{{- end }}
securityContext:
{{- toYaml .Values.machineLearning.securityContext | nindent 12 }}
livenessProbe:
httpGet:
port: http
path: /ping
resources:
{{- toYaml .Values.machineLearning.resources | nindent 12 }}
volumeMounts:
{{- if .Values.machineLearning.cache.enabled }}
- mountPath: /cache
name: model-cache
{{- end }}
{{- with .Values.machineLearning.volumeMounts }}
{{- toYaml . | nindent 12 }}
{{- end }}
volumes:
{{- if .Values.machineLearning.cache.enabled }}
- name: model-cache
emptyDir:
{{- if .Values.machineLearning.cache.useMemory }}
medium: Memory
{{- end }}
sizeLimit: {{ .Values.machineLearning.cache.sizeLimit }}
{{- end }}
{{- with .Values.machineLearning.volumes }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.machineLearning.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.machineLearning.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.machineLearning.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- end }}

View file

@ -0,0 +1,32 @@
{{- if .Values.server.autoscaling.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "immich-server.name" . }}
labels:
{{- include "immich-server.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "immich-server.name" . }}
minReplicas: {{ .Values.server.autoscaling.minReplicas }}
maxReplicas: {{ .Values.server.autoscaling.maxReplicas }}
metrics:
{{- if .Values.server.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ .Values.server.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
{{- if .Values.server.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: {{ .Values.server.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
{{- end }}

View file

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

View file

@ -0,0 +1,118 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "immich-server.name" . }}
labels:
{{- include "immich-server.labels" . | nindent 4 }}
spec:
{{- if not .Values.server.autoscaling.enabled }}
replicas: {{ .Values.server.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "immich-server.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.server.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "immich-server.labels" . | nindent 8 }}
{{- with .Values.server.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "immich.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.server.podSecurityContext | nindent 8 }}
containers:
- name: immich-server
image: "{{ .Values.server.image.repository }}:{{ .Values.server.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.server.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.server.service.port }}
protocol: TCP
env:
{{- with .Values.common.postgres }}
- name: DB_HOSTNAME
value: {{ .host }}
{{- if .existingSecret.enabled}}
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: {{ .existingSecret.secretName }}
key: {{ .existingSecret.usernameKey }}
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .existingSecret.secretName }}
key: {{ .existingSecret.passwordKey }}
{{- else }}
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: {{ include "postgres.secretName" $ }}
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: {{ include "postgres.secretName" $ }}
key: password
{{- end }}
{{- end }}
{{- with .Values.common.redis }}
- name: REDIS_HOSTNAME
value: {{ .host }}
{{- if .existingSecret.enabled}}
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .existingSecret.secretName }}
key: {{ .existingSecret.passwordKey }}
{{- else }}
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: {{ include "redis.secretName" $ }}
key: password
{{- end }}
{{- end }}
{{- if .Values.server.env }}
{{- toYaml .Values.server.env | nindent 12 }}
{{- end }}
securityContext:
{{- toYaml .Values.server.securityContext | nindent 12 }}
livenessProbe:
httpGet:
port: http
path: /api/server/ping
resources:
{{- toYaml .Values.server.resources | nindent 12 }}
{{- with .Values.server.volumeMounts }}
volumeMounts:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.server.volumes }}
volumes:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.server.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.server.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.server.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 "immich.fullname" . }}
labels:
{{- include "immich-server.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 "immich.fullname" $ }}
port:
number: {{ $.Values.service.port }}
{{- end }}
{{- end }}
{{- end }}

View file

@ -0,0 +1,24 @@
{{- if not .Values.common.postgres.existingSecret.enabled }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "immich.fullname" . }}-postgres
labels:
{{- include "immich.commonLabels" . | nindent 4 }}
type: Opaque
data:
username: "{{ .Values.common.postgres.createSecret.username | b64enc }}"
password: "{{ .Values.common.postgres.createSecret.password | b64enc }}"
{{- end }}
---
{{- if not .Values.common.redis.existingSecret.enabled }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "immich.fullname" . }}-redis
labels:
{{- include "immich.commonLabels" . | nindent 4 }}
type: Opaque
data:
password: "{{ .Values.common.redis.createSecret.password | b64enc }}"
{{- end }}

View file

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

214
charts/immich/values.yaml Normal file
View file

@ -0,0 +1,214 @@
# This is to override the chart name.
nameOverride: ""
fullnameOverride: ""
imagePullSecrets: []
# Configurations that are relevant for all the components of the chart
common:
postgres:
host: immich-db
existingSecret:
enabled: false
secretName: ""
usernameKey: username
passwordKey: password
createSecret:
username: ""
password: ""
redis:
host: immich-redis
existingSecret:
enabled: false
secretName: ""
passwordKey: password
createSecret:
password: ""
server:
# This will set the replicaset count. Ignored if autoscaling is enabled.
replicaCount: 1
# This section is for setting up autoscaling more information can be found here: https://kubernetes.io/docs/concepts/workloads/autoscaling/
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
# targetMemoryUtilizationPercentage: 80
image:
repository: ghcr.io/immich-app/immich-server
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: ""
podAnnotations: {}
podLabels: {}
resources: {}
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
service:
type: ClusterIP
port: 2283
nodeSelector: {}
tolerations: []
affinity: {}
podSecurityContext: {}
# fsGroup: 2000
securityContext: {}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
# Additional environment variables to set on the container.
env: []
# - name: DB_VECTOR_EXTENSION
# value: "pgvector"
# - name: SOME_OTHER_ENV
# valueFrom:
# secretKeyRef:
# name: mySecret
# key: secretField
# Additional volumes on the output Deployment definition.
volumes: []
# - name: uploads
# hostPath:
# path: /data/upload
# - name: foo
# secret:
# secretName: mysecret
# optional: false
# Additional volumeMounts on the output Deployment definition.
volumeMounts: []
# - name: uploads
# mountPath: /usr/src/app/upload
machineLearning:
enabled: false
cache:
enabled: true
# If enabled the cache will be stored in memory, rather than local disk. This will increase performance but will require more memory.
useMemory: false
sizeLimit: 10Gi
replicaCount: 1
image:
repository: ghcr.io/immich-app/immich-machine-learning
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: ""
podAnnotations: {}
podLabels: {}
resources: {}
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
service:
type: ClusterIP
port: 3003
nodeSelector: {}
tolerations: []
affinity: {}
podSecurityContext: {}
# fsGroup: 2000
securityContext: {}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
# Additional environment variables to set on the container.
env: []
# - name: DB_VECTOR_EXTENSION
# value: "pgvector"
# - name: SOME_OTHER_ENV
# valueFrom:
# secretKeyRef:
# name: mySecret
# key: secretField
# Additional volumes on the output Deployment definition.
volumes: []
# - name: uploads
# hostPath:
# path: /data/upload
# - name: foo
# secret:
# secretName: mysecret
# optional: false
# Additional volumeMounts on the output Deployment definition.
volumeMounts: []
# - name: uploads
# mountPath: /usr/src/app/upload
# 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 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
# This block is for setting up the HTTPROUTE for more information can be found here: https://kubernetes.io/docs/concepts/services-networking/gateway/#api-kind-httproute
httpRoute:
enabled: false
hostnames: []
# - chart-example.local
parentRefs: []
# - name: public-web
# namespace: ingress

5
cr.yaml Normal file
View file

@ -0,0 +1,5 @@
sign: false
# Enable automatic generation of release notes using GitHubs release notes generator.
# see: https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes
generate-release-notes: true