前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用 pnpm workspace 和 standalone 模式构建 Next.js 的 Docker 镜像

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

作者头像
井九
发布2024-10-12 09:48:20
950
发布2024-10-12 09:48:20
举报
文章被收录于专栏:四楼没电梯

引言

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

Standalone 模式简介

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

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

代码语言:javascript
复制
module.exports = {
  output: 'standalone',
};

目录结构

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

代码语言:javascript
复制
.
├── 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)
代码语言:javascript
复制
FROM node:20.12.0-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"

# 启用 pnpm
RUN corepack enable

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

第二阶段:依赖安装(deps)
代码语言:javascript
复制
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)
代码语言:javascript
复制
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)
代码语言:javascript
复制
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 镜像并检查其大小:

代码语言:javascript
复制
$ docker build -f apps/my-app/Dockerfile --no-cache --target runner --tag myapp:latest --progress=plain

构建后检查镜像:

代码语言:javascript
复制
$ 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 进入容器并查看目录状态来进行调试。

参考链接

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-10-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引言
  • Standalone 模式简介
  • 目录结构
  • Dockerfile 概览
    • 多阶段构建
      • 第一阶段:基础环境(base)
        • 第二阶段:依赖安装(deps)
          • 第三阶段:构建器(builder)
            • 第四阶段:运行器(runner)
            • 构建 Docker 镜像
            • 结论
            • 遇到的问题
            • 参考链接
            相关产品与服务
            容器服务
            腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档