Έργο 12 — Οι υπηρεσίες Kubernetes · Επίπεδο ενδιάμεσο → προχωρημένο · Εκτιμώμενη διάρκεια: 3 έως 5 ώρες
Όλος ο κώδικας εφαρμογής σας παρέχεται στο παράρτημα αυτού του εγγράφου. Η δουλειά σας: να γράψετε, να καθορίσετε και να συμπληρώσετε τα Services που λείπουν — δηλαδή να κάνετε να επικοινωνεί ένα σύστημα που, ως έχει, είναι εντελώς άφωνο.
Μια ομάδα ανέπτυξε μια μικρή πλατφόρμα ηλεκτρονικού εμπορίου στο Kubernetes. Οι εικόνες είναι κατασκευασμένες, τα Deployments και το StatefulSet τρέχουν, όλα τα Pods είναι Running.
Και όμως, τίποτα δεν λειτουργεί.
Το portail εμφανίζει έναν πίνακα ελέγχου εντελώς κόκκινο: δεν φτάνει κανένα στοιχείο. Η βάση δεδομένων είναι απρόσιτη. Η κρυφή μνήμη είναι αόρατη. Καμία σελίδα δεν είναι προσβάσιμη από τον περιηγητή.
Ο λόγος είναι απλός: το άτομο που έπρεπε να γράψει τα Services έφυγε χωρίς να τα παραδώσει.
Θεμελιώδης υπενθύμιση που αυτό το έργο θα σας κάνει να ζήσετε: Pods που τρέχουν δεν αποτελούν εφαρμογή. Χωρίς Services, είναι απομονωμένα νησιά, χωρίς σταθερή διεύθυνση ούτε όνομα, ανίκανα να βρουν το ένα το άλλο.
Η αποστολή σας: να αποκαταστήσετε όλες τις επικοινωνίες, μόνο γράφοντας τα σωστά Services.
Ουσιώδεις έννοιες πριν ξεκινήσετε
Αυτή η ενότητα είναι ένα αυτάρκες μίνι-εγχειρίδιο: περιέχει όλο το λεξιλόγιο που είναι απαραίτητο για τις αποστολές. Διαβάστε την μία φορά, επιστρέψτε όταν σας ξεφεύγει μια λέξη.
1. Το πρόβλημα που λύνει ένα Service
Ένα Pod είναι θνητό: το Kubernetes μπορεί να το διαγράψει, να το μετακινήσει, να το αναδημιουργήσει — και η νέα IP του θα είναι διαφορετική. Δεν συνδεόμαστε λοιπόν ποτέ σε ένα Pod μέσω της IP του.
Ένα Service είναι ένα σταθερό αντικείμενο — όνομα, IP, θύρα — που ακολουθεί τα Pods όπου κι αν πάνε. Ο μηχανισμός είναι απλός:
Αυτό που συνδέει ένα Service με «τα» Pods του είναι ο επιλογέας ετικετών:
yaml
spec: selector: app: api-produits # κάθε Pod που φέρει ΑΥΤΗ την ετικέτα προσεγγίζεται
Η λίστα των Pods που αντιστοιχούν σχηματίζει το αντικείμενο Endpoints — είναι η ακτινογραφία σας του Service.
powershell
kubectl get endpoints api-produits# api-produits 10.244.0.3:8000,10.244.0.4:8000,10.244.0.5:8000
Αν τα Endpoints είναι κενά, ο επιλογέας δεν αντιστοιχεί σε κανένα Pod: σχεδόν πάντα είναι τυπογραφικό σε μια ετικέτα.
2. Οι πέντε τύποι Services (οι μόνοι που χρειάζεστε)
Τύπος
Σε τι χρησιμεύει
Ορατό από έξω;
Σε αυτό το έργο
ClusterIP
Εσωτερική διεύθυνση στο σύμπλεγμα, προεπιλεγμένη τιμή
Ένα ClusterIP χωρίς εικονική IP: επιστρέφει τη λίστα των IP των Pods, συν ένα όνομα DNS ανά Pod
Όχι
bd-interne
ExternalName
Ψευδώνυμο DNS προς εξωτερικό όνομα. Κανένα Pod, κανένας επιλογέας.
Όχι
paiement-externe
Σημαντικό σημείο: ένα Service που «δεν λειτουργεί» σχεδόν ποτέ δεν είναι πρόβλημα τύπου. Είναι σχεδόν πάντα επιλογέας ή θύρα.
3. Εσωτερικό DNS: οι κανόνες που δίνουν την ψευδαίσθηση μαγείας
Στο σύμπλεγμα, το CoreDNS κατασκευάζει αυτόματα ονόματα σύμφωνα με σταθερούς κανόνες:
Καλείτε
Το CoreDNS επιλύει προς
api-produits
το Service api-produits του ίδιου χώρου ονομάτων
api-produits.default
το Service api-produits του χώρου ονομάτων default
api-produits.default.svc.cluster.local
μακρά και πλήρως προσδιορισμένη μορφή
Κεφαλαιώδες πόρισμα: το όνομα του Service είναι το όνομα που καλεί η εφαρμογή. Ένα Service με όνομα notificationδεν απαντά στο http://notifications. Αυτή η παγίδα είναι στην καρδιά της αποστολής 6.
4. StatefulSet και Service headless: το δίδυμο
Ένα Deployment αντιμετωπίζει τα αντίγραφά του ως εναλλάξιμα δίδυμα (web-abc123-x7k9, web-abc123-p2m1…). Ιδανικό για web χωρίς κατάσταση.
Ένα StatefulSet παράγει αντίθετα αριθμημένα και σταθερά Pods: bd-0, bd-1, bd-2. Κάθε Pod κρατά την ταυτότητά του μέσα από τις επανεκκινήσεις — απαραίτητο για μια βάση δεδομένων όπου πρέπει να ορίσουμε ακριβώς την πρωτεύουσα.
Αλλά ένα StatefulSet δεν αρκεί μόνο του: απαιτεί να είναι συνδεδεμένο με ένα Service headless του οποίου δηλώνει το όνομα στο πεδίο serviceName.
yaml
kind: StatefulSetspec: serviceName: bd-interne # <-- δείχνει προς ένα Service headless του ίδιου ονόματος replicas: 3
Αυτό το Service headless δίνει τότε ένα όνομα DNS ανά Pod:
bd-0.bd-interne -> IP του Pod bd-0 ΜΟΝΟbd-1.bd-interne -> IP του Pod bd-1 ΜΟΝΟbd-interne -> IP των τριών Pods (λίστα)
Χωρίς τη λέξη None στο spec.clusterIP, κανένα από αυτά τα ονόματα δεν υπάρχει.
yaml
spec: clusterIP: None # μετατρέπει το Service σε "headless"
Κρατήστε αυτό: για να φτάσετε το bd-0.bd-interne, χρειάζονται δύο ταυτόχρονες συνθήκες — ένα StatefulSet με serviceName: bd-interne, και ένα headless Service με όνομα bd-interne. Αν λείπει μία, το ατομικό όνομα δεν υπάρχει.
5. Ονομασμένες θύρες και Services πολλαπλών θυρών
Όταν ένα Service εκθέτει πολλές θύρες, κάθε εγγραφή γίνεται υποχρεωτικά ονομασμένη:
Χρησιμότητα: ο κώδικάς σας κρατά το ίδιο εσωτερικό όνομα (paiement-externe) είτε πρόκειται για υπηρεσία στο σύμπλεγμα, για εξωτερική υπηρεσία SaaS, είτε για μετακόμιση API. Αλλάζουμε το Service, όχι τον κώδικα.
7. Οι τρεις ερωτήσεις που ξεκλειδώνουν το 90 % των βλαβών
Κάθε φορά που ένα Service δεν λειτουργεί, θέστε αυτές τις τρεις ερωτήσεις με αυτή τη σειρά:
Αυτή είναι ακριβώς η μέθοδος της αποστολής 6.
Τι θα ξέρετε να κάνετε στο τέλος
Να διακρίνετε τους πέντε τύπους Services και να ξέρετε πότε να χρησιμοποιείτε καθέναν.
Να χρησιμοποιείτε τα kubectl get svc, kubectl describe svc, kubectl get endpoints ως τρία συμπληρωματικά εργαλεία.
Να συνδέετε σωστά ένα StatefulSet με ένα Service headless.
Να γράφετε ένα καθαρό Service πολλαπλών θυρών, με targetPort αναφερόμενο με όνομα.
Να διαγιγνώσκετε τις τρεις συχνότερες βλάβες στην επιχείρηση (εσφαλμένη ετικέτα, εσφαλμένη θύρα, εσφαλμένο όνομα).
Η αρχιτεκτονική προς θέση σε λειτουργία
Επτά στοιχεία τρέχουν ήδη. Κανένα δεν είναι προσβάσιμο.
Στοιχείο
Θύρα(-ες) του κοντέινερ
Ετικέτα των Pods
Ελεγκτής
portail
5000
app: portail
Deployment (1 αντίγραφο)
api-produits
8000
app: api-produits
Deployment (3 αντίγραφα)
api-commandes
8000
app: api-commandes
Deployment (2 αντίγραφα)
cache
6379
app: cache
Deployment (1 αντίγραφο)
notifications
7000
app: notifications
Deployment (2 αντίγραφα)
metriques
8080 (ονομασμένο web) και 9090 (ονομασμένο prom)
app: metriques
Deployment (2 αντίγραφα)
bd
5432
app: bd
StatefulSet (3 αντίγραφα)
Διάταξη των αρχείων
Δημιουργήστε ακριβώς αυτή τη δενδροδομή, αντιγράφοντας το περιεχόμενο των παραρτημάτων. Κάθε παράρτημα δηλώνει την ακριβή διαδρομή του αρχείου προς δημιουργία.
projet12-mission-services/├── 00-ENONCE.md <- αυτό το έγγραφο│├── apps/ <- Ο ΚΩΔΙΚΑΣ (ΠΑΡΑΡΤΗΜΑ A) — ΝΑ ΜΗΝ ΤΡΟΠΟΠΟΙΗΘΕΙ│ ├── micro/│ │ ├── app.py│ │ ├── requirements.txt│ │ └── Dockerfile│ ├── metriques/│ │ ├── app.py│ │ ├── requirements.txt│ │ └── Dockerfile│ └── portail/│ ├── app.py│ ├── requirements.txt│ └── Dockerfile│├── k8s/│ ├── 01-deployments.yaml <- ΠΑΡΕΧΕΤΑΙ (ΠΑΡΑΡΤΗΜΑ B) — ΝΑ ΜΗΝ ΤΡΟΠΟΠΟΙΗΘΕΙ│ ├── 02-statefulset-bd.yaml <- ΠΑΡΕΧΕΤΑΙ (ΠΑΡΑΡΤΗΜΑ B) — ΝΑ ΜΗΝ ΤΡΟΠΟΠΟΙΗΘΕΙ│ ││ └── services/ <- ΣΕΙΡΑ ΣΑΣ│ ├── 01-api-produits.yaml <- σκελετός προς συμπλήρωση (ΠΑΡΑΡΤΗΜΑ C)│ ├── 02-portail.yaml <- σκελετός προς συμπλήρωση (ΠΑΡΑΡΤΗΜΑ C)│ ├── 03-bd-interne.yaml <- σκελετός προς συμπλήρωση (ΠΑΡΑΡΤΗΜΑ C)│ ├── 04-metriques.yaml <- σκελετός προς συμπλήρωση (ΠΑΡΑΡΤΗΜΑ C)│ ├── 05-paiement-externe.yaml <- σκελετός προς συμπλήρωση (ΠΑΡΑΡΤΗΜΑ C)│ ││ └── 06-casses/ <- ΠΑΡΕΧΟΝΤΑΙ αλλά ΕΛΑΤΤΩΜΑΤΙΚΑ (ΠΑΡΑΡΤΗΜΑ D)│ ├── casse-1.yaml│ ├── casse-2.yaml│ └── casse-3.yaml│├── outils/│ └── valider.ps1 <- ΠΑΡΕΧΕΤΑΙ (ΠΑΡΑΡΤΗΜΑ E)│└── RAPPORT.md <- ΝΑ ΣΥΝΤΑΧΘΕΙ από εσάς
Τρεις εικόνες μόνο είναι απαραίτητες: το micro:1.0 χρησιμεύει σε πέντε διαφορετικά στοιχεία (η συμπεριφορά αλλάζει μέσω μεταβλητών περιβάλλοντος), το metriques:1.0 εκθέτει δύο θύρες, και το portail:1.0 εμφανίζει τον πίνακα ελέγχου.
Ο πίνακας ελέγχου: ο δείκτης προόδου σας
Το portail ανακρίνει συνεχώς κάθε στοιχείο και εμφανίζει ένα πλακίδιο ανά σύνδεση, ανανεωμένο κάθε 3 δευτερόλεπτα:
Πλακίδιο
Σημασία
Πού να αναζητήσετε το σφάλμα
ΚΟΚΚΙΝΟ
Το όνομα DNS δεν υπάρχει
Το Service δεν δημιουργήθηκε, ή το όνομά του είναι εσφαλμένο
ΠΟΡΤΟΚΑΛΙ
Το όνομα επιλύεται, αλλά κανείς δεν απαντά
Το Service υπάρχει, αλλά ο επιλογέας ή η θύρα του είναι εσφαλμένα
ΠΡΑΣΙΝΟ
Η επικοινωνία αποκαταστάθηκε
Το Service σας είναι σωστό
Τελικός στόχος: τα 8 πλακίδια πράσινα, και ο μετρητής να εμφανίζει 8 / 8.
Αυτή η διάκριση κόκκινο/πορτοκαλί δεν είναι διακοσμητική: σας λέει από ποια πλευρά να αναζητήσετε. Κόκκινο = το Service δεν υπάρχει (τίποτα προς αποσφαλμάτωση, πρέπει να γραφτεί). Πορτοκαλί = το Service υπάρχει αλλά δεν βρίσκει τα Pods του ή χτυπά στη λάθος θύρα.
Οι 8 επαληθευμένες συνδέσεις:
#
Πλακίδιο
Τι δοκιμάζει το portail
1
Εξωτερική πρόσβαση
Ότι συμβουλεύεστε όντως το portail μέσω της θύρας 30500
Απόλυτη απαγόρευση τροποποίησης του φακέλου apps/, καθώς και των 01-deployments.yaml και 02-statefulset-bd.yaml.
(Όλη η δυσκολία συνίσταται στην προσαρμογή στο υπάρχον: αυτή είναι ακριβώς η κατάσταση μιας πραγματικής θέσης εργασίας.)
Δημιουργείτε και τροποποιείτε μόνο αρχεία που βρίσκονται στο k8s/services/.
Καμία διεύθυνση IP στα σκληρά. Όλα πρέπει να βασίζονται στα ονόματα DNS και τους επιλογείς ετικετών.
Πρέπει να καθορίσετε μόνοι σας τον τύπο κάθε Service: τίποτα δεν σας λέει αν πρόκειται για ClusterIP, NodePort, LoadBalancer, υπηρεσία headless ή ExternalName. Αυτό είναι η καρδιά της αξιολόγησης.
Τα ονόματα των Services είναι επιβεβλημένα: ο κώδικας εφαρμογής τα καλεί ως έχουν. Ένα εσφαλμένο όνομα δίνει κόκκινο πλακίδιο.
Εργάζεστε στο ενσωματωμένο Kubernetes του Docker Desktop (Settings → Kubernetes → Enable Kubernetes).
Προετοιμασία
Προαπαιτούμενα — να επαληθευτούν μία μόνο φορά
Το Docker Desktop είναι εκκινημένο (πράσινο εικονίδιο στη γραμμή συστήματος).
Το Kubernetes είναι ενεργό στο Docker Desktop: Settings → Kubernetes → Enable Kubernetes → Apply & Restart. Χωρίς αυτό το πλαίσιο τσεκαρισμένο, καμία εντολή kubectl δεν θα λειτουργήσει.
Το Docker Desktop διαθέτει τουλάχιστον 4 GB RAM: Settings → Resources → Memory ≥ 4 GB. Αυτό το έργο εκκινεί 14 Pods· με 2 GB το μηχάνημα πνίγεται και Pods μένουν σε Pending.
Έχετε Διαδίκτυο (για την αποστολή 5 και για τη λήψη της εικόνας busybox).
Ακολουθία εκκίνησης
powershell
# 0) Τοποθέτηση στο σωστό σύμπλεγμα (απαραίτητο αν έχουν ήδη χρησιμοποιηθεί minikube ή kind)kubectl config use-context docker-desktopkubectl get nodes # πρέπει να εμφανίζει docker-desktop Ready# 1) Κατασκευή των τριών εικόνωνdocker build -t micro:1.0 ./apps/microdocker build -t metriques:1.0 ./apps/metriquesdocker build -t portail:1.0 ./apps/portail# 2) Ανάπτυξη της παρεχόμενης βάσης (Pods, και ΚΑΝΕΝΑ Service)kubectl apply -f k8s/01-deployments.yamlkubectl apply -f k8s/02-statefulset-bd.yaml# 3) Αναμονή να είναι Ready ΟΛΑ τα Pods (περίπου 30 s)kubectl wait --for=condition=ready pod --all --timeout=180s# 4) Διαπίστωση της αρχικής κατάστασηςkubectl get pods # 14 Pods, όλα Runningkubectl get svc # μόνο "kubernetes" : κανένα από τα Services σας
Σε αυτό το στάδιο: όλα τα Pods τρέχουν και τίποτα δεν επικοινωνεί. Αυτό είναι το κανονικό σημείο εκκίνησης.
Δύο τεχνικές προειδοποιήσεις που πρέπει να γνωρίζετε από τώρα — δεν είναι σφάλμα από μέρους σας:
Warning Endpoints is deprecated in v1.33+ — το Kubernetes εμφανίζει συστηματικά αυτό το μήνυμα σε κάθε kubectl get endpoints. Η εντολή λειτουργεί πάντα τέλεια, αγνοήστε την προειδοποίηση. Το νέο ισοδύναμο API είναι kubectl get endpointslices, αλλά όλες οι εντολές αυτού του εργαστηρίου χρησιμοποιούν ηθελημένα τα endpoints, πιο αναγνώσιμα για μάθηση.
Ο πίνακας ελέγχου μπορεί να χρειαστεί 10 έως 15 δευτερόλεπτα για να εμφανιστεί την πρώτη φορά: το portail δοκιμάζει 8 συνδέσεις δικτύου σε κάθε εμφάνιση, καθεμία με καθυστέρηση αναμονής 1,5 s. Όταν τίποτα δεν λειτουργεί ακόμη, περιμένει κάθε καθυστέρηση πριν εμφανίσει κόκκινο ή πορτοκαλί. Μόλις τα Services είναι σωστά, ο χρόνος απόκρισης πέφτει σε μερικές εκατοντάδες χιλιοστά του δευτερολέπτου.
Ερώτηση να θέσετε στον εαυτό σας αμέσως: πώς θα δείτε έστω τον πίνακα ελέγχου, αφού καμία πόρτα εισόδου δεν υπάρχει ακόμη;
Σωσίβιο: το kubectl port-forward λειτουργεί χωρίς κανένα Service, απευθείας σε ένα Pod.
powershell
kubectl port-forward deploy/portail 5000:5000
Έπειτα ανοίξτε το http://localhost:5000. Θα δείτε τον πίνακα ελέγχου εντελώς κόκκινο, με το πλακίδιο « Εξωτερική πρόσβαση » σε πορτοκαλί (κανονικό: δεν περάσατε από τη θύρα 30500).
Οι αποστολές
Αποστολή 1 — Να κάνετε το portail να μιλήσει στην API προϊόντων (15 βαθμοί)
Το portail καλεί το http://api-produits στη θύρα 80. Τα Pods της API ακούν στη θύρα 8000 και φέρουν την ετικέτα app: api-produits.
Αρχείο προς συμπλήρωση:k8s/services/01-api-produits.yaml
Προς καθορισμό: ο τύπος του Service, ο επιλογέας, καθώς και port και targetPort.
Επικύρωση:
powershell
kubectl apply -f k8s/services/01-api-produits.yamlkubectl get svc api-produitskubectl get endpoints api-produits # πρέπει να απαριθμεί 3 διευθύνσεις IP
Το πλακίδιο API Προϊόντων γίνεται πράσινο.
Αποστολή 2 — Άνοιγμα της πόρτας εισόδου (15 βαθμοί)
Ο πίνακας ελέγχου πρέπει να είναι προσβάσιμος από τον περιηγητή σας στην ακριβή διεύθυνση http://localhost:30500. Τα Pods του portail ακούν στη θύρα 5000.
Αρχείο προς συμπλήρωση:k8s/services/02-portail.yaml
Προς καθορισμό: ποιος τύπος Service εκθέτει μια εφαρμογή εκτός του συμπλέγματος σε σταθερή θύρα του μηχανήματος; Ποιο είναι το επιτρεπόμενο εύρος θυρών για αυτό το πεδίο;
Επικύρωση:
powershell
kubectl get svc portail # PORT(S) πρέπει να εμφανίζει 80:30500/TCPstart http://localhost:30500
Το πλακίδιο Εξωτερική πρόσβαση γίνεται πράσινο.
Ερώτηση προς επεξεργασία στην έκθεση: ένας άλλος τύπος Service θα καθιστούσε επίσης το portail προσβάσιμο από τον περιηγητή στο Docker Desktop. Ποιος; Και ποια διαφορά θα έκανε αυτό στην παραγωγή στο νέφος;
Αποστολή 3 — Να δώσετε ταυτότητα σε κάθε βάση δεδομένων (20 βαθμοί)
Το StatefulSet bd παρέχει 3 αντίγραφα. Το portail πρέπει να φτάσει ακριβώς την πρώτη (την πρωτεύουσα), στη διεύθυνση:
bd-0.bd-interne
Τα Pods της βάσης φέρουν την ετικέτα app: bd και ακούν στη θύρα 5432.
Αρχείο προς συμπλήρωση:k8s/services/03-bd-interne.yaml
Προς καθορισμό: ποιος τύπος Service δίνει ένα ατομικό όνομα DNS σε κάθε Pod, αντί για μία μοναδική εικονική IP; Ποιο πεδίο πρέπει να γραφτεί, και με ποια ιδιαίτερη τιμή;
Επικύρωση:
powershell
# 1) Από ένα βοηθητικό Pod, σύνδεση απευθείας στο bd-0 :kubectl run test --rm -i --restart=Never --image=busybox:1.36 -- wget -qO- http://bd-0.bd-interne:5432/ping# πρέπει να απαντήσει : {"pod":"bd-0","port":5432,"service":"base-de-donnees"}# 2) Επαλήθευση των εγγραφών DNS με το πλήρως προσδιορισμένο όνομα# (το busybox nslookup ΔΕΝ εφαρμόζει τα search domains, πρέπει να δοθεί το FQDN) :kubectl run test --rm -i --restart=Never --image=busybox:1.36 -- nslookup bd-0.bd-interne.default.svc.cluster.local# πρέπει να επιστρέψει ΜΙΑ μόνο διεύθυνση (αυτή του Pod bd-0)kubectl run test --rm -i --restart=Never --image=busybox:1.36 -- nslookup bd-interne.default.svc.cluster.local# πρέπει να επιστρέψει ΤΡΕΙΣ διευθύνσεις (μία ανά Pod του StatefulSet)
Παγίδα να μην χάσετε
εξετάστε το πεδίο serviceName του παρεχόμενου StatefulSet. Το όνομα του Service σας πρέπει να του αντιστοιχεί ακριβώς, αλλιώς τα ατομικά ονόματα των Pods δεν θα δημιουργηθούν ποτέ.
Τεχνική παγίδα (busybox)
το nslookup nom-court δεν λειτουργεί από ένα Pod busybox επειδή ο επιλύτης του δεν χρησιμοποιεί τα search domains του /etc/resolv.conf. Από ένα πραγματικό Pod εφαρμογής (όπως το portail), αντίθετα, το bd-0.bd-interneεπιλύεται τέλεια. Χρησιμοποιήστε λοιπόν το wget για να δοκιμάσετε την πραγματική αλυσίδα εφαρμογής, και το FQDN για να άρετε κάθε ασάφεια DNS.
Αποστολή 4 — Έκθεση δύο θυρών στο ίδιο Service (15 βαθμοί)
Το στοιχείο metriques ακούει σε δύο θύρες:
Χρήση
Θύρα του κοντέινερ
Όνομα θύρας δηλωμένο στο Deployment
Διεπαφή web
8080
web
Μετρικές
9090
prom
Το portail καλεί το http://metriques (θύρα 80) και το http://metriques:9090/metrics.
Αρχείο προς συμπλήρωση:k8s/services/04-metriques.yaml
Προς καθορισμό: πώς δηλώνουμε πολλές θύρες σε ένα Service; Ποιος περιορισμός γίνεται τότε υποχρεωτικός για κάθε εγγραφή; Και πώς κάνουμε το targetPort να δείχνει σε θύρα του κοντέινερ με το όνομά της αντί με τον αριθμό της, ώστε το Service να μένει έγκυρο ακόμη κι αν αλλάξει ο αριθμός;
Επικύρωση:
powershell
kubectl describe svc metriques # οι ΔΥΟ θύρες πρέπει να εμφανίζονται
Αποστολή 5 — Να δώσετε εσωτερικό όνομα σε εξωτερική υπηρεσία (10 βαθμοί)
Το portail πρέπει να φτάσει μια υπηρεσία πληρωμής φιλοξενημένη εκτός του συμπλέγματος, αλλά ο κώδικας καλεί ένα εσωτερικό όνομα: paiement-externe. Αυτό το όνομα πρέπει να παραπέμπει στο example.com.
Αρχείο προς συμπλήρωση:k8s/services/05-paiement-externe.yaml
Προς καθορισμό: ποιος τύπος Service δημιουργεί ένα απλό ψευδώνυμο DNS προς εξωτερικό όνομα, χωρίς επιλογέα και χωρίς κανένα Pod;
Επικύρωση:
powershell
kubectl run test --rm -i --restart=Never --image=busybox:1.36 -- nslookup paiement-externe.default.svc.cluster.local# πρέπει να εμφανίζει :# paiement-externe.default.svc.cluster.local canonical name = example.com# Name: example.com# Address: <IP publique>
Αυτή η αποστολή απαιτεί το σύμπλεγμα να μπορεί να επιλύει δημόσια ονόματα. Αν δεν έχετε καμία πρόσβαση στο Διαδίκτυο, αντικαταστήστε τον στόχο με api-produits.default.svc.cluster.local και σημειώστε το στην έκθεσή σας.
Ο φάκελος k8s/services/06-casses/ περιέχει τρία ήδη γραμμένα Services… που δεν λειτουργούν. Καθένα περιέχει ένα μόνο σφάλμα, και είναι τα τρία συχνότερα σφάλματα στην επιχείρηση.
powershell
kubectl apply -f k8s/services/06-casses/
Αρχείο
Παρατηρούμενο σύμπτωμα
casse-1.yaml
Το Service υπάρχει, αλλά το kubectl get endpoints επιστρέφει <none>
casse-2.yaml
Τα Endpoints είναι όντως γεμάτα, αλλά κάθε σύνδεση απορρίπτεται
casse-3.yaml
Το Service φαίνεται τέλειο, αλλά το portail δεν το φτάνει ποτέ
Για καθεμία από τις τρεις περιπτώσεις, η έκθεσή σας πρέπει να περιέχει:
την εντολή διάγνωσης που σας έβαλε στο ίχνος·
την ακριβή αιτία της βλάβης·
τη διόρθωση που εφαρμόστηκε·
την απόδειξη ότι η σύνδεση λειτουργεί (πράσινο πλακίδιο + έξοδος εντολής).
Συνιστώμενη μέθοδος: προχωρήστε ως ερευνητής. kubectl describe svc, kubectl get endpoints, kubectl get pods --show-labels, έπειτα συγκρίνετε γραμμή προς γραμμή το Service και τα Pods. Η διαφορά μεταξύ «κενά Endpoints» και «άρνηση σύνδεσης» σας δείχνει ήδη από ποια πλευρά να αναζητήσετε.
Αποστολή 7 — Μπόνους: το μεγάλο άλμα (5 βαθμοί)
Κατ' επιλογή, ένα μόνο αρκεί:
a) Να κάνετε ώστε ο ίδιος πελάτης να εξυπηρετείται πάντα από το ίδιο Pod της API προϊόντων. (Υπόδειξη: ένα πεδίο του Service επιτρέπει «προσκόλληση» βασισμένη στην IP του πελάτη.)
b) Να δημιουργήσετε ένα Service χωρίς επιλογέα που δείχνει σε σταθερή εξωτερική διεύθυνση IP, γράφοντας εσείς οι ίδιοι τα Endpoints του.
c) Να γράψετε ένα Service τύπου LoadBalancer για το portail, έπειτα να εξηγήσετε τι γίνεται το EXTERNAL-IP στο Docker Desktop, και τι θα γινόταν στο AWS.
Αυτόματη επικύρωση
Ένα σενάριο σας δίνει τη βαθμολογία σας ανά πάσα στιγμή:
powershell
.\outils\valider.ps1
Αν το PowerShell αρνηθεί να εκτελέσει το σενάριο με μήνυμα του τύπου l'exécution de scripts est désactivée sur ce système, παρακάμψτε τον περιορισμό για αυτή τη μόνη εντολή:
Το σενάριο δεν δίνει καμία λύση: δηλώνει μόνο τι αποτυγχάνει και πού να κοιτάξετε.
Παραδοτέα
Ο πλήρης φάκελος k8s/services/: τα 5 γραμμένα Services σας και τα 3 επισκευασμένα Services.
Ένα RAPPORT.md που περιέχει:
για κάθε Service: τον επιλεγμένο τύπο και μια αιτιολόγηση σε δύο προτάσεις («γιατί αυτός και όχι άλλος»)·
την πλήρη έρευνα της αποστολής 6 (εντολή → αιτία → διόρθωση → απόδειξη)·
ένα στιγμιότυπο του πίνακα ελέγχου που εμφανίζει 8 / 8·
ένα στιγμιότυπο του kubectl get svc που δείχνει όλα τα Services σας και τους τύπους τους·
τις απαντήσεις σας στις ερωτήσεις προβληματισμού.
Την τελική έξοδο του .\outils\valider.ps1.
Ερωτήσεις προβληματισμού
Γιατί η εφαρμογή δεν μπορούσε απολύτως να λειτουργήσει χωρίς Services, ενώ όλα τα Pods ήταν Running;
Ποια είναι η συγκεκριμένη διαφορά μεταξύ ενός κόκκινου πλακιδίου και ενός πορτοκαλί; Τι σας μαθαίνει καθένα για το πού βρίσκεται το σφάλμα;
Γιατί το Service της βάσης δεδομένων πρέπει να είναι headless, ενώ ένα συνηθισμένο Service αρκεί για την API προϊόντων;
Τι περιέχει ακριβώς η λίστα των Endpoints, και ποιος την ενημερώνει; Τι συμβαίνει όταν ένα Pod γίνεται NotReady;
Διαγράφετε ένα Pod της API προϊόντων· το Kubernetes αναδημιουργεί ένα με διαφορετική διεύθυνση IP. Γιατί το portail συνεχίζει να λειτουργεί χωρίς την παραμικρή τροποποίηση;
Στην παραγωγή, θα εκθέτατε δέκα εφαρμογές με δέκα Services τύπου LoadBalancer; Αιτιολογήστε, και προτείνετε εναλλακτική.
Βαθμολόγιο
Στοιχείο
Βαθμοί
Αποστολή 1 — Εσωτερικό Service και ανακάλυψη DNS
15
Αποστολή 2 — Εξωτερική έκθεση στη θύρα 30500
15
Αποστολή 3 — Service headless και σταθερές ταυτότητες
20
Αποστολή 4 — Πολλαπλές θύρες και ονομασμένες θύρες
15
Αποστολή 5 — Ψευδώνυμο προς εξωτερική υπηρεσία
10
Αποστολή 6 — Διάγνωση και επισκευή (3 βλάβες)
20
Ποιότητα της έκθεσης και αιτιολόγηση των επιλογών
5
Μπόνους — Αποστολή 7
+5
Σύνολο
100 (+5)
Ποινές: −10 ανά τροποποίηση απαγορευμένου αρχείου (apps/, 01-deployments.yaml, 02-statefulset-bd.yaml)· −5 ανά διεύθυνση IP κωδικοποιημένη στα σκληρά.
Κριτήρια επιτυχίας
Κριτήριο
Αναμενόμενο
Πίνακας ελέγχου
8 / 8 πράσινα πλακίδια
Τύποι Services
Καθένας προσαρμοσμένος στη χρήση του και αιτιολογημένος
Ατομικό DNS
Το bd-0.bd-interne επιλύεται προς ένα μόνο Pod
Πολλαπλές θύρες
Οι δύο θύρες ορατές, targetPort αναφερόμενο με όνομα
Έρευνα
Οι 3 βλάβες εντοπισμένες, εξηγημένες και διορθωμένες
Ανθεκτικότητα
Μετά τη διαγραφή ενός Pod, το portail συνεχίζει να λειτουργεί
Καμία IP στα σκληρά
Μόνο ονόματα DNS και επιλογείς ετικετών
Εργαλειοθήκη
Καμία λύση εδώ — μόνο ίχνη.
powershell
kubectl get svc # τύποι, IP, θύρεςkubectl describe svc <nom> # λεπτομέρειες + Endpointskubectl get endpoints <nom> # ΠΟΙΟΣ βρίσκεται πίσω από το Service ;kubectl get pods --show-labels # οι πραγματικές ετικέτες των Podskubectl get pods -l app=<valeur> # δοκιμή ενός επιλογέαkubectl port-forward deploy/portail 5000:5000 # πρόσβαση σε Pod ΧΩΡΙΣ Servicekubectl run test --rm -i --restart=Never --image=busybox:1.36 -- wget -qO- http://<nom>/pingkubectl run test --rm -i --restart=Never --image=busybox:1.36 -- nslookup <nom>.default.svc.cluster.localkubectl logs -l app=portail --tail=30 # αυτό που το portail δεν καταφέρνει να φτάσειkubectl delete svc <nom> # επανεκκίνηση από το μηδέν σε ένα Service
Παγίδα να γνωρίζετε με το kubectl run test: αν αλυσιδώσετε πολλές εντολές γρήγορα, το προηγούμενο Pod δεν διαγράφεται πάντα εγκαίρως και θα λάβετε:
Error from server (AlreadyExists): pods "test" already exists
Δύο λύσεις: αλλάξτε το όνομα (test1, test2…) σε κάθε εντολή, ή καθαρίστε πριν:
powershell
kubectl delete pod test --ignore-not-found; kubectl run test --rm -i --restart=Never ...
Οι τρεις ερωτήσεις που ξεκλειδώνουν το 90 % των καταστάσεων:
Το Service υπάρχει, με το σωστό όνομα; (αλλιώς → κόκκινο πλακίδιο: δεν υπάρχει τίποτα προς αποσφαλμάτωση, πρέπει να γραφτεί)
Τα Endpoints είναι γεμάτα; (κενά → ο επιλογέας δεν αντιστοιχεί σε καμία ετικέτα Pod)
Το targetPort αντιστοιχεί στη θύρα που ακούει πραγματικά το κοντέινερ; (αλλιώς → άρνηση σύνδεσης)
ΠΑΡΑΡΤΗΜΑ A — Οι εφαρμογές
Μην τροποποιήσετε κανένα από αυτά τα αρχεία. Αντιγράψτε τα ως έχουν στις υποδεικνυόμενες διαδρομές.
A.1 — Η γενική μικροϋπηρεσία
Αυτή η μοναδική εφαρμογή χρησιμεύει σε πέντε στοιχεία (api-produits, api-commandes, cache, notifications, bd). Το όνομα και η θύρα της καθορίζονται από μεταβλητές περιβάλλοντος.
Αρχείο: apps/micro/app.py
python
"""Γενική μικροϋπηρεσία επίδειξης.Η ίδια εικόνα χρησιμεύει σε πολλά στοιχεία : το όνομα και η θύρα ακρόασηςπαρέχονται από μεταβλητές περιβάλλοντος (APP_NAME, PORT).Κάθε απάντηση περιέχει το όνομα του Pod, κάτι που καθιστά ορατή την κατανομήφορτίου που πραγματοποιεί ένα Service."""import osimport socketfrom flask import Flask, jsonifyapp = Flask(__name__)NOM = os.environ.get("APP_NAME", "micro")PORT = int(os.environ.get("PORT", "8000"))@app.route("/")@app.route("/ping")def ping(): return jsonify(service=NOM, pod=socket.gethostname(), port=PORT)@app.route("/health")def health(): return "OK", 200if __name__ == "__main__": app.run(host="0.0.0.0", port=PORT)
# ---------------------------------------------------------------------------# Η ΒΑΣΗ ΔΕΔΟΜΕΝΩΝ (3 αντίγραφα) — ΠΑΡΕΧΕΤΑΙ, ΝΑ ΜΗΝ ΤΡΟΠΟΠΟΙΗΘΕΙ## ΠΡΟΣΟΧΗ : το πεδίο serviceName παρακάτω επιβάλλει το ΟΝΟΜΑ του Service που# θα πρέπει να γράψετε ώστε τα bd-0, bd-1 και bd-2 να λάβει καθένα ένα όνομα DNS.# ---------------------------------------------------------------------------apiVersion: apps/v1kind: StatefulSetmetadata: name: bdspec: serviceName: bd-interne # <-- διαβάστε καλά αυτή τη γραμμή replicas: 3 selector: matchLabels: app: bd template: metadata: labels: app: bd spec: containers: - name: micro image: micro:1.0 imagePullPolicy: IfNotPresent env: - { name: APP_NAME, value: "base-de-donnees" } - { name: PORT, value: "5432" } ports: - containerPort: 5432 readinessProbe: httpGet: { path: /health, port: 5432 } initialDelaySeconds: 3 periodSeconds: 5
ΠΑΡΑΡΤΗΜΑ C — Οι σκελετοί Services προς συμπλήρωση
Αντιγράψτε αυτά τα πέντε αρχεία, έπειτα αντικαταστήστε κάθε TODO με τη σωστή τιμή.
Οι γραμμές που προηγούνται από # ? είναι ερωτήσεις προς απόφαση: σε εσάς εναπόκειται να αποφασίσετε αν πρέπει να προσθέσετε, να τροποποιήσετε ή να διαγράψετε την εν λόγω γραμμή.
Αρχείο: k8s/services/01-api-produits.yaml
yaml
# ΑΠΟΣΤΟΛΗ 1 — Να καταστήσετε την API προϊόντων προσβάσιμη από το portail.## Το portail καλεί : http://api-produits (άρα τη θύρα 80)# Τα Pods ακούν στο : 8000# Τα Pods φέρουν την ετικέτα : app: api-produits## ? Ποιος τύπος Service για ΕΣΩΤΕΡΙΚΗ επικοινωνία στο σύμπλεγμα ;apiVersion: v1kind: Servicemetadata: name: api-produits # ΕΠΙΒΕΒΛΗΜΕΝΟ όνομα : να μην αλλάξειspec: type: TODO selector: TODO: TODO ports: - port: TODO # η θύρα μέσω της οποίας καλούν οι πελάτες targetPort: TODO # η θύρα που ακούει πραγματικά το κοντέινερ
Αρχείο: k8s/services/02-portail.yaml
yaml
# ΑΠΟΣΤΟΛΗ 2 — Να καταστήσετε τον πίνακα ελέγχου προσβάσιμο από τον περιηγητή,# στην ακριβή διεύθυνση : http://localhost:30500## Τα Pods ακούν στο : 5000# Τα Pods φέρουν την ετικέτα : app: portail## ? Ποιος τύπος Service ανοίγει μια θύρα στο ΜΗΧΑΝΗΜΑ ;# ? Ποιο είναι το επιτρεπόμενο εύρος για αυτή τη θύρα ;# ? Ποιο συμπληρωματικό πεδίο πρέπει να προστεθεί για να επιβληθεί η θύρα 30500 ;apiVersion: v1kind: Servicemetadata: name: portail # ΕΠΙΒΕΒΛΗΜΕΝΟ όνομα : να μην αλλάξειspec: type: TODO selector: TODO: TODO ports: - port: TODO targetPort: TODO # ? μια γραμμή λείπει εδώ
Αρχείο: k8s/services/03-bd-interne.yaml
yaml
# ΑΠΟΣΤΟΛΗ 3 — Να δώσετε ΑΤΟΜΙΚΟ όνομα DNS σε κάθε αντίγραφο της βάσης,# ώστε να μπορείτε να φτάσετε ακριβώς : bd-0.bd-interne## Τα Pods ακούν στο : 5432# Τα Pods φέρουν την ετικέτα : app: bd## ? Ποιος τύπος Service ΔΕΝ διαθέτει μοναδική εικονική IP ;# ? Ποιο πεδίο, με ποια πολύ ιδιαίτερη τιμή, παράγει αυτό το αποτέλεσμα ;# ? Το όνομα παρακάτω πρέπει να αντιστοιχεί σε ποιο πεδίο του StatefulSet ;apiVersion: v1kind: Servicemetadata: name: bd-interne # ΕΠΙΒΕΒΛΗΜΕΝΟ όνομα : να μην αλλάξειspec: # ? μια ουσιώδης γραμμή λείπει εδώ selector: TODO: TODO ports: - port: TODO targetPort: TODO
Αρχείο: k8s/services/04-metriques.yaml
yaml
# ΑΠΟΣΤΟΛΗ 4 — Έκθεση ΔΥΟ θυρών στο ίδιο Service.## Το portail καλεί : http://metriques (θύρα 80)# και : http://metriques:9090/metrics## Τα Pods ακούν στο : 8080 (θύρα με όνομα "web") και 9090 (θύρα με όνομα "prom")# Τα Pods φέρουν την ετικέτα : app: metriques## ? Ποιος περιορισμός γίνεται ΥΠΟΧΡΕΩΤΙΚΟΣ μόλις ένα Service εκθέτει πολλές θύρες ;# ? Πώς κάνουμε το targetPort να δείχνει σε θύρα του κοντέινερ ΜΕ ΤΟ ΟΝΟΜΑ ΤΗΣ ;apiVersion: v1kind: Servicemetadata: name: metriques # ΕΠΙΒΕΒΛΗΜΕΝΟ όνομα : να μην αλλάξειspec: type: TODO selector: TODO: TODO ports: - TODO: TODO # ? ένα υποχρεωτικό πεδίο λείπει σε κάθε εγγραφή port: TODO targetPort: TODO - TODO: TODO port: TODO targetPort: TODO
Αρχείο: k8s/services/05-paiement-externe.yaml
yaml
# ΑΠΟΣΤΟΛΗ 5 — Να δείξει ένα ΕΣΩΤΕΡΙΚΟ όνομα προς ΕΞΩΤΕΡΙΚΗ υπηρεσία.## Το portail χρησιμοποιεί το όνομα : paiement-externe# Αυτό το όνομα πρέπει να παραπέμπει στο : example.com## ? Ποιος τύπος Service δημιουργεί ένα απλό ψευδώνυμο DNS (CNAME) ;# ? Αυτός ο τύπος διαθέτει επιλογέα ; θύρες ; Pods ;apiVersion: v1kind: Servicemetadata: name: paiement-externe # ΕΠΙΒΕΒΛΗΜΕΝΟ όνομα : να μην αλλάξειspec: type: TODO TODO: TODO # ? το πεδίο που δηλώνει τον εξωτερικό στόχο
ΠΑΡΑΡΤΗΜΑ D — Τα τρία ελαττωματικά Services
Αντιγράψτε αυτά τα τρία αρχεία ως έχουν, εφαρμόστε τα, έπειτα διαγνώστε και διορθώστε.
Καθένα περιέχει ακριβώς ένα σφάλμα. Μην ξαναγράψετε το αρχείο από το μηδέν: βρείτε το λάθος.
Αρχείο: k8s/services/06-casses/casse-1.yaml
yaml
# ΒΛΑΒΗ 1# Σύμπτωμα : το Service υπάρχει, αλλά το "kubectl get endpoints api-commandes"# επιστρέφει <none>. Το portail εμφανίζει ΠΟΡΤΟΚΑΛΙ πλακίδιο.apiVersion: v1kind: Servicemetadata: name: api-commandesspec: type: ClusterIP selector: app: api-commande ports: - port: 80 targetPort: 8000
Αρχείο: k8s/services/06-casses/casse-2.yaml
yaml
# ΒΛΑΒΗ 2# Σύμπτωμα : το "kubectl get endpoints cache" εμφανίζει όντως μια διεύθυνση IP,# αλλά κάθε σύνδεση αποτυγχάνει. Το portail εμφανίζει ΠΟΡΤΟΚΑΛΙ πλακίδιο.apiVersion: v1kind: Servicemetadata: name: cachespec: type: ClusterIP selector: app: cache ports: - port: 80 targetPort: 6380
Αρχείο: k8s/services/06-casses/casse-3.yaml
yaml
# ΒΛΑΒΗ 3# Σύμπτωμα : αυτό το Service φαίνεται τέλειο (σωστός τύπος, σωστός επιλογέας,# γεμάτα Endpoints, συνεπείς θύρες)... και όμως το portail# εμφανίζει ΚΟΚΚΙΝΟ πλακίδιο και δεν το φτάνει ΠΟΤΕ.apiVersion: v1kind: Servicemetadata: name: notificationspec: type: ClusterIP selector: app: notifications ports: - port: 80 targetPort: 7000
ΠΑΡΑΡΤΗΜΑ E — Το σενάριο επικύρωσης
Αρχείο: outils/valider.ps1
powershell
# ---------------------------------------------------------------------------# Σενάριο επικύρωσης — δίνει βαθμολογία, ΠΟΤΕ τη λύση.# Χρήση : .\outils\valider.ps1# ---------------------------------------------------------------------------$total = 0function Existe($nom) { kubectl get svc $nom -o name 2>$null | Out-Null return $LASTEXITCODE -eq 0}function Afficher($libelle, $points, $max, $note) { $etat = if ($points -eq $max) { "[OK] " } else { "[ECHEC] " } $ligne = "{0} {1} {2}/{3}" -f $etat, $libelle.PadRight(34, '.'), $points, $max if ($note) { $ligne += " -> $note" } Write-Host $ligne}Write-Host ""Write-Host "=== VALIDATION — Mission : retablir les communications ===" -ForegroundColor CyanWrite-Host ""# --- Αποστολή 1 : api-produits ---------------------------------------------$p = 0; $note = ""if (-not (Existe "api-produits")) { $note = "Service api-produits introuvable" }else { $eps = (kubectl get endpoints api-produits -o jsonpath="{.subsets[*].addresses[*].ip}" 2>$null) $tp = (kubectl get svc api-produits -o jsonpath="{.spec.ports[0].targetPort}" 2>$null) if (-not $eps) { $note = "Endpoints vides : le selecteur ne correspond a aucun Pod" } elseif ("$tp" -ne "8000") { $note = "targetPort ne correspond pas au port ecoute" } else { $p = 15 }}Afficher "Mission 1 - api-produits" $p 15 $note; $total += $p# --- Αποστολή 2 : portail ---------------------------------------------------$p = 0; $note = ""if (-not (Existe "portail")) { $note = "Service portail introuvable" }else { $type = (kubectl get svc portail -o jsonpath="{.spec.type}" 2>$null) $np = (kubectl get svc portail -o jsonpath="{.spec.ports[0].nodePort}" 2>$null) if ("$np" -ne "30500") { $note = "le port expose sur la machine doit etre 30500 (actuel : '$np')" } elseif ($type -notin @("NodePort", "LoadBalancer")) { $note = "type inadapte a un acces externe" } else { $p = 15 }}Afficher "Mission 2 - portail" $p 15 $note; $total += $p# --- Αποστολή 3 : bd-interne (headless) -------------------------------------$p = 0; $note = ""if (-not (Existe "bd-interne")) { $note = "Service bd-interne introuvable (verifiez serviceName du StatefulSet)" }else { $cip = (kubectl get svc bd-interne -o jsonpath="{.spec.clusterIP}" 2>$null) $eps = (kubectl get endpoints bd-interne -o jsonpath="{.subsets[*].addresses[*].ip}" 2>$null) if ("$cip" -ne "None") { $note = "ce Service ne doit PAS avoir d'IP virtuelle" } elseif (-not $eps) { $note = "Endpoints vides : verifiez le selecteur" } else { $p = 20 }}Afficher "Mission 3 - bd-interne" $p 20 $note; $total += $p# --- Αποστολή 4 : metriques (multi-port) ------------------------------------$p = 0; $note = ""if (-not (Existe "metriques")) { $note = "Service metriques introuvable" }else { $ports = (kubectl get svc metriques -o jsonpath="{.spec.ports[*].port}" 2>$null) $noms = (kubectl get svc metriques -o jsonpath="{.spec.ports[*].name}" 2>$null) $cible = (kubectl get svc metriques -o jsonpath="{.spec.ports[*].targetPort}" 2>$null) $liste = ($ports -split '\s+') | Where-Object { $_ } if ($liste.Count -lt 2) { $note = "il manque un port : deux sont attendus (80 et 9090)" } elseif (-not $noms) { $note = "chaque port doit porter un nom lorsqu'il y en a plusieurs" } elseif ($cible -match '^\s*\d+(\s+\d+)*\s*$') { $note = "targetPort doit referencer les ports PAR LEUR NOM" } else { $p = 15 }}Afficher "Mission 4 - metriques" $p 15 $note; $total += $p# --- Αποστολή 5 : paiement-externe -------------------------------------------$p = 0; $note = ""if (-not (Existe "paiement-externe")) { $note = "Service paiement-externe introuvable" }else { $type = (kubectl get svc paiement-externe -o jsonpath="{.spec.type}" 2>$null) $cible = (kubectl get svc paiement-externe -o jsonpath="{.spec.externalName}" 2>$null) if ("$type" -ne "ExternalName") { $note = "ce n'est pas le type attendu pour un alias DNS" } elseif (-not $cible) { $note = "la cible externe n'est pas renseignee" } else { $p = 10 }}Afficher "Mission 5 - paiement-externe" $p 10 $note; $total += $p# --- Αποστολή 6 : οι τρεις επισκευές --------------------------------------$p = 0; $notes = @()foreach ($cas in @( @{ nom = "api-commandes"; port = "8000" }, @{ nom = "cache"; port = "6379" }, @{ nom = "notifications"; port = "7000" })) { if (-not (Existe $cas.nom)) { $notes += "$($cas.nom) : Service introuvable"; continue } $eps = (kubectl get endpoints $cas.nom -o jsonpath="{.subsets[*].addresses[*].ip}" 2>$null) $tp = (kubectl get svc $cas.nom -o jsonpath="{.spec.ports[0].targetPort}" 2>$null) if (-not $eps) { $notes += "$($cas.nom) : Endpoints vides" } elseif ("$tp" -ne $cas.port) { $notes += "$($cas.nom) : aucun Pod ne repond sur ce port" } else { $p += 7 }}if ($p -gt 20) { $p = 20 }Afficher "Mission 6 - reparations" $p 20 ($notes -join " | "); $total += $pWrite-Host ""$couleur = if ($total -ge 90) { "Green" } elseif ($total -ge 50) { "Yellow" } else { "Red" }Write-Host ("SCORE AUTOMATIQUE : {0} / 95" -f $total) -ForegroundColor $couleurWrite-Host " (+5 pour la qualite du rapport, +5 de bonus : evalues manuellement)" -ForegroundColor DarkGrayWrite-Host ""Write-Host "Rappel : le tableau de bord doit afficher 8 / 8 sur http://localhost:30500" -ForegroundColor DarkGrayWrite-Host ""
Μάθημα δημιουργημένο από τον Dr. Haythem REHOUMA — Ανάπτυξη και ανάπτυξη λύσεων δεδομένων