Partie 10: Installation de home-assistant avec ArgoCD

Partie 10: Installation de home-assistant avec ArgoCD

Introduction

Après avoir complété la mise en place du pipeline CI/CD sur mon cluster K3s, il était temps d'installer Home Assistant, la plateforme domotique open-source qui va centraliser tous mes devices connectés. L'objectif était de déployer Home Assistant de manière propre et reproductible via GitOps, tout en permettant l'accès à ma clé Zigbee USB pour la communication avec mes capteurs et actionneurs.

Architecture cible

  • Cluster K3s: 5 Raspberry Pi (1x RPi4 8GB master, 2x RPi4 2GB/4GB workers, 2x RPi3 1GB workers)
  • ArgoCD: Déploiement GitOps automatique
  • Home Assistant: Version 2024.12 en conteneur
  • Clé Zigbee: Branchée sur rpi4-worker2 (/dev/ttyUSB0)
  • Accès web: HTTPS via nginx-ingress avec Let's Encrypt (ha.home-fonta.fr)
  • Stockage: Volume persistant 5 Go avec local-path-provisioner

Contexte et défis

Défi 1: Accès au matériel USB dans Kubernetes

Home Assistant a besoin d'un accès direct à la clé Zigbee USB. Dans Kubernetes, l'accès aux devices USB nécessite:

  • Un hostPath volume pointant vers /dev/ttyUSB0
  • Un conteneur en mode privileged
  • Un nodeSelector pour forcer le pod sur le nœud avec la clé USB

Défi 2: Reverse proxy et trusted proxies

Home Assistant refuse par défaut les connexions via reverse proxy pour des raisons de sécurité. Il faut configurer explicitement les proxies de confiance.

Défi 3: Stockage persistant lié au nœud

Le storage class local-path crée des volumes liés à un nœud spécifique. Impossible de migrer le pod sans recréer le volume.

Étape 1: Création du Helm Chart

J'ai créé un Helm chart complet pour Home Assistant dans helm-charts/homeassistant/.

Structure du chart

helm-charts/homeassistant/
├── Chart.yaml
├── values.yaml
└── templates/
    ├── _helpers.tpl
    ├── configmap.yaml
    ├── deployment.yaml
    ├── ingress.yaml
    ├── pvc.yaml
    └── service.yaml

Chart.yaml

apiVersion: v2
name: homeassistant
description: Home Assistant on K3s with Zigbee support
type: application
version: 1.0.0
appVersion: "2024.12"

values.yaml (configuration)

replicaCount: 1

image:
  repository: ghcr.io/home-assistant/home-assistant
  tag: "2024.12"
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 8123

ingress:
  enabled: true
  className: nginx
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    cert-manager.io/cluster-issuer: "letsencrypt-prod-dns"
    nginx.ingress.kubernetes.io/proxy-body-size: "0"
    nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
  hosts:
    - host: ha.home-fonta.fr
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: ha-home-fonta-tls
      hosts:
        - ha.home-fonta.fr

persistence:
  enabled: true
  storageClass: "local-path"
  accessMode: ReadWriteOnce
  size: 5Gi

# Force deployment on rpi4-worker2 for Zigbee USB access
nodeSelector:
  kubernetes.io/hostname: rpi4-worker2

# USB device access
zigbee:
  enabled: true
  device: /dev/ttyUSB0

resources:
  limits:
    cpu: 1000m
    memory: 1Gi
  requests:
    cpu: 200m
    memory: 512Mi

# Security context for USB access
securityContext:
  privileged: true

# Home Assistant specific proxy settings
homeassistant:
  trustedProxies:
    - 10.0.0.0/8
    - 172.16.0.0/12
    - 192.168.0.0/16

Deployment avec accès USB

Le deployment est la partie critique car il doit gérer l'accès USB:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "homeassistant.fullname" . }}
spec:
  replicas: 1
  strategy:
    type: Recreate  # Important: pas de rolling update avec USB
  template:
    spec:
      nodeSelector:
        kubernetes.io/hostname: rpi4-worker2  # Force sur le bon nœud
      containers:
        - name: homeassistant
          image: ghcr.io/home-assistant/home-assistant:2024.12
          securityContext:
            privileged: true  # Nécessaire pour USB
          env:
            - name: TZ
              value: "Europe/Paris"
          volumeMounts:
            - name: config
              mountPath: /config
            - name: ha-config
              mountPath: /config/configuration.yaml
              subPath: configuration.yaml
            - name: usb
              mountPath: /dev/ttyUSB0  # Device Zigbee
      volumes:
        - name: config
          persistentVolumeClaim:
            claimName: homeassistant
        - name: ha-config
          configMap:
            name: homeassistant-config
        - name: usb
          hostPath:
            path: /dev/ttyUSB0  # Accès direct au device

ConfigMap pour la configuration reverse proxy

Le problème majeur rencontré: Home Assistant refusait les connexions avec "HTTP integration is not set-up for reverse proxies".

Solution: créer un ConfigMap avec configuration.yaml:

apiVersion: v1
kind: ConfigMap
metadata:
  name: homeassistant-config
data:
  configuration.yaml: |
    # Loads default set of integrations
    default_config:

    frontend:
      themes: !include_dir_merge_named themes

    automation: !include automations.yaml
    script: !include scripts.yaml
    scene: !include scenes.yaml

    # HTTP Configuration for reverse proxy
    http:
      use_x_forwarded_for: true
      trusted_proxies:
        - 10.42.0.0/16  # Pod network
        - 10.43.0.0/16  # Service network

Étape 2: Application ArgoCD

Création de l'application ArgoCD pour le déploiement GitOps:

# argocd-apps/applications/homeassistant.yaml
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: homeassistant
  namespace: argocd
spec:
  project: default

  source:
    repoURL: git@github.com:Nikob2o/ansible-k3s.git
    targetRevision: HEAD
    path: helm-charts/homeassistant
    helm:
      valueFiles:
      - values.yaml

  destination:
    server: https://kubernetes.default.svc
    namespace: homeassistant

  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
    - CreateNamespace=true

Étape 3: Déploiement

Commit et push

git add helm-charts/homeassistant/ argocd-apps/applications/homeassistant.yaml
git commit -m "Ajout Home Assistant avec la clé Zigbee"
git push

Vérification du déploiement

# Vérifier l'application ArgoCD
kubectl get applications -n argocd

# Vérifier le pod
kubectl get pods -n homeassistant -o wide
NAME                             READY   STATUS    RESTARTS   AGE   NODE
homeassistant-59db64d779-bcdqt   1/1     Running   0          5m    rpi4-worker2

# Vérifier l'accès USB
kubectl exec -n homeassistant homeassistant-59db64d779-bcdqt -- ls -l /dev/ttyUSB0
crw-rw---- 1 root dialout 188, 0 Jan  4 19:58 /dev/ttyUSB0

Problèmes rencontrés et solutions

Problème 1: Manque d'espace disque sur rpi4-master

Symptôme: Pods évictés avec The node was low on resource: ephemeral-storage

Diagnostic:

ansible rpi4-master -i inventory/hosts.ini -m shell -a "df -h /" --become
# Résultat: 11G utilisés sur 14G (77%)

Cause: Fichier swap de 2.1 Go non désactivé + journaux

Solution:

# Désactiver et supprimer le swap
sudo swapoff -a
sudo rm -f /var/swap

# Nettoyer les journaux
sudo journalctl --vacuum-time=7d

# Résultat: passage de 77% à 69% d'utilisation

Changemant de plan, on va mettre home-assistant sur la rpi4-worker2

Problème 2: PVC lié au mauvais nœud

Symptôme: Pod en Pending avec "didn't match Pod's node affinity"

Cause: Le PVC créé sur rpi4-master ne peut pas être monté sur rpi4-worker2 (storage class local-path)

Solution: Supprimer et recréer le PVC

kubectl delete pvc homeassistant -n homeassistant
# ArgoCD recrée automatiquement sur le bon nœud

Problème 3: 400 Bad Request du reverse proxy

Symptôme:

ERROR (MainThread) [homeassistant.components.http.forwarded]
A request from a reverse proxy was received from 10.42.0.63,
but your HTTP integration is not set-up for reverse proxies

Solution: Ajout du ConfigMap avec la configuration HTTP (voir ci-dessus)

Problème 4: rpi4-worker2 ne rejoignait pas le cluster

Symptôme: "Node password rejected, duplicate hostname"

Cause: Secret et fichier /etc/rancher/node/password en conflit

Solution:

# Sur le master
kubectl delete secret rpi4-worker2.node-password.k3s -n kube-system

# Sur le worker
sudo systemctl stop k3s-agent
sudo rm -rf /etc/rancher/node
sudo systemctl start k3s-agent

Résultat final

Home Assistant accessible: https://ha.home-fonta.fr
Clé Zigbee détectée: /dev/ttyUSB0 accessible dans le conteneur
Certificat SSL: Let's Encrypt automatique via cert-manager
GitOps: Tout commit sur le repo déclenche un déploiement automatique
Haute disponibilité: Redémarrage automatique en cas de crash

Configuration post-installation

1. Premier démarrage

Lors du premier accès à https://ha.home-fonta.fr, Home Assistant demande:

  • Création du compte administrateur
  • Nom de la maison
  • Localisation (pour les automations basées sur le lever/coucher du soleil)
  • Partage des analytics (optionnel)

2. Configuration Zigbee (ZHA)

Pour connecter la clé Zigbee:

  1. ParamètresAppareils et servicesAjouter une intégration
  2. Chercher ZHA (Zigbee Home Automation)
  3. Sélectionner le port série: /dev/ttyUSB0
  4. Choisir le type de dongle
  5. Laisser créer le réseau Zigbee

Une fois configuré, on peut ajouter les devices Zigbee en mode appairage.

3. Sécurité recommandée

  • Activer l'IP Banning dans Paramètres → Système → Réseau (Pas trouvé avec ma version)
  • Configurer les Trusted Networks pour les réseaux locaux
  • Utiliser un mot de passe fort
  • Activer l'authentification à deux facteurs (MFA)

Commandes utiles

# Logs du pod
kubectl logs -n homeassistant -l app.kubernetes.io/name=homeassistant -f

# Redémarrer Home Assistant
kubectl rollout restart deployment homeassistant -n homeassistant

# Accéder au shell du conteneur
kubectl exec -it -n homeassistant deployment/homeassistant -- /bin/bash

# Synchroniser manuellement ArgoCD
argocd app sync homeassistant

# Copier une sauvegarde vers le pod
kubectl cp backup.tar homeassistant/homeassistant-xxx:/config/backups/

Ajout du support Bluetooth

Après le déploiement initial, j'ai constaté que l'intégration Bluetooth n'était pas disponible dans Home Assistant. Voici les étapes pour activer le Bluetooth.

Vérification de l'adaptateur Bluetooth

Sur rpi4-worker2, vérification de la présence de l'adaptateur:

hciconfig
# hci0:	Type: Primary  Bus: UART
# 	BD Address: 88:A2:9E:5F:F3:36  ACL MTU: 1021:8  SCO MTU: 64:1
# 	DOWN

L'adaptateur hci0 est bien présent mais non accessible dans le pod.

Modification du Helm Chart pour Bluetooth

Ajout dans values.yaml:

bluetooth:
  enabled: true

securityContext:
  privileged: true
  capabilities:
    add:
      - NET_ADMIN
      - NET_RAW
      - SYS_ADMIN

Les capabilities NET_ADMIN, NET_RAW et SYS_ADMIN sont nécessaires pour que Home Assistant puisse interagir avec l'adaptateur Bluetooth via D-Bus.

Modification du Deployment

Dans templates/deployment.yaml, ajout de hostNetwork et du montage D-Bus:

spec:
  template:
    spec:
      hostNetwork: true  # Nécessaire pour Bluetooth
      containers:
        - name: homeassistant
          volumeMounts:
            - name: config
              mountPath: /config
            - name: usb
              mountPath: /dev/ttyUSB0
            - name: dbus
              mountPath: /var/run/dbus
              readOnly: true
      volumes:
        - name: dbus
          hostPath:
            path: /var/run/dbus

Problème: Erreur 400 avec hostNetwork

L'activation de hostNetwork: true a provoqué une nouvelle erreur 400:

ERROR [homeassistant.components.http.forwarded]
A request from a reverse proxy was received from 192.168.1.51,
but your HTTP integration is not set-up for reverse proxies

Cause: Avec hostNetwork: true, Home Assistant voit mon IP locale (192.168.1.51) au lieu de l'IP du pod nginx-ingress (10.42.x.x).

Solution: Suppression du ConfigMap initial et ajout de la configuration directement dans le PVC:

kubectl exec -n homeassistant $(kubectl get pod -n homeassistant \
  -l app.kubernetes.io/name=homeassistant -o name) -- \
  /bin/bash -c "cat > /config/configuration.yaml << 'EOF'
default_config:

http:
  use_x_forwarded_for: true
  trusted_proxies:
    - 192.168.1.0/24    # Réseau local
    - 10.42.0.0/16      # Réseau pods K3s
    - 10.43.0.0/16      # Réseau services K3s
    - 127.0.0.1
    - ::1
EOF
"

Activation de BlueZ experimental

Lors de la première activation du Bluetooth dans Home Assistant:

Failed to start Bluetooth: passive scanning on Linux requires
BlueZ >= 5.56 with --experimental enabled and Linux kernel >= 5.10

Solution: Activer le mode experimental sur BlueZ directement sur rpi4-worker2:

# Sur rpi4-worker2 en SSH
sudo systemctl edit bluetooth.service --full

# Modifier la ligne ExecStart:
# Avant: ExecStart=/usr/lib/bluetooth/bluetoothd
# Après: ExecStart=/usr/lib/bluetooth/bluetoothd --experimental

# Redémarrer le service
sudo systemctl daemon-reload
sudo systemctl restart bluetooth

# Vérification
sudo systemctl status bluetooth | grep experimental

Résultat Bluetooth

Adaptateur Bluetooth détecté: hci0 accessible dans Home Assistant
Intégration Bluetooth disponible: Dans Paramètres → Appareils et services
Détection d'appareils: Scan Bluetooth fonctionnel

Point d'attention: Configuration non GitOps

⚠️ La configuration http.trusted_proxies est stockée dans le PVC, pas dans le code GitOps. En cas de suppression du PVC, il faudra reconfigurer cette section manuellement.

⚠️ L'activation de BlueZ experimental sur rpi4-worker2 n'est pas gérée par Ansible et devra être refaite manuellement sur de nouveaux nœuds.

Améliorations futures possibles

  • Zigbee2MQTT: Alternative à ZHA avec plus de flexibilité
  • Node-RED: Pour des automations avancées
  • Grafana + InfluxDB: Historisation et graphiques des métriques
  • ESPHome: Intégration de devices ESP32/ESP8266 custom
  • Frigate: Détection d'objets sur caméras avec IA
  • Automatisation BlueZ: Ajouter la configuration --experimental dans les playbooks Ansible

Conclusion

L'installation de Home Assistant sur Kubernetes n'est pas triviale, surtout avec l'accès USB pour Zigbee. Les principaux challenges étaient:

  1. La gestion du device USB dans Kubernetes (hostPath + privileged)
  2. Le storage persistant lié au nœud
  3. La configuration du reverse proxy

Mais le résultat final est robuste et suit les bonnes pratiques GitOps. Tout changement dans le Git se déploie automatiquement, et le système se remet automatiquement de pannes.

Prochaine étape: migration de Passbolt depuis le NAS vers K3s!


Ressources:

Read more