À retenir
- Sur un cluster GKE Dataplane V2, la suppression d’une
NetworkPolicyKubernetes n’a pas rétabli l’egress des pods déjà en cours d’exécution. Leur endpoint eBPF Cilium a continué d’appliquer une règle default-deny obsolète jusqu’au redémarrage du pod.- L’indice tenait dans une séparation nette : un pod
netshootcréé après la suppression se connectait sans problème, le pod d’origine non, et un redémarrage réglait le tout instantanément.- Cause racine : les policy maps eBPF propres à chaque endpoint n’ont pas été recalculées pour les endpoints de longue durée après la suppression de la policy. Les nouveaux endpoints repartaient d’un état correct, de zéro.
- Il s’agit d’un comportement au niveau du dataplane, pas d’un bug applicatif. Cilium équipe désormais la majorité des clusters de production (adoption en hausse de 47 % sur un an, d’après le Cilium Annual Report 2025, CNCF, déc. 2025) : le rayon d’impact est large.
Une équipe plateforme a supprimé toutes les NetworkPolicy d’un namespace, s’attendait à voir le trafic s’ouvrir, et a vu un pod en cours d’exécution rester bloqué malgré tout. Plus aucune policy à appliquer, et pourtant les paquets continuaient d’être rejetés. Voici le post-mortem de cet incident : un bug d’état eBPF obsolète sur GKE Dataplane V2, reproduit de bout en bout, avec les commandes exactes qui l’isolent.
L’environnement était un cluster GKE de production exploité par l’équipe plateforme d’un grand constructeur automobile européen. La charge de travail touchée était un issuer PKI/ACME interne utilisé pour des tests d’intégration cert-manager. Nous avons volontairement gardé les détails génériques. C’est le mode de défaillance qui compte, et il peut frapper n’importe quel cluster Dataplane V2.
Que s’est-il passé sur le cluster ?
La charge de travail a perdu sa connexion à la base de données et ne l’a jamais récupérée. Un issuer PKI/ACME interne s’est mis à lever des timeouts MongoSocketOpenException contre son backend MongoDB sur le port 27017. L’équipe plateforme a supprimé toutes les NetworkPolicy du namespace pour écarter l’application des policies, et les erreurs ont continué à l’identique.
C’est là que quelque chose clochait. Quand on retire tous les objets policy, l’API server n’a plus rien à appliquer. Le résultat attendu, c’est un egress ouvert. Au lieu de ça, le pod en cours d’exécution continuait de tomber en timeout sur une connexion TCP vers mongodb:27017, comme si une règle default-deny était toujours active.
La chaîne de symptômes était courte et précise :
- Le pod issuer a perdu la connectivité MongoDB et remontait des timeouts de connexion.
- L’équipe a supprimé toutes les
NetworkPolicydu namespace, en s’attendant à un trafic totalement ouvert. - La connectivité n’est pas revenue. Le même pod continuait d’échouer.
Piège : Une
NetworkPolicysupprimée disparaît instantanément dekubectl get netpol. Ça vous dit que l’objet API n’existe plus. Ça ne vous dit rien sur le fait que le dataplane ait réellement recalculé les endpoints concernés. Ce sont deux horloges différentes.
Cilium est aujourd’hui le CNI le plus déployé en production sur Kubernetes, avec une adoption en hausse de 47 % sur un an et une présence dans plus de 60 % des déploiements interrogés (plus de 75 % en incluant les dataplanes managés comme GKE Dataplane V2), d’après le Cilium Annual Report 2025 (CNCF, déc. 2025). Le comportement du dataplane n’est pas une préoccupation de niche.
Pourquoi un pod neuf fonctionnait-il quand l’ancien restait bloqué ?
Le test d’isolement, c’était un second pod. Un conteneur de debug netshoot, lancé après la suppression de la policy, atteignait MongoDB immédiatement. Le pod issuer d’origine, créé avant la suppression, n’y arrivait toujours pas. Même namespace, mêmes labels, même destination. La seule différence tenait au cycle de vie. Cette séparation pointe vers l’état, pas vers le routage.
C’est l’embranchement diagnostique qui change tout. Si le chemin réseau lui-même était rompu, un nouveau pod échouerait aussi. Si le DNS ou la base de données étaient hors service, les deux pods échoueraient. Seul l’ancien pod qui échoue, pendant qu’un pod neuf réussit, ramène la cause à quelque chose de rattaché à l’endpoint existant dont un nouvel endpoint n’hérite jamais.
Nous l’avons confirmé avec le test le plus brut qui soit. Redémarrer le pod issuer a rétabli la connectivité aussitôt, sans aucun autre changement. Aucune modification de config, aucune réapplication de policy, aucune action sur le node. On supprime le pod, on laisse le scheduler le recréer, le trafic passe.
Trois observations, une conclusion :
- Nouveau pod créé après la suppression : fonctionne.
- Ancien pod créé avant la suppression : toujours bloqué.
- Ancien pod redémarré : fonctionne.
D’expérience, ce triptyque précis, le neuf marche, l’ancien échoue, le redémarrage corrige, est la signature d’un état de dataplane obsolète. On le retrouve avec les entrées conntrack, avec les service backend maps, et ici avec les policy maps eBPF. Quand un redémarrage est la seule chose qui aide, cessez de soupçonner la policy et commencez à soupçonner la map qui l’applique.
Quelle était la cause racine ?
La cause racine était une policy map eBPF obsolète, propre à l’endpoint de longue durée. Cilium, qui équipe GKE Dataplane V2, maintient une policy map par endpoint. La suppression d’une NetworkPolicy devrait déclencher un recalcul par le control plane et un flush des maps concernées. Pour l’endpoint existant, ce recalcul n’a pas abouti, si bien que la règle deny est restée active dans la map.
Voici le mécanisme, étape par étape. Le control plane de Cilium traduit les objets NetworkPolicy de haut niveau en entrées allow et deny concrètes à l’intérieur de la policy map BPF de chaque endpoint. L’application se fait dans le kernel, contre cette map, pas contre l’objet API. La map est donc la source de vérité au moment du paquet. Quand la policy change, la map doit être recalculée, sinon le kernel continue d’appliquer le dernier état qu’on lui a transmis.
Pour le pod en cours d’exécution, la map n’a jamais été recalculée après la suppression. Elle conservait une entrée egress default-deny qui n’avait plus aucune policy pour la justifier. Un endpoint fraîchement créé construit sa map à partir de l’état désiré courant, qui n’avait alors plus aucune restriction : il est donc remonté ouvert. Le redémarrage a fonctionné parce qu’il a détruit l’endpoint obsolète et en a créé un neuf.
Le recadrage important : sur un dataplane eBPF, kubectl delete netpol est une demande de convergence, pas une garantie que la convergence a eu lieu sur chaque endpoint. La plupart des guides traitent la suppression de policy comme instantanée et universelle. Elle n’est ni l’un ni l’autre quand la réconciliation entre control plane et map oublie un endpoint de longue durée — la même classe de défaillance par cache obsolète qui piège discrètement les contrôleurs Kubernetes quand un watch ou une work queue prend du retard sur l’état désiré.
Comment reproduire l’état eBPF obsolète ?
La défaillance est reproductible sur un cluster Dataplane V2 affecté en sept étapes, et toute la boucle prend quelques minutes. La clé est de comparer le même pod déjà en cours d’exécution à un pod neuf après avoir supprimé la policy. C’est cette comparaison qui transforme un « c’est toujours bloqué » ambigu en un signal clair d’état obsolète.
Faites d’abord tourner ceci sur un namespace hors production. Il vous faut un pod cible, une policy restreignant l’egress, et un second pod servant de témoin.
- Créez un namespace et un pod avec un label connu, par exemple
app=probe. - Appliquez une
NetworkPolicyegress default-deny ciblant ce pod. - Depuis le pod, confirmez que l’egress est bloqué. C’est la référence attendue.
- Supprimez la
NetworkPolicy. - Depuis le même pod, déjà en cours d’exécution, retentez l’egress. Constatez qu’il est toujours bloqué.
- Créez un nouveau pod dans le même namespace avec les mêmes labels. Confirmez que son egress n’est pas bloqué.
- Redémarrez le pod d’origine. Confirmez que son egress fonctionne désormais lui aussi.
# 2. default-deny egress on the target
kubectl -n probe-ns apply -f - <<'EOF'
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-egress
spec:
podSelector:
matchLabels:
app: probe
policyTypes:
- Egress
EOF
# 4. remove it
kubectl -n probe-ns delete networkpolicy deny-egress
# 5. same running pod, still blocked
kubectl -n probe-ns exec deploy/probe -- \
timeout 5 curl -sS https://example.com || echo "STILL BLOCKED"
# 6. fresh pod, works immediately
kubectl -n probe-ns run probe-new --image=nicolaka/netshoot \
--labels app=probe --restart=Never -- \
timeout 5 curl -sS https://example.com
Si l’étape 5 reste bloquée alors que l’étape 6 réussit, vous avez affaire au bug de map obsolète, pas à une policy active.
Quelles commandes révèlent les policy maps BPF obsolètes ?
Trois commandes Cilium transforment l’hypothèse en preuve, et elles s’exécutent depuis le pod agent Cilium sur le node qui héberge l’endpoint coincé. Ensemble, elles montrent la policy calculée de l’endpoint, le contenu brut de la map que le kernel applique, et les drops en temps réel. Ces preuves sont exactement ce dont un ticket de support Google Cloud a besoin.
Trouvez le pod agent sur le node affecté, faites un exec dedans, puis travaillez à partir de l’ID d’endpoint.
# locate the cilium agent on the node running the stuck pod
kubectl -n kube-system get pods -l k8s-app=cilium -o wide
# inside the agent pod:
# a. the endpoint's computed policy state
cilium endpoint get <endpoint-id>
# b. the raw eBPF policy map the kernel actually enforces
# the stale deny entry shows here after the policy is gone
cilium bpf policy get <endpoint-id>
# c. live drops attributable to the stale rule
cilium monitor --type drop
Lisez-les dans l’ordre. cilium endpoint get montre ce que le control plane pense que l’endpoint devrait appliquer. cilium bpf policy get montre ce que le kernel applique réellement : après la suppression, une entrée deny persistante ici, c’est la preuve accablante. cilium monitor --type drop capture les drops en temps réel pour relier une connexion en timeout précise à la map obsolète.
Piège : Comparez
cilium bpf policy getentre le pod coincé et le pod neuf. Si l’endpoint en cours d’exécution porte une entrée deny que le nouvel endpoint n’a pas, vous avez la preuve que la divergence est dans la map, pas dans un quelconque objet policy survivant. Sauvegardez les deux dumps dans un fichier horodaté avant de redémarrer quoi que ce soit, car le redémarrage détruit vos preuves.
Hubble aide aussi. Les flow logs du pod affecté montrent les tentatives d’egress qui se terminent en policy drops, ce qui corrobore le dump de map avec une vue au niveau applicatif.
Quelles autres limites de Dataplane V2 faut-il anticiper ?
L’état de policy obsolète n’est qu’un élément d’une liste plus longue de contraintes Dataplane V2 qu’il vaut mieux intégrer à la conception. La documentation GKE de Google Cloud elle-même signale plusieurs pièges qui surprennent les équipes en migration depuis un CNI historique. Les connaître d’avance évite la catégorie d’incidents du type « on l’a déployé et ça n’a rien fait, en silence », bien plus difficile à déboguer après coup.
Les limites documentées qui mordent le plus souvent :
- Pas de
CiliumNetworkPolicyL7. Dataplane V2 ne prend en charge que les policies L3/L4. Les règles L7 dans uneCiliumNetworkPolicyne sont pas appliquées (documentation GKE de Google Cloud, 2026). endPortsans effet, en silence. UneNetworkPolicyutilisant le champ de plageendPortest acceptée par l’API mais pas appliquée sur Dataplane V2 : une plage de ports que vous croyez ouverte ou fermée peut ne pas l’être.- Plafond de scalabilité du CRD.
CiliumNetworkPolicy(le CRD à portée de namespace) ne passe pas l’échelle au-delà de 5 000 nodes. Au-delà, seulCiliumClusterwideNetworkPolicyest pris en charge. - CPU de l’agent sous forte rotation. L’agent
anetdpeut consommer 2 à 3 vCPU par node sous une forte rotation de connexions TCP, ce qui se traduit par une pression CPU au niveau du node facile à mal attribuer. - ICMP fragmenté rejeté. Les paquets ICMP fragmentés sont rejetés, ce qui peut casser discrètement la découverte de path-MTU et certains diagnostics.
Il y a aussi un bug remonté par la communauté à surveiller : les network policies peuvent cesser de s’appliquer sur les nodes preemptibles (cilium/cilium#17446, GitHub). Si votre cluster mélange capacité spot et à la demande, voilà un autre trou de policy lié au cycle de vie à tester. La leçon vaut pour le reste de votre conception réseau GCP : traitez les topologies GKE et Private Service Connect inhabituelles comme des choses à valider sous charge, pas à supposer fonctionnelles.
GKE Dataplane V2 n’applique que la network policy L3/L4, ignore en silence le champ endPort de NetworkPolicy, et limite le CRD CiliumNetworkPolicy à portée de namespace aux clusters de 5 000 nodes ou moins, d’après la documentation GKE de Google Cloud (2026). Considérez les fonctionnalités de policy non prises en charge comme sans effet, pas comme des erreurs dont vous serez averti.
La tendance est positive. Cilium v1.19, sorti en 2026, durcit les valeurs par défaut des policies et améliore la visibilité sur les grands clusters (InfoQ, févr. 2026), ce qui vise précisément la catégorie de problèmes d’application silencieuse et de passage à l’échelle décrits plus haut.
Comment atténuer et détecter ce problème en production ?
L’atténuation se décompose en deux gestes : forcer la convergence pour l’incident immédiat, et ajouter de la détection pour repérer la divergence avant vos utilisateurs. Le correctif immédiat est celui que nous avons déjà démontré. Redémarrer le pod affecté reconstruit son endpoint et sa map eBPF à partir de l’état courant. C’est brutal, mais fiable et rapide.
Pour l’incident que vous avez sous les yeux :
- Redémarrez la charge de travail affectée après tout changement de policy qui « n’a pas pris ». Un rollout restart reconstruit la map de chaque endpoint à partir de l’état désiré courant.
- Collectez les preuves d’abord. Videz
cilium endpoint get,cilium bpf policy get, et un courtcilium monitor --type dropdans un fichier horodaté avant le redémarrage, pour ouvrir un ticket de support avec de vraies données.
Pour la détection et la prévention dans la durée :
- Traitez les changements de policy comme nécessitant une vérification, pas seulement une application. Après une suppression ou une modification, confirmez que les endpoints concernés la reflètent réellement. Ne partez pas du principe que
kubectl deletea atteint le kernel sur chaque node. - Alertez sur la signature. Une charge de travail qui remonte des timeouts de connexion vers une destination sans policy survivante est un signal fort. Croisez les drop flows de Hubble avec l’absence d’objet policy correspondant.
- Préférez les redéploiements aux modifications de policy en place pour les charges de travail de longue durée et sensibles aux connexions, jusqu’à ce que vous soyez sur une version de Cilium qui corrige ce défaut de recalcul.
Nous avons constaté que le garde-fou durable le moins coûteux est une ligne de runbook : « si un changement de policy ne s’est pas appliqué, redémarrez le pod et joignez le dump eBPF ». Cela transforme un casse-tête de plusieurs heures en une récupération de deux minutes, et préserve les preuves dont l’éditeur de la plateforme a besoin pour corriger la cause racine.
À retenir
Une NetworkPolicy supprimée est une demande de convergence adressée au dataplane, pas la preuve qu’elle a eu lieu. Sur GKE Dataplane V2, un pod de longue durée a continué d’appliquer une règle egress default-deny issue d’une map eBPF jamais recalculée après la disparition de la policy. La signature était sans équivoque une fois repérée : le nouveau pod marche, l’ancien est bloqué, le redémarrage corrige.
Retenez-en trois habitudes. D’abord, quand un changement de policy « ne s’applique pas », soupçonnez la map avant la policy, et prouvez-le avec un test de séparation à pod neuf. Ensuite, collectez cilium bpf policy get et les drop logs avant de redémarrer, car le redémarrage efface vos preuves. Enfin, concevez en tenant compte des limites documentées de Dataplane V2, pour que les fonctionnalités non prises en charge échouent bruyamment en revue, pas silencieusement en production.
C’est dans le dataplane que vit réellement l’application des règles. Surveillez-la à cet endroit, comme vous surveilleriez la gestion déterministe des adresses IP sur une flotte Kubernetes multi-tenant : à la couche qui décide du résultat, pas à celle qui se contente de le décrire.
Réponses directes
Questions fréquentes
Supprimer une NetworkPolicy rétablit-il toujours le trafic sur GKE Dataplane V2 ?
Pas de façon fiable pour les pods déjà en cours d'exécution. Sur un cluster affecté, la policy map eBPF de l'endpoint peut continuer d'appliquer une règle supprimée jusqu'au redémarrage du pod. Les nouveaux pods créés après la suppression construisent leur map à partir de l'état courant et se connectent sans souci : le signe le plus clair d'un état obsolète.
Comment confirmer que le blocage vient d'un état eBPF obsolète et non d'une policy active ?
Faites le test de séparation. Créez un pod neuf avec les mêmes labels dans le même namespace après avoir supprimé la policy. Si le nouveau pod se connecte mais que l'original reste bloqué, et qu'un redémarrage répare l'original, la cause est une map d'endpoint obsolète. Confirmez avec cilium bpf policy get sur les deux endpoints.
Pourquoi le redémarrage du pod règle-t-il le problème ?
Un redémarrage détruit l'ancien endpoint Cilium et en crée un nouveau. Le nouvel endpoint calcule sa policy map eBPF à partir de l'état désiré courant, qui ne contient plus la règle supprimée : il remonte donc sans restriction. La map obsolète qui appliquait le deny fantôme est jetée avec l'ancien endpoint.
Est-ce un bug Cilium ou un bug GKE ?
C'est un comportement au niveau du dataplane dans la stack GKE Dataplane V2 basée sur Cilium, pas une faute applicative ou de contrôleur. L'adoption de Cilium a progressé de 47 % sur un an (Cilium Annual Report 2025, CNCF, déc. 2025) : le schéma touche un large parc installé. Cilium v1.19 (InfoQ, févr. 2026) durcit la gestion des policies.