تعدد الـ Namespace وملكية الفرق في Envoy Gateway: allowedRoutes و ReferenceGrant وحوكمة العمل بين الفرق
إحدى أكبر قيم Gateway API ليست مجرد أن الـ YAML صار أجمل، بل أنه أخيرًا يجيب بوضوح عن سؤال قديم:
من يملك طبقة ingress؟ من يستطيع تغيير الـ routes؟ وهل يمكن للـ namespaces أن تشير إلى بعضها؟
كثير من آلام Ingress جاءت من كون كل شيء متشابكًا معًا. كانت فرق المنصة تخشى تغييرات التعريض غير المصرح بها. وكانت فرق التطبيقات تشعر أنها تحتاج إلى ticket فقط لتغيير path واحد. وفي النهاية كان الجميع يعذب بعضه بعضًا داخل YAML.
ومقاربة Gateway API تقول بوضوح:
- يمكن حوكمة ingress الخاص بالمنصة بشكل مستقل
- يمكن تفويض الـ routes إلى فرق التطبيقات
- السلوك عبر namespaces المختلفة يتطلب تصريحًا صريحًا
وهذه المقالة تشرح نموذج الملكية هذا من الأعلى إلى الأسفل.
ابدأ من هنا: Attachment و Reference شيئان مختلفان
هذا هو الفرق الذي يختلط على الناس أكثر من غيره.
ارتباط Route مع Gateway / Listener
هذا الأمر تُحكمه:
- قيمة
parentRefsفي الـ route - وقيمة
allowedRoutesفي الـ listener
وبمعنى آخر، ما إذا كان route ما يستطيع الارتباط بـ listener ليس أمرًا يحدده ReferenceGrant، بل يحدده ما إذا كان الـ listener مستعدًا لقبوله أم لا.
الإشارات إلى الموارد عبر Namespaces مختلفة
هنا يأتي دور ReferenceGrant غالبًا.
مثلًا:
- يريد
Gatewayالإشارة إلىSecretفي namespace آخر - يريد
HTTPRouteالإشارة إلىServiceفي namespace آخر
هذا ليس سؤال ارتباط، بل سؤال "هل منحك الـ namespace الهدف تفويضًا رسميًا لاستخدام موارده؟"
كثير من الناس يضغطون هذين المفهومين في شيء واحد ثم يبدأون بالتشكيك في سلامتهم العقلية. لكن القواعد ليست غامضة إلى هذه الدرجة عندما تفصل الأدوار.
allowedRoutes: قرّر أولًا من يحق له الارتباط
أكثر شكل مقيّد من الـ listeners يبدو هكذا:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: shared-gateway
namespace: infra
spec:
gatewayClassName: eg
listeners:
- name: https
protocol: HTTPS
port: 443
allowedRoutes:
namespaces:
from: Sameوهذا يعني أن الـ routes الموجودة داخل namespace infra فقط يمكنها الارتباط بهذا الـ listener.
وللسماح لـ namespaces خاصة بفرق محددة بالارتباط، استخدم Selector:
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
shared-gateway-access: "true"هذا النمط مناسب جدًا لفرق المنصة.
فأنت لست مضطرًا لإغلاق كل شيء بالكامل، لكنك أيضًا لا تحتاج إلى فتحه عبر from: All وترك الجميع يدخلون من دون قيود. بل يمكنك جعل الـ namespaces "تستحق" حق الارتباط عبر حمل label معين.
💡
allowedRoutesيُضبط لكل listener على حدة، وليس لكل Gateway. ويمكن أن تملك listeners مختلفة سياسات وصول مختلفة.
ReferenceGrant: الـ Namespace الهدف هو من يقرر
إذا كان route أو gateway يريد الإشارة إلى موارد في namespace آخر، فعلى الـ namespace الهدف أن يمنحه الإذن.
على سبيل المثال، يريد HTTPRoute خاص بفريق تطبيق أن يرسل المرور إلى backend موجود في namespace مشترك:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app-route
namespace: team-a
spec:
parentRefs:
- name: shared-gateway
namespace: infra
sectionName: https
rules:
- backendRefs:
- name: shared-api
namespace: shared-services
port: 8080هنا يحتاج namespace shared-services إلى ReferenceGrant:
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-team-a-route
namespace: shared-services
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: team-a
to:
- group: ""
kind: Serviceوباللغة البسيطة:
- يمكن لموارد
HTTPRouteالموجودة فيteam-a - أن تشير إلى موارد
Serviceالموجودة فيshared-services
لاحظ أن التفويض يُنشأ من الـ namespace الهدف، وليس من المصدر. فالمصدر لا يستطيع فقط أن يعلن رغبته في الوصول، بل صاحب المورد هو من يقرر. وهذا التصميم في الحقيقة مبدئي جدًا، لأن من يملك المورد يجب أن يحدد من يستطيع الوصول إليه.
النموذج الشائع لتقسيم المسؤوليات بين فريق المنصة وفرق التطبيقات
أكثر نموذج شائع للملكية يبدو هكذا:
فريق المنصة يملك
GatewayClassGatewayListener- إدارة شهادات TLS
- سياسات
allowedRoutes
فرق التطبيقات تملك
HTTPRoute/GRPCRouteداخل namespaces الخاصة بها- خدمات الـ backend
- تعديلات قواعد التوجيه
فوائد هذا التقسيم واضحة:
- فريق المنصة يحمي حدود التعريض والأمان في ingress
- فرق التطبيقات لا تزال قادرة على التحرك بسرعة في قواعد مرورها الخاصة
- لا أحد يحتاج إلى لمس الـ Gateway المشترك فقط من أجل تغيير path
يمكنك أيضًا منح كل الصلاحيات لمجموعة واحدة، وهذا ليس خطأ. لكن في الفرق الأكبر، يميل هذا النموذج إلى التحول إلى: "أي شخص يستطيع التغيير، لذلك عندما ينكسر شيء لا أحد يعرف من فعل ماذا."
نصائح عملية: كيف تتجنب كوارث Multi-Tenant
عادات مفيدة فعلًا:
- اجعل Shared Gateways تبدأ افتراضيًا بـ
allowedRoutes.namespaces.from: Selector - يجب أن تكون قواعد تسمية الـ namespaces والـ labels مستقرة، فلا تغيّر
team-aإلىallow-gwالشهر القادم - كل الإشارات عبر namespaces إلى backends أو Secrets تمر عبر
ReferenceGrant - يجب أن تدل أسماء الـ routes على الفريق أو الخدمة، مثل
team-a-api-route - في الـ listeners المشتركة، استخدم دائمًا
sectionNameلمنع الـ routes من الارتباط بالمدخل الخطأ
هناك مبدأ يستحق الاحتفاظ به:
إذا كان بإمكانك فرض حد على طبقة ingress، فلا تعتمد فقط على التوثيق وعلى أمل أن ينظم الناس أنفسهم.
في الهندسة، العملية أوضح وأقوى من الاتفاق الشفهي. قد يبدو هذا كلام شخص أكبر سنًا مما ينبغي، لكنه مفيد فعلًا.
الخلاصة في سطر واحد
في Gateway API، يتحكم allowedRoutes في "من يمكنه الدخول"، بينما يتحكم ReferenceGrant في "ما الذي يمكنه الوصول إليه بعد الدخول."
وحين تفرّق بين هذين الأمرين بوضوح، تصبح سيناريوهات تعدد الـ namespaces وتعدد الفرق أكثر قابلية للإدارة بكثير.
الخطوة التالية
بعد ترتيب الملكية والصلاحيات، تبقى القطعة الأخيرة: عندما لا تأخذ الإعدادات مفعولها، أي status وأي condition وأي command يجب أن تنظر إليه أولًا؟
المقالة التالية تغطي debugging وقراءة الحالات: 👉 دليل الحالة واستكشاف الأخطاء