كيف يدعم APISIX Ingress Controller آلاف نسخ الـ Pod؟
Xin Rong
October 21, 2022
1000+ من الـ Kubernetes Pods
تُعتبر الـ Pods أصغر كائنات قابلة للنشر في Kubernetes. نستخدم الـ Pod لتشغيل نسخة واحدة من التطبيق، لذلك نقوم بتخصيص موارد محدودة للـ Pod بسبب قابلية التوسع. ومع ذلك، إذا واجهنا سيناريوهات ذات حركة مرور عالية، سنستخدم التوسع الأفقي للتعامل معها.
على سبيل المثال، بالنسبة لتجار التجزئة عبر الإنترنت، سيؤدي يوم الجمعة الأسود إلى زيادة سريعة في حركة المرور. للتعامل مع هذا السيناريو، يجب علينا توسيع الخدمات تلقائيًا للتعامل مع المزيد من حركة المرور. سنقوم بنشر المزيد من النسخ بناءً على استراتيجية التوسع التلقائي، مما يؤدي إلى المزيد من الـ Pods.
كل Pod لديه عنوان IP ديناميكي. وفرت واجهة برمجة التطبيقات (API) الخاصة بـ Endpoints طريقة مباشرة لتتبع نقاط نهاية الشبكة في Kubernetes حتى نتمكن من تحقيق موازنة الحمل من خلال مراقبة التغييرات في عنوان IP الخاص بالـ Pod بشكل منتظم. ومع ذلك، مع نمو مجموعات Kubernetes والخدمات للتعامل مع المزيد من حركة المرور إلى المزيد من الـ Pods، على سبيل المثال، سيناريو الجمعة الأسود المذكور أعلاه، يستمر عدد الـ Pods في الزيادة، وتصبح واجهة برمجة التطبيقات (API) الخاصة بـ Endpoints أكبر. وبالتالي، أصبحت قيود واجهة برمجة التطبيقات (API) الخاصة بـ Endpoints أكثر وضوحًا، بل وأصبحت عائقًا للأداء.
لحل مشكلة القيود في واجهة برمجة التطبيقات (API) الخاصة بـ Endpoints، بدأت Kubernetes بدعم واجهة برمجة التطبيقات (API) الخاصة بـ EndpointSlice في الإصدار v1.21. تساعد واجهة برمجة التطبيقات (API) الخاصة بـ EndpointSlice في حل مشكلة الأداء في التعامل مع عدد كبير من نقاط نهاية الشبكة في واجهة برمجة التطبيقات (API) الخاصة بـ Endpoints ولديها قابلية توسع وامتداد ممتازة.
يمكننا معرفة الفروق بينهما مباشرة من الرسم البياني أدناه:
- التغييرات في Endpoints أثناء ذروة حركة المرور
- التغييرات في Endpointslices أثناء ذروة حركة المرور
في Kubernetes، كيف تتواصل التطبيقات مع بعضها البعض؟ ما هي الفروق المحددة بين Endpoints و EndpointSlice؟ ما هي العلاقة بين Pod و Endpoints/EndpointSlice؟ كيف يدعم APISIX هذه الميزات، وكيفية تثبيتها واستخدامها؟ سنركز على هذه الأسئلة في هذه المقالة.
كيفية الوصول إلى التطبيقات في Kubernetes
عبر Service
كل Pod لديه عنوان IP فريد في Kubernetes. عادةً، سيقوم Service ببناء اتصالات مع Pod باستخدام selector
، ويوفر نفس اسم النطاق الفرعي لـ DNS، ويحقق موازنة الحمل. بالإضافة إلى ذلك، يمكن للتطبيقات داخل مجموعة Kubernetes استخدام DNS للتواصل مع بعضها البعض.
عند إنشاء Service، ستقوم Kubernetes بتوصيل Service مع مورد Endpoints. ومع ذلك، إذا لم يحدد Service أي selectors، فلن تقوم Kubernetes بإنشاء Endpoints تلقائيًا لـ Service.
ما هو Endpoints، وما هي العلاقة مع Pod
Endpoints هو كائن مورد في Kubernetes، يتم تخزينه في etcd، ويشمل مراجع لعناوين الوصول إلى مجموعة من الـ Pods التي تطابق Service. لذلك، يمكن أن يكون لكل Service مورد Endpoints واحد فقط. سيقوم مورد Endpoints بمراقبة مجموعات الـ Pods وتحديثها بشكل متزامن بمجرد تغيير أي Pod في Service.
- نشر 3 نسخ من
httpbin
، والتحقق من حالة الـ Pods، بما في ذلك معلومات 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>
- إنشاء خدمة
httpbin
، والتحقق من حالة نقاط نهاية 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
من المثالين أعلاه، يمكننا أن نرى أن كل نقطة نهاية شبكة واحدة لـ httpbin
في Endpoints تطابق عنوان IP لـ Pod.
عيوب Endpoints
- لدى Endpoints حد تخزين، إذا كان أي مورد Endpoints يحتوي على أكثر من 1000 نقطة نهاية، فسيقوم متحكم Endpoints بقطع النقاط النهائية إلى 1000.
- يمكن أن يكون لكل Service مورد Endpoints واحد فقط، مما يعني أن مورد Endpoints يحتاج إلى تخزين عناوين IP ومعلومات شبكة أخرى لكل Pod الذي يدعم Service المقابل. نتيجة لذلك، يصبح مورد API الخاص بـ Endpoints كبيرًا، ويجب تحديثه عند تغيير نقطة نهاية شبكة واحدة في المورد. عندما تحتاج الأعمال إلى تغييرات متكررة في النقاط النهائية، سيتم إرسال مورد API كبير إلى بعضها البعض، مما يؤثر على أداء مكونات Kubernetes.
ما هو Endpointslices
Endpointslices هو بديل أكثر قابلية للتوسع والامتداد لـ Endpoints ويساعد في التعامل مع مشكلة الأداء الناتجة عن معالجة عدد كبير من نقاط نهاية الشبكة. كما يساعد في توفير منصة قابلة للامتداد لميزات إضافية مثل التوجيه الطوبولوجي. تم دعم هذه الميزة في Kubernetes v1.21.
تم تصميم واجهة برمجة التطبيقات (API) الخاصة بـ EndpointSlice لمعالجة هذه المشكلة باستخدام نهج مشابه للتجزئة. بدلاً من تتبع جميع عناوين IP الخاصة بـ Pods لـ Service باستخدام مورد Endpoints واحد، نقوم بتقسيمها إلى عدة EndpointSlices أصغر.
بشكل افتراضي، تقوم لوحة التحكم بإنشاء وإدارة EndpointSlices بحيث لا تحتوي على أكثر من 100 نقطة نهاية لكل منها. يمكنك تكوين هذا باستخدام علامة --max-endpoints-per-slice
الخاصة بـ kube-controller-manager، حتى 1000.
لماذا نحتاج إليها
فكر في سيناريو
افترض أن هناك Service مدعومًا بـ 2000 Pod، مما قد يؤدي إلى مورد Endpoints بحجم 1.0 ميجابايت. في بيئة الإنتاج، إذا كان هذا Service يحتاج إلى تحديثات متتالية أو هجرة نقاط نهاية، فسيتم تحديث موارد Endpoints بشكل متكرر. فكر في أن التحديثات المتتالية ستؤدي إلى استبدال جميع الـ Pods بسبب الحد الأقصى لحجم أي طلب في etcd. قام Kubernetes بتعيين حد أقصى لـ 1000 نقطة نهاية لـ Endpoints. إذا كان هناك أكثر من 1000 نقطة نهاية، فلن يكون لدى مورد Endpoints أي مراجع على نقاط نهاية شبكة إضافية.
إذا كان Service يحتاج إلى تحديثات متتالية متعددة بسبب بعض الاحتياجات الخاصة؛ في هذه الحالة، سيتم نقل كائن مورد API كبير بين مكونات Kubernetes، مما يؤثر بشكل كبير على أداء مكونات Kubernetes.
ماذا لو استخدمنا EndpointSlice
افترض أن هناك Service مدعومًا بـ 2000 Pod، وقمنا بتعيين 100 نقطة نهاية لكل EndpointSlices في التكوين، فسينتهي بنا الأمر بـ 20 EndpointSlices. الآن عند إضافة أو إزالة Pod، يحتاج فقط EndpointSlice صغير واحد إلى التحديث. من الواضح أن هذا تحسن ملحوظ في قابلية التوسع وامتداد الشبكة. افترض أن كل Service لديه متطلبات توسع تلقائي. في هذه الحالة، سيقوم Service بنشر المزيد من الـ Pods، وسيتم تحديث مورد Endpoints بشكل متكرر للتعامل مع زيادة حركة المرور عند حدوث ذروة حركة المرور، وسيصبح الفرق أكثر وضوحًا. والأهم من ذلك، الآن لا نحتاج إلى تخزين جميع عناوين IP الخاصة بـ Pods لـ Service في مورد واحد، ولا داعي للقلق بشأن الحد الأقصى لحجم الكائنات المخزنة في etcd.
استنتاج Endpoints مقابل EndpointSlice
لأن EndpointSlice مدعوم منذ Kubernetes v1.21، فإن جميع الاستنتاجات تشير إلى Kubernetes v1.21.
حالات استخدام Endpoints:
- هناك حاجة للتوسع التلقائي، ولكن عدد الـ Pods صغير نسبيًا، ولن تسبب تحويلات الموارد حركة مرور شبكية كبيرة واحتياجات معالجة إضافية.
- لا توجد حاجة للتوسع التلقائي، ولن يكون عدد الـ Pods كبيرًا. ومع ذلك، حتى إذا كان عدد الـ Pods ثابتًا، فلا يمكن لـ Service تجنب التحديثات المتتالية والأعطال.
حالات استخدام EndpointSlice:
- هناك حاجة للتوسع التلقائي، وعدد الـ Pods كبير (مئات الـ Pods)
- عدد الـ Pods كبير جدًا (مئات الـ Pods) بسبب الحد الأقصى لنقاط النهاية لـ Endpoints وهو 1000؛ أي Pod يحتوي على أكثر من 1000 نقطة نهاية يجب أن يستخدم EndpointSlice.
الممارسة في APISIX Ingress Controller
يحقق APISIX Ingress Controller موازنة الحمل والتحقق من الصحة من خلال مراقبة تغييرات مورد Endpoints أو EndpointSlice. لدعم Kubernetes v1.16+، سيستخدم APISIX Ingress Controller Endpoints بشكل افتراضي أثناء التثبيت.
إذا كان إصدار المجموعة لديك هو Kubernetes v1.21+، فستحتاج إلى تحديد علامة watchEndpointSlice=true
لدعم ميزة EndpointSlice أثناء تثبيت APISIX Ingress Controller.
ملاحظة: في المجموعات التي تعمل بإصدار Kubernetes v1.21+، نوصي باستخدام ميزة Endpointslice، وإلا عندما يتجاوز عدد الـ Pods قيمة التكوين لعلامة
--max-endpoints-per-slice
، سيتم فقد التكوين بسبب أن APISIX Ingress Controller يراقب كائن مورد Endpoints.
إنشاء Service مدعوم بـ 20 نسخة من الـ Pods
تكوين تطبيق httpbin
Service في Kubernetes، وإنشاء 20 نسخة من الـ 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
التوجيه عبر APISIX Ingress
- استخدام Helm لتثبيت APISIX Ingress Controller
استخدم --set ingress-controller.config.kubernetes.watchEndpointSlice=true
لتمكين دعم ميزة 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
- استخدام مورد CRD للتوجيه
لا يمكن للمستخدمين ملاحظة دعم ميزات Endpoints و EndpointSlice في APISIX Ingress Controller، وتكويناتها هي نفسها
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
- بالتحقق من Pod الخاص بـ APISIX، يمكننا أن نرى أن حقل nodes الخاص بكائن upstream في APISIX يحتوي على عناوين IP لـ 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
}
... // تجاهل 13 عقدة أدناه
// 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
}
}
- المطابقة مع نقاط نهاية شبكة 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
استنتاج
تقدم هذه المقالة سيناريوهات حيث يحتاج Kubernetes إلى نشر عدد كبير من الـ Pods والمشاكل التي نواجهها. كما تقارن الفروق بين Endpoints و EndpointSlice، وتقدم طريقة تمكين ميزة EndpointSlice أثناء تثبيت APISIX Ingress Controller. إذا كان إصدار المجموعة لديك هو Kubernetes v1.21+، نوصي بتمكين ميزة EndpointSlice أثناء تثبيت APISIX Ingress Controller. وبالتالي، يمكن تجنب فقد التكوين، ولا داعي للقلق بشأن قيمة التكوين لعلامة --max-endpoints-per-slice
.