حزمة الدورة: 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)
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في طرفية ثانية (تُبقيها ظاهرة طوال المختبر):
kubectl get pods -n premiers-pas -wملفات هذا المختبر موجودة في 01-installer-kubernetes-avec-docker-desktop\pratique\. تحقّق بعد ذلك في المتصفح: http://localhost:8080 (واجهة whoami).
.\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
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في طرفية ثانية (تُبقيها ظاهرة طوال المختبر):
kubectl get pods -n premiers-pas -wملفات هذا المختبر موجودة في 01-installer-kubernetes-avec-docker-desktop/pratique/. تحقّق بعد ذلك في المتصفح: http://localhost:8080 (واجهة whoami).
./labo.sh etat # النتيجة القابلة للتسليم : premiers-pas 7/7 pods Running وService-ان
./labo.sh nettoyer # في النهاية، يحذف namespaces الدورةتنضمّ إلى الفريق الذي يبني كتالوج المكتبة الخاص بمنصة الدورات. تُسلّمك مديرتك حاسوباً محمولاً: «صباح الغد، أريد أن يعمل Kubernetes على جهازك، وأن تُثبت لي بأمر واحد أن كل شيء على ما يرام، وألا تتصل بي في أول مرة ترفض فيها Pod الإقلاع.» ستُثبِّت إذن حزمة الدورة، وتُنفّذ قائمة التحقق الخاصة بها، وتنشر الواجهة البرمجية وتراقبها تتكاثر مباشرة، ثم تكسر شيئين عمداً: تطلب صورة غير موجودة، وتنسى -n. في كلتا الحالتين ستقرأ الرسالة، وتُسمّي السبب، وتُصلح. معرفة التمييز بين «الكتلة ترفض ما أطلبه» و«أنا أنظر في المكان الخطأ»، هذا ما يوفّر ساعة ضائعة. المسار: الحزمة وprerequis ← etat فارغة ← api خلف LoadBalancer، تحت -w ← خمس نسخ متماثلة وعشرة طلبات ← عطلان مُستحضَران ← إصلاح ← frontend عبر NodePort ← النتيجة القابلة للتسليم.
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 …).$env:Path = "C:\Program Files\Docker\Docker\resources\bin;" + $env:Path.prerequisيقوم سكريبت الحزمة في أمر واحد بالتحققات التي كانت الدروس 02 و03 تفعلها يدوياً: docker، العفريت (daemon)، Kubernetes الخاص بـ Docker Desktop، السياق، إصدارات العميل/الخادم، helm، الذاكرة، المنافذ. في كل هذه العملية الموجهة، تُحصَل نسخة bash من أي أمر في الحزمة باستبدال .\labo.ps1 بـ./labo.sh.
.\labo.ps1 prerequisنقطة التحقق: علامة ✔ لكل سطر والجملة الختامية Tout est prêt. المخرجات الحقيقية (تعرض نسخة bash نفس الأسطر، مع المسار /c/Program Files/Docker/Docker/resources/bin/kubectl):
== 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 مرة واحدة نهائياً.
etat على كتلة فارغةقبل إنشاء أي شيء، انظر إلى ما تعرضه الحزمة عندما لا يوجد شيء. ستقارن مع الخطوة 9.
.\labo.ps1 etatنقطة التحقق: العقدة Ready، وpremiers-pas موسوم بـabsent، ولا Service معروض. المخرجات الحقيقية (حُذفت أسطر namespaces الدورة الأخرى، التي ستظهر وحدة تلو الأخرى):
== 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 لا يهمّه.
في الطرفية الثانية، شغّل المراقبة ولا تغادرها حتى التنظيف:
kubectl get pods -n premiers-pas -wلا يحدث شيء طالما أن الـ namespace غير موجود، وهذا طبيعي. في الطرفية الأولى:
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.
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 الأولى، سطراً تلو الآخر:
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 2sPending (يبحث المُجدوِل عن عقدة)، وContainerCreating (يطلب kubelet الحاوية من runtime)، وRunning: ثانيتان، لأن الصورة موجودة بالفعل على الآلة منذ الدرس 04. تحقّق أن الاستجابة تعمل:
curl -s http://localhost:8080curl.exe -s http://localhost:8080Hostname: 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 ثم أعد المحاولة.
راقب جيداً الطرفية الثانية في اللحظة التي تكتب فيها هذا:
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.
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 خلال ثلاث ثوانٍ (مقتطف حقيقي):
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 المختلفة التي استجابت:
.\01-installer-kubernetes-avec-docker-desktop\pratique\repartition.ps1./01-installer-kubernetes-avec-docker-desktop/pratique/repartition.shالمخرجات الحقيقية (PowerShell؛ يعرض السكريبت أولاً القيم العشر لـHostname المستلمة، سطراً تلو الآخر، ثم هذا الإحصاء):
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:. افتحه لقراءته.
تحتوي الحزمة على Pod وسم صورتها خاطئ. اقرأه قبل تطبيقه: 01-installer-kubernetes-avec-docker-desktop/pratique/01-pod-image-inexistante.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: 80kubectl apply -f 01-installer-kubernetes-avec-docker-desktop/pratique/01-pod-image-inexistante.yaml
kubectl get pods -n premiers-pasنقطة التحقق: الـ Pod موجودة لكنها ليست Running، والخمس الأخريات غير متأثرات.
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 المحاولة، يفشل، ينتظر مدة أطول فأطول (مقتطف حقيقي):
api-cassee 0/1 ErrImagePull 0 2s
api-cassee 0/1 ImagePullBackOff 0 17s
api-cassee 0/1 ErrImagePull 0 29sيُخبرك get أن هناك فشلاً؛ ولا يُخبرك إلا describe لماذا. اذهب مباشرة إلى قسم Events، في الأسفل:
kubectl describe pod api-cassee -n premiers-pasالمخرجات الحقيقية (النهاية فقط مُعروضة):
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). عادة يجب التخلّص منها فوراً: البحث عن السبب في السجلات. لا وجود لها، فالحاوية لم تُقلع أبداً:
kubectl logs api-cassee -n premiers-pasError from server (BadRequest): container "whoami" in pod "api-cassee" is waiting to start: image can't be pulledيبقى RESTARTS عند 0 للسبب نفسه. اكتب في جملة واحدة سبب العطل: ستحتاجها في الخطوة 9.
-nتريد التحقق من الـ Pod المكسورة، وتكتب الأمر بدون الـ namespace:
kubectl get pod api-cassee
kubectl logs api-casseeنقطة التحقق: خطآن NotFound، بينما الـ Pod موجودة فعلاً.
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، بل أنت تنظر في الدُرج الخطأ. الدليل:
kubectl get pod api-cassee -n premiers-pasNAME READY STATUS RESTARTS AGE
api-cassee 0/1 ErrImagePull 0 36sاحتفظ بهذه القاعدة: خطأ NotFound بخصوص كائن أنشأته للتو، هو دائماً تقريباً -n منسي. لا تُغيّر هذه الدورة أبداً الـ namespace الافتراضي للسياق، تحديداً كي تكتسب عادة ذكره في كل أمر.
kubectl set imagePod لا تُحمَّل صورتها لا تُصلَح بالانتظار. علاجان: حذفها وإعادة تطبيق YAML مُصحَّح، أو تغيير الصورة في مكانها. نفّذ الثاني، وهو الأسرع، بينما تراقب الطرفية الثانية:
kubectl set image pod/api-cassee whoami=traefik/whoami:v1.10 -n premiers-pas
kubectl get pod api-cassee -n premiers-paswhoami هو اسم الحاوية في ملف YAML (containers[0].name)، وليس اسم الصورة.
نقطة التحقق:
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 تشغيل حاوية، بل شغّل واحدة للمرة الأولى بالصورة الصحيحة.
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.
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-pasdeployment.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 10s80:32740: 80 هو منفذ الـ Service داخل الكتلة، و32740 منفذ العقدة المسحوب عشوائياً بين 30000 و32767. عندك سيكون مختلفاً: اقرأه، لا تنسخ هذا.
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.)
<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).
etat وثلاثة أسئلةأعد تشغيل سكريبت الحزمة وقارن مع الخطوة 2.
.\labo.ps1 etatالمخرجات الحقيقية (نفس الحذوفات كما في الخطوة 2):
== 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. انسخ هذه المخرجات في نتيجتك القابلة للتسليم، ثم أجب في جملة أو جملتين على كل سؤال:
kubectl logs api-cassee -n premiers-pas عرض أي شيء في الخطوة 5، وأين وجدت السبب؟not found in namespace "default": هل اختفت الـ Pod؟ ماذا كان عليك أن تكتب؟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 الدورة دفعة واحدة، لكنها ترفض فعل ذلك دون تأكيد. انظر أولاً إلى ما ستفعله:
.\labo.ps1 nettoyer== 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:
kubectl delete namespace premiers-pas
kubectl get namespace premiers-pasnamespace "premiers-pas" deleted
Error from server (NotFound): namespaces "premiers-pas" not foundCtrl+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ème ← Set-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 directory ← chmod +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.