Zum Hauptinhalt springen

Übung 3 - Multistage

Multistage Builds​

Wir erstellen einen einfachen Webserver mithilfe von NodeJs und ExpressJs. Wenn eine (Web)-Applikation "dockerisiert" wird, gibt es immer zwei Schritte.

  1. Im Ersten wird die Applikation gebaut (Pakete installiert, kompiliert und Co.).
    • Dafür braucht es häufig zusätzliche Pakete, welche nur zum Kompilieren gebraucht werden.
  2. Im zweiten Schritt wird die fertige Applikation gestartet.
    • Dafür braucht es in der Regel nur noch die Programmiersprache, ohne zusätzliche Pakete, auf dem System.

Um den Nutzen zu veranschaulichen wird die Applikation zuerst als einfaches Dockerfile "containerisiert". Danach wird das Dockerfile mit einem weiteren "Build-Stage" erweitert, sodass das gebaute Image, nur noch die absolut nötigsten Pakete beinhaltet.

Diese zwei Images werden schlussendlich verglichen.

Schritte​

  • Erstellen Sie eine Datei mit dem Namen package.json und kopieren Sie den untenstehenden Inhalt hinein. In dieser Datei werden die Abhängigkeiten und weiteres gespeichert.
package.json
{
"name": "multistage-node-example",
"version": "1.0.0",
"description": "A simple Node.js web server using Express.js",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.17.1"
}
}
  • Kreieren Sie die Datei für den Server mit dem Namen server.js und kopieren Sie folgenden Code hinein:
server.js
const express = require("express");
const app = express();
const port = 3000;

app.get("/", (req, res) => {
res.send(
"Hello, World! This is a simple Node.js web server using Express.js."
);
});

app.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`);
});
  • Nun erstellen wir das Dockerfile, in diesem Fall noch nicht als Multistage Build.
Dockerfile
FROM node:24
WORKDIR /app
# Copy package.json and package-lock.json
COPY package*.json ./
# Copy the rest of the application code
COPY server.js .
# Install dependencies
RUN npm install
# Expose the port the app runs on
EXPOSE 3000
# Command to run the application
CMD ["npm", "start"]
  • Bilden Sie das Image mit dem Tag -t security:singlestage
  • Erstellen Sie einen Container mit folgendem Befehl und testen Sie den Webserver, indem Sie auf http://localhost:3000 gehen.
    • docker run --rm -p 3000:3000 --name singlestage security:singlestage
  • Stoppen Sie den Container wieder mit docker stop singlestage.
  • Nun optimieren wir das Image, indem wir ein Multistage Build kreieren. Das heisst, wir verlagern die Installation der Abhängigkeiten in einen separaten Build Stage.
    • Kreieren Sie eine Datei mit dem Namen Dockerfile.multistage und kopieren Sie den folgenden Inhalt hinein.
Dockerfile.multistage
# Stage 1: Build stage
FROM node:24 AS builder
WORKDIR /app
COPY package*.json ./
# Installiert die Pakete
RUN npm install
COPY . .

# Stage 2: Production stage
FROM node:24-slim
WORKDIR /app
# Use USER node with limited rights
USER node
# Nur die nötigen Dateien vom Stage "builder" kopieren
COPY --from=builder /app .
EXPOSE 3000
CMD ["npm", "start"]
  • Bilden Sie das Image mit dem Tag mit dem Befehl docker build -f Dockerfile.multistage -t security:multistage ..
    • Mit -f Dockerfile.multistage geben wir an, welches Dockerfile verwendet werden soll. Wenn nichts angegeben, sucht er ein Dockerfile ohne Endung.
  • Erstellen Sie einen Container mit folgendem Befehl und testen Sie den Webserver, indem Sie auf http://localhost:3000 gehen.
    • docker run --rm -p 3000:3000 --name multistage security:multistage
    • Achtung: wenn der Container "singlestage" noch läuft, gibt es einen Portkonflikt!
  • Vergleichen Sie die beiden Dockerfiles.
  • Vergleichen Sie die beiden Images auf Grösse und Layers.
    • docker inspect security:singlestage
    • docker inspect security:multistage
tipp
  • Multistage Images sind nicht nur kleiner und dadurch performanter. Da sie weniger Abhängigkeiten beinhalten sind sie auch sicherer!

Quelle