Καθοδηγούμενη πρακτική — Εγκατάσταση, επαλήθευση και επιδιόρθωση του cluster σου

Καθοδηγούμενη πρακτική17 λεπτά
Διάρκεια
45 έως 60 λεπτά
Ενότητα
1/8
Θα χτίσεις
το kit του μαθήματος εγκατεστημένο και επικυρωμένο από το labo.ps1 prerequis, το API whoami της βιβλιοθήκης σε πέντε replicas πίσω από ένα Service LoadBalancer στο http://localhost:8080, ένα επίτηδες σπασμένο Pod (ανύπαρκτο tag εικόνας) που διαγιγνώσκεις και επιδιορθώνεις, ένα ξεχασμένο -n που αναγνωρίζεις από το μήνυμα, και ένα frontend nginx εκτεθειμένο σε NodePort
Παραδοτέο
η έξοδος του labo.ps1 etatlabo.sh etat) που δείχνει premiers-pas 7/7 pods Running και τα δύο Services, πλέον οι απαντήσεις σου στις τρεις ερωτήσεις του βήματος 9

Εν συντομία: οι εντολές του lab

Kit του μαθήματος: https://github.com/hrhouma2/aiopsatlas-kubernetes-docker-desktop-labo-fr

Κλωνοποιείς το kit σε έναν φάκελο lab2, επαληθεύεις ότι το Docker Desktop και το Kubernetes είναι έτοιμα, διαβάζεις την κατάσταση ενός άδειου cluster, έπειτα αναπτύσσεις το API whoami της βιβλιοθήκης σε πέντε replicas πίσω από ένα 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

Σε ένα δεύτερο τερματικό (να το κρατήσεις ορατό σε όλο το lab):

powershell
kubectl get pods -n premiers-pas -w

Τα αρχεία αυτού του lab βρίσκονται στο 01-installer-kubernetes-avec-docker-desktop\pratique\. Έπειτα επαλήθευσε στον browser: http://localhost:8080 (API whoami).

powershell
.\labo.ps1 etat          # livrable : premiers-pas 7/7 pods Running et deux Services
.\labo.ps1 nettoyer      # à la fin, supprime les namespaces du cours

Αν το PowerShell αρνείται το .\labo.ps1 («η εκτέλεση scripts είναι απενεργοποιημένη»): 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

Σε ένα δεύτερο τερματικό (να το κρατήσεις ορατό σε όλο το lab):

bash
kubectl get pods -n premiers-pas -w

Τα αρχεία αυτού του lab βρίσκονται στο 01-installer-kubernetes-avec-docker-desktop/pratique/. Έπειτα επαλήθευσε στον browser: http://localhost:8080 (API whoami).

bash
./labo.sh etat           # livrable : premiers-pas 7/7 pods Running et deux Services
./labo.sh nettoyer       # à la fin, supprime les namespaces du cours

Στόχος

Εντάσσεσαι στην ομάδα που χτίζει τον κατάλογο της βιβλιοθήκης της πλατφόρμας μαθημάτων. Η προϊσταμένη σου σού δίνει ένα laptop: «Αύριο το πρωί, θέλω το Kubernetes να τρέχει στον σταθμό σου, να μου αποδείξεις με μία εντολή ότι όλα είναι εντάξει, και να μη με καλέσεις την πρώτη φορά που ένα Pod αρνείται να ξεκινήσει.» Θα εγκαταστήσεις λοιπόν το kit του μαθήματος, θα ξετυλίξεις τη λίστα ελέγχου του, θα αναπτύξεις το API και θα το δεις να πολλαπλασιάζεται ζωντανά, έπειτα θα σπάσεις δύο πράγματα επίτηδες: να ζητήσεις μια εικόνα που δεν υπάρχει, και να ξεχάσεις το -n. Και στις δύο περιπτώσεις θα διαβάσεις το μήνυμα, θα ονομάσεις την αιτία και θα επιδιορθώσεις. Το να ξέρεις να διακρίνεις το «το cluster αρνείται αυτό που του ζητώ» από το «κοιτάζω στο λάθος μέρος» είναι αυτό που αποτρέπει μία χαμένη ώρα. Η διαδρομή: kit και prerequisetat σε κενό → api πίσω από LoadBalancer, υπό -w → πέντε replicas και δέκα αιτήματα → δύο προκλητές βλάβες → επιδιόρθωση → 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 πρώτα).
  • Το kit του μαθήματος κλωνοποιημένο: 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 είναι ταυτόσημες, και κάθε εντολή του kit υπάρχει και στις δύο γεύσεις (.\labo.ps1 … / ./labo.sh …).
  • Σε Windows, αν το μάθημα 03 σου έδειξε προειδοποίηση έκδοσης, ξεκίνα κάθε τερματικό με $env:Path = "C:\Program Files\Docker\Docker\resources\bin;" + $env:Path.

Βήμα 1 — Εγκατάσταση του kit και εκτέλεση του prerequis

Το script του kit κάνει με μία εντολή τις επαληθεύσεις των μαθημάτων 02 και 03: docker, daemon, Kubernetes Docker Desktop, context, εκδόσεις πελάτη/server, helm, μνήμη, θύρες. Παντού σε αυτή την πρακτική, η παραλλαγή bash μιας εντολής του kit προκύπτει αντικαθιστώντας το .\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 αρνείται να εκτελέσει το script → Set-ExecutionPolicy -Scope CurrentUser RemoteSigned μία και καλή.

Βήμα 2 — Διάβασε το etat σε ένα άδειο cluster

Πριν δημιουργήσεις οτιδήποτε, κοίτα τι εμφανίζει το kit όταν δεν υπάρχει τίποτα. Θα το συγκρίνεις με το βήμα 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, μια προειδοποίηση), ουδέτερη πληροφορία (απόν, τίποτα να εμφανιστεί). Το script κοιτάζει μόνο τα namespaces του μαθήματος: ό,τι ζει στο default ή στο kube-system δεν το ενδιαφέρει.

Βήμα 3 — Ανάπτυξη του API και έκθεσή του, παρατηρώντας

Στο δεύτερο τερματικό, ξεκίνα την παρακολούθηση και μην την εγκαταλείψεις μέχρι τον καθαρισμό:

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 (ο scheduler ψάχνει κόμβο), ContainerCreating (το kubelet ζητά το container από το 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 η διεύθυνσή του στο cluster· το RemoteAddr: 192.168.65.3 είναι η διεύθυνση του κόμβου, μέσω της οποίας το Docker Desktop εισάγει το αίτημά σου. Το ίδιο Deployment και το ίδιο Service, γραμμένα σε πλήρες YAML, είναι στο kit: 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 — Πέντε replicas, δέκα αιτήματα

Κοίτα καλά το δεύτερο τερματικό τη στιγμή που πληκτρολογείς αυτό:

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 δευτ.) έχει δώδεκα δευτερόλεπτα παραπάνω από τα τέσσερα άλλα: είναι αυτό του βήματος 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

Το kit περιέχει ένα μικρό script που στέλνει δέκα αιτήματα και μετρά τα διακριτά Pods που απάντησαν:

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

Πραγματική έξοδος (PowerShell· το script εμφανίζει πρώτα τα δέκα 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

Το ίδιο script εκτελεσμένο σε bash αμέσως μετά έδωσε 10 requêtes, 4 Pods distincts με ένα Pod να σερβίρεται τέσσερις φορές: η κατανομή είναι τυχαία, όχι εκ περιτροπής. Σε δέκα αιτήματα, να δεις τέσσερα ή πέντε Pods είναι φυσιολογικό· να δεις μόνο ένα δεν είναι (βλέπε «Αν κολλήσει»). Το script δεν κάνει τίποτα μαγικό: ένας βρόχος curl που κρατά τη γραμμή Hostname:. Άνοιξέ το για να το διαβάσεις.

Βήμα 5 — Σπάσε: ζήτα μια εικόνα που δεν υπάρχει

Το kit περιέχει ένα Pod του οποίου το tag εικόνας είναι λάθος. Διάβασέ το πριν το εφαρμόσεις: 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). Ένα αντανακλαστικό που πρέπει να χάσεις αμέσως: να ψάχνεις την αιτία στα logs. Δεν υπάρχουν, το container δεν ξεκίνησε ποτέ:

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 του context, 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 του context, ακριβώς για να πάρεις τη συνήθεια να το λες σε κάθε εντολή.

Βήμα 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 είναι το όνομα του container στο YAML (containers[0].name), όχι αυτό της εικόνας.

Σημείο ελέγχου:

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

Το δεύτερο τερματικό έδειξε τη μετάβαση ImagePullBackOffRunning ανάμεσα στο 36ο και το 37ο δευτερόλεπτο ζωής του Pod: ένα δευτερόλεπτο, γιατί το traefik/whoami:v1.10 είναι ήδη στο μηχάνημα. Σημείωσε ότι το RESTARTS είναι πάντα 0: το Kubernetes δεν επανεκκίνησε ένα container, εκκίνησε ένα για πρώτη φορά με τη σωστή εικόνα.

Βήμα 8 — Η σειρά σου: έκθεσε το frontend σε NodePort

Το API τρέχει. Πρόσθεσε τώρα το δεύτερο στοιχείο της βιβλιοθήκης: ένα Deployment frontend από την εικόνα nginx:1.27-alpine (θύρα 80), εκτεθειμένο από ένα Service τύπου NodePort (όχι LoadBalancer). Έπειτα βρες σε ποια θύρα του μηχανήματός σου απαντά και εμφάνισε τον τίτλο της αρχικής του σελίδας με curl.

Ενδείξεις: τρεις εντολές του μαθήματος 04 αρκούν, αντικαθιστώντας τον τύπο του Service· η θύρα που επιλέγει το Kubernetes διαβάζεται στη στήλη PORT(S) του kubectl get svc, μετά την άνω-κάτω τελεία· στο Docker Desktop, ένα NodePort απαντά στο localhost:<θύρα>. Το kit περιέχει το ισοδύναμο 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 μέσα στο cluster, 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 επιβεβαιώνει το tag 1.27-alpine. Το EXTERNAL-IP <none> είναι φυσιολογικό για ένα NodePort: κανείς δεν του αποδίδει διεύθυνση, ανοίγει μια θύρα στον κόμβο, και το Docker Desktop αναμεταδίδει αυτή τη θύρα στο localhost. Η ενότητα 4 συγκρίνει τους τρεις τύπους Service αναλυτικά. Με το YAML του kit: το kubectl apply -f 01-installer-kubernetes-avec-docker-desktop/pratique/02-frontend-nginx-nodeport.yaml δημιουργεί τα ίδια δύο αντικείμενα (επαληθευμένο: deployment.apps/frontend created, service/frontend created).

Βήμα 9 — Το παραδοτέο: etat και τρεις ερωτήσεις

Ξαναεκτέλεσε το script του kit και σύγκρινε με το βήμα 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 και τα δύο Services (πραγματική έξοδος: επτά Pods, δύο Services, δύο Deployments, δύο ReplicaSets).
  • Το curl -s http://localhost:8080 | grep Hostname (PowerShell: curl.exe -s http://localhost:8080 | Select-String Hostname) επιστρέφει ένα Hostname: api-… που αλλάζει από κλήση σε κλήση.
  • Το curl -sI http://localhost:<θύρα του frontend> επιστρέφει HTTP/1.1 200 OK και Server: nginx/1.27.5.
  • Ξέρεις να πεις, χωρίς να ξαναδιαβάσεις, τι σημαίνουν ErrImagePull, ImagePullBackOff και not found in namespace "default".

Καθαρισμός

Το kit ξέρει να διαγράφει όλα τα 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. Το script είναι σε 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.shgit 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 → μια τοπική εφαρμογή ακούει ήδη (ένας άλλος server ανάπτυξης, ένα ξεχασμένο port-forward)· το script ονομάζει τη διεργασία. Κλείσε την, ή διάλεξε --port=8081 στο βήμα 3 και προσάρμοσε τα URL. Αν η θύρα κατέχεται από ένα 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 έχει μόνο ένα endpoint: το 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 → πληκτρολόγησες λάθος όνομα container ή tag: το kubectl describe pod api-cassee -n premiers-pas δείχνει την εικόνα που πραγματικά ζητήθηκε στο Containers: whoami: Image:. Το όνομα αριστερά του = είναι αυτό του container (whoami), όχι της εικόνας.

Τι έμαθες

  • Το labo.ps1 prerequis / labo.sh prerequis επαληθεύει με μία εντολή αυτά που τα μαθήματα 02 και 03 έκαναν με το χέρι, και ονομάζει το εκτελέσιμο kubectl που απαντά.
  • Το labo.ps1 etat συνοψίζει κόμβο, Pods ανά namespace του μαθήματος και εκτεθειμένα Services: είναι η φωτογραφία που επισυνάπτεις όταν ζητάς βοήθεια.
  • Υπό kubectl get pods -w, ένα Pod περνά από Pending, ContainerCreating, Running· πέντε replicas γεννιούνται σε τρία δευτερόλεπτα όταν η εικόνα είναι ήδη εκεί.
  • ErrImagePull και έπειτα ImagePullBackOff = η εικόνα δεν κατεβαίνει· η αιτία είναι στο kubectl describe, ενότητα Events, ποτέ στο kubectl logs (το container δεν ξεκίνησε).
  • not found in namespace "default" = ξεχασμένο -n· το αντικείμενο είναι πάντα εκεί, σε άλλο namespace.
  • Το kubectl set image pod/<όνομα> <container>=<εικόνα> διορθώνει μια εικόνα επιτόπου· ένα Service NodePort απαντά στο localhost:<30000-32767> με Docker Desktop, ένα LoadBalancer στη θύρα που επέλεξες.

Πρόκληση μπόνους (προαιρετική)

Χωρίς να διαγράψεις το namespace, προκάλεσε μια τρίτη βλάβη και επιδιόρθωσέ την: πέρασε το Deployment api σε ένα ανύπαρκτο tag με 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.