Minimuma Realigebla Kubernetes

La traduko de la artikolo estis preparita sojle de la komenco de la kurso "DevOps-praktikoj kaj iloj".

Minimuma Realigebla Kubernetes

Se vi legas ĉi tion, vi verŝajne aŭdis ion pri Kubernetes (kaj se ne, kiel vi alvenis ĉi tie?) Sed kio ĝuste estas Kubernetes? Ĉi tio "Orkestrado de industrikvalitaj ujoj"? Aŭ "Nub-indiĝena operaciumo"? Kion ĉi tio eĉ signifas?

Verdire, mi ne estas 100% certa. Sed mi pensas, ke estas interese fosi en la internon kaj vidi kio vere okazas en Kubernetes sub ĝiaj multaj tavoloj de abstraktaĵoj. Do nur por amuzo, ni rigardu kiel efektive aspektas minimuma "Kubernetes-areto". (Ĉi tio estos multe pli facila ol Kubernetes La Malmola Vojo.)

Mi supozas, ke vi havas bazan scion pri Kubernetes, Linukso kaj ujoj. Ĉio, pri kio ni parolas ĉi tie, estas nur por esploro/lernado, ne metu ion el ĝi en produktadon!

trarigardo

Kubernetes enhavas multajn komponantojn. Laŭ Vikipedio, la arkitekturo aspektas jene:

Minimuma Realigebla Kubernetes

Estas almenaŭ ok komponantoj montritaj ĉi tie, sed ni ignoros la plej multajn el ili. Mi volas konstati, ke la minimuma afero, kiu povas racie nomi Kubernetes, konsistas el tri ĉefaj komponantoj:

  • kubeto
  • kube-apiserver (kiu dependas de etcd - ĝia datumbazo)
  • ujo rultempo (Docker ĉi-kaze)

Ni vidu, kion diras la dokumentaro pri ĉiu el ili (rus., Angla.). Unue kubeto:

Agento kuranta sur ĉiu nodo en la areto. Ĝi certigas, ke ujoj funkcias en la balgo.

Sonas sufiĉe simple. Kio pri ujo rultempoj (ujo rultempo)?

Uja rultempo estas programo dizajnita por ruli ujojn.

Tre informa. Sed se vi konas Docker, tiam vi devus havi ĝeneralan ideon pri tio, kion ĝi faras. (La detaloj de la disigo de respondecoj inter la ujo rultempo kaj la kubelet estas fakte sufiĉe subtilaj kaj mi ne eniros ilin ĉi tie.)

И API-servilo?

API-Servilo estas la komponento de kontrolpanelo de Kubernetes, kiu elmontras la API de Kubernetes. La API-servilo estas la klienta flanko de la kontrolpanelo Kubernetes

Ĉiu, kiu iam faris ion ajn kun Kubernetes, devis interagi kun la API aŭ rekte aŭ per kubectl. Ĉi tio estas la koro de tio, kio faras Kubernetes Kubernetes - la cerbo, kiu igas la montojn de YAML, kiujn ni ĉiuj konas kaj amas (?) en funkcian infrastrukturon. Ŝajnas evidente, ke la API devus ĉeesti en nia minimuma agordo.

Antaŭkondiĉoj

  • Linukso virtuala aŭ fizika maŝino kun radika aliro (mi uzas Ubuntu 18.04 sur virtuala maŝino).
  • Kaj ĉio estas!

Enuiga instalado

Ni devas instali Docker sur la maŝino, kiun ni uzos. (Mi ne eniros en detalojn pri kiel funkcias Docker kaj ujoj; se vi interesiĝas, estas mirindaj artikoloj). Ni simple instalu ĝin per apt:

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

Post tio, ni devas akiri la binarojn de Kubernetes. Fakte, por la komenca lanĉo de nia "areto" ni bezonas nur kubelet, ĉar por ruli aliajn servilkomponentojn ni povas uzi kubelet. Por interagi kun nia areto post kiam ĝi funkcias, ni ankaŭ uzos 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

Kio okazas se ni nur kuras kubelet?

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

kubelet devas ruliĝi kiel radiko. Sufiĉe logika, ĉar li bezonas administri la tutan nodon. Ni rigardu ĝiajn parametrojn:

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

Ve, tiom da ebloj! Feliĉe, ni bezonas nur kelkajn el ili. Jen unu el la parametroj pri kiuj ni interesiĝas:

--pod-manifest-path string

Vojo al la dosierujo enhavanta dosierojn por senmovaj podoj, aŭ vojo al dosiero priskribanta senmovaj podoj. Dosieroj komenciĝantaj per punktoj estas ignoritaj. (Malaprobita: Ĉi tiu opcio devas esti agordita en la agorda dosiero transdonita al la Kubelet per la opcio --config. Por pliaj informoj, vidu kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file .)

Ĉi tiu opcio permesas al ni kuri senmovaj podoj — podoj kiuj ne estas administritaj per la Kubernetes API. Senmovaj guŝoj estas malofte uzataj, sed ili estas tre oportunaj por rapide levi areton, kaj ĝuste ĉi tion ni bezonas. Ni ignoros ĉi tiun grandan averton (denove, ne rulu ĉi tion en produktado!) kaj vidu ĉu ni povas funkcii la podon.

Unue ni kreos dosierujon por senmovaj podoj kaj funkcios kubelet:

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

Tiam, en alia terminalo/tmux-fenestro/kio ajn, ni kreos pod manifeston:

$ cat <<EOF > pods/hello.yaml
apiVersion: v1
kind: Pod
metadata:
  name: hello
spec:
  containers:
  - image: busybox
    name: hello
    command: ["echo", "hello world!"]
EOF

kubelet komencas skribi kelkajn avertojn kaj ŝajnas, ke nenio okazas. Sed tio ne estas vera! Ni rigardu 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 Mi legis la pod manifeston kaj donis al Docker la komandon lanĉi kelkajn ujojn laŭ niaj specifoj. (Se vi miras pri la "paŭzo" ujo, ĝi estas Kubernetes-hako - vidu ĉi tiu blogo.) Kubelet lanĉos nian ujon busybox kun la specifita komando kaj rekomencos ĝin senfine ĝis la statika pod estos forigita.

Gratulu vin mem. Ni ĵus elpensis unu el la plej konfuzaj manieroj eligi tekston al la terminalo!

Lanĉi ktpd

Nia fina celo estas ruli la Kubernetes API, sed por fari tion ni unue devas funkcii ktp. Ni komencu minimuman etcd-grupon metante ĝiajn agordojn en la dosierujon pods (ekzemple, 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

Se vi iam laboris kun Kubernetes, ĉi tiuj YAML-dosieroj devus esti konataj al vi. Estas nur du punktoj atentindaj ĉi tie:

Ni muntis la gastigan dosierujon /var/lib/etcd en la pod por ke la etcd-datumoj estas konservitaj post rekomenco (se tio ne estas farita, la grapolŝtato estos forviŝita ĉiufoje kiam la pod estas rekomencita, kio ne estos bona por eĉ minimuma instalo de Kubernetes).

Ni instalis hostNetwork: true. Ĉi tiu agordo, nesurprize, agordas etcd por uzi la gastigan reton anstataŭ la internan reton de la pod (ĉi tio faciligos al la API-servilo trovi la etcd-areton).

Simpla kontrolo montras, ke etcd efektive funkcias sur localhost kaj konservas datumojn al disko:

$ 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

Lanĉante la API-servilon

Ruli Kubernetes API-servilon estas eĉ pli facila. La sola parametro, kiun oni devas pasi, estas --etcd-servers, faras tion, kion vi atendas:

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

Metu ĉi tiun YAML-dosieron en la dosierujon pods, kaj la API-servilo komenciĝos. Kontrolante kun curl montras, ke la Kubernetes API aŭskultas sur la haveno 8080 kun tute malferma aliro - ne necesas aŭtentigo!

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

(Denove, ne rulu ĉi tion en produktado! Mi estis iomete surprizita, ke la defaŭlta agordo estas tiel nesekura. Sed mi supozas, ke tio estas por faciligi disvolviĝon kaj testadon.)

Kaj, agrabla surprizo, kubectl funkcias el la skatolo sen aldonaj agordoj!

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

problemo

Sed se vi fosas iom pli profunde, io ŝajnas esti malbone:

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

La senmovaj podoj, kiujn ni kreis, malaperis! Fakte, nia kubelet-nodo tute ne estas malkovrita:

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

Kio estas la problemo? Se vi memoras antaŭ kelkaj alineoj, ni komencis la kubelet per ekstreme simpla aro de komandliniaj parametroj, do la kubelet ne scias kiel kontakti la API-servilon kaj sciigi ĝin pri ĝia stato. Post studado de la dokumentado, ni trovas la respondan flagon:

--kubeconfig string

La vojo al la dosiero kubeconfig, kiu precizigas kiel konekti al la API-servilo. Havebleco --kubeconfig ebligas API-servilan reĝimon, ne --kubeconfig ebligas senkonektan reĝimon.

Dum ĉi tiu tempo, sen scii ĝin, ni funkciis la kubelet en "senreta reĝimo". (Se ni estus pedantaj, ni povus pensi pri memstara kubelet kiel "minimuma realigebla Kubernetes", sed tio estus tre enuiga). Por ke la "reala" agordo funkciu, ni devas transdoni la kubeconfig-dosieron al la kubelet, por ke ĝi sciu kiel paroli kun la API-servilo. Feliĉe ĝi estas sufiĉe simpla (ĉar ni ne havas problemojn pri aŭtentigo aŭ atestilo):

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

Konservu ĉi tion kiel kubeconfig.yaml, mortigu la procezon kubelet kaj rekomencu kun la necesaj parametroj:

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

(Cetere, se vi provas aliri la API per buklo kiam la kubelet ne funkcias, vi trovos, ke ĝi ankoraŭ funkcias! Kubelet ne estas "gepatro" de siaj podoj kiel Docker, ĝi pli similas al "kontrolo". demono." Ujoj administritaj de kubelet daŭros funkcii ĝis la kubelet haltigos ilin.)

Post kelkaj minutoj kubectl devus montri al ni la podojn kaj nodojn kiel ni atendas:

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

Ni vere gratulu nin ĉi-foje (mi scias, ke mi jam gratulis nin) - ni havas minimuman "areton" de Kubernetes funkcianta kun plene funkcia API!

Ni lanĉas sub

Nun ni vidu, kion kapablas la API. Ni komencu per la nginx pod:

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

Ĉi tie ni ricevas sufiĉe interesan eraron:

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

Ĉi tie ni vidas kiom bedaŭrinde nekompleta estas nia Kubernetes-medio - ni ne havas kontojn por servoj. Ni provu denove permane kreante servokonton kaj vidu kio okazas:

$ 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

Eĉ kiam ni kreis la servokonton permane, la aŭtentikigĵetono ne estas generita. Dum ni daŭre eksperimentas kun nia minimumisma "areto", ni trovos, ke la plej multaj el la utilaj aferoj, kiuj kutime okazas aŭtomate, mankos. La Kubernetes API-servilo estas sufiĉe minimumisma, kun la plej granda parto de la peza levado kaj aŭtomata agordo okazas en diversaj regiloj kaj fonaj laboroj, kiuj ankoraŭ ne funkcias.

Ni povas trakti ĉi tiun problemon agordante la opcion automountServiceAccountToken por la servokonto (ĉar ni tamen ne devos uzi ĝin):

$ 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

Fine, la balgo aperis! Sed fakte ĝi ne komenciĝos ĉar ni ne havas planisto (planilo) estas alia grava ero de Kubernetes. Denove, ni vidas, ke la Kubernetes API estas surprize "stulta" - kiam vi kreas Pod en la API, ĝi registras ĝin, sed ne provas eltrovi sur kiu nodo ruli ĝin.

Fakte, vi ne bezonas planilon por ruli pod. Vi povas permane aldoni nodon al la manifesto en la parametro nodeName:

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

(Anstataŭigi mink8s al la nomo de la nodo.) Post forigi kaj apliki, ni vidas, ke nginx komenciĝis kaj aŭskultas la internan IP-adreson:

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

Por certigi, ke la reto inter podoj funkcias ĝuste, ni povas ruli curl de alia 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>

Estas sufiĉe interese fosi ĉi tiun medion kaj vidi kio funkcias kaj kio ne. Mi trovis, ke ConfigMap kaj Secret funkcias kiel atendite, sed Servo kaj Deplojo ne funkcias.

Sukceso!

Ĉi tiu afiŝo fariĝas longa, do mi proklamos venkon kaj diros, ke tio estas realigebla agordo, kiun oni povas nomi "Kubernetes". Resume: kvar binaroj, kvin komandliniaj parametroj kaj "nur" 45 linioj de YAML (ne tiom laŭ normoj Kubernetes) kaj ni havas sufiĉe da aferoj funkcias:

  • Pods estas administritaj per la regula Kubernetes API (kun kelkaj hakoj)
  • Vi povas alŝuti kaj administri publikajn ujajn bildojn
  • Pods restas vivantaj kaj aŭtomate rekomencas
  • Retoj inter balgoj ene de la sama nodo funkcias sufiĉe bone
  • ConfigMap, Sekreta kaj simpla konservada muntado funkcias kiel atendite

Sed multe de tio, kio faras Kubernetes vere utila, ankoraŭ mankas, kiel ekzemple:

  • Pod Scheduler
  • Aŭtentigo/rajtigo
  • Multoblaj nodoj
  • Reto de servoj
  • Amasigita interna DNS
  • Regiloj por servokontoj, deplojoj, integriĝo kun nubaj provizantoj kaj la plej multaj el la aliaj bonaĵoj, kiujn Kubernetes alportas

Do kion ni efektive ricevis? La Kubernetes API, funkcianta memstare, estas vere nur platformo por ujo aŭtomatigo. Ĝi ne faras multon - ĝi estas laboro por diversaj regiloj kaj funkciigistoj uzantaj la API - sed ĝi disponigas konsekvencan medion por aŭtomatigo.

Lernu pli pri la kurso en la senpaga retseminario.

Legu pli:

fonto: www.habr.com

Aldoni komenton