GNU/Linux >> LINUX-Kenntnisse >  >> Panels >> Docker

So reduzieren Sie die Docker-Image-Größe:6 Optimierungsmethoden

Wenn Sie die Größe des Docker-Bildes reduzieren möchten , müssen Sie beim Erstellen eines Docker-Images die standardmäßigen Best Practices anwenden.

Dieser Blog spricht über verschiedene Optimierungstechniken, die Sie schnell implementieren können, um das kleinste und minimale Docker-Image zu erstellen . Wir werden uns auch einige der besten Tools für die Docker-Image-Optimierung ansehen .

Docker als Container-Engine macht es einfach, ein Stück Code zu nehmen und es in einem Container auszuführen. Es ermöglicht Ingenieuren, alle Code-Abhängigkeiten und Dateien an einem einzigen Ort zu sammeln, der überall schnell und einfach ausgeführt werden kann.

Das gesamte Konzept von „Run Anywhere“-Images beginnt mit einer einfachen Konfigurationsdatei namens Dockerfile. Zuerst fügen wir alle Build-Anweisungen wie Code-Abhängigkeiten, Befehle und Basis-Image-Details in Dockerfile hinzu.

Notwendigkeit für Docker-Image-Optimierung

Obwohl der Docker-Build-Prozess einfach ist, machen viele Organisationen den Fehler, aufgeblähte Docker-Images zu erstellen ohne die Container-Images zu optimieren.

Bei der typischen Softwareentwicklung hat jeder Dienst mehrere Versionen/Releases, und jede Version erfordert mehr Abhängigkeiten, Befehle und Konfigurationen. Dies führt wie jetzt zu einer Herausforderung beim Erstellen von Docker-Images – derselbe Code erfordert mehr Zeit und Ressourcen, um erstellt zu werden, bevor er als Container versendet werden kann.

Ich habe Fälle gesehen, in denen das anfängliche Anwendungs-Image mit 350 MB begann und im Laufe der Zeit auf über 1,5 GB anwuchs.

Außerdem erhöhen wir durch die Installation unerwünschter Bibliotheken die Wahrscheinlichkeit eines potenziellen Sicherheitsrisikos, indem wir die Angriffsfläche vergrößern.

Daher müssen DevOps-Ingenieure die Docker-Images optimieren um sicherzustellen, dass das Docker-Image nach dem Erstellen von Anwendungen oder zukünftigen Versionen nicht aufgebläht wird. Nicht nur für Produktionsumgebungen, in jeder Phase des CI/CD-Prozesses sollten Sie Ihre Docker-Images optimieren.

Außerdem ist es bei Container-Orchestrierungstools wie Kubernetes am besten, kleine Images zu haben, um die Image-Übertragung und die Bereitstellungszeit zu verkürzen .

Wie kann ich die Docker-Image-Größe reduzieren?

Wenn wir ein Container-Image einer typischen Anwendung nehmen, enthält es ein Basis-Image, Abhängigkeiten/Dateien/Konfigurationen , und cruft (unerwünschte Software).

Es läuft also alles darauf hinaus, wie effizient wir diese Ressourcen innerhalb des Container-Images verwalten können.

Sehen wir uns verschiedene etablierte Methoden zur Optimierung von Docker-Images an Darüber hinaus haben wir praktische Beispiele gegeben, um die Docker-Bildoptimierung in Echtzeit zu verstehen.

Entweder Sie verwenden die im Artikel angegebenen Beispiele oder probieren die Optimierungstechniken an vorhandenen Dockerfiles aus.

Im Folgenden sind die Methoden aufgeführt, mit denen wir eine Docker-Image-Optimierung erreichen können.

  1. Verwendung distroloser/minimaler Basis-Images
  2. Mehrstufige Builds
  3. Minimierung der Anzahl der Ebenen
  4. Caching verstehen
  5. Mit Dockerignore
  6. Aufbewahrung von Anwendungsdaten an anderer Stelle

Docker-Übungsdateien: Alle in diesem Artikel verwendeten Anwendungscodes, Dockerfiles und Konfigurationen werden in diesem Github-Repository gehostet. Sie können es klonen und dem Tutorial folgen.

Methode 1:Minimale Basisbilder verwenden

Ihr erster Fokus sollte auf der Auswahl des richtigen Basis-Images mit minimalem Betriebssystem-Fußabdruck liegen.

Ein solches Beispiel sind alpine Basisbilder. Alpenbilder können so klein wie 5,59 MB sein . Es ist nicht nur klein; es ist auch sehr sicher.

alpine       latest    c059bfaa849c     5.59MB

Nginx Alpine das Basis-Image ist nur 22 MB groß.

Standardmäßig wird es mit der sh-Shell geliefert, die beim Debuggen des Containers hilft, indem sie ihn anhängt.

Sie können die Basis-Image-Größe weiter reduzieren, indem Sie Distroless-Images verwenden. Es ist eine abgespeckte Version des Betriebssystems. Distroless-Basisimages sind für Java, nodejs, Python, Rust usw. verfügbar.

Distroless-Images sind so minimal, dass sie nicht einmal eine Hülle enthalten . Sie fragen sich vielleicht, wie debuggen wir dann Anwendungen? Sie haben die Debug-Version desselben Bildes, das mit der Busybox zum Debuggen geliefert wird.

Außerdem haben die meisten Distributionen jetzt ihre minimalen Basis-Images.

Hinweis: Sie können die öffentlich verfügbaren Basisimages nicht direkt in Projektumgebungen verwenden. Sie müssen die Genehmigung des Unternehmenssicherheitsteams einholen, um das Basisimage zu verwenden. In einigen Organisationen veröffentlicht das Sicherheitsteam selbst jeden Monat Basis-Images nach dem Testen und Sicherheitsscannen. Diese Bilder wären im privaten Docker-Repository der gemeinsamen Organisation verfügbar.

Methode 2:Docker Multistage Builds verwenden

Das mehrstufige Build-Muster wurde aus dem Konzept des Builder-Musters entwickelt, bei dem wir verschiedene Dockerfiles zum Erstellen und Packen des Anwendungscodes verwenden. Auch wenn dieses Muster hilft, die Bildgröße zu reduzieren, verursacht es wenig Overhead, wenn es um das Erstellen von Pipelines geht.

Beim mehrstufigen Build erhalten wir ähnliche Vorteile wie beim Builder-Muster. Wir verwenden Zwischenbilder (Build-Stufen) zum Kompilieren von Code, Installieren von Abhängigkeiten und Packen von Dateien in diesem Ansatz. Die Idee dahinter ist, unerwünschte Schichten zu eliminieren im Bild.

Danach werden nur die zum Ausführen der Anwendung erforderlichen App-Dateien auf ein anderes Image kopiert, das nur die erforderlichen Bibliotheken enthält, d. h. leichter zum Ausführen der Anwendung.

Lassen Sie uns dies anhand eines praktischen Beispiels in Aktion sehen, in dem wir eine einfache Nodejs-Anwendung erstellen und ihr Dockerfile optimieren.

Lassen Sie uns zuerst den Code erstellen. Wir haben die folgende Ordnerstruktur.

├── Dockerfile1
├── Dockerfile2
├── env
├── index.js
└── package.json

Speichern Sie Folgendes als index.js .

const dotenv=require('dotenv'); 
dotenv.config({ path: './env' });

dotenv.config();

const express=require("express");
const app=express();

app.get('/',(req,res)=>{
  res.send(`Learning to Optimize Docker Images with DevOpsCube!`);
});


app.listen(process.env.PORT,(err)=>{
    if(err){
        console.log(`Error: ${err.message}`);
    }else{
        console.log(`Listening on port ${process.env.PORT}`);
    }
  }
)

Speichern Sie Folgendes als package.json .

{
  "name": "nodejs",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "dotenv": "^10.0.0",
    "express": "^4.17.2"
  }
}

Speichern Sie die folgende Portvariable in einer Datei namens env .

PORT=8080

Ein einfaches Dockerfile für diese Anwendung möchte dies – Speichern Sie es als Dockerfile1 .

FROM node:16
COPY . .
RUN npm installEXPOSE 3000
CMD [ "node", "index.js" ]

Sehen wir uns den Speicherplatz an, den es benötigt, indem wir es bauen.

docker build -t devopscube/node-app:1.0 --no-cache -f Dockerfile1 .

Nachdem der Bau abgeschlossen ist. Lassen Sie uns seine Größe mit –

überprüfen
docker image ls

Das bekommen wir.

devopscube/node-app   1.0       b15397d01cca   22 seconds ago   910MB

Die Größe beträgt also 910MBs .

Lassen Sie uns nun diese Methode verwenden, um einen mehrstufigen Build zu erstellen.

Wir verwenden node:16 als Basis-Image, d. h. das Image für alle Abhängigkeiten und Modulinstallationen Danach verschieben wir den Inhalt in ein minimales und leichteres „alpine ‘basiertes Bild. Die „alpine Das Bild hat nur das Nötigste und ist daher sehr leicht.

Hier ist eine bildliche Darstellung eines mehrstufigen Docker-Builds.

Auch in einem einzigen Dockerfile , können Sie mehrere Phasen mit unterschiedlichen Basisimages haben. Beispielsweise können Sie verschiedene Phasen für Build, Test, statische Analyse und Paketierung haben mit unterschiedlichen Basisbildern.

Mal sehen, wie das neue Dockerfile aussehen könnte. Wir kopieren nur die notwendigen Dateien vom Basis-Image auf das Haupt-Image.

Speichern Sie Folgendes als Dockerfile2 .

FROM node:16 as build

WORKDIR /app
COPY package.json index.js env ./
RUN npm install

FROM node:alpine as main

COPY --from=build /app /
EXPOSE 8080
CMD ["index.js"]

Sehen wir uns den Speicherplatz an, den es benötigt, indem wir es bauen.

docker build -t devopscube/node-app:2.0 --no-cache -f Dockerfile2 .

Nachdem der Bau abgeschlossen ist. Lassen Sie uns seine Größe mit

überprüfen
docker image ls

Das bekommen wir.

devopscube/node-app   2.0       fa6ae75da252   32 seconds ago   171MB

Die neue reduzierte Bildgröße beträgt also 171 MB im Vergleich zum Image mit allen Abhängigkeiten.

Das ist eine Optimierung von über 80 %!

Hätten wir jedoch dasselbe Basis-Image wie in der Build-Phase verwendet, würden wir keinen großen Unterschied feststellen.

Sie können die Bildgröße mit distrolosen Bildern weiter reduzieren . Hier ist das gleiche Dockerfile mit einem mehrstufigen Build-Schritt, der das distroless-Image von Google NodeJS anstelle von Alpine verwendet.

FROM node:16 as build

WORKDIR /app
COPY package.json index.js env ./
RUN npm install

FROM gcr.io/distroless/nodejs

COPY --from=build /app /
EXPOSE 3000
CMD ["index.js"]

Wenn Sie das obige Dockerfile erstellen, wird Ihr Image 118 MB groß sein ,

devopscube/distroless-node   1.0       302990bc5e76     118MB

Methode 3:Minimieren Sie die Anzahl der Ebenen

Docker-Images funktionieren folgendermaßen – jeweils RUN, COPY, FROM Dockerfile-Anweisungen fügen eine neue Ebene hinzu und jede Ebene verlängert die Build-Ausführungszeit und erhöht die Speicheranforderungen des Bildes.

Lassen Sie uns dies anhand eines praktischen Beispiels in Aktion sehen:Lassen Sie uns ein Ubuntu-Image mit aktualisierten und aktualisierten Bibliotheken erstellen, zusammen mit einigen erforderlichen installierten Paketen wie vim, net-tools, dnsutils.

Ein Dockerfile um dies zu erreichen, wäre wie folgt – Speichern Sie dies als Dockerfile3 .

FROM ubuntu:latest
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update -y
RUN apt-get upgrade -y
RUN apt-get install vim -y
RUN apt-get install net-tools -y
RUN apt-get install dnsutils -y

Wir möchten auch die Erstellungszeit für dieses Image untersuchen.

Der Docker-Daemon hat eine eingebaute Fähigkeit, die Gesamtausführungszeit anzuzeigen, die eine Docker-Datei benötigt.

Führen Sie die folgenden Schritte aus, um diese Funktion zu aktivieren –

  1. Erstellen Sie eine daemon.json Datei mit folgendem Inhalt unter /etc/docker/
{
  "experimental": true
}

2. Führen Sie den folgenden Befehl aus, um die Funktion zu aktivieren.

export DOCKER_BUILDKIT=1

Bauen wir es und sehen uns die Speicher- und Bauzeit an.

time docker build -t devopscube/optimize:3.0 --no-cache -f Dockerfile3 .

Es würde die Ausführungszeiten im Terminal anzeigen.

time docker build -t devopscube/optimize:3.0 --no-cache -f Dockerfile3 .


[+] Building 117.1s (10/10) FINISHED                                                                   
 => [internal] load build definition from Dockerfile                                              
.
.
.
.                                                                       
 => => writing image sha256:9601bcac010062c656dacacbc7c554b8ba552c7174f32fdcbd24ff9c7482a805      0.0s 
 => => naming to docker.io/devopscube/optimize:3.0                                                0.0s 
                                                                                                       
real    1m57.219s                                                                                      
user	0m1.062s
sys	0m0.911s

Nachdem der Build abgeschlossen ist, beträgt die Ausführungszeit 117,1 Sekunden .

Lassen Sie uns seine Größe mit

überprüfen
docker image ls

Das bekommen wir.

devopscube/optimize  3.0   9601bcac0100   About a minute ago   227MB

Die Größe beträgt also 227 MB .

Kombinieren wir die RUN-Befehle in einer einzigen Ebene und speichern Sie sie als Dockerfile4.

FROM ubuntu:latest
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update -y && apt-get upgrade -y && apt-get install --no-install-recommends vim net-tools dnsutils -y

Im obigen RUN-Befehl haben wir --no-install-recommends verwendet Flag, um empfohlene Pakete zu deaktivieren. Es wird immer dann empfohlen, wenn Sie install verwenden in Ihren Dockerfiles

Sehen wir uns die Speicher- und Bauzeit an, die für den Bau erforderlich ist.

time docker build -t devopscube/optimize:4.0 --no-cache -f Dockerfile4 .

Es würde die Ausführungszeiten im Terminal anzeigen.

time docker build -t devopscube/optimize:0.4 --no-cache -f Dockerfile4 .


[+] Building 91.7s (6/6) FINISHED                                                                      
 => [internal] load build definition from Dockerfile2                                             0.4s
.
.
.  
 => => naming to docker.io/devopscube/optimize:4.0                                                0.0s 
                                                                                                       
real    1m31.874s                                                                                      
user	0m0.884s
sys	0m0.679s

Nachdem der Build abgeschlossen ist, beträgt die Ausführungszeit 91,7 Sekunden.

Lassen Sie uns seine Größe mit

überprüfen
docker image ls

Das bekommen wir.

devopscube/optimize  4.0   37d746b976e3   42 seconds ago      216MB

Die Größe beträgt also 216 MB.

Mithilfe dieser Optimierungstechnik wurde die Ausführungszeit von 117,1 s auf 91,7 s und die Speichergröße von 227 MB auf 216 MB reduziert.

Methode 4:Caching verstehen

Oft muss das gleiche Image mit geringfügigen Änderungen im Code immer wieder neu erstellt werden.

Docker hilft in solchen Fällen, indem es den Cache jeder Schicht eines Builds speichert, in der Hoffnung, dass es in Zukunft nützlich sein könnte.

Aufgrund dieses Konzepts wird empfohlen, die Zeilen, die zum Installieren von Abhängigkeiten und Paketen verwendet werden, früher in die Dockerdatei einzufügen – vor den COPY-Befehlen.

Der Grund dafür ist, dass Docker das Bild mit den erforderlichen Abhängigkeiten zwischenspeichern kann und dieser Cache dann in den folgenden Builds verwendet werden kann, wenn der Code geändert wird.

Schauen wir uns zum Beispiel die folgenden beiden Dockerfiles an.

Dockerfile5

FROM ubuntu:latest
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update -y
RUN apt-get upgrade -y
RUN apt-get install vim -y
RUN apt-get install net-tools -y
RUN apt-get install dnsutils -y
COPY . .

Dockerfile6

FROM ubuntu:latest
COPY . .
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update -y
RUN apt-get upgrade -y
RUN apt-get install vim -y
RUN apt-get install net-tools -y
RUN apt-get install dnsutils -y

Docker könnte die Cache-Funktionalität besser nutzen mit Dockerfile6 als Dockerfile5 aufgrund der besseren Platzierung des COPY-Befehls.

Methode 5:Verwenden Sie Dockerignore

In der Regel müssen nur die notwendigen Dateien über das Docker-Image kopiert werden.

Docker kann die im Arbeitsverzeichnis vorhandenen Dateien ignorieren, wenn dies in .dockerignore konfiguriert ist Datei.

Diese Funktion sollte bei der Optimierung des Docker-Images berücksichtigt werden.

Methode 6:Anwendungsdaten an anderer Stelle aufbewahren

Durch das Speichern von Anwendungsdaten im Bild wird die Größe der Bilder unnötig erhöht.

Es wird dringend empfohlen, die Volume-Funktion der Containerlaufzeiten zu verwenden, um das Image von den Daten getrennt zu halten.

Docker-Image-Optimierungstools

Im Folgenden sind einige der Open-Source-Tools aufgeführt, die Ihnen bei der Optimierung helfen werden

  1. Tauchen :Es ist ein Image-Explorer-Tool, mit dem Sie Ebenen in den Docker- und OCI-Container-Images entdecken können. Mit dive finden Sie Möglichkeiten, Ihre Docker-Images zu optimieren. Weitere Informationen finden Sie im Dive Github-Repo.
  2. Docker Slim: Es hilft Ihnen, Ihre Docker-Images hinsichtlich Sicherheit und Größe zu optimieren. Weitere Informationen finden Sie im Docker Slim Github-Repo.

Ich werde dieser Liste weitere Tools hinzufügen.


Docker
  1. So verschieben Sie Docker-Images zwischen Hosts

  2. So verwenden Sie ein Dockerfile zum Erstellen eines Docker-Images

  3. So teilen Sie Docker-Images mit anderen

  4. So ändern Sie Docker-Images

  5. So aktualisieren/fügen Sie eine Datei im Docker-Image hinzu

So erstellen Sie ein Docker-Image aus einem laufenden Container

So reduzieren Sie die Docker-Image-Größe in Docker-Containern

So aktualisieren Sie Docker-Images auf die neueste Version

So verwenden Sie Docker Commit zum Ändern von Container-Images

So erstellen Sie ein benutzerdefiniertes Docker-Image mit Dockerfile

So listen / suchen / ziehen Sie Docker-Images unter Linux