Översättningen av artikeln är framtagen inför kursstarten .

Om du läser detta har du förmodligen hört talas om Kubernetes (och om inte, hur kom du hit?). Men vad är egentligen Kubernetes? Det är ? Или ? Что вообще это значит?
Ärligt talat är jag inte 100 % säker. Men jag tror det är intressant att gräva ner i detaljerna och se vad som verkligen händer i Kubernetes under dess många abstraktionslager. Så för intressets skull, låt oss titta på hur en minimal “Kubernetes-kluster” faktiskt ser ut. (Det kommer att bli mycket enklare än .)
Jag antar att du har grundläggande kunskaper om Kubernetes, Linux och containrar. Allt vi kommer att diskutera här är bara avsett för utforskning/lärande, kör inget av detta i produktion!
Översikt
Kubernetes innehåller många komponenter. Enligt , ser arkitekturen ut på följande sätt:

Här visas åtminstone åtta komponenter, men de flesta av dem kommer vi att ignorera. Jag vill påstå att den minimi- enhet som rimligt kan kallas Kubernetes består av tre huvudkomponenter:
- kubelet
- kube-apiserver (som beror på etcd — dess databas)
- container runtime (i det här fallet Docker)
Låt oss se vad som sägs om var och en av dem i dokumentationen (., .). Först kubelet:
Agenten som kör på varje nod i klustret. Den ser till att containrarna körs i podden.
Det låter ganska enkelt. Vad sägs om container runtime? Container-runtime är ett program avsett för att köra containrar.
Mycket informativt. Men om du är bekant med Docker, borde du ha en generell förståelse för vad det gör. (Detaljerna kring ansvarsfördelningen mellan container runtime och kubelet är faktiskt ganska subtila och jag kommer inte att gå in på dem här.)
API-servern
Och API-servern är en komponent av Kubernetes kontrollplan som presenterar Kubernetes API. API-servern är klientsidan av Kubernetes kontrollplan.?
Vem som helst som någonsin har gjort något med Kubernetes har behövt interagera med API:et antingen direkt eller via kubectl. Det är hjärtat av vad som gör Kubernetes till Kubernetes — hjärnan som omvandlar de mängder YAML som vi alla känner till och älskar (?) till fungerande infrastruktur. Det verkar uppenbart att API:t måste finnas med i vår minimala konfiguration.
Любому, кто когда-либо что-либо делал с Kubernetes, приходилось взаимодействовать с API либо напрямую, либо через kubectl. Это сердце того, что делает Kubernetes Kubernetes’ом — мозг, превращающий горы YAML, который мы все знаем и любим (?), в работающую инфраструктуру. Кажется очевидным, что API должен присутствовать в нашей минимальной конфигурации.
Förutsättningar
- En virtuell eller fysisk Linux-maskin med root-åtkomst (jag använder Ubuntu 18.04 på en virtuell maskin).
- Och det är allt!
Tråkig installation
På maskinen vi ska använda behöver vi installera Docker. (Jag kommer inte att gå in på detaljer om hur Docker och containrar fungerar; om du är intresserad finns det ). Låt oss bara installera det med apt:
$ sudo apt install docker.io
$ sudo systemctl start docker Därefter behöver vi hämta Kubernetes-binärerna. För att starta vår "kluster" behöver vi faktiskt bara kubelet, eftersom vi kan använda kubeletför att köra andra serverkomponenter. För att interagera med vårt kluster när det väl är igång, kommer vi också att använda 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 Vad händer om vi bara kör kubelet?
$ ./kubelet
F0609 04:03:29.105194 4583 server.go:254] mkdir /var/lib/kubelet: permission denied kubelet måste köras som root. Ganska logiskt, eftersom den behöver hantera hela noden. Låt oss titta på dess alternativ:
$ ./kubelet -h
$ ./kubelet -h | wc -l
284Oj, så många alternativ! Som tur är behöver vi bara ett par av dem. Här är ett av alternativen som intresserar oss:
--pod-manifest-path stringSökväg till katalogen som innehåller filer för statiska pods, eller sökvägen till en fil med beskrivning av statiska pods. Filer som börjar med punkter ignoreras. (URDATERAD: detta alternativ bör ställas in i konfigurationsfilen som överförs till Kubelet via alternativet —config. För mer information se. .)
Detta alternativ gör att vi kan köra — pods som inte hanteras via Kubernetes API. Statiska pods används sällan, men de är mycket praktiska för att snabbt sätta upp ett kluster, vilket är precis vad vi behöver. Vi kommer att ignorera denna varning (igen, kör inte detta i produktion!) och se om vi kan köra en pod.
Först kommer vi att skapa en katalog för statiska pods och köra kubelet:
$ mkdir pods
$ sudo ./kubelet --pod-manifest-path=podsSedan, i ett annat terminalfönster/tmux/eller någon annanstans, kommer vi att skapa manifestet för podden:
$ cat < pods/hello.yaml
apiVersion: v1
kind: Pod
metadata:
name: hello
spec:
containers:
- image: busybox
name: hello
command: ["echo", "hello world!"]
EOF kubelet börjar skriva några varningar och det verkar som att inget händer. Men så är det inte! Låt oss titta 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 för podden och gav Docker kommandot att starta ett par containrar enligt vår specifikation. (Om du är intresserad av containern “pause”, så är det en Kubernetes-hack — detaljer kan läsas i .) Kubelet kommer att starta vår container busybox med det angivna kommandot och kommer att starta om det oändligt, så länge den statiska podden inte tas bort.
Grattis! Vi har just kommit på ett av de mest invecklade sätten att skriva ut text i terminalen!
Startar etcd
Vårt slutgiltiga mål är att starta Kubernetes API, men för det behöver vi först starta . Låt oss starta en minimal etcd-kluster och placera dess inställningar i katalogen pods (till exempel, 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-dataOm du någonsin har arbetat med Kubernetes, bör liknande YAML-filer vara bekanta för dig. Här är det bara två saker att notera:
Vi har monterat värddatorns mapp /var/lib/etcd i podden så att etcd-data sparas efter omstart (om detta inte görs kommer klustrets tillstånd att raderas vid varje omstart av podden, vilket inte är bra ens för en minimal Kubernetes-installation).
Vi har ställt in hostNetwork: true. Denna parameter, vilket inte är förvånande, konfigurerar etcd att använda värd-nätverket istället för poddens interna nätverk (detta kommer att göra det lättare för API-servern att hitta etcd-klustret).
En enkel kontroll visar att etcd verkligen körs på localhost och sparar data på disk:
$ 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.walStarta API-servern
Att starta Kubernetes API-servern är ännu enklare. Det enda parameter som måste anges, --etcd-servers, gör vad du förväntar dig:
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 Placera den här YAML-filen i katalogen pods, och API-servern startar. Kontroll med curl visar att Kubernetes API lyssnar på port 8080 med fullständig öppen åtkomst — autentisering krävs inte!
$ curl localhost:8080/healthz
ok
$ curl localhost:8080/api/v1/pods
{
"kind": "PodList",
"apiVersion": "v1",
"metadata": {
"selfLink": "/api/v1/pods",
"resourceVersion": "59"
},
"items": []
}(Återigen, kör inte detta i produktion! Jag blev lite överraskad över att standardinställningen är så osäker. Men jag antar att det är för att underlätta utveckling och testning.)
Och en trevlig överraskning, kubectl fungerar direkt utan några ytterligare inställningar!
$ ./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 om vi gräver lite djupare verkar det som att något går snett:
$ ./kubectl get pod -n kube-system
No resources found in kube-system namespace.De statiska poddar vi skapade är borta! Faktisk, vår kubelet-nod kan inte ens hittas:
$ ./kubectl get nodes
No resources found in default namespace.Vad är problemet? Om ni kommer ihåg, i några stycken tillbaka körde vi kubelet med ett mycket enkelt set av kommandoradsparametrar, så kubelet vet inte hur man ska kontakta API-servern och rapportera sitt tillstånd. Genom att studera dokumentationen hittar vi den relevanta flaggan:
--kubeconfig string
Sökväg till filen kubeconfig, som anger hur man ansluter till API-servern. Närvaro av --kubeconfig aktiverar API-serverläge, frånvaro av --kubeconfig aktiverar fristående läge.
Under hela den här tiden, utan att veta om det, har vi kört kubelet i "autonomt läge". (Om vi var pedantiska skulle vi kunna betrakta kubelets autonoma läge som "minimalt livskraftig Kubernetes", men det skulle vara väldigt tråkigt). För att få den "riktiga" konfigurationen att fungera behöver vi överföra kubeconfig-filen till kubelet så att den vet hur man kommunicerar med API-servern. Lyckligtvis är detta ganska enkelt (eftersom vi inte har problem med autentisering eller certifikat):
apiVersion: v1
kind: Config
clusters:
- cluster:
server: http://127.0.0.1:8080
name: mink8s
contexts:
- context:
cluster: mink8s
name: mink8s
current-context: mink8s Spara detta som kubeconfig.yaml, döda processen kubelet och starta om med nödvändiga parametrar:
$ sudo ./kubelet --pod-manifest-path=pods --kubeconfig=kubeconfig.yaml(Förresten, om du försöker komma åt API:et via curl när kubelet inte kör, kommer du att upptäcka att det fortfarande fungerar! Kubelet är inte "förälder" till sina pods, liknande Docker, det liknar mer en "hanterande demon". De containrar som hanteras av kubelet kommer att fortsätta köra tills kubelet stoppar dem.)
Efter några minuter kubectl bör det visa oss pods och noder, precis som vi förväntade oss:
$ ./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.6Låt oss verkligen gratulera oss den här gången (jag vet att jag redan har gratulerat) — vi har fått en minimal "kluster" Kubernetes som fungerar med en fullt fungerande API!
Vi startar en pod
Nu ska vi se vad API:et kan göra. Låt oss börja med en nginx-pod:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- image: nginx
name: nginxHär kommer vi att få ett ganska intressant felmeddelande:
$ ./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.Här ser vi hur fruktansvärt ofullständig vår Kubernetes-miljö är — vi har inga tjänstekonton. Låt oss försöka igen, genom att skapa ett tjänstekonto manuellt, och se vad som händer:
$ 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Även när vi skapade servicekontot manuellt, skapas ingen autentiseringstoken. När vi fortsätter att experimentera med vår minimalistiska "kluster" kommer vi att upptäcka att de flesta användbara saker som vanligtvis sker automatiskt kommer att saknas. Kubernetes API-servern är ganska minimalistisk, och det mesta av de tunga automatiska inställningarna sker i olika kontrollanter och bakgrundsuppgifter som ännu inte körs.
Vi kan kringgå detta problem genom att ställa in alternativet automountServiceAccountToken för servicekontot (eftersom vi ändå inte behöver använda det):
$ 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 13mTill slut dyker podden upp! Men den kommer faktiskt inte att starta, eftersom vi inte har någon (scheduler) — en annan viktig komponent av Kubernetes. Igen ser vi att Kubernetes API är förvånansvärt "dum" — när du skapar en pod i API:et registrerar det den, men försöker inte räkna ut på vilken nod den ska köras.
För att starta en pod behövs faktiskt ingen planerare. Du kan manuellt lägga till noden i manifestet i parameter nodeName:
apiversion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- image: nginx
name: nginx
nodeName: mink8s
(Ersätt mink8s med nodens namn.) Efter delete och apply ser vi att nginx har startat och lyssnar på det interna IP-adressen:
$ .\/kubectl delete pod nginx
pod "nginx" raderad
$ .\/kubectl apply -f nginx.yaml
pod/nginx skapad
$ .\/kubectl get pods -owide
NAMN KLAR STATUS OMSTARTNINGAR ÅLDER IP NOD NOMINERAD NOD LÄSNINGS GATAN
nginx 1/1 Körning 0 30s 172.17.0.2 mink8s <none> <none>
$ curl -s 172.17.0.2 | head -4
<!DOCTYPE html>
<html>
<head>
<title>Välkommen till nginx!</title>För att säkerställa att nätverket mellan poddar fungerar korrekt kan vi köra curl från en annan podd:
$ 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 skapad
$ .\/kubectl logs curl | head -6
% Total % Mottagen % Xferd Genomsnittlig Hastighet Tid Tid Tid Aktuell
Dload Ladda Upp Totalt Tillbringad Kvar Hastighet
<!DOCTYPE html>
<html>
<head>
<title>Välkommen till nginx!</title>Det är ganska intressant att gräva i denna miljö och se vad som fungerar och vad som inte gör det. Jag har upptäckt att ConfigMap och Secret fungerar som förväntat, men Service och Deployment gör det inte.
Framgång!
Detta inlägg blir långt, så jag tänker avsluta och säga att detta är en livskraftig konfiguration som kan kallas “Kubernetes”. Sammanfattningsvis: fyra binärfiler, fem kommandoradsparametrar och “bara” 45 rader YAML (inte så mycket i Kubernetes standarder) och vi har fått många saker att fungera:
- Pods hanteras med hjälp av det vanliga Kubernetes API:et (med några hacks)
- Det går att ladda upp offentliga containerbilder och hantera dem
- Pods förblir aktiva och startas automatiskt om
- Nätverket mellan pods inom en nod fungerar ganska bra
- ConfigMap, Secret och enklast montering av lagring fungerar som det ska
Men mycket av det som gör Kubernetes verkligen användbart saknas fortfarande, till exempel:
- Pod-schemaläggare
- Autentisering / auktorisation
- Flera noder
- Tjänsternätverk
- Klonens interna DNS
- Kontroller för tjänstekonton, distributioner, integrationer med molnleverantörer och de flesta andra "funktioner" som Kubernetes medför
Vad har vi egentligen fått? Kubernetes API:et som fungerar på egen hand är faktiskt bara en plattform för automatisering av containrar. Det gör inte mycket — det är jobbet för olika kontroller och operatörer som använder API:et — men det ger en konsekvent miljö för automatisering.
Läsa mer:
Källa: habr.com
