node.js - ใช้ Docker กับ Nextjs ที่มี typescript
ฉันกำลังพยายามสร้าง Docker Image ของแอปพลิเคชัน Nextjs frontend (React) ของฉันสำหรับการใช้งานจริง และตอนนี้ติดขัดอยู่ที่การรวม TypeScript
นี่คือไฟล์ 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"]
ตอนนี้สิ่งนี้ทำให้เกิดข้อผิดพลาด:
It looks like you're trying to use TypeScript but do not have the required package(s) installed.
โดยพื้นฐานแล้วเนื่องจากฉันกำลังใช้คำสั่ง npm ci --production มันจึงไม่ติดตั้ง devDependencies ซึ่งมี TypeScript อยู่
หลังจากการค้นหาฉันได้พบวิธีแก้ไขไม่กี่วิธี
วิธีแก้ 1: วิธีแรกคือการเพิ่ม typescript เข้าไปใน dependencies แม้ว่าจะมีคำแนะนำว่าเนื่องจาก typescript เป็นเพียง devDependency จึงไม่ควรอยู่ใน dependencies ปกติ
วิธีแก้ปัญหา 2: เพิ่ม typescript ผ่าน npm install โดยหลัก ๆ ก็เหมือนกับวิธีแก้ปัญหา 1 ฉันได้แก้ไข Dockerfile ดังนี้:
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
ในกรณีนี้ ขนาดภาพรวมทั้งหมดจะเป็น: 981.58 MB
วิธีแก้ปัญหา 3: ใช้ npm install แบบง่าย ๆ แทน 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
ในกรณีนี้ฉันจบการติดตั้ง devDependencies ทั้งหมดด้วย ในกรณีนี้ขนาดรวมของภาพคือ: 537.32 MB
ตอนนี้ฉันมีคำถามนิดหน่อยเกี่ยวกับเรื่องนี้
คำถาม 1: ทำไมการเพิ่ม TypeScript ผ่าน npm install --save-dev typescript @types/node ในวิธีแก้ปัญหาที่ 2 จึงทำให้ขนาดไฟล์ใหญ่ขึ้นเมื่อเทียบกับวิธีแก้ปัญหาที่ 3 ซึ่งเราติดตั้ง dependencies ทั้งหมด?
คำถามที่ 2: ถ้าใน Solution 3 ผมทำ npm ci แทน npm install ขนาดรวมของอิมเมจจะออกมาเป็น 972.59 MB ทำไมการใช้ npm ci ถึงเพิ่มขนาดอิมเมจได้ ล่ะ? มันไม่ควรแค่ติดตั้งแพ็กเกจตามที่ระบุไว้ใน package-lock.json อย่างแม่นยำหรือ?
คำถาม 3: ฉันดูการสนทนา ถูกถามให้ติดตั้ง Typescript ทั้งที่ติดตั้งแล้วเมื่อกำลังสร้าง Docker image
มันแนะนำวิธีแก้ปัญหาที่มีการสร้างหลายขั้นตอนแบบนี้
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"]
วิธีแก้นี้ไม่ดีหรือไม่ เพราะในกรณีนี้คุณต้องติดตั้ง dependencies ซ้ำสองครั้ง หนึ่งครั้งในขั้นตอน build และครั้งที่สองในขั้นตอน runner แม้ว่าคุณจะติดตั้งเพียง dependencies สำหรับ production ในขั้นตอน runner ก็ตาม
ฉันลองใช้วิธีแก้ปัญหานี้และตามที่คาดไว้ ฉันก็ได้ขนาดภาพอยู่ที่ 1.18 GB
คำถามที่ 4: วิธีแก้ปัญหาข้างต้นวิธีไหนดีกว่าที่ควรเลือก? หรือมีวิธีที่ดีกว่านี้ในการทำหรือไม่?
คำตอบ #1
ใช้ตัวกลางคอนเทนเนอร์เพื่อติดตั้งเฉพาะแพ็กเกจสำหรับการผลิต
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"]
คำตอบ #2
สำหรับกรณีนี้ คุณสามารถใช้รูปภาพพื้นฐาน https://github.com/ryanbekhen/feserve/pkgs/container/feserve เป็นขั้นตอนการผลิต ซึ่งเป็นรูปภาพที่ฉันสร้างขึ้นจากข้อร้องเรียนที่เกิดขึ้นบนส่วนหน้า รูปภาพพื้นฐานมีขนาดเพียงประมาณ 8 MB ดังนั้นจึงไม่กินพื้นที่เก็บข้อมูลมากนัก
