Ce qu’on va faire
Nous allons installer Nginx Gateway Fabric pour exposer nos services Kubernetes sur internet, ainsi que Cert Manager pour gérer les certificats TLS.
Architecture cible
Je possède un nom de domaine chez Cloudflare, je vais donc utiliser Cloudflare comme DNS provider pour gérer les enregistrements DNS de mon domaine. Nginx Gateway Fabric sera configuré pour utiliser un LoadBalancer de type External, ce qui me permettra d’obtenir une adresse IP publique pour exposer mes services.
Voici un schéma pour une meilleur compréhension :
graph TD
A[Internet] --> B[Cloudflare DNS + Proxy]
B --> C[Enregistrement DNS pointant vers l'IP du LoadBalancer GCP]
C --> D[LoadBalancer GCP]
D --> E[Nginx Gateway Fabric sur Kubernetes]
E --> F[HTTPRoute -> Services Kubernetes]
F --> G[Cert Manager pour TLS]
Étapes
1. Installation de la CRD gateway API
Les ingress controller ne sont plus supportés, il faut désormais ce tourner vers les gateway API. Il existe plusieurs implémentations, j’ai choisi d’installer Nginx Gateway Fabric qui est une solution robuste et bien supportée.
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.0/standard-install.yaml
ou via ArgoCD (la sync-wave à 0 permet de s’assurer que les CRDs sont installées avant les autres ressources) :
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: gateway-api-crds
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
annotations:
argocd.argoproj.io/sync-wave: "0"
spec:
project: default
source:
repoURL: https://github.com/nginx/nginx-gateway-fabric.git
targetRevision: v2.6.3
path: config/crd/gateway-api/standard
destination:
server: https://kubernetes.default.svc
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- ServerSideApply=true
2. Installation de Nginx Gateway Fabric
Je vais aller assez vite sur l’installation de Nginx Gateway Fabric, si vous avez suivi les articles précédents cela ne devrait pas poser de problème.
On ajoute dans notre repo de CD la configuration suivante :
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: nginx-gateway-fabric
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
annotations:
argocd.argoproj.io/sync-wave: "1"
spec:
project: default
source:
repoURL: ghcr.io/nginx/charts
chart: nginx-gateway-fabric
targetRevision: 2.6.3
helm:
releaseName: nginx-gateway-fabric
destination:
server: https://kubernetes.default.svc
namespace: nginx-gateway
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
Après quelques secondes les ressources sont créées et nous avons notre gateway qui tourne.
kubectl get pods -n nginx-gateway
NAME READY STATUS RESTARTS AGE
nginx-gateway-fabric-77c559bb4f-c7zlb 1/1 Running 0 93s
3. Installation de Cert Manager
Le principe est le même, voici la configuration à appliquer :
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: cert-manager
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
annotations:
argocd.argoproj.io/sync-wave: "0"
spec:
project: default
source:
repoURL: https://charts.jetstack.io
chart: cert-manager
targetRevision: v1.20.2
helm:
releaseName: cert-manager
values: |
crds:
enabled: true
keep: true
destination:
server: https://kubernetes.default.svc
namespace: cert-manager
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
Après quelques secondes les ressources sont créées et nous avons notre cert manager qui tourne.
kubectl get pods -n cert-manager
NAME READY STATUS RESTARTS AGE
cert-manager-65dd558d9c-qzjw9 1/1 Running 0 5m6s
cert-manager-cainjector-77bd875bf4-6bcpn 1/1 Running 0 5m5s
cert-manager-startupapicheck-fhsng 0/1 ContainerCreating 0 2m29s
cert-manager-webhook-69666f4d4c-7wccg 1/1 Running 0 5m6s
4. Configuration Cloudflare
Récupération de l’IP du LoadBalancer GCP
Maintenant que ma gateway est en place, je vais configurer Cloudflare pour pointer mon domaine vers l’IP publique de mon LoadBalancer GCP. Je vais créer un enregistrement A dans Cloudflare qui pointe vers l’IP du LoadBalancer, et je vais activer le proxy de Cloudflare pour bénéficier de la protection DDoS et du CDN.
Pour cela il faut récupérer l’IP du LoadBalancer GCP qui a été créé pour exposer Nginx Gateway Fabric. Vous pouvez le faire avec la commande suivante :
kubectl get svc -n nginx-gateway
# Noter l'EXTERNAL-IP → à mettre dans Cloudflare DNS (enregistrement A)
Création d’un token API Cloudflare
Pour automatiser la configuration de Cloudflare, je vais créer un token API avec les permissions nécessaires pour gérer les enregistrements DNS. Dans Cloudflare → My Profile → API Tokens → Create Token :
Template : Edit zone DNS
Permissions : Zone / DNS / Edit
Zone : mon domaine (ici valerian-pyckaert.dev)
kubectl create secret generic cloudflare-api-token \
--namespace cert-manager \
--from-literal=api-token=<TOKEN_CF>
5. Configuration de Cert Manager pour Cloudflare
Il s’agit maintenant d’ajouter une ressource ClusterIssuer pour utiliser le token API Cloudflare et gérer les certificats TLS automatiquement.
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt
spec:
acme:
email: ton@email.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-key
solvers:
- dns01:
cloudflare:
apiTokenSecretRef:
name: cloudflare-api-token
key: api-token
Une fois cette configuration appliquée, Cert Manager pourra automatiquement obtenir et renouveler les certificats TLS pour les domaines gérés via Cloudflare. Pour vérifier le bon fonctionnement :
kubectl get clusterissuer -A
NAME READY AGE
letsencrypt-prod True 17h
kubectl get certificate -A
default wildcard-cert True wildcard-cert 54m
6. Configuration d’une HTTPRoute pour exposer un service
Maintenant que tout est en place, nous allons créer une ressource HTTPRoute pour exposer un service Kubernetes via Nginx Gateway Fabric et positionner le certificat généré par Cert Manager.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: main-gateway
namespace: default
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
gatewayClassName: nginx
listeners:
- name: https
hostname: "*.mondomaine.com"
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: wildcard-cert
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-app
spec:
parentRefs:
- name: main-gateway
hostnames:
- "app.mondomaine.com"
rules:
- backendRefs:
- name: my-service
port: 80
---
Résultat
Si vous avez suivi chaque étapes vous devriez avoir un résultat similaire à celui-ci :
❯ kubectl get gateway -A
NAMESPACE NAME CLASS ADDRESS PROGRAMMED AGE
default main-gateway nginx 34.156.85.195 True 29m
❯ kubectl get httproute -A
NAMESPACE NAME HOSTNAMES AGE
default nginx-app ["nginx.valerian-pyckaert.dev"] 29m
❯ kubectl get certificate -A
NAMESPACE NAME READY SECRET AGE
default wildcard-cert True wildcard-cert 21m
Votre service devrait être accessible via l’URL https://nginx.mondomaine.com et le certificat TLS devrait être valide, en l’occurence pour moi c’était https://nginx.valerian-pyckaert.dev avec un certificat pour valerian-pyckaert.dev généré par Let’s Encrypt.

Pour ce faire j’ai enregistré un enregistrement A dans Cloudflare qui pointe vers l’IP du LoadBalancer GCP :

Obstacles rencontrés
La gateway ne peut pas accéder au namespace demo et au service nginx
J’avais installé ma ressource HTTPRoute dans le namespace default alors que mon service nginx était dans le namespace demo. Par défaut, la gateway n’a pas accès aux ressources d’autres namespaces, il faut donc lui accorder les permissions nécessaires.
Soit via un ReferenceGrant :
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-gateway-to-demo
namespace: demo
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: default
to:
- group: ""
kind: Service
Soit installé la ressource HTTPRoute dans le même namespace que le service nginx (ici demo).
Pods en pending
Par péché de rapidité j’ai volontairement omis de spécifier les ressources CPU et memory de mes workloads, ce qui a pour conséquence que les pods sont restés en pending car le scheduler ne pouvait pas les placer sur un nœud. Il faut donc veiller à toujours spécifier les ressources de vos workloads pour éviter ce genre de problème.
Pour la suite
Dans le prochain et dernier article de cette série The Forge je vous parlerai de SOPS et de comment gérer vos secrets de manière sécurisée dans Kubernetes. Nous verrons comment chiffrer vos secrets avec SOPS et les stocker dans Git pour une gestion centralisée et sécurisée de vos secrets.
