Oversættelsen af artiklen er udarbejdet forud for begyndelsen af kurset .

Hvis du læser dette, har du sandsynligvis hørt om Kubernetes (og hvis ikke, hvordan er du så her?) Men hvad er Kubernetes faktisk? Det er ? Или ? Что вообще это значит?
Ærligt talt er jeg ikke 100% sikker. Men jeg tror det er interessant at grave ned i det og se, hvad der faktisk sker i Kubernetes under de mange lag af abstraktion. Så lad os af nysgerrighed se, hvordan et minimum "Kubernetes-klynge" ser ud. (Det bliver meget lettere end .)
Jeg formoder, at du har en grundlæggende forståelse af Kubernetes, Linux og containere. Alt det, vi vil tale om her, er kun til udforskning/studie, kør ikke noget af dette i produktion!
Oversigt
Kubernetes indeholder mange komponenter. Ifølge , ser arkitekturen således ud:

Her vises mindst otte komponenter, men de fleste af dem vil vi ignorere. Jeg vil hævde, at den mindste ting, der med rimelighed kan kaldes Kubernetes, består af tre hovedkomponenter:
- kubelet
- kube-apiserver (som afhænger af etcd — dens database)
- container runtime (i dette tilfælde Docker)
Lad os se, hvad der siges om hver af dem i dokumentationen (., .). Først kubelet:
Agenten, der kører på hver node i klyngen. Den holder øje med, at containere er startet i pod'en.
Det lyder ret enkelt. Hvad med container runtimen (container runtime)?
Container runtime er et program designet til at køre containere.
Meget informativt. Men hvis du er bekendt med Docker, skal du have en generel forståelse af, hvad det gør. (Detaljerne om opdelingen af ansvar mellem container runtime og kubelet er faktisk temmelig fine, og jeg vil ikke dykke ned i dem her.)
Og API-server?
API-serveren er en komponent af Kubernetes kontrolpanelet, som repræsenterer Kubernetes API. API-serveren er den klientdel af Kubernetes kontrolpanelet
Alle, der nogensinde har arbejdet med Kubernetes, har skullet interagere med API'en enten direkte eller gennem kubectl. Dette er kernen i, hvad der gør Kubernetes til Kubernetes — hjernen, der omdanner de mange YAML-filer, som vi alle kender og elsker (?), til fungerende infrastruktur. Det virker indlysende, at API'en skal være til stede i vores minimale konfiguration.
Forudsætninger
- En virtuel eller fysisk Linux-maskine med root-adgang (jeg bruger Ubuntu 18.04 på en virtuel maskine).
- Og det var det!
Kedelig installation
Docker skal installeres på den maskine, vi vil bruge. (Jeg vil ikke detajere, hvordan Docker og containere fungerer; hvis du er interesseret, er der ). Lad os bare installere det ved hjælp af apt:
$ sudo apt install docker.io
$ sudo systemctl start docker Derefter skal vi hente Kubernetes-binærfilerne. Faktisk har vi kun brug for kubelet, da vi kan bruge til at køre de andre serverkomponenter kubelet. For at interagere med vores klynge, efter den er startet, vil vi også bruge kubectl.
$ curl -L https://dl.k8s.io/v1.18.5/kubernetes-server-linux-amd64.tar.gz > server.tar.gz
$ tar xzvf server.tar.gz
$ cp kubernetes/server/bin/kubelet .
$ cp kubernetes/server/bin/kubectl .
$ ./kubelet --version
Kubernetes v1.18.5 Hvad vil der ske, hvis vi bare kører kubelet?
$ ./kubelet
F0609 04:03:29.105194 4583 server.go:254] mkdir /var/lib/kubelet: permission denied kubelet skal køre som root. Det giver mening, da det skal styre hele noden. Lad os se på dets parametre:
$ ./kubelet -h
$ ./kubelet -h | wc -l
284Wow, så mange muligheder! Heldigvis har vi kun brug for et par af dem. Her er en af parametrene, der er interessante for os:
--pod-manifest-path stringStien til mappen, der indeholder filer til statiske pods, eller stien til en fil med beskrivelse af statiske pods. Filer, der starter med punktummer, ignoreres. (FORLADT: denne parameter skal angives i konfigurationsfilen, der sendes til Kubelet via optionen —config. For mere information, se. .)
Denne parameter giver os mulighed for at køre — pods, der ikke administreres via Kubernetes API. Statisk pods anvendes sjældent, men de er meget nyttige til hurtigt at etablere et cluster, hvilket er netop, hvad vi har brug for. Vi vil ignorere denne høje advarsel (igen, kør ikke dette i produktion!) og se, om vi kan starte pod.
Først opretter vi et bibliotek til statiske pods og starter kubelet:
$ mkdir pods
$ sudo ./kubelet --pod-manifest-path=podsDerefter i et andet terminal-vindue/tmux/andre steder, vil vi oprette manifestet for pod:
$ cat < pods/hello.yaml
apiVersion: v1
kind: Pod
metadata:
name: hello
spec:
containers:
- image: busybox
name: hello
command: ["echo", "hello world!"]
EOF kubelet begynder at skrive nogle advarsler og ser ud til, at der ikke sker noget. Men det er ikke sandt! Lad os kigge på Docker:
$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8c8a35e26663 busybox "echo 'hello world!'" 36 seconds ago Exited (0) 36 seconds ago k8s_hello_hello-mink8s_default_ab61ef0307c6e0dee2ab05dc1ff94812_4
68f670c3c85f k8s.gcr.io/pause:3.2 "pause" 2 minutes ago Up 2 minutes k8s_POD_hello-mink8s_default_ab61ef0307c6e0dee2ab05dc1ff94812_0
$ sudo docker logs k8s_hello_hello-mink8s_default_ab61ef0307c6e0dee2ab05dc1ff94812_4
hello world! kubelet læste manifestet for pod og gav Docker besked om at starte et par containere i henhold til vores specifikation. (Hvis du er interesseret i at lære om containeren “pause”, så er det et trick fra Kubernetes — se nærmere på .) Kubelet vil starte vores container busybox med den angivne kommando og vil genstarte den uendeligt, indtil den statiske pod bliver fjernet.
Tillykke med dig selv. Vi har lige opfundet en af de mest indviklede måder at generere tekst i terminalen!
Starter etcd
Vores endelige mål er at starte Kubernetes API, men for at gøre dette skal vi først starte . Lad os starte et minimalt etcd-cluster ved at placere dets indstillinger i biblioteket pods (f.eks. pods/etcd.yaml):
apiVersion: v1
kind: Pod
metadata:
name: etcd
namespace: kube-system
spec:
containers:
- name: etcd
command:
- etcd
- --data-dir=/var/lib/etcd
image: k8s.gcr.io/etcd:3.4.3-0
volumeMounts:
- mountPath: /var/lib/etcd
name: etcd-data
hostNetwork: true
volumes:
- hostPath:
path: /var/lib/etcd
type: DirectoryOrCreate
name: etcd-dataHvis du nogensinde har arbejdet med Kubernetes, så burde sådanne YAML-filer være velkendte for dig. Her skal kun nævnes to punkter:
Vi har monteret en værtsmappe /var/lib/etcd i pod for at sikre, at etcd-data bevares efter genstart (hvis dette ikke gøres, vil klusterets tilstand blive slettet ved hver genstart af pod'et, hvilket ikke er optimalt, selv for en minimal Kubernetes-installation).
Vi har installeret hostNetwork: true. Denne parameter, ikke overraskende, konfigurerer etcd til at bruge værtens netværk i stedet for pod'ens interne netværk (det vil gøre det lettere for API-serveren at finde etcd-klusteret).
En simpel kontrol viser, at etcd faktisk kører på localhost og gemmer data på disken:
$ curl localhost:2379/version
{"etcdserver":"3.4.3","etcdcluster":"3.4.0"}
$ sudo tree /var/lib/etcd/
/var/lib/etcd/
└── member
├── snap
│ └── db
└── wal
├── 0.tmp
└── 0000000000000000-0000000000000000.walStart af API-serveren
Det er endnu nemmere at starte Kubernetes API-serveren. Den eneste parameter, du skal give, --etcd-servers, gør præcis, hvad du forventer:
apiVersion: v1
kind: Pod
metadata:
name: kube-apiserver
namespace: kube-system
spec:
containers:
- name: kube-apiserver
command:
- kube-apiserver
- --etcd-servers=http://127.0.0.1:2379
image: k8s.gcr.io/kube-apiserver:v1.18.5
hostNetwork: true Placer denne YAML-fil i mappen pods, og API-serveren vil starte. En kontrol med curl viser, at Kubernetes API lytter på port 8080 med fuld adgang - autentificering er ikke nødvendig!
$ curl localhost:8080/healthz
ok
$ curl localhost:8080/api/v1/pods
{
"kind": "PodList",
"apiVersion": "v1",
"metadata": {
"selfLink": "/api/v1/pods",
"resourceVersion": "59"
},
"items": []
}(Igen, kør ikke dette i produktion! Jeg var lidt overrasket over, at standardindstillingen er så usikker. Men jeg formoder, at det er lavet for at lette udvikling og testning.)
Og en dejlig overraskelse, kubectl fungerer uden problemer uden yderligere indstillinger!
$ ./kubectl version
Client Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.5", GitCommit:"e6503f8d8f769ace2f338794c914a96fc335df0f", GitTreeState:"clean", BuildDate:"2020-06-26T03:47:41Z", GoVersion:"go1.13.9", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.5", GitCommit:"e6503f8d8f769ace2f338794c914a96fc335df0f", GitTreeState:"clean", BuildDate:"2020-06-26T03:39:24Z", GoVersion:"go1.13.9", Compiler:"gc", Platform:"linux/amd64"}
$ ./kubectl get pod
No resources found in default namespace.Problemet
Men hvis vi ser nærmere på det, ser det ud til, at noget ikke fungerer:
$ ./kubectl get pod -n kube-system
No resources found in kube-system namespace.De statiske pods, vi oprettede, er forsvundet! Vores kubelet-node opdages faktisk slet ikke:
$ ./kubectl get nodes
No resources found in default namespace.Hvad er problemet? Hvis du husker, så startede vi kubelet med et ekstremt simpelt sæt af kommandolinjeparametre for et par afsnit siden, så kubelet ikke ved, hvordan den skal kommunikere med API-serveren og informere den om sin tilstand. Ved at se i dokumentationen finder vi det relevante flag:
--kubeconfig string
Stien til filen kubeconfig, der angiver, hvordan man forbinder til API-serveren. At have --kubeconfig aktiverer API-server tilstand, mens fraværet --kubeconfig aktiverer offline tilstand.
Hele tiden, uden at vide det, har vi kørt kubelet i "offline tilstand". (Hvis vi var pedantiske, kunne vi betragte kubelets offline tilstand som "minimalt levedygtig Kubernetes", men det ville være meget kedeligt). For at få en "ægte" konfiguration til at fungere, skal vi overføre kubeconfig-filen til kubelet, så den ved, hvordan man kommunikerer med API-serveren. Heldigvis er det ret simpelt (da vi ikke har problemer med autentificering eller certificater):
apiVersion: v1
kind: Config
clusters:
- cluster:
server: http://127.0.0.1:8080
name: mink8s
contexts:
- context:
cluster: mink8s
name: mink8s
current-context: mink8s Gem dette som kubeconfig.yaml, dræb processen kubelet og genstart med de nødvendige parametre:
$ sudo ./kubelet --pod-manifest-path=pods --kubeconfig=kubeconfig.yaml(Forresten, hvis du prøver at tilgå API'en via curl, når kubelet ikke kører, vil du opdage, at den stadig kører! Kubelet er ikke "forælder" til sine pods, ligesom Docker, den er mere som en "overvågningsdæmon". Containere, der administreres af kubelet, vil fortsætte med at køre, indtil kubelet stopper dem.)
Om et par minutter kubectl bør vise os pods og noder, som vi forventer:
$ ./kubectl get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
default hello-mink8s 0/1 CrashLoopBackOff 261 21h
kube-system etcd-mink8s 1/1 Running 0 21h
kube-system kube-apiserver-mink8s 1/1 Running 0 21h
$ ./kubectl get nodes -owide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
mink8s Ready 21h v1.18.5 10.70.10.228 Ubuntu 18.04.4 LTS 4.15.0-109-generic docker://19.3.6Lad os virkelig give os selv et klap på skulderen denne gang (jeg ved, jeg allerede har gjort det) — vi har fået en minimal "klynge" Kubernetes, der kører med en fuld funktionel API!
Start en pod
Lad os nu se, hvad API'en kan. Lad os starte med nginx-poden:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- image: nginx
name: nginxHer vil vi få en ret interessant fejl:
$ .\/kubectl apply -f nginx.yaml
Fejl fra server (Forbidden): fejler ved oprettelse af "nginx.yaml": pods "nginx" er
forbudt: fejler ved at kigge efter servicekonto default\/default: servicekonto
"default" blev ikke fundet
$ .\/kubectl get serviceaccounts
Ingen ressourcer fundet i standardnavnerummet.Her ser vi, hvor ufuldstændig vores Kubernetes-miljø er — vi har ikke nogen servicekonti. Lad os prøve igen ved at oprette en servicekonto manuelt og se, hvad der sker:
$ cat <<EOS | .\/kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: default
namespace: default
EOS
serviceaccount\/default oprettet
$ .\/kubectl apply -f nginx.yaml
Fejl fra server (ServerTimeout): fejler ved oprettelse af "nginx.yaml": Ingen API
token fundet for servicekonto "default", prøv igen, efter at tokenet er
automatiskt oprettet og tilføjet til servicekontoenSelv når vi har oprettet servicekontoen manuelt, oprettes autentificeringstokenet ikke. Når vi fortsætter med at eksperimentere med vores minimalistiske "klynge", vil vi opdage, at de fleste nyttige ting, der normalt sker automatisk, vil mangle. Kubernetes API-serveren er ret minimalistisk, og den tunge automatiske opsætning sker i forskellige controllere og baggrundsopgaver, som endnu ikke kører.
Vi kan omgå dette problem ved at installere indstillingen automountServiceAccountToken for servicekontoen (da vi ikke alligevel skal bruge den):
$ cat <<EOS | .\/kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: default
namespace: default
automountServiceAccountToken: false
EOS
serviceaccount\/default konfigureret
$ .\/kubectl apply -f nginx.yaml
pod\/nginx oprettet
$ .\/kubectl get pods
NAVN KLAR STATUS GENSTARTS ALDER
nginx 0\/1 Pending 0 13mEndelig er pod'en dukket op! Men den vil faktisk ikke starte, da vi ikke har nogen (scheduler) — endnu en vigtig komponent i Kubernetes. Igen ser vi, at Kubernetes API'en overraskende er "dum" — når du opretter en pod i API'en, registrerer den den, men forsøger ikke at finde ud af, hvilken node den skal køre på.
Faktisk er en planlægger ikke nødvendig for at køre en pod. Man kan manuelt tilføje noden i manifestet med parameteren nodeName:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- image: nginx
name: nginx
nodeName: mink8s
(Erstat mink8s med nodens navn.) Efter delete og apply ser vi, at nginx er startet og lytter på den interne IP-adresse:
$ .\/kubectl delete pod nginx
pod "nginx" slettet
$ .\/kubectl apply -f nginx.yaml
pod/nginx oprettet
$ .\/kubectl get pods -owide
NAVN KLAR STATUS GENSTARTS ALDER IP NODE NOMINERET NODE READINESS GATES
nginx 1\/1 Kører 0 30s 172.17.0.2 mink8s <none> <none>
$ curl -s 172.17.0.2 | head -4
<!DOCTYPE html>
<html>
<head>
<title>Velkommen til nginx!</title>For at sikre, at netværket mellem pods fungerer korrekt, kan vi køre curl fra en anden pod:
$ kat <<EOS | .\/kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: curl
spec:
containers:
- image: curlimages\/curl
name: curl
command: ["curl", "172.17.0.2"]
nodeName: mink8s
EOS
pod\/curl created
$ .\/kubectl logs curl | head -6
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
<!DOCTYPE html>
<html>
<head>
<title>Velkommen til nginx!</title>Det er ganske interessant at dykke ned i dette miljø og se, hvad der fungerer, og hvad der ikke gør. Jeg har opdaget, at ConfigMap og Secret fungerer som forventet, mens Service og Deployment ikke gør.
Succes!
Dette indlæg bliver stort, så jeg vil annoncere vinderen og sige, at dette er en levedygtig konfiguration, som kan kaldes “Kubernetes”. For at opsummere: fire binære filer, fem kommandolinjeparametre og “kun” 45 linjer YAML (ikke så mange efter Kubernetes-standarder), og vi har en hel del funktioner kørende:
- Pods styres med det almindelige Kubernetes API (med nogle hacks)
- Det er muligt at uploade offentlige containerbilleder og administrere dem
- Pods forbliver aktive og genstartes automatisk
- Netværket mellem pods på samme node fungerer ganske godt
- ConfigMap, Secret, og simpel montering af lagringspladser fungerer som de skal
Men det meste af det, der virkelig gør Kubernetes nyttigt, mangler stadig, for eksempel:
- Pod-planlægger
- Godkendelse / autorisation
- Flere noder
- Service-netværk
- Klynge-intern DNS
- Controllere til servicekonti, implementeringer, integration med cloud-udbydere, og de fleste andre “godbidder”, som Kubernetes bringer med sig
Hvad har vi så egentlig fået? Kubernetes API, der kører af sig selv, er faktisk kun en platform til automatisering af containere. Det gør ikke meget — det er arbejdet for forskellige controllere og operatører, der bruger API'et — men det sikrer et konsistent miljø til automatisering.
Læs mere:
Kilde: habr.com
