Fuldstændig Kubernetes fra bunden på Raspberry Pi

Fuldstændig Kubernetes fra bunden på Raspberry Pi

For nylig meddelte et kendt selskab, at de vil overføre deres serie af bærbare computere til ARM-arkitekturen. Da jeg hørte denne nyhed, kom jeg i tanker om, at jeg under endnu en gennemgang af priserne på EC2 i AWS lagde mærke til Gravitons med en meget attraktiv pris. Problemet var selvfølgelig, at det er ARM. Dengang havde jeg ikke engang overvejet, at ARM er noget alvorligt...

For mig har denne arkitektur altid været forbeholdt mobile enheder og andre IoT-sager. "Rigtige" servere på ARM — det virker lidt usædvanligt, nærmest vildt... Men en ny tanke fik fat i mig, så en weekend besluttede jeg mig for at undersøge, hvad man overhovedet kan køre på ARM i dag. Og derfor besluttede jeg mig for at starte med noget, jeg kender godt — et Kubernetes-kluster. Desuden ikke bare et hvilket som helst "kluster", men helt professionelt, så det skulle være så tæt på, som jeg er vant til at se det i produktionen.

Jeg forestillede mig, at klustret skulle være tilgængeligt fra internettet, der skulle køre en webapplikation, og der skulle være mindst overvågning. For at realisere denne idé vil jeg have brug for et par (eller flere) Raspberry Pi, ikke lavere end model 3B+. Et muligt eksperimenteringssted kunne også være AWS, men jeg var specifikt interesseret i "hindbærne" (som alligevel bare stod stille). Så vi vil opsætte et Kubernetes-kluster på dem med Ingress, Prometheus og Grafana.

Forberedelse af "hindbærne"

Installation af OS og SSH

Jeg har ikke gjort mig for mange anstrengelser med valget af OS til installationen: jeg tog bare den nyeste Raspberry Pi OS Lite fra den officielle hjemmeside. Her er der også tilgængelig dokumentation til installationen, som skal følges på alle noder i det fremtidige kluster. Derefter skal følgende handlinger udføres (også på alle noder).

Når jeg har tilsluttet skærm og tastatur, er det nødvendigt at konfigurere netværket og SSH på forhånd:

  1. For at klustret kan fungere, skal der være en statisk IP-adresse på masteren, mens det er valgfrit på de arbejdstagende noder. Jeg foretrak statiske adresser overalt af hensyn til nemmere konfiguration.
  2. Den statiske adresse kan konfigureres i OS (i filen /etc/dhcpcd.conf er der et passende eksempel) eller ved at fastlåse lease i DHCP-serveren på den anvendte (i mit tilfælde — hjemme) router.
  3. ssh-serveren aktiveres blot i raspi-config (interfacing options → ssh).

Efter dette kan du logge ind via SSH (standard login — pi, og adgangskoden — raspberry eller den, du har ændret til) og fortsætte med konfigurationen.

Andre indstillinger

  1. Lad os indstille værtsnavnet. I mit eksempel bruges pi-control og pi-worker.
  2. Lad os tjekke, at filsystemet er udvidet til hele disken (df -h /). Om nødvendigt kan det udvides ved hjælp af raspi-config.
  3. Vi ændrer standardbrugerens adgangskode i raspi-config.
  4. Vi deaktiverer swap-filen (det er et krav fra Kubernetes; hvis du er interesseret i detaljer om dette, se issue #53533):
    dphys-swapfile swapoff
    systemctl disable dphys-swapfile
  5. Vi opdaterer pakkerne til de nyeste versioner:
    apt-get update && apt-get dist-upgrade -y
  6. Vi installerer Docker og ekstra pakker:
    apt-get install -y docker docker.io apt-transport-https curl bridge-utils iptables-persistent

    Ved installationen iptables-persistent skal bruges til at gemme iptables-indstillingerne for ipv4, og i filen /etc/iptables/rules.v4 — tilføje regler til kæden FORWARD, sådan her:

    # 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. Det er kun tilbage at genstarte.

Nu er alt klar til installation af Kubernetes-klusteret.

Installation af Kubernetes

På dette stadium har jeg bevidst udsat alle mine og vores virksomheders erfaringer med automatisering af installationen og konfigurationen af K8s-klusteret. I stedet vil vi bruge den officielle dokumentation fra kubernetes.io (let suppleret med kommentarer og forkortelser).

Lad os tilføje Kubernetes-repositoriet:

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

Dernæst foreslår dokumentationen at installere CRI (container runtime interface). Da Docker allerede er installeret, går vi videre og installer støttekomponenterne:

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

Ved installationen af støttekomponenterne tilføjede jeg straks kubernetes-cni, som er nødvendig for at klusteret kan fungere. Her er et vigtigt punkt: pakken kubernetes-cni af en eller anden grund ikke opretter standardmappen til CNI-interfaceindstillinger, så jeg måtte oprette den manuelt:

mkdir -p /etc/cni/net.d

For at netværks-backenden, som jeg vil tale om nedenfor, skal fungere, er det nødvendigt at installere en plugin til CNI. Jeg valgte den velkendte plugin portmap (se hele listen i dokumentationen):

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

Kubernetes konfiguration

Noden med kontrolplanen

Installation af selve klusteret er ret enkel. For at fremskynde denne proces og kontrollere, at Kubernetes-billederne er tilgængelige, kan vi først udføre:

kubeadm config images pull

Nu gennemfører vi selve installationen — vi initialiserer kontrolplanen for klusteret:

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

Bemærk, at subnetværk til tjenester og pods ikke må overlappe hinanden eller eksisterende netværk.

Til sidst viser systemet en besked om, at alt er i orden, og giver samtidig viden om, hvordan man tilslutter arbejdsnoder til kontrolplanet:

Dit Kubernetes kontrolplan er blevet initialiseret med succes! For at begynde at bruge dit cluster skal du køre følgende som almindelig bruger: mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config Du skal nu implementere et pod-netværk til clustret. Kør "kubectl apply -f [podnetwork].yaml" med et af de valgmuligheder, der er nævnt på: https://kubernetes.io/docs/concepts/cluster-administration/addons/ Du kan nu tilslutte et vilkårligt antal kontrolplanenoder ved at køre følgende kommando på hver som root: kubeadm join 192.168.88.30:6443 --token a485vl.xjgvzzr2g0xbtbs4 --discovery-token-ca-cert-hash sha256:9da6b05aaa5364a9ec59adcc67b3988b9c1b94c15e81300560220acb1779b050 --contrl-plane --certificate-key 72a3c0a14c627d6d7fdade1f4c8d7a41b0fac31b1faf0d8fdf9678d74d7d2403. Bemærk venligst, at certifikatnøglen giver adgang til følsomme data i clustret, skal opbevares hemmeligt! Som et sikkerhedstiltag vil uploadede certifikater blive slettet om to timer; hvis nødvendigt kan du bruge "kubeadm init phase upload-certs --upload-certs" for at genindlæse certifikaterne bagefter. Derefter kan du tilslutte et vilkårligt antal arbejdsnoder ved at køre følgende på hver som root: kubeadm join 192.168.88.30:6443 --token a485vl.xjgvzzr2g0xbtbs4 --discovery-token-ca-cert-hash sha256:9da6b05aaa5364a9ec59adcc67b3988b9c1b94c15e81300560220acb1779b050

Lad os følge anbefalingerne til at tilføje konfigurationen for brugeren. Og jeg anbefaler også at tilføje autoudfyldelse for 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

På dette tidspunkt kan man allerede se den første node i clustret (dog er den endnu ikke klar):

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

Netværkskonfiguration

Derefter, som nævnt i beskeden efter installationen, skal der installeres netværk i clustret. Dokumentationen tilbyder valg mellem Calico, Cilium, contiv-vpp, Kube-router og Weave Net… Her har jeg valgt et mere kendt og forståeligt alternativ: flannel i mode host-gw (læs mere om de tilgængelige backends i projektets dokumentation).

At installere det i clustret er ganske enkelt. Først downloader vi manifesterne:

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

Derefter ændrer vi indstillingerne fra type vxlan og få en fungerende kommando. host-gw:

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

… og podnetsubnettet — fra standardværdien til den, der blev angivet ved initialiseringen af clustret:

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

Efter dette opretter vi ressourcerne:

kubectl create -f kube-flannel.yml

Færdig! Efter lidt tid vil den første K8s-node skifte til status Klar:

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

Tilføjelse af en arbejdsnode

Nu kan du tilføje en worker. For at gøre dette skal du — efter installationen af Kubernetes i henhold til det ovenfor beskrevne scenarie — blot udføre den tidligere modtagne kommando:

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

På dette punkt kan vi betragte klyngen som klar:

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

Jeg havde kun to Raspberry Pi ved hånden, så jeg ville ikke give en af dem kun til kontrolplanet. Derfor fjernede jeg den automatisk tildelte taint fra noden pi-control ved at køre:

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

… og fjernede linjerne:

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

Udfyldning af klyngen med det nødvendige minimum

I første omgang har vi brug for Helm. Selvfølgelig kan man gøre alt uden det, men Helm gør det muligt at konfigurere nogle komponenter uden at redigere filer. Og i virkeligheden er det bare en binær fil, der "ikke kræver meget".

Så lad os gå til helm.sh i sektionen docs/installation og udføre kommandoen derfra:

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

Derefter tilføjer vi chart-repositoriet:

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

Nu installerer vi infrastrukturkomponenterne i henhold til planen:

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

Ingress controller

Den første komponent — Ingress controller — installeres temmelig enkelt og er klar til brug "out of the box". For at gøre dette skal du blot gå til sektionen bare-metal på webstedet og udføre installationskommandoen derfra:

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

Men på dette tidspunkt begyndte "hindbæret" at knirke og stødte på diskens IOPS. Problemet er, at der med Ingress-controlleren installeres en stor mængde ressourcer, mange API-anmodninger kører, og mange data skrives til etcd. Kort sagt, enten er det 10. klasses hukommelseskort ikke særlig effektivt, eller SD-kortet er generelt utilstrækkeligt til en sådan belastning. Alligevel blev alt startet op efter cirka 5 minutter.

Der blev oprettet et namespace, og der dukkede en controller op i det med alt, hvad den havde brug for:

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

De følgende to komponenter er ret nemme at installere via Helm fra chart repo.

Find Prometheus, vi opretter et namespace og installerer det i det:

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"}

Som standard bestiller Prometheus 2 diske: til selve Prometheus-data og til AlertManager-data. Da der ikke er oprettet en storage class i klyngen, vil diske ikke blive bestilt, og pod'erne vil ikke starte. For bare metal-installationer af Kubernetes bruger vi typisk Ceph rbd, men i tilfælde af Raspberry Pi er dette åbenlyst overkill.

Derfor vil vi oprette en simpel lokal storage på hostpath. Manifesterne PV (persistent volume) for prometheus-server og prometheus-alertmanager er samlet i filen prometheus-pv.yaml i Git-repositorier med eksempler til artiklen. Biblioteket til PV skal oprettes på forhånd på disken af den node, vi ønsker at binde Prometheus til: i eksemplet er der angivet nodeAffinity efter hostname pi-worker og der er oprettet biblioteker /data/localstorage/prometheus-server og /data/localstorage/prometheus-alertmanager.

Downloader (kloner) manifestet og tilføjer det til Kubernetes:

kubectl create -f prometheus-pv.yaml

På dette stadium stødte jeg for første gang på problematikken med ARM-arkitektur. Kube-state-metrics, som som standard installeres i Prometheus-chartet, nægtede at starte. Det gav en fejl:

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"

Problemet er, at kube-state-metrics bruger et billede fra CoreOS-projektet, som ikke bygges til 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

Jeg var nødt til at google lidt og finde for eksempel dette billede. For at bruge det vil vi opdatere udgivelsen og angive, hvilket billede der skal bruges til 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

Lad os kontrollere, at alt er startet:

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 og cert-manager

Til grafer og dashboards installerer vi Grafana:

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

Til sidst vil vi vise, hvordan man får adgangskoden:

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

For at bestille certifikater, lad os installere cert-manager. For installationen skal vi kontakte dokumentationen, som tilbyder de relevante kommandoer til 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

Til selvsignerede certifikater til hjemmebrug er dette helt tilstrækkeligt. Hvis man derimod ønsker at få samme Let’s Encrypt, er det nødvendigt at konfigurere en cluster issuer. Detaljer om dette kan findes i vores artikel «SSL-certifikater fra Let’s Encrypt med cert-manager i Kubernetes».

Jeg valgte selv varianten fra eksemplet i dokumentationen, da jeg mente, at staging-varianten LE ville være tilstrækkelig. Vi ændrer e-mail i eksemplet, gemmer det i en fil og tilføjer det til klyngen (cert-manager-cluster-issuer.yaml):

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

Nu kan vi bestille et certifikat, for eksempel til Grafana. For dette kræves der et domæne og adgang til klyngen udefra. Jeg har et domæne, og jeg har konfigureret portforwarding af porte 80 og 443 på min hjemme-router i overensstemmelse med den oprettede ingress-controller-tjeneste:

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

Port 80 videresendes i dette tilfælde til 31303, og 443 til 30498. (Portene genereres tilfældigt, så jeres vil være andre.)

Her er et eksempel på et certifikat (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

Vi tilføjer det til klyngen:

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

Derefter vil der blive oprettet en Ingress-ressource, gennem hvilken validering vil finde sted af 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

Når valideringen er gennemført, vil vi se, at ressourcen certificate er klar, og i den nævnte hemmelighed grafana-tls er certifikatet og nøglen. Man kan straks kontrollere, hvem der har udstedt certifikatet:

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

Lad os vende tilbage til Grafana. Vi skal lave et par justeringer til dens Helm-udgivelse ved at ændre indstillingerne for TLS i overensstemmelse med det oprettede certifikat.

For at gøre dette downloader vi chartet, redigerer det og opdaterer fra den lokale mappe:

helm pull --untar stable/grafana

Vi redigerer i filen grafana/values.yaml TLS-parametre:

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

Her kan vi også konfigurere den installerede Prometheus som datasource:

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

Nu opdaterer vi Grafana-chartet fra den lokale mappe:

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

Vi kontrollerer, at Ingress grafana har fået tilføjet port 443 og har adgang over 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

For at demonstrere Grafana i aktion kan man downloade og tilføje dashboard til kube-state-metrics. Sådan ser det ud:

Fuldstændig Kubernetes fra bunden på Raspberry Pi

Jeg anbefaler også at tilføje et dashboard til node exporter: det vil detaljeret vise, hvad der sker med "hindbærerne" (CPU-belastning, hukommelsesforbrug, netværk, disk osv.).

Efter dette mener jeg, at klusteret er klar til at tage imod og køre applikationer!

Bemærkning om opbygning

Der er mindst to muligheder for at bygge applikationer til ARM-arkitektur. For det første kan man bygge på en ARM-enhed. Men efter at have set på den nuværende udnyttelse af de to Raspberry Pi, indså jeg, at de ikke engang vil kunne klare bygningen. Derfor har jeg bestilt en ny Raspberry Pi 4 (den er kraftigere og har hele 4 GB hukommelse) — jeg planlægger at bygge på den.

Den anden mulighed er at bygge et multi-arkitekturs Docker-billede på en kraftigere maskine. Til dette findes der en udvidelse docker buildx. Hvis applikationen er skrevet i et kompilérbart sprog, vil der være behov for kryds-kompilering til ARM. Jeg vil ikke beskrive alle indstillinger for denne proces, da det ville kræve en særskilt artikel. Ved at implementere denne tilgang kan man opnå "universelle" billeder: Docker, der kører på en ARM-maskine, vil automatisk downloade det passende arkitektur-billede.

Konklusion

Det udførte eksperiment overgik alle mine forventninger: [mindst] "vanilla" Kubernetes med den nødvendige database fungerer ganske godt på ARM, og jeg stødte kun på et par nuancer under konfigurationen.

Raspberry Pi 3B+ kan håndtere CPU-belastningen, men deres SD-kort er et klart flaskehals. Mine kolleger nævnte, at nogle versioner har mulighed for at starte fra USB, hvor man kan tilslutte SSD: så bliver situationen sandsynligvis bedre.

Her er et eksempel på CPU-belastning under installationen af Grafana:

Fuldstændig Kubernetes fra bunden på Raspberry Pi

Til eksperimenter og "for at prøve", synes jeg, at et Kubernetes-kluster på "Raspberry Pi" giver en meget bedre oplevelse end den samme Minikube, fordi alle klusterkomponenterne både installeres og fungerer "professionelt".

Der er en idé om at tilføje hele CI/CD-cyklussen til klusteret, helt implementeret på Raspberry Pi. Jeg vil også være glad, hvis nogen deler deres erfaring med opsætning af K8s på AWS Graviton.

P.S. Ja, "produktion" kan være tættere på, end jeg troede:

Fuldstændig Kubernetes fra bunden på Raspberry Pi

P.P.S.

Læs også på vores blog:

Kilde: habr.com

Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere 🔥 Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster