Heim >Web-Frontend >js-Tutorial >Dockerisieren einer Next.js-Anwendung mithilfe eines Standalone-Builds
Docker hat in den letzten Jahren an Popularität gewonnen, da es die Platzierung von Anwendungen in Containern ermöglicht. Diese Container können in jeder Umgebung bereitgestellt werden und funktionieren in allen auf die gleiche Weise und sorgen für ein einheitliches Verhalten, unabhängig von der Plattform, auf der die Anwendung ausgeführt wird. Diese Container verwenden Bilder, bei denen es sich um eine Kopie oder einen komprimierten Schnappschuss der Anwendung handelt. Durch die Platzierung in einem Container werden sie genau so angezeigt, wie sie sind. Dies ist eine dieser Technologien, nach denen einige verzweifelt gesucht haben, während andere erst erkennen, dass sie sie brauchen, wenn sie davon hören.
Next.js ist seinerseits das beliebteste React-Framework. Wie bei jeder anderen JavaScript-Anwendung, die einen Bundler wie Webpack oder Vite verwendet, wird für die Produktion eine kompilierte Version des Projekts verwendet. Dies wird als Build bezeichnet. Ziel eines Builds ist es, die Mindestmenge an Code bereitzustellen, die erforderlich ist, damit die Anwendung genauso funktioniert wie in der Entwicklung. Dadurch wird sichergestellt, dass JavaScript-Dateien sehr leichtgewichtig sind, sodass der Browser sie in kürzester Zeit abrufen und interpretieren kann, um die Benutzeroberfläche darzustellen oder alle Aufgaben auszuführen, die die Anwendung erfordert.“
Next.js bietet insbesondere eine Version an, die die Build-Größe weiter reduziert: den Standalone Build. Wenn wir Docker verwenden, um ein Image für unsere Next.js-Anwendung zu erstellen, können wir die großartige Anwendung, die wir erstellt haben, problemlos in jeder Umgebung bereitstellen, ohne uns Gedanken über Kompatibilität oder zusätzliche Konfigurationen machen zu müssen. In diesem Artikel erfahren Sie, wie Sie dies erreichen.
In meinem Fall verwende ich gerne pnpm, um die Festplattengröße des Ordners „node_modules“ zu reduzieren. Daher verwendet das Beispiel des Next.js-Docker-Images diesen Paketmanager, Sie können jedoch geringfügige Anpassungen vornehmen, um bei Bedarf npm oder Yarn zu verwenden.
In der Datei next.config.js müssen wir angeben, dass der resultierende Build-Typ eigenständig sein wird, wenn die Anwendung für die Produktion kompiliert wird. Dazu müssen wir Folgendes einschließen:
/** @type {import('next').NextConfig} */ const nextConfig = { output: "standalone" }; export default nextConfig;
Auf diese Weise ist die Ausgabe der Anwendung vom Typ „Standalone“.
Die Datei, die unser Docker-Image darstellt, ist die Docker-Datei. Normalerweise wird diese Datei im Stammverzeichnis des Projekts abgelegt. Lassen Sie es uns Schritt für Schritt erstellen.
Jedes Docker-Image beginnt mit einem Basis-Image. In diesem Fall benötigt jedes JavaScript-Projekt, das einen Server ausführt, eine Laufzeitumgebung wie Node.js. Als Basis nehmen wir das Docker-Image einer Node.js-Version, die mit unserem Projekt kompatibel ist. In meinem Fall verwende ich gerne die alpine Version der Bilder, da diese leichter ist. Allerdings müssen wir beim Erstellen des Images prüfen, ob es keine Kompatibilitätsprobleme gibt, andernfalls müssen wir die „nicht-alpine“ Version des Images verwenden. Für dieses Beispiel verwende ich das Bild node:22.6.0-alpine3.19 als Basis.
/** @type {import('next').NextConfig} */ const nextConfig = { output: "standalone" }; export default nextConfig;
Wir platzieren einen Alias, um ihn in den verschiedenen Schritten oder Phasen des Bildes wiederzuverwenden.
Der nächste Schritt besteht darin, die Abhängigkeiten zu installieren. In diesem Fall ist nur eine Systemabhängigkeit erforderlich: libc6-compat. Hier wird erwähnt, warum.
FROM node:22.6.0-alpine3.19 AS base
Da pnpm nicht standardmäßig in Node.js enthalten ist, ist es notwendig, es zu aktivieren und die Umgebungsvariablen festzulegen, damit die installierten Pakete zwischengespeichert werden können.
FROM base AS build-deps RUN apk add --no-cache libc6-compat
Dann müssen wir das Arbeitsverzeichnis so einstellen, dass eine klare Trennung zwischen den Systemordnern und dem Anwendungsordner besteht. In diesem Fall verwenden wir /app.
ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" RUN corepack enable RUN corepack prepare pnpm@latest --activate
Jetzt müssen wir die Dateien mit den Projektabhängigkeitsinformationen kopieren und installieren.
WORKDIR /app
Die Argumente --frozen-lockfile und --prefer-frozen-lockfile werden verwendet, um die in der Sperrdatei von pnpm angegebenen Versionen zu berücksichtigen.
Zum Abschluss dieser Phase wird die Sharp-Bibliothek hinzugefügt. Dies ist notwendig, um Bilder in einer Produktionsumgebung in Next.js zu optimieren.
COPY package.json pnpm-lock.yaml ./ RUN pnpm install --frozen-lockfile --prefer-frozen-lockfile
Die gesamte Bühne sieht so aus:
RUN pnpm add sharp
Der nächste Schritt besteht darin, die Next.js-Anwendung zu kompilieren. Hier liegt der Schlüssel dafür, dass das Image funktioniert, denn der Rest der Docker-Datei ist nichts anderes und kann auch in keinem anderen Beispiel gefunden werden. In diesem Stadium ist es notwendig, die im Projekt verwendeten Umgebungsvariablen als Build-Argumente zu übergeben und sie vor der Generierung des Builds festzulegen.
Das liegt daran, dass es zwei Zeitpunkte gibt, in denen die Anwendungen funktionieren, nämlich die Erstellungszeit und die Laufzeit. Wenn die Umgebungsvariablen zur Laufzeit nicht verfügbar sind, haben alle statischen Assets, die sie verwenden, keinen Wert für sie und die Anwendung funktioniert nicht richtig. In diesem Beispiel werden drei Umgebungsvariablen verwendet: NEXT_PUBLIC_BACKEND_URL, FRONTEND_URL und JWT_SECRET.
FROM base AS build-deps RUN apk add --no-cache libc6-compat ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" RUN corepack enable RUN corepack prepare pnpm@latest --activate WORKDIR /app COPY package.json pnpm-lock.yaml ./ RUN pnpm install --frozen-lockfile --prefer-frozen-lockfile RUN pnpm add sharp
Dann wird pnpm aktiviert, das Arbeitsverzeichnis festgelegt, alle Anwendungsdateien kopiert und der Build generiert.
FROM base AS builder ARG NEXT_PUBLIC_BACKEND_URL ENV NEXT_PUBLIC_BACKEND_URL=$NEXT_PUBLIC_BACKEND_URL ARG FRONTEND_URL ENV FRONTEND_URL=$FRONTEND_URL ARG JWT_SECRET ENV JWT_SECRET=$JWT_SECRET
Die gesamte Bühne sieht so aus:
RUN corepack enable RUN corepack prepare pnpm@latest --activate WORKDIR /app COPY --from=build-deps /app/node_modules ./node_modules COPY . . RUN pnpm build
Der letzte Schritt besteht darin, die Anwendung auszuführen. Dazu legen wir zunächst die Node-Produktionsumgebung fest:
/** @type {import('next').NextConfig} */ const nextConfig = { output: "standalone" }; export default nextConfig;
Aus persönlichen Gründen ist die Next.js-Telemetrie deaktiviert. Das heißt, wir senden unsere Anwendungsdaten grundsätzlich nicht an Vercel, um Next.js durch Fehlerdiagnose und Nutzungsmetriken zu verbessern.
FROM node:22.6.0-alpine3.19 AS base
Außerdem wird als bewährte Vorgehensweise empfohlen, in Docker-Images einen Nicht-Root-Benutzer zu verwenden. Dies vermeidet beispielsweise Sicherheitslücken für den Fall, dass der Container Zugriff auf das Host-Netzwerk hat. Dazu werden eine Nodejs-Gruppe und ein Nextjs-Benutzer hinzugefügt und ihnen die Ordnereigenschaft .next zugewiesen.
FROM base AS build-deps RUN apk add --no-cache libc6-compat
Dann werden die vom Standalone-Build generierten Dateien kopiert, um dieselbe Struktur wie der Standard-Build von Next.js zu erstellen.
ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" RUN corepack enable RUN corepack prepare pnpm@latest --activate
Da wir den nextjs-Benutzer erstellt haben, müssen wir angeben, dass dies der zu verwendende Benutzer sein wird.
WORKDIR /app
Ebenso ist es erforderlich, den bereitgestellten Port des Containers sowie den Node-Port und den Hostnamen anzugeben, der verwendet werden soll. Dieser lautet 0.0.0.0, da wir die genaue Adresse nicht kennen.
COPY package.json pnpm-lock.yaml ./ RUN pnpm install --frozen-lockfile --prefer-frozen-lockfile
Anschließend werden die Umgebungsvariablen für die Anwendungslaufzeit aus den Build-Argumenten angegeben.
RUN pnpm add sharp
Angegebene Umgebungsvariablen in einer docker-compose.yml-Datei können verwendet werden, ebenso wie beim Ausführen des Containers. Es wäre jedoch nicht sinnvoll, wenn die Umgebungsvariablen in diesem Kontext zur Build- und Laufzeit unterschiedlich wären .
Zuletzt betreiben wir den Server.
FROM base AS build-deps RUN apk add --no-cache libc6-compat ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" RUN corepack enable RUN corepack prepare pnpm@latest --activate WORKDIR /app COPY package.json pnpm-lock.yaml ./ RUN pnpm install --frozen-lockfile --prefer-frozen-lockfile RUN pnpm add sharp
Die komplette Docker-Datei sieht so aus:
FROM base AS builder ARG NEXT_PUBLIC_BACKEND_URL ENV NEXT_PUBLIC_BACKEND_URL=$NEXT_PUBLIC_BACKEND_URL ARG FRONTEND_URL ENV FRONTEND_URL=$FRONTEND_URL ARG JWT_SECRET ENV JWT_SECRET=$JWT_SECRET
Sie finden die Datei auch in diesem Gist.
Das Erstellen eines Docker-Images für eine Next.js-Anwendung kann aufgrund all der Überlegungen, die wir berücksichtigen müssen, zunächst entmutigend sein. Darüber hinaus gibt es die weit verbreitete Meinung, dass das Selbsthosten einer Next.js-Anwendung, d. außerhalb von Vercel, ist kompliziert. Das ist es nicht. Wenn man die wichtigsten Teile versteht, ist es eigentlich einfach.
Ich hoffe, dass Sie mit diesen Informationen Ihre Next.js-Anwendung problemlos andocken können. Und Sie kennen die Übung: Wenn Sie Fragen haben oder etwas mitteilen möchten, hinterlassen Sie es in den Kommentaren :)
Das obige ist der detaillierte Inhalt vonDockerisieren einer Next.js-Anwendung mithilfe eines Standalone-Builds. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!