Minimum levedygtige Kubernetes

Oversættelsen af ​​artiklen blev udarbejdet på tærsklen til kursets start "DevOps-praksis og værktøjer".

Minimum levedygtige Kubernetes

Hvis du læser dette, har du sikkert hørt noget om Kubernetes (og hvis ikke, hvordan endte du her?) Men hvad er Kubernetes egentlig? Det her "Orkestrering af containere af industrikvalitet"? Eller "Cloud-native operativsystem"? Hvad betyder det overhovedet?

For at være ærlig er jeg ikke 100% sikker. Men jeg synes, det er interessant at grave i det indre og se, hvad der virkelig foregår i Kubernetes under dets mange lag af abstraktioner. Så bare for sjov, lad os tage et kig på, hvordan en minimal "Kubernetes-klynge" faktisk ser ud. (Dette vil være meget nemmere end Kubernetes på den hårde måde.)

Jeg antager, at du har grundlæggende viden om Kubernetes, Linux og containere. Alt, hvad vi taler om her, er kun til forsknings-/læringsformål, lad være med at sætte noget af det i produktion!

Anmeldelse

Kubernetes indeholder mange komponenter. Ifølge Wikipedia, arkitekturen ser sådan ud:

Minimum levedygtige Kubernetes

Der er mindst otte komponenter vist her, men vi vil ignorere de fleste af dem. Jeg vil gerne slå fast, 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 (Docker i dette tilfælde)

Lad os se, hvad dokumentationen siger om hver af dem (russisk., English.). Først kubelet:

En agent kører på hver node i klyngen. Det sørger for, at beholdere kører i poden.

Det lyder simpelt nok. Hvad med containerens køretider (beholderens køretid)?

En container runtime er et program designet til at køre containere.

Meget informativ. Men hvis du er bekendt med Docker, bør du have en generel idé om, hvad den gør. (Detaljerne omkring adskillelsen af ​​ansvar mellem container-runtime og kubelet er faktisk ret subtile, og jeg vil ikke komme ind på dem her.)

И API-server?

API Server er Kubernetes kontrolpanelkomponent, der afslører Kubernetes API. API-serveren er klientsiden af ​​Kubernetes kontrolpanel

Enhver, der nogensinde har gjort noget med Kubernetes, har været nødt til at interagere med API'en enten direkte eller gennem kubectl. Dette er hjertet af det, der gør Kubernetes Kubernetes - hjernen, der forvandler bjergene i YAML, 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

  • Virtuel eller fysisk Linux-maskine med root-adgang (jeg bruger Ubuntu 18.04 på en virtuel maskine).
  • Og det er det hele!

Kedelig installation

Vi skal installere Docker på den maskine, vi skal bruge. (Jeg vil ikke gå i detaljer om, hvordan Docker og containere fungerer; hvis du er interesseret, er der fantastiske artikler). Lad os bare installere det med apt:

$ sudo apt install docker.io
$ sudo systemctl start docker

Derefter skal vi hente Kubernetes binære filer. Faktisk har vi kun brug for den første lancering af vores "klynge". kubelet, da vi kan bruge andre serverkomponenter til at køre kubelet. For at interagere med vores klynge, efter at den kører, 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 sker der, hvis vi bare løber kubelet?

$ ./kubelet
F0609 04:03:29.105194    4583 server.go:254] mkdir /var/lib/kubelet: permission denied

kubelet skal køre som root. Helt logisk, da han skal styre hele noden. Lad os se på dens parametre:

$ ./kubelet -h
<слишком много строк, чтобы разместить здесь>
$ ./kubelet -h | wc -l
284

Wow, så mange muligheder! Heldigvis mangler vi kun et par af dem. Her er en af ​​de parametre, som vi er interesserede i:

--pod-manifest-path string

Sti til den mappe, der indeholder filer til statiske pods, eller sti til en fil, der beskriver statiske pods. Filer, der starter med prikker, ignoreres. (UDDAGET: Denne indstilling skal indstilles i den konfigurationsfil, der sendes til Kubelet via --config-indstillingen. For mere information, se kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file .)

Denne mulighed giver os mulighed for at køre statiske bælg - pods, der ikke administreres via Kubernetes API. Statiske bælg bruges sjældent, men de er meget praktiske til hurtigt at rejse en klynge, og det er præcis, hvad vi har brug for. Vi vil ignorere denne store advarsel (igen, lad være med at køre dette i produktion!) og se, om vi kan få poden til at køre.

Først vil vi oprette en mappe til statiske pods og køre kubelet:

$ mkdir pods
$ sudo ./kubelet --pod-manifest-path=pods

Derefter, i et andet terminal/tmux-vindue/whatever, vil vi oprette et pod-manifest:

$ cat <<EOF > 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 det ser ud til, at der ikke sker noget. Men det er ikke sandt! Lad os se 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 Jeg læste pod-manifestet og gav Docker kommandoen til at starte et par containere i henhold til vores specifikationer. (Hvis du undrer dig over "pause"-beholderen, er det et Kubernetes-hack - se denne blog.) Kubelet vil lancere vores container busybox med den angivne kommando og vil genstarte den på ubestemt tid, indtil den statiske pod er slettet.

Ønske dig selv. Vi har lige fundet på en af ​​de mest forvirrende måder at sende tekst til terminalen på!

Start osv

Vores ultimative mål er at køre Kubernetes API, men for at gøre det skal vi først køre osv. Lad os starte en minimal etcd-klynge ved at placere dens indstillinger i pods-mappen (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-data

Hvis du nogensinde har arbejdet med Kubernetes, burde disse YAML-filer være bekendte for dig. Der er kun to punkter, der er værd at bemærke her:

Vi har monteret værtsmappen /var/lib/etcd i poden, så etcd-dataene bevares efter en genstart (hvis dette ikke gøres, vil klyngetilstanden blive slettet hver gang poden genstartes, hvilket ikke vil være godt for selv en minimal Kubernetes-installation).

Vi har installeret hostNetwork: true. Denne indstilling konfigurerer, ikke overraskende, etcd til at bruge værtsnetværket i stedet for pod'ens interne netværk (dette vil gøre det nemmere for API-serveren at finde etcd-klyngen).

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.wal

Starter API-serveren

Det er endnu nemmere at køre en Kubernetes API-server. Den eneste parameter, der skal passeres, er --etcd-servers, gør 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 starter. Tjek med curl viser, at Kubernetes API lytter på port 8080 med fuldstændig åben adgang - ingen godkendelse påkrævet!

$ curl localhost:8080/healthz
ok
$ curl localhost:8080/api/v1/pods
{
  "kind": "PodList",
  "apiVersion": "v1",
  "metadata": {
    "selfLink": "/api/v1/pods",
    "resourceVersion": "59"
  },
  "items": []
}

(Igen, lad være med at køre dette i produktion! Jeg var lidt overrasket over, at standardindstillingen er så usikker. Men jeg gætter på, at dette er for at gøre udvikling og test nemmere.)

Og behagelig overraskelse, kubectl fungerer ud af kassen 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.

problem

Men hvis du graver lidt dybere, ser noget ud til at gå galt:

$ ./kubectl get pod -n kube-system
No resources found in kube-system namespace.

De statiske pods, vi skabte, er væk! Faktisk er vores kubelet-knude slet ikke opdaget:

$ ./kubectl get nodes
No resources found in default namespace.

Hvad er der galt? Hvis du husker et par afsnit siden, startede vi kubelet med et ekstremt simpelt sæt kommandolinjeparametre, så kubelet ved ikke, hvordan man kontakter API-serveren og giver den besked om dens tilstand. Efter at have studeret dokumentationen finder vi det tilsvarende flag:

--kubeconfig string

Stien til filen kubeconfig, som specificerer, hvordan der oprettes forbindelse til API-serveren. Tilgængelighed --kubeconfig aktiverer API-servertilstand, nej --kubeconfig aktiverer offlinetilstand.

Hele denne tid, uden at vide det, kørte vi kubelet i "offline-tilstand." (Hvis vi var pedantiske, kunne vi tænke på en selvstændig kubelet som "minimum levedygtige Kubernetes", men det ville være meget kedeligt). For at få den "rigtige" konfiguration til at fungere, skal vi sende kubeconfig-filen til kubelet, så den ved, hvordan den skal tale med API-serveren. Heldigvis er det ret simpelt (da vi ikke har nogen godkendelses- eller certifikatproblemer):

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æbe processen kubelet og genstart med de nødvendige parametre:

$ sudo ./kubelet --pod-manifest-path=pods --kubeconfig=kubeconfig.yaml

(Forresten, hvis du forsøger at få adgang til API'et via curl, når kubelet ikke kører, vil du opdage, at det stadig kører! Kubelet er ikke en "forælder" til sine pods som Docker, det er mere som en "kontrol dæmon." Containere, der administreres af en kubelet, vil fortsætte med at køre, indtil kubelet stopper dem.)

Om et par minutter kubectl skal 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    <none>   21h   v1.18.5   10.70.10.228   <none>        Ubuntu 18.04.4 LTS   4.15.0-109-generic   docker://19.3.6

Lad os virkelig lykønske os selv denne gang (jeg ved, at jeg allerede har lykønsket os selv) - vi har en minimal Kubernetes "cluster" kørende med en fuldt funktionel API!

Vi lancerer under

Lad os nu se, hvad API'en er i stand til. Lad os starte med nginx pod:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx

Her får vi en ret interessant fejl:

$ ./kubectl apply -f nginx.yaml
Error from server (Forbidden): error when creating "nginx.yaml": pods "nginx" is
forbidden: error looking up service account default/default: serviceaccount
"default" not found
$ ./kubectl get serviceaccounts
No resources found in default namespace.

Her ser vi, hvor sørgeligt ufuldstændigt vores Kubernetes-miljø er - vi har ingen konti for tjenester. Lad os prøve igen ved manuelt at oprette en servicekonto og se, hvad der sker:

$ cat <<EOS | ./kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: default
  namespace: default
EOS
serviceaccount/default created
$ ./kubectl apply -f nginx.yaml
Error from server (ServerTimeout): error when creating "nginx.yaml": No API
token found for service account "default", retry after the token is
automatically created and added to the service account

Selv når vi oprettede servicekontoen manuelt, genereres godkendelsestokenet ikke. Når vi fortsætter med at eksperimentere med vores minimalistiske "klynge", vil vi opdage, at de fleste af de nyttige ting, der normalt sker automatisk, vil mangle. Kubernetes API-serveren er ret minimalistisk, hvor det meste af de tunge løft og automatisk konfiguration sker i forskellige controllere og baggrundsjob, der endnu ikke kører.

Vi kan løse dette problem ved at indstille indstillingen automountServiceAccountToken for servicekontoen (da vi alligevel ikke skal bruge den):

$ cat <<EOS | ./kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: default
  namespace: default
automountServiceAccountToken: false
EOS
serviceaccount/default configured
$ ./kubectl apply -f nginx.yaml
pod/nginx created
$ ./kubectl get pods
NAME    READY   STATUS    RESTARTS   AGE
nginx   0/1     Pending   0          13m

Endelig er poden dukket op! Men faktisk starter den ikke, fordi vi ikke har planlægger (planlægning) er en anden vigtig komponent i Kubernetes. Igen ser vi, at Kubernetes API er overraskende "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 den på.

Faktisk behøver du ikke en skemalægger for at køre en pod. Du kan manuelt tilføje en node til manifestet i parameteren nodeName:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
  nodeName: mink8s

(Erstatte mink8s til navnet på noden.) Efter sletning og anvendelse ser vi, at nginx er startet og lytter til den interne IP-adresse:

$ ./kubectl delete pod nginx
pod "nginx" deleted
$ ./kubectl apply -f nginx.yaml
pod/nginx created
$ ./kubectl get pods -owide
NAME    READY   STATUS    RESTARTS   AGE   IP           NODE     NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          30s   172.17.0.2   mink8s   <none>           <none>
$ curl -s 172.17.0.2 | head -4
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>

For at sikre, at netværket mellem pods fungerer korrekt, kan vi køre curl fra en anden pod:

$ cat <<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>Welcome to nginx!</title>

Det er ret interessant at grave ned i dette miljø og se, hvad der virker, og hvad der ikke gør. Jeg fandt ud af, at ConfigMap og Secret fungerede som forventet, men det gør Service og Deployment ikke.

Succes!

Dette indlæg bliver langt, så jeg har tænkt mig at erklære sejr og sige, at dette er en levedygtig konfiguration, der kan kaldes "Kubernetes". For at opsummere: fire binære filer, fem kommandolinjeparametre og "kun" 45 linjer YAML (ikke så meget efter standarder Kubernetes), og vi har en del ting, der fungerer:

  • Pods administreres ved hjælp af den almindelige Kubernetes API (med et par hacks)
  • Du kan uploade og administrere offentlige containerbilleder
  • Pods forbliver i live og genstarter automatisk
  • Netværk mellem pods inden for den samme node fungerer ganske godt
  • ConfigMap, Secret og enkel lagermontering fungerer som forventet

Men meget af det, der gør Kubernetes virkelig nyttigt, mangler stadig, såsom:

  • Pod Scheduler
  • Autentificering/autorisation
  • Flere noder
  • Netværk af tjenester
  • Klynget intern DNS
  • Controllere til servicekonti, implementeringer, integration med cloud-udbydere og de fleste andre godbidder, som Kubernetes bringer

Så hvad fik vi egentlig? Kubernetes API, der kører på egen hånd, er egentlig bare en platform til container automatisering. Det gør ikke meget - det er et job for forskellige controllere og operatører, der bruger API'et - men det giver et konsistent miljø for automatisering.

Lær mere om kurset i det gratis webinar.

Læs mere:

Kilde: www.habr.com

Tilføj en kommentar