Eerlijk gezegd weet ik het niet 100% zeker. Maar ik denk dat het interessant is om in de interne aspecten te duiken en te zien wat er werkelijk aan de hand is in Kubernetes onder de vele lagen van abstracties. Laten we dus voor de lol eens kijken hoe een minimaal “Kubernetes-cluster” er eigenlijk uitziet. (Dit zal veel gemakkelijker zijn dan Kubernetes op de harde manier.)
Ik neem aan dat je basiskennis hebt van Kubernetes, Linux en containers. Alles waar we het hier over hebben is alleen bedoeld voor onderzoeks-/leerdoeleinden, breng er niets van in productie!
Recensie
Kubernetes bevat veel componenten. Volgens wikipedia, ziet de architectuur er als volgt uit:
Er worden hier minstens acht componenten getoond, maar we zullen de meeste ervan negeren. Ik wil stellen dat het minimale dat redelijkerwijs Kubernetes genoemd kan worden, uit drie hoofdcomponenten bestaat:
kubus
kube-apiserver (die afhankelijk is van etcd - zijn database)
containerruntime (Docker in dit geval)
Laten we eens kijken wat de documentatie over elk van hen zegt (Russisch., Engels.). Aanvankelijk kubus:
Een agent die op elk knooppunt in het cluster draait. Het zorgt ervoor dat containers in de pod draaien.
Klinkt eenvoudig genoeg. Hoe zit het met looptijden van containers (containerruntime)?
Een containerruntime is een programma dat is ontworpen om containers uit te voeren.
Erg informatief. Maar als u bekend bent met Docker, zou u een algemeen idee moeten hebben van wat het doet. (De details van de scheiding van verantwoordelijkheden tussen de containerruntime en de kubelet zijn eigenlijk behoorlijk subtiel en ik zal er hier niet op ingaan.)
И API-server?
API Server is het onderdeel van het Kubernetes-configuratiescherm dat de Kubernetes API beschikbaar stelt. De API-server is de clientzijde van het Kubernetes-configuratiescherm
Iedereen die ooit iets met Kubernetes heeft gedaan, heeft rechtstreeks of via kubectl met de API moeten communiceren. Dit is de kern van wat Kubernetes Kubernetes maakt - het brein dat de bergen van YAML die we allemaal kennen en waar we van houden (?) omzet in werkende infrastructuur. Het lijkt voor de hand te liggen dat de API aanwezig zou moeten zijn in onze minimale configuratie.
Randvoorwaarden
Virtuele of fysieke Linux-machine met root-toegang (ik gebruik Ubuntu 18.04 op een virtuele machine).
En het is alles!
Saaie installatie
We moeten Docker installeren op de machine die we gaan gebruiken. (Ik ga niet in detail treden over hoe Docker en containers werken; als je geïnteresseerd bent, dat is zo prachtige artikelen). Laten we het gewoon installeren met apt:
Daarna moeten we de binaire bestanden van Kubernetes ophalen. In feite hebben we alleen nodig voor de eerste lancering van ons “cluster”. kubelet, omdat we andere servercomponenten kunnen gebruiken om andere servercomponenten uit te voeren kubelet. Om met ons cluster te communiceren nadat het actief is, zullen we ook gebruiken kubectl.
kubelet moet als root draaien. Best logisch, aangezien hij het hele knooppunt moet beheren. Laten we eens kijken naar de parameters:
$ ./kubelet -h
<слишком много строк, чтобы разместить здесь>
$ ./kubelet -h | wc -l
284
Wauw, zoveel opties! Gelukkig hebben we er maar een paar nodig. Hier is een van de parameters waarin we geïnteresseerd zijn:
--pod-manifest-path string
Pad naar de map met bestanden voor statische pods, of pad naar een bestand dat statische pods beschrijft. Bestanden die beginnen met punten worden genegeerd. (AFGESCHAFT: deze optie moet worden ingesteld in het configuratiebestand dat via de optie --config aan de Kubelet wordt doorgegeven. Zie voor meer informatie kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file .)
Met deze optie kunnen we rennen statische peulen — pods die niet worden beheerd via de Kubernetes API. Statische peulen worden zelden gebruikt, maar ze zijn erg handig om snel een cluster groot te brengen, en dit is precies wat we nodig hebben. We negeren deze grote waarschuwing (nogmaals, voer dit niet uit in productie!) en kijken of we de pod aan de praat kunnen krijgen.
Eerst zullen we een map voor statische pods maken en uitvoeren kubelet:
kubelet begint enkele waarschuwingen te schrijven en het lijkt alsof er niets gebeurt. Maar dat is niet waar! Laten we naar Docker kijken:
$ 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 Ik las het pod-manifest en gaf Docker de opdracht om een paar containers te lanceren volgens onze specificaties. (Als je je afvraagt wat de "pauze" -container is: het is een Kubernetes-hack - zie deze blog.) Kubelet zal onze container lanceren busybox met de opgegeven opdracht en zal het voor onbepaalde tijd opnieuw opstarten totdat de statische pod is verwijderd.
Feliciteer jezelf. We hebben zojuist een van de meest verwarrende manieren bedacht om tekst naar de terminal uit te voeren!
Lancering enz
Ons uiteindelijke doel is om de Kubernetes API uit te voeren, maar om dat te doen moeten we eerst draaien enz. Laten we een minimaal etcd-cluster starten door de instellingen ervan in de map pods te plaatsen (bijvoorbeeld pods/etcd.yaml):
Als u ooit met Kubernetes heeft gewerkt, zullen deze YAML-bestanden u bekend voorkomen. Er zijn hier slechts twee punten die het vermelden waard zijn:
We hebben de hostmap aangekoppeld /var/lib/etcd in de pod zodat de etcd-gegevens behouden blijven na een herstart (als dit niet gebeurt, wordt de clusterstatus elke keer gewist wanneer de pod opnieuw wordt opgestart, wat zelfs voor een minimale Kubernetes-installatie niet goed zal zijn).
Wij hebben geïnstalleerd hostNetwork: true. Het is niet verrassend dat deze instelling etcd configureert om het hostnetwerk te gebruiken in plaats van het interne netwerk van de pod (dit maakt het gemakkelijker voor de API-server om het etcd-cluster te vinden).
Een eenvoudige controle laat zien dat etcd inderdaad op localhost draait en gegevens op schijf opslaat:
$ 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
Het starten van de API-server
Het runnen van een Kubernetes API-server is nog eenvoudiger. De enige parameter die moet worden doorgegeven is --etcd-servers, doet wat je verwacht:
Plaats dit YAML-bestand in de directory podsen de API-server zal starten. Controleren met curl laat zien dat de Kubernetes API luistert op poort 8080 met volledig open toegang - geen authenticatie vereist!
(Nogmaals, voer dit niet in productie uit! Ik was een beetje verrast dat de standaardinstelling zo onveilig is. Maar ik vermoed dat dit is om het ontwikkelen en testen eenvoudiger te maken.)
En, aangename verrassing, kubectl werkt direct uit de doos zonder extra instellingen!
$ ./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.
probleem
Maar als je wat dieper graaft, lijkt er iets mis te gaan:
$ ./kubectl get pod -n kube-system
No resources found in kube-system namespace.
De statische pods die we hebben gemaakt, zijn verdwenen! In feite wordt ons kubelet-knooppunt helemaal niet ontdekt:
$ ./kubectl get nodes
No resources found in default namespace.
Wat is er aan de hand? Als u zich een paar paragrafen geleden nog herinnert, zijn we de kubelet gestart met een extreem eenvoudige set opdrachtregelparameters, zodat de kubelet niet weet hoe hij contact moet maken met de API-server en hoe hij deze op de hoogte moet stellen van de status ervan. Na bestudering van de documentatie vinden we de bijbehorende vlag:
--kubeconfig string
Het pad naar het bestand kubeconfig, waarmee wordt aangegeven hoe verbinding moet worden gemaakt met de API-server. Beschikbaarheid --kubeconfig schakelt API-servermodus in, nee --kubeconfig schakelt de offlinemodus in.
Al die tijd draaiden we, zonder het te weten, de kubelet in ‘offline modus’. (Als we pedant zouden zijn, zouden we een op zichzelf staande kubelet kunnen beschouwen als "minimaal levensvatbare Kubernetes", maar dat zou erg saai zijn). Om de "echte" configuratie te laten werken, moeten we het kubeconfig-bestand doorgeven aan de kubelet, zodat deze weet hoe hij met de API-server moet praten. Gelukkig is het vrij eenvoudig (aangezien we geen authenticatie- of certificaatproblemen hebben):
(Trouwens, als je via curl toegang probeert te krijgen tot de API terwijl de kubelet niet actief is, zul je merken dat deze nog steeds actief is! Kubelet is geen “ouder” van zijn pods zoals Docker, het is meer een “controle” daemon." Containers die door een kubelet worden beheerd, blijven actief totdat de kubelet ze stopt.)
In een paar minuten kubectl zou ons de pods en knooppunten moeten laten zien zoals we verwachten:
$ ./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
Laten we onszelf deze keer echt feliciteren (ik weet dat ik onszelf al heb gefeliciteerd) - we hebben een minimaal Kubernetes "cluster" met een volledig functionele API!
We lanceren onder
Laten we nu eens kijken waartoe de API in staat is. Laten we beginnen met de nginx-pod:
$ ./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.
Hier zien we hoe jammerlijk onvolledig onze Kubernetes-omgeving is: we hebben geen accounts voor services. Laten we het opnieuw proberen door handmatig een serviceaccount aan te maken en kijken wat er gebeurt:
$ 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
Zelfs als we het serviceaccount handmatig hebben aangemaakt, wordt het authenticatietoken niet gegenereerd. Terwijl we blijven experimenteren met ons minimalistische ‘cluster’, zullen we ontdekken dat de meeste nuttige dingen die gewoonlijk automatisch gebeuren, zullen ontbreken. De Kubernetes API-server is vrij minimalistisch, waarbij het grootste deel van het zware werk en de automatische configuratie plaatsvindt in verschillende controllers en achtergrondtaken die nog niet actief zijn.
We kunnen dit probleem omzeilen door de optie in te stellen automountServiceAccountToken voor het serviceaccount (aangezien we het toch niet hoeven te gebruiken):
$ 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
Eindelijk is de pod verschenen! Maar in feite zal het niet starten omdat we dat niet hebben planner (planner) is een ander belangrijk onderdeel van Kubernetes. Opnieuw zien we dat de Kubernetes API verrassend "dom" is: wanneer je een Pod in de API maakt, registreert deze deze, maar probeert hij niet uit te vinden op welk knooppunt deze moet worden uitgevoerd.
In feite hebt u geen planner nodig om een pod uit te voeren. U kunt handmatig een knooppunt toevoegen aan het manifest in de parameter nodeName:
(Vervangen mink8s naar de naam van het knooppunt.) Na verwijderen en toepassen zien we dat nginx is gestart en luistert naar het interne IP-adres:
$ ./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>
Om er zeker van te zijn dat het netwerk tussen de pods correct werkt, kunnen we curl uitvoeren vanaf een andere 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>
Het is best interessant om in deze omgeving te duiken en te zien wat werkt en wat niet. Ik ontdekte dat ConfigMap en Secret werken zoals verwacht, maar Service en Deployment niet.
Succes!
Dit bericht wordt lang, dus ik ga de overwinning uitroepen en zeggen dat dit een haalbare configuratie is die 'Kubernetes' kan worden genoemd. Samenvattend: vier binaire bestanden, vijf opdrachtregelparameters en 'slechts' 45 regels YAML (niet zoveel volgens Kubernetes-standaarden) en er werken nogal wat dingen:
Pods worden beheerd met behulp van de reguliere Kubernetes API (met een paar hacks)
U kunt openbare containerimages uploaden en beheren
Pods blijven in leven en worden automatisch opnieuw opgestart
Netwerken tussen pods binnen hetzelfde knooppunt werkt redelijk goed
ConfigMap, geheime en eenvoudige opslagmontage werkt zoals verwacht
Maar veel van wat Kubernetes echt nuttig maakt, ontbreekt nog steeds, zoals:
Pod-planner
Authenticatie autorisatie
Meerdere knooppunten
Netwerk van diensten
Geclusterde interne DNS
Controllers voor serviceaccounts, implementaties, integratie met cloudproviders en de meeste andere goodies die Kubernetes met zich meebrengt
Dus wat hebben we eigenlijk gekregen? De Kubernetes API, die op zichzelf draait, is eigenlijk slechts een platform voor automatisering van containers. Het levert niet veel op (het is een taak voor verschillende controllers en operators die de API gebruiken), maar het biedt wel een consistente omgeving voor automatisering.