Software Development

Kubernetes – Wer nicht skaliert, verliert

'Wer nicht skaliert, verliert' oder 'Wem`s gelingt, gewinnt' sind Leitsätze von Kubernetes. Die Cluster- Container-Technologie aus dem Hause Google setzt dabei konsequent auf Hochverfügbarkeit von Netzwerkservices (protokollbasierte, adressierbare Dienste im Netz).

Kubernetes ist dabei die ideale Ergänzung zu Docker. Wo Docker aufwendig wird, z.B. beim Container- Monitoring und der (Re)start-Automatisierung, fängt Kubernetes an. Es kümmert sich neben der Hochverfügbarkeit (0-Downtime bzw. 24/7-Verfügbarkeit eines Netzwerkservice) von Services um den Lebenszyklus von Containern (run, start, stop, remove). Das betrifft alle Dinge rund um die Konfiguration und den Betrieb von Containern auf unterschiedlichen Docker-Hosts. Das oberste Ziel ist einfache Konfigurierbarkeit und maximale Automatisierung hochverfügbarer Netzwerkservices.

Die Idee von Kubernetes ist die Entkopplung der Container von ihrer Serviceadresse, was durch eine Serviceschicht umgesetzt ist. Somit kann jede Serviceanfrage an einen Service gestellt und von mehreren Containern beantwortet werden. Jeder Service ist somit horizontal skalierbar.

kubernetes-service


Zentrale Kubernetes-Instrumente sind dabei:

  1. Automatisierte Liveness- und Readyness-Probe von Pods (kleinste Kubernetes-Resource. Synonym für einen Container.)
  2. Automatisierter Up- und Down-scale von Services – durch Resource-Monitoring

Automatisierte Liveness- und Readyness-Probe von Pods

Jeder Pod wird von Kubernetes überwacht, um ihn entweder bei Bedarf neu zu starten oder ihm Zeit zur Selbstheilung zu geben, falls der Pod ausgelastet ist. Der Neustart wird durch die Liveness-Probe ausgelöst und die Selbstheilung durch die Readiness-Probe. Beide Probes können entweder HTTP-Endpunkte oder Container-Kommandos sein. Sie werden von Kubernetes in einem definierbaren Takt abgefragt. Im wiederholten Fehlerfall passiert folgendes:

  • Liveness-Fehlerfall: Kubernetes started den Pod neu.
  • Readiness-Fehlerfall: Kubernetes routet keinen weiteren Traffic zu diesem Pod.

Beide Probes können Bestandteil eines Pods sein und werden folgendermaßen im YAML-Format (YAML und JSON sind neben kubectl die zwei Konfigurationsformate, um die Kubernetes-REST-Endpunkte zu konfigurieren) konfiguriert:

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: demo
  name: demo-pod
spec:
  containers:
  - image: jwausle/de.jwausle.kubernetes:demo
    imagePullPolicy: Never
    name: demo
    ports:
    - containerPort: 8080
      protocol: TCP
    livenessProbe: # - Kubernetes started den Pod neu
      failureThreshold: 3
      httpGet:
        path: ⁄liveness
        port: 8080
        scheme: HTTP
      initialDelaySeconds: 30
      periodSeconds: 3
      successThreshold: 1
      timeoutSeconds: 1
    readinessProbe: # - Kubernetes routet keinen weiteren Traffic zu diesem Pod    
      failureThreshold: 3
      httpGet:
        path: ⁄readiness
        port: 8080
        scheme: HTTP
      initialDelaySeconds: 30
      periodSeconds: 3
      successThreshold: 1
      timeoutSeconds: 1

 

Das 'initialDelaySeconds: 30' definiert die initiale Wartezeit beim Podstart, wo Kubernetes keine Probeanfrage stellt. Danach stellt Kubernetes alle 3 Sekunden ( 'periodSeconds: 3' ) eine Probe-Anfrage. Sollte dreimal hintereinander ein Fehler passieren ( 'failureThreshold: 3' ), dann setzt Kubernetes die entsprechende Probe in den Fehlermodus. Um den Fehlermodus wieder zu verlassen, kann man mit 'successThreshold: N' die Anzahl erfolgreicher Anfragen für den Statuswechsel konfigurieren. Damit nicht jeder Pod einzeln konfiguriert werden muss, können alternativ beide Probes im ReplicationController, ReplicaSet oder Deployment konfiguriert werden.

Zusammen mit Punkt 2. "Automatisierter Up-scale von Services" kann Kubernetes Hochverfügbarkeit für jeden Service gewährleisten.

kubernetes-layer

2. Automatisierter Up- und Down-scale von Services – durch Resource-Monitoring

Kubernetes überwacht periodisch die Ressourcen aller Pods wie z.B. vCPU- und Speicherauslastung. Für jede Ressource kann ein Schwellwert (bezüglich aller Pods) definiert werden, ab welchem Kubernetes anfängt, automatisch zusätzliche Pods zu starten beziehungsweise bestehende herunterzufahren. Dieser Schwellwert wird im HorizontalPodAutoscaler konfiguriert:

apiVersion: v1
items:
- apiVersion: autoscaling/v1
  kind: HorizontalPodAutoscaler
  metadata:
    name: ${DEPLOYMENT_CONFIG_NAME}
  spec:
    maxReplicas: 10
    minReplicas: 2
    scaleTargetRef:
      apiVersion: v1
      kind: ReplicationController
      name: ${DEPLOYMENT_CONFIG_NAME}
    targetCPUUtilizationPercentage: 20
kind: List

 

Die Anzahl der gestarteten Pods wird durch die folgende Formel bestimmt:

'desiredReplicas = currentReplicas * ( currentMetricValue / desiredMetricValue )'

Bsp.1 - Auslastung der bestehenden Pods führt zum Start von 2 weiteren:

  • currentReplicas= 2
  • currentMetricValue=40%
  • desiredMetricValue=20%
  • desiredReplicas= 4 = 2 * ( 40 / 20)

Bsp.2 - Auslastung der bestehenden Pods führt zum Stopp von 2 existierenden:

  • currentReplicas= 4
  • currentMetricValue=20%
  • desiredMetricValue=40%
  • desiredReplicas= 2 = 4 * ( 20 / 40)

Wann Kubernetes?

Microservices eignen sich ideal für den Einsatz von Kubernetes. Statuslosigkeit und/oder Clusterfähigkeit sind dabei Haupteigenschaften eines Microservices.

Ein sehr gutes Beispiel sind 'Build Slaves' für Jenkins oder Gitlab, da ein wichtiges Merkmal deren Skalierfähigkeit pro Commit ist, natürlich je nach Ausbau des (C)ontinues-(I)ntegration-Prozesses. D.h., jeder Commit, der einen CI Build auslöst, soll einen atomaren 'Build Slave' erhalten.

Eher nicht geeignet sind zustandsabhängige Services wie klassische Datenbanken. Deren Clusterfähigkeit ist oft integraler Bestandteil des Gesamtsystems und nicht des einzelnen Containers. Datenbanken sollten besser außerhalb von Kubernetes betrieben werden. Das Overlay-Netzwerk von Kubernetes bietet auch dafür eine nahtlose Integrationsmöglichkeit.

Fazit

Kubernetes ist die ideale Ergänzung zu Docker im Produktiveinsatz. Wo Docker aufwendig wird, setzt Kubernetes an. Es kümmert sich neben der Hochverfügbarkeit und des Routings von Services hauptsächlich um den Betrieb von Containern über einen längeren Zeitraum. Der automatisierte Neustart eines Pods ist dabei zentrales Instrument zur Skalierung.

Wie alles hat auch Kubernetes eine Kehrseite. Die Konfiguration erfolgt fast ausschliesslich über YAML-/JSON-Dateien, für die der Toolsupport meist mit dem Syntax-Highlighting aufhört. Das schafft ein erhöhtes Konfigurationsrisiko bei stark vernetzten Microservice-Infrastrukturen.

   
Über Jan Winter

Jan Winter arbeitet seit 2012 bei itemis in Leipzig und sagt über sich selbst: „KISS ist mein Mantra, Clean Code meine Bibel, REST nicht nur ein Nebenprodukt, Docker die Erleuchtung und Blockchain eine Offenbarung.“