K3S, Traefik et Cilium sur la même machine
Pour permettre l’accès depuis l’extérieur à vos services dans Kubernetes, plusieurs méthodes existent avec leurs avantages et inconvénients. Que ce soit un “NodePort”, un “Ingress” ou encore un “Port Forward”, vous avez le choix selon vos besoins et contraintes. Dans cet article, nous allons nous focaliser sur la gateway-api.
J’ai écrit il y a déjà plusieurs années un document pour installer k3s et Cilium (mis à jour le 08/2025) que vous pouvez retrouver ici :
Installer k3s et déployer cilium sous Debian 12Avec ces quelques lignes, vous aurez tout le nécessaire pour installer un nœud Kubernetes avec Cilium, rapidement.J.HOMMET.NETJulien HOMMETÀ partir de 2024, la fonctionnalité Ingress de Kubernetes est dépréciée, nécessitant une mise à niveau vers une autre solution. Gateway-API est la solution qui prendra en charge tous les services offerts par l’ingress, tout en simplifiant la configuration et la gestion pour les administrateurs.
Désormais, la configuration réside dans deux fichiers principaux et des fichiers annexes. Il y a une ressource GatewayClass, une ressource Gateway et autant de HTTProute (ouTCProute, GRPCroute) que vous souhaitez exposer de services.
Contexte d’exécution#
Toutefois, en faisant les essais Web > Traefik > GatewayAPI (Cilium) > Services-Pods, je n’ai pas réussi à faire fonctionner le système dans son intégralité. Finalement, ayant qu’un nœud K3S et Traefik en frontal, il n’est pas nécessaire d’avoir en plus Gateway API, Traefik faisant déjà le travail.
J’ai choisi de déployer des services dans Kubernetes sur une nouvelle machine, en utilisant Cilium pour le réseau interne et Traefik comme reverse-proxy devant K3S. Cette architecture nécessite des ajustements pour fonctionner sur un seul hôte, car nous ne pouvons pas créer de nouvelles adresses locales ni générer de sous-réseaux chez le prestataire.
🤔 Pourquoi utiliser K3S, qui plus est sur un seul nœud, quand on peut tout faire avec docker-compose ?
😏 Pour repousser les limites et prouver qu’il est possible de faire différemment.
Installation de Cilium#
Pendant l’installation de K3S, j’ai volontairement désactivé la CNI, pour utiliser Cilium en lieu et place de Flannel. Avant de se lancer dans l’installation de Cilium, assurez-vous de plusieurs choses :
- la compatibilité entre votre version de Kubernetes et la version cible de Cilium
- avoir désactivé les options “
--flannel-backend=none --disable-kube-proxy --disable servicelb --disable-network-policy --disable traefik” de k3s ; - ne pas avoir de CNI déjà en place dans le cluster ;
- avoir sauvegardé ses données.
Rapidement, voici comment installer Cilium avec Helm :
helm repo add cilium https://helm.cilium.io/
helm repo update
helm upgrade --install cilium cilium/cilium \
--namespace kube-system \
--version 1.18.1 \
--set kubeProxyReplacement=true \
--set ipam.mode=kubernetes \
--set nodePort.enabled=true \
--set k8sServiceHost=127.0.0.1 \
--set k8sServicePort=6443 \
--set hubble.enabled=true \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=truekubeProxyReplacement=trueetipam.mode=kubernetes: Remplacekube-proxypar l’implémentation eBPF de Cilium ;nodePort.enabled=true: activation des NodePort pour exposer des services sur le nœud en direct ;hubble: une interface web pour visionner facilement les flux dans Kubernetes dans tous les namespace, grâce à eBPF. Option facultative.
À partir de maintenant, Cilium va se déployer dans le kube’ et permettre à vos pods d’avoir du réseau. Au bout de quelques minutes, les pods de Cilium doivent être démarrés et fonctionnels. Vous pouvez valider l’opération en récupérant les logs et en affichant le statut de Cilium :
kubectl -n kube-system exec ds/cilium -- cilium status --verbose
KVStore: Disabled
Kubernetes: Ok 1.33 (v1.33.3+k3s1) [linux/amd64]
Kubernetes APIs: ["EndpointSliceOrEndpoint", "cilium/v2::CiliumCIDRGroup", "cilium/v2::CiliumClusterwideNetworkPolicy", "cilium/v2::CiliumEndpoint", "cilium/v2::CiliumNetworkPolicy", "cilium/v2::CiliumNode", "core/v1::Pods", "networking.k8s.io/v1::NetworkPolicy"]
KubeProxyReplacement: True [ens3 <ip machine> (Direct Routing), wg0 10.0.0.1]
Host firewall: Disabled
SRv6: Disabled
CNI Chaining: none
CNI Config file: successfully wrote CNI configuration file to /host/etc/cni/net.d/05-cilium.conflist
Cilium: Ok 1.18.1 (v1.18.1-e8a7070f)
NodeMonitor: Listening for events on 6 CPUs with 64x4096 of shared memory
Cilium health daemon: Ok
...
IPv4 BIG TCP: Disabled
IPv6 BIG TCP: Disabled
BandwidthManager: Disabled
Routing: Network: Tunnel [vxlan] Host: Legacy
Attach Mode: Legacy TC
Device Mode: veth
Masquerading: IPTables [IPv4: Enabled, IPv6: Disabled]
Clock Source for BPF: ktime
Controller Status: 150/150 healthy
...
Proxy Status: OK, ip 10.42.0.221, 0 redirects active on ports 10000-20000, Envoy: external
Global Identity Range: min 256, max 65535
Hubble: Ok Current/Max Flows: 4095/4095 (100.00%), Flows/s: 47.52 Metrics: Disabled
KubeProxyReplacement Details:
Status: True
Socket LB: Enabled
Socket LB Tracing: Enabled
Socket LB Coverage: Full
Devices: ens3 <ip machine> (Direct Routing), wg0 10.0.0.1
Mode: SNAT
Backend Selection: Random
Session Affinity: Enabled
NAT46/64 Support: Disabled
XDP Acceleration: Disabled
Services:
- ClusterIP: Enabled
- NodePort: Enabled (Range: 30000-32767)
- LoadBalancer: Enabled
- externalIPs: Enabled
- HostPort: Enabled
Annotations:
- service.cilium.io/node
- service.cilium.io/node-selector
- service.cilium.io/proxy-delegation
- service.cilium.io/src-ranges-policy
- service.cilium.io/type
...Déploiement et exposition d’un service#
Maintenant, je vais créer un namespace demo, y déployer un pod web de démonstration, et lui ajouter le service pour l’exposition. Voici le manifest de demo.yaml :
apiVersion: v1
kind: Namespace
metadata:
name: demo-np
---
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: demo-np
name: demo-web-server
labels:
app: demo-web
spec:
replicas: 1
selector:
matchLabels:
app: demo-web
template:
metadata:
labels:
app: demo-web
spec:
containers:
- name: busybox-http
image: busybox:1.36
command: ["/bin/sh", "-c"]
args:
- |
mkdir -p /www
echo '<html><body><h1>Public Information Test Page</h1><p>This is a lightweight server running httpd on busybox for Gateway API testing.</p></body></html>' > /www/index.html
httpd -f -p 8080 -h /www
ports:
- containerPort: 8080
name: http
---
apiVersion: v1
kind: Service
metadata:
namespace: demo-np
name: demo-web-service
spec:
selector:
app: demo-web
ports:
- protocol: TCP
port: 8080
targetPort: 15000
type: NodePortAppliquez ce manifest (kubectl apply -f demo.yaml), vous devriez avoir toutes ces ressources :
kubectl get pod,service,deployment -n demo-np
NAME READY STATUS RESTARTS AGE
pod/demo-web-server-d4b5497b-zlqft 1/1 Running 0 11s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/demo-web-service NodePort 10.43.115.147 <none> 8080:30080/TCP 11s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/demo-web-server 1/1 1 1 11sNe reste plus qu’à configurer l’accès depuis l’extérieur avec Traefik. Si vous disposez d’un pare-feu ou tout autre système de filtrage avant votre machine, pensez à ouvrir le port TCP/80 pour ce test.
Configuration de Traefik#
Traefik est devant le cluster K3S, et il est temps de créer le fichier de configuration dynamique pour le service de test. J’ai installé Traefik en dur (article J.HOMMET.NET
), dans le dossier /etc/traefik. La configuration de base de Traefik est la plus simple possible (je n’ai pas configuré la partie TLS et HTTPS pour le moment, volontairement) :
---
global:
sendAnonymousUsage: true
checkNewVersion: false
api:
dashboard: false
debug: false
log:
filePath: "/var/log/traefik.log"
level: "INFO"
maxSize: 32 # Taille en Mb
maxBackups: 30 # 30 fichiers max
compress: true # Gzip
providers:
file:
directory: "/etc/traefik/dynamic"
watch: true
entryPoints:
web:
address: ":80"Dans /etc/traefik/dynamic/rt-k3s-demo.yml, saisissez ce contenu :
http:
routers:
rt-k3s-demo-http:
rule: "HostRegexp(`demo.hommet.net`)"
entryPoints:
- web
service: svc-k3s-demo-http
services:
svc-k3s-demo-http:
loadBalancer:
servers:
- url: "http://127.0.0.1:30080"Le service pointe vers le port 30080 qui est le NodePort décrit dans le service déployé précédemment, et avec une regex sur le nom « demo.hommet.net ». En faisant un curl depuis la machine K3S, voici ce qu’on obtient :
$ curl -H "Host: demo.hommet.net" http://127.0.0.1:30080/
<html><body><h1>Public Information Test Page</h1><p>This is a lightweight server running httpd on busybox for testing.</p></body></html>Et depuis l’extérieur : curl http://demo.hommet.net, le résultat doit aussi être ceci :
curl -v http://demo.hommet.net/
* Host demo.hommet.net:80 was resolved.
* IPv6: (none)
* IPv4: ...
* Trying ...:80...
* Connected to demo.hommet.net port 80
> GET / HTTP/1.1
> Host: demo.hommet.net
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Content-Length: 137
< Content-Type: text/html
< Date: Mon, 25 Aug 2025 14:07:09 GMT
< Etag: "68ac6c41-89"
< Last-Modified: Mon, 25 Aug 2025 13:59:29 GMT
<
<html><body><h1>Public Information Test Page</h1><p>This is a lightweight server running httpd on busybox for testing.</p></body></html>
* Connection #0 to host demo.hommet.net left intactConclusion#
Dans cette situation, K3S est utilisé comme orchestrateur de conteneurs, et Traefik, en dehors de Kubernetes, agit comme reverse-proxy. Cette mise en place facilite la maintenance côté réseau (pas de complexité induite par Gateway-API et autres outils de réseau internes Kubernetes). Pour mon cas, c’est la méthode la plus simple pour utiliser k3s pour déployer des services et exposer des services par Traefik.
Il y a toutefois une limitation de taille : chaque service oblige une exposition de chaque service par des NodePort, ne permettant pas la mise en place dans des environnements disposants de plusieurs nœuds.
Sources#
Cilium Quick Installation — Cilium 1.18.1 documentationLogoGateway API Support — Cilium 1.18.1 documentationLogoGatewayClass Parameters Support — Cilium 1.19.0-dev documentationLogoServiceUne manière abstraite d’exposer une application s’exécutant sur un ensemble de Pods en tant que service réseau. Avec Kubernetes, vous n’avez pas besoin de modifier votre application pour utiliser un mécanisme de découverte de services inconnu. Kubernetes donne aux pods leurs propres adresses IP et un nom DNS unique pour un ensemble de pods, et peut équilibrer la charge entre eux. Motivation Les Pods Kubernetes sont mortels. Ils naissent et lorsqu’ils meurent, ils ne ressuscitent pas.Kubernetes
- Mots-clés
- #kubernetes #traefik #cilium
- Auteur
- Julien HOMMET
- date +"%Y-%m-%d"
- Temps_lecture
- 7 minutes
- quantité_mots
- 1464 mots
- Catégorie
- tuto
- maj $(date +"%Y-%m-%d")