当前位置: 首页 > article >正文

使用 pnpm workspace 和 standalone 模式构建 Next.js 的 Docker 镜像

引言

本文将探讨如何利用 pnpm workspace 和 standalone 模式来构建 Next.js 应用程序的轻量级 Docker 镜像。这种方法通过仅在 node_modules 目录中包含必要的文件,显著减少了最终 Docker 镜像的大小。
在这里插入图片描述

Standalone 模式简介

通常情况下,所有在 dependencies 中列出的包都会被放置在 node_modules 目录中,这会导致镜像体积增大。而在 standalone 模式下构建时,可以从 node_modules 中仅复制必需的文件到一个特定的目录。这样一来,node_modules 将只包含应用程序运行所需的文件,从而大幅减少镜像的大小!

要启用 standalone 模式,请在 next.config.js 文件中添加如下配置:

module.exports = {
  output: 'standalone',
};

目录结构

我们采用了类似于 Turborepo 结构的目录布局:

.
├── apps
│   └── my-app
│       ├── package.json
│       └── ...
├── Dockerfile
├── packages
│   └── other-app
│       └── package.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── Dockerfile
└── .npmrc

apps 目录包含了作为服务器启动的应用程序,而 packages 目录则包含了 apps 中使用的共享包。

Dockerfile 概览

Dockerfile 设计用于维护每个应用程序的独立性,并将其放置在 /apps/my-app 目录下。下面是 Dockerfile 的概览:

多阶段构建

本 Dockerfile 采用了多阶段构建的方法来分离关注点并最小化最终镜像的大小。

第一阶段:基础环境(base)

FROM node:20.12.0-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"

# 启用 pnpm
RUN corepack enable

这一阶段设置了 pnpm 的环境。

第二阶段:依赖安装(deps)

FROM base AS deps

WORKDIR /app

# 复制 `pnpm install` 所需的文件
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./
COPY ./apps/my-app/package.json /app/apps/my-app/package.json
COPY ./packages/ /app/packages/

RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
    pnpm install --frozen-lockfile

这一阶段安装了所需的依赖项。

第三阶段:构建器(builder)

FROM base AS builder

WORKDIR /app

# 复制已安装的 `node_modules` 从 deps 阶段
COPY --from=deps /app/node_modules ./node_modules
# 复制构建目标的目录
COPY ./apps/my-app ./apps/my-app
COPY --from=deps /app/packages ./packages
COPY --from=deps /app/.npmrc ./

# 展开符号链接并复制到 pruned 目录
RUN pnpm --filter=@monorepo/my-app deploy /pruned

WORKDIR /pruned

# 构建应用程序
RUN pnpm --filter=@monorepo/my-app build

这一阶段构建了应用程序并准备部署。

第四阶段:运行器(runner)

FROM base AS runner

WORKDIR /app

ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1

# 复制已构建的文件从 builder 阶段
COPY --from=builder /app/apps/my-app/public ./public
COPY --from=builder /pruned/.next/standalone .
COPY --from=builder /pruned/.next/static ./.next/static

CMD ["node", "server.js"]

这一阶段是启动应用程序的实际阶段。

构建 Docker 镜像

使用以下命令来构建 Docker 镜像并检查其大小:

$ docker build -f apps/my-app/Dockerfile --no-cache --target runner --tag myapp:latest --progress=plain

构建后检查镜像:

$ docker images

您将看到一个准备就绪且体积轻量的镜像。

结论

尽管在过程中遇到了一些挑战,比如理解 pnpm deploy 命令及管理多个应用程序在单一 workspace 中的过程,但最终还是成功地构建了一个高效且轻量的 Docker 镜像。

遇到的问题

  • 管理多个应用程序的复杂性:尝试在一个 Dockerfile 中处理所有应用程序目录下的应用,导致 --build-arg 参数增加,使得 Dockerfile 变得繁琐。
    • 解决方案:通过在每个 apps 目录中配置 Dockerfile 来避免这种情况。这样还可以针对每个应用程序单独进行 Node.js 的更新等操作。
  • pnpm deploy -> build 流程的理解不足:对 pnpm 通过符号链接工作的方式以及在 deploy 后将实际文件复制的行为理解不够深入,导致应用程序启动失败。
    • 解决方案:将 docker build--target 参数更改为 builder,然后通过 docker run -it my-app /bin/bash 进入容器并查看目录状态来进行调试。

参考链接

  • Next.js 文档:自动复制追踪文件

http://www.kler.cn/news/284533.html

相关文章:

  • ceph rgw reshard (by quqi99)
  • Ubuntu 24.04 中安装网易邮箱大师
  • JVM下篇:性能监控与调优篇-02-JVM监控及诊断工具-命令行篇
  • mybatisplus + oracle + spring boot遇到的一些问题
  • python基础学习(最终篇)
  • Unclutter - 苹果电脑(Mac)桌面文件笔记剪贴板管理工具
  • jenkins如何生成报告并查看报告,如何安装allure插件
  • MySQL-基础篇-事务(事务简介、事务操作、事务的四大特性、并发事务引发的问题、事务的隔离级别)
  • 前波士顿咨询Platinion董事总经理陈果加入望繁信科技
  • RK3568平台(平台总线篇)SPI驱动框架分析
  • 今日算法:蓝桥杯基础题之“星系炸弹”
  • Python | Leetcode Python题解之第384题打乱数组
  • Claude 与 ChatGPT:哪个更适合学术写作,深入对比分析
  • linux批量解压tar.gz文件
  • I/0系统基本概念
  • ORACLE 统计信息的备份与恢复
  • Servlet 简介+ Cookie和session+过滤器Filter和监听器Listener
  • Vue3 ref 和 reactive 的区别
  • 命名空间using namespace std
  • Python编码系列—Python代码重构:提升代码质量
  • 编成笔记-atan2函数学习分析
  • spring 声明式事务方法自调用问题
  • C与C++的三种区分方式
  • 脚手架工具的应用(前端和后端搭建)
  • 惠中科技光伏清洁领域的专业引领者
  • Android音视频开发,需要学些什么?
  • 排序算法之希尔排序详细解读(附带Java代码解读)
  • TCP 协议详解
  • 同城小程序怎么做 同城小程序系统开发制作方案
  • 利用Spring Boot实现微服务的链路追踪