起因
公司的项目为 monorepo 构建,主项目为 app,NextJs 14 + react 构建,其余项目为 ui 库以及一些组件库
项目结构如下
project
│ package.json
│
└───app
│ │ package.json
│ │ next.config.js
│ │ pnpm-workspace.yaml
│
└───ui
│ │ xxx
│ │ xxx
│
└───components 组件库
│ │ xxx
│ │ xxx
│
└───deployments 部署用到的 docker 系列文件
│ │ dockerfile
现在需要将它 docker 化,由于使用了 SSR 的特性,编译后文件为非静态文件,中途也踩了不少坑
配置
-
NextJs 配置
next.config.js 中添加output: 'standalone'
这样输出的文件会将运行时的依赖一起扔出来 -
环境变量的处理方式
由于业务是 to b ,所以需要满足一份 docker image ,到处使用。这里的问题在于,需要在镜像运行时加载环境变量,但是 .env 是在编译阶段就注入了,所以这里有些特殊的处理。
详情参考这篇文章
值得注意的是,在 NextJs 的 getInitialProps 中为 Node 环境,可以直接使用 process.env.xxx 获取到环境变量
- dockerfile 配置
# 编译命令
# docker build -t app ./ -f ./deployments/Dockerfile
FROM node-18-alpine AS builder
ARG HTTPS_PROXY
ARG HTTP_PROXY
# set http proxy
ENV http_proxy=${HTTP_PROXY}
ENV https_proxy=${HTTPS_PROXY}
# RUN npm config set proxy=${HTTP_PROXY}
RUN npm config set registry https://registry.npmmirror.com/
WORKDIR /app
# 复制项目依赖组件,等下要安装依赖,这里也可以只复制 package.json
COPY ./components ./components
COPY ./types ./types
COPY ./ui ./ui
COPY ./app/package*.json ./app/
COPY ./package*.json ./
COPY ./pnpm-lock.yaml ./
COPY ./pnpm-workspace.yaml ./
RUN pnpm i
# 复制所有文件,然后编译
COPY ./app ./app
COPY ./app/.env.dev.example ./app/.env
RUN pnpm build --filter "app"
# 分层构建
FROM node:18-alpine AS production
WORKDIR /
# 关键点: 这里的复制路径不能弄错,否则一些文件会 404
COPY --from=builder /app/app/.next/standalone /web
COPY --from=builder /app/app/.next/static /web/app/.next/static
# i18n 在 public 里面
COPY --from=builder /app/app/public /web/app/public
COPY --from=builder /app/app/next-i18next.config.js /web/app
# 参考动态注入文章,如果没有这个脚本就去掉
COPY --from=builder /app/app/entrypoint.sh /web
WORKDIR /web
EXPOSE 3000
ENV PORT 3000
ENTRYPOINT ["sh", "./entrypoint.sh"]
CMD ["node", "./app/server.js"]
# 调试模式,注释掉上面的 CMD ,ENTRYPOINT 看情况注释
# 调试模式下会可以镜像,但不运行 nextjs ,方便查看文件进行调试
# CMD ["sh", "-c", "while true; do sleep 1; done"]
核心就是上面的 dockerfile,主要是最后的输出文件在 copy 时文件目录结构要写好,否则会启动不了项目或者找不到依赖文件