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.