Der Autor hat Code.org ausgewählt, um eine Spende im Rahmen des Programms Write for DOnations zu erhalten.
Einführung
In einer Produktionsumgebung ist es mit Docker einfach, Anwendungen in Containern zu erstellen, bereitzustellen und auszuführen. Mit Containern können Entwickler Anwendungen und alle ihre Hauptanforderungen und Abhängigkeiten in einem einzigen Paket zusammenfassen, das Sie in ein Docker-Image verwandeln und replizieren können. Docker-Images werden aus Dockerfiles erstellt. Die Docker-Datei ist eine Datei, in der Sie definieren, wie das Image aussehen soll, welches Basisbetriebssystem es haben soll und welche Befehle darin ausgeführt werden sollen.
Große Docker-Images können die Zeit zum Erstellen und Senden von Images zwischen Clustern und Cloud-Anbietern verlängern. Wenn Sie beispielsweise jedes Mal, wenn einer Ihrer Entwickler einen Build auslöst, ein Image in Gigabyte-Größe zum Pushen haben, summiert sich der Durchsatz, den Sie in Ihrem Netzwerk erstellen, während des CI / CD-Prozesses, was Ihre Anwendung träge macht und letztendlich Ihre Ressourcen kostet . Aus diesem Grund sollten Docker-Images, die für die Produktion geeignet sind, nur das Nötigste installiert haben.
Es gibt verschiedene Möglichkeiten, die Größe von Docker-Bildern zu verringern, um sie für die Produktion zu optimieren. Zuallererst benötigen diese Images normalerweise keine Build-Tools, um ihre Anwendungen auszuführen. Sie müssen sie also überhaupt nicht hinzufügen. Mithilfe eines multi-stage build process können Sie mithilfe von Zwischenimages den Code kompilieren und erstellen, Abhängigkeiten installieren und alles in ein Paket packen Kopieren Sie dann die endgültige Version Ihrer Anwendung auf ein leeres Image ohne Build-Tools. Darüber hinaus können Sie ein Image mit einer winzigen Basis wie Alpine Linux verwenden. Alpine ist eine geeignete Linux-Distribution für die Produktion, da sie nur das Nötigste enthält, was Ihre Anwendung zum Ausführen benötigt.
In diesem Lernprogramm optimieren Sie Docker-Bilder in wenigen einfachen Schritten, sodass sie kleiner, schneller und besser für die Produktion geeignet sind. Sie erstellen Bilder für ein Beispiel Go API in verschiedenen Docker-Containern, beginnend mit Ubuntu und sprachspezifischen Bildern, und fahren dann mit Alpine fort Verteilung. Sie verwenden auch mehrstufige Builds, um Ihre Bilder für die Produktion zu optimieren. Das Endziel dieses Tutorials ist es, den Größenunterschied zwischen der Verwendung von Standard-Ubuntu-Images und optimierten Gegenstücken aufzuzeigen und den Vorteil von mehrstufigen Builds aufzuzeigen. Nachdem Sie dieses Tutorial gelesen haben, können Sie diese Techniken auf Ihre eigenen Projekte und CI / CD-Pipelines anwenden.
Voraussetzungen
Bevor Sie beginnen, benötigen Sie:
-
Ein Ubuntu 18.04-Server mit einem Nicht-Root-Benutzerkonto mit "+ sudo +" - Berechtigungen. Befolgen Sie unsere Initial Server Setup with Ubuntu 18.04 Anleitung. Obwohl dieses Tutorial auf Ubuntu 18.04 getestet wurde, können Sie viele der Schritte auf jeder Linux-Distribution ausführen.
-
Docker auf Ihrem Server installiert. Folgen Sie den Schritten 1 und 2 von Installation und Verwendung von Docker unter Ubuntu 18.04 für Installationsanweisungen.
Schritt 1 - Herunterladen der Sample Go-API
Bevor Sie Ihr Docker-Image optimieren können, müssen Sie zunächst die sample API herunterladen, aus der Sie Ihre Docker-Images erstellen. Die Verwendung einer einfachen Go-API zeigt alle wichtigen Schritte zum Erstellen und Ausführen einer Anwendung in einem Docker-Container. In diesem Tutorial wird Go verwendet, da es sich um eine kompilierte Sprache wie C++ oder Java handelt, die sich jedoch von diesen unterscheidet hat einen sehr kleinen Platzbedarf.
Beginnen Sie auf Ihrem Server mit dem Klonen der Beispiel-Go-API:
git clone https://github.com/do-community/mux-go-api.git
Sobald Sie das Projekt geklont haben, befindet sich auf Ihrem Server ein Verzeichnis mit dem Namen "+ mux-go-api w". Verschieben Sie mit + cd +
in dieses Verzeichnis:
cd mux-go-api
Dies ist das Ausgangsverzeichnis für Ihr Projekt. Sie erstellen Ihre Docker-Images aus diesem Verzeichnis. Im Inneren finden Sie den Quellcode für eine in Go geschriebene API in der Datei + api.go +
. Obwohl diese API minimal ist und nur wenige Endpunkte hat, ist sie für die Simulation einer produktionsbereiten API für die Zwecke dieses Lernprogramms geeignet.
Nachdem Sie die Beispiel-Go-API heruntergeladen haben, können Sie ein Basis-Ubuntu-Docker-Image erstellen, mit dem Sie die späteren, optimierten Docker-Images vergleichen können.
Schritt 2 - Ein Basis-Ubuntu-Image erstellen
Für Ihr erstes Docker-Image ist es hilfreich zu sehen, wie es aussieht, wenn Sie mit einem Basis-Ubuntu-Image beginnen. Dadurch wird Ihre Beispiel-API in eine Umgebung gepackt, die derjenigen ähnelt, die Sie bereits auf Ihrem Ubuntu-Server ausführen. Innerhalb des Images installieren Sie die verschiedenen Pakete und Module, die Sie zum Ausführen Ihrer Anwendung benötigen. Sie werden jedoch feststellen, dass dieser Prozess ein ziemlich umfangreiches Ubuntu-Image erstellt, das sich auf die Erstellungszeit und die Lesbarkeit des Codes Ihrer Docker-Datei auswirkt.
Beginnen Sie mit dem Schreiben einer Docker-Datei, die Docker anweist, ein Ubuntu-Image zu erstellen, Go zu installieren und die Beispiel-API auszuführen. Stellen Sie sicher, dass Sie das Dockerfile im Verzeichnis des geklonten Repos erstellen. Wenn Sie in das Ausgangsverzeichnis geklont haben, sollte es "+ $ HOME / mux-go-api" sein.
Erstellen Sie eine neue Datei mit dem Namen "+ Dockerfile.ubuntu". Öffne es in + nano +
oder deinem Lieblings-Texteditor:
nano ~/mux-go-api/Dockerfile.ubuntu
In dieser Docker-Datei definieren Sie ein Ubuntu-Image und installieren Golang. Anschließend installieren Sie die erforderlichen Abhängigkeiten und erstellen die Binärdatei. Fügen Sie den folgenden Inhalt zu + Dockerfile.ubuntu
hinzu:
~ / mux-go-api / Dockerfile.ubuntu
FROM ubuntu:18.04
RUN apt-get update -y \
&& apt-get install -y git gcc make golang-
ENV GOROOT /usr/lib/go-
ENV PATH $GOROOT/bin:$PATH
ENV GOPATH /root/go
ENV APIPATH /root/go/src/api
WORKDIR $APIPATH
COPY . .
RUN \
go get -d -v \
&& go install -v \
&& go build
EXPOSE 3000
CMD ["./api"]
Von oben beginnend gibt der Befehl + FROM +
an, welches Basisbetriebssystem das Image haben soll. Dann installiert der Befehl + RUN +
die Sprache Go während der Erstellung des Images. + ENV +
legt die spezifischen Umgebungsvariablen fest, die der Go-Compiler benötigt, um richtig zu funktionieren. "+ WORKDIR " gibt das Verzeichnis an, in das der Code kopiert werden soll, und der Befehl " COPY " übernimmt den Code aus dem Verzeichnis, in dem sich " Dockerfile.ubuntu " befindet, und kopiert ihn in das Bild. Der abschließende ' RUN'-Befehl installiert die Go-Abhängigkeiten, die der Quellcode benötigt, um die API zu kompilieren und auszuführen.
Speichern und schließen Sie die Datei. Jetzt können Sie den Befehl + build +
ausführen, um ein Docker-Image aus der soeben erstellten Docker-Datei zu erstellen:
docker build -f Dockerfile.ubuntu -t ubuntu .
Der Befehl + build +
erstellt ein Bild aus einer Docker-Datei. Das Flag "+ -f " gibt an, dass Sie aus der Datei " Dockerfile.ubuntu " erstellen möchten, während " -t " für "tag" steht. Dies bedeutet, dass Sie den Namen " ubuntu " verwenden. Der letzte Punkt steht für den aktuellen Kontext, in dem sich " Dockerfile.ubuntu +" befindet.
Dies wird eine Weile dauern, machen Sie also eine Pause. Sobald der Build abgeschlossen ist, haben Sie ein Ubuntu-Image, mit dem Sie Ihre API ausführen können. Die endgültige Größe des Bildes ist jedoch möglicherweise nicht ideal. Alles über ein paar hundert MB für diese API würde als zu großes Bild angesehen.
Führen Sie den folgenden Befehl aus, um alle Docker-Images aufzulisten und die Größe Ihres Ubuntu-Images zu ermitteln:
docker images
Sie erhalten eine Ausgabe mit dem soeben erstellten Bild:
OutputREPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 61b2096f6871 33 seconds ago
. . .
Wie in der Ausgabe hervorgehoben, hat dieses Bild eine Größe von * 636 MB * für eine grundlegende Golang-API, eine Zahl, die von Maschine zu Maschine leicht variieren kann. Bei mehreren Builds wirkt sich diese Größe erheblich auf die Bereitstellungszeiten und den Netzwerkdurchsatz aus.
In diesem Abschnitt haben Sie ein Ubuntu-Image mit allen erforderlichen Go-Tools und Abhängigkeiten erstellt, um die in Schritt 1 geklonte API auszuführen. Im nächsten Abschnitt verwenden Sie ein vorgefertigtes sprachspezifisches Docker-Image, um Ihre Docker-Datei zu vereinfachen und den Erstellungsprozess zu optimieren.
Schritt 3 - Erstellen eines sprachspezifischen Basisimages
Vorgefertigte Bilder sind gewöhnliche Basisbilder, die Benutzer mit situationsspezifischen Tools bearbeitet haben. Benutzer können diese Images dann in das Image-Repository Docker Hub verschieben, sodass andere Benutzer das freigegebene Image verwenden können, anstatt ihre eigenen Docker-Dateien schreiben zu müssen. Dies ist ein in Produktionssituationen üblicher Vorgang, und in Docker Hub finden Sie verschiedene vorgefertigte Images für nahezu jeden Anwendungsfall. In diesem Schritt erstellen Sie Ihre Beispiel-API mit einem Go-spezifischen Image, auf dem der Compiler und die Abhängigkeiten bereits installiert sind.
Mit vorgefertigten Basisimages, die bereits die Tools enthalten, die Sie zum Erstellen und Ausführen Ihrer App benötigen, können Sie die Erstellungszeit erheblich verkürzen. Da Sie mit einer Basis beginnen, auf der alle erforderlichen Tools vorinstalliert sind, können Sie das Hinzufügen dieser Tools zu Ihrer Docker-Datei überspringen, damit sie viel sauberer aussieht und letztendlich die Erstellungszeit verkürzt.
Erstellen Sie ein weiteres Dockerfile und nennen Sie es "+ Dockerfile.golang +". Öffne es in deinem Texteditor:
nano ~/mux-go-api/Dockerfile.golang
Diese Datei ist wesentlich übersichtlicher als die vorherige, da alle Go-spezifischen Abhängigkeiten, Tools und der Compiler vorinstalliert sind.
Fügen Sie nun die folgenden Zeilen hinzu:
~ / mux-go-api / Dockerfile.golang
FROM golang:
WORKDIR /go/src/api
COPY . .
RUN \
go get -d -v \
&& go install -v \
&& go build
EXPOSE 3000
CMD ["./api"]
Wenn Sie von oben beginnen, werden Sie feststellen, dass die "+ FROM " - Anweisung jetzt " golang: +" lautet. Dies bedeutet, dass Docker ein vorgefertigtes Go-Image von Docker Hub abruft, auf dem bereits alle erforderlichen Go-Tools installiert sind.
Erstellen Sie jetzt erneut das Docker-Image mit:
docker build -f Dockerfile.golang -t golang .
Überprüfen Sie die endgültige Größe des Bildes mit dem folgenden Befehl:
docker images
Dies ergibt eine Ausgabe ähnlich der folgenden:
OutputREPOSITORY TAG IMAGE ID CREATED SIZE
golang latest eaee5f524da2 40 seconds ago
. . .
Obwohl das Dockerfile selbst effizienter und die Erstellungszeit kürzer ist, hat sich die Gesamtgröße des Image tatsächlich erhöht. Das vorgefertigte Golang-Image ist mit etwa * 744MB * eine beachtliche Größe.
Dies ist die bevorzugte Methode zum Erstellen von Docker-Images. Sie erhalten ein Basis-Image, das von der Community als Standard für die angegebene Sprache genehmigt wurde, in diesem Fall Go. Um ein Bild für die Produktion vorzubereiten, müssen Sie jedoch Teile ausschneiden, die von der ausgeführten Anwendung nicht benötigt werden.
Denken Sie daran, dass die Verwendung dieser schweren Bilder in Ordnung ist, wenn Sie sich über Ihre Anforderungen nicht sicher sind. Fühlen Sie sich frei, sie sowohl als Wegwerfbehälter als auch als Basis für die Erstellung anderer Bilder zu verwenden. Für Entwicklungs- oder Testzwecke, bei denen Sie nicht über das Senden von Bildern über das Netzwerk nachdenken müssen, ist es völlig in Ordnung, schwere Bilder zu verwenden. Wenn Sie jedoch Bereitstellungen optimieren möchten, müssen Sie Ihr Bestes geben, um Ihre Images so klein wie möglich zu gestalten.
Nachdem Sie ein sprachspezifisches Image getestet haben, können Sie mit dem nächsten Schritt fortfahren, in dem Sie die schlanke Alpine Linux-Distribution als Basisimage verwenden, um Ihr Docker-Image aufzuhellen.
Schritt 4 - Aufbau von Base Alpine Images
Einer der einfachsten Schritte zur Optimierung Ihrer Docker-Bilder ist die Verwendung kleinerer Basisbilder. Alpine ist eine schlanke Linux-Distribution, die auf Sicherheit und Ressourceneffizienz ausgelegt ist. Das Alpine Docker-Image verwendet musl libc und BusyBox, um kompakt zu bleiben und benötigt nicht mehr als 8 MB in einem Container, um ausgeführt zu werden . Die winzige Größe ist darauf zurückzuführen, dass Binärpakete ausgedünnt und aufgeteilt werden, wodurch Sie mehr Kontrolle über Ihre Installation haben und die Umgebung so klein und effizient wie möglich bleibt.
Das Erstellen eines Alpine-Images ähnelt dem Erstellen des Ubuntu-Images in Schritt 2. Erstellen Sie zunächst eine neue Datei mit dem Namen "+ Dockerfile.alpine +":
nano ~/mux-go-api/Dockerfile.alpine
Fügen Sie nun diesen Ausschnitt hinzu:
~ / mux-go-api / Dockerfile.alpine
FROM alpine:
RUN apk add --no-cache \
ca-certificates \
git \
gcc \
musl-dev \
openssl \
go
ENV GOPATH /go
ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
ENV APIPATH $GOPATH/src/api
RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" "$APIPATH" && chmod -R 777 "$GOPATH"
WORKDIR $APIPATH
COPY . .
RUN \
go get -d -v \
&& go install -v \
&& go build
EXPOSE 3000
CMD ["./api"]
Hier fügen Sie den Befehl "+ apk add +" hinzu, um mit dem Paket-Manager von Alpine Go und alle erforderlichen Bibliotheken zu installieren. Wie beim Ubuntu-Image müssen Sie auch die Umgebungsvariablen festlegen.
Mach weiter und baue das Image:
docker build -f Dockerfile.alpine -t alpine .
Überprüfen Sie noch einmal die Bildgröße:
docker images
Sie erhalten eine Ausgabe ähnlich der folgenden:
OutputREPOSITORY TAG IMAGE ID CREATED SIZE
alpine latest ee35a601158d 30 seconds ago
. . .
Die Größe ist auf ca. * 426MB * gesunken.
Die geringe Größe des alpinen Basisbilds hat die endgültige Bildgröße verringert. Sie können jedoch noch einige weitere Maßnahmen ergreifen, um es noch kleiner zu machen.
Versuchen Sie als Nächstes, ein vorgefertigtes Alpenbild für Go zu verwenden. Dadurch wird das Dockerfile kürzer und das endgültige Bild verkleinert. Da das vorgefertigte Alpine-Image für Go mit aus dem Quellcode kompiliertem Go erstellt wird, ist der Platzbedarf erheblich geringer.
Erstellen Sie zunächst eine neue Datei mit dem Namen "++":
nano ~/mux-go-api/Dockerfile.golang-alpine
Fügen Sie der Datei den folgenden Inhalt hinzu:
~ / mux-go-api / Dockerfile.golang-alpine
FROM golang:
RUN apk add --no-cache --update git
WORKDIR /go/src/api
COPY . .
RUN go get -d -v \
&& go install -v \
&& go build
EXPOSE 3000
CMD ["./api"]
Die einzigen Unterschiede zwischen "+ Dockerfile.golang-alpine " und " Dockerfile.alpine " sind der Befehl " FROM " und der erste Befehl " RUN ". Jetzt gibt der Befehl ` FROM ` ein ` golang ` Image mit dem Tag `+` an, und + RUN +
enthält nur einen Befehl zum Installieren von Git. Sie benötigen Git, damit der Befehl + go get
im zweiten Befehl` + RUN + am unteren Rand von
+ Dockerfile.golang-alpine` funktioniert.
Erstellen Sie das Image mit dem folgenden Befehl:
docker build -f Dockerfile.golang-alpine -t golang-alpine .
Rufen Sie Ihre Bilderliste ab:
docker images
Sie erhalten folgende Ausgabe:
OutputREPOSITORY TAG IMAGE ID CREATED SIZE
golang-alpine latest 97103a8b912b 49 seconds ago
Jetzt ist die Bildgröße auf ca. * 288MB * gesunken.
Obwohl Sie es geschafft haben, die Größe erheblich zu reduzieren, können Sie noch eine letzte Maßnahme ergreifen, um das Bild für die Produktion vorzubereiten. Es handelt sich um einen mehrstufigen Build. Wenn Sie mehrstufige Builds verwenden, können Sie ein Image zum Erstellen der Anwendung verwenden, während Sie ein anderes, leichteres Image zum Packen der kompilierten Anwendung für die Produktion verwenden. Dieser Prozess wird im nächsten Schritt ausgeführt.
Schritt 5 - Ausschließen von Build-Tools mit einem mehrstufigen Build
Im Idealfall sollten auf Images, die in der Produktion ausgeführt werden, keine Build-Tools oder Abhängigkeiten installiert sein, die für die Ausführung der Produktionsanwendung redundant sind. Sie können diese aus dem endgültigen Docker-Image entfernen, indem Sie mehrstufige Builds verwenden. Dies funktioniert, indem die Binär- oder anders ausgedrückt, die kompilierte Go-Anwendung in einem Zwischencontainer erstellt und dann in einen leeren Container kopiert wird, der keine unnötigen Abhängigkeiten aufweist.
Beginnen Sie mit der Erstellung einer weiteren Datei mit dem Namen "++":
nano ~/mux-go-api/Dockerfile.multistage
Was Sie hier hinzufügen, ist bekannt. Beginnen Sie mit dem exakt gleichen Code wie mit + Dockerfile.golang-alpine +
. Fügen Sie diesmal jedoch auch ein zweites Bild hinzu, in das Sie die Binärdatei aus dem ersten Bild kopieren.
~ / mux-go-api / Dockerfile.multistage
FROM golang:1.10-alpine3.8
RUN apk add --no-cache --update git
WORKDIR /go/src/api
COPY . .
RUN go get -d -v \
&& go install -v \
&& go build
##
FROM alpine:3.8
COPY /go/bin/api /go/bin/
EXPOSE 3000
CMD ["/go/bin/api"]
Speichern und schließen Sie die Datei. Hier haben Sie zwei + FROM +
Befehle. Das erste ist identisch mit "+ Dockerfile.golang-alpine ", außer dass das " FROM " - Kommando ein zusätzliches " AS multistage " enthält. Dies gibt ihm den Namen " mehrstufig ", auf den Sie dann im unteren Teil der Datei " Dockerfile.multistage " verweisen. Im zweiten " FROM" -Befehl nehmen Sie ein Basis-Alpen-Bild und "+ COPY +" über die kompilierte Go-Anwendung aus dem mehrstufigen Bild. Durch diesen Vorgang wird das endgültige Bild weiter verkleinert und für die Produktion vorbereitet.
Führen Sie den Build mit dem folgenden Befehl aus:
docker build -f Dockerfile.multistage -t prod .
Überprüfen Sie jetzt die Bildgröße, nachdem Sie einen mehrstufigen Build verwendet haben.
docker images
Sie finden zwei neue Bilder anstelle von nur einem:
OutputREPOSITORY TAG IMAGE ID CREATED SIZE
prod latest 82fc005abc40 38 seconds ago
<none> <none> d7855c8f8280 38 seconds ago
. . .
Das + <keine> +
Bild ist das + mehrstufige +
Bild, das mit dem Befehl + FROM golang: 1.10-alpine3.8 +
erstellt wurde. Es ist nur ein Vermittler, der zum Erstellen und Kompilieren der Go-Anwendung verwendet wird, während das "+ prod +" - Image in diesem Kontext das endgültige Image ist, das nur die kompilierte Go-Anwendung enthält.
Von anfänglichen * 744MB * haben Sie jetzt die Bildgröße auf ca. * 11,3MB * verringert. Ein winziges Image wie dieses zu verfolgen und über das Netzwerk an Ihre Produktionsserver zu senden, ist viel einfacher als mit einem Image von über 700 MB und spart Ihnen auf lange Sicht erhebliche Ressourcen.
Fazit
In diesem Lernprogramm haben Sie Docker-Images für die Produktion unter Verwendung verschiedener Docker-Basisimages und eines Zwischenimages optimiert, um den Code zu kompilieren und zu erstellen. Auf diese Weise haben Sie Ihre Beispiel-API auf die kleinstmögliche Größe verpackt. Mit diesen Techniken können Sie die Erstellungs- und Bereitstellungsgeschwindigkeit Ihrer Docker-Anwendungen und aller CI / CD-Pipelines verbessern.
Wenn Sie mehr über das Erstellen von Anwendungen mit Docker erfahren möchten, lesen Sie unsere How To Erstellen Sie eine Node.js-Anwendung mit Docker. Weitere Informationen zum Optimieren von Containern finden Sie unter Building Optimized Containers for Kubernetes.