Kubernetes mit Rancher RKE

29 Jul 2019 Lesezeit: 10 Minuten

Wer sich mit Kubernetes beschäftigt der kommt irgendwann auch einmal an den Punkt, an dem er darüber nachdenkt wie man seine(n) Cluster einfacher verwalten kann. Kubernetes selbst sehe ich eher als Framework als als eine vollständige Lösung. Zu viele Punkte sind unbeachtet und müssen nachgearbeitet werden.

Im Unternehmensumfeld hat sich sicherlich OpenShift ziemlich gut etabliert - für mich auch eine wirklich schicke Lösung. Allerdings trifft es nicht immer den Geschmack den man haben möchte. So zum Beispiel will viele Dinge nicht haben die OpenShift mir bietet, wenn ich zum Beispiel nur einen Cluster betreibe der einfache Workloads abfangen möchte.

An dieser Stelle kommt Rancher ins Spiel. Die Firma dahinter hat ihre Produkte sauber voneinander getrennt, sodass man nutzen kann was man grade braucht - oder eben man setzt komplett auf das Pferd das einem geboten wird.

In meinem Fall tue ich das, da ich keinen Grund sehe auf die Annehmlichkeiten zu verzichten die es mir bringt.

Wer nun einen Kubernetes Cluster mit Hilfe von RKE installieren möchte und zusätzlich auch noch die Verwaltungssoftware Rancher installiert haben will: der darf jetzt weiterlesen! Ich nutze wie so oft ein CentOS Linux auf meinen Servern.

Systemvorbereitung

Zunächst einmal stellen wir sicher, dass wir auch die Docker CE Version installiert haben und die notwendigen Softwarepakete an Board sind.

yum remove -y docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine
yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum install docker-ce docker-ce-cli containerd.io

als nächstes aktivieren wir den Docker-Dienst.

systemctl start docker
systemctl enable docker
systemctl status docker

Sobald das geschehen ist, legen wir einen neuen Benutzer an und sorgen dafür, das unser SSH-Public-Key hinterlegt wird.

useradd -m -G docker rke
mkdir /home/rke/.ssh
echo "ssh-rsa 111111111111122222222222223333333333444444444445555555566666666667777777777777778888888888 keiner@aol.com" >> /home/rke/.ssh/authorized_keys 
chmod -vR 700 /home/rke/.ssh
chown -vR rke: /home/rke/.ssh

Weil wir ordentlich sind sorgen wir natürlich auch dafür, dass wir die Firewall nutzen und firewalld so konfigurieren, dass er alle unsere Ports für Kubernetes durchlässt

firewall-cmd --zone=public --add-port=6443/tcp --permanent
firewall-cmd --zone=public --add-port=2376/tcp --permanent
firewall-cmd --zone=public --add-port=2379/tcp --permanent
firewall-cmd --zone=public --add-port=2380/tcp --permanent
firewall-cmd --zone=public --add-port=9099/udp --permanent
firewall-cmd --zone=public --add-port=10250/tcp --permanent
firewall-cmd --zone=public --add-port=80/tcp --permanent
firewall-cmd --zone=public --add-port=443/tcp --permanent
firewall-cmd --zone=public --add-port=8472/tcp --permanent
firewall-cmd --zone=public --add-port=10254/tcp --permanent
firewall-cmd --zone=public --add-port=30000-32767/tcp --permanent
firewall-cmd --zone=public --add-port=30000-32767/udp --permanent
firewall-cmd --reload

Projektverzeichnis

Das coole an dem ganzen modernen Kram ist ja, das er darauf ausgelegt ist die Systeme reproduzierbar zu bauen. Das heisst auch, dass man alles was zur Einrichtung etc notwendig ist extern in einem Repository einchecken können muss (zum Beispiel). Daher erstellen wir uns nun einen neuen Ordner in dem wir alles sammeln was mit der Einrichtung, der Wartung und Administration des Clusters zu tun hat - samt der Binary die wir verwenden

mkdir rke-cluster
cd rke-cluster
wget https://github.com/rancher/rke/releases/download/v0.2.4/rke_linux-amd64
mv rke_linux-amd64 rke

Sobald wir das getan haben, erstellen wir auch einen weiteren SSH-PublicKey, den wir auf den Clusterteilnehmern verteilen. Das hat nochmal ein schickes Geschmäkle, weil es nicht nur allein unserer ist der zum Arbeiten genutzt wird. Dazu einfach den obigen Schritt nochmals durchführen der in die Datei /home/rke/.ssh/authorized_keys schreibt.

ssh-keygen -f rke-cluster -b 4096

Anschließend brauchen wir noch eine Konfigurationsdatei für RKE, welche unseren kompletten Cluster darstellt. Mit allen Nodes und Einstellungen und so weiter. Wer einfach starten will, der nimmt sich die minimal Konfiguration von hier oder führt den Befehl ./rke config --empty --name cluster.yml aus. In meinem Fall sieht das Ding dann ungefähr so aus:

cluster.yml
nodes:
- address: "rancher1"
  port: "22"
  role:
  - controlplane
  - etcd
  user: rke
  ssh_key_path: ./rke-cluster
  labels: 
    app: ingress
- address: "rancher2"
  port: "22"
  role:
  - controlplane
  - etcd
  user: rke
  ssh_key_path: ./rke-cluster
  labels: 
    app: ingress
- address: "rancher3"
  port: "22"
  role:
  - controlplane
  - etcd
  user: rke
  ssh_key_path: ./rke-cluster
  labels: 
    app: ingress
services:
  kube-api:
    image: ""
    extra_args: {}
    extra_binds: []
    extra_env: []
    service_cluster_ip_range: 10.43.0.0/16
    service_node_port_range: ""
    pod_security_policy: true
    always_pull_images: false
  kube-controller:
    image: ""
    extra_args: {}
    extra_binds: []
    extra_env: []
    cluster_cidr: 10.42.0.0/16
    service_cluster_ip_range: 10.43.0.0/16
  kubelet:
    image: ""
    extra_args: {}
    extra_binds: []
    extra_env: []
    cluster_domain: cluster.local
    infra_container_image: ""
    cluster_dns_server: 10.43.0.10
    fail_swap_on: false
network:
  plugin: flannel
  options: 
        flannel_iface: eth0
        flannel_backend_type: vxlan
authentication:
  strategy: x509
  sans: []
  webhook: null
addons: ""
addons_include: []
system_images:
  etcd: rancher/coreos-etcd:v3.2.24-rancher1
  alpine: rancher/rke-tools:v0.1.28
  nginx_proxy: rancher/rke-tools:v0.1.28
  cert_downloader: rancher/rke-tools:v0.1.28
  kubernetes_services_sidecar: rancher/rke-tools:v0.1.28
  kubedns: rancher/k8s-dns-kube-dns:1.15.0
  dnsmasq: rancher/k8s-dns-dnsmasq-nanny:1.15.0
  kubedns_sidecar: rancher/k8s-dns-sidecar:1.15.0
  kubedns_autoscaler: rancher/cluster-proportional-autoscaler:1.0.0
  coredns: rancher/coredns-coredns:1.2.6
  coredns_autoscaler: rancher/cluster-proportional-autoscaler:1.0.0
  kubernetes: rancher/hyperkube:v1.13.5-rancher1
  flannel: rancher/coreos-flannel:v0.10.0-rancher1
  flannel_cni: rancher/flannel-cni:v0.3.0-rancher1
  weave_node: weaveworks/weave-kube:2.5.0
  weave_cni: weaveworks/weave-npc:2.5.0
  pod_infra_container: rancher/pause:3.1
  ingress: rancher/nginx-ingress-controller:0.21.0-rancher3
  ingress_backend: rancher/nginx-ingress-controller-defaultbackend:1.4-rancher1
  metrics_server: rancher/metrics-server:v0.3.1
ssh_key_path: ~/rke-cluster
ssh_agent_auth: false
authorization:
  mode: rbac
  options: {}
ignore_docker_version: false
kubernetes_version: ""
private_registries: []
ingress:
  provider: ""
  options: {}
  node_selector:
    app: ingress
  extra_args: {}
cluster_name: ""
monitoring:
  provider: ""
  options: {}
restore:
  restore: false
  snapshot_name: ""
dns: null

Das ist aber natürlich jedem selbst überlassen - ich mag es einfach, wenn ich so viel wie möglich von dem sehen kann, was los ist und was ich einstellen kann. Allerdings ist das auch weitaus mehr, als man minimal braucht. Wer sich mal die Beispiele anschaut wird merken, dass man hier ziemlich freue Hand hat.

Da wir jetzt so weit sind, starten wir das Setup mit einem einfachen

./rke up

Wenn das durchgelaufen ist, sollten wir einen funktionierenden Cluster haben. Während der Einrichtung wurden auch ein paar weitere Dateien erstellt. Diese sind wichtig. Testen kann man seinen Cluster nun wie folgt

export KUBECONFIG=$(pwd)/kube_config_cluster.yml
kubectl get nodes

Optional: Installation von Helm

Wer so richtig fancy mit seinem Cluster angeben will, der installiert sich auch Helm. Denn dann kann man noch einfacher die unterschiedlichsten Weichwaren installieren und damit total gut angeben. Abgesehen davon brauchen wir es um die Rancher Verwaltung zu installieren.

wget https://get.helm.sh/helm-v2.14.1-linux-amd64.tar.gz
gunzip helm-v2.14.1-linux-amd64.tar.gz
mv linux-amd64 helm
rm helm-v2.14.1-linux-amd64.tar 

kubectl -n kube-system create serviceaccount tiller
kubectl create clusterrolebinding tiller   --clusterrole=cluster-admin   --serviceaccount=kube-system:tiller ./helm init --service-account tiller

Ist diese Aufgabe durchgelaufen, kann es auch schon losgehen mit der Installation von Rancher.

./helm repo add rancher-alpha https://releases.rancher.com/server-charts/alpha
./helm/helm repo update
./helm/helm install rancher-latest/rancher --name rancher --namespace cattle-system --set hostname=k8s.MEINETOLLEDOMAIN.de

Day two tasks

Wenn man angeben will, dann macht man sich sofort daran LDAP einzurichten um Benutzerauthentifizierung zu haben und natürlich aktiviert man auch das Monitoring. Backup ist auch keine schlechte Sache, auch wenn einem die Cloud-Dingsis immer vermitteln, das man das nicht mehr haben will. So kann man doch arbeiten.

Quellen und Verweise


Docker Swarm an einem Beispiel

16 Jun 2019 Lesezeit: 5 Minuten

Wenn es ein Thema gibt, das in den letzten Jahren fahrt aufgenommen hat, dann ist es wohl das Thema der Containerisierung. Nicht das es neu oder revolitionär wäre. Immerhin habe es einige gute Systeme vorher schon implementiert gehabt. Aber das Tooling drum herum war nur den adeligen gegeben. Dank Docker ist dem nun nicht mehr so. Standalone auf einem Server ein paar Container zu starten ist wirklich kein Hexenwerk. Spaß macht es aber erst, wenn man sich einen Docker-Swarm aufsetzt, damit diese Container dann auch Hoch verfügbar oder skalierbar sind.

Dazu habe ich mir in der Vergangenheit mal ein paar Gedanken gemacht. Ziel dieser Anleitung ist es nun meinerseits einmal ein mögliches, einfaches Szenario aufzuzeigen, mit dem man den Swarm für die Build-Prozesse nutzen kann.

Zielsetzung: Mit dem Swarm wird es möglich sein Buildserver mit persistenten Daten zu starten und variable Agenten in einer beliebigen Anzahl von Workern anzubinden. Dabei bekommt jeder Jenkinsmaster ein eigenes Netzwerk das er mit den Slaves nutzt um daten auzutauschen. So ist es u.a. möglich das die Slaves den Master einfach mit seinem Namen ansprechen können. Der Aufbau ist ersteinmal exemplarisch und bietet keine Ausfallsicherheit für die Jenkins Master, da die Daten persistent vorgehalten werden - das lässt sich aber leicht ändern.

Aufbau des swarms Der Swarm kann grundsätzlich ausschließlich aus managern bestehen, was die Ausfallsicherheit gewährleistet. Wenn wir dann allerdings sicherstellen wollen, dass die Jenkins Master nur auf einem Gerät laufen müssen wir mit Tags arbeiten. Nur so können wir ohne shared storage die Daten Konsitent halten.

Auf dem LEADER (1. Rechner)

docker swarm init --advertise-addr 192.168.XXX.YYY

Dann 

docker swarm join-token manager

und diese Anweisungen auf den anderen Rechner ausführen. Somit läuft der Swarm und hat auch schon eine Ausfallsicherheit.

Nun des ersten Rechner des Swarms als Jenkins-Master kennzeichnen:

docker node update --label-add JENKINS-MASTER SWAMNODE1
docker node update --label-add JENKINS-SLAVE SWARMNODE2 SWARMNODE3 ...

Nun ist es Zeit ein Netzwerk für den ersten Master zu erstellen:

docker network create --driver overlay jenkins-test-netzwerk

Dieses Netzwerk nutzen wir um den Datenverkehr zwischen den Slaves und dem Master zu kapseln.

Nun können wir eigentlich schon den ersten Jenkins-Master starten und geben ihm dabei sowohl das Netzwerk mit zu dem er verbunden ist, als auch das Volume indem die Daten gespeichert werden sollen:

Zuerst wird ein Volume angelegt in dem dann die Daten vorgehalten werden:

docker volume create jenkins-test
docker service create --name jenkins-test-master -p 8080:8080 -p 50000:50000 --network jenkins-test-netzwerk --network docker_gwbridge --placement-pref 'spread=node.labels.JENKINS-MASTER' -m source=jenkins-test,target=/var/jenkins_home jenkins

Somit findet sich laut der Dokumentation vom Jenkins-Image alles relevante (Plugins, workspaces, etc) ab jetzt in diesem externen Volume. Das kann natürlich auch gern mit bind-mounts genutzt werden.

Jetzt schon sollte es möglich sein den Jenkins unter der Adresse des Managers:8080 anzusprechen. Was nun noch fehlt sind die Worker. Hier könnten direkt deine images zum Einsatz gebracht werden. Da du dich besser mit auskennst - einfach mal die Unterschiede:

docker service create --name jenkins-slave --network jenkins-test-netzwerk --placement-pref 'spread=node.labels.JENKINS-SLAVE' --replicas 4 jenkins-slave

Auf diese Weise können die Slaves sich nur mit dem Jenkins verbinden. Dieser muss ihnen dann auch die Daten zur Verfügung stellen. Da da vielleicht nicht geht kann man das  --network dockergwbridge noch dazu packen.

Die Slaves können den Master aber nun durch die Verwendung eines eigenen Netzwerkes direkt mit dem Containernamen ansprechen - also jenkins-test-master. Das macht das Verbinden und zuordnen einfacher, wenn man sich vorstellt, dass man das vielleicht Skripten will.

Jetzt ist es ja eigentlich schon mal cool, das man einfach auf diese Ressourcen zugreifen kann und die Laufzeitumgebung gesteuert wird. Auch ist es ja erst mal cool, dass wir den Jenkins unter dem Port ansprechen können.

Wenn man nun einen Schritt weiter gehen wollte, dann haut man sich noch einen Reverseproxy davor - wie zum Beispiel Traefik (macht auch schöne bunte Bilder).

Wenn man jetzt noch die Datenhaltung gern auf alle Server verteilen möchte oder auf einem Storage ablegen will, oder sonst was tun möchte, dann kann das sogar fast schon während der Laufzeit passieren. und man kann so den bestehenden Swarm ausbauen.

Fazit Diese Überlegungen sind schon etwas älter und mit einem Kubernetes-Plugin samt Cluster ist die Aufgabe auch schon fast gelöst. Allerdings bemerke ich immer wieder, das Docker Swarm einfach sexyer ist was die simplicity angeht und auch der Overhead ist nicht so enorm. Zwar ist dieses Beispiel noch nicht fertig - aber es zeigt wie einfach man seinen Kram in einen Docker-Swrm verfrachten und so mit den großen Jungs am Tisch mitreden kann.