Γιατί Kubernetes, και γιατί Docker Desktop

13 λεπτά
Κοινό
προγραμματιστής ή φοιτητής που έχει ήδη πληκτρολογήσει docker run και docker compose up, ποτέ kubectl
Διάρκεια
25 έως 35 λεπτά
Ενότητα
1/8
Στοχευόμενη δεξιότητα
να εξηγείς τι προσθέτει το Kubernetes στο Docker, να ονομάζεις τα στοιχεία του control plane και ενός κόμβου, και να δικαιολογείς την επιλογή του Docker Desktop για μάθηση

Σε μία εικόνα

Ξέρεις τον θερμοστάτη του σαλονιού σου: ρυθμίζεις 21 °C και δεν το ξανασκέφτεσαι. Μετράει τη θερμοκρασία, τη συγκρίνει με την επιθυμητή τιμή σου, θερμαίνει ή σταματά, και αν κάποιος ανοίξει το παράθυρο το αναπληρώνει χωρίς να κουνηθείς. Το Kubernetes κάνει ακριβώς αυτό με τα containers σου. Δηλώνεις «θέλω τρία αντίγραφα του API μου, πάντα προσβάσιμα στη θύρα 8080»· συγκρίνει διαρκώς αυτό που θέλεις με αυτό που τρέχει, και διορθώνει: ένα container πεθαίνει στις τρεις το πρωί, ξαναεκκινεί ένα· μια μηχανή πέφτει, μεταφέρει τα containers αλλού· αλλάζεις την έκδοση της εικόνας, αντικαθιστά τα αντίγραφα ένα προς ένα χωρίς να διακόψει την υπηρεσία. Με docker run, εσύ είσαι ο θερμοστάτης: εσύ παρακολουθείς και εσύ επανεκκινείς. Σε αυτό το μάθημα, θα δεις τη διαφορά με τα ίδια σου τα μάτια: θα σκοτώσεις ένα container Docker και θα διαπιστώσεις ότι μένει νεκρό, έπειτα θα κοιτάξεις τα «κομμάτια» του Kubernetes που τρέχουν ήδη στο μηχάνημά σου, έτοιμα να κάνουν αυτή τη δουλειά στη θέση σου.

Πώς λειτουργεί

Τρεις καταστάσεις που όλοι καταλήγουν να ζήσουν με το Docker μόνο. Ένα: το API σου τρέχει σε ένα container σε έναν server· ένα βράδυ καταρρέει (διαρροή μνήμης, εξωτερική εξάρτηση σε βλάβη) και κανείς δεν το βλέπει πριν το πρωί. Το --restart=always επανεκκινεί τη διεργασία, αλλά δεν σου λέει τίποτα, δεν επαληθεύει ότι η εφαρμογή απαντά πραγματικά, και δεν κάνει τίποτα αν σβήσει ολόκληρος ο server. Δύο: έχεις δέκα containers του ίδιου API πίσω από ένα reverse proxy και πρέπει να παραδώσεις την έκδοση 2 χωρίς οι χρήστες να δουν λάθος· με το χέρι, αυτό σημαίνει να σταματήσεις, να επανεκκινήσεις και να αφαιρέσεις από το proxy κάθε container στη σωστή σειρά, δέκα φορές, χωρίς λάθος. Τρία: η κίνησή σου δεν χωράει πια σε ένα μηχάνημα· πρέπει να προσθέσεις δύο, να αποφασίσεις ποιο container πάει πού, και τα containers να βρίσκουν το ένα το άλλο ενώ οι διευθύνσεις IP τους αλλάζουν σε κάθε επανεκκίνηση.

Αυτά τα τρία προβλήματα είναι η δουλειά ενός ορχηστρωτή. Το Docker προσφέρει δύο μερικές απαντήσεις: το Compose περιγράφει πολλά containers σε ένα αρχείο αλλά μένει σε ένα μόνο μηχάνημα και δεν παρακολουθεί τίποτα· το Swarm προσθέτει τα πολλαπλά μηχανήματα και τα replicas αλλά το οικοσύστημά του έμεινε μικρό. Το Kubernetes (K8s: «K», οκτώ γράμματα, «s»), γεννημένο στη Google το 2014 και σήμερα διαχειριζόμενο από το CNCF, έγινε το πρότυπο: αυτό θα ξαναβρείς στην AWS (EKS), στη Google (GKE), στην Azure (AKS), στην OVH, στη Scaleway, και στους servers των περισσότερων επιχειρήσεων.

ΚριτήριοDocker ComposeDocker SwarmKubernetes
Εμβέλειαένα μηχάνημαπολλά μηχανήματαπολλά μηχανήματα (χιλιάδες)
Αυτόματη επανεκκίνησημόνο restart:ναιναι, με ανιχνευτές υγείας (readiness, liveness)
Ενημέρωση χωρίς διακοπήόχιναι, βασικήναι, προοδευτική, με επιστροφή πίσω με μία εντολή
Ενσωματωμένη κατανομή φορτίουόχιναιναι (Services), πλέον Ingress, εσωτερικό DNS
Αυτόματη κλιμάκωσηόχιόχιναι (HPA, σε CPU, μνήμη ή επιχειρησιακές μετρικές)
Οικοσύστημα (Helm, operators, cloud)μικρόαπέραντο
Καμπύλη μάθησηςήπιαμέτριααπότομη, εξ ου και αυτό το μάθημα

Ένα cluster Kubernetes αποτελείται από δύο μέρη. Το control plane αποφασίζει· οι κόμβοι εκτελούν. Στο Docker Desktop, τα δύο ζουν στην ίδια εικονική μηχανή, αλλά οι ρόλοι μένουν οι ίδιοι όπως στην παραγωγή.

ΣτοιχείοΠούΡόλος σε μία φράση
kube-apiservercontrol planeΛαμβάνει όλες τις εντολές (kubectl, controllers, kubelet) σε HTTPS στη θύρα 6443· τίποτα δεν συμβαίνει χωρίς να περάσει από αυτόν.
etcdcontrol planeΒάση κλειδιού-τιμής που αποθηκεύει την επιθυμητή κατάσταση και την παρατηρούμενη κατάσταση ολόκληρου του cluster· να το χάσεις χωρίς αντίγραφο ασφαλείας σημαίνει να χάσεις το cluster.
kube-schedulercontrol planeΕπιλέγει σε ποιον κόμβο θα τοποθετηθεί κάθε νέο Pod (ελεύθεροι πόροι, περιορισμοί). Δεν εκκινεί τίποτα ο ίδιος.
kube-controller-managercontrol planeΣυγκεντρώνει τους βρόχους συμφιλίωσης: «λείπει ένα replica; δημιουργώ ένα», «ένας κόμβος δεν απαντά πια; σημειώνω τα Pods του για επανατοποθέτηση».
kubeletκάθε κόμβοςΟ πράκτορας που διαβάζει στο API τα Pods που του έχουν ανατεθεί, ζητά από το runtime να εκκινήσει τα containers, και αναφέρει την κατάστασή τους.
kube-proxyκάθε κόμβοςΠρογραμματίζει τους κανόνες δικτύου ώστε μια διεύθυνση Service να καταλήγει στο σωστό Pod.
runtimeκάθε κόμβοςΤο λογισμικό που εκτελεί πραγματικά τα containers: containerd ή CRI-O γενικά, docker://29.3.1 στο Docker Desktop.

Η καρδιά του μοντέλου είναι η δήλωση επιθυμητής κατάστασης. Δεν διατάζεις «εκκίνησε ένα container»· γράφεις (ή ζητάς να παραχθεί) ένα αντικείμενο που λέει «θέλω ένα Deployment api, εικόνα traefik/whoami:v1.10, 3 replicas». Το API το καταγράφει στο etcd. Οι controllers συγκρίνουν έπειτα, σε βρόχο και για πάντα, αυτή την επιθυμητή κατάσταση με την παρατηρούμενη κατάσταση που αναφέρουν τα kubelets· κάθε απόκλιση πυροδοτεί μια ενέργεια. Γι' αυτό ένα διαγραμμένο Pod ξαναγεννιέται: κανείς δεν το «επανεκκίνησε», ο controller απλώς διαπίστωσε 2 αντί για 3 και διόρθωσε. Θα δεις τον βρόχο σε δράση στο μάθημα 04.

Γιατί να μάθεις στο Docker Desktop παρά στο minikube, στο kind ή σε ένα cluster cloud; Γιατί το έχεις πιθανότατα ήδη, γιατί ένα κλικ ενεργοποιεί ένα πλήρες cluster, γιατί οι εικόνες που χτίζεις με docker build είναι άμεσα ορατές από το Kubernetes (ίδιο αποθετήριο εικόνων, κανένα registry να εγκαταστήσεις), και γιατί τα Services τύπου LoadBalancer λαμβάνουν τη διεύθυνση localhost: ανοίγεις τον browser σου και απαντά, όπως στην παραγωγή πίσω από έναν πραγματικό εξισορροπητή φορτίου. Τα όριά του είναι εξίσου ξεκάθαρα: ένας μόνο κόμβος (καμία «μηχανή που πέφτει» να προσομοιώσεις), καμία NetworkPolicy που να εφαρμόζεται από προεπιλογή (το ενσωματωμένο CNI τις αγνοεί), κανένα δικτυακό storage ούτε δημόσια IP. Όλα όσα θα γράψεις εδώ θα εφαρμοστούν ως έχουν σε ένα πραγματικό cluster· μόνο ο τρόπος πρόσβασης σε αυτό θα αλλάξει.

ΕπιλογήΕγκατάστασηΚόμβοιLoadBalancer στο localhostΤοπικές εικόνες χωρίς registryΓια ποιον
Docker Desktop (αυτό το μάθημα)ένα κλικ στην εφαρμογή1 (kubeadm) ή πολλοί (kind, 4.38+)ναιναιμάθηση και ανάπτυξη στον σταθμό εργασίας σου
minikubeεκτελέσιμο + driver (Docker, VM)1, πολλαπλοί δυνατοίμέσω minikube tunnelμέσω minikube image loadμάθηση, add-ons έτοιμα προς χρήση
kindεκτελέσιμο, κόμβοι = containers Dockerπολλοίόχι (port-forward)μέσω kind load docker-imageαυτοματοποιημένα tests, CI
Cloud (EKS, GKE, AKS…)λογαριασμός, χρέωση, δίκτυοόσοι θέλειςπραγματική δημόσια IPόχι, χρειάζεται registryπαραγωγή

Βήμα προς βήμα

Όλες οι εντολές πληκτρολογούνται στο PowerShell (Windows) ή σε ένα τερματικό (macOS, Linux)· είναι ταυτόσημες. Τίποτα από όσα ακολουθούν δεν τροποποιεί το cluster σου: παρατηρείς.

  1. Εκκίνησε ένα container με Docker, όπως ήδη ξέρεις να κάνεις. Το traefik/whoami είναι μια μίνι εφαρμογή web που εμφανίζει το όνομα της μηχανής που απαντά: είναι η εικόνα-οδηγός των τριών πρώτων ενοτήτων.

    bash
    docker run -d --name essai-whoami -p 8089:80 traefik/whoami:v1.10
    docker ps --filter name=essai-whoami --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"

    Πραγματική έξοδος:

    text
    a088124875835ed375ddb25a4e4246faf62416a9772751ec7eedecc48bb758f8
    NAMES          IMAGE                  STATUS                  PORTS
    essai-whoami   traefik/whoami:v1.10   Up Less than a second   0.0.0.0:8089->80/tcp, [::]:8089->80/tcp

    Τι πρέπει να δεις: μία γραμμή, Up, η θύρα 8089 του μηχανήματός σου ανακατευθυνόμενη στην 80 του container.

  2. Ρώτησέ το. Το Hostname που επιστρέφεται είναι το αναγνωριστικό του container: κράτα αυτή τη λεπτομέρεια, θα γίνει το όνομα ενός Pod στο μάθημα 04.

    bash
    curl -s http://localhost:8089

    Σε Windows, πληκτρολόγησε curl.exe (το curl χωρίς επέκταση είναι ψευδώνυμο PowerShell του Invoke-WebRequest). Πραγματική έξοδος (δύο πρώτες γραμμές):

    text
    Hostname: a08812487583
    IP: 127.0.0.1
  3. Κοίτα καλά τι θα συμβεί: σκότωσε το container. Είναι η βλάβη των τριών το πρωί, σε γρήγορη κίνηση.

    bash
    docker rm -f essai-whoami
    docker ps --filter name=essai-whoami --format "table {{.Names}}\t{{.Status}}"

    Πραγματική έξοδος:

    text
    essai-whoami
    NAMES     STATUS

    Τι πρέπει να δεις: ο πίνακας είναι άδειος. Κανείς δεν θα επανεκκινήσει αυτό το container· το curl http://localhost:8089 αποτυγχάνει πλέον (curl: (52) Empty reply from server, (56) Recv failure ή Connection refused ανάλογα με τη στιγμή που το Docker Desktop απελευθερώνει τη θύρα). Το Docker έκανε αυτό που του είπαν, τίποτα περισσότερο, τίποτα λιγότερο. Κράτα αυτή την εικόνα στο μυαλό σου: στο μάθημα 04, θα κάνεις το ίδιο σε ένα Pod και θα έχει επιστρέψει πριν προλάβεις να ξαναδιαβάσεις τη γραμμή.

  4. Επαλήθευσε ότι το Kubernetes είναι εκεί. Αν δεν έχεις ακόμη ενεργοποιήσει το Kubernetes στο Docker Desktop, πήγαινε στο μάθημα 02 και γύρνα πίσω· αλλιώς, κοίτα το cluster σου.

    bash
    kubectl get nodes

    Πραγματική έξοδος:

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

    Τι πρέπει να δεις: ένας μόνο κόμβος, Ready, ρόλος control-plane. Στο Docker Desktop, η μηχανή που αποφασίζει και αυτή που εκτελεί είναι η ίδια. Το AGE είναι η ηλικία του cluster σου, το VERSION αυτή του Kubernetes (1.34 εδώ).

  5. Άγγιξε το control plane με το δάχτυλο. Τα τέσσερα στοιχεία «που αποφασίζουν» τρέχουν τα ίδια ως Pods, στο δεσμευμένο namespace kube-system, με ετικέτα tier=control-plane.

    bash
    kubectl get pods -n kube-system -l tier=control-plane

    Πραγματική έξοδος:

    text
    NAME                                     READY   STATUS    RESTARTS   AGE
    etcd-docker-desktop                      1/1     Running   0          18d
    kube-apiserver-docker-desktop            1/1     Running   0          18d
    kube-controller-manager-docker-desktop   1/1     Running   0          18d
    kube-scheduler-docker-desktop            1/1     Running   0          18d

    Τι πρέπει να δεις: etcd, ο API server, ο controller-manager και ο scheduler, καθένας 1/1 Running, 0 επανεκκινήσεις. Το όνομά τους τελειώνει με το όνομα του κόμβου που τα φιλοξενεί. Σύγκρινε με τον πίνακα του «Πώς λειτουργεί»: έχεις τώρα ένα πρόσωπο για κάθε ρόλο.

  6. Δες το υπόλοιπο του kube-system. Χωρίς το φίλτρο, ανακαλύπτεις τα στοιχεία του κόμβου και τις προσθήκες που είναι ειδικές στο Docker Desktop.

    bash
    kubectl get pods -n kube-system

    Πραγματική έξοδος στο μηχάνημα του μαθήματος (η γραμμή metrics-server προέρχεται από ένα στοιχείο που θα εγκαταστήσουμε στην ενότητα 6: δεν το έχεις ακόμη, είναι φυσιολογικό):

    text
    NAME                                     READY   STATUS    RESTARTS   AGE
    coredns-66bc5c9577-6gwhl                 1/1     Running   0          18d
    coredns-66bc5c9577-9p89d                 1/1     Running   0          18d
    etcd-docker-desktop                      1/1     Running   0          18d
    kube-apiserver-docker-desktop            1/1     Running   0          18d
    kube-controller-manager-docker-desktop   1/1     Running   0          18d
    kube-proxy-2ddzv                         1/1     Running   0          18d
    kube-scheduler-docker-desktop            1/1     Running   0          18d
    metrics-server-7c5fdf4664-6882s          1/1     Running   0          26m
    storage-provisioner                      1/1     Running   0          18d
    vpnkit-controller                        1/1     Running   0          18d

    Τι πρέπει να δεις: kube-proxy (οι κανόνες δικτύου του κόμβου), coredns ×2 (το εσωτερικό DNS που θα επιτρέψει στο frontend να καλεί το api με το όνομά του), και δύο κομμάτια ειδικά για το Docker Desktop: storage-provisioner (δημιουργεί τόμους hostpath κατ' απαίτηση, ενότητα 5) και vpnkit-controller (αυτός δίνει το localhost στα Services LoadBalancer, μάθημα 04). Το kubelet, από την άλλη, δεν εμφανίζεται: δεν είναι Pod αλλά υπηρεσία συστήματος του κόμβου.

  7. Προαιρετικό: δες ότι το Kubernetes είναι «μόνο» containers. Αν η επιλογή «Show system containers» είναι τσεκαρισμένη στο Docker Desktop (μάθημα 02), το Docker σου δείχνει τα containers που φέρουν το control plane. Είναι το ίδιο Docker με αυτό του βήματος 1.

    bash
    docker ps --filter "name=k8s_kube-apiserver_" --filter "name=k8s_etcd_" --filter "name=k8s_kube-scheduler_" --filter "name=k8s_kube-controller-manager_" --format "table {{.Names}}\t{{.Status}}"

    Πραγματική έξοδος:

    text
    NAMES                                                                                                               STATUS
    k8s_etcd_etcd-docker-desktop_kube-system_0b753cb7812d40a401f3a8f63b18f779_0                                         Up 44 minutes
    k8s_kube-apiserver_kube-apiserver-docker-desktop_kube-system_647244f1c75810d936baf4253b7903ef_0                     Up 44 minutes
    k8s_kube-scheduler_kube-scheduler-docker-desktop_kube-system_b44739859c757a4712b786569a89a1f3_0                     Up 44 minutes
    k8s_kube-controller-manager_kube-controller-manager-docker-desktop_kube-system_10b0d524eef4b9a12d5827ba17a36f4f_0   Up 44 minutes

    Τι πρέπει να δεις: το όνομα Docker κατασκευάζεται ως k8s_<container>_<pod>_<namespace>_<uid>_<n>. Αν η επιλογή δεν είναι τσεκαρισμένη, ο πίνακας είναι άδειος: τίποτα σοβαρό, είναι μια ρύθμιση εμφάνισης. Το AGE των 18 ημερών από την πλευρά του Kubernetes και το Up 44 minutes από την πλευρά του Docker δεν αντιφάσκουν: το cluster υπάρχει εδώ και 18 ημέρες, το Docker Desktop επανεκκινήθηκε πριν από 44 λεπτά και ξαναδημιούργησε τα containers του χωρίς να χάσει την κατάσταση, που είναι αποθηκευμένη στο etcd.

  8. Πού μιλάμε; Μια τελευταία εντολή για να εντοπίσεις την πόρτα εισόδου από την οποία θα περάσουν όλες οι άλλες.

    bash
    kubectl cluster-info

    Πραγματική έξοδος:

    text
    Kubernetes control plane is running at https://kubernetes.docker.internal:6443
    CoreDNS is running at https://kubernetes.docker.internal:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
    
    To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

    Τι πρέπει να δεις: ο API server ακούει σε HTTPS στη θύρα 6443 του kubernetes.docker.internal, ένα όνομα που το Docker Desktop κάνει να δείχνει στο μηχάνημά σου. Όταν το Docker Desktop είναι σταματημένο, αυτή η θύρα είναι που «αρνείται τη σύνδεση» (μάθημα 02, «Αν κολλήσει»). Τίποτα να καθαρίσεις: το container essai-whoami διαγράφηκε στο βήμα 3 και δεν δημιούργησες τίποτα στο Kubernetes.

Αν κολλήσει

  • docker: Error response from daemon: failed to set up container networking: … Bind for 0.0.0.0:8089 failed: port is already allocated → ένα άλλο container δημοσιεύει ήδη τη θύρα 8089 (μήνυμα που προκλήθηκε εκκινώντας δύο φορές το βήμα 1· αν είναι ένα πρόγραμμα εκτός Docker που την καταλαμβάνει, το μήνυμα λέει ports are not available). Άλλαξε τη θύρα από την πλευρά του μηχανήματος (-p 8090:80) ή εντόπισε τον κάτοχο: Get-NetTCPConnection -LocalPort 8089 (PowerShell) / lsof -i :8089 (macOS, Linux).
  • kubectl : Le terme 'kubectl' n'est pas reconnukubectl: command not found) → το Kubernetes δεν είναι ενεργοποιημένο, ή το τερματικό ανοίχτηκε πριν την ενεργοποίηση και δεν ξαναφόρτωσε το PATH. Μάθημα 02 για να το ενεργοποιήσεις· έπειτα κλείσε και ξανάνοιξε το τερματικό.
  • Unable to connect to the server: dial tcp 127.0.0.1:6443: connectex: No connection could be made because the target machine actively refused it. → κανείς δεν ακούει στη 6443: το Docker Desktop είναι σταματημένο ή το Kubernetes απενεργοποιημένο. Εκκίνησε το Docker Desktop, περίμενε το «Kubernetes is up and running» στην προβολή Kubernetes, ξαναδοκίμασε.
  • kubectl get pods -n kube-system -l tier=control-plane επιστρέφει No resources found → είσαι πιθανότατα σε άλλο cluster (context minikube, kind-…) όπου το control plane δεν έχει τις ίδιες ετικέτες ή δεν είναι ορατό. Το kubectl config current-context πρέπει να απαντά docker-desktop· το μάθημα 03 εξηγεί πώς να το διορθώσεις.
  • Η εντολή docker ps --filter "name=k8s_…" δεν δείχνει τίποτα ενώ το Kubernetes τρέχει → το «Show system containers (advanced)» δεν είναι τσεκαρισμένο στις ρυθμίσεις Kubernetes του Docker Desktop. Δεν είναι βλάβη· το βήμα 7 είναι προαιρετικό.

Να κρατήσεις

  • Το Docker εκτελεί ένα container και σταματά εκεί· το Kubernetes διατηρεί μια επιθυμητή κατάσταση (πόσα αντίγραφα, ποια εικόνα, ποια θύρα) και διορθώνει σε βρόχο την απόκλιση από την παρατηρούμενη κατάσταση.
  • Control plane = kube-apiserver (πόρτα εισόδου, θύρα 6443), etcd (μνήμη του cluster), kube-scheduler (επιλέγει τον κόμβο), kube-controller-manager (βρόχοι συμφιλίωσης).
  • Κόμβος = kubelet (ο πράκτορας), kube-proxy (δίκτυο των Services), runtime (docker://29.3.1 στο Docker Desktop)· το kubectl get pods -n kube-system -l tier=control-plane δείχνει τα τέσσερα πρώτα σε λειτουργία.
  • Docker Desktop: ένα κλικ, ένας κόμβος docker-desktop v1.34.1, τοπικές εικόνες κοινές με το Kubernetes, LoadBalancer στο localhost· όρια: ένας μόνο κόμβος, καμία εφαρμοζόμενη NetworkPolicy, κανένα πραγματικό cloud.
  • Τα manifests που θα γράψεις εδώ λειτουργούν χωρίς αλλαγή σε minikube, kind, EKS, GKE ή AKS.
  • Επόμενο μάθημα: εγκατάσταση του Docker Desktop στο σύστημά σου, ενεργοποίηση του Kubernetes, και εμφάνιση του context docker-desktop.

Για να πας παραπέρα

Ο μοναδικός κόμβος του Docker Desktop κρύβει μια σημαντική διαφορά από ένα πραγματικό cluster: στην παραγωγή, το control plane ζει σε αφιερωμένα μηχανήματα (συχνά τρία, ώστε το etcd να διατηρεί απαρτία) και δεν φιλοξενεί κανένα Pod εφαρμογής, χάρη σε ένα «taint» node-role.kubernetes.io/control-plane:NoSchedule. Εδώ, το kubectl describe node docker-desktop δείχνει Taints: <none>: όλα τρέχουν στο ίδιο μέρος. Η σελίδα Kubernetes Components της επίσημης τεκμηρίωσης αναλύει κάθε στοιχείο που είδες σε αυτό το μάθημα, με τις πιθανές παραλλαγές (cloud-controller-manager, άλλα runtimes).