Kubernetes-Workshop Teil 6: Die erste Applikation

Im fünften Teil haben wir unseren Kubernetes-Cluster eingerichtet. Heute wollen wir die erste Applikation installieren. Wir verwenden für die Demo einfach das kleine webbasierte Spiel game2048. Dieses gibt es praktischerweise schon fertig als Docker-Container.

Im ersten Schritt erstellen wir einen Namespace:

kubectl create namespace game2048

Dann erzeugen wir eine deployment.yaml mit folgendem Inhalt:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: game2048
spec:  
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: game2048      
  template: 
    metadata:
      labels:
        app.kubernetes.io/name: game2048        
    spec:      
      containers:
        - name: game2048
          securityContext:
            {}
          image: "ponsfrilus/2048nginx:latest"
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
              protocol: TCP

und deployen diese mit:

kubectl -n game2048 create -f deployment.yaml

Mit folgendem Kommando kann man sich das Ergebnis anschauen:

kubectl -n game2048 get deployments

Wir sollten folgendes Ergebnis erhalten:

NAME       READY   UP-TO-DATE   AVAILABLE   AGE
game2048   1/1     1            1           16s

Damit wir auf den installierten Server zugreifen können, benötigen wir einen Service, den wir in der Datei service.yaml definieren:

apiVersion: v1
kind: Service
metadata:
  name: game2048
  labels:    
    app.kubernetes.io/name: game2048    
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app.kubernetes.io/name: game2048

Auch diesen Service installieren wir mit:

kubectl -n game2048 create -f service.yaml

Nun definieren wir eine Ingress-Route und erstellen eine Datei ingress.yaml. Das Entscheidende ist die Angabe der Domain unter „host:“. Unter dieser Adresse wollen wir die Anwendung aufrufen.

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: game2048
  labels:    
    app.kubernetes.io/name: game2048
spec:  
  rules:
    - host: "2048.ronnywalter.de"
      http:
        paths:
          - path: /
            backend:
              serviceName: game2048
              servicePort: 80

Auch diese installieren wir mit:

kubectl -n game2048 create -f ingress.yaml

Damit haben wir folgende Komponenten deployed:

kubectl -n game2048 get all
NAME                            READY   STATUS    RESTARTS   AGE
pod/game2048-5fb4546466-266hv   1/1     Running   0          7m10s

NAME               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/game2048   ClusterIP   10.43.114.142   <none>        80/TCP    3m45s

NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/game2048   1/1     1            1           7m10s

NAME                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/game2048-5fb4546466   1         1         1       7m10s

Natürlich kann man sich das Ganze auch in der Rancher-Oberfläche ansehen. Der soeben erzeugte Namespace ist standardmäßig noch keinem Rancher-Projekt zugeordnet. Das liegt daran, dass der Projekt-Begriff eine Rancher-Eigenschaft ist, und kein Kubernetes-Objekt. Wir haben soeben nur mit Kubernetes-Mitteln gearbeitet. Man kann den Namespace über die Oberfläche in ein Projekt (wie z.B. Default) verschieben:

Rancher: Namespace verschieben

Es gibt auch die Möglichkeit, an den Namespace sogenannte Labels anzubringen und damit die Zuordnung zu Projekten zu realisieren, doch dazu später mehr.

Im Projekt Default sieht man nun die installierte Anwendung:

Rancher-Resources-Workloads.

Oder die installierten Ingress-Routen (unter „Load Balancing“):

Rancher-Ingress-Route

Nun wollen wir endlich die Applikation auch aufrufen und können das unter der URL, die wir in der Ingress-Definition angegeben haben, auch tun. Im obigen Beispiel haben wir http://2048.ronnywalter.de verwendet.

In Teil 2 haben wir die Server mit der Firewall ufw eingerichtet. Es ist natürlich notwendig, den http-Port zuzulassen, damit wir auf den Server zugreifen können. Das machen wir in dem Beispiel auf den 3 Nodes, auf denen wir Kubernetes installiert haben. Den https-Port öffnen wir gleich mit, da wir den später noch brauchen.

ufw allow http
ufw allow https

Nun können wir die Seite aufrufen.

Der Browser zeigt nun noch an, dass die Seite „Nicht Sicher“ ist. Heutzutage betreibt man keine Seiten mehr ohne https und ein gültiges Zertifikat. Daher wollen wir uns im nächsten Teil ein eben solches via LetsEncrypt besorgen.

Kubernetes-Workshop Teil 5: Kubernetes

Nachdem nun endlich unsere Rancher-Installation läuft, gehts heute endlich mal an die Einrichtung eines Kubernetes-Clusters. Dazu loggen wir uns in Rancher ein und gehen auf „Global“ und wählen „Add Cluster“ aus. Wir wollen heute noch keinen Cluster in der Cloud anlegen. Das machen wir später einmal. Heute erstellen wir einen „Custom“ Cluster auf unseren Nodes.

Man wählt zunächst einen Namen, wie zum Beispiel „k8s“. Im unteren Bereich hat man die Möglichkeit, noch diverse Einstellungen, wie z.B. die Kubernetes-Version festzulegen. Ich belasse es zunächst bei der Standardvorgabe. Allerdings möchte ich, dass die Kubernetes-Komponenten über das wireguard-Interface miteinander reden, das wir bereits eingerichtet haben. Einen Einstieg dazu gibt es hier.

Wir gehen auf „Edit as YAML“ und passen den Teil „network“ an:

  network:
    mtu: 0
    options:
      canal_iface: wg0
      flannel_backend_type: vxlan
    plugin: canal

Entscheidend ist der Eintrag „canal_iface“. In der Rancher-Dokumentation gibt’s noch mehr Details. Wir klicken auf Next und schon existiert unser Cluster. Dieser braucht natürlich noch Nodes. Hier nochmal der Plan:

NameCoresRAMIPZweck
defiant48 GB10.0.0.1Rancher-Installation
discovery616 GB10.0.0.2Master, Worker
reliant830 GB10.0.0.3Worker
yorktown616 GB10.0.0.4Worker

Wir erstellen nun als erstes den Master Node. Ich selektiere hier alle Rollen und trage in den Advanced Options die zu nutzenden IPs ein. Bei der public-IP gibt man die „ganz normale“ öffentliche IP des Servers an. Bei der internen IP geben wir die Wireguard IP-Adresse an. Als Beispiel für „discovery“:

Das erzeugte Kommando kopieren wir uns, und führen es auf dem Server „discovery“ aus. Ich klicke dann auf Done und warte, bis der Cluster „Ready“ ist. Man kann vermutlich auch alle 3 Nodes in einem Rutsch hinzufügen, ich habe das aber immer nacheinander gemacht. Den zweiten Node fügen wir hinzu, in dem wir auf „Edit“ klicken und dann im unteren Bereich genau so den nächsten Node hinzufügen. Wir wählen dabei aber immer nur die Role „Worker“ aus. Wie gesagt, dieses Setup hat nicht den Anspruch einer Produktionsumgebung mit hoher Verfügbarkeit. Im echten Leben würde man sicherlich auf den Mastern keine Workload haben wollen und natürlich mehrere Master installieren.

Wenn alle 3 Nodes hinzugefügt sind, sollte die Übersicht unter „Nodes“ so ähnlich aussehen:

Perfekt, der Cluster läuft!

Wenn man lokal mit kubectl auf den Cluster zugreifen möchte, holt man sich noch die Kubeconfig unter „Cluster“ und speichert den Inhalt lokal in der Datei ˜/.kube/config. Natürlich muss man sich lokal vorher noch den kubectl-client installieren. Auf dem Mac geht das wunderbar mit Homebrew:

brew install kubectl

Danach kann man mit kubectl schon mit seinem Cluster arbeiten und die erste Workload deployen oder sich z.B. die nodes anzeigen lassen:

~ » kubectl get nodes                                                                                                                
NAME        STATUS   ROLES                      AGE   VERSION
discovery   Ready    controlplane,etcd,worker   23d   v1.17.4
reliant     Ready    worker                     23d   v1.17.4
yorktown    Ready    worker                     23d   v1.17.4

Beim nächsten Mal deployen wir die erste Applikation auf dem Cluster.

Kubernetes Workshop Teil 1: Vorüberlegungen

Ich möchte mir einen kleinen Kubernetescluster aufbauen, um dort verschiedenste Applikationen zu deployen, die ich mehr oder weniger im Alltag nutzen möchte. Weiterhin möchte ich diverse Anwendungen aus dem Bereich der Softwareentwicklung installieren, um verschiedene Aspekte insbesondere aus dem Bereich CI/CD ausprobieren zu können. Anfangs denke ich hierbei an folgende Applikationen:

Weiterhin möchte ich CI/CD-Buildstrecken mit eben dieser Infrastruktur aufsetzen. Das werde ich mit kleinen Java-Beispielen ausprobieren.

Ziel soll sein, dass alle Applikationen mit Kubernetes-Mitteln bzw. Helm installiert werden können. Ich hoffe, dabei viel zu lernen.

Um den Kubernetes-Cluster aufzusetzen, möchte ich Rancher einsetzen. Ich möchte ebenfalls einen zentrales Logging (ElasticStack) und ein Monitoring installieren, vor allem um zu lernen, wie das geht.

Es gibt ja die Möglichkeit, sich mit Tools wie minikube einen eigenen kleinen Cluster für Experimente zu erzeugen. Ich wollte mir das Ganze aber auf „richigen“ Servern im Internet installieren. Dabei muss ich dann auch einige Sachen wie Firewall-Einstellungen berücksichtigen. Ebenso soll die Kommunikation zwischen den Servern und von meinem PC aus verschlüsselt erfolgen. Dazu habe ich mich etwas mit Wireguard beschäftigt.

In meinem Heimnetz steht eine Synology Diskstation. Diese hat die wunderbare Eigenschaft, dass man darauf auch Docker-Container starten kann. Das möchte ich nutzen, um dort u.a. einen kleinen Git-Server (gitea) und ein S3-Storage (minio) zu betreiben. In dieses S3-Storage soll mein Kubernetes Cluster z.B. Backups ablegen. Weiterhin habe ich einen Raspberry PI, den ich dazu nutzen möchte, eine VPN-Verbindung zwischen meinem lokalen Netz und den Servern herzustellen.

Das Ganze soll dann schematisch so aussehen:

Für mein Experiment nutze ich virtuelle Server von contabo (VPS), da diese relativ günstig sind. Experimente in der Cloud mit AWS EKS oder GKE werde ich später machen. Ich habe mir insgesamt 4 Maschinen bestellt:

  1. VPS S – Rancher Installation
  2. VPS L – Kubernetes Node 1 (Worker, etcd, controllpane)
  3. VPS M – Kubernetes Node 2 (Worker)
  4. VPS M – Kubernetes Node 3 (Worker)

Warum ausgerechnet 4 Server? Auf dem ersten kleinen Server soll die Rancher-Installation laufen. In Rancher wiederum soll ein Kubernetes-Cluster auf den anderen 3 Nodes angelegt werden. Damit habe ich auf dem Cluster genug RAM (ca. 60GB) und auch etwas CPU-Power (20 Cores) um einige Applikationen laufen lassen zu können. Das Ganze kostet mich im Monat ca. 38€. Das bin ich aktuell bereit auszugeben. 🙂

Wie das geübte Auge sieht, ist das kein Hochverfügbarkeits-Setup. Dafür bräuchte man einige Nodes mehr. Rancher selbst soll auch „nur“ als Single-Node installiert werden. Für meine Experimente reicht das soweit aber aus.

Im nächsten Teil wollen wir die 4 Server einrichten.