node.js - Menggunakan Docker untuk Nextjs dengan TypeScript
Saya sedang mencoba membangun Docker Image dari aplikasi frontend Nextjs (React) saya untuk produksi dan saat ini terjebak pada integrasi TypeScript.
Berikut adalah Dockerfile.
FROM node:14-alpine3.14 as deps
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]
EXPOSE 4500
RUN apk add --no-cache libc6-compat
RUN mkdir /app && chown -R node:node /app
WORKDIR /app
COPY --chown=node:node package.json package-lock.json ./
RUN npm ci --production && npm cache clean --force
FROM node:14-alpine3.14 as build
RUN mkdir /app && chown -R node:node /app
WORKDIR /app
ENV NODE_ENV=production
COPY --chown=node:node . ./
COPY --from=deps /app/node_modules ./node_modules
RUN npm run build
FROM node:14-alpine3.14 as prod
RUN mkdir /app && chown -R node:node /app
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=7777
COPY --from=build /app ./
USER node
CMD ["node_modules/.bin/next", "start"]
Sekarang ini menghasilkan sebuah kesalahan:
It looks like you're trying to use TypeScript but do not have the required package(s) installed.
Pada dasarnya karena saya menjalankan npm ci --production, itu tidak menginstal devDependencies di mana typescript berada.
Setelah mencari, saya telah menemukan beberapa solusi.
Solusi 1: Yang pertama adalah menambahkan typescript ke dependencies. Meskipun disarankan bahwa karena typescript hanya devDependency, itu seharusnya tidak ada dalam dependencies normal.
Solusi 2: Menambahkan typescript melalui npm install. Pada dasarnya sama seperti solusi 1. Saya mengubah Dockerfile menjadi:
FROM node:14-alpine3.14 as deps
COPY --chown=node:node package.json package-lock.json ./
RUN npm ci --production && npm cache clean --force
Added typescript and node types here
RUN npm install --save-dev typescript @types/node
Dalam kasus ini, ukuran total gambar menjadi: 981,58 MB.
Solusi 3: Melakukan npm install sederhana alih-alih npm ci --production.
FROM node:14-alpine3.14 as deps
COPY --chown=node:node package.json package-lock.json ./
Simple npm install
RUN npm install && npm cache clean --force
Dalam kasus ini saya akhirnya juga menginstal semua devDependencies. Dalam kasus ini ukuran total gambar adalah: 537,32 MB.
Sekarang saya punya beberapa pertanyaan mengenai ini.
Pertanyaan 1: Mengapa menambahkan typescript melalui npm install --save-dev typescript @types/node dalam Solusi 2 mengakibatkan ukuran file lebih besar dibandingkan Solusi 3 di mana kita menginstal semua dependensi?
Pertanyaan 2: Jika di Solusi 3 saya melakukan npm ci alih-alih npm install, total ukuran gambar menjadi 972,59 MB. Mengapa menggunakan npm ci meningkatkan ukuran gambar? Bukankah seharusnya hanya menginstal paket yang tepat berdasarkan package-lock.json.
Pertanyaan 3: Saya melihat diskusi Diminta untuk menginstal Typescript padahal sudah terinstal saat membangun gambar Docker.
Itu menyarankan solusi dengan build bertahap seperti ini.
FROM gcr.io/companyX/companyX-node-base:12-alpine AS build
Copy in only the parts needed to install dependencies
(This avoids rebuilds if the package.json hasn’t changed)
COPY package.json package.lock .
Install dependencies (including dev dependencies)
RUN npm install
Copy in the rest of the project
(include node_modules in a .dockerignore file)
COPY . .
Build the project
RUN npm run build
Second stage: runtime
FROM gcr.io/companyX/companyX-node-base:12-alpine
ENV NODE_ENV=production
Again get dependencies, but this time only install
runtime dependencies
COPY package.json package.lock .
RUN npm install
Get the built application from the first stage
COPY --from=build /app/dist dist
Set runtime metadata
EXPOSE 3000
CMD [ "npm", "start" ]
CMD ["node", "dist/index.js"]
Bukankah solusi ini buruk karena Anda akhirnya menginstal dependensi dua kali dalam kasus ini. Sekali di tahap build dan kedua di tahap runner meskipun Anda hanya menginstal dependensi produksi di tahap runner.
Saya mencoba solusi ini dan seperti yang diharapkan, saya akhirnya mendapatkan ukuran gambar sebesar 1,18 GB.
Pertanyaan 4: Solusi mana dari yang di atas yang lebih baik untuk dipilih? Atau apakah ada cara yang lebih baik untuk melakukan ini?
Jawaban #1
Gunakan wadah perantara untuk menginstal hanya paket untuk produksi
FROM node:14-alpine AS build
Disable telemetry
ENV NEXT_TELEMETRY_DISABLED 1
WORKDIR /build
Copy package and package-lock
COPY package.json package-lock.json ./
Clean install dependencies based package-lock
Note: We also install dev deps as typeScript may be needed
RUN npm ci
Copy files
Use .dockerignore to avoid copying node_modules and others folders and files
COPY . .
Build application
RUN npm run build
=======================================
Image generate dependencies production
=======================================
FROM node:14-alpine AS dependencies
Environment Production
ENV NODE_ENV production
WORKDIR /dependencies
Copy package and package-lock
COPY --from=build /build/package.json .
COPY --from=build /build/package-lock.json ./
Clean install dependencies based package-lock
RUN npm ci --production
=======================================
Image distroless final
=======================================
FROM gcr.io/distroless/nodejs:14
Mark as prod, disable telemetry, set port
ENV NODE_ENV production
ENV PORT 3000
ENV NEXT_TELEMETRY_DISABLED 1
WORKDIR /app
Copy from build
COPY --from=build /build/next.config.js .
COPY --from=build /build/public/ ./public
COPY --from=build /build/.next ./.next
COPY --from=dependencies /dependencies/node_modules ./node_modules
EXPOSE 3000
Run app command
CMD ["node_modules/.bin/next", "start"]
Jawaban #2
Untuk kasus ini, Anda dapat menggunakan gambar dasar https://github.com/ryanbekhen/feserve/pkgs/container/feserve sebagai tahap produksi. Ini adalah gambar yang saya buat berdasarkan keluhan yang terjadi pada frontend. Gambar dasar ini hanya sekitar 8 MB, jadi tidak memakan banyak penyimpanan.
