Dockerizing Nextjs with typescript



我正在尝试构建我的Nextjs前端(React)应用程序的Docker图像用于生产,我目前卡在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,所以它不会在typescript所在的地方安装devDependencies。

经过搜索,我找到了几个解决方案。

解决方案1:第一个是将typescript添加到依赖项中。尽管建议typescript只是devDependency,但它不应该出现在正常的依赖项中。

方案二:通过npm install添加typescript。和溶液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:为什么在解决方案2中通过npm install --save-dev typescript @types/node添加typescript会导致比我们安装所有依赖项的解决方案3更大的文件大小?

问题2:如果在解决方案3中,我使用npm ci而不是npm install,那么总图像大小为972.59 MB。为什么使用npm ci会增加图像大小?它不应该根据package-lock.json精确地安装包吗?

问题3:我看了讨论,要求在构建Docker镜像时安装已经安装的Typescript。

它建议一个多阶段构建的解决方案,像这样。

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"]

这个解决方案是不是很糟糕,因为在这种情况下,您最终会安装两次依赖项。在build阶段安装一次,在runner阶段安装第二次,即使你只在运行器阶段安装生产依赖。

我尝试了这个解决方案,正如预期的那样,我最终得到了1.18 GB的图像大小。

问题4:以上哪个解决方案更好?还是有更好的方法?

使用容器中间体只安装用于生产的软件包

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"]

对于这种情况,您可以使用基础映像https://github.com/ryanbekhen/feserve/pkgs/container/feserve作为生产阶段。这是我根据在前端发生的投诉制作的图像。基本映像只有大约8 MB,所以它不会占用很多存储空间。

最新更新