Best Practices für den Betrieb von Containern

Last reviewed 2023-02-28 UTC

In diesem Artikel wird eine Reihe von Best Practices zur Verwendung von Containern beschrieben. Dabei wird eine große Auswahl von Themen behandelt, von Sicherheit bis hin zu Überwachung und Logging. Mithilfe dieser Methoden soll das Ausführen von Anwendungen in Google Kubernetes Engine und in Containern im Allgemeinen erleichtert werden. Viele der hier erläuterten Methoden können auf die Twelve-Factor-Methodologie zurückgeführt werden, die eine nützliche Ressource beim Erstellen cloudnativer Anwendungen darstellt.

Einige der hier aufgeführten Best Practices sind wichtiger als andere. Bei der Ausführung einer Produktionsarbeitslast können Sie z. B. auf einige davon verzichten, andere sind jedoch von grundlegender Bedeutung. Insbesondere ist die Bedeutung der Best Practices für die Sicherheit subjektiv. Ob Sie sie umsetzen, hängt von Ihrer Umgebung und den jeweiligen Einschränkungen ab.

Sie benötigen Kenntnisse über Docker und Kubernetes, um die Informationen in diesem Artikel optimal nutzen zu können. Einige der hier beschriebenen Best Practices gelten auch für Windows-Container, aber bei den meisten wird davon ausgegangen, dass Sie mit Linux-Containern arbeiten. Informationen zum Erstellen von Containern finden Sie unter Best Practices für die Containererstellung.

Native Logging-Mechanismen von Containern verwenden

Wichtigkeit: HOCH

Als integraler Bestandteil des Anwendungsmanagements enthalten Logs wertvolle Informationen zu den Ereignissen in der Anwendung. Docker und Kubernetes machen die Log-Verwaltung so einfach wie möglich.

Auf einem klassischen Server müssen Sie Ihre Logs wahrscheinlich in eine bestimmte Datei schreiben und die Log-Rotation bearbeiten, damit die Laufwerke nicht gefüllt werden. Wenn Sie ein erweitertes Logging-System verwenden, können Sie diese Logs zur Zentralisierung an einen Remoteserver weiterleiten.

Mit Containern lassen sich Logs einfach und auf standardisierte Weise verarbeiten, da Sie sie in stdout und stderr schreiben können. Docker erfasst diese Logzeilen und ermöglicht mit dem Befehl docker logs den Zugriff darauf. Als Anwendungsentwickler müssen Sie keine erweiterten Logging-Mechanismen implementieren. Sie können einfach die systemeigenen Mechanismen verwenden.

Der Plattformbetreiber muss ein System bereitstellen, in dem Logs zentralisiert werden und durchsucht werden können. In GKE wird dieser Dienst von Fluent Bit und Cloud Logging bereitgestellt. Abhängig von der Version des GKE-Clustermasters werden entweder Fluentd oder Fluent Bit zum Erfassen von Logs verwendet. Ab GKE 1.17 werden Logs mit einem Fluentbit-basierten Agent erfasst. GKE-Cluster mit Versionen vor GKE 1.17 verwenden einen Fluentd-basierten Agent. In anderen Kubernetes-Distributionen verwenden sie häufig einen EFK-Stack (Elasticsearch, Fluentd, Kibana).

Diagramm: Klassisches Logverwaltungssystem in Kubernetes
Abbildung 1: Diagramm eines typischen Logverwaltungssystems in Kubernetes

JSON-Logs

Die meisten Log-Verwaltungssysteme sind eigentlich Zeitachsendatenbanken, in denen Dokumente mit Zeitindex gespeichert werden. Diese Dokumente können normalerweise im JSON-Format bereitgestellt werden. In Cloud Logging und in EFK wird eine einzelne Logzeile zusammen mit einigen Metadaten (Informationen zu Pod, Container, Knoten usw.) als Dokument gespeichert.

Sie können aus diesem Verhalten einen Nutzen ziehen, indem Sie Logs mit verschiedenen Feldern direkt im JSON-Format erstellen. Die Logs können dann basierend auf diesen Feldern effektiver durchsucht werden.

Betrachten Sie beispielsweise die Transformation des folgenden Logs in das JSON-Format:

[2018-01-01 01:01:01] foo - WARNING - foo.bar - There is something wrong.

So sieht das transformierte Log aus:

{
  "date": "2018-01-01 01:01:01",
  "component": "foo",
  "subcomponent": "foo.bar",
  "level": "WARNING",
  "message": "There is something wrong."
}

Mit dieser Transformation können Sie in Ihren Logs einfach nach allen Logs auf WARNING-Ebene oder allen Logs der Unterkomponente foo.bar suchen.

Wenn Sie JSON-formatierte Logs verwenden, muss jedes Ereignis in eine separate Zeile geschrieben werden, damit es richtig geparst wird. In der Realität sieht das Ergebnis so aus:

{"date":"2018-01-01 01:01:01","component":"foo","subcomponent":"foo.bar","level": "WARNING","message": "There is something wrong."}

Das Ergebnis ist jetzt nicht so gut lesbar wie eine normale Log-Zeile. Wenn Sie sich für diese Methode entscheiden, sollten sich Ihre Teams nicht zu sehr auf die manuelle Log-Prüfung verlassen.

Sidecar-Muster zur Log-Zusammenfassung

Manche Anwendungen wie z. B. Tomcat können nicht so einfach für das Schreiben von Logs in stdout und stderr konfiguriert werden. Diese Anwendungen schreiben Daten in verschiedene Log-Dateien auf dem Laufwerk. Deshalb sollten Sie zur Verarbeitung in Kubernetes am besten das Sidecar-Muster für Logging verwenden. Ein Sidecar ist ein kleiner Container, der im selben Pod wie Ihre Anwendung ausgeführt wird. Ausführlichere Informationen zu Sidecars finden Sie in der offiziellen Kubernetes-Dokumentation.

In dieser Lösung fügen Sie Ihrer Anwendung im selben Pod einen Logging-Agent in einem Sidecar-Container hinzu und nutzen ein emptyDir-Volume für beide Container, wie in diesem YAML-Beispiel auf GitHub gezeigt. Anschließend konfigurieren Sie die Anwendung so, dass Logs auf das freigegebene Volume geschrieben werden, und stellen ein, dass der Logging-Agent die Logs liest und je nach Bedarf weiterleitet.

Da Sie für dieses Muster nicht die nativen Logging-Mechanismen von Docker und Kubernetes verwenden, müssen Sie sich um die Log-Rotation kümmern. Wenn der verwendete Logging-Agent nicht die Logrotation verwaltet, kann diese Aufgabe von einem weiteren Sidecar-Container im selben Pod übernommen werden.

Diagramm: Sidecar-Muster für die Logverwaltung
Abbildung 2: Sidecar-Muster für die Logverwaltung

Zustandslose und unveränderliche Container verwenden

Wichtigkeit: HOCH

Wenn Sie Container zum ersten Mal ausprobieren, sollten Sie sie nicht wie herkömmliche Server behandeln. Sie könnten z. B. versucht sein, Ihre Anwendung innerhalb eines aktiven Containers zu aktualisieren oder einen aktiven Container zu patchen, wenn Sicherheitslücken bekannt werden.

Container sind jedoch nicht darauf ausgelegt, auf diese Weise verwendet zu werden. Sie wurden absichtlich dafür entwickelt, zustandslos und unveränderlich zu sein.

Zustandslosigkeit

Zustandslos bedeutet, dass jeder Zustand (nichtflüchtige Daten jeglicher Art) außerhalb eines Containers gespeichert wird. Dieser externe Speicher kann je nach Ihren Anforderungen verschiedene Formen annehmen:

  • Zum Speichern von Dateien empfehlen wir einen Objektspeicher wie Cloud Storage.
  • Zum Speichern von Informationen wie z. B. Nutzersitzungen empfehlen wir die Verwendung eines externen Schlüssel/Wert-Speichers mit niedriger Latenz, wie Redis oder Memcached.
  • Wenn Sie Speicher auf Blockebene benötigen (z. B. für Datenbanken), können Sie ein externes Laufwerk verwenden, das mit dem Container verbunden ist. Im Fall von GKE empfehlen wir die Verwendung nichtflüchtiger Speicher.

Bei diesen Optionen werden die Daten aus dem Container selbst entfernt, sodass er jederzeit ohne Datenverlust heruntergefahren und gelöscht werden kann. Wenn ein neuer Container erstellt wird, der den alten ersetzt, müssen Sie den neuen Container lediglich mit demselben Datenspeicher verbinden oder an dasselbe Laufwerk anschließen.

Unveränderlichkeit

Unveränderlich bedeutet, dass ein Container im Laufe seiner Lebensdauer nicht geändert wird: keine Updates, keine Patches, keine Konfigurationsänderungen. Wenn Sie den Anwendungscode aktualisieren oder einen Patch anwenden müssen, erstellen Sie ein neues Image und stellen es neu bereit. Unveränderlichkeit erhöht die Sicherheit und Wiederholbarkeit von Bereitstellungen. Wenn Sie ein Rollback durchführen müssen, können Sie einfach das alte Image bereitstellen. Mit diesem Ansatz können Sie dasselbe Container-Image in allen Ihren Umgebungen bereitstellen und sie dadurch so identisch wie möglich machen.

Damit Sie dasselbe Container-Image in verschiedenen Umgebungen verwenden können, sollten Sie die Containerkonfiguration (Überwachungsport, Laufzeitoptionen usw.) externalisieren. Container werden normalerweise mit Umgebungsvariablen oder Konfigurationsdateien konfiguriert, die in einem bestimmten Pfad bereitgestellt werden. In Kubernetes können Sie sowohl mit Secrets als auch mit ConfigMaps Konfigurationen als Umgebungsvariablen oder Dateien in Container einfügen.

Wenn Sie eine Konfiguration aktualisieren müssen, stellen Sie einen neuen Container (basierend auf demselben Image) mit der aktualisierten Konfiguration bereit.

Diagramm: Beispiel für die Aktualisierung der Konfiguration eines Deployment mithilfe einer ConfigMap, die als Konfigurationsdatei in den Pods bereitgestellt wurde
Abbildung 3: Beispiel für die Aktualisierung der Konfiguration eines Deployment mithilfe einer ConfigMap, die als Konfigurationsdatei in den Pods bereitgestellt wurde

Die Kombination aus Zustandslosigkeit und Unveränderlichkeit ist ein Verkaufsargument für containerbasierte Infrastrukturen. Sie ermöglicht es Ihnen, Bereitstellungen zu automatisieren und ihre Häufigkeit und Zuverlässigkeit zu erhöhen.

Privilegierte Container vermeiden

Wichtigkeit: HOCH

Auf einer virtuellen Maschine oder einem Bare-Metal-Server würden Sie es vermeiden, Anwendungen mit dem Root-Nutzer auszuführen. Dies hat einen einfachen Grund: Sollte die Anwendung gehackt werden, hat der Angreifer vollen Zugriff auf den Server. Aus demselben Grund sollten Sie auf privilegierte Container verzichten. Ein privilegierter Container hat Zugriff auf alle Geräte des Hostcomputers und umgeht dabei fast alle üblichen Sicherheitsfunktionen von Containern.

Wenn Sie privilegierte Container benötigen, prüfen Sie sich folgende Alternativen:

  • Sie können für den Container über die securityContext-Option von Kubernetes oder über das Flag --cap-add von Docker bestimmte Funktionen bereitstellen. In der Docker-Dokumentation sind sowohl die standardmäßig aktivierten als auch die gesondert zu aktivierenden Funktionen aufgeführt.
  • Wenn die Hosteinstellungen für die Ausführung der Anwendung geändert werden müssen, können Sie dies entweder in einem Sidecar-Container oder in einem init-Container ausführen. Im Gegensatz zu Ihrer Anwendung müssen diese Container weder für internen noch für externen Traffic freigegeben werden und sind somit isolierter.
  • Wenn Sie "sysctls" in Kubernetes ändern müssen, verwenden Sie die dedizierte Annotation.

Sie können privilegierte Container in Kubernetes mithilfe von Policy Controller verbieten. Im Kubernetes-Cluster können Sie keine Pods erstellen, die gegen die mit Policy Controller konfigurierten Richtlinien verstoßen.

Anwendungsüberwachung vereinfachen

Wichtigkeit: HOCH

Wie das Logging ist die Überwachung ein wesentlicher Bestandteil der Anwendungsverwaltung. Für die Überwachung containerisierter Anwendungen gelten in vielerlei Hinsicht die gleichen Prinzipien wie für die Überwachung von nicht containerisierten Anwendungen. Da containerisierte Infrastrukturen tendenziell jedoch sehr dynamisch sind, Container also häufig erstellt oder gelöscht werden, können Sie es sich nicht leisten, Ihr Überwachungssystem mit jedem Erstell- oder Löschvorgang neu zu konfigurieren.

Sie können zwischen zwei Hauptkategorien der Überwachung unterscheiden: Blackbox-Überwachung und Whitebox-Überwachung. Blackbox-Überwachung bezieht sich auf die Untersuchung Ihrer Anwendung von außen, aus der Perspektive eines Endnutzers. Diese Form der Überwachung ist nützlich, wenn der Dienst, den Sie anbieten möchten, verfügbar und funktionsfähig ist. Da es sich um eine externe Überwachung der Infrastruktur handelt, wird bei der Blackbox-Überwachung nicht zwischen herkömmlichen und containerisierten Infrastrukturen unterschieden.

Whitebox-Überwachung beinhaltet die Untersuchung Ihrer Anwendung mit einer Art privilegiertem Zugriff. Dabei werden Messwerte zum Anwendungsverhalten erfasst, die einem Endnutzer nicht gezeigt werden. Da bei der Whitebox-Überwachung die tiefsten Schichten Ihrer Infrastruktur untersucht werden, unterscheidet sie sich erheblich für herkömmliche und containerisierte Infrastrukturen.

Eine beliebte Option für die Whitebox-Überwachung in der Kubernetes-Community ist Prometheus, ein System, das die zu überwachenden Pods automatisch erkennt. In Prometheus werden die Pods nach Messwerten abgesucht. Das System geht dabei von einem bestimmten Format aus. Google Cloud bietet Google Cloud Managed Service for Prometheus, einen Dienst, mit dem Sie Ihre Arbeitslasten global überwachen und melden können, ohne Prometheus manuell in großem Umfang verwalten und betreiben zu müssen. Standardmäßig ist Google Cloud Managed Service for Prometheus so konfiguriert, dass Systemmesswerte aus GKE-Clustern erfasst und an Cloud Monitoring gesendet werden. Weitere Informationen finden Sie unter Beobachtbarkeit für GKE.

Damit Sie die Vorteile von Prometheus oder Monitoring nutzen können, müssen Ihre Anwendungen Messwerte bereitstellen. Anhand der zwei folgenden Methoden wird gezeigt, wie Sie dies erreichen.

HTTP-Endpunkt für Messwerte

Der HTTP-Endpunkt für Messwerte funktioniert so ähnlich wie die Endpunkte, die später unter Zustand Ihrer Anwendung freigeben beschrieben werden. Er stellt die internen Messwerte der Anwendung bereit, normalerweise über einen /metrics-URI. Eine Antwort sieht so aus:

http_requests_total{method="post",code="200"} 1027
http_requests_total{method="post",code="400"}    3
http_requests_total{method="get",code="200"} 10892
http_requests_total{method="get",code="400"}    97

In diesem Beispiel ist http_requests_total der Messwert, method und code sind Labels und die Zahl ganz rechts steht für den Wert dieses Messwerts für diese Labels. Die Beispielanwendung hat seit ihrem Start 97-mal mit dem Fehlercode 400 auf eine HTTP GET-Anfrage reagiert.

Die Erstellung dieses HTTP-Endpunkts wird durch die Prometheus-Clientbibliotheken erleichtert, die für viele Sprachen verfügbar sind. Auch OpenCensus kann Messwerte in diesem Format exportieren (und bietet viele weitere Features). Setzen Sie diesen Endpunkt nicht dem öffentlichen Internet aus.

In der offiziellen Prometheus-Dokumentation finden Sie ausführliche Informationen zu diesem Thema. Weitere Informationen zum Whitebox- und Blackbox-Monitoring finden Sie in Kapitel 6 von Site Reliability Engineering.

Sidecar-Muster für Monitoring verwenden

Nicht alle Anwendungen können mit einem /metrics-HTTP-Endpunkt instrumentiert werden. Für ein standardisiertes Monitoring empfehlen wir, das Sidecar-Muster zu verwenden, um Messwerte im richtigen Format zu exportieren.

Im Abschnitt Sidecar-Muster zur Log-Zusammenfassung wurde erläutert, wie Sie einen Sidecar-Container zur Verwaltung von Anwendungs-Logs nutzen können. Dasselbe Muster können Sie auch für die Überwachung verwenden: Der Sidecar-Container hostet einen Überwachungsagenten, der die von der Anwendung bereitgestellten Messwerte in ein Format und ein Protokoll umwandelt, das vom globalen Überwachungssystem verstanden wird.

Ein konkretes Beispiel: Java-Anwendungen und Java Management Extensions (JMX). Viele Java-Anwendungen stellen Messwerte mithilfe von JMX bereit. Anstatt eine Anwendung neu zu schreiben, um Messwerte im Prometheus-Format bereitzustellen, können Sie den jmx_exporter nutzen. Der jmx_exporter sammelt die Messwerte einer Anwendung über JMX und stellt sie über einen /metrics-Endpunkt bereit, der von Prometheus gelesen werden kann. Diese Vorgehensweise hat den Vorteil, dass die Freigabe des JMX-Endpunkts, der zum Ändern der Anwendungseinstellungen verwendet werden kann, eingeschränkt wird.

Diagramm: Sidecar-Muster für Monitoring
Abbildung 4: Sidecar-Muster für Monitoring

Zustand Ihrer Anwendung freigeben

Wichtigkeit: MITTEL

Wenn Sie die Verwaltung einer Anwendung in der Produktion vereinfachen möchten, muss sie ihren Zustand an das Gesamtsystem kommunizieren: Wird die Anwendung ausgeführt? Ist sie fehlerfrei? Ist sie bereit, Traffic zu empfangen? Wie verhält sie sich?

Kubernetes hat zwei Arten von Systemdiagnosen: Aktivitäts- und Bereitschaftsprüfungen. Beide haben einen bestimmten Anwendungszweck, der jeweils in diesem Abschnitt beschrieben wird. Sie können beide Prüfungen auf verschiedene Weise implementieren, unter anderem durch die Ausführung eines Befehls im Container oder durch die Überprüfung eines TCP-Ports. Am besten verwenden Sie jedoch die in dieser Best Practice beschriebenen HTTP-Endpunkte. Weitere Informationen zu diesem Thema finden Sie in der Kubernetes-Dokumentation.

Aktivitätsprüfung

Die empfohlene Methode zum Implementieren der Aktivitätsprüfung ist die Bereitstellung eines /healthz-HTTP-Endpunkts über Ihre Anwendung. Nach dem Empfang einer Anfrage an diesem Endpunkt sollte die Anwendung die Antwort "200 OK" senden, wenn sie als fehlerfrei gilt. Fehlerfrei bedeutet in Kubernetes, dass der Container nicht gelöscht oder neu gestartet werden muss. Was als fehlerfrei gilt, kann sich von Anwendung zu Anwendung unterscheiden, bedeutet aber in der Regel Folgendes:

  • Die Anwendung wird ausgeführt.
  • Die Hauptabhängigkeiten der Anwendung sind erfüllt. Sie kann z. B. auf die zugehörige Datenbank zugreifen.

Bereitschaftsprüfung

Die empfohlene Methode zum Implementieren der Bereitschaftsprüfung ist die Bereitstellung eines /ready-HTTP-Endpunkts über Ihre Anwendung. Nach dem Empfang einer Anfrage an diesem Endpunkt sollte die Anwendung die Antwort "200 OK" senden, wenn sie bereit ist, Traffic zu empfangen. Das bedeutet Folgendes:

  • Die Anwendung ist fehlerfrei.
  • Alle möglichen Initialisierungsschritte sind abgeschlossen.
  • Gültige Anfragen, die an die Anwendung gesendet werden, lösen keinen Fehler aus.

Kubernetes nutzt die Bereitschaftsprüfung, um die Bereitstellung Ihrer Anwendung zu orchestrieren. Wenn Sie ein Deployment aktualisieren, führt Kubernetes ein Rolling Update für die Pods durch, die zu diesem Deployment gehören. Die standardmäßige Aktualisierungsrichtlinie sieht vor, dass dabei ein Pod nach dem anderen aktualisiert wird. Kubernetes wartet, bis der neue Pod von der Bereitschaftsprüfung als bereit angezeigt wird, bevor es mit der Aktualisierung des nächsten beginnt.

Ausführung als Root vermeiden

Wichtigkeit: MITTEL

Container bieten Isolation: Unter Verwendung der Standardeinstellungen kann ein Prozess innerhalb eines Docker-Containers nicht auf Informationen des Hostcomputers oder der anderen zusammengeschlossenen Container zugreifen. Da Container jedoch den Kernel des Hostcomputers gemeinsam nutzen, ist die Isolation nicht so absolut wie bei virtuellen Maschinen. In diesem Blogpost wird dies erklärt. Ein Angreifer könnte bisher unbekannte Sicherheitslücken (entweder in Docker oder im Linux-Kernel selbst) finden, die es ihm ermöglichen würden, den Container zu verlassen. Wenn der Angreifer eine Sicherheitslücke findet und Ihr Prozess im Container als Root ausgeführt wird, erhält er Root-Zugriff auf den Hostcomputer.

Linkes Diagramm: Virtuelle Maschinen nutzen virtualisierte Hardware.
Rechtes Diagramm: Anwendungen in Containern nutzen den Host-Kernel.
Abbildung 5: Auf der linken Seite nutzen virtuelle Maschinen virtualisierte Hardware. Auf der rechten Seite nutzen Anwendungen in Containern den Host-Kernel.

Damit diese Möglichkeit gar nicht erst besteht, sollten Sie innerhalb von Containern keine Prozesse als Root ausführen. Mithilfe von Policy Controller können Sie dieses Verhalten in Kubernetes erzwingen. Zum Erstellen eines Pods in Kubernetes verwenden Sie die Option runAsUser, um den Linux-Nutzer anzugeben, von dem der Prozess ausgeführt wird. Dadurch wird die USER-Anleitung des Dockerfile überschrieben.

In der Praxis gibt es ein paar Herausforderungen. Bei vielen bekannten Softwarepaketen wird der Hauptprozess als Root ausgeführt. Wenn Sie die Ausführung als Root vermeiden möchten, entwerfen Sie Ihren Container so, dass er mit einem unbekannten, nicht privilegierten Nutzerkonto ausgeführt werden kann. Das bedeutet oft, dass Sie die Berechtigungen für verschiedene Ordner anpassen müssen. Wenn Sie in einem Container die Best Practice einer einzigen Anwendung pro Container anwenden und eine einzelne Anwendung nur mit einem Nutzer ausführen – vorzugsweise nicht mit dem Root-Konto –, können Sie allen Nutzern Schreibberechtigungen für die Ordner und Dateien gewähren, in die geschrieben werden soll, und für alle anderen Ordner und Dateien festlegen, dass sie nur vom Root-Konto beschrieben werden können.

Wenn Sie prüfen möchten, ob Ihr Container dieser Methode entspricht, können Sie ihn einfach lokal mit einem zufälligen Nutzerkonto ausführen und seine Funktion testen. Ersetzen Sie [YOUR_CONTAINER] durch den Namen Ihres Containers.

docker run --user $((RANDOM+1)) [YOUR_CONTAINER]

Falls der Container ein externes Volume benötigt, können Sie die Kubernetes Option fsGroup so konfigurieren, dass die Eigentumsrechte für dieses Volume einer bestimmten Linux-Gruppe zugewiesen werden. Mit dieser Konfiguration wird das Problem der Eigentumsrechte für externe Dateien gelöst.

Wenn Ihr Prozess von einem Nutzer ausgeführt wird, dem die nötigen Berechtigungen fehlen, kann keine Verbindung mit Ports unter 1024 stattfinden. Das ist normalerweise kein Problem, da Kubernetes-Dienste so konfiguriert werden können, dass der Traffic von einem Port zu einem anderen weitergeleitet wird. Sie können einen HTTP-Server z. B. so konfigurieren, dass er eine Verbindung mit Port 8080 herstellt und den Traffic über Port 80 mit einem Kubernetes-Dienst weiterleitet.

Image-Version sorgfältig auswählen

Wichtigkeit: MITTEL

Wenn Sie ein Docker-Image als Basis-Image in einem Dockerfile oder als in Kubernetes bereitgestelltes Image verwenden, müssen Sie das Tag des verwendeten Images auswählen.

Das Tagging der meisten öffentlichen und privaten Images erfolgt gemäß einem System, das dem unter Best Practices für die Containererstellung beschriebenen System ähnelt. Wenn das Image ein System verwendet, das der semantischen Versionierung nahekommt, müssen Sie einige Tagging-Besonderheiten berücksichtigen.

Die wichtigste Besonderheit: das Tag "latest" kann oft von Image zu Image übergeben werden. Sie können sich für vorhersagbare oder reproduzierbare Builds daher nicht auf dieses Tag verlassen. Betrachten Sie zum Beispiel folgendes Dockerfile:

FROM debian:latest
RUN apt-get -y update && \ apt-get -y install nginx

Wenn Sie mit diesem Dockerfile zweimal zu unterschiedlichen Zeiten ein Image erstellen, erhalten Sie möglicherweise zwei verschiedene Versionen von Debian und NGINX. Vergleichen Sie stattdessen diese überarbeitete Version:

FROM debian:11.6
RUN apt-get -y update && \ apt-get -y install nginx

Durch die Verwendung eines präziseren Tags sorgen Sie dafür, dass das resultierende Image immer auf einer bestimmten Nebenversion von Debian basiert. Da eine bestimmte Debian-Version auch eine bestimmte NGINX-Version enthält, haben Sie viel mehr Kontrolle über das Image, das erstellt wird.

Dieses Ergebnis gilt nicht nur zum Zeitpunkt der Build-Erstellung, sondern auch während der Laufzeit. Wenn Sie in einem Kubernetes-Manifest auf das Tag "latest" verweisen, kann die Version, die von Kubernetes verwendet wird, nicht garantiert werden. Verschiedene Knoten Ihres Clusters können dasselbe "latest"-Tag an verschiedenen Zeitpunkten abrufen. Wenn das Tag zwischen den Abrufvorgängen aktualisiert wurde, werden am Ende von verschiedenen Knoten möglicherweise verschiedene Images ausgeführt, die alle irgendwann mit "latest" gekennzeichnet waren.

Idealerweise sollten Sie in Ihrer FROM-Zeile immer ein unveränderliches Tag verwenden. Mit diesem Tag können Sie reproduzierbare Builds erstellen. Dabei müssen Sie allerdings ein paar Kompromisse im Hinblick auf die Sicherheit eingehen: Je mehr Sie die Version fixieren, die Sie verwenden möchten, desto weniger automatisiert werden die Sicherheitspatches in Ihren Images sein. Wenn das von Ihnen verwendete Image eine geeignete semantische Versionierung verwendet, sollte die Patchversion (das "Z" in "X.Y.Z") keine Änderungen beinhalten, die nicht abwärtskompatibel sind: Sie können das Tag "X.Y" verwenden und Fehlerkorrekturen werden automatisch durchgeführt.

Beispiel: Sie haben eine Software namens "SuperSoft". Der Sicherheitsprozess für SuperSoft besteht darin, Sicherheitslücken durch eine neue Patchversion zu beheben. Sie möchten die Software anpassen und haben das folgende Dockerfile geschrieben:

FROM supersoft:1.2.3
RUN a-command

Nach einer Weile ermittelt der Hersteller eine Schwachstelle und veröffentlicht Version 1.2.4 von SuperSoft, um das Problem zu beheben. In diesem Fall liegt es an Ihnen, sich über die Patches für SuperSoft zu informieren und Ihr Dockerfile entsprechend zu aktualisieren. Wenn Sie in Ihrem Dockerfile aber FROM supersoft:1.2 angeben, wird die neue Version automatisch abgerufen.

Es liegt letztendlich in Ihrer Verantwortung, das Tagging-System für jedes verwendete externe Image sorgfältig zu untersuchen, zu entscheiden, wie sehr Sie den Personen vertrauen, von denen diese Images erstellt werden, und ein zu verwendendes Tag auszuwählen.

Weitere Informationen

Referenzarchitekturen, Diagramme und Best Practices zu Google Cloud kennenlernen. Weitere Informationen zu Cloud Architecture Center