Aller au contenu principal

Jour 2 - Après Midi


Les plugins réseaux de Kubernetes

La spécification CNI (Container Network Interface) vise à standardiser la configuration des réseaux de conteneurs.

En définissant ces composants principaux, la spécification CNI garantit que différents environnements d'exécution de conteneurs et plugins réseau peuvent interagir de manière cohérente, permettant l'automatisation et la standardisation de la configuration réseau.

Pour la convention complète c'est ici : https://www.cni.dev/docs/spec/

Il y a plusieurs solutions d'orchestration qui s'appuient sur CNI, pas seulement Kubernetes.


Utilisation des services pour l’automatisation du réseau

Les services sont les objets réseau de base.

Dans Kubernetes, un service est un objet qui :

  • Désigne un ensemble de pods (grâce à des labels) généralement géré par un déploiement.
  • Fournit un endpoint réseau pour les requêtes à destination de ces pods.
  • Configure une politique permettant d’y accéder depuis l'intérieur ou l'extérieur du cluster.
  • Configure un nom de domaine pointant sur le groupe de pods en backend.

L’ensemble des pods ciblés par un service est déterminé par un selector.

Par exemple, considérons un backend de traitement d’image (stateless, c'est-à-dire ici sans base de données) qui s’exécute avec 3 replicas.

Ces replicas sont interchangeables et les frontends ne se soucient pas du backend qu’ils utilisent.

Bien que les pods réels qui composent l’ensemble backend puissent changer, les clients frontends ne devraient pas avoir besoin de le savoir, pas plus qu’ils ne doivent suivre eux-mêmes l'état de l’ensemble des backends.

L’abstraction du service permet ce découplage : les clients frontend s'addressent à une seule IP avec un seul port dès qu'ils ont besoin d'avoir recours à un backend.

Les backends vont recevoir la requête du frontend aléatoirement.


Les Services sont de trois types principaux :

  • ClusterIP: expose le service sur une IP interne au cluster.

  • NodePort: expose le service depuis l'IP de chacun des noeuds du cluster en ouvrant un port directement sur le nœud, entre 30000 et 32767.
    Cela permet d'accéder aux pods internes répliqués.

    Comme l'IP est stable on peut faire pointer un DNS ou Loadbalancer classique dessus.

  • LoadBalancer: expose le service en externe à l’aide d'un Loadbalancer de fournisseur de cloud.
    Les services NodePort et ClusterIP, vers lesquels le Loadbalancer est dirigé sont automatiquement créés.

Deux autres types plus avancés:


Lorsqu'on souhaite exposer un service Kubernetes, plusieurs méthodes sont possibles, dont l'utilisation de kubectl pour exposer un Pod ou un ensemble de Pods.

La commande kubectl expose permet de créer rapidement un Service pour un Pod, un Deployment, ou un autre type de ressource, sans avoir à écrire un fichier manifeste YAML.

Par exemple, pour exposer un déploiement avec un type de service ClusterIP (le type par défaut qui expose uniquement le service au sein du cluster), la commande est :

kubectl expose deployment my-app --type=ClusterIP --port=80 --target-port=8080

Voici un exemple de fichier YAML pour exposer un Pod via un Service de type ClusterIP (le service par défaut, qui n'est accessible qu'au sein du cluster).

apiVersion: v1
kind: Pod
metadata:
name: my-app
labels:
app: my-app
spec:
containers:
- name: my-app-container
image: nginx
ports:
- containerPort: 80

---

apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
selector:
app: my-app # Relie le service au Pod via ce label
ports:
- protocol: TCP
port: 80 # Port sur lequel le service écoute
targetPort: 80 # Port sur lequel le conteneur écoute
type: ClusterIP # Type de service (par défaut)

DNS Kubernetes

Le DNS interne de Kubernetes permet aux applications de communiquer facilement entre elles sans nécessiter de configuration manuelle des adresses IP.

Kubernetes fournit un service DNS interne qui résout automatiquement les noms des ressources du cluster en adresses IP, facilitant ainsi la communication entre les différents composants.


Contacter les services en DNS interne

Dans Kubernetes, les pods peuvent se contacter directement en utilisant leurs noms DNS internes s’ils se trouvent dans le même namespace.

Un pod a comme nom de host son metadata.name :

root@my-app-678c59c7d5-4t259:/# getent ahostsv4 my-app-678c59c7d5-4t259
10.42.0.123 STREAM my-app-678c59c7d5-4t259
10.42.0.123 DGRAM
10.42.0.123 RAW

Cependant, les pods n'ont pas de DNS direct par défaut. Pour les contacter, un service doit être utilisé, par exemple :

apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: my-namespace
spec:
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 8080

Une fois ce service défini, les pods peuvent y accéder avec :

my-service.my-namespace.svc.cluster.local

Contacter les Services depuis d’autres namespaces

Pour contacter un service d’un autre namespace, il faut préciser son namespace dans la requête DNS.

Par défaut, un service Kubernetes peut être atteint depuis un autre namespace en utilisant la convention DNS suivante :

<SERVICE_NAME>.<NAMESPACE>.svc.cluster.local

Exemple : si un service nommé backend est situé dans le namespace production, il pourra être contacté avec :

backend.production.svc.cluster.local

Si des restrictions réseau sont appliquées via Network Policies, ces connexions peuvent être limitées.


Requêtes DNS vers l’extérieur

Kubernetes permet aux pods d’effectuer des requêtes DNS vers l’extérieur en utilisant un résolveur DNS intégré.

Les pods utilisent par défaut le service CoreDNS de Kubernetes pour résoudre les noms externes. Toute requête qui ne correspond pas à un nom interne Kubernetes est transmise aux serveurs DNS externes définis dans /etc/resolv.conf du pod.

Exemple de configuration DNS dans un pod :

search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5

Ici :

  • search définit les suffixes DNS à essayer.
  • nameserver pointe vers CoreDNS (10.96.0.10 par défaut).
  • ndots:5 force la résolution des noms courts en les testant avec les suffixes DNS avant d’être envoyés à un DNS externe.

Si un pod fait une requête vers google.com, CoreDNS la transfère au résolveur DNS configuré sur le cluster, qui est généralement celui de l’hôte ou un service DNS externe.

Pour personnaliser cette configuration, un ConfigMap peut être utilisé pour modifier le comportement de CoreDNS :

apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
forward . /etc/resolv.conf
}

Cela permet de rediriger toutes les requêtes externes vers le résolveur DNS par défaut.


DaemonSets et leur utilité pour le DNS

Les DaemonSets permettent de déployer un pod sur chaque nœud d’un cluster Kubernetes, assurant une disponibilité et une répartition homogène des services critiques.

Un DaemonSet garantit qu’un pod spécifique est exécuté sur tous (ou certains) nœuds du cluster. Il est utile pour les services d’infrastructure comme la journalisation, la surveillance et le DNS.

Exemple de DaemonSet :

apiVersion: apps/v1
kind: DaemonSet
metadata:
name: dns-resolver
namespace: kube-system
spec:
selector:
matchLabels:
app: dns-resolver
template:
metadata:
labels:
app: dns-resolver
spec:
containers:
- name: dns-resolver
image: custom-dns-resolver:latest
ports:
- containerPort: 53

L’utilisation d’un DaemonSet pour un résolveur DNS est une bonne pratique car :

  • Chaque nœud dispose de son propre pod DNS, réduisant la latence des requêtes.
  • Il assure une résilience et une disponibilité accrues du service DNS.
  • Il évite les goulots d’étranglement en répartissant la charge entre tous les nœuds.
  • Il permet de gérer localement les requêtes DNS sans dépendre uniquement de CoreDNS centralisé.

Cela garantit ainsi une résolution DNS rapide et fiable pour tous les pods du cluster.


Le DNS de Kubernetes est un élément clé pour l’interconnexion des services, simplifiant la découverte et la gestion des dépendances entre les applications déployées. L’utilisation de DaemonSets pour un résolveur DNS distribué améliore encore la robustesse et l’efficacité du système.


Utilisation de l’exposition via Ingress et Gateway

Les objets Ingresses

Crédits Ahmet Alp Balkan

Un Ingress est un objet pour gérer dynamiquement le reverse proxy HTTP/HTTPS dans Kubernetes. Documentation: https://kubernetes.io/docs/concepts/services-networking/ingress/#what-is-ingress

Exemple de syntaxe d'un ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-wildcard-host
spec:
rules:
- host: "domain1.bar.com"
http:
paths:
- pathType: Prefix
path: "/bar"
backend:
service:
name: service1
port:
number: 80
- pathType: Prefix
path: "/foo"
backend:
service:
name: service2
port:
number: 80
- host: "domain2.foo.com"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service3
port:
number: 80


Pour pouvoir créer des objets ingress il est d'abord nécessaire d'installer un ingress controller dans le cluster:

  • Il s'agit d'un déploiement conteneurisé d'un logiciel de reverse proxy (comme nginx) et intégré avec l'API de kubernetes
  • Le controlleur agit donc au niveau du protocole HTTP et doit lui-même être exposé (port 80 et 443) à l'extérieur, généralement via un service de type LoadBalancer.
  • Le controlleur redirige ensuite vers différents services (généralement configurés en ClusterIP) qui à leur tour redirigent vers différents ports sur les pods selon l'URL del a requête.

Il existe plusieurs variantes d'ingress controller:

  • Un ingress basé sur Nginx plus ou moins officiel à Kubernetes et très utilisé: https://kubernetes.github.io/ingress-nginx/
  • Un ingress Traefik optimisé pour k8s.
  • il en existe d'autres : celui de payant l'entreprise Nginx, Contour, HAProxy...

Chaque provider de cloud et flavour de kubernetes est légèrement différent au niveau de la configuration du controlleur ce qui peut être déroutant au départ:

  • minikube permet d'activer l'ingress nginx simplement (voir TP)
  • autre example: k3s est fourni avec traefik configuré par défaut
  • On peut installer plusieurs ingress controllers correspondant à plusieurs IngressClasses

Comparaison des controlleurs: https://medium.com/flant-com/comparing-ingress-controllers-for-kubernetes-9b397483b46b


La nouvelle API Gateway

Ingress vs. Gateway API :


  • Ingress : Le modèle Ingress, traditionnellement utilisé dans Kubernetes, permet de définir des règles pour diriger le trafic HTTP/S entrant vers les services. Bien qu'il soit largement utilisé, il présente des limitations en termes de flexibilité et de contrôle fin du trafic.
  • Gateway API : Le nouveau modèle Gateway API offre une approche plus flexible et extensible pour la gestion du trafic. Il permet de définir des routes HTTP/S, TCP, et autres, avec un contrôle granulaire sur les politiques de trafic, la sécurité et l'observabilité.

Rôle de l'API Gateway dans Kubernetes :

  • Fonctions Clés : La Gateway API peut gérer des tâches comme l'authentification, l'autorisation, le contrôle de taux (rate limiting), et la gestion des certificats TLS. Elle offre une flexibilité accrue par rapport à l'Ingress, permettant des configurations plus avancées et spécifiques aux besoins de l'application.
  • Remplacement de l'Ingress : Avec la montée en puissance de la Gateway API, l'Ingress est progressivement remplacé pour les cas d'utilisation avancés. La Gateway API fournit une abstraction plus puissante et unifiée pour gérer le trafic entrant dans Kubernetes.

État Actuel :

  • Adoption : La Gateway API est en cours d'adoption et devient de plus en plus standardisée dans les déploiements Kubernetes modernes. Elle offre une solution plus robuste et flexible pour la gestion du trafic entrant, complémentant et parfois chevauchant les fonctionnalités des services mesh.
  • Évolution : Les services mesh continuent d'évoluer pour tirer parti des nouvelles capacités offertes par la Gateway API. Cela inclut l'intégration étroite avec les API de Gateway pour offrir une solution unifiée et cohérente pour la gestion du trafic, la sécurité et l'observabilité dans Kubernetes.

Gestion dynamique des certificats à l'aide de certmanager

Certmanager est une application kubernetes (un operator) capable de générer automatiquement des certificats TLS/HTTPS pour nos ingresses.

Exemple de syntaxe d'un ingress utilisant certmanager:

apiVersion: networking.k8s.io/v1 
kind: Ingress
metadata:
name: kuard
annotations:
kubernetes.io/ingress.class: "nginx"
cert-manager.io/issuer: "letsencrypt-prod"
spec:
tls:
- hosts:
- example.example.com
secretName: quickstart-example-tls
rules:
- host: example.example.com
http:
paths:
- path: /
pathType: Exact
backend:
service:
name: kuard
port:
number: 80