Minimum Viable Kubernetes

Ang pagsasalin ng artikulo ay inihanda sa bisperas ng pagsisimula ng kurso "Mga kasanayan at tool ng DevOps".

Minimum Viable Kubernetes

Kung binabasa mo ito, malamang na may narinig ka tungkol sa Kubernetes (at kung hindi, paano ka napunta dito?) Ngunit ano nga ba ang Kubernetes? Ito "Orkestrasyon ng mga lalagyan na pang-industriya"? O kaya "Cloud-Native Operating System"? Ano ang ibig sabihin nito?

Sa totoo lang, hindi ako 100% sigurado. Ngunit sa tingin ko ito ay kagiliw-giliw na bungkalin ang mga panloob at makita kung ano talaga ang nangyayari sa Kubernetes sa ilalim ng maraming mga layer ng abstraction nito. Kaya para lang masaya, tingnan natin kung ano talaga ang hitsura ng minimal na β€œKubernetes cluster”. (Ito ay magiging mas madali kaysa sa Kubernetes Ang Mahirap na Daan.)

Ipinapalagay ko na mayroon kang pangunahing kaalaman sa Kubernetes, Linux, at mga lalagyan. Lahat ng pinag-uusapan natin dito ay para sa pananaliksik/pag-aaral lamang, huwag ilagay ang alinman sa mga ito sa produksyon!

Repasuhin

Ang Kubernetes ay naglalaman ng maraming bahagi. Ayon kay Wikipedia, ganito ang hitsura ng arkitektura:

Minimum Viable Kubernetes

Mayroong hindi bababa sa walong bahagi na ipinapakita dito, ngunit hindi namin papansinin ang karamihan sa mga ito. Gusto kong sabihin na ang pinakamababang bagay na maaaring makatwirang tawaging Kubernetes ay binubuo ng tatlong pangunahing bahagi:

  • kubelet
  • kube-apiserver (na nakasalalay sa etcd - database nito)
  • runtime ng container (Docker sa kasong ito)

Tingnan natin kung ano ang sinasabi ng dokumentasyon tungkol sa bawat isa sa kanila (rus., Ingles.). Sa simula kubelet:

Isang ahente na tumatakbo sa bawat node sa cluster. Tinitiyak nito na ang mga lalagyan ay tumatakbo sa pod.

Parang simple lang. Paano kung mga runtime ng container (runtime ng container)?

Ang container runtime ay isang program na idinisenyo upang magpatakbo ng mga container.

Napaka informative. Ngunit kung pamilyar ka sa Docker, dapat ay mayroon kang pangkalahatang ideya kung ano ang ginagawa nito. (Ang mga detalye ng paghihiwalay ng mga responsibilidad sa pagitan ng runtime ng container at ng kubelet ay talagang banayad at hindi ko ito sasagutin dito.)

И API server?

Ang API Server ay ang Kubernetes control panel component na naglalantad sa Kubernetes API. Ang API server ay ang client side ng Kubernetes control panel

Ang sinumang nakagawa ng anumang bagay sa Kubernetes ay kailangang makipag-ugnayan sa API nang direkta o sa pamamagitan ng kubectl. Ito ang puso kung bakit ang Kubernetes Kubernetes - ang utak na ginagawang gumaganang imprastraktura ang mga bundok ng YAML na kilala at mahal nating lahat (?). Mukhang halata na ang API ay dapat naroroon sa aming minimal na configuration.

Preconditions

  • Linux virtual o pisikal na makina na may root access (Gumagamit ako ng Ubuntu 18.04 sa isang virtual machine).
  • At lahat na!

Nakakainip na pag-install

Kailangan nating i-install ang Docker sa machine na gagamitin natin. (Hindi ko na idedetalye kung paano gumagana ang Docker at mga container; kung interesado ka, mayroong kahanga-hangang mga artikulo). I-install na lang natin apt:

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

Pagkatapos nito, kailangan nating makuha ang mga binary ng Kubernetes. Sa katunayan, para sa paunang paglulunsad ng aming "kumpol" kailangan lang namin kubelet, dahil upang magpatakbo ng iba pang mga bahagi ng server na magagamit namin kubelet. Para makipag-ugnayan sa aming cluster pagkatapos itong tumakbo, gagamitin din namin 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

Ano ang mangyayari kung tatakbo na lang tayo kubelet?

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

kubelet dapat tumakbo bilang ugat. Medyo lohikal, dahil kailangan niyang pamahalaan ang buong node. Tingnan natin ang mga parameter nito:

$ ./kubelet -h
<слишком ΠΌΠ½ΠΎΠ³ΠΎ строк, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Ρ€Π°Π·ΠΌΠ΅ΡΡ‚ΠΈΡ‚ΡŒ здСсь>
$ ./kubelet -h | wc -l
284

Wow, napakaraming pagpipilian! Sa kabutihang-palad, kailangan lang namin ng isang pares ng mga ito. Narito ang isa sa mga parameter na interesado kami:

--pod-manifest-path string

Path sa direktoryo na naglalaman ng mga file para sa mga static na pod, o path sa isang file na naglalarawan ng mga static na pod. Binabalewala ang mga file na nagsisimula sa mga tuldok. (DEPRECATED: Dapat itakda ang opsyong ito sa configuration file na ipinasa sa Kubelet sa pamamagitan ng --config na opsyon. Para sa higit pang impormasyon, tingnan ang kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file .)

Ang pagpipiliang ito ay nagpapahintulot sa amin na tumakbo mga static na pod β€” mga pod na hindi pinamamahalaan sa pamamagitan ng Kubernetes API. Ang mga static na pod ay bihirang ginagamit, ngunit ang mga ito ay napaka-maginhawa para sa mabilis na pagpapalaki ng isang kumpol, at ito mismo ang kailangan namin. Hindi namin papansinin ang malaking babala na ito (muli, huwag itong patakbuhin sa produksyon!) at tingnan kung mapapatakbo namin ang pod.

Una, gagawa kami ng isang direktoryo para sa mga static na pod at tatakbo kubelet:

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

Pagkatapos, sa isa pang terminal/tmux window/kahit ano, gagawa kami ng 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 nagsimulang magsulat ng ilang babala at parang walang nangyayari. Ngunit hindi iyon totoo! Tingnan natin ang 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 Binasa ko ang pod manifest at binigyan si Docker ng utos na maglunsad ng ilang container ayon sa aming mga detalye. (Kung nagtataka ka tungkol sa container na "pause", isa itong hack ng Kubernetes - tingnan mo ang blog na ito.) Ilulunsad ng Kubelet ang aming lalagyan busybox gamit ang tinukoy na utos at i-restart ito nang walang katiyakan hanggang sa matanggal ang static na pod.

Batiin ang iyong sarili. Nakaisip lang kami ng isa sa mga pinakanakalilitong paraan para mag-output ng text sa terminal!

Ilunsad atbp

Ang aming pinakalayunin ay patakbuhin ang Kubernetes API, ngunit para magawa iyon kailangan muna naming tumakbo atbp. Magsimula tayo ng kaunting etcd cluster sa pamamagitan ng paglalagay ng mga setting nito sa direktoryo ng pods (halimbawa, 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

Kung nakatrabaho mo na ang Kubernetes, dapat na pamilyar sa iyo ang mga YAML file na ito. Mayroon lamang dalawang puntos na dapat tandaan dito:

Na-mount namin ang folder ng host /var/lib/etcd sa pod upang ang etcd data ay mapangalagaan pagkatapos ng pag-restart (kung hindi ito nagawa, ang estado ng cluster ay mabubura sa tuwing magre-restart ang pod, na hindi magiging maganda para sa kahit kaunting pag-install ng Kubernetes).

Na-install na namin hostNetwork: true. Ang setting na ito, hindi nakakagulat, ay nagko-configure ng etcd upang gamitin ang host network sa halip na ang panloob na network ng pod (ito ay magiging mas madali para sa API server na mahanap ang etcd cluster).

Ang isang simpleng pagsusuri ay nagpapakita na ang etcd ay talagang tumatakbo sa localhost at nagse-save ng data sa 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.wal

Pagsisimula ng API server

Ang pagpapatakbo ng isang Kubernetes API server ay mas madali. Ang tanging parameter na kailangang ipasa ay --etcd-servers, ginagawa ang iyong inaasahan:

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

Ilagay ang YAML file na ito sa direktoryo pods, at magsisimula ang API server. Sinusuri gamit ang curl nagpapakita na ang Kubernetes API ay nakikinig sa port 8080 na may ganap na bukas na access - walang kinakailangang pagpapatunay!

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

(Muli, huwag patakbuhin ito sa produksyon! Medyo nagulat ako na ang default na setting ay napaka-insecure. Ngunit sa palagay ko ito ay para gawing mas madali ang pag-develop at pagsubok.)

At, nakakatuwang sorpresa, gumagana ang kubectl sa labas ng kahon nang walang anumang karagdagang mga setting!

$ ./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.

problema

Ngunit kung maghuhukay ka ng kaunti pa, tila may mali:

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

Wala na ang mga static na pod na ginawa namin! Sa katunayan, ang aming kubelet node ay hindi natuklasan:

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

Anong problema? Kung naaalala mo ang ilang talata na nakalipas, sinimulan namin ang kubelet gamit ang napakasimpleng hanay ng mga parameter ng command line, kaya hindi alam ng kubelet kung paano makipag-ugnayan sa API server at ipaalam ito sa estado nito. Pagkatapos pag-aralan ang dokumentasyon, nakita namin ang kaukulang bandila:

--kubeconfig string

Ang landas sa file kubeconfig, na tumutukoy kung paano kumonekta sa API server. Availability --kubeconfig pinapagana ang mode ng server ng API, hindi --kubeconfig pinapagana ang offline mode.

Sa lahat ng oras na ito, nang hindi nalalaman, pinapatakbo namin ang kubelet sa "offline mode." (Kung tayo ay nagiging pedantic, maaari nating isipin ang isang standalone na kubelet bilang "minimum viable Kubernetes", ngunit iyon ay magiging napakaboring). Para gumana ang "totoong" configuration, kailangan nating ipasa ang kubeconfig file sa kubelet para malaman nito kung paano makipag-usap sa API server. Sa kabutihang palad ito ay medyo simple (dahil wala kaming anumang mga isyu sa pagpapatunay o sertipiko):

apiVersion: v1
kind: Config
clusters:
- cluster:
    server: http://127.0.0.1:8080
  name: mink8s
contexts:
- context:
    cluster: mink8s
  name: mink8s
current-context: mink8s

I-save ito bilang kubeconfig.yaml, patayin ang proseso kubelet at i-restart gamit ang mga kinakailangang parameter:

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

(Nga pala, kung susubukan mong i-access ang API sa pamamagitan ng curl kapag ang kubelet ay hindi tumatakbo, makikita mo na ito ay tumatakbo pa rin! Kubelet ay hindi isang "magulang" ng mga pod nito tulad ng Docker, ito ay mas katulad ng isang "kontrol". daemon.” Ang mga lalagyan na pinamamahalaan ng isang kubelet ay patuloy na tatakbo hanggang sa mapahinto sila ng kubelet.)

Sa ilang minuto kubectl dapat ipakita sa amin ang mga pod at node gaya ng inaasahan namin:

$ ./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

Talagang batiin natin ang ating mga sarili sa pagkakataong ito (alam kong binati ko na ang ating mga sarili) - mayroon tayong kaunting Kubernetes na "cluster" na tumatakbo na may fully functional na API!

Inilunsad namin sa ilalim

Ngayon tingnan natin kung ano ang kaya ng API. Magsimula tayo sa nginx pod:

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

Dito nakakakuha kami ng isang medyo kawili-wiling error:

$ ./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.

Dito natin makikita kung gaano kahirap ang ating kapaligiran sa Kubernetes - wala tayong mga account para sa mga serbisyo. Subukan nating muli sa pamamagitan ng manu-manong paggawa ng account ng serbisyo at tingnan kung ano ang mangyayari:

$ 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

Kahit na ginawa namin nang manu-mano ang account ng serbisyo, hindi nabuo ang token ng pagpapatunay. Habang patuloy kaming nag-eeksperimento sa aming minimalistic na "cluster", makikita namin na ang karamihan sa mga kapaki-pakinabang na bagay na karaniwang nangyayari ay awtomatikong mawawala. Ang server ng Kubernetes API ay medyo minimalistic, na ang karamihan sa mabigat na pag-angat at awtomatikong pagsasaayos ay nangyayari sa iba't ibang mga controller at mga trabaho sa background na hindi pa tumatakbo.

Maaari naming lutasin ang problemang ito sa pamamagitan ng pagtatakda ng opsyon automountServiceAccountToken para sa account ng serbisyo (dahil hindi na namin ito kailangang gamitin pa rin):

$ 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

Sa wakas, lumitaw na ang pod! Pero sa totoo lang hindi magsisimula dahil wala naman kami tagaplano (scheduler) ay isa pang mahalagang bahagi ng Kubernetes. Muli, nakita namin na ang Kubernetes API ay nakakagulat na "pipi" - kapag gumawa ka ng Pod sa API, nirerehistro ito, ngunit hindi sinusubukang alamin kung saang node ito tatakbo.

Sa katunayan, hindi mo kailangan ng scheduler para magpatakbo ng pod. Maaari kang manu-manong magdagdag ng node sa manifest sa parameter nodeName:

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

(Palitan mink8s sa pangalan ng node.) Pagkatapos tanggalin at ilapat, nakita namin na ang nginx ay nagsimula at nakikinig sa panloob na IP address:

$ ./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>

Upang matiyak na gumagana nang tama ang network sa pagitan ng mga pod, maaari naming patakbuhin ang curl mula sa isa pang 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>

Medyo kawili-wiling maghukay sa kapaligirang ito at makita kung ano ang gumagana at kung ano ang hindi. Nalaman kong gumagana ang ConfigMap at Secret gaya ng inaasahan, ngunit hindi gumagana ang Serbisyo at Pag-deploy.

Tagumpay!

Ang post na ito ay humahaba, kaya't ako ay magdedeklara ng tagumpay at sasabihin na ito ay isang mabubuhay na pagsasaayos na maaaring tawaging "Kubernetes". Upang ibuod: apat na binary, limang mga parameter ng command line at "lamang" 45 na linya ng YAML (hindi na magkano ayon sa mga pamantayan ng Kubernetes) at mayroon kaming ilang bagay na gumagana:

  • Ang mga pod ay pinamamahalaan gamit ang regular na Kubernetes API (na may ilang mga hack)
  • Maaari kang mag-upload at mamahala ng mga pampublikong larawan ng lalagyan
  • Nananatiling buhay ang mga pod at awtomatikong magre-restart
  • Ang networking sa pagitan ng mga pod sa loob ng parehong node ay gumagana nang maayos
  • ConfigMap, Secret at simpleng storage mounting work gaya ng inaasahan

Ngunit karamihan sa kung bakit tunay na kapaki-pakinabang ang Kubernetes ay nawawala pa rin, tulad ng:

  • Pod Scheduler
  • Authentication/authorization
  • Maramihang node
  • Network ng mga serbisyo
  • Naka-cluster na panloob na DNS
  • Mga controller para sa mga service account, deployment, integration sa cloud providers at karamihan sa iba pang goodies na hatid ng Kubernetes

Kaya ano talaga ang nakuha natin? Ang Kubernetes API, na tumatakbo sa sarili nitong, ay talagang isang platform para sa automation ng lalagyan. Wala itong gaanong nagagawa - ito ay isang trabaho para sa iba't ibang mga controller at operator na gumagamit ng API - ngunit nagbibigay ito ng pare-parehong kapaligiran para sa automation.

Matuto nang higit pa tungkol sa kurso sa libreng webinar.

Magbasa pa:

Pinagmulan: www.habr.com

Magdagdag ng komento