> ## Content Index
> Fetch the complete content index at: https://arabroot.io/llms.txt
> Use this file to discover other available public pages before exploring further.

# مصطلحات Kubernetes مشروحة بالترتيب، من Pod إلى Ingress
- URL: https://arabroot.io/articles/مصطلحات-kubernetes-بالرسم/
- Published: 2026-10-06T08:45:00.000Z
- Updated: 2026-10-09T09:43:45.000Z
- Description: شرح مصطلحات Kubernetes بالترتيب الذي يبني بعضها على بعض، مع رسم لكل مفهوم وملفات YAML حقيقية.
- Author: فريق عرب رووت
- Tags: الأدلة التقنية, DevOps والتشغيل, DevOps وCI/CD, Docker والحاويات, الاستضافة الذاتية

عندما تقرأ أول مرة عن Kubernetes تقابل في الصفحة الأولى عشرين كلمة جديدة: Pod وNode وDeployment وReplicaSet وService وIngress وNamespace، وكل شرح يفترض أنك تعرف الكلمات الأخرى، فتدور في دائرة. وفي [هل تحتاج Kubernetes فعلاً؟](https://arabroot.io/articles/%D9%87%D9%84-%D8%AA%D8%AD%D8%AA%D8%A7%D8%AC-kubernetes-%D9%81%D8%B9%D9%84%D8%A7/) ناقشنا متى يستحق Kubernetes تكلفته، أما هذا المقال فيشرح المصطلحات نفسها بالترتيب الذي يبني بعضها على بعض، ولكل مفهوم رسم يوضحه قبل الكلام، ومعه ملف YAML حقيقي ومقارنة بما تعرفه من Docker Compose.

وهذا المقال من [مرحلة Kubernetes وGitOps في خريطة الطريق](https://arabroot.io/start/#stage-kubernetes)، والمقالات العملية مثل تثبيت K3s وHelm والتخزين مع Longhorn مذكورة هناك، وسوف نتناول في هذا المقال ما يلي:

- العنقود Cluster والعقد Nodes وأجزاء التحكم Control Plane، وماذا يحدث عندما تنفذ `kubectl apply`.
- ال Pod وال ReplicaSet وال Deployment، وال Labels وال Selectors التي تربطها ببعض.
- ال Service بأنواعه، وIngress أم Gateway API لدخول الطلبات من الإنترنت.
- ConfigMap وSecret وNamespace.
- التخزين الدائم PersistentVolume وPersistentVolumeClaim، ثم StatefulSet وDaemonSet وJob وCronJob.
- الأوامر المباشرة Imperative أم ملفات YAML مع `kubectl apply`، وجدول يلخص كل المصطلحات.

### كيف ولدنا ملفات YAML في هذا المقال

لم نشغل عنقوداً حقيقياً في هذا المقال، والسبب أن المساحة على جهاز التجربة لم تسمح بتنزيل Docker Images جديدة ل K3s أو kind، لذلك اعتمدنا على مصدرين: الأول أداة kubectl نفسها، فأغلب أوامر الإنشاء فيها تقبل الخيار `--dry-run=client -o yaml` فتطبع ملف YAML الذي كانت سوف ترسله دون أن تتصل بأي سيرفر، وهذه طريقة مفيدة لك أيضاً لتبدأ ملفاتك منها، والثاني ملفات كتبناها بأيدينا على أمثلة [توثيق Kubernetes](https://kubernetes.io/docs/concepts/overview/?ref=arabroot.io) وتحققنا أنها YAML صحيح بمكتبة PyYAML. والنسخة التي استخدمناها:

```bash
kubectl version --client
```

والمخرج سوف يكون كما يلي:

```text
Client Version: v1.36.1
Kustomize Version: v5.8.1
```

## العنقود Cluster والعقد Nodes

العنقود Cluster هو مجموعة سيرفرات يديرها Kubernetes كأنها جهاز واحد كبير، وكل سيرفر فيها يسمى عقدة Node، سواء كان سيرفراً حقيقياً أو VPS أو جهازاً افتراضياً. والعقد نوعان: عقد التحكم التي تشغل أجزاء التحكم Control Plane وتقرر ما يحدث، والعقد العاملة Worker Nodes التي تشغل تطبيقاتك، وفي العناقيد الصغيرة مثل K3s على سيرفر واحد يقوم السيرفر نفسه بالدورين. والصورة التالية تبين الأجزاء كلها:

![مخطط عنقود: kubectl يتصل بأجزاء التحكم وفيها kube-apiserver في الأعلى ومن تحته etcd وkube-scheduler وcontroller-manager، مع عبارة ثلاثة سيرفرات للتوفر العالي وعدد فردي لأعضاء etcd، وبجانبها ثلاث عقد عاملة على كل منها Pods فيها Containers، ثم kubelet وkube-proxy وcontainerd، وسهم من kubelet إلى أجزاء التحكم](https://arabroot.io/content/images/2026/10/k8s-terms-01-cluster.webp)

أجزاء التحكم تقرر، وال Kubelet على كل عقدة ينفذ

### ما الذي يعمل على كل عقدة؟

بحسب [صفحة مكونات Kubernetes](https://kubernetes.io/docs/concepts/overview/components/?ref=arabroot.io) يعمل على كل عقدة ثلاثة أشياء:

- **ال Kubelet:** الوكيل الذي يتأكد أن ال Pods المطلوبة على هذه العقدة تعمل بال Containers التي فيها، فهو يسأل ال API Server عما يجب أن يعمل هنا، ويشغله، ويرسل الحالة.
- **kube-proxy:** يضبط قواعد الشبكة على العقدة حتى يصل الطلب الموجه إلى Service إلى أحد ال Pods خلفه، ويصفه التوثيق بأنه اختياري لأن بعض إضافات الشبكة تقوم بعمله بنفسها.
- **بيئة تشغيل ال Containers Container Runtime:** البرنامج الذي يشغل ال Containers فعلاً، مثل containerd أو CRI-O، ويتحدث معه ال Kubelet عبر واجهة CRI. ولاحظ أن Kubernetes لم يعد يتحدث مع Docker Engine مباشرة منذ أزيل ال dockershim في النسخة 1.24 كما في [أسئلة dockershim الشائعة](https://kubernetes.io/blog/2022/02/17/dockershim-faq/?ref=arabroot.io)، لكن أي Docker Image تبنيها ب Docker تعمل عليه لأنها بالصيغة القياسية نفسها.

## أجزاء التحكم Control Plane

أجزاء التحكم هي العقل الذي يحفظ ما طلبته ويحاول أن يجعل العنقود يطابقه، وهي أربعة أجزاء أساسية بحسب الصفحة نفسها:

- **ال API Server واسمه kube-apiserver:** الباب الوحيد إلى العنقود، فكل أمر من kubectl وكل متحكم وكل Kubelet يتحدث معه وحده، وهو الذي يتحقق من الطلب وصلاحيته ثم يحفظه، ولذلك فسجل التدقيق الذي شرحناه في [سجل تدقيق Kubernetes API](https://arabroot.io/articles/%D8%B3%D8%AC%D9%84-%D8%A7%D9%84%D8%AA%D8%AF%D9%82%D9%8A%D9%82-audit-trail-%D9%85%D9%86-%D9%81%D8%B9%D9%84-%D9%85%D8%A7%D8%B0%D8%A7-%D9%88%D9%85%D8%AA%D9%89/#kubernetes) يكتبه هو.
- **etcd:** قاعدة بيانات مفتاح وقيمة Key-Value يحفظ فيها ال API Server حالة العنقود كلها، وتعمل بالإجماع عبر خوارزمية Raft التي شرحناها خطوة خطوة في [الأنظمة الموزعة](https://arabroot.io/articles/%D8%A7%D9%84%D8%A7%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D9%85%D9%88%D8%B2%D8%B9%D8%A9-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-cap-%D9%88%D8%A7%D9%84%D8%A7%D8%AA%D8%B3%D8%A7%D9%82-%D9%88%D8%A7%D9%84%D8%A7%D8%AC%D9%85%D8%A7%D8%B9/#raft)، وتراها تعمل في [تجربة عنقود etcd من ثلاثة سيرفرات](https://arabroot.io/articles/%D8%A7%D9%84%D8%A7%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D9%85%D9%88%D8%B2%D8%B9%D8%A9-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-cap-%D9%88%D8%A7%D9%84%D8%A7%D8%AA%D8%B3%D8%A7%D9%82-%D9%88%D8%A7%D9%84%D8%A7%D8%AC%D9%85%D8%A7%D8%B9/#lab)، ولذلك يكون عدد أعضائها فردياً، وإذا فقدت etcd ونسختها الاحتياطية فقدت العنقود.
- **المجدول kube-scheduler:** يبحث عن كل Pod لم تحدد له عقدة ويختار له عقدة مناسبة بحسب الموارد التي يطلبها والقيود التي تكتبها.
- **مدير المتحكمات kube-controller-manager:** يشغل المتحكمات Controllers، وكل متحكم حلقة تراقب نوعاً من الكائنات وتصلح الفرق بين الحالة المطلوبة والحالة الحالية، فمتحكم ال Deployment ينشئ ReplicaSets، ومتحكم ال ReplicaSet ينشئ Pods، ومتحكم العقد يلاحظ العقدة التي توقفت.

ويوجد جزء خامس اختياري هو cloud-controller-manager، وهو الذي يربط العنقود بمزود السحابة، فينشئ مثلاً موزع حمل حقيقياً عندما تطلب Service من نوع LoadBalancer. والصورة التالية تبين كيف تعمل هذه الأجزاء معاً عندما ترسل ملفاً:

![سبع خطوات مرقمة: kubectl apply يرسل الملف إلى API server الذي يتحقق منه ويحفظه في etcd، ثم متحكم ال Deployment ينشئ ReplicaSet وثلاثة Pods، ثم المجدول يختار node-2، ثم kubelet على node-2 يشغل ال Container عبر containerd، ثم سهم أخضر يعيد الحالة Running إلى ال API Server، مع ملاحظة أن المتحكمات والمجدول لا يتحدثون مع بعضهم وإنما يراقبون ال API Server](https://arabroot.io/content/images/2026/10/k8s-terms-02-apply-flow.webp)

كل جزء يراقب ال API Server ويكتب فيه، ولا يتحدث مع الأجزاء الأخرى مباشرة

في الصورة أعلاه لاحظ التالي:

- kubectl لا يشغل شيئاً بنفسه، فهو يرسل ما تريده إلى ال API Server ثم ينتهي عمله، ولذلك ينجح الأمر قبل أن يعمل التطبيق فعلاً، وتحتاج `kubectl get pods` أو `kubectl rollout status` لتعرف النتيجة.
- إذا توقفت أجزاء التحكم كلها تبقى ال Containers التي تعمل الآن تعمل، لأن ال Kubelet على كل عقدة يشغلها، لكن لا يوجد من يصلح أو يجدول أو يقبل ملفاً جديداً حتى تعود.

## ال Pod: أصغر شيء تشغله

ال Pod هو أصغر وحدة تنشئها وتديرها في Kubernetes، وهو بحسب [توثيق ال Pods](https://kubernetes.io/docs/concepts/workloads/pods/?ref=arabroot.io) مجموعة من Container واحد أو أكثر يشتركون في التخزين والشبكة ومعهم مواصفات تشغيلهم. وأغلب ال Pods فيها Container واحد، وأحياناً تضيف بجانبه Container مساعداً Sidecar يرسل السجلات أو يجدد شهادة، والصورة التالية تبين ما يشترك فيه:

![Pod باسم web-7c9b-x2kq فيه Container للتطبيق على المنفذ 8080 وContainer اسمه log-shipper يتصلان عبر localhost، ولهما عنوان واحد 10.42.1.7 وVolume مشترك، وPod آخر يتصل بهذا العنوان، وفي الجزء الثاني Pod قديم عليه علامة خطأ يستبدل ب Pod جديد اسمه web-7c9b-p8zt وعنوانه 10.42.2.9 مع عبارة اسم جديد وعنوان جديد وملفات محلية فارغة](https://arabroot.io/content/images/2026/10/k8s-terms-03-pod.webp)

ال Pod له عنوان واحد، وعندما يستبدل يأتي باسم وعنوان جديدين

والملف التالي ينشئ Pod فيه Container واحد:

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: web
  labels:
    app: web
spec:
  containers:
  - name: app
    image: nginx:1.31-alpine
    ports:
    - containerPort: 80
```

في الإعداد أعلاه لاحظ التالي:

- كل كائن في Kubernetes له أربعة أجزاء: `apiVersion` و`kind` لنوعه، و`metadata` لاسمه وال Labels، و`spec` لما تريده منه، ويضيف Kubernetes جزءاً خامساً هو `status` يكتب فيه الحالة الحالية.
- لا تنشئ Pods بهذا الشكل في الإنتاج Production، والسبب أن هذا ال Pod إذا حذف أو توقفت عقدته لا يعيده أحد، فهو يشبه `docker run` دون `--restart`، والذي يعيده هو ال Deployment في القسم التالي.
- عنوان ال Pod يتغير عند كل استبدال، فلا تكتب عنوانه في أي إعداد، واستخدم Service كما سيأتي.

## ال ReplicaSet وال Deployment

ال ReplicaSet كائن وظيفته واحدة: أن يبقى عدد محدد من ال Pods المتطابقة يعمل دائماً، فإذا طلبت ثلاثة وحذف أحدها أنشأ بديلاً. وال Deployment فوقه، فهو يملك ReplicaSet لكل نسخة من إعداد التطبيق، وعندما تغير ال Docker Image ينشئ ReplicaSet جديداً ويكبره تدريجياً ويصغر القديم، كما في [توثيق ال Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/?ref=arabroot.io). والصورة التالية تبين التحديث من 1.4.1 إلى 1.4.2:

![Deployment اسمه web بأربع نسخ يتغير من shop:1.4.1 إلى 1.4.2، وتحته ReplicaSet قديم web-5f6d بلون رمادي ينقص من أربعة إلى صفر، وReplicaSet جديد web-7c9b بلون أخضر يزيد من صفر إلى أربعة على خمس مراحل، مع maxSurge 25% أي خمسة Pods على الأكثر وmaxUnavailable 25% أي ثلاثة جاهزة على الأقل، والأمر kubectl rollout undo يعيد القديم](https://arabroot.io/content/images/2026/10/k8s-terms-04-deployment.webp)

ال Deployment ينقل ال Pods من ال ReplicaSet القديم إلى الجديد ويحتفظ بالقديم للتراجع

والأمر التالي يولد ملف Deployment بثلاث نسخ دون أن يتصل بأي عنقود:

```bash
kubectl create deployment web --image=nginx:1.31-alpine --replicas=3 --port=80 --dry-run=client -o yaml
```

والمخرج سوف يكون كما يلي:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: web
  name: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  strategy: {}
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - image: nginx:1.31-alpine
        name: nginx
        ports:
        - containerPort: 80
        resources: {}
status: {}
```

في المخرج أعلاه لاحظ التالي:

- الجزء `template` هو قالب ال Pod نفسه الذي رأيته في القسم السابق، وال Deployment ينشئ منه ثلاث نسخ.
- الجزء `selector.matchLabels` يقول إن ال Pods التي يملكها هذا ال Deployment هي التي تحمل `app: web`، ولذلك يجب أن يطابق ال Labels في القالب، وهذا ما نشرحه في القسم التالي.
- `strategy: {}` فارغة، فيستخدم Kubernetes الاستراتيجية الافتراضية RollingUpdate بقيم `maxSurge` و`maxUnavailable` تساوي 25%، و`resources: {}` فارغة أيضاً، وفي الإنتاج أضف طلبات الموارد Requests وحدودها Limits حتى يعرف المجدول أين يضع ال Pod.
- لا تعدل ال ReplicaSet الذي يملكه Deployment بيدك، فالتوثيق ينص على ذلك صراحة، والسبب أن ال Deployment سوف يعيده إلى ما في ملفه.

## ال Labels وال Selectors

ال Labels أزواج من مفتاح وقيمة تلصقها على أي كائن، مثل `app: web` و`env: prod`، وال Selector شرط يختار الكائنات التي تحمل Labels معينة، كما في [توثيق ال Labels](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/?ref=arabroot.io). وهذه هي الطريقة التي يجد بها كل جزء في Kubernetes ما يخصه، فال Deployment لا يعرف ال Pods بأسمائها، وال Service لا يعرف عناوينها، وإنما يبحث كل منهما عن ال Labels، والصورة التالية تبين ذلك:

![Selector فيه app=web وenv=prod، وأربعة Pods: اثنان يحملان app=web وenv=prod باللون الأخضر وعليهما علامة صح، وواحد يحمل app=web وenv=staging وآخر يحمل app=db وenv=prod باللون الرمادي وعليهما علامة خطأ، مع عبارة يختار ال Pod فقط إذا حمل كل ال Labels في ال Selector](https://arabroot.io/content/images/2026/10/k8s-terms-05-labels.webp)

يختار ال Selector ال Pod الذي يحمل كل ال Labels المطلوبة فقط

وتستخدمها أنت أيضاً من سطر الأوامر، فالأمر `kubectl get pods -l app=web,env=prod` يعرض ال Pods التي تحمل الاثنين معاً. ولاحظ أن خطأ صغيراً في ال Labels، مثل `app: web` في ال Service و`app: Web` في ال Pods، يجعل ال Service لا يجد أحداً فيرد بخطأ دون أن يظهر أي شيء غريب في ال Pods نفسها، وهذا من أكثر الأخطاء شيوعاً عند البداية.

## ال Service وأنواعه

ال Service يعطي مجموعة Pods يختارها ب Selector اسماً ثابتاً وعنواناً ثابتاً، ويوزع الطلبات على الجاهزة منها، وله في DNS داخل العنقود اسم بالشكل `web.shop.svc.cluster.local` كما في [توثيق DNS](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/?ref=arabroot.io). وهذا يشبه اسم الخدمة في Compose الذي شرحناه في [شبكات Docker](https://arabroot.io/articles/%D8%B4%D8%A8%D9%83%D8%A7%D8%AA-docker-%D9%88%D8%A3%D9%81%D8%B6%D9%84-%D8%A7%D9%84%D9%85%D9%85%D8%A7%D8%B1%D8%B3%D8%A7%D8%AA/#compose-networks)، والفرق أن ال Pods قد تكون على عقد مختلفة. وأنواعه الأربعة في [توثيق ال Service](https://kubernetes.io/docs/concepts/services-networking/service/?ref=arabroot.io) تحدد من أين يأتي الطلب، والصورة التالية تبينها:

![أربع لوحات: ClusterIP الافتراضي حيث Pod داخلي يتصل بالعنوان 10.43.0.15 الذي يوزع على ثلاثة Pods، وNodePort حيث مستخدم يتصل بالمنفذ 30080 على عقدتين، وLoadBalancer حيث مستخدم يمر بموزع حمل سحابي أو ServiceLB ثم العقد ثم ال Pods، وExternalName حيث الاسم db اسم بديل CNAME إلى db.example.com خارج العنقود](https://arabroot.io/content/images/2026/10/k8s-terms-06-services.webp)

ClusterIP للداخل، وNodePort منفذ على كل عقدة، وLoadBalancer عنوان خارجي، وExternalName اسم بديل

- **ClusterIP:** النوع الافتراضي، عنوان داخلي لا يصل إليه أحد من خارج العنقود، وهو المناسب لقاعدة البيانات وللخدمات الداخلية، ويشبه خدمة في Compose لم تنشر منفذها.
- **NodePort:** يفتح منفذاً من النطاق الافتراضي 30000 إلى 32767 على كل عقدة، ويشبه `ports:` في Compose، ولا ينصح به للإنترنت مباشرة لأنه يفتح المنفذ على كل العقد.
- **LoadBalancer:** يطلب من مزود السحابة موزع حمل بعنوان خارجي أمام ال NodePorts، وفي K3s يقوم بهذا ServiceLB على السيرفر نفسه.
- **ExternalName:** لا يوجه أي حركة، وإنما يعيد اسماً بديلاً CNAME لخدمة خارج العنقود.

ويوجد شكل خاص اسمه Headless Service تكتب فيه `clusterIP: None`، فلا يأخذ عنواناً واحداً، وإنما يعيد DNS عناوين ال Pods نفسها، وهو الذي يحتاجه ال StatefulSet كما سيأتي. والأمر التالي يولد Service من نوع NodePort:

```bash
kubectl create service nodeport web --tcp=80:80 --node-port=30080 --dry-run=client -o yaml
```

والمخرج سوف يكون كما يلي:

```yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: web
  name: web
spec:
  ports:
  - name: 80-80
    nodePort: 30080
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: web
  type: NodePort
status:
  loadBalancer: {}
```

في المخرج أعلاه لاحظ التالي:

- `selector: app: web` هو الذي يربط ال Service بال Pods التي أنشأها ال Deployment في القسم السابق، فلا يوجد أي ذكر لأسماء ال Pods.
- `port` هو منفذ ال Service نفسه، و`targetPort` منفذ ال Container، و`nodePort` المنفذ المفتوح على كل عقدة، وثلاثتها قد تكون مختلفة.

## Ingress أم Gateway API؟

ال Service من نوع LoadBalancer يعطي كل خدمة عنواناً خارجياً، وهذا مكلف وغير عملي مع عشر خدمات تحت نطاق واحد، ولذلك يوجد مستوى أعلى يعمل كال Reverse Proxy الذي تعرفه: يستقبل كل الطلبات على المنفذين 80 و443 ويوجهها بحسب النطاق والمسار إلى ال Service المناسب. وفي Kubernetes طريقتان لذلك، والصورة التالية تضعهما بجانب بعضهما:

![على اليمين Ingress مجمد: مستخدم يصل إلى متحكم Ingress مثل Traefik أو HAProxy، وكائن Ingress واحد فيه النطاقات والمسارات وTLS يوجه shop.example.com إلى web و/api إلى api، مع عبارة مدعوم ولا تضاف إليه ميزات والإضافات في Annotations خاصة بكل متحكم. وعلى اليسار Gateway API: GatewayClass لمزود البنية، وGateway على المنفذ 443 لمشغل العنقود، وHTTPRoute لمطور التطبيق، ثم Service web وService api](https://arabroot.io/content/images/2026/10/k8s-terms-07-ingress-gateway.webp)

Ingress كائن واحد، وGateway API ثلاثة كائنات موزعة بحسب الدور

الطريقة الأولى Ingress، وهي كائن واحد تكتب فيه النطاقات والمسارات والشهادات، ويقرؤه متحكم Ingress Controller تثبته أنت مثل Traefik الذي يأتي مع K3s. وتقول [صفحة Ingress في التوثيق](https://kubernetes.io/docs/concepts/services-networking/ingress/?ref=arabroot.io) إن واجهتها مجمدة ولن تتغير، وإن المشروع يوصي ب Gateway API بدلاً منها، مع بقائها مدعومة دون نية لحذفها. وأشهر متحكم لها Ingress NGINX [توقف تطويره](https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement/?ref=arabroot.io) بعد مارس 2026، فإذا بدأت اليوم فلا تختره. والأمر التالي يولد Ingress:

```bash
kubectl create ingress web --class=traefik --rule="app.example.com/*=web:80" --dry-run=client -o yaml
```

والمخرج سوف يكون كما يلي:

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web
spec:
  ingressClassName: traefik
  rules:
  - host: app.example.com
    http:
      paths:
      - backend:
          service:
            name: web
            port:
              number: 80
        path: /
        pathType: Prefix
status:
  loadBalancer: {}
```

والطريقة الثانية Gateway API، وهي بحسب [صفحة Gateway API](https://kubernetes.io/docs/concepts/services-networking/gateway/?ref=arabroot.io) إضافة تثبت كتعريفات CRD، وفيها أربعة أنواع مستقرة: GatewayClass وGateway وHTTPRoute وGRPCRoute، وفكرتها أن المسؤولية موزعة على ثلاثة أدوار: مزود البنية يعرف GatewayClass، ومشغل العنقود ينشئ Gateway بالمنافذ والشهادات، ومطور التطبيق يكتب HTTPRoute لتطبيقه في Namespace الخاص به، وآخر إصدار من [مشروع Gateway API](https://github.com/kubernetes-sigs/gateway-api/releases?ref=arabroot.io) عند كتابة المقال هو v1.6.3\. والملف التالي يكتب الدخول نفسه بطريقة Gateway API:

```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: public
  namespace: infra
spec:
  gatewayClassName: traefik
  listeners:
  - name: https
    protocol: HTTPS
    port: 443
    hostname: "*.example.com"
    tls:
      certificateRefs:
      - name: wildcard-example-com
    allowedRoutes:
      namespaces:
        from: All
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: web
  namespace: shop
spec:
  parentRefs:
  - name: public
    namespace: infra
  hostnames:
  - shop.example.com
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: web
      port: 80
```

في الإعداد أعلاه لاحظ التالي:

- ال Gateway في Namespace اسمه `infra` يملكه مشغل العنقود، وفيه الشهادة، وال HTTPRoute في Namespace التطبيق `shop`، فيستطيع المطور أن يغير مساراته دون أن يلمس الشهادة.
- `allowedRoutes.namespaces.from: All` يسمح لل Routes من كل ال Namespaces بأن ترتبط بهذا ال Gateway، وفي عنقود مشترك بين فرق قيده ب Selector.
- اسم `gatewayClassName` يحدده المتحكم الذي ثبته، فراجع توثيقه قبل أن تنسخ هذا الملف.

## ConfigMap وSecret

في [البيئات ومعاينة الميزات](https://arabroot.io/articles/%D8%A7%D9%84%D8%A8%D9%8A%D8%A6%D8%A7%D8%AA-%D9%88%D9%85%D8%B9%D8%A7%D9%8A%D9%86%D8%A9-%D8%A7%D9%84%D9%85%D9%8A%D8%B2%D8%A7%D8%AA-%D9%88-gitops-%D9%84%D9%84%D9%81%D8%B1%D9%82/#config-per-env) ذكرنا أن الكود لا يختلف بين البيئات والإعداد هو الذي يختلف، وفي Kubernetes يوضع هذا الإعداد في كائنين: ConfigMap للقيم العادية، وهو بحسب [توثيق ال ConfigMap](https://kubernetes.io/docs/concepts/configuration/configmap/?ref=arabroot.io) كائن يحفظ بيانات غير سرية بشكل مفتاح وقيمة ولا يتجاوز حجمه 1 ميغابايت، وSecret للباسوردات والمفاتيح. والاثنان يصلان إلى ال Pod كمتغيرات بيئة Environment Variables أو كملفات داخل ال Container، والصورة التالية تبين ذلك:

![ConfigMap باسم web-config فيه APP_MODE=production وSecret باسم db-pass فيه password بقيمة UzNjcjN0LVZhbHVl يدخلان إلى Pod كمتغيرات بيئة وكملف /etc/app/config.yaml، وخط متقطع من ال Secret إلى etcd مع علامة حمراء base64 ليس تشفيراً، وعبارات تحفظ دون تشفير افتراضياً، فعل التشفير وقيد من يقرأ ال Secrets](https://arabroot.io/content/images/2026/10/k8s-terms-08-config-secret.webp)

ال Docker Image نفسها في كل بيئة، والإعداد يأتي من ConfigMap وSecret

والأمران التاليان يولدان ConfigMap وSecret:

```bash
kubectl create configmap web-config --from-literal=APP_MODE=production --from-literal=LOG_LEVEL=info --dry-run=client -o yaml
kubectl create secret generic db-pass --from-literal=password=S3cr3t-Value --dry-run=client -o yaml
```

والمخرج سوف يكون كما يلي:

```yaml
apiVersion: v1
data:
  APP_MODE: production
  LOG_LEVEL: info
kind: ConfigMap
metadata:
  name: web-config
---
apiVersion: v1
data:
  password: UzNjcjN0LVZhbHVl
kind: Secret
metadata:
  name: db-pass
```

لاحظ أن قيمة الباسورد في ال Secret ليست مشفرة، وإنما مكتوبة بترميز base64، والأمر التالي يعيدها كما هي:

```bash
echo 'UzNjcjN0LVZhbHVl' | base64 -d
```

والمخرج سوف يكون كما يلي:

```text
S3cr3t-Value
```

🛑

لا تضع ملف Secret بهذا الشكل في مستودع Git، والسبب أن base64 ترميز Encoding وليس تشفيراً Encryption كما شرحنا في [الترميز Base64 وBase32 وBase58](https://arabroot.io/articles/%D8%A7%D9%84%D8%AA%D8%B1%D9%85%D9%8A%D8%B2-base64-%D9%88-base32-%D9%88-base58/#not-encryption)، وبحسب [توثيق ال Secrets](https://kubernetes.io/docs/concepts/configuration/secret/?ref=arabroot.io) فهي تحفظ في etcd دون تشفير بشكل افتراضي، وكل من يستطيع أن ينشئ Pod في ال Namespace يستطيع أن يقرأ ال Secrets فيه. فعل التشفير عند التخزين Encryption at Rest، وقيد الصلاحيات ب RBAC، واستخدم أداة مثل Sealed Secrets أو مخزن أسرار خارجي إذا أردت أن تحفظ الأسرار مع بقية الملفات.

## ال Namespace

ال Namespace مساحة أسماء داخل العنقود، فتستطيع أن تنشئ Deployment اسمه `web` في `shop` وآخر بالاسم نفسه في `shop-staging` دون تعارض، وتضبط الصلاحيات وحدود الموارد لكل واحدة على حدة. ويبدأ كل عنقود بأربع مساحات بحسب [توثيق ال Namespaces](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/?ref=arabroot.io): `default` التي يوصي التوثيق بألا تستخدمها في عنقود الإنتاج، و`kube-system` لكائنات النظام، و`kube-public` المقروءة للجميع، و`kube-node-lease` التي تحفظ نبض العقد حتى يعرف العنقود أن عقدة توقفت. والصورة التالية تبين التقسيم:

![عنقود واحد فيه أربع مساحات: default مع عبارة تجنبها للعمل الحقيقي، وkube-system فيها coredns وtraefik، وshop فيها web وdb والاسم web.shop.svc، وshop-staging فيها web وdb والاسم web.shop-staging.svc، ومربع متقطع للكائنات التي لا تتبع أي Namespace: Nodes وPersistentVolumes وStorageClasses وNamespaces، مع عبارة ال Namespace مجلد للأسماء وليس جداراً أمنياً بنفسه](https://arabroot.io/content/images/2026/10/k8s-terms-09-namespaces.webp)

نفس الاسم في مساحتين دون تعارض، وبعض الكائنات للعنقود كله

والأمر التالي يولد Namespace:

```bash
kubectl create namespace shop --dry-run=client -o yaml
```

والمخرج سوف يكون كما يلي:

```yaml
apiVersion: v1
kind: Namespace
metadata:
  name: shop
spec: {}
status: {}
```

وقد يتساءل البعض: هل يكفي أن أضع كل فريق في Namespace لأعزله عن الآخرين؟ والإجابة لا، فال Pods في مساحات مختلفة تستطيع أن تتصل ببعضها بشكل افتراضي، والعزل الحقيقي يحتاج صلاحيات RBAC لكل مساحة وسياسات شبكة NetworkPolicy تمنع الاتصال، وهذا موضوع مقال قادم في المرحلة.

## التخزين الدائم PersistentVolume وPersistentVolumeClaim

الملفات داخل ال Container تختفي عندما يستبدل ال Pod، وفي Compose تحل هذا ب Volume على قرص السيرفر، أما في Kubernetes فال Pod قد يبدأ على عقدة أخرى، ولذلك فصل Kubernetes التخزين إلى كائنين بحسب [توثيق التخزين الدائم](https://kubernetes.io/docs/concepts/storage/persistent-volumes/?ref=arabroot.io): ال PersistentVolume واختصاراً PV، وهو مساحة تخزين حقيقية في العنقود، وال PersistentVolumeClaim واختصاراً PVC، وهو طلب يكتبه التطبيق «أريد 10 غيغابايت يكتب فيها جهاز واحد». وبينهما StorageClass تنشئ PV تلقائياً عندما يأتي طلب لا يوجد ما يطابقه، والصورة التالية تبين الخطوات:

![أربع خطوات مرقمة: Pod باسم db-0 يشير إلى PVC اسمه data-db-0 بحجم 10Gi وReadWriteOnce، ثم StorageClass تنشئ PV عند الطلب، ثم يرتبط الطلب ب PV اسمه pvc-3f2a بحجم 10Gi، ثم يقع ال PV على قرص حقيقي قد يكون مساراً محلياً أو Longhorn أو قرصاً سحابياً](https://arabroot.io/content/images/2026/10/k8s-terms-10-storage.webp)

التطبيق يطلب، وال StorageClass تنشئ، وال PV يبقى بعد حذف ال Pod

والملف التالي يطلب 10 غيغابايت من ال StorageClass التي يأتي بها K3s:

```yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: uploads
  namespace: shop
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: local-path
  resources:
    requests:
      storage: 10Gi
```

في الإعداد أعلاه لاحظ التالي:

- طريقة الوصول `ReadWriteOnce` تعني أن عقدة واحدة تكتب فيه، وهناك `ReadOnlyMany` للقراءة من عدة عقد، و`ReadWriteMany` للكتابة من عدة عقد، و`ReadWriteOncePod` ل Pod واحد فقط، وليست كل أنواع التخزين تدعم كل الطرق، فالقرص Block Storage يدعم غالباً RWO فقط كما شرحنا في [أنواع التخزين](https://arabroot.io/articles/%D8%A7%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-block-%D9%88-file-%D9%88-object-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/).
- `local-path` في K3s يضع البيانات على قرص العقدة نفسها، فإذا توقفت العقدة لا تنتقل البيانات معها، ولهذا تحتاج في عنقود حقيقي تخزيناً موزعاً مثل Longhorn، وهو مقال قادم في المرحلة.
- عند حذف ال PVC تقرر سياسة الاسترجاع Reclaim Policy مصير البيانات: `Delete` يحذف القرص، و`Retain` يبقيه لتستعيده بيدك، فتأكد من القيمة قبل أن تحذف أي PVC لقاعدة بيانات.

## StatefulSet: لقواعد البيانات وما يشبهها

ال Pods التي ينشئها Deployment متطابقة وقابلة للتبديل، أسماؤها عشوائية ويشترك كلها في قالب واحد، وهذا مناسب لتطبيق ويب لا يحفظ شيئاً محلياً، أما قاعدة البيانات أو عنقود etcd أو Kafka فكل نسخة فيها لها هوية: هذه هي الرئيسية وتلك نسخة منها، ولكل واحدة بياناتها. ولذلك يوجد StatefulSet، وهو بحسب [توثيق ال StatefulSet](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/?ref=arabroot.io) يعطي كل Pod اسماً ثابتاً في الشبكة، وتخزيناً دائماً خاصاً به، وتشغيلاً وتحديثاً بالترتيب، والصورة التالية تقارنه بال Deployment:

![على اليمين Deployment فيه ثلاثة Pods بأسماء عشوائية web-7c9b-x2kq وweb-7c9b-p8zt وweb-7c9b-4mnd مع عبارة أسماء عشوائية وأي ترتيب وقرص مشترك أو لا يوجد، وعلى اليسار StatefulSet اسمه db فيه db-0 وdb-1 وdb-2 بأسماء DNS ثابتة مثل db-0.db.shop.svc، ولكل منها PVC خاص data-db-0 وdata-db-1 وdata-db-2، وأسهم تبين التشغيل بالترتيب من 0 إلى 2](https://arabroot.io/content/images/2026/10/k8s-terms-11-statefulset.webp)

إذا استبدل db-1 يعود بالاسم نفسه ويرتبط بالقرص نفسه

والجزء المهم في ملف ال StatefulSet هو `volumeClaimTemplates`، الذي ينشئ PVC لكل Pod بالاسم `data-db-0` ثم `data-db-1` وهكذا، ومعه `serviceName` الذي يشير إلى Headless Service يعطي كل Pod اسمه في DNS:

```yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: db
spec:
  serviceName: db
  replicas: 1
  selector:
    matchLabels:
      app: db
  template:
    metadata:
      labels:
        app: db
    spec:
      containers:
      - name: postgres
        image: postgres:18
        env:
        - name: POSTGRES_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-pass
              key: password
        ports:
        - containerPort: 5432
        volumeMounts:
        - name: data
          mountPath: /var/lib/postgresql
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi
```

ولاحظ أن StatefulSet بثلاث نسخ من PostgreSQL لا يعطيك عنقود قاعدة بيانات، فهو يشغل ثلاث قواعد مستقلة بأسماء ثابتة، أما النسخ المتماثل والانتقال عند العطل فتحتاج لهما أداة مثل Patroni أو مشغلاً Operator مثل CloudNativePG، وقد شرحنا ما يحتاجه العنقود الحقيقي في [عناقيد قواعد البيانات](https://arabroot.io/articles/%D8%B9%D9%86%D8%A7%D9%82%D9%8A%D8%AF-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/).

## DaemonSet وJob وCronJob

بقيت ثلاثة أنواع من الأحمال Workloads تقابلها كثيراً، والصورة التالية تبينها:

![على اليمين DaemonSet: أربع عقد على كل منها Pod أزرق، والعقدة الرابعة جديدة أضيف إليها الوكيل تلقائياً، مع عبارة نسخة على كل عقدة مثل وكيل السجلات والمراقبة والشبكة، وعلى اليسار CronJob بجدول 0 2 * * * وخط زمني لثلاثة أيام، في كل يوم Job، في اليوم الأول والثالث اكتمل بنجاح، وفي الثاني فشل ثم أعيدت المحاولة](https://arabroot.io/content/images/2026/10/k8s-terms-12-daemonset-jobs.webp)

نسخة على كل عقدة، ومهام تعمل حتى تنتهي في موعدها

- **DaemonSet:** يضمن نسخة من ال Pod على كل عقدة أو على مجموعة منها، وعندما تضاف عقدة جديدة يضاف إليها ال Pod تلقائياً، كما في [توثيق ال DaemonSet](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/?ref=arabroot.io)، ويستخدم لوكلاء جمع السجلات والمراقبة والشبكة.
- **Job:** ينشئ Pod أو أكثر ويعيد المحاولة حتى ينجح عدد محدد منها ثم يتوقف، كما في [توثيق ال Job](https://kubernetes.io/docs/concepts/workloads/controllers/job/?ref=arabroot.io)، ويناسب ترحيل قاعدة البيانات Migration قبل الإصدار أو معالجة دفعة من الملفات.
- **CronJob:** ينشئ Job في كل موعد من جدول بصيغة crontab، كما في [توثيق ال CronJob](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/?ref=arabroot.io)، الذي ينبه إلى أن الموعد تقريبي، فقد ينشأ Job مرتين أو لا ينشأ في حالات نادرة، ولذلك اكتب المهمة بحيث لا يضرها التكرار.

والملف التالي DaemonSet لوكيل سجلات يقرأ مجلد `/var/log` من كل عقدة:

```yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: log-agent
  namespace: kube-system
spec:
  selector:
    matchLabels:
      app: log-agent
  template:
    metadata:
      labels:
        app: log-agent
    spec:
      containers:
      - name: agent
        image: fluent/fluent-bit:5.1
        volumeMounts:
        - name: varlog
          mountPath: /var/log
          readOnly: true
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
```

والأمر التالي يولد CronJob ينسخ قاعدة البيانات كل ليلة في الثانية:

```bash
kubectl create cronjob db-backup --image=postgres:18 --schedule="0 2 * * *" --dry-run=client -o yaml -- sh -c 'pg_dump -h db -U app app > /backup/app.sql'
```

والمخرج سوف يكون كما يلي:

```yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: db-backup
spec:
  jobTemplate:
    metadata:
      name: db-backup
    spec:
      template:
        metadata: {}
        spec:
          containers:
          - command:
            - sh
            - -c
            - pg_dump -h db -U app app > /backup/app.sql
            image: postgres:18
            name: db-backup
            resources: {}
          restartPolicy: OnFailure
  schedule: 0 2 * * *
status: {}
```

في المخرج أعلاه لاحظ التالي:

- CronJob يحوي قالب Job، وال Job يحوي قالب Pod، فهي ثلاث طبقات، وكل طبقة لها متحكم خاص بها.
- `restartPolicy: OnFailure` يعني أن ال Container الذي يفشل يعاد في ال Pod نفسه، ولا يقبل ال Job القيمة `Always` لأن المهمة يجب أن تنتهي.
- هذا الملف بداية فقط، فالمسار `/backup` غير مربوط بأي Volume، وكلمة المرور غير موجودة، فأضف PVC أو ارفع النسخة إلى تخزين كائنات، وأضف Secret، ثم اختبر الاستعادة.

## الأوامر المباشرة أم ملفات YAML مع kubectl apply؟

في كل الأقسام السابقة استخدمنا أوامر `kubectl create` لنولد الملفات فقط، وهذا مقصود، فالتوثيق في [إدارة الكائنات](https://kubernetes.io/docs/concepts/overview/working-with-objects/object-management/?ref=arabroot.io) يذكر ثلاث طرق: الأوامر المباشرة Imperative Commands على الكائنات الحية، وهي مناسبة للتجربة لكنها لا تترك سجلاً يراجع، والأوامر على ملفات مثل `kubectl create -f`، والطريقة التصريحية Declarative بملفات في مجلد تطبقها ب `kubectl apply` وتراجع الفرق قبلها ب `kubectl diff`، وهي الموصى بها للإنتاج، والصورة التالية تبين الفرق:

![على اليمين أوامر مباشرة: kubectl create deployment وkubectl scale وkubectl set image تذهب إلى العنقود الحي مع عبارة سريعة للتجربة ولا يوجد ما يسجل لماذا أصبح العنقود هكذا، وعلى اليسار ملفات تصريحية: k8s/web.yaml في Git ثم kubectl diff ثم kubectl apply إلى العنقود حيث الحالة المطلوبة هي الملف، مع سهم يعود إلى Git وعبارات طلب دمج ومراجعة وgit log للتاريخ والتراجع، وArgo CD أو Flux يمكن أن ينفذ ال apply عنك، ولا تخلط الطريقتين على الكائن نفسه](https://arabroot.io/content/images/2026/10/k8s-terms-13-declarative.webp)

الأوامر للتجربة، والملفات في Git للإنتاج

وهي الفكرة نفسها التي شرحناها في [البنية كشيفرة IaC للمبتدئين](https://arabroot.io/articles/%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D8%A9-%D9%83%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-iac-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/): الحالة المطلوبة مكتوبة في ملفات، والأداة تقارنها بالواقع وتصلح الفرق، وعندما يتولى أداة مثل Argo CD تطبيق ما في المستودع تلقائياً يسمى ذلك GitOps، كما في [GitOps ببساطة](https://arabroot.io/articles/%D8%A7%D9%84%D8%A8%D9%8A%D8%A6%D8%A7%D8%AA-%D9%88%D9%85%D8%B9%D8%A7%D9%8A%D9%86%D8%A9-%D8%A7%D9%84%D9%85%D9%8A%D8%B2%D8%A7%D8%AA-%D9%88-gitops-%D9%84%D9%84%D9%81%D8%B1%D9%82/#gitops). ولاحظ تحذير التوثيق بأن الكائن الواحد يجب أن يدار بطريقة واحدة فقط، فإذا غيرت عدد النسخ بالأمر `kubectl scale` ثم طبقت الملف عاد العدد إلى ما في الملف، وهذا صحيح لأن الملف هو المرجع.

### ماذا يحدث إذا نفذت apply دون عنقود؟

لنكسر الأمر عمداً لنفهم ما يفعله، فالملف الذي ولدناه للتو ما زال على جهازنا، ولا يوجد أي عنقود في إعداد kubectl:

```bash
kubectl apply -f web-deploy.yaml
```

والمخرج سوف يكون كما يلي:

```text
error: error validating "web-deploy.yaml": error validating data: failed to download openapi: Get "http://localhost:8080/openapi/v2?timeout=32s": dial tcp [::1]:8080: connectex: No connection could be made because the target machine actively refused it.; if you choose to ignore these errors, turn validation off with --validate=false
```

ثم نجرب الخيار الذي يقترحه الخطأ مع `--dry-run=client`، لعل kubectl يكتفي بالملف:

```bash
kubectl apply --validate=false --dry-run=client -f web-deploy.yaml
```

والمخرج سوف يكون كما يلي:

```text
E1007 07:55:58.748956   26520 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"http://localhost:8080/api?timeout=32s\": dial tcp [::1]:8080: connectex: No connection could be made because the target machine actively refused it."
error: unable to recognize "web-deploy.yaml": Get "http://localhost:8080/api?timeout=32s": dial tcp [::1]:8080: connectex: No connection could be made because the target machine actively refused it.
```

في المخرج أعلاه لاحظ التالي:

- عندما لا يجد kubectl ملف إعداد `~/.kube/config` يحاول الاتصال بالعنوان `localhost:8080`، فإذا رأيت هذا العنوان في خطأ فالمشكلة أن kubectl لا يعرف أي عنقود، وليست في الملف.
- الخطأ الأول يأتي من التحقق Validation، فkubectl ينزل وصف الواجهات OpenAPI من ال API Server ليتحقق من الحقول قبل الإرسال.
- والخطأ الثاني أوضح: حتى مع `--dry-run=client` يحتاج kubectl أن يسأل ال API Server عن الأنواع التي يعرفها ليفهم أن `apps/v1 Deployment` نوع موجود، فكل شيء في Kubernetes يمر عبر ال API Server كما رأيت في الصورة الثانية، أما `kubectl create … --dry-run=client` فينجح لأنه يعرف أنواعه مسبقاً.

## كل المصطلحات في جدول واحد

| المصطلح               | ماذا يفعل                                     | أقرب شيء في Docker Compose                   |
| --------------------- | --------------------------------------------- | -------------------------------------------- |
| Cluster               | مجموعة سيرفرات يديرها Kubernetes كجهاز واحد   | لا يوجد، فCompose على سيرفر واحد             |
| Node                  | سيرفر في العنقود، للتحكم أو للعمل             | السيرفر الذي عليه Docker Engine              |
| Control Plane         | API Server وetcd والمجدول والمتحكمات          | Docker Engine نفسه                           |
| Kubelet               | يشغل ال Pods على العقدة ويرسل حالتها          | Docker Engine على السيرفر                    |
| Pod                   | Container أو أكثر بعنوان واحد                 | Container واحد                               |
| ReplicaSet            | يبقي عدداً محدداً من ال Pods                  | deploy.replicas                              |
| Deployment            | يملك ال ReplicaSets ويحدث ويتراجع             | خدمة في services:                            |
| Label وSelector       | يربط الكائنات ببعضها                          | اسم الخدمة                                   |
| Service               | اسم وعنوان ثابتان أمام ال Pods                | اسم الخدمة في الشبكة، وports: للنوع NodePort |
| Ingress وGateway API  | توجيه الطلبات من الإنترنت بحسب النطاق والمسار | ال Reverse Proxy مثل Traefik أو Caddy        |
| ConfigMap             | إعدادات غير سرية                              | environment: وconfigs:                       |
| Secret                | باسوردات ومفاتيح، بترميز base64               | secrets:                                     |
| Namespace             | مساحة أسماء داخل العنقود                      | اسم المشروع \-p                              |
| PV وPVC وStorageClass | تخزين دائم يطلبه التطبيق وينشأ تلقائياً       | volumes:                                     |
| StatefulSet           | Pods بأسماء ثابتة وقرص لكل منها               | خدمة قاعدة بيانات مع Volume خاص بها          |
| DaemonSet             | نسخة على كل عقدة                              | لا يوجد، وmode: global في Swarm              |
| Job وCronJob          | مهمة تنتهي، مرة أو في كل موعد                 | docker compose run مع cron على السيرفر       |
| kubectl apply         | يطبق الحالة المكتوبة في الملفات               | docker compose up -d                         |

## الخلاصة

وصلنا لنهاية الموضوع، وأهم ما فيه:

- العنقود سيرفرات بعضها للتحكم وبعضها للعمل، وكل شيء يمر عبر ال API Server الذي يحفظ الحالة في etcd، والمجدول يختار العقدة، والمتحكمات تصلح الفرق، وال Kubelet ينفذ على كل عقدة.
- ال Pod أصغر وحدة وعنوانه يتغير، فلا تنشئه وحده، وإنما عبر Deployment يملك ReplicaSet لكل نسخة ويحدث ويتراجع.
- ال Labels وال Selectors هي الرابط بين كل شيء، وخطأ حرف واحد فيها يجعل ال Service لا يجد ال Pods.
- ال Service يعطي اسماً ثابتاً، ونوعه يحدد من أين يأتي الطلب، وIngress مجمدة والمشروع يوصي ب Gateway API.
- ConfigMap للإعدادات وSecret للأسرار، وال Secret مكتوب ب base64 ومحفوظ دون تشفير افتراضياً، فلا تضعه في Git كما هو.
- PVC يطلب التخزين وPV هو التخزين، وStatefulSet يعطي كل Pod اسماً وقرصاً ثابتين، وDaemonSet نسخة على كل عقدة، وJob وCronJob للمهام التي تنتهي.
- أدر الإنتاج بملفات في Git تطبقها ب `kubectl apply`، واستخدم الأوامر المباشرة للتجربة فقط، ولا تخلط الطريقتين على الكائن نفسه.

[![](https://arabroot.io/content/images/2026/10/k8s-need-cover-cover.webp)دليلهل تحتاج Kubernetes فعلاً؟ متى يكفي Docker Composeما الذي يحله Kubernetes وما الذي يكلفك، ومتى يكفيك Docker Compose على سيرفر واحد أو بضعة سيرفرات.](https://arabroot.io/articles/%D9%87%D9%84-%D8%AA%D8%AD%D8%AA%D8%A7%D8%AC-kubernetes-%D9%81%D8%B9%D9%84%D8%A7/)[![](https://arabroot.io/content/images/2026/10/distributed-systems-cover-cover.webp)دليلالأنظمة الموزعة للمبتدئين: ماذا يحدث عندما تنقطع الشبكةلماذا تتصرف الأنظمة الموزعة بشكل غريب عند انقطاع الشبكة، وكيف تتفق السيرفرات على قرار واحد رغم ذلك.](https://arabroot.io/articles/%D8%A7%D9%84%D8%A7%D9%86%D8%B8%D9%85%D8%A9-%D8%A7%D9%84%D9%85%D9%88%D8%B2%D8%B9%D8%A9-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86-cap-%D9%88%D8%A7%D9%84%D8%A7%D8%AA%D8%B3%D8%A7%D9%82-%D9%88%D8%A7%D9%84%D8%A7%D8%AC%D9%85%D8%A7%D8%B9/)[![](https://arabroot.io/content/images/2026/10/environments-gitops-cover-cover.webp)دليلمن الفرع إلى الإنتاج: البيئات وGitOps للفرقكيف يختبر فريقك كل ميزة وحدها ويعرضها على العميل دون أن تصل ميزة غير جاهزة إلى الإنتاج.](https://arabroot.io/articles/%D8%A7%D9%84%D8%A8%D9%8A%D8%A6%D8%A7%D8%AA-%D9%88%D9%85%D8%B9%D8%A7%D9%8A%D9%86%D8%A9-%D8%A7%D9%84%D9%85%D9%8A%D8%B2%D8%A7%D8%AA-%D9%88-gitops-%D9%84%D9%84%D9%81%D8%B1%D9%82/)[![](https://arabroot.io/content/images/2026/10/iac-terms-cover-cover.webp)دليلمصطلحات البنية كشيفرة IaC التي تحتاجها قبل أي أداةما معنى أن تكتب بنيتك في ملفات داخل Git، ولماذا يفشل السكربت العادي عند تشغيله مرة ثانية، وما الذي تحفظه الأداة في ملف الحالة.](https://arabroot.io/articles/%D8%A7%D9%84%D8%A8%D9%86%D9%8A%D8%A9-%D9%83%D8%B4%D9%8A%D9%81%D8%B1%D8%A9-iac-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/)[![](https://arabroot.io/content/images/2026/10/storage-types-cover-cover.webp)دليلأنواع التخزين وواجهة S3: أين تضع بيانات تطبيقك؟أين تضع قاعدة البيانات وملفات المستخدمين والنسخ الاحتياطية، ولماذا يناسب كلاً منها نوع مختلف من التخزين.](https://arabroot.io/articles/%D8%A7%D9%86%D9%88%D8%A7%D8%B9-%D8%A7%D9%84%D8%AA%D8%AE%D8%B2%D9%8A%D9%86-block-%D9%88-file-%D9%88-object-%D9%84%D9%84%D9%85%D8%A8%D8%AA%D8%AF%D8%A6%D9%8A%D9%86/)

## سجل التحديثات

- أكتوبر 2026: كتابة المقال، وتوليد ملفات Deployment وService وIngress وConfigMap وSecret وNamespace وCronJob ب kubectl v1.36.1 مع `--dry-run=client` دون عنقود، وكتابة ملفات Pod وPVC وStatefulSet وDaemonSet وGateway وHTTPRoute على أمثلة توثيق Kubernetes 1.37 والتحقق منها بمكتبة PyYAML.