Kompletní Kubernetes od nuly na Raspberry Pi

Kompletní Kubernetes od nuly na Raspberry Pi

Nedávno oznámila jedna známá společnost, že přechází na ARM architekturu pro svou řadu notebooků. Když jsem slyšel tuto zprávu, vzpomněl jsem si, že když jsem znovu procházel ceny EC2 v AWS, všiml jsem si Gravitonů s velmi lákavou cenou. Podvod byl samozřejmě v tom, že jde o ARM. Tehdy mě ani nenapadlo, že ARM je velmi vážná technologie...

Pro mě byla tato architektura vždy spojena s mobilními zařízeními a dalšími IoT gadgety. "Opravdové" servery na ARM - to je nějaké zvláštní, v něčem dokonce šílené... Nicméně se mi v hlavě usadila nová myšlenka, a tak jsem se rozhodl o víkendu zjistit, co lze dnes na ARM spustit. A začal jsem s něčím blízkým a známým - clusterem Kubernetes. A nejen tak nějakým "clusterem", ale pořádně, aby byl maximálně podobný tomu, co jsem zvyklý vidět v produkci.

Podle mých plánů by měl být cluster přístupný z internetu, měl by hostit nějakou webovou aplikaci a také by měl mít alespoň monitoring. Pro realizaci této myšlenky budu potřebovat pár (nebo více) Raspberry Pi minimálně model 3B+. Experimentální platformou by mohl být AWS, ale zajímaly mě právě "maliny" (které stejně ležely bez používání). Takže na nich rozběhneme cluster Kubernetes s Ingress, Prometheem a Grafanou.

Příprava "malin"

Instalace OS a SSH

S výběrem OS pro instalaci jsem se nijak nemazal: vzal jsem jednoduše nejnovější Raspberry Pi OS Lite z oficiálního webu. Tam je k dispozici dokumentace k instalaci, všechny kroky je třeba provést na všech uzlech budoucího clusteru. Dále je třeba provést následující úpravy (také na všech uzlech).

Po připojení monitoru a klávesnice je nutné předem nastavit síť a SSH:

  1. Pro správnou funkci clusteru musí mít master statickou IP adresu, pracovní uzly mohou mít adresy podle uvážení. Preferoval jsem statické adresy všude z důvodu usnadnění nastavení.
  2. Statickou adresu lze nakonfigurovat v OS (ve souboru /etc/dhcpcd.conf je vhodný příklad) nebo fixací lease v DHCP serveru použitého (v mém případě domácího) routeru.
  3. ssh-server se jednoduše aktivuje v raspi-config (interfacing options → ssh).

Po tomto kroku se můžete přihlásit přes SSH (uživatelské jméno je defaultně pi, a heslo je raspberry nebo to, které jste změnili) a pokračovat v nastavení.

Další nastavení

  1. Nastavíme název hostitele. V mém příkladu budeme používat pi-control a pi-worker.
  2. Zkontrolujeme, zda je souborový systém rozšířen na celý disk (df -h /). V případě potřeby jej lze rozšířit pomocí raspi-config.
  3. Změníme výchozí heslo uživatele v raspi-config.
  4. Vypneme swap soubor (to je požadavek Kubernetes; pokud vás zajímají podrobnosti k tomuto tématu, issue #53533):
    dphys-swapfile swapoff
    systemctl disable dphys-swapfile
  5. Aktualizujeme balíčky na poslední verze:
    apt-get update && apt-get dist-upgrade -y
  6. Nainstalujeme Docker a další balíčky:
    apt-get install -y docker docker.io apt-transport-https curl bridge-utils iptables-persistent

    Při instalaci iptables-persistent bude třeba uložit nastavení iptables pro ipv4, a v souboru /etc/iptables/rules.v4 — přidat pravidla do řetězce FORWARD, takto:

    # Generated by xtables-save v1.8.2 on Sun Jul 19 00:27:43 2020
    *filter
    :INPUT ACCEPT [0:0]
    :FORWARD ACCEPT [0:0]
    :OUTPUT ACCEPT [0:0]
    -A FORWARD -s 10.1.0.0/16  -j ACCEPT
    -A FORWARD -d 10.1.0.0/16  -j ACCEPT
    COMMIT
  7. Teď už zbývá jen restartovat.

Nyní je vše připraveno k instalaci clustera Kubernetes.

Instalace Kubernetes

V této fázi jsem úmyslně odložil všechny svoje i naše firemní práce na automatizaci instalace a konfigurace clustera K8s. Místo toho využijeme oficiální dokumentaci z kubernetes.io (lehce doplněné komentáři a zjednodušeními).

Přidáme repozitář Kubernetes:

curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
sudo apt-get update

Dále dokumentace doporučuje nainstalovat CRI (container runtime interface). Protože je Docker již nainstalován, pokračujeme dál a nainstalujeme základní komponenty:

sudo apt-get install -y kubelet kubeadm kubectl kubernetes-cni

Při instalaci základních komponent jsem hned přidal kubernetes-cni, který je potřebný k fungování clustera. A tady je důležitý moment: balíček kubernetes-cni z nějakého důvodu nevytváří výchozí adresář pro nastavení CNI rozhraní, takže jsem ho musel vytvořit ručně:

mkdir -p /etc/cni/net.d

Pro fungování network backendu, o kterém se zmíním níže, je nutné doinstalovat plugin pro CNI. Vybral jsem známý a srozumitelný plugin portmap (úplný seznam najdete v dokumentaci):

curl -sL https://github.com/containernetworking/plugins/releases/download/v0.7.5/cni-plugins-arm-v0.7.5.tgz | tar zxvf - -C /opt/cni/bin/ ./portmap

Nastavení Kubernetes

Uzlový stroj s control plane

Instalace samotného clustera se provádí poměrně jednoduše. Pro urychlení tohoto procesu a ověření, že jsou obrázky Kubernetes dostupné, lze předem provést:

kubeadm config images pull

Nyní provedeme samotnou instalaci — inicializujeme control plane clustera:

kubeadm init --pod-network-cidr=10.1.0.0/16 --service-cidr=10.2.0.0/16 --upload-certs

Všimněte si, že podsítě pro služby a pody by se neměly překrývat mezi sebou a s existujícími sítěmi.

Na konci nám zobrazí zprávu, že vše je v pořádku, a zároveň nám poradí, jak připojit pracovní uzly k řídicímu plánu:

Vaše ovládací plán Kubernetes byl úspěšně inicializován! Chcete-li začít používat svůj cluster, musíte provést následující jako běžný uživatel: mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config Nyní byste měli nasadit síť podů do clusteru. Spusťte "kubectl apply -f [podnetwork].yaml" s jednou z možností uvedených na: https://kubernetes.io/docs/concepts/cluster-administration/addons/ Nyní se můžete připojit k jakémukoli počtu uzlů řídicího plánu spuštěním následujícího příkazu jako root: kubeadm join 192.168.88.30:6443 --token a485vl.xjgvzzr2g0xbtbs4 --discovery-token-ca-cert-hash sha256:9da6b05aaa5364a9ec59adcc67b3988b9c1b94c15e81300560220acb1779b050 --contrl-plane --certificate-key 72a3c0a14c627d6d7fdade1f4c8d7a41b0fac31b1faf0d8fdf9678d74d7d2403 Vezměte prosím na vědomí, že certificate-key poskytuje přístup k citlivým údajům clusteru, uchovávejte jej v tajnosti! Jako ochranu budou uploaded-certs smazány za dvě hodiny; Pokud je potřeba, můžete použít "kubeadm init phase upload-certs --upload-certs" pro načtení certifikátů poté. Pak se můžete připojit k jakémukoli počtu pracovních uzlů spuštěním následujícího příkazu na každém jako root: kubeadm join 192.168.88.30:6443 --token a485vl.xjgvzzr2g0xbtbs4 --discovery-token-ca-cert-hash sha256:9da6b05aaa5364a9ec59adcc67b3988b9c1b94c15e81300560220acb1779b050

Vykonáme doporučení pro přidání konfigurace pro uživatele. A doporučuji hned přidat také automatické dokončování pro kubectl:

 kubectl completion bash > $HOME/.kube/completion.bash.inc printf "
 # Kubectl shell completion
 source '$HOME/.kube/completion.bash.inc'
 " >> $HOME/.bash_profile source $HOME/.bash_profile

V této fázi již můžeme vidět první uzel v clusteru (i když ještě není připraven):

root@pi-control:~# kubectl get no NAME STATUS ROLES AGE VERSION pi-control NotReady master 29s v1.18.6

Konfigurace sítě

Dále, jak bylo řečeno ve zprávě po instalaci, bude potřeba nastavit síť v clusteru. V dokumentaci se nabízí výběr z Calico, Cilium, contiv-vpp, Kube-router a Weave Net… Zde jsem odstoupil od oficiální instrukce a zvolil známější a srozumitelnější variantu: flannel v režimu host-gw (více o dostupných backendů viz dokumentaci projektu).

Nainstalovat ho do clusteru je docela jednoduché. Nejprve stáhneme manifesty:

wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

Pak změníme v nastavení typ na vxlan na host-gw:

sed -i 's/vxlan/host-gw/' kube-flannel.yml

… a podsíť podů — ze základní hodnoty na tu, která byla uvedena při inicializaci clusteru:

sed -i 's#10.244.0.0/16#10.1.0.0/16#' kube-flannel.yml

Po tomto vytvoříme zdroje:

kubectl create -f kube-flannel.yml

Hotovo! Po nějaké době první uzel K8s přejde do stavu Ready:

NAME         STATUS   ROLES    AGE   VERSION
pi-control   Ready    master   2m    v1.18.6

Přidání pracovního uzlu

Nyní můžete přidat worker. K tomu je třeba na něm — po instalaci samotného Kubernetes podle scénáře popsaného výše — jednoduše provést předtím získaný příkaz:

kubeadm join 192.168.88.30:6443 --token a485vl.xjgvzzr2g0xbtbs4 
    --discovery-token-ca-cert-hash sha256:9da6b05aaa5364a9ec59adcc67b3988b9c1b94c15e81300560220acb1779b050

Na tomto místě lze považovat klastr za připravený:

root@pi-control:~# kubectl get no
NAME         STATUS   ROLES    AGE    VERSION
pi-control   Ready    master   28m    v1.18.6
pi-worker    Ready       2m8s   v1.18.6

Měl jsem po ruce pouze dvě Raspberry Pi, takže se mi nechtělo jednu z nich pouze pouštět pod control plane. Proto jsem odstranil automaticky nastavený taint z uzlu pi-control pomocí:

root@pi-control:~# kubectl edit node pi-control

… a odstranil řádky:

 - effect: NoSchedule
   key: node-role.kubernetes.io/master

Naplnění klastru nezbytným minimem

V první řadě budeme potřebovat Helm. Samozřejmě, můžete to dělat i bez něj, ale Helm umožňuje doslova bez úprav souborů nastavit některé komponenty podle vlastního uvážení. A ve skutečnosti je to prostě spustitelný soubor, který "nevyžaduje nic".

Takže přejdeme na helm.sh do sekce docs/installation a provedeme tam příkaz:

curl -s https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash

Poté přidáme repozitář chartů:

helm repo add stable https://kubernetes-charts.storage.googleapis.com/

Nyní nainstalujeme infrastrukturní komponenty podle plánu:

  • Ingress controller;
  • Prometheus;
  • Grafana;
  • cert-manager.

Ingress controller

První komponenta — Ingress controller — se instalujeme docela snadno a je připravena k použití "z krabice". K tomu stačí přejít na sekci bare-metal na webu a provést instalační příkaz odtud:

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.34.1/deploy/static/provider/baremetal/deploy.yaml

Během této chvíle začala "malina" zatěžovat diskový IOPS. Je to tím, že společně s Ingress kontrolérem se instaluje velké množství zdrojů, provádí se hodně požadavků na API a tím pádem se zapisuje mnoho dat do etcd. V zásadě, buď paměťová karta třídy 10 nemá dostatečný výkon, nebo SD karty pro tuto zátěž jednoduše nestačí. Nicméně, po 5 minutách se všechno spustilo.

Byl vytvořen namespace a v něm se objevil kontrolér a vše potřebné:

root@pi-control:~# kubectl -n ingress-nginx get pod
NAME                                        READY   STATUS      RESTARTS   AGE
ingress-nginx-admission-create-2hwdx        0/1     Completed   0          31s
ingress-nginx-admission-patch-cp55c         0/1     Completed   0          31s
ingress-nginx-controller-7fd7d8df56-68qp5   1/1     Running     0          48s

Prometheus

Následující dva komponenty se snadno instalují přes Helm z chart repo.

Najdeme Prometheus, vytvoříme namespace a nainstalujeme do něj:

helm search repo stable | grep prometheus
kubectl create ns monitoring
helm install prometheus --namespace monitoring stable/prometheus --set server.ingress.enabled=True --set server.ingress.hosts={"prometheus.home.pi"}

Ve výchozím nastavení Prometheus objednává 2 disky: pro data samotného Promethea a pro data AlertManageru. Vzhledem k tomu, že v clusteru nebyl vytvořen storage class, disky se neobjednají a pod'y se nespustí. Pro bare metal instalace Kubernetes obvykle používáme Ceph rbd, ale v případě Raspberry Pi to je zbytečné.

Proto vytvoříme jednoduché local storage na hostpath. Manifesty PV (persistent volume) pro prometheus-server a prometheus-alertmanager jsou spojeny v souboru prometheus-pv.yaml v Git-repozitáře s příklady pro článek. Direktorium pro PV je třeba předem vytvořit na disku toho uzlu, ke kterému chceme připojit Prometheus: v příkladu je uvedeno nodeAffinity podle hostname pi-worker a na něm jsou vytvořeny adresáře /data/localstorage/prometheus-server a /data/localstorage/prometheus-alertmanager.

Stáhneme (klonujeme) manifest a přidáváme do Kubernetes:

kubectl create -f prometheus-pv.yaml

V této fázi jsem se poprvé setkal s problémem architektury ARM. Kube-state-metrics, který je ve výchozím nastavení instalován v chartu Prometheus, se odmítl spustit. Vydával chybu:

root@pi-control:~# kubectl -n monitoring logs prometheus-kube-state-metrics-c65b87574-l66d8
standard_init_linux.go:207: exec user process caused "exec format error"

Jde o to, že pro kube-state-metrics se používá obraz projektu CoreOS, který není sestaven pro ARM:

kubectl -n monitoring get deployments.apps prometheus-kube-state-metrics -o=jsonpath={.spec.template.spec.containers[].image}
quay.io/coreos/kube-state-metrics:v1.9.7

Musel jsem si trochu vygooglit a najít například tento obraz. Abychom jej mohli použít, aktualizujeme release a určíme, jaký obraz použít pro kube-state-metrics:

helm upgrade prometheus --namespace monitoring stable/prometheus --set server.ingress.enabled=True --set server.ingress.hosts={"prometheus.home.pi"} --set kube-state-metrics.image.repository=carlosedp/kube-state-metrics --set kube-state-metrics.image.tag=v1.9.6

Kontrolujeme, že všechno běží:

root@pi-control:~# kubectl -n monitoring get po
NAME                                             READY   STATUS              RESTARTS   AGE
prometheus-alertmanager-df65d99d4-6d27g          2/2     Running             0          5m56s
prometheus-kube-state-metrics-5dc5fd89c6-ztmqr   1/1     Running             0          5m56s
prometheus-node-exporter-49zll                   1/1     Running             0          5m51s
prometheus-node-exporter-vwl44                   1/1     Running             0          4m20s
prometheus-pushgateway-c547cfc87-k28qx           1/1     Running             0          5m56s
prometheus-server-85666fd794-z9qnc               2/2     Running             0          4m52s

Grafana a cert-manager

Pro grafy a dashboardy instalujeme Grafana:

helm install grafana --namespace monitoring stable/grafana  --set ingress.enabled=true --set ingress.hosts={"grafana.home.pi"}

Na konci výstupu nám ukážou, jak získat heslo pro přístup:

kubectl get secret --namespace monitoring grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo

Pro objednání certifikátů nainstalujeme cert-manager. Pro jeho instalaci se obrátíme na dokumentaci, která nabízí odpovídající příkazy pro Helm:

helm repo add jetstack https://charts.jetstack.io

helm install 
  cert-manager jetstack/cert-manager 
  --namespace cert-manager 
  --version v0.16.0 
  --set installCRDs=true

Pro self-signed certifikáty pro domácí použití je to zcela dostačující. Pokud však chcete získat stejný Let’s Encrypt, pak je potřeba nastavit také cluster issuer. Podrobnosti o tom naleznete v našem článku „SSL certifikáty od Let’s Encrypt s cert-manager v Kubernetes».

Já jsem se zastavil u varianty z příkladu v dokumentaci, rozhodl jsem se, že staging varianta LE bude dostatečná. Změňte v příkladu e-mail, uložit do souboru a přidat do clusteru (cert-manager-cluster-issuer.yaml):

kubectl create -f cert-manager-cluster-issuer.yaml

Nyní můžeme objednat certifikát, například pro Grafanu. K tomu budeme potřebovat doménu a přístup do clusteru zvenčí. Doménu mám, a provoz jsem nastavil přesměrováním portů 80 a 443 na domácím routeru podle vytvořeného service ingress-controller’a:

kubectl -n ingress-nginx get svc
NAME                                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller             NodePort    10.2.206.61            80:31303/TCP,443:30498/TCP   23d

80. port je v tomto případě přenášen na 31303, a 443 na 30498. (Porty se generují náhodně, takže u vás budou jiné.)

Zde je příklad certifikátu (cert-manager-grafana-certificate.yaml):

apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: grafana
  namespace: monitoring
spec:
  dnsNames:
    - grafana.home.pi
  secretName: grafana-tls
  issuerRef:
    kind: ClusterIssuer
    name: letsencrypt-staging

Přidáme ho do clusteru:

kubectl create -f cert-manager-grafana-certificate.yaml

Po této akci se objeví zdroj Ingress, přes který proběhne validace Let’s Encrypt:

root@pi-control:~# kubectl -n monitoring get ing
NAME                        CLASS    HOSTS                        ADDRESS         PORTS   AGE
cm-acme-http-solver-rkf8l      grafana.home.pi      192.168.88.31   80      72s
grafana                        grafana.home.pi      192.168.88.31   80      6d17h
prometheus-server              prometheus.home.pi   192.168.88.31   80      8d

Jakmile proběhne validace, uvidíme, že zdroj certificate je připraven, a v výše uvedeném secretu grafana-tls je certifikát a klíč. Můžete okamžitě zkontrolovat, kdo certifikát vydal:

root@pi-control:~# kubectl -n monitoring get certificate
NAME      READY   SECRET        AGE
grafana   True    grafana-tls   13m

root@pi-control:~# kubectl -n monitoring get secrets grafana-tls -ojsonpath="{.data['tls.crt']}" | base64 -d | openssl x509 -issuer -noout
issuer=CN = Fake LE Intermediate X1

Vraťme se k Grafaně. Budeme potřebovat trochu upravit její Helm release, změnit nastavení pro TLS podle vytvořeného certifikátu.

K tomu si stáhneme chart, upravíme ho a aktualizujeme z místního adresáře:

helm pull --untar stable/grafana

Upravujeme v souboru grafana/values.yaml parametry TLS:

  tls:
    - secretName: grafana-tls
      hosts:
        - grafana.home.pi

Zde můžeme hned nastavit nainstalovaný Prometheus jako datasource:

datasources:
  datasources.yaml:
    apiVersion: 1
    datasources:
    - name: Prometheus
      type: prometheus
      url: http://prometheus-server:80
      access: proxy
      isDefault: true

Nyní z místního adresáře aktualizujeme chart Grafany:

helm upgrade grafana --namespace monitoring ./grafana --set ingress.enabled=true --set ingress.hosts={"grafana.home.pi"}

Kontrolujeme, že Ingress grafana přidává port 443 a je dostupný přes HTTPS:

root@pi-control:~# kubectl -n monitoring get ing grafana
NAME CLASS HOSTS ADDRESS PORTS AGE
grafana  grafana.home.pi 192.168.88.31 80, 443 63m

root@pi-control:~# curl -kI https://grafana.home.pi
HTTP/2 302
server: nginx/1.19.1
date: Tue, 28 Jul 2020 19:01:31 GMT
content-type: text/html; charset=utf-8
cache-control: no-cache
expires: -1
location: /login
pragma: no-cache
set-cookie: redirect_to=; Path=/; HttpOnly; SameSite=Lax
x-frame-options: deny
strict-transport-security: max-age=15724800; includeSubDomains

Pro demonstraci Grafany v akci si můžete stáhnout a přidat dashboard pro kube-state-metrics. Takhle to vypadá:

Kompletní Kubernetes od nuly na Raspberry Pi

Doporučuji také přidat dashboard pro node exporter: detailně ukáže, co se děje s "malinami" (zátěž CPU, využití paměti, sítě, disku atd.).

Po tomto považuji, že klastr je připraven přijímat a spouštět aplikace!

Poznámka o sestavení

Pro sestavení aplikací pro architekturu ARM existují minimálně dvě možnosti. Za prvé, můžete stavět na ARM zařízení. Nicméně, podíval jsem se na aktuální využití dvou Raspberry Pi a uvědomil jsem si, že ani to by nevydržely. Proto jsem si objednal novou Raspberry Pi 4 (je výkonnější a má dokonce 4 GB paměti) — plánuju na ní sestavovat.

Druhá varianta — sestavení multiarchitektonického obrazu Docker na výkonnějším stroji. K tomu je rozšíření docker buildx. Pokud je aplikace napsána v kompilovaném jazyce, bude potřeba kros-kompilace pro ARM. Nepopisuji všechny nastavení pro tuto cestu, protože by to vyžadovalo samostatný článek. Při realizaci takového přístupu lze dosáhnout "univerzálních" obrazů: Docker spuštěný na ARM stroji si automaticky stáhne odpovídající obraz pro architekturu.

Závěr

Prováděný experiment předčil všechna má očekávání: [alespoň] „vanilkový“ Kubernetes s potřebným základem si na ARM vedl docela dobře, a při jeho konfiguraci vzniklo pouze pár nuancí.

Sami Raspberry Pi 3B+ zvládají zátěž na CPU, avšak jejich SD karty jsou jasným úzkým hrdlem. Kolegové mi naznačili, že v některých verzích je možnost bootovat z USB, kam lze připojit SSD: pak se situace pravděpodobně zlepší.

Tady je příklad zatížení CPU při instalaci Grafana:

Kompletní Kubernetes od nuly na Raspberry Pi

Pro experimenty a "zkušební" provoz, podle mě, Kubernetes cluster na „malinách“ daleko lépe přenáší pocity z používání, než ten samý Minikube, protože všechny komponenty clusteru se instalují a fungují "jako v reálu".

Do budoucna mám v úmyslu přidat do clusteru celý cyklus CI/CD, realizovaný zcela na Raspberry Pi. A budu rád, pokud se někdo podělí o své zkušenosti s nastavením K8s na AWS Graviton.

P.S. Ano, "produkce" může být blíže, než jsem si myslel:

Kompletní Kubernetes od nuly na Raspberry Pi

P.P.S.

Přečtěte si také náš blog:

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster