الوحدة 5 · التشغيل في الإنتاج
الحركة إلى الداخل والمشاكل إلى الخارج: Gateway API والتشخيص
22 دقراءة 4 داختر بحكمة
الدرس في لمحة
وجّه حركة HTTPS العامة إلى notes-api باستخدام Gateway API، وحصّن الـ Pods الخاصة به بـ securityContext، وشخّص الـ Pods المعطوبة بسرعة بالأوامر get وdescribe وlogs وevents وkubectl debug.
لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.
شيئان يقفان بين notes-api والمستخدمين الحقيقيين. لا يمكن الوصول إليه من الإنترنت بعد، وحين ينكسر شيء في الثالثة فجرًا، تحتاج إلى معرفة ما هو خلال دقائق. هذا الدرس يغطّي الأمرين، وينتهي بإعدادات الأمان التي تكمل صورة الإنتاج.
إدخال حركة HTTP
Service من نوع LoadBalancer لكل تطبيق يصلح لخدمة واحدة. أما مع عشر خدمات، فأنت تدفع ثمن عشرة موازنات أحمال، وتدير عشر شهادات، والتوجيه حسب اسم المضيف أو المسار مستحيل. ما تريده نقطة دخول واحدة مشتركة توجّه طلبات HTTP إلى الـ Service الصحيح.
لسنوات كانت الإجابة Ingress. ما زال يعمل، لكن واجهة Ingress البرمجية مجمَّدة ولا تحصل على ميزات جديدة، والـ controller المجتمعي الواسع الانتشار ingress-nginx تقاعد في مارس 2026: لا إصدارات جديدة ولا إصلاحات أمنية. مشروع Kubernetes يوجّه العمل الجديد نحو Gateway API، المتاح رسميًا (GA) بالإصدار gateway.networking.k8s.io/v1. يُثبَّت كإضافة (الـ CRDs الخاصة به مع أداة تنفيذ (implementation)، مثل controller موازن الأحمال في منصّتك السحابية، أو Envoy Gateway، أو Cilium، أو Istio)، وكثير من الـ clusters المُدارة توفّره جاهزًا.
يقسّم Gateway API المهمّة حسب الأدوار:
- GatewayClass: أي أداة تنفيذ تتولّى الحركة. يضبطه مرة واحدة من يشغّل الـ cluster.
- Gateway: نقطة دخول فيها listeners ومنافذ وأسماء مضيفات وشهادات TLS. يملكها فريق المنصّة.
- HTTPRoute: قواعد التوجيه لتطبيق واحد، مرتبطة بـ Gateway. يملكها فريق التطبيق.
الـ Gateway الخاص بفريق المنصّة، مرة واحدة لكل cluster:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: public
namespace: infra
spec:
gatewayClassName: skylane-lb # provided by your implementation
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: "*.skylane.example"
tls:
mode: Terminate
certificateRefs:
- name: skylane-wildcard-tls
allowedRoutes:
namespaces:
from: Allوالـ route الخاص بك، في الـ namespace notes:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: notes-api
namespace: notes
spec:
parentRefs:
- name: public
namespace: infra
hostnames:
- notes.skylane.example
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: notes-api
port: 80الأمر kubectl describe httproute notes-api يُظهر تحت Status هل قبل الـ Gateway هذا الـ route. وإذا كنت تنقل كائنات Ingress موجودة، فالأداة ingress2gateway تحوّلها، بما في ذلك كثير من annotations الخاصة بـ ingress-nginx.
دليل التشخيص
حين يسيء notes-api التصرّف، قاوم التخمين. اتّبع الترتيب نفسه في كل مرة، ودع كل خطوة تختار التالية:
kubectl get pods -l app.kubernetes.io/name=notes-api
kubectl describe pod <pod>
kubectl logs <pod> --previous
kubectl events --for pod/<pod> --types=Warningالعمود STATUS يخبرك أين تنظر:
| الحالة | ماذا تعني | أين تنظر |
|---|---|---|
Pending |
لا يوجد node يستطيع استقبال الـ Pod | describe: أحداث مثل Insufficient cpu أو node selectors غير مطابقة |
ImagePullBackOff |
لا يستطيع الـ kubelet سحب الـ image | describe: tag خاطئ، أو بيانات اعتماد سحب مفقودة، أو معمارية خاطئة |
CreateContainerConfigError |
مفتاح ConfigMap أو Secret مُشار إليه مفقود | describe يسمّي المفتاح |
CrashLoopBackOff |
التطبيق يبدأ ويخرج، مرارًا | logs --previous، مع رمز الخروج في describe (137 = نفاد ذاكرة أو قتل) |
Running مع 0/1 جاهز |
الـ readiness probe يفشل | describe يُظهر إخفاقات الـ probe؛ تحقّق مما يعتمد عليه /readyz |
إذا كانت الـ Pods سليمة لكن الطلبات تفشل، فاعمل من الداخل إلى الخارج: kubectl get endpointslices -l kubernetes.io/service-name=notes-api (الفراغ يعني مشكلة في الـ selector أو الجاهزية)، ثم حالة الـ route، ثم الـ Gateway.
الـ images من نوع distroless لا تحتوي على shell، فلا يفيد kubectl exec. ألحِق ephemeral debug container (container تشخيص مؤقتًا) بدلًا من ذلك. ينضمّ إلى الـ network namespace الخاصة بالـ Pod، ومع --target يستطيع رؤية عمليات التطبيق أيضًا:
kubectl debug -it <pod> --image=busybox:1.37 --target=api
# inside: nslookup notes-db, wget -qO- localhost:3000/readyz, psالقطعة الأخيرة: الـ securityContext
في الدرس 2.2 شغّلت الـ image كمستخدم غير root ووعدت بفرض ذلك في الـ cluster. ها هو ذا، على container notes-api:
securityContext:
runAsNonRoot: true
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]الحقل runAsNonRoot يجعل الـ kubelet يرفض بدء الـ container إذا كانت الـ image ستعمل بـ UID 0. إنه يفحص UID رقميًا، لذا فإن image يكون فيها USER اسمًا مثل node تفشل بـ CreateContainerConfigError؛ اكتب USER 1000 في الـ Dockerfile أو أضف runAsUser: 1000 هنا. وأعطِ التطبيق volume من نوع emptyDir في /tmp إن احتاج إلى مساحة مؤقتة تحت نظام ملفات جذري للقراءة فقط.
بهذا يكتمل مسار notes-api: بناء multi-stage يستفيد من الـ cache، وimage صغيرة بمستخدم غير root مثبّتة بالـ digest ومفحوصة، وبيئة Compose للتطوير، وDeployment مع probes وموارد وتوسّع تلقائي وrollouts آمنة وroute عبر Gateway وsecurity context محصّن. كل واحد من هذه الإعدادات موجود في الملف لأن حادثًا ما علّم أحدهم أنه ضروري. حافظ على هذه العادة: حين ينكسر شيء، اسأل أي إعداد كان سيجعله مستحيلًا، وأضِفه.
أهم النقاط
- يقسّم Gateway API الحركة الخارجية إلى GatewayClass (أداة التنفيذ)، وGateway (نقطة الدخول وTLS)، وHTTPRoute (قواعد التوجيه لتطبيق ما)، فيملك كلٌّ من فريق المنصّة وفريق التطبيق جزأه.
- الـ Ingress ما زال يعمل لكنه مجمَّد، والـ controller المجتمعي ingress-nginx تقاعد في مارس 2026؛ والإعدادات الجديدة يجب أن تستخدم Gateway API.
- اقرأ حالة الـ Pod أولًا، ثم
kubectl describeللأحداث، ثمkubectl logs --previousللانهيارات؛ فالحالة تخبرك بالأداة التي تلجأ إليها. - الأمر
kubectl debug --targetيُلحق container مؤقتًا فيه أدوات بـ Pod يعمل، وهكذا تفحص الـ images من نوع distroless. - Pod الإنتاج يعمل كمستخدم غير root، دون تصعيد للصلاحيات، مع نظام ملفات جذري للقراءة فقط، وبعد إسقاط كل الـ capabilities.