Installation de Immich

  1. Ajout du helm : immich-repo oci oci://ghcr.io/immich-app/immich-charts/immich
  2. Création du Projet Immich et du namespace immich
  3. Affectation du PV : Nouveau PVC, taille 200G (la taille est extensible avec Longhorn) : pv-immich-data-claim

Création du service

  1. Création d'un service Headless dans Services Discovery / Services immich-db
  • port name : postgre
  • port : 5432
  1. Modifier le service en IPv4 : Éditer le YAML
YAML
spec:
clusterIP: None
ipFamilyPolicy: SingleStack
ipFamilies:
  - IPv4

Note

On reviendra après pour définir le pod utilisant ce service

Création d'un secret

Storage -> Secret -> Create -> Opaque

  • DB_DATABASE_NAME
  • DB_HOSTNAME
  • DB_PASSWORD
  • DB_USERNAME
  • POSTGRES_DB
  • POSTGRES_PASSWORD
  • POSTGRES_USER

Base de données

  1. Nouveau StateFullSet (pour des bases de données et pas Deployment).
  2. Choisir l'image ghcr.io/tensorchord/cloudnative-vectorchord:17.5-0.4.3 car immich-server ne supporte pas les dernières versions vector.
  3. Créer dans le pod le pv-immich-pgsql-claim et l'attribuer à /var/lib/postgresql/data dans le set.
  4. Ajouter le Secret dans l'environment, et détail qui a son importance les variables
    • PGDATA : /var/lib/postgresql/data/pgdata
    • POSTGRES_INITDB_ARGS : "-c shared_preload_libraries=vchord"
  5. Créer le config map suivant pour l'init de la db :
YAML
apiVersion: v1
kind: ConfigMap
metadata:
  name: immich-postgres-init
  namespace: immich
data:
  init.sql: |
    CREATE EXTENSION IF NOT EXISTS vchord CASCADE;
    CREATE EXTENSION IF NOT EXISTS earthdistance CASCADE;
  1. Ajouter cette config map dans le pod
  2. Chose qui ne m'était pas encore arrivé, le pod n'avait pas les droits root, donc impossible d'écrire sur son volume, il a fallu rajouter le groupe 999 au contexte de sécurité du pod ET du set.

Attribution du service

  1. Dans les paramètres du pod dans le StateFullSet, ajouter un Label [app:pgsql]
  2. Dans les paramètres du service, ajouter le même Label

Installation du chart

Enfin... :p Enfin, il va surtout falloir configurer avant d'utiliser

Middleware Traefik

Note

On va se passer complétement de cette couche d'ingress, trop d'instabilités avec le reverse Ce chart est prévu pour un Ingress nginx, chose qui n'est plus supporté dans quelques mois, on va donc créer un middleware Traefik pour permettre de ne pas couper les upload de videos On va ajouter ce middleware avec upload YAML

YAML
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: immich-buffering
  namespace: immich
spec:
  buffering:
    maxRequestBodyBytes: 0

Créer également le transport pour le timeout :

YAML
apiVersion: traefik.io/v1alpha1
kind: ServersTransport
metadata:
  name: immich-transport
  namespace: immich
spec:
  serverName: ""
  insecureSkipVerify: false
  rootCAs: []
  maxIdleConnsPerHost: 200
  forwardingTimeouts:
    dialTimeout: 30s
    responseHeaderTimeout: 3600s
    idleConnTimeout: 3600s

Adaptation du reverse proxy

Ici, on va différencier les WebSocket (utilisé par les uploads) et le http conventionnel.

TXT
# ------------------------------------------------------------
# Immich
# ------------------------------------------------------------
https://immich.dsvs.ddns.net {
    # Compression (sauf WebSocket)
    encode gzip

    # Configuration spéciale pour WebSocket
    @websocket {
        header Connection *Upgrade*
        header Upgrade websocket
    }

    # Route WebSocket (sans buffering ni timeout court)
    reverse_proxy @websocket http://192.168.1.150:32283 http://192.168.1.151:32283 http://192.168.1.152:32283 {
        transport http {
            read_timeout    0  # Pas de timeout pour WebSocket
            write_timeout   0
            dial_timeout    30s
            keepalive       0  # Pas de keepalive pour WS
        }

        header_up Host {host}
        header_up X-Real-IP {remote}
        header_up X-Forwarded-For {remote}
        header_up X-Forwarded-Proto https
        header_up X-Forwarded-Host {host}

        flush_interval -1
    }

    # Route normale pour HTTP (uploads, etc.)
    reverse_proxy http://192.168.1.150:32283 http://192.168.1.151:32283 http://192.168.1.152:32283 {

        header_up Host {host}
        header_up X-Real-IP {remote}
        header_up X-Forwarded-For {remote}
        header_up X-Forwarded-Proto https
        header_up X-Forwarded-Host {host}
    }
}

Immich App

  • Apps -> Charts -> Immich, Install et bien cocher Customize
    1. Ajouter les secrets dans env :
YAML
        env:
          IMMICH_MACHINE_LEARNING_URL: '{{ printf "http://%s-machine-learning:3003" .Release.Name }}'
          REDIS_HOSTNAME: '{{ printf "%s-valkey" .Release.Name }}'
        image:
          tag: v2.0.0
        envFrom:
          - secretRef:
              name: immich-secret
  1. Ajouter le pv dans la section persistance :
YAML
immich:
  configuration: {}
  metrics:
    enabled: false
  persistence:
    library:
      existingClaim: pv-immich-data-claim
  1. Modifier l'Ingress et prendre en compte le middleware

    Note

    ⚠️⚠️ On va se passer complétement de cette couche d'ingress, trop d'instabilités avec le reverse + ⚠️⚠️

YAML
  ingress:
    main:
      enabled: true
      hosts:
        - host: immich.dsvs.ddns.net
          paths:
            - path: /
              service:
                identifier: main
      tls: []
      annotations:
        traefik.ingress.kubernetes.io/router.middlewares: immich-immich-buffering@kubernetescrd
        traefik.ingress.kubernetes.io/service.serverstransport: immich-immich-transport@kubernetescrd

Note

Voici le Node port retenu

YAML
  ingress:
    main:
      annotations:
        nginx.ingress.kubernetes.io/proxy-body-size: null
      enabled: false
      hosts: null
      tls: []
  service:
    main:
      ports:
        http:
          nodePort: 32283
          port: 2283
          protocol: TCP
      type: NodePort
  1. Un peu bête mais le valkey n'était pas enabled, changer a true

Divers

Pour éviter trop de redémarrages j'ai allongé la durée du ping check sur les Deployments Dans la section server : main :

YAML
probes:
            liveness:
              enabled: true
              custom: true
              spec:
                httpGet:
                  path: /api/server/ping
                  port: 2283
                initialDelaySeconds: 60
                periodSeconds: 60
                timeoutSeconds: 10
                failureThreshold: 5
            readiness:
              enabled: true
              custom: true
              spec:
                httpGet:
                  path: /api/server/ping
                  port: 2283
                initialDelaySeconds: 30
                periodSeconds: 30
                timeoutSeconds: 10
                failureThreshold: 5

Tip

Config définitive de l'app

YAML
controllers:
  main:
    containers:
      main:
        env:
          IMMICH_MACHINE_LEARNING_URL: '{{ printf "http://%s-machine-learning:3003" .Release.Name }}'
          REDIS_HOSTNAME: '{{ printf "%s-valkey" .Release.Name }}'
        image:
          tag: v2.0.0
        envFrom:
          - secretRef:
              name: immich-secret
immich:
  configuration: {}
  metrics:
    enabled: false
  persistence:
    library:
      existingClaim: pv-immich-data-claim
machine-learning:
  controllers:
    main:
      containers:
        main:
          env:
            HF_XET_CACHE: /cache/huggingface-xet
            MPLCONFIGDIR: /cache/matplotlib-config
            TRANSFORMERS_CACHE: /cache
          image:
            pullPolicy: IfNotPresent
            repository: ghcr.io/immich-app/immich-machine-learning
  enabled: true
  persistence:
    cache:
      accessMode: ReadWriteMany
      enabled: true
      size: 10Gi
      type: emptyDir
server:
  controllers:
    main:
      containers:
        main:
          image:
            pullPolicy: IfNotPresent
            repository: ghcr.io/immich-app/immich-server
          probes:
            liveness:
              custom: true
              enabled: true
              spec:
                failureThreshold: 5
                httpGet:
                  path: /api/server/ping
                  port: 2283
                initialDelaySeconds: 60
                periodSeconds: 60
                timeoutSeconds: 10
            readiness:
              custom: true
              enabled: true
              spec:
                failureThreshold: 5
                httpGet:
                  path: /api/server/ping
                  port: 2283
                initialDelaySeconds: 30
                periodSeconds: 30
                timeoutSeconds: 10
  enabled: true
  ingress:
    main:
      annotations:
        nginx.ingress.kubernetes.io/proxy-body-size: null
      enabled: false
      hosts: null
      tls: []
  service:
    main:
      ports:
        http:
          nodePort: 32283
          port: 2283
          protocol: TCP
      type: NodePort
valkey:
  controllers:
    main:
      containers:
        main:
          image:
            pullPolicy: IfNotPresent
            repository: docker.io/valkey/valkey
            tag: >-
              9.0-alpine@sha256:b4ee67d73e00393e712accc72cfd7003b87d0fcd63f0eba798b23251bfc9c394
  enabled: true
  persistence:
    data:
      accessMode: ReadWriteOnce
      enabled: true
      size: 1Gi
      type: emptyDir
global:
  cattle:
    systemProjectId: p-vtsj2