简体   繁体   English

使用 typescript 对 Nextjs 进行 Docker 化

[英]Dockerizing Nextjs with typescript

I'm trying to build a Docker Image of my Nextjs frontend(React) application for production and am currently stuck at typescript integration.我正在尝试构建我的 Nextjs 前端(React)应用程序的 Docker 图像以用于生产,目前停留在 typescript 集成。

Here's the Dockerfile.这是 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"]

Now this results in an error:现在这会导致错误:

It looks like you're trying to use TypeScript but do not have the required package(s) installed.

Basically since I'm doing npm ci --production it doesn't install devDependencies where typescript is.基本上,因为我正在执行 npm ci --production,所以它不会在 typescript 所在的位置安装 devDependencies。

After searching I've arrived at few solutions.经过搜索,我找到了一些解决方案。

Solution 1: The first one is to add typescript to dependencies.解决方案一:第一种是将typescript添加到dependencies中。 Though it is advised that since typescript is only devDependency it should not be in normal dependencies.虽然建议 typescript 只是 devDependency,但它不应处于正常依赖关系中。

Solution 2: Adding typescript via npm install .解决方案2:通过npm install添加typescript。 Basically same as solution 1. I modified the Dockerfile as:与解决方案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

In this case the total image size becomes: 981.58 MB.在这种情况下,总图像大小变为:981.58 MB。

Solution 3: Doing simple npm install instead of npm ci --production .解决方案 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

In this case I end installing all devDependencies also.在这种情况下,我也结束安装所有 devDependencies。 In this case the total image size is: 537.32 MB.在这种情况下,总图像大小为:537.32 MB。

Now I have few questions regarding this.现在我对此几乎没有疑问。

Question 1: Why does adding typescript via npm install --save-dev typescript @types/node in Solution 2 results in bigger file size compared to Solution 3 where we install all the dependencies?问题 1:为什么在解决方案 2 中通过npm install --save-dev typescript @types/node添加 typescript 与我们安装所有依赖项的解决方案 3 相比会产生更大的文件大小?

Question 2: If in Solution 3 I do npm ci instead of npm install the total image size comes out to be 972.59 MB.问题 2:如果在解决方案 3 中我执行npm ci而不是npm install ,则总图像大小为 972.59 MB。 Why does using npm ci increase the image size.为什么使用npm ci会增加图像大小。 Shouldn't it just install exact packages based on package-lock.json.它不应该只根据 package-lock.json 安装确切的包吗?

Question 3: I looked at discussion Asked to install Typescript when already installed when building Docker image.问题 3:我看了讨论在构建 Docker 映像时要求安装 Typescript when already installed。

It suggested a solution with multi-staged build like this.它提出了一个像这样的多阶段构建的解决方案。

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

Isn't this solution bad since you end up installing dependencies twice in this case.这个解决方案不是很糟糕吗,因为在这种情况下你最终安装了两次依赖项。 Once in the build stage and 2nd in runner stage even if you install only production dependencies in runner stage.一次进入build阶段,第二次进入runner阶段,即使您只在运行阶段安装生产依赖项。

I tried this solution and as expected I ended up with an image size of 1.18 GB.我尝试了这个解决方案,正如预期的那样,我最终得到了 1.18 GB 的图像大小。

Question 4: Which of the above solution is better to go for?问题4:以上解决方案中go哪个更好? Or is there a better way of doing this?或者有更好的方法吗?

Use a container intermediate to install only packages for production使用容器中间体仅安装用于生产的软件包

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

For this case, you can use the base image http://ghcr.io/ryanbekhen/feserve as the production stage.对于这种情况,您可以使用基础镜像http://ghcr.io/ryanbekhen/feserve作为生产阶段。 I made this based on the complaints that occurred on the frontend.我是根据前端发生的投诉做的。 The base image is only around 8mb so it doesn't take up a lot of storage.基本图像只有 8mb 左右,因此不会占用大量存储空间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM