التوجيه العملي لـ Envoy Gateway HTTP: Host و Path و Header وتقسيم المرور
المقالتان الأوليان منحتاك أساس "ما هذا الشيء". وهذه المقالة تدخل في الشيء الذي ستعدله فعليًا يومًا بعد يوم: HTTPRoute.
فكّر في HTTPRoute على أنه سكربت توجيه للمرور الوارد، لكن بدلًا من كتابة قواعد Nginx أو حفظ annotations الخاصة بالـ controllers، فأنت تستخدم موارد أصلية في Kubernetes.
شيء يجب توضيحه من البداية: هذه المقالة تغطي فقط HTTPRoute لأنه يتعامل مع دلالات HTTP/HTTPS. أما gRPC فالأرجح أنك ستتجه إلى GRPCRoute، وبالنسبة إلى TCP الخام فإلى TCPRoute، وبالنسبة إلى TLS passthrough فإلى TLSRoute. لا تُجبر كل شيء على الدخول في HTTPRoute لأن الـ YAML سيبدأ بإعطاء انطباع "نحن فقط نحاول الترقيع."
أكثر أنواع التوجيه شيوعًا: Host و Path
هذا هو المثال الأكثر شيوعًا:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app-route
spec:
parentRefs:
- name: eg
hostnames:
- "app.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: api-service
port: 8080ومعناه:
- فقط
Host: app.example.comسيطابق هذا الـ route - يجب أن يبدأ الـ path بـ
/api - سيتم تحويل المرور إلى
api-service:8080
إذا حذفت hostnames، يصبح الـ route "معتمدًا على path فقط، ولأي domain."
ما الذي يمكن لـ matches أن يطابقه
matches أحد أكثر المفاهيم مركزية في HTTPRoute.
فكّر فيه على أنه "ما الشروط التي تفعّل هذه القاعدة."
شروط المطابقة الشائعة:
pathheadersqueryParamsmethod
مثال:
matches:
- method: GET
path:
type: PathPrefix
value: /api
headers:
- name: version
value: v2ومعناه:
- فقط طلبات
GET - الـ path يبدأ بـ
/api - يجب أن يحتوي الطلب على header باسم
version: v2
هذا النمط مثالي لتجارب نسخ الـ API أو الإطلاقات المرحلية لمستخدمين محددين.
Route واحد، وعدة Rules
يمكن لـ HTTPRoute واحد أن يوجّه paths مختلفة إلى خدمات مختلفة:
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: api-service
port: 8080
- matches:
- path:
type: PathPrefix
value: /admin
backendRefs:
- name: admin-service
port: 8080وهذا ممتاز لتقسيم المسارات الفرعية تحت domain واحد، مثلًا:
/apiيذهب إلى خدمة API/adminيذهب إلى backend الإدارة
منطق التوجيه هنا ظاهر فورًا. وليس مثل بعض ملفات الإعدادات القديمة التي تصل إلى منتصفها وتبدأ بالتشكيك في وجودك أصلًا.
استخدام Header و Method و Query كشروط
HTTPRoute لا يقتصر على المطابقة حسب path، بل يمكنه أيضًا المطابقة حسب request headers و methods و query params.
على سبيل المثال، وجّه فقط الطلبات التي تحمل version: v2 إلى الخدمة الجديدة:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: versioned-route
spec:
parentRefs:
- name: eg
rules:
- matches:
- path:
type: PathPrefix
value: /api
headers:
- name: version
value: v2
backendRefs:
- name: api-v2
port: 8080
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: api-v1
port: 8080هذا النمط مفيد جدًا في:
- Canary releases
- تمكين عملاء محددين من تجربة الإصدارات الجديدة مبكرًا
- التحقق اليدوي من الميزات الجديدة
مقارنةً بالانتقال الكامل إلى domain جديد، فإن التوجيه المبني على header أدق وأسهل للتجربة.
filters: معالجة الطلبات قبل التوجيه
HTTPRoute لا يكتفي بـ "المطابقة ثم التوجيه"، بل يمكنه أيضًا تطبيق تحويلات قبل التوجيه أو بعده. ولهذا توجد filters.
من الاستخدامات الشائعة إضافة headers:
rules:
- filters:
- type: RequestHeaderModifier
requestHeaderModifier:
add:
- name: x-env
value: prod
backendRefs:
- name: api-service
port: 8080وهذا مفيد في:
- حقن بيانات tracing قبل تمرير الطلب إلى backend
- إضافة headers ثابتة على طبقة ingress
- تحويلات الطلب/الاستجابة
ومع ذلك، فـ filters قوية لكنها ليست تصريحًا مجانيًا لتحويل طبقة ingress إلى blob ضخم من middleware. تكديس منطق كثير على الـ routes سيجعل debugging يبدو وكأنه يحتاج إلى جلسة علاج نفسي خاصة به.
تقسيم المرور بالأوزان: أكثر أنماط Canary شيوعًا
لإرسال 90% من المرور إلى النسخة القديمة و10% إلى النسخة الجديدة، استخدم weight:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: canary-route
spec:
parentRefs:
- name: eg
hostnames:
- "app.example.com"
rules:
- backendRefs:
- name: api-v1
port: 8080
weight: 90
- name: api-v2
port: 8080
weight: 10هذا هو الشكل الأساسي للـ canary / traffic splitting.
أنت لا تحتاج إلى service mesh كامل فقط كي تنفذ تجربة على المرور. بالنسبة إلى كثير من خدمات الـ API، فهذا أكثر من كافٍ، كما أن ضبطه أنظف بكثير.
💡
weightقيمة نسبية، ولا يشترط أن يكون مجموعها 100. فـ9و1تعبّران عن النسبة نفسها التي تعبّر عنها90و10.
timeouts: لا تجعل الطلبات معلّقة للأبد
يدعم HTTPRoute إعدادات timeout على مستوى rule. وهذا مهم، فإذا لم تكن هناك حدود في طبقة ingress، فستبقى الطلبات السيئة معلقة بلا نهاية مثل عميل وقح يرفض المغادرة.
rules:
- matches:
- path:
type: PathPrefix
value: /reports
timeouts:
request: 10s
backendRequest: 2s
backendRefs:
- name: report-service
port: 8080فهم الحقلين:
request: الحد الأقصى للانتظار الكلي لطلب العميلbackendRequest: الحد الأقصى لانتظار طلب واحد إلى الـ backend
لاحظ أن backendRequest لا ينبغي أن يتجاوز request، وإلا يصبح المنطق شبيهًا بقولك: "الرحلة كلها 10 دقائق، لكن انتظار حافلة واحدة قد يستغرق 20 دقيقة." وهنا يبدأ الكون نفسه بالارتباك.
نقطتان شائعتان ودقيقتان
1. يمكن لـ parentRefs أن يستهدف Listener محددًا
إذا كان الـ Gateway يملك عدة listeners، فيمكنك الارتباط بواحد فقط:
parentRefs:
- name: eg
sectionName: httpوهذا يعني أن الـ route يرتبط فقط بالـ listener المسمى http.
وفي البنى متعددة الـ listeners، يكون هذا أكثر قابلية للتوقع بكثير من الارتباط بكل شيء.
2. عدم وجود matches يعني مطابقة كل شيء
إذا كانت الـ rule لا تحتوي على matches، فهي افتراضيًا تطابق /، أي أنها catch-all عمليًا.
وهذا مريح، لكنه قد يبتلع بصمت ما كنت تظنه rules أكثر تحديدًا. فلا تتجاوزه بدافع الكسل ثم تقضي ساعة تتساءل لماذا قواعدك الدقيقة لا تعمل.
التحقق من أن التوجيه يعمل
بعد ضبط الـ routes، تحقّق باستخدام curl:
curl -H "Host: app.example.com" http://$GATEWAY_HOST/api/users
curl -H "Host: app.example.com" -H "version: v2" http://$GATEWAY_HOST/api/usersولفحص حالة HTTPRoute:
kubectl get httproute app-route -o yamlركّز بشكل خاص على:
status.parents- حالة
Accepted
أحيانًا يبدو الـ YAML صحيحًا، لكن الـ Gateway رفضه. وهذا يشبه أن ترسل طلب توظيف وتفترض أنك قُبلت لمجرد أن لا أحد رد عليك.
الخلاصة في سطر واحد
جوهر HTTPRoute هو: "طابِق أولًا، ثم وجّه." وبمجرد أن تتقن host و path و header و weight، ستتمكن من تغطية نحو 80% من متطلبات HTTP ingress اليومية.
الخطوة التالية
المقالة التالية تتناول شيئًا ستواجهه في كل بيئة إنتاج تقريبًا: HTTPS والشهادات و TLS termination والأمان: 👉 TLS والأمان