k3s Cluster installieren mit MetalLB und Traefik

k3s Cluster installieren mit MetalLB und Traefik

Achtung! Dieser Text ist schon über zwei Jahre alt und kann veraltet sein!

k3s ist eine schlanke Variante zum vollen Kubernetes (k8s) Stack. Der k3s Cluster lässt sich daher mit wenigen Schritten installieren und einrichten. Dieses Tutorial beschreibt dir wie du den k3s Cluster installieren kannst.

Voraussetzungen

Für dieses Tutorial gibt es ein paar Voraussetzungen:

  • Mindestens zwei Linux Systeme (egal ob VM oder Raspberry Pi) mit mind. 2 Kernen und 1 GB RAM
  • Grundlegende Kenntnisse von Linux
  • Die Fähigkeit Tutorials komplett zu lesen (entschuldige den Sarkasmus)

Vorbereitungen

Solltest du eine VM verwenden, musst du nichts weiter vorbereiten als dein System auf den aktuellen Stand zu patchen. Zudem wäre es empfehlenswert, wenn du feste IP-Adressen verwenden würdest. Andernfalls kannst du natürlich auch die Kommunikation via DNS machen, dann benötigst du aber ein zuverlässiges DNS!

Solltest du allerdings einen Raspberry Pi verwenden müssen wir noch etwas am boot Prozess einstellen. Und zwar erweitern wir die cmdline.txt Datei. Diese liegt bei Rasbian und Ubuntu (ja auch das gibts für den RPi) unter verschiedenen Orten:

Rasbian

sudo vim /boot/cmdline.txt

Ubuntu

sudo vim /boot/firmware/cmdline.txt

Am Ende der Zeile fügen wir noch folgendes ein

cgroup_enable=1 cgroup_memory=1 cgroup_enable=memory

Solltest du das bei den Raspberry Pis nicht tun, wird k3s nicht starten!

k3s Cluster installieren

Master installieren

Fangen wir mir dem Master an. Dieser lässt sich quasi mit copy & paste von der k3s.io Webseite kopieren. Allerdings wollten wir ja die Installation ein wenig anpassen. Wir verwenden z. B. MetalLB als Cluster Load Balancer um IP Adressen aus dem normalen Netzbereich unseres Netzwerkes zu verwenden. Zudem bin ich ein Fan von Traefik. Zwar kommt der k3s Cluster im Standard schon mit Traefik daher, allerdings wollen wir die Funktion erweitern. Entsprechend müssen wir die Installation ein wenig abändern.

curl -sfL https://get.k3s.io | sh -s - --disable traefik --disable servicelb

Nach ein paar Augenblicken sollte der Cluster soweit sein. Wir kontrollieren das mit

kubectl get nodes

Und sollten eine Node sehen die den Status ready hat.

Agenten hinzufügen

Diesen Schritt wiederholst du entsprechend so oft wie viele Agenten du zu dem Cluster hinzufügen möchtest.

Allerdings benötigen wir als erstes deinen Token um die verschlüsselte Kommunikation zu erlauben. Diesen lesen wir wie folgt aus

cat /var/lib/rancher/k3s/server/node-token

Es erscheint eine lange Zeile aus Buchstaben und Zahlen z. B.

K10c8c26950c31536167a7a38f11979060f977cf33cfe10a5e7b130e057a8e1d4df::server:de0d941e347301d8ac112e4c2ef2826b

Natürlich sieht das bei dir anders aus, daher ist ein einfaches kopieren aller Befehle in diesem Tutorial nicht möglich.

Nun verbinden wir die Agenten mit dem Master

curl -sfL https://get.k3s.io | K3S_URL=https://{IP-Server}:6443 K3S_TOKEN={Token} sh -

Passe bitte die zwei Variablen in dem Befehl an deine Gegebenheiten an.

Auf dem Master führen wir nun den Befehl

kubectl get nodes --watch

aus. Nach ein paar Augenblicken sollte der Agent dort erscheinen und nach ein paar weiteren in den Status Ready wechseln.

Da jetzt der Cluster läuft, können wir uns um MetalLB kümmern.

MetalLB installieren

MetalLB ist wie gesagt ein LoadBalancer um Anfragen über den Cluster zu verteilen. Wir werden MetalLB als Layer2 LB verwenden. Alternativen für Menschen mit mehr Netzwerkkenntnissen wären z. B. noch BGP. Layer2 reicht aber für das Homelab und benötigt keine spezielle Konfiguration an Firewall und/oder Switchen.

Die Version des Programmes ändert sich natürlich fortlaufend. Entsprechend ist zu beachten, dass du in den Befehlen die Version auf den neusten Stand bringst. Am besten ist, du wirfst immer vorher ein Blick in die offizielle Dokumentation.

kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.5/manifests/namespace.yaml
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.5/manifests/metallb.yaml

# On first install only
kubectl create secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)"

Ein paar Sekunden später ist die Grundarbeit erledigt. Jetzt legen wir noch den IP-Bereich fest, den MetalLB zur Verfügung hat. Dazu erstellen wir uns eine kleine Datei

vim ConfigMap.yaml

und fügen folgenden Inhalt ein

---
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name:
configdata:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - 192.168.10.50-192.168.10.59

Den IP-Bereich unten passt du bitte so an, dass er bei dir passt!

Last but not leased benötigen wir noch einen reverse proxy. Wie immer bei mir: Traefik.

Traefik installieren

Für Traefik verwenden wir zwei sogenannte Manifest Dateien. Erste Datei:

vim CustomRessourceDefinition.yaml

Und da rein kommt folgender langer Inhalt

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: ingressroutes.traefik.containo.us
spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: IngressRoute
    plural: ingressroutes
    singular: ingressroute
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: middlewares.traefik.containo.us
spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: Middleware
    plural: middlewares
    singular: middleware
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: ingressroutetcps.traefik.containo.us
spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: IngressRouteTCP
    plural: ingressroutetcps
    singular: ingressroutetcp
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: ingressrouteudps.traefik.containo.us
spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: IngressRouteUDP
    plural: ingressrouteudps
    singular: ingressrouteudp
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: tlsoptions.traefik.containo.us
spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: TLSOption
    plural: tlsoptions
    singular: tlsoption
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: tlsstores.traefik.containo.us
spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: TLSStore
    plural: tlsstores
    singular: tlsstore
  scope: Namespaced

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: traefikservices.traefik.containo.us
spec:
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: TraefikService
    plural: traefikservices
    singular: traefikservice
  scope: Namespaced

---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: traefik-ingress-controller
rules:
  - api
Groups:
      - ""
    resources:
      - services
      - endpoints
      - secrets
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - extensions
    resources:
      - ingresses
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - extensions
    resources:
      - ingresses/status
    verbs:
      - update
  - apiGroups:
      - traefik.containo.us
    resources:
      - middlewares
      - ingressroutes
      - traefikservices
      - ingressroutetcps
      - ingressrouteudps
      - tlsoptions
      - tlsstores
    verbs:
      - get
      - list
      - watch

---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: traefik-ingress-controller
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: traefik-ingress-controller
subjects:
  - kind: ServiceAccount
    name: traefik-ingress-controller
    namespace: default

Hiermit werden alle benötigen Clusterweiten Ressourcen und Abhängigkeiten eingerichtet. Anschließend brauchen wir natürlich noch den Container selbst.

vim Deployment.yaml

mit folgendem Inhalt

---
apiVersion: v1
kind: Service
metadata:
  name: traefik
spec:
  ports:
  - protocol: TCP
   name: web
   port: 80
 - protocol: TCP
   name: admin
   port: 8080
 - protocol: TCP
   name: websecure
   port: 443
 type: LoadBalancer
 selector:
   app: traefik

---
apiVersion: v1
kind: ServiceAccount
metadata:
  namespace: kube-system
  name: traefik-ingress-controller

---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: kube-system
  name: traefik
  labels:
    app: traefik
spec:
  replicas: 1
  selector:
    matchLabels:
      app: traefik
  template:
    metadata:
      labels:
        app: traefik
    spec:
      serviceAccountName: traefik-ingress-controller
      containers:
       - name: traefik
         image: traefik:v2.3
         args:
            - --api.insecure
            - --accesslog
            - --entrypoints.web.Address=:80
            - --entrypoints.websecure.Address=:443
            - --providers.kubernetescrd
            - --certificatesresolvers.myresolver.acme.tlschallenge
            - --certificatesresolvers.myresolver.acme.email=mail@example.com
            - --certificatesresolvers.myresolver.acme.storage=acme.json
         ports:
            - name: web
              containerPort: 80
            - name: websecure
              containerPort: 443
            - name: admin
              containerPort: 8080

Nach kurzer Zeit sollte der Container auch erscheinen und den Status Ready bekommen

kubectl get pods --watch

Als letztes kontrollieren wir noch ob Traefik auch eine IP-Adresse aus dem LoadBalancer Bereich erhalten hat

kubectl get svc

Hier sollten wir eine Zeile ähnlich zu dieser haben

NAME      TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)                                     AGEtraefik   LoadBalancer   10.43.43.165   192.168.10.50   80:32049/TCP,8080:31750/TCP,443:31764/TCP   18h

Jetzt kannst du beginnen entsprechende Workload auf deinen Cluster zu bringen. Allerdings derzeit nur mit lokalem Storage. In weiteren Beiträgen werde ich noch Storagemöglichkeiten beschreiben.