Heim >Web-Frontend >js-Tutorial >Dockerisieren einer Next.js-Anwendung mithilfe eines Standalone-Builds

Dockerisieren einer Next.js-Anwendung mithilfe eines Standalone-Builds

Linda Hamilton
Linda HamiltonOriginal
2024-10-20 06:20:02757Durchsuche

Dockerizing a Next.js Application using a Standalone Build

Einführung

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.

Paketmanager

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.

Next.js-Konfiguration

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“.

Docker-Datei

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.

Basisbild

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.

System- und pnpm-Abhängigkeiten

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

Erstellen der Anwendung

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

Ausführen der Anwendung

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

Vollständige Datei

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.

Abschluss

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!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn