Kubernetes Networking : Guide Complet CNI, Services et Ingress
Le networking est l'un des aspects les plus complexes de Kubernetes. Comprendre comment les Pods communiquent entre eux, avec les Services, et avec le monde extérieur est essentiel pour déployer et opérer des applications en production.
Architecture Réseau Kubernetes
Le Modèle Réseau K8s
Kubernetes impose un modèle réseau "flat" avec ces règles fondamentales :
┌─────────────────────────────────────────────────────────────────────────────┐
│ MODÈLE RÉSEAU KUBERNETES │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ RÈGLE 1: Chaque Pod a sa propre adresse IP unique │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ RÈGLE 2: Tous les Pods peuvent communiquer entre eux sans NAT │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ RÈGLE 3: Les agents sur un Node peuvent communiquer avec tous Pods │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ RÈGLE 4: L'IP vue par le Pod est la même que celle vue par les │ │
│ │ autres Pods (pas de NAT interne) │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Les 4 Problèmes Réseau Résolus par K8s
┌─────────────────────────────────────────────────────────────────────────────┐
│ COMMUNICATIONS KUBERNETES │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. CONTAINER ↔ CONTAINER (même Pod) │
│ └── Via localhost (partage network namespace) │
│ │
│ 2. POD ↔ POD (même Node) │
│ └── Via bridge virtuel (veth pairs) │
│ │
│ 3. POD ↔ POD (Nodes différents) │
│ └── Via overlay network ou routage direct (CNI) │
│ │
│ 4. POD ↔ EXTERNE │
│ └── Via Services (ClusterIP, NodePort, LoadBalancer, Ingress) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Container Network Interface (CNI)
Qu'est-ce que CNI ?
CNI est la spécification standard pour configurer le réseau des conteneurs. Kubernetes délègue la configuration réseau aux plugins CNI.
┌─────────────────────────────────────────────────────────────────────────────┐
│ ARCHITECTURE CNI │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ │
│ │ kubelet │ │
│ └──────┬──────┘ │
│ │ 1. Crée Pod │
│ ▼ │
│ ┌─────────────┐ 2. Appelle CNI ┌─────────────────────┐ │
│ │ Container │ ───────────────────────────▶│ Plugin CNI │ │
│ │ Runtime │ │ (Calico/Cilium) │ │
│ └─────────────┘ └──────────┬──────────┘ │
│ │ │
│ 3. Configure│ │
│ réseau │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ - Crée veth pair │ │
│ │ - Assigne IP │ │
│ │ - Configure routes │ │
│ │ - Applique rules │ │
│ └─────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Comparaison des Plugins CNI
| Plugin | Mode | Performance | Features | Complexité |
|---|---|---|---|---|
| Calico | BGP/VXLAN/IPIP | Excellente | Network Policies, eBPF | Moyenne |
| Cilium | eBPF natif | Excellente | L7 Policies, Observabilité | Haute |
| Flannel | VXLAN/host-gw | Bonne | Basique | Faible |
| Weave | Mesh overlay | Moyenne | Encryption native | Faible |
| Canal | Flannel + Calico | Bonne | Network Policies | Moyenne |
Installation et Configuration de Calico
# calico-installation.yaml
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
name: default
spec:
# Configuration du pool d'IPs
calicoNetwork:
ipPools:
- blockSize: 26
cidr: 10.244.0.0/16
encapsulation: VXLANCrossSubnet
natOutgoing: Enabled
nodeSelector: all()
# Mode de données
linuxDataplane: Iptables # ou BPF pour eBPF
# MTU automatique
mtu: 0
# BGP configuration
bgp: Enabled
# Ressources des composants
typhaDeployment:
spec:
template:
spec:
containers:
- name: calico-typha
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 1000m
memory: 512Mi
---
# Configuration BGP pour bare-metal
apiVersion: crd.projectcalico.org/v1
kind: BGPConfiguration
metadata:
name: default
spec:
logSeverityScreen: Info
nodeToNodeMeshEnabled: true
asNumber: 64512
---
# BGP Peer pour router externe
apiVersion: crd.projectcalico.org/v1
kind: BGPPeer
metadata:
name: rack-tor-switch
spec:
peerIP: 192.168.1.1
asNumber: 64513
nodeSelector: rack == 'rack-1'
Installation et Configuration de Cilium
# Installation via Helm
helm repo add cilium https://helm.cilium.io/
helm repo update
helm install cilium cilium/cilium --version 1.15.0 \
--namespace kube-system \
--set kubeProxyReplacement=true \
--set k8sServiceHost=<API_SERVER_IP> \
--set k8sServicePort=6443 \
--set hubble.enabled=true \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true \
--set ipam.mode=kubernetes \
--set bpf.masquerade=true \
--set loadBalancer.mode=dsr \
--set bandwidthManager.enabled=true
# cilium-config.yaml - Configuration avancée
apiVersion: cilium.io/v2alpha1
kind: CiliumLoadBalancerIPPool
metadata:
name: production-pool
spec:
cidrs:
- cidr: 192.168.100.0/24
---
apiVersion: cilium.io/v2alpha1
kind: CiliumBGPPeeringPolicy
metadata:
name: bgp-peering
spec:
nodeSelector:
matchLabels:
bgp-policy: production
virtualRouters:
- localASN: 64512
exportPodCIDR: true
neighbors:
- peerAddress: 192.168.1.1/32
peerASN: 64513
connectRetryTimeSeconds: 30
holdTimeSeconds: 90
keepAliveTimeSeconds: 30
gracefulRestart:
enabled: true
restartTimeSeconds: 120
Services Kubernetes
Types de Services
┌─────────────────────────────────────────────────────────────────────────────┐
│ TYPES DE SERVICES │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ CLUSTERIP (défaut) │ │
│ │ ┌─────────┐ │ │
│ │ │ Pod A │───────▶ 10.96.0.100:80 ───────▶ ┌─────────┐ │ │
│ │ └─────────┘ (Virtual IP) │ Pod B │ │ │
│ │ │ Pod C │ │ │
│ │ Accessible uniquement dans le cluster └─────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ NODEPORT │ │
│ │ ┌─────────┐ │ │
│ │ │ Client │───▶ NodeIP:30080 ───▶ ClusterIP ───▶ ┌─────────┐ │ │
│ │ │ Externe │ (30000-32767) │ Pods │ │ │
│ │ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ LOADBALANCER │ │
│ │ ┌─────────┐ ┌──────────┐ │ │
│ │ │ Client │───▶ │ Cloud LB │───▶ NodePort ───▶ ┌─────────┐ │ │
│ │ │ Externe │ │ (L4) │ │ Pods │ │ │
│ │ └─────────┘ └──────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ EXTERNALNAME │ │
│ │ ┌─────────┐ │ │
│ │ │ Pod │───▶ my-db.default.svc ───▶ db.external.com │ │
│ │ └─────────┘ (DNS CNAME) │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Service ClusterIP avec Session Affinity
apiVersion: v1
kind: Service
metadata:
name: api-service
namespace: production
labels:
app: api
tier: backend
spec:
type: ClusterIP
selector:
app: api
tier: backend
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
- name: grpc
port: 9090
targetPort: 9090
protocol: TCP
# Session affinity pour applications stateful
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 3600
# Traffic policy
internalTrafficPolicy: Local # Préfère les pods locaux
Service LoadBalancer avec MetalLB
# Installation MetalLB
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: production-pool
namespace: metallb-system
spec:
addresses:
- 192.168.1.100-192.168.1.150
autoAssign: true
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: production-l2
namespace: metallb-system
spec:
ipAddressPools:
- production-pool
interfaces:
- eth0
---
# Service LoadBalancer
apiVersion: v1
kind: Service
metadata:
name: frontend
namespace: production
annotations:
metallb.universe.tf/address-pool: production-pool
metallb.universe.tf/loadBalancerIPs: 192.168.1.100
spec:
type: LoadBalancer
loadBalancerIP: 192.168.1.100
externalTrafficPolicy: Local # Préserve l'IP client
ports:
- name: http
port: 80
targetPort: 8080
- name: https
port: 443
targetPort: 8443
selector:
app: frontend
Headless Service pour StatefulSets
apiVersion: v1
kind: Service
metadata:
name: kafka-headless
namespace: kafka
spec:
type: ClusterIP
clusterIP: None # Headless!
publishNotReadyAddresses: true
selector:
app: kafka
ports:
- name: broker
port: 9092
targetPort: 9092
- name: controller
port: 9093
targetPort: 9093
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: kafka
namespace: kafka
spec:
serviceName: kafka-headless # Lié au headless service
replicas: 3
selector:
matchLabels:
app: kafka
template:
metadata:
labels:
app: kafka
spec:
containers:
- name: kafka
image: confluentinc/cp-kafka:7.5.0
env:
- name: KAFKA_ADVERTISED_LISTENERS
value: "PLAINTEXT://$(POD_NAME).kafka-headless.kafka.svc.cluster.local:9092"
# DNS: kafka-0.kafka-headless.kafka.svc.cluster.local
# DNS: kafka-1.kafka-headless.kafka.svc.cluster.local
# DNS: kafka-2.kafka-headless.kafka.svc.cluster.local
Ingress Controllers
Architecture Ingress
┌─────────────────────────────────────────────────────────────────────────────┐
│ ARCHITECTURE INGRESS │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Internet │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Load Balancer (L4) │ │
│ │ 192.168.1.100:80/443 │ │
│ └───────────────────────────────┬─────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ INGRESS CONTROLLER │ │
│ │ (NGINX, Traefik, HAProxy, etc.) │ │
│ │ │ │
│ │ ┌───────────────────────────────────────────────────────────────┐ │ │
│ │ │ Ingress Rules │ │ │
│ │ │ │ │ │
│ │ │ api.example.com/* ────────────▶ api-service:80 │ │ │
│ │ │ app.example.com/* ────────────▶ frontend-service:80 │ │ │
│ │ │ example.com/api/* ────────────▶ api-service:80 │ │ │
│ │ │ example.com/* ────────────▶ frontend-service:80 │ │ │
│ │ └───────────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Installation NGINX Ingress Controller
# Via Helm
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--create-namespace \
--set controller.replicaCount=3 \
--set controller.metrics.enabled=true \
--set controller.podAnnotations."prometheus\.io/scrape"=true \
--set controller.podAnnotations."prometheus\.io/port"=10254
# nginx-ingress-values.yaml - Configuration production
controller:
replicaCount: 3
# Ressources
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 2000m
memory: 2Gi
# Autoscaling
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 70
targetMemoryUtilizationPercentage: 80
# Pod Disruption Budget
podDisruptionBudget:
enabled: true
minAvailable: 2
# Anti-affinity pour HA
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
topologyKey: kubernetes.io/hostname
# Configuration NGINX
config:
use-gzip: "true"
gzip-level: "6"
worker-processes: "auto"
max-worker-connections: "65535"
use-http2: "true"
keep-alive: "75"
keep-alive-requests: "10000"
upstream-keepalive-connections: "200"
proxy-body-size: "50m"
proxy-buffer-size: "16k"
proxy-buffers: "4 16k"
ssl-protocols: "TLSv1.2 TLSv1.3"
ssl-ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256"
enable-real-ip: "true"
forwarded-for-header: "X-Forwarded-For"
# Métriques
metrics:
enabled: true
serviceMonitor:
enabled: true
namespace: monitoring
Configuration Ingress Avancée
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: production-ingress
namespace: production
annotations:
# TLS
cert-manager.io/cluster-issuer: letsencrypt-prod
# Rate limiting
nginx.ingress.kubernetes.io/limit-rps: "100"
nginx.ingress.kubernetes.io/limit-connections: "50"
nginx.ingress.kubernetes.io/limit-rpm: "1000"
# Timeouts
nginx.ingress.kubernetes.io/proxy-connect-timeout: "10"
nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
# CORS
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: "https://app.example.com"
nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, PUT, DELETE, OPTIONS"
nginx.ingress.kubernetes.io/cors-allow-headers: "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization"
# Sécurité
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "X-Frame-Options: DENY";
more_set_headers "X-Content-Type-Options: nosniff";
more_set_headers "X-XSS-Protection: 1; mode=block";
more_set_headers "Strict-Transport-Security: max-age=31536000; includeSubDomains";
# Canary deployment
# nginx.ingress.kubernetes.io/canary: "true"
# nginx.ingress.kubernetes.io/canary-weight: "20"
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
- app.example.com
secretName: example-tls
rules:
- host: api.example.com
http:
paths:
- path: /v1
pathType: Prefix
backend:
service:
name: api-v1
port:
number: 80
- path: /v2
pathType: Prefix
backend:
service:
name: api-v2
port:
number: 80
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend
port:
number: 80
Traefik Ingress Controller
# traefik-values.yaml
deployment:
replicas: 3
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 1000m
memory: 512Mi
# Entrypoints
ports:
web:
port: 8000
exposedPort: 80
redirectTo: websecure
websecure:
port: 8443
exposedPort: 443
tls:
enabled: true
# Dashboard
ingressRoute:
dashboard:
enabled: true
matchRule: Host(`traefik.example.com`)
entryPoints:
- websecure
# Logs
logs:
general:
level: INFO
access:
enabled: true
format: json
# Metrics
metrics:
prometheus:
enabled: true
entryPoint: metrics
---
# IngressRoute Traefik CRD
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: api-route
namespace: production
spec:
entryPoints:
- websecure
routes:
- match: Host(`api.example.com`) && PathPrefix(`/v1`)
kind: Rule
services:
- name: api-v1
port: 80
weight: 100
middlewares:
- name: rate-limit
- name: headers-security
tls:
certResolver: letsencrypt
---
# Middleware Rate Limiting
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: rate-limit
namespace: production
spec:
rateLimit:
average: 100
burst: 200
period: 1s
sourceCriterion:
ipStrategy:
depth: 1
---
# Middleware Headers Sécurité
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: headers-security
namespace: production
spec:
headers:
frameDeny: true
contentTypeNosniff: true
browserXssFilter: true
stsIncludeSubdomains: true
stsSeconds: 31536000
customResponseHeaders:
X-Robots-Tag: "noindex,nofollow"
Network Policies
Comprendre les Network Policies
┌─────────────────────────────────────────────────────────────────────────────┐
│ NETWORK POLICIES │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Sans Network Policy: TOUT est autorisé par défaut │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Pod A │◀───▶│ Pod B │◀───▶│ Pod C │◀───▶│ Pod D │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ Avec Network Policy: Whitelist des flux autorisés │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Pod A │────▶│ Pod B │────▶│ Pod C │ │ Pod D │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ │ ▲ │
│ └───────────────────────────────┘ │
│ │
│ Policy Types: │
│ • Ingress: Contrôle le trafic ENTRANT vers les pods sélectionnés │
│ • Egress: Contrôle le trafic SORTANT des pods sélectionnés │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Default Deny All
# Deny all ingress traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: production
spec:
podSelector: {} # Sélectionne tous les pods
policyTypes:
- Ingress
---
# Deny all egress traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
---
# Allow DNS egress (nécessaire!)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
Network Policies Complètes pour Microservices
# Frontend peut recevoir du trafic Ingress et appeler l'API
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-policy
namespace: production
spec:
podSelector:
matchLabels:
app: frontend
policyTypes:
- Ingress
- Egress
ingress:
# Depuis l'Ingress Controller
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
ports:
- protocol: TCP
port: 8080
egress:
# Vers l'API
- to:
- podSelector:
matchLabels:
app: api
ports:
- protocol: TCP
port: 8080
# DNS
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
---
# API peut recevoir du frontend et appeler la DB
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-policy
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
- Egress
ingress:
# Depuis le frontend
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
# Depuis l'Ingress (API publique)
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
ports:
- protocol: TCP
port: 8080
egress:
# Vers PostgreSQL
- to:
- podSelector:
matchLabels:
app: postgresql
ports:
- protocol: TCP
port: 5432
# Vers Redis
- to:
- podSelector:
matchLabels:
app: redis
ports:
- protocol: TCP
port: 6379
# Vers Kafka
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kafka
- podSelector:
matchLabels:
app: kafka
ports:
- protocol: TCP
port: 9092
# DNS
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
---
# PostgreSQL isolé
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: postgresql-policy
namespace: production
spec:
podSelector:
matchLabels:
app: postgresql
policyTypes:
- Ingress
- Egress
ingress:
# Uniquement depuis l'API
- from:
- podSelector:
matchLabels:
app: api
ports:
- protocol: TCP
port: 5432
egress:
# Réplication vers autre PostgreSQL (si HA)
- to:
- podSelector:
matchLabels:
app: postgresql
ports:
- protocol: TCP
port: 5432
Cilium Network Policies (L7)
# Policy L7 avec Cilium
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: api-l7-policy
namespace: production
spec:
endpointSelector:
matchLabels:
app: api
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
- method: GET
path: "/api/v1/.*"
- method: POST
path: "/api/v1/users"
headers:
- "Content-Type: application/json"
- method: DELETE
path: "/api/v1/users/[0-9]+"
---
# Policy DNS avec Cilium
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: egress-dns-policy
namespace: production
spec:
endpointSelector:
matchLabels:
app: api
egress:
- toFQDNs:
- matchName: "api.external-service.com"
- matchPattern: "*.googleapis.com"
toPorts:
- ports:
- port: "443"
protocol: TCP
DNS dans Kubernetes
Architecture CoreDNS
┌─────────────────────────────────────────────────────────────────────────────┐
│ ARCHITECTURE COREDNS │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────────────────────────────────┐ │
│ │ Pod │ │ CoreDNS │ │
│ │ │ │ │ │
│ │ /etc/resolv.conf │ ┌─────────────────────────────────┐ │ │
│ │ nameserver: │───────▶│ │ Corefile │ │ │
│ │ 10.96.0.10 │ │ │ │ │ │
│ └─────────────┘ │ │ cluster.local { │ │ │
│ │ │ kubernetes cluster.local │ │ │
│ │ │ } │ │ │
│ │ │ . { │ │ │
│ │ │ forward . /etc/resolv.conf │ │ │
│ Résolution: │ │ } │ │ │
│ │ └─────────────────────────────────┘ │ │
│ my-svc │ │ │
│ └─▶ my-svc.default.svc.cluster.local │ │
│ │ │ │
│ pod-ip.ns.pod.cluster.local │ │
│ └─▶ 10-244-1-5.default.pod.cluster.local │ │
│ │ │ │
│ SRV records pour ports nommés: │ │
│ _http._tcp.my-svc.default.svc.cluster.local │ │
│ └──────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Configuration CoreDNS Avancée
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
# Cluster DNS
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
# External DNS zones
example.com:53 {
forward . 10.0.0.53
}
# Cache
cache 30 {
success 9984 30
denial 9984 5
}
# Forward external queries
forward . /etc/resolv.conf {
max_concurrent 1000
policy sequential
}
# Prometheus metrics
prometheus :9153
# Logging
log . {
class denial error
}
loop
reload
loadbalance
}
DNS Debugging
# Pod de debug DNS
kubectl run dns-debug --image=busybox:1.36 --restart=Never -- sleep 3600
# Tester la résolution
kubectl exec dns-debug -- nslookup kubernetes.default
kubectl exec dns-debug -- nslookup my-service.my-namespace.svc.cluster.local
# Vérifier les records SRV
kubectl exec dns-debug -- nslookup -type=SRV _http._tcp.my-service.my-namespace.svc.cluster.local
# Logs CoreDNS
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=100 -f
# Métriques CoreDNS
kubectl port-forward -n kube-system svc/kube-dns 9153:9153
curl localhost:9153/metrics | grep coredns_dns_requests_total
Troubleshooting Réseau
Script de Diagnostic Complet
#!/bin/bash
# k8s-network-diag.sh - Diagnostic réseau Kubernetes
set -e
NAMESPACE=${1:-default}
POD_NAME=${2:-}
echo "=== Diagnostic Réseau Kubernetes ==="
echo "Namespace: $NAMESPACE"
echo ""
# 1. État des nodes
echo "=== 1. État des Nodes ==="
kubectl get nodes -o wide
echo ""
# 2. État des pods
echo "=== 2. État des Pods ==="
kubectl get pods -n $NAMESPACE -o wide
echo ""
# 3. Services
echo "=== 3. Services ==="
kubectl get svc -n $NAMESPACE -o wide
echo ""
# 4. Endpoints
echo "=== 4. Endpoints ==="
kubectl get endpoints -n $NAMESPACE
echo ""
# 5. Network Policies
echo "=== 5. Network Policies ==="
kubectl get networkpolicies -n $NAMESPACE
echo ""
# 6. État CNI
echo "=== 6. État du CNI ==="
# Calico
if kubectl get pods -n calico-system &>/dev/null; then
echo "Calico détecté:"
kubectl get pods -n calico-system
kubectl get ippool -o wide 2>/dev/null || true
fi
# Cilium
if kubectl get pods -n kube-system -l k8s-app=cilium &>/dev/null; then
echo "Cilium détecté:"
kubectl get pods -n kube-system -l k8s-app=cilium
kubectl exec -n kube-system -l k8s-app=cilium -- cilium status 2>/dev/null || true
fi
echo ""
# 7. CoreDNS
echo "=== 7. État CoreDNS ==="
kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=20 2>/dev/null || true
echo ""
# 8. Ingress
echo "=== 8. Ingress Controllers ==="
kubectl get pods -n ingress-nginx 2>/dev/null || echo "NGINX Ingress non trouvé"
kubectl get pods -n traefik 2>/dev/null || echo "Traefik non trouvé"
echo ""
kubectl get ingress -n $NAMESPACE
echo ""
# 9. Test de connectivité si pod spécifié
if [ -n "$POD_NAME" ]; then
echo "=== 9. Tests de connectivité depuis $POD_NAME ==="
# DNS
echo "Test DNS:"
kubectl exec -n $NAMESPACE $POD_NAME -- nslookup kubernetes.default 2>/dev/null || echo "DNS test failed"
# Kubernetes API
echo ""
echo "Test API Server:"
kubectl exec -n $NAMESPACE $POD_NAME -- wget -qO- --timeout=5 https://kubernetes.default/healthz 2>/dev/null || echo "API test failed"
# Service local
echo ""
echo "Services accessibles:"
for svc in $(kubectl get svc -n $NAMESPACE -o jsonpath='{.items[*].metadata.name}'); do
echo -n " $svc: "
kubectl exec -n $NAMESPACE $POD_NAME -- nc -zv $svc 80 2>&1 | grep -o "succeeded\|failed" || echo "unknown"
done
fi
echo ""
echo "=== Diagnostic terminé ==="
Debugging avec Netshoot
# Pod de debug réseau avancé
apiVersion: v1
kind: Pod
metadata:
name: netshoot
namespace: production
spec:
containers:
- name: netshoot
image: nicolaka/netshoot:latest
command: ["sleep", "infinity"]
securityContext:
capabilities:
add:
- NET_ADMIN
- NET_RAW
hostNetwork: false # true pour debug host network
# Lancer netshoot
kubectl run netshoot --image=nicolaka/netshoot -it --rm -- /bin/bash
# Tests courants
# TCP connect
nc -zv my-service 80
# DNS lookup détaillé
dig +trace my-service.default.svc.cluster.local
# Trace route
mtr --tcp -P 80 my-service
# Capture packets
tcpdump -i eth0 -n port 80
# HTTP debug
curl -v http://my-service/
# Test TLS
openssl s_client -connect my-service:443 -servername my-service
# Vérifier les routes
ip route
ip addr
# Tester avec différentes IP sources
curl --interface eth0 http://my-service/
# iperf pour performance
iperf3 -c my-service -p 5201
Problèmes Courants et Solutions
┌─────────────────────────────────────────────────────────────────────────────┐
│ PROBLÈMES RÉSEAU COURANTS │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ PROBLÈME: Pod ne peut pas résoudre DNS │
│ ───────────────────────────────────────── │
│ Symptôme: nslookup kubernetes échoue │
│ Causes: │
│ • CoreDNS pods down │
│ • Network Policy bloque DNS (UDP 53) │
│ • /etc/resolv.conf mal configuré │
│ Debug: │
│ kubectl get pods -n kube-system -l k8s-app=kube-dns │
│ cat /etc/resolv.conf dans le pod │
│ │
│ PROBLÈME: Service non accessible (Connection refused) │
│ ───────────────────────────────────────────────────── │
│ Symptôme: curl http://my-svc échoue │
│ Causes: │
│ • Aucun endpoint (pods non ready) │
│ • Selector ne match pas │
│ • Port incorrect │
│ Debug: │
│ kubectl get endpoints my-svc │
│ kubectl describe svc my-svc │
│ │
│ PROBLÈME: Pods ne communiquent pas entre nodes │
│ ──────────────────────────────────────────────── │
│ Symptôme: ping pod-ip-other-node timeout │
│ Causes: │
│ • CNI mal configuré │
│ • Firewall bloque overlay (VXLAN 4789, IPIP) │
│ • Routes inter-nodes manquantes │
│ Debug: │
│ kubectl get pods -n calico-system │
│ iptables -L -n sur les nodes │
│ │
│ PROBLÈME: Ingress retourne 502/503 │
│ ───────────────────────────────────── │
│ Symptôme: Bad Gateway depuis l'extérieur │
│ Causes: │
│ • Backend pods non healthy │
│ • Service mal configuré │
│ • Network Policy bloque Ingress→Pods │
│ Debug: │
│ kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx │
│ kubectl describe ingress my-ingress │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Performance Réseau
Optimisation CNI
# Calico avec eBPF pour meilleures performances
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
name: default
spec:
calicoNetwork:
linuxDataplane: BPF # eBPF au lieu d'iptables
hostPorts: Disabled # Désactive hostPorts pour perf
ipPools:
- cidr: 10.244.0.0/16
encapsulation: None # Pas d'overlay si possible
natOutgoing: Enabled
---
# Cilium avec DSR et bandwidth manager
# helm values
kubeProxyReplacement: true
loadBalancer:
mode: dsr # Direct Server Return
bandwidthManager:
enabled: true
bbr: true # BBR congestion control
bpf:
masquerade: true
hostRouting: true
Tests de Performance
# iperf3 entre pods
kubectl run iperf-server --image=networkstatic/iperf3 -- -s
kubectl run iperf-client --image=networkstatic/iperf3 --rm -it -- -c iperf-server -t 30
# Latence
kubectl exec netshoot -- hping3 -S -p 80 -c 100 my-service
# HTTP performance
kubectl exec netshoot -- ab -n 10000 -c 100 http://my-service/
# wrk pour load testing
kubectl exec netshoot -- wrk -t12 -c400 -d30s http://my-service/
Conclusion
Le networking Kubernetes est complexe mais essentiel. Points clés à retenir :
- Choisissez le bon CNI : Calico pour la polyvalence, Cilium pour les features avancées
- Utilisez les Network Policies : Sécurisez vos workloads dès le départ
- Configurez bien vos Services : Comprenez les différences entre ClusterIP, NodePort, LoadBalancer
- Ingress Controller en HA : 3 replicas minimum avec anti-affinity
- Monitorez CoreDNS : Le DNS est critique, surveillez-le
- Testez régulièrement : Scripts de diagnostic et tests de connectivité
Le networking est souvent la source de problèmes subtils. Investissez du temps pour bien le comprendre et le documenter dans votre contexte.