عملية موجهة — تثبيت والتحقق من وإصلاح الكتلة الخاصة بك

تدريب موجَّه15 دقيقة
المدة
45 إلى 60 دقيقة
الوحدة
1/8
ستبني
حزمة الدورة مُثبَّتة ومُتحقَّقاً منها بـlabo.ps1 prerequis، وواجهة whoami الخاصة بالمكتبة بخمس نسخ متماثلة خلف Service من نوع LoadBalancer على http://localhost:8080، وPod مكسورة عمداً (وسم صورة غير موجود) تُشخّصها ثم تُصلحها، و-n منسي تتعرّف عليه من الرسالة، وواجهة أمامية (frontend) بـnginx معروضة عبر NodePort
النتيجة القابلة للتسليم
مخرجات labo.ps1 etat (أو labo.sh etat) التي تُظهر premiers-pas 7/7 pods Running والـService-ين، بالإضافة إلى إجاباتك على الأسئلة الثلاثة في الخطوة 9

في اختصار: أوامر المختبر

حزمة الدورة: https://github.com/hrhouma2/aiopsatlas-kubernetes-docker-desktop-labo-fr

تستنسخ الحزمة في مجلد lab2، تتحقق من جاهزية Docker Desktop وKubernetes، تقرأ حالة كتلة فارغة، ثم تنشر واجهة whoami الخاصة بالمكتبة بخمس نسخ متماثلة خلف Service من نوع LoadBalancer على http://localhost:8080. بعد ذلك تكسر شيئين عمداً (صورة غير موجودة، -n منسي)، تُصلح، وتعرض frontend عبر NodePort. احتفظ بطرفية ثانية مفتوحة مع kubectl get pods -n premiers-pas -w لمراقبة حركة الـ Pods. تفاصيل كل خطوة تلي؛ ابدأ بتنفيذ هذه الكتلة.

Windows (PowerShell)

powershell
git clone https://github.com/hrhouma2/aiopsatlas-kubernetes-docker-desktop-labo-fr.git lab2
cd lab2
ls                       # explorer le contenu : labo.ps1, labo.sh, un dossier par module (01-… à 08-…)
$env:Path = "C:\Program Files\Docker\Docker\resources\bin;" + $env:Path   # kubectl de Docker Desktop en priorité
.\labo.ps1 prerequis
.\labo.ps1 etat          # cluster vide : attendu docker-desktop Ready, aucun namespace du cours

في طرفية ثانية (تُبقيها ظاهرة طوال المختبر):

powershell
kubectl get pods -n premiers-pas -w

ملفات هذا المختبر موجودة في 01-installer-kubernetes-avec-docker-desktop\pratique\. تحقّق بعد ذلك في المتصفح: http://localhost:8080 (واجهة whoami).

powershell
.\labo.ps1 etat          # النتيجة القابلة للتسليم : premiers-pas 7/7 pods Running وService-ان
.\labo.ps1 nettoyer      # في النهاية، يحذف namespaces الدورة

إذا رفض PowerShell تنفيذ .\labo.ps1 (« l'exécution de scripts est désactivée ») : Set-ExecutionPolicy -Scope CurrentUser RemoteSigned، أجب بـO، أعد التشغيل.

Linux، macOS، WSL 2، Git Bash

bash
git clone https://github.com/hrhouma2/aiopsatlas-kubernetes-docker-desktop-labo-fr.git lab2
cd lab2
ls                       # explorer le contenu : labo.sh, labo.ps1, un dossier par module (01-… à 08-…)
./labo.sh prerequis
./labo.sh etat           # cluster vide : attendu docker-desktop Ready, aucun namespace du cours

في طرفية ثانية (تُبقيها ظاهرة طوال المختبر):

bash
kubectl get pods -n premiers-pas -w

ملفات هذا المختبر موجودة في 01-installer-kubernetes-avec-docker-desktop/pratique/. تحقّق بعد ذلك في المتصفح: http://localhost:8080 (واجهة whoami).

bash
./labo.sh etat           # النتيجة القابلة للتسليم : premiers-pas 7/7 pods Running وService-ان
./labo.sh nettoyer       # في النهاية، يحذف namespaces الدورة

الهدف

تنضمّ إلى الفريق الذي يبني كتالوج المكتبة الخاص بمنصة الدورات. تُسلّمك مديرتك حاسوباً محمولاً: «صباح الغد، أريد أن يعمل Kubernetes على جهازك، وأن تُثبت لي بأمر واحد أن كل شيء على ما يرام، وألا تتصل بي في أول مرة ترفض فيها Pod الإقلاع.» ستُثبِّت إذن حزمة الدورة، وتُنفّذ قائمة التحقق الخاصة بها، وتنشر الواجهة البرمجية وتراقبها تتكاثر مباشرة، ثم تكسر شيئين عمداً: تطلب صورة غير موجودة، وتنسى -n. في كلتا الحالتين ستقرأ الرسالة، وتُسمّي السبب، وتُصلح. معرفة التمييز بين «الكتلة ترفض ما أطلبه» و«أنا أنظر في المكان الخطأ»، هذا ما يوفّر ساعة ضائعة. المسار: الحزمة وprerequisetat فارغة ← api خلف LoadBalancer، تحت -w ← خمس نسخ متماثلة وعشرة طلبات ← عطلان مُستحضَران ← إصلاح ← frontend عبر NodePort ← النتيجة القابلة للتسليم.

قبل أن تبدأ

  • قراءة دروس الوحدة الأربعة: 01، و02، و03، و04.
  • Docker Desktop مُشغَّل، Kubernetes مُفعَّل بوضع kubeadm (الدرس 02). يجب ألا يكون namespace باسم premiers-pas موجوداً بعد: يُجيب kubectl get namespace premiers-pas بـNotFound (وإلا kubectl delete namespace premiers-pas أولاً).
  • حزمة الدورة مُستنسَخة: git clone https://github.com/hrhouma2/aiopsatlas-kubernetes-docker-desktop-labo-fr.git، ثم انتقل إلى جذرها (الذي يحتوي على labo.ps1 وlabo.sh). تُنفَّذ كل الأوامر أدناه من هناك.
  • طرفيتان جنباً إلى جنب: الأولى للعمل، والثانية للمراقبة (kubectl get pods -n premiers-pas -w). PowerShell أو bash (Git Bash، WSL 2، macOS، Linux): أوامر kubectl متطابقة، وكل أمر من الحزمة موجود بالنكهتين (.\labo.ps1 … / ./labo.sh …).
  • على Windows، إذا أظهر لك الدرس 03 تحذير إصدار، ابدأ كل طرفية بـ$env:Path = "C:\Program Files\Docker\Docker\resources\bin;" + $env:Path.

الخطوة 1 — تثبيت الحزمة وتشغيل prerequis

يقوم سكريبت الحزمة في أمر واحد بالتحققات التي كانت الدروس 02 و03 تفعلها يدوياً: docker، العفريت (daemon)، Kubernetes الخاص بـ Docker Desktop، السياق، إصدارات العميل/الخادم، helm، الذاكرة، المنافذ. في كل هذه العملية الموجهة، تُحصَل نسخة bash من أي أمر في الحزمة باستبدال .\labo.ps1 بـ./labo.sh.

powershell
.\labo.ps1 prerequis

نقطة التحقق: علامة لكل سطر والجملة الختامية Tout est prêt. المخرجات الحقيقية (تعرض نسخة bash نفس الأسطر، مع المسار /c/Program Files/Docker/Docker/resources/bin/kubectl):

text
== Prérequis ==
  ✔ docker : Docker version 29.3.1, build c2be9cc
  ✔ le démon Docker répond (Docker Engine 29.3.1)
  ✔ Kubernetes Docker Desktop : running (mode kubeadm, 1 nœud, v1.34.1)
  ✔ contexte kubectl courant : docker-desktop
  ✔ kubectl client v1.34.1 (C:\Program Files\Docker\Docker\resources\bin\kubectl.exe)
  ✔ serveur Kubernetes v1.34.1 (même version mineure que le client)
  ✔ helm : v4.2.4+g3900f43
  ✔ mémoire disponible pour Docker : 31 Go
  ✔ port 8080 : libre
  ✔ port 8443 : libre
  ✔ port 30080 : libre

Tout est prêt. Étape suivante : .\labo.ps1 etat

اقرأ السطر kubectl client: يُخبرك بأي ملف تنفيذي يستجيب، مع مساره. إنها الإجابة في سطر واحد عن مشكلة الدرس 03. لا يُستخدَم Helm إلا في الوحدة 7: إذا كان مفقوداً، يتحوّل السطر إلى ! (تحذير)، لا إلى .

إذا رأيت شيئاً آخر: ✘ kubectl client v1.30.0 trop ancien (minimum 1.33) — mettez C:\Program Files\Docker\Docker\resources\bin en tête du PATH ; where.exe kubectl montre l'ordre actuel (مخرجات حقيقية حُصل عليها بنسخة قديمة من kubectl في مقدّمة PATH) ← الدرس 03، الخطوة 3؛ ✘ le démon Docker ne répond pas ← Docker Desktop غير مُشغَّل؛ يرفض PowerShell تنفيذ السكريبت ← Set-ExecutionPolicy -Scope CurrentUser RemoteSigned مرة واحدة نهائياً.

الخطوة 2 — قراءة etat على كتلة فارغة

قبل إنشاء أي شيء، انظر إلى ما تعرضه الحزمة عندما لا يوجد شيء. ستقارن مع الخطوة 9.

powershell
.\labo.ps1 etat

نقطة التحقق: العقدة Ready، وpremiers-pas موسوم بـabsent، ولا Service معروض. المخرجات الحقيقية (حُذفت أسطر namespaces الدورة الأخرى، التي ستظهر وحدة تلو الأخرى):

text
== Nœuds ==
  NAME             STATUS   ROLES           AGE   VERSION
  docker-desktop   Ready    control-plane   18d   v1.34.1

== Namespaces du cours ==
  — premiers-pas     absent

== Services exposés (LoadBalancer / NodePort) dans les namespaces du cours ==
  — aucun Service exposé pour l'instant (les leçons en créent avec kubectl expose)

ثلاثة رموز يجب معرفتها: كل شيء يعمل، ! شيء يستحق نظرة (Pods ليست Running، تحذير)، معلومة محايدة (غائب، لا شيء لعرضه). لا ينظر السكريبت إلا إلى namespaces الدورة: ما يعيش في default أو kube-system لا يهمّه.

الخطوة 3 — نشر الواجهة البرمجية وعرضها، مع المراقبة

في الطرفية الثانية، شغّل المراقبة ولا تغادرها حتى التنظيف:

bash
kubectl get pods -n premiers-pas -w

لا يحدث شيء طالما أن الـ namespace غير موجود، وهذا طبيعي. في الطرفية الأولى:

bash
kubectl create namespace premiers-pas
kubectl create deployment api --image=traefik/whoami:v1.10 --port=80 -n premiers-pas
kubectl expose deployment api --type=LoadBalancer --port=8080 --target-port=80 -n premiers-pas
kubectl get svc -n premiers-pas

نقطة التحقق: ثلاثة تأكيدات ثم Service بـEXTERNAL-IP localhost.

text
namespace/premiers-pas created
deployment.apps/api created
service/api exposed
NAME   TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
api    LoadBalancer   10.96.92.182   localhost     8080:31427/TCP   5s

في هذه الأثناء، عرضت الطرفية الثانية ميلاد الـ Pod الأولى، سطراً تلو الآخر:

text
NAME                  READY   STATUS    RESTARTS   AGE
api-9bfb55fc6-mh24c   0/1     Pending   0          0s
api-9bfb55fc6-mh24c   0/1     Pending   0          0s
api-9bfb55fc6-mh24c   0/1     ContainerCreating   0          0s
api-9bfb55fc6-mh24c   1/1     Running             0          2s

Pending (يبحث المُجدوِل عن عقدة)، وContainerCreating (يطلب kubelet الحاوية من runtime)، وRunning: ثانيتان، لأن الصورة موجودة بالفعل على الآلة منذ الدرس 04. تحقّق أن الاستجابة تعمل:

bash
curl -s http://localhost:8080
powershell
curl.exe -s http://localhost:8080
text
Hostname: api-9bfb55fc6-mh24c
IP: 10.1.2.255
RemoteAddr: 192.168.65.3:49552
Host: localhost:8080

مقتطف: Hostname هو اسم الـ Pod التي استجابت، وIP عنوانها داخل الكتلة؛ وRemoteAddr: 192.168.65.3 هو عنوان العقدة، الذي يُدخِل Docker Desktop من خلاله طلبك. نفس الـ Deployment ونفس الـ Service، مكتوبان بصيغة YAML كاملة، موجودان في الحزمة: 01-installer-kubernetes-avec-docker-desktop/04-api-declaratif.yaml (الوحدة 2 لقراءتهما سطراً بسطر).

إذا رأيت شيئاً آخر: EXTERNAL-IP <pending> لا يتحرّك ← Service آخر من نوع LoadBalancer ينشر المنفذ 8080 بالفعل (kubectl get svc -A)، احذفه أو غيّر المنفذ؛ AlreadyExists ← الـ namespace متبقٍ من الدرس 04، kubectl delete namespace premiers-pas ثم أعد المحاولة.

الخطوة 4 — خمس نسخ متماثلة، عشرة طلبات

راقب جيداً الطرفية الثانية في اللحظة التي تكتب فيها هذا:

bash
kubectl scale deployment api --replicas=5 -n premiers-pas
kubectl get pods -n premiers-pas -o wide

نقطة التحقق: خمس Pods Running، كل واحدة بعنوان IP خاص بها من نطاق 10.1.x.x، جميعها على العقدة docker-desktop.

text
deployment.apps/api scaled
NAME                  READY   STATUS    RESTARTS   AGE   IP           NODE             NOMINATED NODE   READINESS GATES
api-9bfb55fc6-7b7br   1/1     Running   0          15s   10.1.3.1     docker-desktop   <none>           <none>
api-9bfb55fc6-hp76n   1/1     Running   0          15s   10.1.3.3     docker-desktop   <none>           <none>
api-9bfb55fc6-mh24c   1/1     Running   0          27s   10.1.2.255   docker-desktop   <none>           <none>
api-9bfb55fc6-r4d5w   1/1     Running   0          15s   10.1.3.0     docker-desktop   <none>           <none>
api-9bfb55fc6-rd95r   1/1     Running   0          15s   10.1.3.2     docker-desktop   <none>           <none>

الـ Pod الأولى (mh24c، 27 ثانية) أكبر بإثنتي عشرة ثانية من الأربع الأخريات: إنها Pod الخطوة 3.

أظهرت الطرفية الثانية الـ Pods الأربع الجديدة تمرّ معاً بـPending، ثم ContainerCreating ثم Running خلال ثلاث ثوانٍ (مقتطف حقيقي):

text
api-9bfb55fc6-r4d5w   0/1     Pending             0          0s
api-9bfb55fc6-r4d5w   0/1     ContainerCreating   0          0s
api-9bfb55fc6-hp76n   1/1     Running             0          2s
api-9bfb55fc6-r4d5w   1/1     Running             0          3s

تحتوي الحزمة على سكريبت صغير يُرسل عشرة طلبات ويعدّ الـ Pods المختلفة التي استجابت:

powershell
.\01-installer-kubernetes-avec-docker-desktop\pratique\repartition.ps1
bash
./01-installer-kubernetes-avec-docker-desktop/pratique/repartition.sh

المخرجات الحقيقية (PowerShell؛ يعرض السكريبت أولاً القيم العشر لـHostname المستلمة، سطراً تلو الآخر، ثم هذا الإحصاء):

text
10 requêtes, 5 Pods distincts ont répondu :
  api-9bfb55fc6-7b7br  x2
  api-9bfb55fc6-hp76n  x2
  api-9bfb55fc6-mh24c  x2
  api-9bfb55fc6-r4d5w  x2
  api-9bfb55fc6-rd95r  x2

أعطى نفس السكريبت، مُشغَّلاً على bash بعد ذلك مباشرة، النتيجة 10 requêtes, 4 Pods distincts مع Pod واحدة خُدمت أربع مرات: التوزيع عشوائي، وليس تناوباً. من عشرة طلبات، رؤية أربع أو خمس Pods أمر طبيعي؛ رؤية واحدة فقط ليست كذلك (انظر «إذا واجهت مشكلة»). لا يفعل السكريبت شيئاً سحرياً: مجرد حلقة من curl تحتفظ بسطر Hostname:. افتحه لقراءته.

الخطوة 5 — كسر: طلب صورة غير موجودة

تحتوي الحزمة على Pod وسم صورتها خاطئ. اقرأه قبل تطبيقه: 01-installer-kubernetes-avec-docker-desktop/pratique/01-pod-image-inexistante.yaml.

yaml
apiVersion: v1
kind: Pod
metadata:
  name: api-cassee
  namespace: premiers-pas
  labels:
    app: api-cassee
    partie: bibliotheque
spec:
  containers:
    - name: whoami
      image: traefik/whoami:v9.99
      ports:
        - containerPort: 80
bash
kubectl apply -f 01-installer-kubernetes-avec-docker-desktop/pratique/01-pod-image-inexistante.yaml
kubectl get pods -n premiers-pas

نقطة التحقق: الـ Pod موجودة لكنها ليست Running، والخمس الأخريات غير متأثرات.

text
pod/api-cassee created
NAME                  READY   STATUS         RESTARTS   AGE
api-9bfb55fc6-7b7br   1/1     Running        0          27s
api-9bfb55fc6-mh24c   1/1     Running        0          39s
api-cassee            0/1     ErrImagePull   0          4s

(حُذفت ثلاثة أسطر متطابقة من api-9bfb55fc6-….)

في الطرفية الثانية، تتذبذب الحالة: يُعيد kubelet المحاولة، يفشل، ينتظر مدة أطول فأطول (مقتطف حقيقي):

text
api-cassee            0/1     ErrImagePull        0          2s
api-cassee            0/1     ImagePullBackOff    0          17s
api-cassee            0/1     ErrImagePull        0          29s

يُخبرك get أن هناك فشلاً؛ ولا يُخبرك إلا describe لماذا. اذهب مباشرة إلى قسم Events، في الأسفل:

bash
kubectl describe pod api-cassee -n premiers-pas

المخرجات الحقيقية (النهاية فقط مُعروضة):

text
Events:
  Type     Reason     Age                From               Message
  ----     ------     ----               ----               -------
  Normal   Scheduled  34s                default-scheduler  Successfully assigned premiers-pas/api-cassee to docker-desktop
  Normal   Pulling    18s (x2 over 34s)  kubelet            Pulling image "traefik/whoami:v9.99"
  Warning  Failed     18s (x2 over 34s)  kubelet            Failed to pull image "traefik/whoami:v9.99": Error response from daemon: failed to resolve reference "docker.io/traefik/whoami:v9.99": docker.io/traefik/whoami:v9.99: not found
  Warning  Failed     18s (x2 over 34s)  kubelet            Error: ErrImagePull
  Normal   BackOff    6s (x2 over 33s)   kubelet            Back-off pulling image "traefik/whoami:v9.99"
  Warning  Failed     6s (x2 over 33s)   kubelet            Error: ImagePullBackOff

كل شيء هنا: من (kubelet)، وماذا (Failed to pull image)، ولماذا (docker.io/traefik/whoami:v9.99: not found)، والإيقاع (x2 over 34s). عادة يجب التخلّص منها فوراً: البحث عن السبب في السجلات. لا وجود لها، فالحاوية لم تُقلع أبداً:

bash
kubectl logs api-cassee -n premiers-pas
text
Error from server (BadRequest): container "whoami" in pod "api-cassee" is waiting to start: image can't be pulled

يبقى RESTARTS عند 0 للسبب نفسه. اكتب في جملة واحدة سبب العطل: ستحتاجها في الخطوة 9.

الخطوة 6 — كسر: نسيان -n

تريد التحقق من الـ Pod المكسورة، وتكتب الأمر بدون الـ namespace:

bash
kubectl get pod api-cassee
kubectl logs api-cassee

نقطة التحقق: خطآن NotFound، بينما الـ Pod موجودة فعلاً.

text
Error from server (NotFound): pods "api-cassee" not found
error: error from server (NotFound): pods "api-cassee" not found in namespace "default"

السطر الثاني يقول كل شيء: in namespace "default". بدون -n، يبحث kubectl في namespace السياق، أي default، حيث لا وجود لـapi-cassee. لم تختفِ الـ Pod، بل أنت تنظر في الدُرج الخطأ. الدليل:

bash
kubectl get pod api-cassee -n premiers-pas
text
NAME         READY   STATUS         RESTARTS   AGE
api-cassee   0/1     ErrImagePull   0          36s

احتفظ بهذه القاعدة: خطأ NotFound بخصوص كائن أنشأته للتو، هو دائماً تقريباً -n منسي. لا تُغيّر هذه الدورة أبداً الـ namespace الافتراضي للسياق، تحديداً كي تكتسب عادة ذكره في كل أمر.

الخطوة 7 — إصلاح باستخدام kubectl set image

Pod لا تُحمَّل صورتها لا تُصلَح بالانتظار. علاجان: حذفها وإعادة تطبيق YAML مُصحَّح، أو تغيير الصورة في مكانها. نفّذ الثاني، وهو الأسرع، بينما تراقب الطرفية الثانية:

bash
kubectl set image pod/api-cassee whoami=traefik/whoami:v1.10 -n premiers-pas
kubectl get pod api-cassee -n premiers-pas

whoami هو اسم الحاوية في ملف YAML (containers[0].name)، وليس اسم الصورة.

نقطة التحقق:

text
pod/api-cassee image updated
NAME         READY   STATUS    RESTARTS   AGE
api-cassee   1/1     Running   0          44s

أظهرت الطرفية الثانية الانتقال من ImagePullBackOff إلى Running بين الثانية 36 و37 من عمر الـ Pod: ثانية واحدة، لأن traefik/whoami:v1.10 موجودة بالفعل على الآلة. لاحظ أن RESTARTS لا يزال يساوي 0: لم يُعِد Kubernetes تشغيل حاوية، بل شغّل واحدة للمرة الأولى بالصورة الصحيحة.

الخطوة 8 — إليك التحدي: عرض frontend عبر NodePort

تعمل الواجهة البرمجية. أضف الآن المكوّن الثاني للمكتبة: Deployment باسم frontend انطلاقاً من الصورة nginx:1.27-alpine (المنفذ 80)، معروض بواسطة Service من نوع NodePort (وليس LoadBalancer). ثم اكتشف على أي منفذ من جهازك يستجيب واعرض عنوان صفحته الرئيسية باستخدام curl.

تلميحات: تكفي ثلاثة أوامر من الدرس 04، مع استبدال نوع الـ Service؛ يُقرأ المنفذ الذي يختاره Kubernetes في عمود PORT(S) من kubectl get svc، بعد النقطتين؛ وعلى Docker Desktop، يستجيب NodePort على localhost:<port>. تحتوي الحزمة على YAML مكافئ إذا فضّلته: 01-installer-kubernetes-avec-docker-desktop/pratique/02-frontend-nginx-nodeport.yaml.

التصحيح

bash
kubectl create deployment frontend --image=nginx:1.27-alpine --port=80 -n premiers-pas
kubectl expose deployment frontend --type=NodePort --port=80 -n premiers-pas
kubectl get svc frontend -n premiers-pas
text
deployment.apps/frontend created
service/frontend exposed
NAME       TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
frontend   NodePort   10.111.158.77   <none>        80:32740/TCP   10s

80:32740: 80 هو منفذ الـ Service داخل الكتلة، و32740 منفذ العقدة المسحوب عشوائياً بين 30000 و32767. عندك سيكون مختلفاً: اقرأه، لا تنسخ هذا.

bash
curl -s http://localhost:32740 | grep "<title>"
curl -sI http://localhost:32740 | head -3

(PowerShell: curl.exe -s … | Select-String "<title>" وcurl.exe -sI … | Select-Object -First 3.)

text
<title>Welcome to nginx!</title>
HTTP/1.1 200 OK
Server: nginx/1.27.5
Date: Fri, 11 Sep 2026 03:47:01 GMT

يُؤكّد Server: nginx/1.27.5 الوسم 1.27-alpine. EXTERNAL-IP <none> طبيعي بالنسبة لـ NodePort: لا أحد يُسنِد له عنواناً، فهو يفتح منفذاً على العقدة، ويُرحّل Docker Desktop هذا المنفذ إلى localhost. تقارن الوحدة 4 أنواع الـ Service الثلاثة بالتفصيل. باستخدام YAML الحزمة: kubectl apply -f 01-installer-kubernetes-avec-docker-desktop/pratique/02-frontend-nginx-nodeport.yaml يُنشئ نفس الكائنين (تم التحقق: deployment.apps/frontend created، service/frontend created).

الخطوة 9 — النتيجة القابلة للتسليم: etat وثلاثة أسئلة

أعد تشغيل سكريبت الحزمة وقارن مع الخطوة 2.

powershell
.\labo.ps1 etat

المخرجات الحقيقية (نفس الحذوفات كما في الخطوة 2):

text
== Nœuds ==
  NAME             STATUS   ROLES           AGE   VERSION
  docker-desktop   Ready    control-plane   18d   v1.34.1

== Namespaces du cours ==
  ✔ premiers-pas     7/7 pods Running

== Services exposés (LoadBalancer / NodePort) dans les namespaces du cours ==
  NAMESPACE        NAME                                 TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
  premiers-pas     api                                  LoadBalancer   10.96.92.182     localhost     8080:31427/TCP               91s
  premiers-pas     frontend                             NodePort       10.111.158.77    <none>        80:32740/TCP                 17s

سبع Pods: خمس api، وapi-cassee مُصلَحة، وواحدة frontend. انسخ هذه المخرجات في نتيجتك القابلة للتسليم، ثم أجب في جملة أو جملتين على كل سؤال:

  1. لماذا لم يستطع kubectl logs api-cassee -n premiers-pas عرض أي شيء في الخطوة 5، وأين وجدت السبب؟
  2. في الخطوة 6، قالت الرسالة not found in namespace "default": هل اختفت الـ Pod؟ ماذا كان عليك أن تكتب؟
  3. تعرض api القيمة EXTERNAL-IP localhost وتعرض frontend القيمة <none>: أي نوع من الـ Service تملكه كل واحدة، وبأي منفذ من جهازك تستجيب كل واحدة منها؟

التحقق النهائي

  • ينتهي .\labo.ps1 prerequis (أو ./labo.sh prerequis) بـTout est prêt.
  • يُظهر kubectl get all -n premiers-pas القيم deployment.apps/api 5/5 5 5، وdeployment.apps/frontend 1/1 1 1، وpod/api-cassee 1/1 Running، وreplicaset.apps/api-9bfb55fc6 5 5 5 والـService-ين (المخرجات الحقيقية: سبع Pods، وService-ان، وDeployment-ان، وReplicaSet-ان).
  • يُعيد curl -s http://localhost:8080 | grep Hostname (PowerShell: curl.exe -s http://localhost:8080 | Select-String Hostname) قيمة Hostname: api-… تتغيّر من استدعاء إلى آخر.
  • يُعيد curl -sI http://localhost:<port de frontend> القيمتين HTTP/1.1 200 OK وServer: nginx/1.27.5.
  • تعرف كيف تشرح، دون إعادة القراءة، معنى ErrImagePull وImagePullBackOff وnot found in namespace "default".

التنظيف

تعرف الحزمة كيف تحذف كل namespaces الدورة دفعة واحدة، لكنها ترفض فعل ذلك دون تأكيد. انظر أولاً إلى ما ستفعله:

powershell
.\labo.ps1 nettoyer
text
== Nettoyage des namespaces du cours ==
  Namespaces qui seront supprimés (avec tout ce qu'ils contiennent) : premiers-pas
  Jamais touchés : default, kube-system, kube-public, kube-node-lease.

  ! rien n'a été supprimé. Pour confirmer : .\labo.ps1 nettoyer -oui

في هذه الوحدة، احذف فقط premiers-pas، يدوياً، واحتفظ بالطرفية الثانية مفتوحة لرؤية الـ Pods السبع تنتقل إلى Terminating:

bash
kubectl delete namespace premiers-pas
kubectl get namespace premiers-pas
text
namespace "premiers-pas" deleted
Error from server (NotFound): namespaces "premiers-pas" not found

Ctrl+C في الطرفية الثانية. لا يعود http://localhost:8080 يستجيب (يفشل curl برمز 7، رفض الاتصال، أو 52، استجابة فارغة). سيفيدك .\labo.ps1 nettoyer -oui / ./labo.sh nettoyer --oui عندما تريد إعادة تصفير الدورة بأكملها: فهو لا يمسّ أبداً default ولا kube-system.

إذا واجهت مشكلة

  • يرفض labo.ps1 الانطلاق: l'exécution de scripts est désactivée sur ce systèmeSet-ExecutionPolicy -Scope CurrentUser RemoteSigned، أغلق وأعد فتح PowerShell. السكريبت بترميز UTF-8 مع BOM ويعمل على PowerShell 5.1 كما على PowerShell 7.

  • ./labo.sh: Permission denied أو /usr/bin/env: 'bash\r': No such file or directorychmod +x labo.sh للأول؛ للثاني، حوّل Git نهايات الأسطر إلى CRLF عند الاستخراج: sed -i 's/\r$//' labo.sh (أو git config core.autocrlf false ثم إعادة الاستنساخ).

  • يعرض prerequis الرسالة ✘ port 8080 déjà occupé par <programme> (PID …) — arrêtez-le ou choisissez un autre port dans les leçons ← تطبيق محلي يستمع بالفعل (خادم تطوير آخر، port-forward منسي)؛ يُسمّي السكريبت العملية. أغلقه، أو اختر --port=8081 في الخطوة 3 وعدّل العناوين. إذا كان المنفذ مُستخدَماً بواسطة Service من Kubernetes خاص بالدورة، يبقى السطر : utilisé par un Service du cours (LoadBalancer ou NodePort).

  • EXTERNAL-IP <pending> في الخطوة 3 ← على Docker Desktop، المنفذ المطلوب منشور بالفعل بواسطة Service آخر من نوع LoadBalancer (تم التحقق: Service ثانٍ على المنفذ 8080 يبقى <pending> طالما أن الأول موجود). يُظهر kubectl get svc -A أيّهما. على kind أو minikube، <pending> هي الحالة الطبيعية: استخدم kubectl port-forward.

  • يُعيد repartition نفس Hostname عشر مرات ← إما أن scale لم ينتهِ بعد (kubectl get pods -n premiers-pas: انتظر خمس Running)، أو أن الـ Service له نقطة نهاية واحدة فقط: يجب أن يسرد kubectl get endpointslice -n premiers-pas خمسة عناوين IP (10.1.2.255,10.1.3.3,10.1.3.0 + 2 more... على آلة الدورة).

  • بعد set image، تبقى api-cassee في ImagePullBackOff ← كتبت اسم حاوية أو وسماً خاطئاً: يُظهر kubectl describe pod api-cassee -n premiers-pas الصورة المطلوبة فعلياً في Containers: whoami: Image:. الاسم على يسار = هو اسم الحاوية (whoami)، وليس اسم الصورة.

ما تعلّمته

  • يتحقّق labo.ps1 prerequis / labo.sh prerequis في أمر واحد مما كانت الدروس 02 و03 تفعله يدوياً، ويُسمّي الملف التنفيذي kubectl الذي يستجيب.
  • يُلخّص labo.ps1 etat العقدة والـ Pods حسب namespace الدورة والـ Services المعروضة: إنها الصورة التي تُرفقها عندما تطلب المساعدة.
  • تحت kubectl get pods -w، تمرّ Pod بـPending، ثم ContainerCreating، ثم Running؛ وتُولَد خمس نسخ متماثلة خلال ثلاث ثوانٍ عندما تكون الصورة موجودة بالفعل.
  • ErrImagePull ثم ImagePullBackOff = لا تُحمَّل الصورة؛ والسبب موجود في kubectl describe، قسم Events، وليس أبداً في kubectl logs (الحاوية لم تُقلع).
  • not found in namespace "default" = -n منسي؛ الكائن لا يزال موجوداً، في namespace آخر.
  • يُصحِّح kubectl set image pod/<nom> <conteneur>=<image> صورة في مكانها؛ يستجيب Service من نوع NodePort على localhost:<30000-32767> مع Docker Desktop، بينما يستجيب LoadBalancer على المنفذ الذي اخترته.

تحدٍّ إضافي (اختياري)

دون حذف الـ namespace، استحضر عطلاً ثالثاً وأصلحه: حوّل الـ Deployment باسم api إلى وسم غير موجود بـkubectl set image deployment/api whoami=traefik/whoami:v9.99 -n premiers-pas، ثم لاحظ في الطرفية الثانية أن Kubernetes لا يُدمّر الـ Pods التي تعمل: على آلة الدورة، أنشأ ثلاث Pods باسم api-78d7f59657-… في حالة ErrImagePull، وأزال قديمة واحدة فقط، وأبقى أربع api-9bfb55fc6-… في حالة Running، وتوقّف عند ذلك (يبقى kubectl rollout status deployment/api -n premiers-pas عند Waiting for deployment "api" rollout to finish: 3 out of 5 new replicas have been updated...). يستمر http://localhost:8080 في الاستجابة. أصلح باستخدام kubectl rollout undo deployment/api -n premiers-pas (deployment.apps/api rolled back) وتحقّق من اختفاء الـ Pods المكسورة وأن خمس api-9bfb55fc6-… تعمل. لقد رأيت للتو التحديث المتدرج (rolling update)، وحواجزه (maxSurge، maxUnavailable)، والتراجع (rollback)، وهي مواضيع الوحدة 3.