Portfolio |

Pod versus Container

Im dritten Blogbeitrag unserer Reihe „Docker vs. OpenShift“ erklären wir euch, was es mit Pods auf sich hat und welche Rolle Container dabei einnehmen.

Im vorhergehenden Beitrag unserer Serie haben wir einen kleinen Einblick in Kubernetes gezeigt und wie einfach es ist, eine kleine OpenShift-Instanz aufzusetzen. In diesem Blogbeitrag steigen wir etwas tiefer in die Materie ein und erklären, was es mit Pods auf sich hat und welche Rolle Container dabei einnehmen.

Was ist ein Container?

Wie wir bereits in vorangegangenen Artikeln gelernt haben, ist ein Container eine Laufzeitinstanz eines Container-Images, welches eine Applikation mit ihren Abhängigkeiten zusammenfasst. Damit wird sichergestellt, dass die Applikation zuverlässig in verschiedenen Umgebungen laufen kann. Die Container können dabei über den entsprechenden Host, auf dem sie laufen, erreicht werden und nutzen dabei seine Ressourcen. In großen Umgebungen kann dies schnell zu Problemen führen, da man den Überblick darüber verliert, welche Ports auf welchen Hosts noch frei sind oder welche weiteren Ressourcen des Hosts von welchem Container genutzt werden. Und hier kommen Pods ins Spiel.

Was ist ein Pod?

Pods (aus dem englischen für kleine Gruppe an Meerestieren, Analogie zum „Docker-“Wal) sind die kleinste einsetzbare Einheit innerhalb Kubernetes beziehungsweise OpenShift. Jeder Pod hat eine eindeutige IP-Adresse und ist unter dieser IP von anderen Pods innerhalb des Kubernetes-Clusters erreichbar. Darüber hinaus werden jedem Pod entsprechende Speicherressourcen zugeteilt, was große Vorteile bei der Skalierbarkeit bietet. Im Sinne von Docker ist ein Pod eine Gruppe von einem oder mehreren Containern. Pods stellen im übertragenen Sinne dementsprechend einen Wrapper um Container dar, der es ermöglicht, Container auf Kubernetes zu betreiben. Denn Container selbst sind unter Kubernetes nicht direkt lauffähig. Gleichzeitig lösen Pods das oben beschriebene Problem der Ressourcenteilung zwischen den Containern, da jeder Anwendungscontainer unter der IP des Pods und dem Standard-Port der Anwendung erreichbar ist. Dadurch ist kein aufwändiges manuelles Port-Mapping mehr nötig..

Aus der Best Practice, Anwendungen möglichst kleinteilig zu trennen, ergibt sich, dass ein Pod nur einen Primär-Container, den Application Container in dem die Anwendung läuft, enthalten sollte. Darüber hinaus gibt es Helfer-Container. Sie werden im Verlauf des Beitrags näher vorgestellt. Das Umsetzen der Best Practices bietet große Vorteile bei der Skalierung. Wie oben beschrieben, werden jedem Pod Netzwerk- und Speicherressourcen zugeteilt, deren Werte der Pod nicht übersteigen kann. Bei der Skalierung nach oben sollten somit neue Pods mit den entsprechenden Anwendungscontainern ausgerollt werden, die im Bedarfsfall die maximalen Ressourcen ihres Pods ausschöpfen können. Abbildung 1 veranschaulicht diesen Vorteil, in dem es einen Anwendungscontainer je Pod in Vergleich zu mehreren Anwendungscontainern in einem Pod setzt. 

Abbildung 1: Pod Skalierung, 1 Pod = 1 Container und Sidecar-Container

Sidecar- und Init-Container

Wie bereits angemerkt, sollte immer nur ein Anwendungscontainer in einem Pod laufen. Daneben können aber sogenannte Helper-Container innerhalb des Pods laufen. Bei diesen Helper-Containern kann zwischen Sidecar- und Init-Containern unterschieden werden. Dabei bilden die Container sowie Speicher- und Netzwerkressourcen in einem Pod gebündelt eine Einheit. Wenn mehrere Container in einem Pod betrieben werden sollen, dann sollten diese auch stark voneinander abhängen.

Sidecar-Container gehören zu den populärsten Containern, die im laufenden Betrieb neben dem Anwendungscontainer in einem Pod laufen. Sie übernehmen sekundäre Aufgaben, die für das Ausführen des Anwendungscontainers nicht relevant sind, jedoch der Aufrechterhaltung des Betriebs dienen. Zum Beispiel ist es gängige Praxis, einen Logging Deamon in einem Sidecar-Container laufen zu lassen. Dieser Container liest die Logdateien aus dem primären Applikationscontainer aus und leitet sie an eine zentrale Logging-Instanz weiter. Zu weiteren typischen Aufgaben von Sidecar-Containern gehören Synchronizer, Watcher und Monitoring.

Im Gegensatz zu Sidecar-Containern werden Init-Container nur zum Aufsetzen der Rahmenbedingungen für den Anwendungscontainer verwendet. Sie haben ein fest definiertes Ziel, auf das sie hinarbeiten und müssen beendet sein, bevor der Application Container starten kann. Ein Pod kann auch mehrere Init-Container beinhalten, diese werden jedoch sequenziell abgearbeitet. Ein Init-Container wird erst ausgeführt, sobald der vorhergehende erfolgreich durchgelaufen ist. Init-Container bauen den Anwendungscontainer auf und versorgen ihn mit den notwendigen Informationen während des Ausrollens. Für folgende Anwendungsfälle bieten sich Init-Container an:

  • Klonen eines Git-Repositories in ein Volume (Erklärung zu Storage und Volumes folgt in einem der nächsten Beiträge)
  • Erstellen von dynamischen Konfigurationsdateien, zum Beispiel das Auslesen der Pod-IP und setzen des Wertes innerhalb einer Konfiguration
  • Warten auf die Erstellung eines Service
  • Registrierung des Pods an einem externen Service

Vorteile von Pods gegenüber Containern

Das Zusammenfassen stark voneinander abhängiger Container zu einem Pod ist einer der wesentlichsten Vorteile. Die Container eines Pods können sich Volumes teilen und untereinander über localhost (127.0.0.1) kommunizieren. Daneben bilden Pods die Grundlage weiterer wichtiger Features von Kubernetes und OpenShift, wie Deployments, Replica Sets, Daemon Sets und vielen weiteren. Vertiefende Informationen zu diesen Themen werden in weiteren Blogbeiträgen dieser Reihe erscheinen.

Health Checks mit Probes (Startup, Liveness und Readiness)

Pods können nur zwei Zustände haben, „ausgerollt“ (deployed) oder „nicht ausgerollt“ (not deployed), es gibt kein „teilweise ausgerollt“. Um sicherzustellen, dass Anwendungen ordnungsgemäß ausgerollt, funktionsbereit und erreichbar sind, gehört es zur Best Practice, regelmäßige Health Checks zu implementieren. Im Kubernetes-Umfeld übernehmen dies sogenannte Probes (zu Deutsch: Sonden), die in drei Kategorien eingeordnet werden können. Man unterscheidet je nach Aufgabe zwischen Startup-, Liveness- und Readiness-Probes.

Probes können auf drei Arten implementiert werden, als Execution-Command, als TCP Port-Abfrage und als HTTP-Request. Beim Execution-Command wird innerhalb des Containers ein Befehl ausgeführt. Wird dieser mit dem Exit-Code 0 beendet, gilt die Operation als erfolgreich. Bei der TCP Port-Abfrage, wird geprüft, ob ein bestimmter Port des Pods offen ist. Ist dies der Fall, gilt die Operation als erfolgreich. Beim HTTP-Request wird ein GET an einen bestimmten Port des Pods gesendet. Liefert die Anfrage einen Status-Code aus, der größer oder gleich 200 und kleiner 400 ist, gilt die Operation als erfolgreich. Die Implementierung von Probes auf Basis von HTTP-Requests setzt voraus, dass auf dem Pod ein (wenn auch minimaler) HTTP-Server läuft.   

Startup-Probes werden häufig verwendet, wenn die Initialisierung eines Containers sehr lange dauert und die Dauer nicht vorhersehbar ist. Somit kann beim Start das Warteintervall für die Probe deutlich größer eingestellt werden, als es in einer Liveness-Probe der Fall wäre. Startup-Probes kommen häufig bei containerisierten Legacy-Anwendungen zum Einsatz.

Eine Liveness-Probe gibt an, ob der Container läuft. Wenn eine Liveness-Probe fehlschlägt, wird der Container gelöscht. Je nachdem welcher Wert für die RestartPolicy gesetzt ist, wird der Container neugestartet oder nicht.

Eine Readiness-Probe gibt an, ob ein Container bereit ist, auf Requests zu antworten. Wenn eine Readiness-Probe fehlschlägt, wird die Zuordnung der Pod-IP zu einem Endpoint Service in Kubernetes entfernt.

Das war der Beitrag Pod versus Container in unserer Blogreihe „Docker vs Openshift“. Im nächsten Beitrag werden wir uns dem Thema Image-Erstellung widmen.