NextJs monorepo 项目 docker 化

起因

公司的项目为 monorepo 构建,主项目为 app,NextJs 14 + react 构建,其余项目为 ui 库以及一些组件库

项目结构如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
project
│ package.json

└───app
│ │ package.json
│ │ next.config.js
│ │ pnpm-workspace.yaml

└───ui
│ │ xxx
│ │ xxx

└───components 组件库
│ │ xxx
│ │ xxx

└───deployments 部署用到的 docker 系列文件
│ │ dockerfile

现在需要将它 docker 化,由于使用了 SSR 的特性,编译后文件为非静态文件,中途也踩了不少坑

配置

  1. NextJs 配置
    next.config.js 中添加 output: 'standalone' 这样输出的文件会将运行时的依赖一起扔出来

  2. 环境变量的处理方式
    由于业务是 to b ,所以需要满足一份 docker image ,到处使用。这里的问题在于,需要在镜像运行时加载环境变量,但是 .env 是在编译阶段就注入了,所以这里有些特殊的处理。

详情参考这篇文章

https://juejin.cn/post/7327613318741557283

值得注意的是,在 NextJs 的 getInitialProps 中为 Node 环境,可以直接使用 process.env.xxx 获取到环境变量

  1. dockerfile 配置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    # 编译命令
    # 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 时文件目录结构要写好,否则会启动不了项目或者找不到依赖文件