K3s Deployment

-1/ Contexte

Okay, Ca fait un moment que je veux faire ça. Je veux passer de ma petite infra actuellement en podman vers du kube. Par le passé j’ai déjà eu à bosser avec du kubernetes (souvent open shift), mais j’ai jamais eu à gérer le tout du début à la fin en solo.
Et je vais être honnête, je me suis pris plus souvent les pieds dans le tapis que ce que je voudrais l’admettre et que j’avais prévu.
Outre le fait que les fois précédentes ou j’ai eu à faire du kube, je n’étais pas tout seul dans le déploiement et la MCO, là je suis le seul “architecte” de cette “équipe”. Du coup il faut pas mal lire et se taper des vidéos relou de youtubeur en mal de visibilité qui te présente des outils pour pouvoir faire son choix.
Et ça m’a pris du temppppps.

Je veux partir sur du kube car je me rends compte que j’étais entrain de ré inventer la roue: j’ai eu des petits besoins. Pour commencer un petit Nextcloud et donc un PostgreSQL, puis sont venus d’autres services, et d’autres utilisateurs, donc un petit LDAP, accompagné d’OAuth.

L’évolution de cette “infra” a suivi le parcours classique, et a surtout suivi “l’air du temps”. Je suis passé par des VM administrées entièrement à la mano, puis je suis passé sur du LXC dans du Proxmox pour alléger un peu tout ça. De nouveau une grosse VM dans laquelle on pose des containeurs. Au début du docker, puis du podman avec du Quadlet (bah ouais ça me permettait de faire un peu de IaC avec du Ansible). A chaque fois j’ai les backup (un peu) et le monitoring (beaucoup) à la traine. J’essaye de faire des petites conf réseaux entre les différentes machines qui gèrent tout ça mais je me retrouve avec des trucs vraiment bancals (bancaux ?).

Et je me suis rendu compte que kube est devenu plus accessible (plus besoin de méga infra). La marche d’entrée est un peu costaud, mais j’y gagne au final : vazy intègre proprement loki sur tous tes containeurs podman ou gère proprement ton mTLS sur ton propre CA avec des durées de certificats très courtes avec du podman.

En gros l’objectif :

  • quelques applis web classiques (genre nextcloud, un lecteur rss)
  • la possibilité de tester des applis web à la va vite
  • une forge
  • des runners
  • BDD
  • Backup
  • Monitoring

Le tout en IaC pour redéployer à la va vite.

0/ Installation

Bon dans la période IA qui achete toutes les ressources ça devient galère de louer des serveurs Je me retrouve avec un serveur ARM64 Ampere chez Hertzner, on verra ça me va. Commande brute de décoffrage. On verra dans d’autres installations comment changer ça

curl -sfL https://get.k3s.io | sh -

Bon et vu que comme moi le nom de la machine ne correspond pas au certificat/domaine par lequel vous allez contacter le serveur. On regénère directement le certificat avec un SAN additionnel

systemctl stop k3s
k3s server --tls-san NOM-DE-DOMAINE-SUPPLEMENTAIRE

(petit coup de CTRL-C, j’avoue c’est crade, mais j’ai pas trouvé d’autres méthodes pour le moment.)

systemct restart k3s

Wouh, on a un piti cluster K3s 🎉🥳.

Aller première étape, on est en 2026, on va configurer le traefik qui est venu avec K3s en gateway.

1/ Traefik

La doc de K3s nous le dit , des morceaux de K3s sont configurés à l’aide de helm. On va donc utiliser helm pour configurer notre traefik et on se base sur GitHub de K3s. On ne prend pas la peine de mettre en place l’ancienne méthode, à base d’Ingress et on s’assure de pouvoir déclarer des Gateway dans tous les namespace.
Hop on applique ça:

apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
  name: traefik
  namespace: kube-system
spec:
  valuesContent: |-
    providers:
      kubernetesIngress:
        enabled: false
      kubernetesGateway:
        enabled: true
    logs:
      access:
        enabled: true
    gateway:
      FromNamespaces: All
      listeners:
        web:
          namespacePolicy:
              from: All

Pour vérifier le fonctionnement de notre Gateway toute fraîche (encore un peu fragile), on va aller tester ça avec un petit containeur (trouvé dans la doc de traefik)

apiVersion: v1
kind: Namespace
metadata:
  name: whoami
---
# Application to expose
kind: Deployment
apiVersion: apps/v1
metadata:
  name: whoami
  namespace: whoami
spec:
  replicas: 1
  selector:
    matchLabels:
      app: whoami
  template:
    metadata:
      labels:
        app: whoami
    spec:
      containers:
        - name: whoami
          image: traefik/whoami
---
# Service to reach the application on the cluster
apiVersion: v1
kind: Service
metadata:
  name: whoami
  namespace: whoami
  labels:
    app: whoami
spec:
  type: ClusterIP
  ports:
    - port: 80
      name: whoami
  selector:
    app: whoami
---
# HTTPRoute
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: whoami-httproute
  namespace: whoami
spec:
  parentRefs:
    - name: traefik-gateway
      namespace: kube-system
  hostnames:
    - whoami.hetzner.htnl.fr
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: whoami
          namespace: whoami
          port: 80

Dans ce petit extrait, outre le déploiement accompagné de son service, on crée surtout une HTTPRoute. Ça c’est la ressource qui “vient” avec les Gateway Kube. Et pour le moment la plateforme ne fait que servir notre petit containeur whoami en HTTP (oui sans le S, ça viendrait plus tard)

2/ ArgoCD

Allez, parce que c’est sympa d’avoir un peu de visuel, on va se mettre un ArgoCD (oui oui, il y a plein d’autres supers fonctions a ArgoCD, les rollback et tout…)

kubectl create namespace argocd
kubectl apply -n argocd --server-side --force-conflicts -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

Il nous faut maintenant rajouter une HTTPRoute et aussi un gRPCRoute. L’API gRPC de ArgoCD sert sur le port 443, et à l’air de vouloir de l’enrober dans du SSL. Je veux pas que ArgoCD s’occupe du SSL, ça sera le job de la Gateway (d’où l’uasage du server.insecure).

apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cmd-params-cm
  namespace: argocd
  labels:
    app.kubernetes.io/name: argocd-cmd-params-cm
    app.kubernetes.io/part-of: argocd
data:
  server.insecure: "true"

Et pour les routes

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: httproute-argocd
  namespace: argocd
spec:
  parentRefs:
    - name: traefik-gateway
      namespace: kube-system
  hostnames:
    - argo.hetzner.htnl.fr
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: argocd-server
          namespace: argocd
          port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: GRPCRoute
metadata:
  name: grpcroute-argocd
  namespace: argocd
spec:
  parentRefs:
    - name: traefik-gateway
      namespace: kube-system
  hostnames:
    - "argo.hetzner.htnl.fr"
  rules:
    - backendRefs:
        - name: argocd-server
          port: 443
      matches:
        - headers:
            - name: Content-Type
              type: RegularExpression
              value: "^application/grpc.*$"

Pour récupérer votre mot de passe argocd:

k get secret argocd-initial-admin-secret -o jsonpath={.data.password} |base64 -d

*(Bien sur, on ne prend pas le % final)

Et vu qu’on vient de modifier la configmap pour dire au serveur ArgoCD de ne plus faire de SSL, il faut le reload

k delete pod -l app.kubernetes.io/name=argocd-server

(Aller on change son MdP, ou au moins on le retire de secret kube)

3/ Cert-Manager

Grooos sujet Maintenant qu’on a ArgoCD, on va souvent utiliser la resource “Application”

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: cert-manager
spec:
  destination:
    namespace: cert-manager
    server: https://kubernetes.default.svc
  source:
    path: .
    repoURL: oci://quay.io/jetstack/charts/cert-manager:v1.19.4
    targetRevision: 1.19.4
    helm:
      parameters:
        - name: crds.enabled
          value: "true"
        - name: installCRDs
          value: "true"
      values: |-
        config:
          enableGatewayAPI: true   # <- MEGA IMPORTANT
  sources: []
  project: default
  syncPolicy:
    syncOptions:
      - CreateNamespace=true
      - ServerSideApply=true

Une fois cert-manager installé on a besoin de le configurer. Le premier truc à faire c’est mettre en place un Issuer (en gros c’est quoi ton CA, et comment le contacter). Il existe Issuer et ClusterIssuer, il y en a un qui est défini dans un namespace, l’autre qui flotte dans l’ether.
Dans mon cas, je ne veux pas que l’Issuer soit limité à un namespace, donc je vais utiliser un ClusterIssuer, et mon CA, je vais le “contacter” en ACME (coucou Let’s Encrypt).

Pour commencer on va tester avec le service de test (staging) de Let’s Encrypt et donc on va créer un ClusterIssuer relié à ce CA.

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
  namespace: kube-system
spec:
  acme:
    email: METTRE_SON_EMAIL_ICI
    profile: tlsserver
    privateKeySecretRef:
      name: k3s-issuer-staging-account-key
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    solvers:
      - http01:
          gatewayHTTPRoute:
            parentRefs:
              - name: traefik-gateway
                namespace: kube-system
                kind: Gateway

On peut déjà voir si on est en bonne relation avec Let’s Encrypt

kubectl get clusterissuers.cert-manager.io -o wide

Maintenant le but c’est de pousser cert-manager à générer les certificats par la déclaration dans la gateway.
D’abord pour la configuration générale de Traefik on va rester basé sur ce qu’offre K3s/Traefik.

Par contre on va arreter de gérer la configuration des différentes gateway par ce chart. La syntaxe de ce Helm chart est un peu trop “tordue” à mon gout pour ça

Petite note: il y a une valeur par défaut dans la chart pour le tag de l’image à déployer qui est ’erroné’ (je dois surement manquer quelque chose), le tag n’est pas préfixé par un ‘v’ ainsi je suis obligé de le préciser manuellement.

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: traefik
spec:
  destination:
    namespace: kube-system
    server: https://kubernetes.default.svc
  source:
    path: charts/traefik/38.0.201+up38.0.2
    repoURL: https://github.com/k3s-io/k3s-charts/
    targetRevision: HEAD
    helm:
      values: |-
        image:
          tag: "3.6.7"
        providers:
          kubernetesIngress:
            enabled: false
          kubernetesGateway:
            enabled: true
        logs:
          access:
            enabled: true
        gateway:
          enabled: false

  sources: []
  project: default

Je met le manifest de “gestion” de la gateway dans un git (pour moi ça sera ici)
Le fichier hetzner/global-gateway.yaml

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: traefik-gateway
  namespace: kube-system
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-staging
spec:
  gatewayClassName: traefik
  listeners:
    - name: web
      protocol: HTTP
      port: 8000
      allowedRoutes:
        namespaces:
          from: All

    - name: whoami-https
      protocol: HTTPS
      port: 8443
      hostname: whoami.hetzner.htnl.fr
      tls:
        certificateRefs:
          - group: ""
            kind: Secret
            name: whoami-htnl-fr-tls
        mode: Terminate
      allowedRoutes:
        namespaces:
          from: All

Ce fichier, bien brut de décoffrage, mérite une transformation en Helm. On verra ça plus tard. Là, le but c’est de tester cert-manager.

L’application ArgoCD:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: traefik-gateway
spec:
  destination:
    namespace: kube-system
    server: https://kubernetes.default.svc
  source:
    path: hetzner
    repoURL: https://git.halletienne.fr/halletienne/traefik-gateway.git
    targetRevision: HEAD
    directory:
      include: global-gateway.yaml
  sources: []
  project: default

Si tout s’est bien passé, on voit dans l’application ArgoCD le certificat généré. Bon actuellement si vous vous rendez à l’adresse du whoami vous aurez une alerte sécurité. Oui, l’environnement staging de Let’s Encrypt n’offre pas des certificats “trusted”.
Si quelqu’un suit ce ‘guide’, je vous conseille fortement, de vous assurer que votre génération de certificat marche avec l’environnement staging de Let’s Encrypt avant de passer sur leur prod. Ils ont des mécanismes de RateLimite plus sévère en prod (compréhensible), et c’est jamais agréable de devoir attendre quand on a fait des bêtises dans sa conf.

On va passer le tout sur l’environnement de prod chez Let’s Encrypt.
D’abord le ClusterIssuer de prod:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
  namespace: kube-system
spec:
  acme:
    email: METTRE_SON_EMAIL_ICI
    profile: tlsserver
    privateKeySecretRef:
      name: k3s-issuer-account-key
    server: https://acme-v02.api.letsencrypt.org/directory
    solvers:
      - http01:
          gatewayHTTPRoute:
            parentRefs:
              - name: traefik-gateway
                namespace: kube-system
                kind: Gateway

Et on met à jour la Gateway avec ce nouveau ClusterIssuer.
J’ai aussi rajouté le domaine ArgoCD:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: traefik-gateway
  namespace: kube-system
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod # <---- LAAAA
spec:
  gatewayClassName: traefik
  listeners:
    - name: web
      protocol: HTTP
      port: 8000
      allowedRoutes:
        namespaces:
          from: All

    - name: whoami-https
      protocol: HTTPS
      port: 8443
      hostname: whoami.hetzner.htnl.fr
      tls:
        certificateRefs:
          - group: ""
            kind: Secret
            name: whoami-htnl-fr-tls
        mode: Terminate
      allowedRoutes:
        namespaces:
          from: All

    - name: argocd-https
      protocol: HTTPS
      port: 8443
      hostname: argo.hetzner.htnl.fr
      tls:
        certificateRefs:
          - group: ""
            kind: Secret
            name: argo-htnl-fr-tls
        mode: Terminate
      allowedRoutes:
        namespaces:
          from: All

Petite note: quay.io (le fournisseur de la chart de cert-manager) peut parfois vous rate-limite si vous restez non authentifié. Il peut donc être intéressant de donner un couple Login/MdP pour avoir un peu plus de bande passante.
Si tu vois des HTTP 401 quand tu configures cert-manager, c’est surement ça.

Vous pouvez créer un secret pour ça:

---
apiVersion: v1
kind: Secret
metadata:
  name: quay-oci-repo
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: repo-creds
stringData:
  username: COUPLE_LOGIN_MDP
  password: VOTRE_MDP
  project: default
  type: oci
  url: oci://quay.io/jetstack/charts/cert-manager

4/ Local storage

Je sais que faire du stockage directos sur le node, c’est bof (et encore ça dépend des situations). Mais pour commencer on va faire comme ça. On réserve l’object storage et autre stockage, pour un autre article.
(Et en plus je lis que dans les reco CloudNative-PG, c’est pas trop mal de faire du local, puis d’utiliser la synchro native de PostgreSQL).

K3s met à dispo un petit mécanisme, largement suffisant pour notre usage actuel, le local-path-provisioner. Pour le coup, je me suis ‘amusé’ et j’ai écrit ma petite chart Helm pour le gérer (git) comme je l’entendais: je voulais juste avoir à spécifier le nom du node, et indiquer où le stockage doit se faire.
Sinon de base k3s met tout dans /var/lib/rancher/k3s/storage (aller voir dans la cm local-patch-config -n kube-system).

En application ArgoCD ça donne :

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: local-path-k3s
spec:
  destination:
    namespace: kube-system
    server: https://kubernetes.default.svc
  source:
    path: .
    repoURL: https://git.halletienne.fr/halletienne/local-path-k3s.git
    targetRevision: HEAD
    helm:
      values: |-
        nodes:
          - nodeName: k3s-demo
            pathList:
              - /opt/kubernetes
  sources: []
  project: default

5/ Metrics

5.1/ Prometheus

Bon, on commence à se rapprocher de plus en plus d’une situation de base que j’avais avec du Ansible + Podman Quadlets. Mais cette fois je veux avoir des metric tout de suite !

Par chance (non, c’est tout le but de la manoeuvre), il existe une super chart et des super conf déjà dispo sur GitHub.

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: prometheus-stack
spec:
  destination:
    namespace: monitoring
    server: https://kubernetes.default.svc
  source:
    path: charts/kube-prometheus-stack
    repoURL: https://github.com/prometheus-community/helm-charts/
    targetRevision: kube-prometheus-stack-82.4.1
  sources: []
  project: default
  syncPolicy:
    syncOptions:
      - CreateNamespace=true
      - ServerSideApply=true

Attention, un peu lourd à déployer. ArgoCD peut croire qu’il se met en carafe car il mouline longtemps, mais normalement ça réussi sans soucis

Et pour avoir accès aux jolis dashboard de grafana, il nous faut une HTTPRoute:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: httproute-mon
  namespace: monitoring
spec:
  parentRefs:
    - name: traefik-gateway
      namespace: kube-system
  hostnames:
    - mon.hetzner.htnl.fr
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: prometheus-stack-grafana
          namespace: monitoring
          port: 80

Avec ça, on a déjà un paquet de dashboards.
Si on veut s’amuser à récolter les info Traefik, on modifie l’application ArgoCD du Traefik avec les infos suivantes (partie ‘metrics’)

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: traefik
spec:
  destination:
    namespace: kube-system
    server: https://kubernetes.default.svc
  source:
    path: charts/traefik/38.0.201+up38.0.2
    repoURL: https://github.com/k3s-io/k3s-charts/
    targetRevision: HEAD
    helm:
      values: |-
        image:
          tag: "3.6.7"
        providers:
          kubernetesIngress:
            enabled: false
          kubernetesGateway:
            enabled: true
        logs:
          access:
            enabled: true
        gateway:
          enabled: false
        metrics:
          prometheus:
            service:
              enabled: true
            serviceMonitor:
              enabled: true
              additionalLabels:
                release: prometheus-stack

  sources: []
  project: default

5.2/ Collection de log

Bon c’est beaucoup moins sexy que les beaux dashboards de metrics, mais ça reste utile. J’avoue que pour le sujet, je ne me casse pas trop la tête pour le moment.
J’ai créé deux applications ArgoCD, une pour Loki et une pour Alloy (le remplaçant de Promtail). Et pour le moment, j’ai pas non plus cherché comment intégrer en IaC cette source de donnée dans Grafana, je l’ai rajouté à la main…

Pour Loki:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: loki
spec:
  destination:
    namespace: loki
    server: https://kubernetes.default.svc
  source:
    path: ""
    repoURL: https://grafana.github.io/helm-charts
    targetRevision: 6.53.0
    chart: loki
    helm:
      values: |-
        loki:
          commonConfig:
            replication_factor: 1
          storage:
            type: "filesystem"
            bucketNames:
              chunks: chunks
              ruler: ruler
              admin: admin
          schemaConfig:
            configs:
              - from: "2024-04-01"
                store: tsdb
                object_store: filesystem
                schema: v13
                index:
                  prefix: loki_index_
                  period: 24h
          storage_config:
            filesystem:
              directory: /tmp/loki/chunks
          pattern_ingester:
            enabled: true
          limits_config:
            allow_structured_metadata: true
            volume_enabled: true
          ruler:
            enable_api: true
          auth_enabled: false

        minio:
          enabled: false

        deploymentMode: SingleBinary

        singleBinary:
          replicas: 1
          persistence:
            storageClass: local-path
            accessModes:
              - ReadWriteOnce
            size: 2Gi

          resources:
            requests:
              cpu: "1"
              memory: "1Gi"
            limits:
              cpu: "2"
              memory: "2Gi"

        sidecar:
          image:
            repository: kiwigrid/k8s-sidecar
            tag: 1.30.0
          resources:
            requests:
              cpu: 50m
              memory: 50Mi
            limits:
              cpu: 100m
              memory: 100Mi

        backend:
          replicas: 0
        read:
          replicas: 0
        write:
          replicas: 0

        chunksCache:
          allocatedMemory: 500
  sources: []
  project: default
  syncPolicy:
    syncOptions:
      - CreateNamespace=true
      - ServerSideApply=true

Et pour Alloy:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: alloy
spec:
  destination:
    namespace: loki
    server: https://kubernetes.default.svc
  source:
    path: ""
    repoURL: https://grafana.github.io/helm-charts
    targetRevision: 1.6.0
    chart: alloy
    helm:
      values: |-
        alloy:
          configMap:
            content: |-
              logging {
                level = "debug"
                format = "logfmt"
              }
              discovery.kubernetes "pods" {
                role = "pod"
              }
              discovery.relabel "pods" {
                targets = discovery.kubernetes.pods.targets

                rule {
                  source_labels = ["__meta_kubernetes_namespace"]
                  target_label = "namespace"
                  action = "replace"
                }

                rule {
                  source_labels = ["__meta_kubernetes_pod_label_app_kubernetes_io_name"]
                  target_label = "app"
                  action = "replace"
                }

                rule {
                  source_labels = ["__meta_kubernetes_pod_container_name"]
                  target_label = "container"
                  action = "replace"
                }

                rule {
                  source_labels = ["__meta_kubernetes_pod_name"]
                  target_label = "pod"
                  action = "replace"
                }
              }
              loki.source.kubernetes "pods" {
                targets    = discovery.relabel.pods.output
                forward_to = [loki.process.process.receiver]
              }
              loki.process "process" {
                forward_to = [loki.write.loki.receiver]

                stage.drop {
                  older_than          = "1h"
                  drop_counter_reason = "too old"
                }
                stage.match {
                  selector = "{instance=~\".*\"}"
                  stage.json {
                    expressions = {
                      level = "\"level\"",
                    }
                  }
                  stage.labels {
                    values = {
                      level = "level",
                    }
                  }
                }
                stage.label_drop {
                  values = [ "service_name" ]
                }
              }
              loki.write "loki" {
                endpoint {
                  url = "http://loki.loki.svc.cluster.local:3100/loki/api/v1/push"
                }
              }
          mounts:
            varlog: true
            dockercontainers: true

          resources:
            limits:
              cpu: 200m
              memory: 128Mi
            requests:
              cpu: 100m
              memory: 128Mi
  sources: []
  project: default
  syncPolicy:
    syncOptions:
      - CreateNamespace=true
      - ServerSideApply=true

La source de donnée à ajouter dans grafana est http://loki.loki.svc.cluster.local:3100
En vrai les logs centralisés c’est très bien, mais c’est un peu de boulot pour que ça soit propre.

Les configurations de cette sous section était fortement inspirée par cet article de blog

6/ Cloud Native PG

L’installation n’est pas si compliquée. Mais l’usage et la configuration de ce magnifique outil méritera un article à part. (C’est magique, Cloud Native PG)

Petite note: Un cluster est sensé être dédié à une base

7/ Git - Forgejo

Pour déployer Forgejo

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: forgejo
  namespace: argocd
spec:
  destination:
    namespace: forgejo
    server: https://kubernetes.default.svc
  source:
    path: .
    repoURL: https://code.forgejo.org/forgejo-helm/forgejo-helm.git
    targetRevision: v16.0.2
    helm:
      values: |-
        httpRoute:
          enabled: True
          parentRefs:
            - name: traefik-gateway
              namespace: kube-system
          port: 3000
          hostnames:
            - git.hetzner.htnl.fr
        global:
          storageClass: local-path
        gitea:
          config:
            APP_NAME: "My humble forge"
          admin:
            email: ENTER_YOUR_EMAIL
            username: gitea_admin

  sources: []
  project: default
  syncPolicy:
    syncOptions:
      - CreateNamespace=true

Et on va rajouter l’entrée dans Traefik Gateway

- name: git-https
  protocol: HTTPS
  port: 8443
  hostname: git.hetzner.htnl.fr
  tls:
    certificateRefs:
      - group: ""
        kind: Secret
        name: git-htnl-fr-tls
    mode: Terminate
  allowedRoutes:
    namespaces:
      from: All

-2/ Améliorations

Toute cette mise en place est vraiment plaisante à faire. Mais il y a beaucoup de chose à redire sur tout ça:

  • Ressources

    Les outils de monitoring sont vraiment bien pour ça. Il va falloir optimiser, voir où sont les abus . Et le premier que je vois, c’est l’usage en monitoring pour une infra si petite. Donc je pense qu’un des premiers trucs à faire c’est limiter les ressources. Ensuite, on va diviser les usages : l’application argocd prometheus-stack est sympa, mais elle fait venir grafana et prometheus en un seul tas. Je pense qu’il serait plus sympa de diviser ça en grafana d’un côté et prometheus d’un autre

  • mTLS de partoutttttt

    Je sais qu’un des trucs que j’avais aimé faire quand je bidouillais avec mes podman, c’était la mise en place de certains services derrière du mTLS en plus de leur authentification classique. Soyons honnête ça rassure, ça évite l’usage à gogo d’un {tail,head}scale sur les machines d’usage.

  • Remplacer ArgoCD

    J’ai lu à plein d’endroit des choses sur ‘flux’ qui ont l’air coule

 

blog

Mon petit espace


Premieres briques d'une infra avec K3s

By halletienne, 2026-04-23