La connectivité privée a cessé d’être un besoin de niche pour devenir la norme par défaut. Google Cloud rapporte que le trafic Private Service Connect (PSC) a quadruplé en 2025 et sert désormais de façade à plus de 40 services Google publiés (Google Cloud, “What’s new in cloud networking at Next ‘26”, 2026). Dans le même temps, 82 % des organisations font aujourd’hui tourner Kubernetes en production (CNCF Annual Survey 2025, 2026). Superposez ces deux courbes et vous obtenez la question épineuse que la plupart des équipes plateforme finissent par affronter : comment laisser des dizaines d’équipes internes exposer leurs services Kubernetes en privé, à travers les projets et les VPC, sans que chacune ait à maîtriser la tuyauterie sous-jacente ? Cet article est la réponse que nous avons retenue après l’avoir construite.
À retenir
- Faites transiter tout le trafic entrant par un petit ensemble de gateways à périmètre dédié (apps, admin, API, générique), jamais par des IP publiques improvisées.
- Câblez le producteur et le consommateur PSC derrière une abstraction unique
ServiceExposurepour que les équipes demandent l’exposition de façon déclarative.- Standardisez d’entrée les conventions de DNS et de TLS par service. Le trafic PSC a quadruplé en 2025 (Google Cloud, 2026) : le schéma de nommage doit donc passer à l’échelle.
- Automatisez les certificats avec cert-manager et une zone de délégation CNAME ACME DNS-01 pour garder une empreinte IAM minimale.
Tout ce qui suit provient de la mise en œuvre de ce pattern sur la plateforme cloud interne d’un grand constructeur automobile européen. Les noms et identifiants sont volontairement génériques. C’est l’architecture qui compte.
Pourquoi l’exposition privée de services devient-elle difficile à l’échelle de l’entreprise ?
L’exposition privée devient difficile parce que le nombre de pièces mobiles se multiplie à chaque équipe. Prenez un seul service : il a besoin d’une VIP, d’une règle de transfert, d’un sous-réseau NAT PSC, d’un service attachment, d’endpoints par consommateur, d’enregistrements DNS et de TLS. Faites donc tout cela à la main sur plus de 40 services et vous héritez d’un emploi à temps plein doublé d’une dérive qui ne cesse de grossir. Ça vous parle ?
Le premier mode de défaillance que nous avons observé, c’est l’incohérence. Par exemple, une équipe terminait le TLS au niveau de la gateway pendant qu’une autre le laissait passer tel quel. L’une publiait un enregistrement A interne, l’autre laissait fuiter une IP publique dans une spec. Chaque exposition était légèrement différente, ce qui rendait les audits lents et la réponse aux incidents encore plus lente. La solution n’a donc pas été plus de documentation. C’était plutôt une abstraction déclarative unique qui masque la complexité de PSC tout en imposant les règles que les équipes ne cessaient d’enfreindre. Cette abstraction n’est qu’une ressource personnalisée d’un control plane Kubernetes as a Service plus large.
Mais la pression de conformité est tout aussi réelle, et elle est régionale. À travers l’Europe et l’Amérique du Nord, les entreprises considèrent de plus en plus la connectivité privée comme un contrôle de résidence des données et de gouvernance, et non comme un simple confort. Concrètement, régulateurs et équipes de sécurité internes veulent la preuve que le trafic interne ne traverse jamais l’internet public. PSC vous fournit cette preuve au niveau réseau, mais uniquement si vous en faites la voie balisée.
Comment Private Service Connect fonctionne-t-il concrètement ?
PSC relie un producteur de service à un consommateur de service en privé, à l’aide d’une couche NAT plutôt que d’un peering de VPC. D’abord, le producteur publie un ServiceAttachment adossé à un sous-réseau NAT. Ensuite, le consommateur crée un endpoint qui mappe une IP privée de son propre VPC vers cet attachment. Résultat : le trafic ne touche jamais une IP publique, et les deux VPC n’ont jamais besoin d’un peering compatible en plages d’adresses.
Cette séparation producteur/consommateur, c’est toute l’astuce. En pratique, les services managés s’appuient déjà dessus : Databricks, Confluent, ClickHouse et OpenShift Dedicated proposent tous une connectivité PSC vers les VPC clients (Google Cloud docs, 2026). Mais ce que presque personne ne documente, c’est comment appliquer le même modèle à vos propres services Kubernetes internes, exposés en façade par Gateway API. C’est ce chaînon manquant que cet article vient combler.
Voici l’abstraction centrale que nous avons retenue. Une seule CRD dérive la région, le VPC et les sous-réseaux à partir du cluster référencé. Elle alloue la VIP de la gateway via une adresse managée, jamais une IP brute, puis découvre la règle de transfert qui en résulte et provisionne le sous-réseau NAT PSC ainsi que le service attachment.
L’étape de découverte de la règle de transfert cache un vrai piège. En production, nous avons constaté que la règle de transfert créée par le cloud peut mettre un intervalle de réconciliation par défaut complet, soit dix minutes, à apparaître. Le contrôleur abaisse donc temporairement l’intervalle de réconciliation de l’adresse à 10 secondes pendant qu’il patiente, puis restaure la valeur par défaut de 600 secondes une fois le self-link apparu. Petit bricolage, forte baisse de la latence de réconciliation.
apiVersion: platform.example.io/v1alpha1
kind: ServiceExposure
metadata:
name: checkout-apps
spec:
gateway:
purpose: apps # apps | admin | apigee | generic
implementation: envoy-gateway
vip:
allocation: Auto # Auto lets the cloud pick; Desired reserves a fixed IP
pscProducer:
nat:
mode: Managed # Managed | BringYourOwn
rangeSelector:
matchNames: [psc-nat-pool]
managedConsumers:
- name: exchange-internal
tls:
mode: Terminate
certificate:
strategy: cert-manager
Note : Les endpoints consommateurs ne sont provisionnés automatiquement que pour les zones d’échange pré-approuvées où la plateforme détient déjà les droits IAM. Tout le reste est ignoré et remonté via une condition
ConsumersSkippedplutôt que d’échouer en silence. Ce point de friction délibéré arrête toute connectivité inter-frontières non gouvernée avant même qu’elle ne débute.
Le trafic Private Service Connect a quadruplé au cours de l’année 2025 et sert désormais de façade à plus de 40 services Google publiés (Google Cloud, “What’s new in cloud networking at Next ‘26”, 2026). De grandes plateformes de données comme Databricks, Confluent et ClickHouse se sont désormais standardisées sur PSC pour l’ingress privé vers les VPC (Google Cloud docs, 2026).
Pourquoi tout faire transiter par des gateways à périmètre dédié ?
Les gateways à périmètre dédié vous donnent un point d’application par classe de trafic plutôt qu’un par service. Nous en exploitons quatre : apps pour le trafic applicatif exposé au public, admin pour l’outillage strictement interne comme une UI Argo CD, apigee pour le trafic d’API qui doit d’abord se voir appliquer une politique, et generic pour tout ce que le modèle managé ne sait pas exprimer. Chacune reçoit sa propre VIP et peut utiliser un backend différent.
Pourquoi ne pas simplement laisser chaque équipe choisir sa propre gateway ? Parce que c’est la classe de trafic, et non l’équipe, qui doit décider de la politique. Ainsi, une UI d’admin ne partage jamais la même VIP que le trafic applicatif public, et les appels d’API passent toujours par la politique Apigee avant d’atteindre le cluster. En pratique, ce seul découpage évite toute une catégorie d’incidents du type « pourquoi cet endpoint est-il joignable depuis internet ? ».
Envoy Gateway est l’implémentation sous-jacente aux quatre. Un an après sa GA, il a enregistré une forte croissance des pulls Helm et une adoption par des entreprises du Fortune 500 (CNCF blog, 2025), soit exactement le signal de maturité que l’on veut avant de miser une plateforme dessus. Il implémente proprement Gateway API, si bien que les mêmes abstractions Gateway et route couvrent HTTP, HTTPS et TCP sans colle sur mesure.
Le nommage est d’un déterminisme ennuyeux, et c’est le but. Chaque gateway est nommée <purpose>-<hash(exposureName)>, et les ressources Gateway et EnvoyProxy sont créées dans le cluster de charge distant, pas dans le cluster de management. Faire de generic une valeur d’énumération explicite plutôt qu’un label libre était un choix de sûreté délibéré : une chaîne purpose mal orthographiée ne doit jamais basculer en silence un service vers un autre chemin de code. Petite décision, forte réduction du rayon d’impact.
Comment nommer et résoudre les services avec un DNS par service ?
Chaque service exposé reçoit un nom pleinement qualifié issu d’un gabarit fixe, pour que la résolution soit prévisible et auditable. La convention encode le périmètre, l’identité du cluster, le mode de tenance, la value stream et l’environnement. Faites-le bien une fois et le DNS devient auto-documenté : vous pouvez lire n’importe quel nom d’hôte et savoir précisément à quelle gateway, quel cluster et quel tenant il appartient.
Le gabarit ressemble à ceci :
*.<purpose>.<clusterHash>.<shared|dedicated>.<vsNN>.<env>.svc.platform.example.io.
Le segment shared ou dedicated provient du fait que le tenant est mutualisé ou non. Le code de value stream vsNN est formaté par un unique helper partagé, réutilisé à la fois par l’étape d’enregistrement DNS et par le hostname du listener de gateway, si bien que les deux ne peuvent jamais diverger. Résultat : les enregistrements A des consommateurs sont publiés automatiquement pour les consommateurs managés.
Nous avons centralisé ces zones de façon délibérée. Au lieu que chaque projet de charge possède son propre Cloud DNS interne, les enregistrements vivent désormais dans des zones communes par environnement, hébergées dans le projet de management. L’ancienne approche par VPC obligeait à répliquer partout la logique IAM et de nettoyage, et elle dérivait en permanence. Une seule zone partagée par environnement, écrite par un seul contrôleur, a donc supprimé toute une catégorie de tickets « pourquoi cet enregistrement manque-t-il ? ».
Un incident survenu ici laisse encore des traces. Nous avons ajouté un nouveau profil de consommateur managé à la table interne, mais oublié la branche correspondante dans l’étape d’enregistrement DNS. Le résultat était une référence de zone vide que Config Connector a rejetée avec « must validate one and only one schema (oneOf) ». Quatre endroits distincts, l’énumération de la CRD, l’énumération du proto, l’allowlist du validateur et la table de profils, devaient tous bouger de concert. Oubliez-en un et la réconciliation échoue d’une manière que le message d’erreur n’explique quasiment pas. Nous traitons donc désormais tout changement de profil comme une édition atomique de quatre fichiers.
NAT managé ou apporté par vos soins : lequel choisir ?
Choisissez le NAT managé, sauf si une frontière réseau vous force la main. En mode Managed, le sous-réseau NAT PSC est dérivé automatiquement du pool de sous-réseaux du cluster, de sorte que les équipes ne touchent jamais à la planification d’IP. Le mode BringYourOwn, à l’inverse, prend des sous-réseaux fournis par l’appelant. Il existe pour les cas où une équipe réseau distincte possède l’espace d’adressage et refuse toute délégation.
Alors quel mode privilégier par défaut ? Managed, presque toujours. Les deux modes sont mutuellement exclusifs, contrainte imposée par une règle de validation pour qu’une spec ne puisse jamais activer les deux. C’est plus important que ça n’en a l’air. Une mauvaise configuration de NAT n’échoue pas bruyamment ; elle échoue plutôt sous forme de resets de connexion intermittents, des semaines plus tard. Rendre les modes exclusifs au moment de l’admission transforme un cauchemar de débogage en un apply rejeté.
BringYourOwn justifie son existence dans les topologies tordues. Nous avons rencontré un cas où un environnement hors production n’avait aucune route vers un autre VPC, une frontière délibérée posée par l’équipe réseau. Ses sous-réseaux NAT ont donc dû être supprimés puis recréés sous un VPC différent, avec des noms identiques. Comme les self-links de sous-réseau encodent le projet et la région mais pas le nom du VPC, la table de profils a continué de fonctionner sans changement une fois les sous-réseaux revenus. C’est un heureux hasard du format de self-link, et il vaut la peine de le connaître avant de concevoir autour.
Piège : Retirer un consommateur d’une spec ne nettoyait pas, à l’origine, sa règle de transfert, son enregistrement DNS et son adresse. Le pipeline de création ne touchait qu’aux consommateurs présents à l’instant T, sans aucun diff par rapport à la spec précédente. Des ressources cloud orphelines s’accumulaient en silence. Le correctif a été une étape de nettoyage explicite qui compare la spec courante aux ressources découvertes et supprime tout ce qui n’est plus référencé. Si vous construisez ce pattern, ajoutez cette étape de diff dès le premier jour.
Comment les gateways génériques gèrent-elles le TCP et le passthrough TLS ?
Les gateways génériques gèrent les protocoles que le modèle HTTP managé ne sait pas couvrir : le TCP brut, et le TLS qui se termine au niveau du backend. Une gateway générique supprime toutes les étapes de DNS et de règle de transfert liées aux consommateurs managés et expose à la place des customListeners, chacun doté d’un nom, d’un port, d’un protocole et d’un TLS optionnel. Les deux sont mutuellement exclusifs : une gateway générique ne mélange jamais consommateurs managés et listeners personnalisés.
Le cas d’usage à l’origine de cette conception était concret : un broker d’événements parlant MQTTS, où le client et le broker négocient le TLS de bout en bout. Le terminer au niveau de la gateway casserait ce handshake. Les listeners personnalisés supportent donc un mode TLS Passthrough, mode seul, sans référence de certificat, aux côtés du Terminate classique. Par exemple, un listener MQTTS sur le port 8883 se contente de transférer les octets et laisse le broker gérer le certificat.
spec:
gateway:
purpose: generic
implementation: envoy-gateway
customListeners:
- name: mqtts
port: 8883
protocol: TCP # TCP listeners forbid a hostname
tls:
mode: Passthrough # broker terminates TLS itself, no certificateRefs
pscProducer: # optional for generic TCP gateways
nat:
mode: BringYourOwn
subnets: [projects/example/regions/eu-west1/subnetworks/broker-nat]
Deux notes de conception rendent tout cela sûr. D’abord, les listeners TCP interdisent un hostname là où HTTP et HTTPS en exigent un, ce qui est validé à l’admission. Ensuite, pscProducer est devenu optionnel pour les gateways génériques, puisqu’une gateway TCP peut n’être joignable que via la VIP de son propre load-balancer interne. Résultat : chaque étape de contrôleur qui touche au producteur a nécessité l’ajout de garde-fous contre le nil.
Piège : La toute première exposition en gateway générique se réconciliait à l’infini. Une étape consommateur vérifiait le statut du consommateur avant de vérifier s’il existait le moindre consommateur, si bien qu’avec zéro consommateur elle ne se stabilisait jamais. Le correctif a été le même garde-fou « retour anticipé si vide » que les autres étapes appliquaient déjà. Chaque fois que vous ajoutez un nouveau type de ressource à un réconciliateur par étapes, auditez chaque étape pour le cas d’entrée vide.
Comment automatiser le TLS avec la délégation CNAME ACME DNS-01 ?
Vous l’automatisez en donnant à cert-manager un accès en écriture à exactement une zone DNS, puis en lui déléguant toutes les autres zones via un CNAME. Le contrôleur crée le Certificate et le ClusterIssuer de cert-manager dans le namespace Envoy Gateway uniquement lorsque la stratégie TLS est fixée à cert-manager, et supprime le certificat si la stratégie change.
Le pattern de délégation, c’est l’astuce maligne. À côté de l’enregistrement DNS habituel de chaque consommateur, le contrôleur écrit aussi un CNAME dans chaque zone consommateur :
_acme-challenge.<hash>.<shared|dedicated>.<env>.<consumer-zone>.
CNAME _acme-challenge.<hash>.<shared|dedicated>.<env>.acme.platform.example.io.
Ainsi, cert-manager n’écrit jamais le véritable enregistrement TXT ACME que dans l’unique zone partagée acme.platform.example.io. Le CNAME fait alors en sorte que cet enregistrement se résolve correctement depuis chaque zone consommateur aussi. Le bénéfice, c’est le périmètre IAM : l’empreinte d’écriture de cert-manager reste figée à une seule zone, quel que soit le nombre de zones consommateur ajoutées. La plupart des équipes voient les permissions de l’identité de certificat croître linéairement avec les services. La délégation, au contraire, les maintient à plat.
Piège : Accorder
roles/dns.adminau niveau du projet ne vous permet pas d’écrire de l’IAM au niveau d’une zone. Ce rôle inclutdns.managedZones.getIamPolicymais passetIamPolicy, si bien que restreindre un binding à une seule zone échoue avec un 403 alors même que le compte est déjà admin DNS à l’échelle du projet. Le correctif est un petit rôle personnalisé qui porte les deux permissions. Ne vous rabattez pas sur l’admin à l’échelle du projet juste pour faire disparaître l’erreur.
Envoy Gateway a atteint un an après sa GA avec une forte croissance des pulls de charts Helm et une adoption par des entreprises du Fortune 500 (CNCF blog, 2025). C’est cette maturité de production qui vous permet d’ancrer l’automatisation des certificats et le routage multi-protocole sur une seule implémentation de Gateway API.
Quelles défaillances silencieuses surveiller ?
Les défaillances dangereuses ici sont les silencieuses, et la politique de contrôle d’admission en est une source de premier ordre. Un webhook qui ne se déclenche jamais, un rôle auquel il manque une permission, une ressource orpheline que personne ne supprime : aucune ne lève d’erreur bruyante, si bien qu’elles coûtent des heures avant qu’on ne les soupçonne. Voici les trois qui nous ont le plus fait mal.
Vous est-il déjà arrivé de traquer un bug pendant des heures avant de découvrir que le code ne s’était jamais exécuté ? Nous avons tenté de faire muter par une ClusterPolicy Kyverno un secret TLS émis par cert-manager pour y ajouter une chaîne intermédiaire. L’opération a échoué sans PolicyReport, sans erreur et sans mutation. L’écriture a simplement réussi, inchangée.
La cause racine était un namespaceSelector global sur le webhook mutant de Kyverno, défini une seule fois dans un ConfigMap plutôt que par politique. Ainsi, si le namespace cible n’a pas le label requis, l’API server n’invoque jamais Kyverno. Zéro trace d’admission. Nous y avons perdu de vraies heures, parce que tout l’instinct pousse à « vérifier la politique » quand le vrai problème est que la politique n’a jamais été consultée.
Mais il y a une leçon plus profonde aussi. Muter un secret cert-manager après émission est fragile même quand ça marche, parce que cert-manager réécrit tls.crt au renouvellement et supprime en silence la chaîne que vous aviez ajoutée. Le correctif durable, au contraire, c’est d’obtenir la chaîne complète à l’émission, via le preferredChain d’ACME ou un bundle de CA au niveau de l’issuer, plutôt que de patcher le secret après coup. Corrigez la source, pas le symptôme.
Pour conclure
L’exposition privée de services à l’échelle de l’entreprise n’est pas un seul problème difficile. C’est une douzaine de petits problèmes, chacun facile à rater subtilement. Le pattern qui a fonctionné pour nous ramène cette douzaine à une seule demande déclarative : une équipe dit « exposez mon service », et la plateforme prend en charge le NAT PSC, les service attachments, le câblage de gateway, le DNS et le TLS derrière des gateways à périmètre dédié.
Avec un trafic PSC multiplié par 4 en un an (Google Cloud, 2026) et Kubernetes à 82 % d’adoption en production (CNCF, 2026), cela cesse d’être optionnel. Commencez par l’abstraction, pas par la tuyauterie. Faites du chemin sûr le seul chemin : des gateways à périmètre dédié, un DNS déterministe, une seule zone ACME déléguée, et des conditions explicites pour tout ce que la plateforme refuse de faire. Les pièges de cet article sont ceux qui nous ont coûté du temps. Volez-nous les correctifs.
Réponses directes
Questions fréquentes
Ai-je besoin de PSC si mes services tournent déjà dans un seul VPC ?
Pas pour le trafic intra-VPC. PSC gagne sa place quand des services doivent être consommés à travers des projets ou des VPC sans peering, ce qui est la norme à l'échelle de l'entreprise. Il offre aussi un récit d'audit propre : le trafic utilise des IP privées de bout en bout, ce que les régulateurs attendent de plus en plus.
Pourquoi Envoy Gateway plutôt que la Gateway native de GKE ?
Envoy Gateway implémente Gateway API de façon portable et supporte les listeners HTTP, HTTPS et TCP sous une même abstraction, ce qui compte pour les gateways génériques. Un an après sa GA, il a connu une forte adoption, y compris par le Fortune 500. Le contrôleur natif fonctionne bien aussi ; le facteur décisif est la portabilité entre types de clusters et protocoles.
En quoi la délégation CNAME ACME réduit-elle le risque ?
Elle plafonne l'accès en écriture DNS de l'identité de certificat à une seule zone. Chaque zone consommateur délègue son enregistrement de challenge ACME via un CNAME vers cette unique zone partagée, où cert-manager écrit l'enregistrement TXT. Ajoutez cent zones consommateur et l'empreinte IAM reste à plat. C'est la différence entre moindre privilège et prolifération des permissions.
Quel est le plus grand piège opérationnel de ce pattern ?
Les défaillances silencieuses. Un label de namespace manquant fait sauter un webhook Kyverno sans trace, un mauvais rôle DNS fait échouer l'IAM de zone avec un 403 trompeur, et des ressources consommateur orphelines subsistent après une modification de spec. Aucune ne lève d'erreur bruyante. Construisez un nettoyage par diff et des conditions skipped explicites.