k3s und Netcup
K3S auf zwei fast frischen Netcup Server initialisieren und die Kommunikation via vLan realisieren.

TLDR: https://github.com/JimTim/kubernetes-samples/tree/main/setup-k3s-on-netcup
Meine Server bei Netcup lagen etwas brach und so habe ich mich nochmal an mein Herz gefasst und probiert Kubernetes darauf zu installieren. Da K3S ein gutes Gesamtpaket bietet, wollte ich es damit probieren. Damit das Projekt nicht an irgendwelchen dummen und alten Einstellungen scheitert, habe ich die Server kurzerhand platt gemacht und ein neues Ubuntu 20.04 minimal ausgerollt.


Das heißt, bei diesen Beitrag werde ich quasi beim Urschleim ansetzen. Ich setze lediglich voraus, das der Server minimal abgesichert ist und die normalen ufw Firewall Regeln gesetzt hat:
sudo ufw allow ssh
sudo ufw enable
Vorrausetzungen schaffen
Ich besitze bei Netcup 2 Server, die unterschiedliche Ressourcen zur Verfügung stellen.
Ressourcen | Server 1 aka manager | Server 2 aka worker |
---|---|---|
Speicher | 156,44GB | 1,44TB |
Arbeitsspeicher | 8GB | 16GB |
CPU | 2CPUs | 8CPUs |
Der etwas schwächere Server wird demnach der Manager des K3S Clusters werden und wird nachfolgend auch nur noch manager genannt.
Der andere Server mit mehr Ressourcen wird die ganzen Deployments ausführen und ist demnach der Worker und wird demzufolge auch nachfolgend worker genannt.
Damit die Knoten untereinander ungestört kommunizieren können, liegt an beiden Servern ein Cloud vLAN Free an. Damit wird also ein privates Lan aufgebaut und ich muss so manche Ports nicht für die Außenwelt öffnen.
Hostname setzen und vLan aufbauen
manager
Als erstes wird der Hostname neu gesetzt. Dadurch kann man später viel besser erkennen, wer etwas macht:
sudo hostnamectl set-hostname k8s-manager
Um das vLan aufzubauen, muss dieses im ServerControlPanel am Server gebunden sein. Ich habe diesen Schritt bei Netcup nie manuell gemacht und deswegen bin ich mir nicht sicher, warum meine Server das vLan direkt gebunden hatten. Vielleicht ist das also ein Service von Netcup, wenn der Kauf eines Cloud vLans ausgeführt wird.

Nun muss dieses Interface im Ubuntu 20.04 noch initialisiert werden. Das passiert bei dieser Version mit Netplan:
sudo vi /etc/netplan/02-vlan.yaml
network:
version: 2
renderer: networkd
ethernets:
eth1:
dhcp4: no
addresses:
- 192.168.100.1/24
Anschließend muss aus der Yaml der Netplan generiert und zusätzlich angewendet werden.
sudo netplan generate
sudo netplan apply
Danach sollte ein neues Interface mit dem Namen "eth1" auftauchen. Kontrolliert werden kann das mit ip a.
Damit später die IPs einfach gefunden werden können, werden sie zusätzlich in die /etc/hosts eingetragen:
echo "192.168.100.1 k8s-manager" >> /etc/hosts
echo "192.168.100.2 k8s-worker" >> /etc/hosts
Mit Voraussicht wurde hier schon die IP des Workers eingetragen.
worker
Nun müssen diese Schritte abgewandelt auf dem worker ausgeführt werden. Ich halte mich hier aber etwas kürzer.
Als Erstes natürlich wieder den Hostname setzen:
sudo hostnamectl set-hostname k8s-manager
Danach wird das vLan an einen Interface initialisiert:
sudo vi /etc/netplan/02-vlan.yaml
network:
version: 2
renderer: networkd
ethernets:
eth1:
dhcp4: no
addresses:
- 192.168.100.2/24
Dann wird der Netplan generiert und angewendet:
sudo netplan generate
sudo netplan apply
Und als letzten Schritt wird die /etc/hosts aktualisiert;
echo "192.168.100.1 k8s-manager" >> /etc/hosts
echo "192.168.100.2 k8s-worker" >> /etc/hosts
K3S installieren und mit dem richtigen Netzwerk versehen
manager
Damit die ganze Kommunikation zwischen den Knoten privat passiert, muss das Flannel Interface richtig gesetzt werden.
Quelle
Mit folgenden Befehl wird K3S heruntergeladen und das Skript wird ausgeführt:
export EXTERNAL_IP=""
export INTERNAL_IP=""
export INTERNAL_INTERFACE="eth1"
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--flannel-iface=$INTERNAL_INTERFACE --node-ip=$INTERNAL_IP --node-external-ip=$EXTERNAL_IP --tls-san $EXTERNAL_IP" sh -
Damit der Befehl etwas dynamischer ist, habe ich vorher noch die Umgebungsvariablen EXTERNAL_IP, INTERNAL_IP und INTERNAL_INTERFACE auf die richtigen Werte gesetzt.
Anschließend kann die generierte kubeconfig an die richtige Stelle kopiert werden.
sudo cp /etc/rancher/k3s/k3s.yaml .kube/config
Die erfolgreiche Installation kann dann mit folgenden Befehl verifiziert werden:
kubectl get node -o wide
An dieser Stelle wird nun die erste Node, der k8s-manager, ausgegeben und die richtigen IPs sind hinterlegt.

Der aufmerksame Leser sieht natürlich hier schon den Worker. Der wurde wie folgt initialisiert:
worker
Damit der Worker aber überhaupt angegangen werden kann, braucht es noch einen sogenannten Node Token vom Manager:
cat /var/lib/rancher/k3s/server/node-token
Dieser Token sollte irgendwo extern notiert werden, da er nun auf dem Worker gebraucht wird.
export EXTERNAL_IP_MANAGER=""
export EXTERNAL_IP=""
export INTERNAL_IP=""
export INTERNAL_INTERFACE="eth1"
export TOKEN="<wurde ein Block oberhalb extern notiert und sollte nun hier eingefügt werden>"
curl -sfL https://get.k3s.io | K3S_URL=https://$EXTERNAL_IP_MANAGER:6443 K3S_TOKEN=$TOKEN INSTALL_K3S_EXEC="--flannel-iface=$INTERNAL_INTERFACE --node-ip=$INTERNAL_IP --node-external-ip=$EXTERNAL_IP" sh -
Auch hier werden vorher die Umgebungsvariablen EXTERNAL_IP_MANAGER, EXTERNAL_IP, INTERNAL_IP, INTERNAL_INTERFACE und TOKEN mit sinnvollen Werten belegt.
Das Installationsskript von K3S erkennt nun automatisch, das es als Agent installiert werden soll.
Auf dem Manager kann nun wieder der kubectl get nodes -o wide Befehl ausgeführt werden. Zu diesem Zeitpunkt sollte sich aber der Worker beim Manager nicht registrieren können, da die notwendigen Firewall Regeln nicht angewendet wurden.
Firewall Regeln
Der Server wurde ja nur mit einer Firewall Regel initialisiert. Das heißt, aktuell ist nur die Kommunikation via SSH erlaubt. Damit die Kommunikation aber privat und öffentlich im Kubernetes Umfeld funktioniert, müssen noch einige Ports wieder geöffnet werden:
manager
sudo ufw allow ssh comment "SSH"
sudo ufw allow http comment "HTTP"
sudo ufw allow https comment "HTTPS"
sudo ufw allow 6443/tcp comment "Kubernetes API"
sudo ufw allow from 192.168.100.0/24 comment "Kubernetes Flannel Network Communication (internal)"
sudo ufw allow in on eth1 from 192.168.100.0/24 comment "Kubernetes Flannel Network Communication (internal) on eth1 incoming"
sudo ufw allow out on eth1 from 192.168.100.0/24 "Kubernetes Flannel Network Communication (internal) on eth1 outgoing"
sudo ufw enable
Ich habe einige Kommentare an die Regeln gepackt und damit sind die Befehle Erklärung genug.
worker
sudo ufw allow ssh comment "SSH"
sudo ufw allow http comment "HTTP"
sudo ufw allow https comment "HTTPS"
sudo ufw allow from 192.168.100.0/24 comment "Kubernetes Flannel Network Communication (internal)"
sudo ufw allow in on eth1 from 192.168.100.0/24 comment "Kubernetes Flannel Network Communication (internal) on eth1 incoming"
sudo ufw allow out on eth1 from 192.168.100.0/24 "Kubernetes Flannel Network Communication (internal) on eth1 outgoing"
sudo ufw enable
Auch hier sind die Kommentare Erklärung genug.
Damit sollte sich nun der Worker am Manager registrieren können. Ansonsten kann auch ein kurzer Uninstall & Install auf dem Worker helfen:
/usr/local/bin/k3s-agent-uninstall.sh
curl -sfL https://get.k3s.io | K3S_URL=https://$EXTERNAL_IP_MANAGER:6443 K3S_TOKEN=$TOKEN INSTALL_K3S_EXEC="--flannel-iface=$INTERNAL_INTERFACE --node-ip=$INTERNAL_IP --node-external-ip=$EXTERNAL_IP" sh -
Cert Manager und wie bekommen meine Anwendungen eigentlich ein Zertifikat?
An dieser Stelle könnte man auch schon aufhören und fröhlich Anwendungen via kubectl deployen. Aber jede Anwendung hat auch irgendwann eine Route in die Öffentlichkeit. Dieser sogenannte Ingress sollte natürlich nur via HTTPs erreichbar sein. Da ich keine Zertifikate einfach so rumliegen habe, werden diese immer mit Hilfe von LetsEncrypt generiert werden. Diese Aufgabe übernimmt das Tool cert-manager im Kubernetes Umfeld.
K3S bringt bereits Traefik als Ingress Manager mit und so muss der Cert-Manager mit Traefik initialisiert werden. Aber erstmal die Deployments vom Cert-Manager im Kubernetes bereitstellen:
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.8.0/cert-manager.yaml
Da ich dieses Tutorial am 10.04.2022 schreibe, ist die Version 1.8.0 aktuell. Unter Installation von Cert-Manager kann aber die aktuelle Version herausgefunden werden. Noch besser, der aktuellste Installationsschritt ist dort dokumentiert.
Damit Zertifikate bei LetsEncrypt beantragt werden können, muss eine valide Mailadresse hinterlegt werden. Die Ressource heißt im Cert-Manager Umfeld Cluster-Issuer. Diesen Issuer kann man einmal für die Staging Plattform anlegen und dann später für die Produktionslinie von LetsEncrypt. Ich dokumentiere einfach mal beides:
Folgenden Inhalt als letsencrypt-staging.yaml speichern:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
# You must replace this email address with your own.
# Let's Encrypt will use this to contact you about expiring
# certificates, and issues related to your account.
email: mail@example.com
server: https://acme-staging-v02.api.letsencrypt.org/directory
privateKeySecretRef:
# Secret resource that will be used to store the account's private key.
name: staging-issuer-account-key
# Add a single challenge solver, HTTP01 using nginx
solvers:
- http01:
ingress:
class: traefik
Danach kann diese Datei mithilfe der kubectl angewendet werden:
kubectl apply -f letsencrypt-staging.yaml
Folgenden Inhalt als letsencrypt-prod.yaml speichern:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
# You must replace this email address with your own.
# Let's Encrypt will use this to contact you about expiring
# certificates, and issues related to your account.
email: mail@example.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
# Secret resource that will be used to store the account's private key.
name: prod-issuer-account-key
# Add a single challenge solver, HTTP01 using nginx
solvers:
- http01:
ingress:
class: traefik
Auch diese Datei wird mithilfe der kubectl angewendet:
kubectl apply -f letsencrypt-prod.yaml
Ab diesem Zeitpunkt ist das Tutorial fertig. Beide Server wurden erfolgreich initialisiert, vLan ist fertig aufgebaut und funktioniert mit der ufw Firewall und das K3S tauscht sich munter und sicher aus.
Als Sahnehäubchen werde ich im nächsten Tutorial dokumentieren, wie ich diesen Blog im Kubernetes betreibe und damit auch den Cert-Manager für das Zertifikat benutze.