Rehellisesti sanottuna en ole 100% varma. Mutta mielestäni on mielenkiintoista kaivaa sisäisyyteen ja nähdä, mitä Kubernetesissa todella tapahtuu sen monien abstraktiokerrosten alla. Joten huvin vuoksi katsotaanpa miltä pieni "Kubernetes-klusteri" itse asiassa näyttää. (Tämä on paljon helpompaa kuin Kubernetes kova tie.)
Oletan, että sinulla on perustiedot Kubernetesista, Linuxista ja konteista. Kaikki, mistä täällä puhumme, on vain tutkimus-/oppimistarkoituksiin, älä laita sitä tuotantoon!
Arvostelu
Kubernetes sisältää monia komponentteja. Mukaan wikipedia, arkkitehtuuri näyttää tältä:
Tässä näkyy ainakin kahdeksan komponenttia, mutta jätämme useimmat niistä huomiotta. Haluan todeta, että vähimmäisasia, jota voidaan kohtuudella kutsua Kubernetesiksi, koostuu kolmesta pääkomponentista:
kuutio
kube-apiserver (joka riippuu etcd:stä - sen tietokannasta)
kontin suoritusaika (tässä tapauksessa Docker)
Katsotaanpa mitä dokumentaatio sanoo jokaisesta heistä (venäläinen., Englanti.). Ensiksi kuutio:
Agentti, joka toimii klusterin jokaisessa solmussa. Se varmistaa, että säiliöt kulkevat kotelossa.
Kuulostaa riittävän yksinkertaiselta. Entä kontin ajoajat (säilön käyttöaika)?
Säilön suoritusaika on ohjelma, joka on suunniteltu suorittamaan säiliöitä.
Erittäin informatiivinen. Mutta jos olet perehtynyt Dockeriin, sinulla pitäisi olla yleinen käsitys siitä, mitä se tekee. (Säilön suoritusajan ja kubeletin välisten vastuiden jakamisen yksityiskohdat ovat itse asiassa melko hienovaraisia, enkä käsittele niitä tässä.)
И API-palvelin?
API-palvelin on Kubernetes-ohjauspaneelin komponentti, joka paljastaa Kubernetes API:n. API-palvelin on Kubernetes-ohjauspaneelin asiakaspuoli
Jokainen, joka on koskaan tehnyt mitään Kubernetesin kanssa, on joutunut olemaan vuorovaikutuksessa API:n kanssa joko suoraan tai kubectlin kautta. Tämä on Kubernetes Kubernetesin ydin – aivot, jotka muuttavat YAML-vuoret, jotka me kaikki tunnemme ja rakastamme (?) toimivaksi infrastruktuuriksi. Vaikuttaa ilmeiseltä, että API:n pitäisi olla läsnä minimaalisessa kokoonpanossamme.
Edellytykset
Linux-virtuaalinen tai fyysinen kone pääkäyttäjän oikeuksilla (käytän Ubuntu 18.04:ää virtuaalikoneessa).
Ja siinä kaikki!
Tylsä asennus
Meidän on asennettava Docker käytettävälle koneelle. (En aio mennä yksityiskohtiin siitä, kuinka Docker ja kontit toimivat; jos olet kiinnostunut, siellä on upeita artikkeleita). Asennamme sen kanssa apt:
Sen jälkeen meidän on hankittava Kubernetes-binäärit. Itse asiassa tarvitsemme vain "klusterimme" ensimmäistä käynnistämistä varten kubelet, koska voimme käyttää muita palvelinkomponentteja kubelet. Käytämme myös klusterin kanssa vuorovaikutukseen sen käytön jälkeen kubectl.
kubelet täytyy suorittaa root-käyttäjänä. Melko loogista, koska hänen täytyy hallita koko solmua. Katsotaanpa sen parametreja:
$ ./kubelet -h
<слишком много строк, чтобы разместить здесь>
$ ./kubelet -h | wc -l
284
Vau, niin monta vaihtoehtoa! Onneksi niitä tarvitaan vain pari. Tässä on yksi meitä kiinnostavista parametreista:
--pod-manifest-path string
Polku hakemistoon, joka sisältää tiedostot staattisia podeja varten, tai polku tiedostoon, joka kuvaa staattisia podeja. Pisteillä alkavat tiedostot ohitetaan. (POISTETTU: Tämä asetus on asetettava asetustiedostossa, joka välitetään Kubeletille --config-valitsimen kautta. Lisätietoja on kohdassa kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file .)
Tämä vaihtoehto antaa meille mahdollisuuden juosta staattiset pussit — podit, joita ei hallita Kubernetes API:n kautta. Staattisia podeja käytetään harvoin, mutta ne ovat erittäin käteviä klusterin nopeaan nostamiseen, ja juuri tätä tarvitsemme. Ohitamme tämän suuren varoituksen (jälleen, älä käytä tätä tuotannossa!) ja katsomme, saammeko podin toimimaan.
Ensin luodaan hakemisto staattisille podille ja suoritetaan kubelet:
kubelet alkaa kirjoittaa varoituksia ja näyttää siltä, että mitään ei tapahdu. Mutta se ei ole totta! Katsotaanpa Dockeria:
$ 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 Luin pod-luettelon ja annoin Dockerille komennon käynnistää pari konttia määritystemme mukaan. (Jos ihmettelet "tauko"-säiliötä, se on Kubernetes-hakkerointi - katso tämä blogi.) Kubelet käynnistää konttimme busybox määritetyllä komennolla ja käynnistää sen uudelleen rajoituksetta, kunnes staattinen pod poistetaan.
Onnittele itseäsi. Keksimme juuri yhden hämmentävämmistä tavoista lähettää tekstiä terminaaliin!
Käynnistä etcd
Lopullinen tavoitteemme on käyttää Kubernetes API:ta, mutta tehdäksemme sen meidän on ensin suoritettava jne. Aloitetaan minimaalinen etcd-klusteri asettamalla sen asetukset pods-hakemistoon (esim. pods/etcd.yaml):
Jos olet koskaan työskennellyt Kubernetesin kanssa, näiden YAML-tiedostojen pitäisi olla sinulle tuttuja. Tässä on vain kaksi huomionarvoista seikkaa:
Olemme asentaneet isäntäkansion /var/lib/etcd podissa niin, että etcd-tiedot säilyvät uudelleenkäynnistyksen jälkeen (jos näin ei tehdä, klusterin tila poistetaan aina, kun pod käynnistetään uudelleen, mikä ei ole hyvä edes minimaaliselle Kubernetes-asennukselle).
Olemme asentaneet hostNetwork: true. Tämä asetus ei ole yllättävää, että etcd määrittää käyttämään isäntäverkkoa podin sisäisen verkon sijaan (tämä helpottaa API-palvelimen löytämistä etcd-klusterin).
Yksinkertainen tarkistus osoittaa, että etcd todellakin toimii localhostissa ja tallentaa tietoja levylle:
$ 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
API-palvelimen käynnistäminen
Kubernetes API -palvelimen käyttäminen on vieläkin helpompaa. Ainoa parametri, joka on välitettävä, on --etcd-servers, tekee mitä odotat:
Aseta tämä YAML-tiedosto hakemistoon pods, ja API-palvelin käynnistyy. Tarkistetaan kanssa curl osoittaa, että Kubernetes API kuuntelee porttia 8080 täysin avoimella pääsyllä - todennusta ei vaadita!
(Jälleen, älä käytä tätä tuotannossa! Olin hieman yllättynyt, että oletusasetus on niin epävarma. Mutta veikkaan, että tämä helpottaa kehitystä ja testausta.)
Ja iloinen yllätys, kubectl toimii heti ilman lisäasetuksia!
$ ./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.
ongelma
Mutta jos kaivaa hieman syvemmälle, jokin näyttää olevan vialla:
$ ./kubectl get pod -n kube-system
No resources found in kube-system namespace.
Luomamme staattiset kotelot ovat poissa! Itse asiassa kubelet-solmuamme ei löydetä ollenkaan:
$ ./kubectl get nodes
No resources found in default namespace.
Mikä hätänä? Jos muistat muutaman kappaleen sitten, aloitimme kubeletin erittäin yksinkertaisella komentoriviparametrijoukolla, joten kubelet ei osaa ottaa yhteyttä API-palvelimeen ja ilmoittaa sille tilastaan. Tutkittuamme dokumentaatiota löydämme vastaavan lipun:
--kubeconfig string
Tiedoston polku kubeconfig, joka määrittää kuinka muodostaa yhteys API-palvelimeen. Saatavuus --kubeconfig ottaa käyttöön API-palvelintilan, ei --kubeconfig ottaa käyttöön offline-tilan.
Koko tämän ajan, tietämättämme, käytimme kubeletia "offline-tilassa". (Jos olisimme pedanttisia, voisimme ajatella erillisen kubeletin "vähintään elinkelpoisena Kubernetesina", mutta se olisi erittäin tylsää). Jotta "oikea" määritys toimisi, meidän on välitettävä kubeconfig-tiedosto kubeletille, jotta se osaa puhua API-palvelimen kanssa. Onneksi se on melko yksinkertainen (koska meillä ei ole todennus- tai varmenneongelmia):
(Muuten, jos yrität päästä API:aan curlin kautta, kun kubelet ei ole käynnissä, huomaat, että se on edelleen käynnissä! Kubelet ei ole podistiensa "vanhempi" kuten Docker, se on enemmän kuin "hallinta" daemon." Kubeletin hallitsemat säilöt jatkavat toimintaansa, kunnes kubelet pysäyttää ne.)
Muutamassa minuutissa kubectl pitäisi näyttää meille podit ja solmut odotetulla tavalla:
$ ./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
Onnittelemme todella itseämme tällä kertaa (tiedän, että onnittelin jo itseämme) - meillä on minimaalinen Kubernetes "klusteri" täysin toimivalla API:lla!
Aloitamme alta
Katsotaan nyt, mihin API pystyy. Aloitetaan nginx podista:
$ ./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.
Tässä näemme, kuinka surkeasti epätäydellinen Kubernetes-ympäristömme on - meillä ei ole palveluita. Yritetään uudelleen luomalla palvelutili manuaalisesti ja katsotaan mitä tapahtuu:
$ 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
Vaikka loimme palvelutilin manuaalisesti, todennustunnusta ei luoda. Jatkaessamme minimalistisen "klusterin" kokeilua huomaamme, että suurin osa hyödyllisistä asioista, jotka yleensä tapahtuvat automaattisesti, puuttuvat. Kubernetes API-palvelin on melko minimalistinen, ja suurin osa raskaiden nostoista ja automaattisista määrityksistä tapahtuu erilaisissa ohjaimissa ja taustatöissä, jotka eivät vielä ole käynnissä.
Voimme kiertää tämän ongelman asettamalla vaihtoehdon automountServiceAccountToken palvelutilille (koska meidän ei tarvitse käyttää sitä joka tapauksessa):
$ 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
Vihdoinkin koukku on ilmestynyt! Mutta itse asiassa se ei ala, koska meillä ei ole suunnittelija (scheduler) on toinen tärkeä Kubernetesin osa. Näemme jälleen, että Kubernetes API on yllättävän "tyhmä" - kun luot sovellusliittymään Podin, se rekisteröi sen, mutta ei yritä selvittää, missä solmussa se ajaa.
Itse asiassa et tarvitse ajastinta podin suorittamiseen. Voit lisätä parametrin luetteloon solmun manuaalisesti nodeName:
(Korvata mink8s solmun nimeen.) Poistamisen ja käytön jälkeen näemme, että nginx on käynnistynyt ja kuuntelee sisäistä IP-osoitetta:
$ ./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>
Varmistaaksemme, että podien välinen verkko toimii oikein, voimme suorittaa curlin toisesta podista:
$ 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>
On mielenkiintoista kaivaa tähän ympäristöön ja nähdä, mikä toimii ja mikä ei. Huomasin, että ConfigMap ja Secret toimivat odotetusti, mutta Service ja Deployment eivät.
Selvä!
Tästä viestistä tulee pitkä, joten julistan voiton ja sanon, että tämä on käyttökelpoinen kokoonpano, jota voidaan kutsua "Kubernetesiksi". Yhteenvetona: neljä binaaritiedostoa, viisi komentoriviparametria ja "vain" 45 riviä YAML:a (ei niin paljon standardien mukaan Kubernetes) ja meillä on melko vähän toimivia asioita:
Podit pysyvät elossa ja käynnistyvät automaattisesti uudelleen
Verkostoituminen podien välillä saman solmun sisällä toimii melko hyvin
ConfigMap, Secret ja yksinkertainen tallennustilan asennus toimivat odotetusti
Mutta paljon siitä, mikä tekee Kubernetesista todella hyödyllisen, puuttuu edelleen, kuten:
Pod-aikataulu
Todennus/valtuutus
Useita solmuja
Palveluverkosto
Klusteroitu sisäinen DNS
Ohjaimet palvelutileille, käyttöönottoille, integraatiolle pilvipalveluntarjoajien kanssa ja useimpiin muihin Kubernetesin tuomiin herkkuihin
Mitä me siis oikeastaan saimme? Kubernetes API, joka toimii yksinään, on oikeastaan vain alusta konttiautomaatio. Se ei tee paljoa – se on työskentely useille APIa käyttäville ohjaajille ja operaattoreille – mutta se tarjoaa johdonmukaisen ympäristön automaatiota varten.