Comment le APISIX Ingress Controller prend-il en charge des milliers de répliques de Pods ?
Xin Rong
October 21, 2022
1 000+ Pods Kubernetes
Les Pods sont les plus petits objets déployables dans Kubernetes. Nous utilisons un Pod pour exécuter une seule instance d'une application, donc nous n'attribuons que des ressources limitées au Pod en raison de la scalabilité. Cependant, si nous rencontrons des scénarios de trafic élevé, nous utiliserons la mise à l'échelle horizontale pour les gérer.
Par exemple, pour les détaillants en ligne, le Black Friday entraînera une augmentation rapide du trafic. Pour gérer ce scénario, nous devons mettre à l'échelle automatiquement les services pour gérer plus de trafic. Nous déploierions plus de réplications en fonction de la stratégie de mise à l'échelle automatique, ce qui entraînerait plus de Pods.
Chaque Pod a une adresse IP dynamique. L'API Endpoints a fourni un moyen simple de suivre les points de terminaison réseau dans Kubernetes afin que nous puissions réaliser l'équilibrage de charge en surveillant en temps réel les changements d'IP des Pods. Cependant, à mesure que les clusters Kubernetes et les Services ont grandi pour gérer plus de trafic vers plus de Pods, par exemple, le scénario du Black Friday mentionné ci-dessus, le nombre de Pods continue d'augmenter, et l'API Endpoints devient plus volumineuse. Par conséquent, les limitations de l'API Endpoints sont devenues plus visibles, et elle est même devenue le goulot d'étranglement des performances.
Pour résoudre le problème de limitation de l'API Endpoints, Kubernetes commence à prendre en charge l'API EndpointSlice dans la version v1.21. L'API EndpointSlice aide à résoudre le problème de performance lié à la gestion d'un grand nombre de points de terminaison réseau dans l'API Endpoints et offre une excellente scalabilité et extensibilité.
Nous pouvons directement voir les différences entre eux dans le diagramme ci-dessous :
- Les changements des Endpoints pendant un pic de trafic
- Les changements des Endpointslices pendant un pic de trafic
Dans Kubernetes, comment les applications communiquent-elles entre elles ? Quelles sont les différences spécifiques entre Endpoints et EndpointSlice ? Quelle est la relation entre Pod et Endpoints/EndpointSlice ? Comment APISIX prend-il en charge ces fonctionnalités, et comment les installer et les utiliser ? Nous nous concentrerons sur ces questions dans cet article.
Comment accéder aux applications dans Kubernetes
Via Service
Chaque Pod a sa propre adresse IP unique dans Kubernetes. Généralement, Service établira des connexions avec les Pods en utilisant selector
, fournira le même sous-domaine DNS et réalisera l'équilibrage de charge. De plus, les applications à l'intérieur du cluster Kubernetes peuvent utiliser DNS pour communiquer entre elles.
Lorsqu'un Service est créé, Kubernetes connectera le Service à une ressource Endpoints. Cependant, si le Service n'a spécifié aucun sélecteur, Kubernetes ne créera pas automatiquement de Endpoints pour le Service.
Qu'est-ce que Endpoints, et quelle est la relation avec Pod
Endpoints est un objet de ressource dans Kubernetes, stocké dans etcd, et comprend des références à un ensemble d'adresses d'accès des Pods qui correspondent à un Service. Par conséquent, chaque Service ne peut avoir qu'une seule ressource Endpoints. La ressource Endpoints surveillera les clusters de Pods et mettra à jour de manière synchrone dès qu'un Pod dans le Service change.
- Déployez 3 réplications de
httpbin
, et vérifiez l'état des Pods, y compris les informations IP
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
httpbin-deployment-fdd7d8dfb-8sxxq 1/1 Running 0 49m 10.1.36.133 docker-desktop <none> <none>
httpbin-deployment-fdd7d8dfb-bjw99 1/1 Running 4 (5h39m ago) 23d 10.1.36.125 docker-desktop <none> <none>
httpbin-deployment-fdd7d8dfb-r5nf9 1/1 Running 0 49m 10.1.36.131 docker-desktop <none> <none>
- Créez le service
httpbin
, et vérifiez l'état des Endpoints
$ kubectl get endpoints httpbin
NAME ENDPOINTS AGE
httpbin 10.1.36.125:80,10.1.36.131:80,10.1.36.133:80 23d
À partir des deux exemples ci-dessus, nous pouvons voir que chaque point de terminaison réseau de la ressource httpbin
dans Endpoints correspond à une adresse IP de Pod.
Inconvénients de Endpoints
- Endpoints a une limite de stockage, si une ressource Endpoints a plus de 1000 points de terminaison, alors le contrôleur Endpoints tronquera les points de terminaison à 1000.
- Un service ne peut avoir qu'une seule ressource Endpoints, ce qui signifie que la ressource Endpoints doit stocker les adresses IP et autres informations réseau pour chaque Pod qui soutient le Service correspondant. Par conséquent, la ressource API Endpoints devient énorme, et elle doit être mise à jour lorsqu'un seul point de terminaison réseau dans la ressource change. Lorsque l'entreprise a besoin de changements fréquents de points de terminaison, une énorme ressource API sera envoyée, ce qui affecte les performances des composants Kubernetes.
Qu'est-ce que Endpointslices
Endpointslices est une alternative plus évolutive et extensible à Endpoints et aide à gérer le problème de performance causé par le traitement d'un grand nombre de points de terminaison réseau. Il aide également à fournir une plateforme extensible pour des fonctionnalités supplémentaires telles que le routage topologique. Cette fonctionnalité est prise en charge dans Kubernetes v1.21.
L'API EndpointSlice a été conçue pour résoudre ce problème avec une approche similaire au sharding. Au lieu de suivre toutes les IP des Pods pour un Service avec une seule ressource Endpoints, nous les divisons en plusieurs EndpointSlices plus petits.
Par défaut, le plan de contrôle crée et gère des EndpointSlices pour ne pas avoir plus de 100 points de terminaison chacun. Vous pouvez configurer cela avec le drapeau --max-endpoints-per-slice
de kube-controller-manager, jusqu'à 1000.
Pourquoi en avons-nous besoin
Considérons un scénario
Supposons qu'il y ait un Service soutenu par 2000 Pods, ce qui pourrait aboutir à une ressource Endpoints de 1,0 Mo. Dans l'environnement de production, si ce Service a des mises à jour progressives ou des migrations de points de terminaison, les ressources Endpoints seront fréquemment mises à jour. Pensez aux mises à jour progressives qui entraîneront le remplacement de tous les Pods en raison de la limite de taille maximale de toute requête dans etcd. Kubernetes a fixé une limite maximale de 1000 points de terminaison pour Endpoints. S'il y a plus de 1000 points de terminaison, la ressource Endpoints n'aura aucune référence sur les points de terminaison réseau supplémentaires.
Supposons que le Service nécessite plusieurs mises à jour progressives en raison de besoins spéciaux ; dans ce cas, un énorme objet de ressource API sera transféré entre les composants Kubernetes, ce qui affecte considérablement les performances des composants Kubernetes.
Et si nous utilisons EndpointSlice
Supposons qu'il y ait un Service soutenu par 2000 Pods, et que nous attribuions 100 points de terminaison à chaque Endpointslices dans la configuration, alors nous aurons 20 Endpointslices. Maintenant, lorsqu'un Pod est ajouté ou supprimé, un seul petit EndpointSlice doit être mis à jour. Évidemment, c'est une amélioration remarquable en termes de scalabilité et d'extensibilité réseau. Supposons que chaque Service ait une exigence de mise à l'échelle automatique. Dans ce cas, le Service déploiera plus de Pods, et la ressource Endpoints sera fréquemment mise à jour pour gérer l'augmentation du trafic lors d'un pic de trafic, et la différence deviendra plus perceptible. Plus important encore, maintenant que toutes les IP des Pods pour un Service n'ont pas besoin d'être stockées dans une seule ressource, nous n'avons pas à nous soucier de la limite de taille pour les objets stockés dans etcd.
Conclusion de Endpoints VS EndpointSlice
Étant donné que EndpointSlice est pris en charge depuis Kubernetes v1.21, toutes les conclusions se réfèrent à Kubernetes v1.21.
Cas d'utilisation de Endpoints :
- Il y a un besoin de mise à l'échelle automatique, mais le nombre de Pods est relativement faible, et les transferts de ressources ne causeront pas d'énormes trafics réseau et de besoins de traitement supplémentaires.
- Il n'y a pas besoin de mise à l'échelle automatique, et le nombre de Pods ne sera pas énorme. Cependant, même si le nombre de Pods est fixe, le Service ne peut pas omettre les mises à jour progressives et les pannes.
Cas d'utilisation de EndpointSlice :
- Il y a un besoin de mise à l'échelle automatique, et le nombre de Pods est énorme (des centaines de Pods)
- Le nombre de Pods est énorme (des centaines de Pods) en raison de la limite maximale de points de terminaison de Endpoints étant de 1000 ; tout Pod qui a plus de 1000 points de terminaison doit utiliser EndpointSlice.
Pratique dans APISIX Ingress Controller
APISIX Ingress Controller réalise l'équilibrage de charge et les vérifications de santé en surveillant les changements de la ressource Endpoints ou EndpointSlice. Pour prendre en charge Kubernetes v1.16+, APISIX Ingress Controller utilisera Endpoints par défaut lors de l'installation.
Si la version de votre cluster est Kubernetes v1.21+, alors vous devez spécifier le drapeau watchEndpointSlice=true
pour prendre en charge la fonctionnalité EndpointSlice lors de l'installation d'APISIX Ingress Controller.
Remarque : Dans les clusters avec Kubernetes v1.21+, nous recommandons d'utiliser la fonctionnalité Endpointslice, sinon lorsque le nombre de Pods dépasse la valeur de configuration du drapeau
--max-endpoints-per-slice
, la configuration serait perdue car APISIX Ingress Controller surveille l'objet de ressource Endpoints.
Création d'un Service soutenu par 20 réplications de Pods
Configurez le Service de l'application httpbin
dans Kubernetes, et créez 20 réplications de Pods
- kubectl apply -f httpbin-deploy.yaml
# htppbin-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin-deployment
spec:
replicas: 20
selector:
matchLabels:
app: httpbin-deployment
strategy:
rollingUpdate:
maxSurge: 50%
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
labels:
app: httpbin-deployment
spec:
terminationGracePeriodSeconds: 0
containers:
- livenessProbe:
failureThreshold: 3
initialDelaySeconds: 2
periodSeconds: 5
successThreshold: 1
tcpSocket:
port: 80
timeoutSeconds: 2
readinessProbe:
failureThreshold: 3
initialDelaySeconds: 2
periodSeconds: 5
successThreshold: 1
tcpSocket:
port: 80
timeoutSeconds: 2
image: "kennethreitz/httpbin:latest"
imagePullPolicy: IfNotPresent
name: httpbin-deployment
ports:
- containerPort: 80
name: "http"
protocol: "TCP"
---
apiVersion: v1
kind: Service
metadata:
name: httpbin
spec:
selector:
app: httpbin-deployment
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
type: ClusterIP
Proxy via APISIX Ingress
- Utilisez Helm pour installer APISIX Ingress Controller
Utilisez --set ingress-controller.config.kubernetes.watchEndpointSlice=true
pour activer la prise en charge de la fonctionnalité EndpointSlice.
helm repo add apisix https://charts.apiseven.com
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
kubectl create ns ingress-apisix
helm install apisix apisix/apisix \
--set gateway.type=NodePort \
--set ingress-controller.enabled=true \
--namespace ingress-apisix \
--set ingress-controller.config.apisix.serviceNamespace=ingress-apisix \
--set ingress-controller.config.kubernetes.watchEndpointSlice=true
- Utilisez la ressource CRD pour le proxy
Les utilisateurs ne peuvent pas remarquer la prise en charge des fonctionnalités Endpoints et EndpointSlice dans APISIX Ingress Controller, et leurs configurations sont les mêmes
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: httpbin-route
spec:
http:
- name: rule
match:
hosts:
- httpbin.org
paths:
- /get
backends:
- serviceName: httpbin
servicePort: 80
- En vérifiant le Pod APISIX, nous pouvons voir que le champ nodes de l'objet upstream d'APISIX contient les adresses IP des 20 Pods.
kubectl exec -it ${Pod for APISIX} -n ingress-apisix -- curl "http://127.0.0.1:9180/apisix/admin/upstreams" -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1'
{
"action": "get",
"count": 1,
"node": {
"key": "\/apisix\/upstreams",
"nodes": [
{
"value": {
"hash_on": "vars",
"desc": "Created by apisix-ingress-controller, DO NOT modify it manually",
"pass_host": "pass",
"nodes": [
{
"weight": 100,
"host": "10.1.36.100",
"priority": 0,
"port": 80
},
{
"weight": 100,
"host": "10.1.36.101",
"priority": 0,
"port": 80
},
{
"weight": 100,
"host": "10.1.36.102",
"priority": 0,
"port": 80
},
{
"weight": 100,
"host": "10.1.36.103",
"priority": 0,
"port": 80
},
{
"weight": 100,
"host": "10.1.36.104",
"priority": 0,
"port": 80
},
{
"weight": 100,
"host": "10.1.36.109",
"priority": 0,
"port": 80
},
{
"weight": 100,
"host": "10.1.36.92",
"priority": 0,
"port": 80
}
... // ignorer les 13 nœuds ci-dessous
// 10.1.36.118
// 10.1.36.115
// 10.1.36.116
// 10.1.36.106
// 10.1.36.113
// 10.1.36.111
// 10.1.36.108
// 10.1.36.114
// 10.1.36.107
// 10.1.36.110
// 10.1.36.105
// 10.1.36.112
// 10.1.36.117
],
"labels": {
"managed-by": "apisix-ingress-controller"
},
"type": "roundrobin",
"name": "default_httpbin_80",
"scheme": "http"
},
"key": "\/apisix\/upstreams\/5ce57b8e"
}
],
"dir": true
}
}
- Correspondance avec les points de terminaison réseau d'EndpointSlice
addressType: IPv4
apiVersion: discovery.k8s.io/v1
endpoints:
- addresses:
- 10.1.36.92
...
- addresses:
- 10.1.36.100
...
- addresses:
- 10.1.36.104
...
- addresses:
- 10.1.36.102
...
- addresses:
- 10.1.36.101
...
- addresses:
- 10.1.36.103
...
- addresses:
- 10.1.36.109
...
- addresses:
- 10.1.36.118
...
- addresses:
- 10.1.36.115
...
- addresses:
- 10.1.36.116
...
- addresses:
- 10.1.36.106
...
- addresses:
- 10.1.36.113
...
- addresses:
- 10.1.36.111
...
- addresses:
- 10.1.36.108
...
- addresses:
- 10.1.36.114
...
- addresses:
- 10.1.36.107
...
- addresses:
- 10.1.36.110
...
- addresses:
- 10.1.36.105
...
- addresses:
- 10.1.36.112
...
- addresses:
- 10.1.36.117
...
kind: EndpointSlice
metadata:
labels:
endpointslice.kubernetes.io/managed-by: endpointslice-controller.k8s.io
kubernetes.io/service-name: httpbin
name: httpbin-dkvtr
namespace: default
ports:
- name: http
port: 80
protocol: TCP
Conclusion
Cet article présente les scénarios où Kubernetes doit déployer un grand nombre de Pods et les problèmes que nous rencontrons. Il compare également les différences entre Endpoints et EndpointSlice, et présente la manière d'activer la fonctionnalité EndpointSlice lors de l'installation d'APISIX Ingress Controller. Si la version de votre cluster est Kubernetes v1.21+, nous recommandons d'activer la fonctionnalité EndpointSlice lors de l'installation d'APISIX Ingress Controller. Par conséquent, cela peut éviter la perte de configuration, et nous n'avons pas à nous soucier de la valeur de configuration du drapeau --max-endpoints-per-slice
.