如何为Next.js编写优化的Dockerfiles

当将现代应用程序部署到生产环境中,使用容器化技术已成为一种常见的做法。Docker,作为一种流行的容器化平台,为开发者提供了一种便捷的方式来封装应用程序及其依赖,从而实现一致性和可移植性。对于基于 Next.js 构建的应用程序,编写优化的 Dockerfiles 是确保应用在容器环境中高效运行的关键一步。通过合理的镜像构建流程,可以最大程度地减小镜像的大小,提升应用的性能和可维护性。

有时候编写Dockerfile可能会有些棘手。我们需要注意我们推送的文件和我们分配的权限。

构建镜像中最具挑战性的事情之一就是保持镜像的大小。一个写得好的 Docker 镜像占用的空间尽可能小。这可以通过多种方式来确保:

  1. 我们可以决定限制我们推送到图像的文件。这涉及到过滤掉不需要的文件。
  2. 在生成镜像时使用多阶段构建。
  3. 在使用案例耗尽后,删除不需要的文件。

多阶段构建是一种在Docker中使用的技术,旨在优化镜像构建过程。它允许我们在一个Dockerfile中定义多个构建阶段,每个阶段都可以使用不同的基础镜像和构建步骤。 使用多阶段构建的主要优势是减小最终镜像的大小。通过在不同的阶段中只包含必要的构建工具和依赖项,我们可以避免将不必要的文件和库打包到最终的镜像中。这样可以显著减少镜像的体积,提高部署效率。 另一个优势是提高构建速度。由于每个阶段都可以并行执行,我们可以在不同的阶段中同时进行构建,从而加快整个构建过程。这对于大型项目或需要频繁构建的应用程序特别有用。 多阶段构建还可以帮助我们更好地组织和管理构建过程。通过将构建步骤分解为多个阶段,我们可以更清晰地了解每个阶段的作用和目的。这样可以使构建过程更可靠、可维护和可扩展。 总之,多阶段构建是一种强大的技术,可以帮助我们优化镜像构建过程,减小镜像大小,提高构建速度,并更好地组织和管理构建过程。它是在Docker中构建高效和可靠镜像的重要工具

多阶段构建是一种非常流行的方法,可以减小镜像的大小。要编写一个真正高效的Dockerfile,您需要使用shell技巧和其他逻辑,尽量保持每个层尽可能小,并确保每个层都具有来自前一个层的所需构件,而不包含其他多余的内容。

多阶段构建使用多个 FROM 语句,可以与不同的基础镜像一起使用,并且每个语句都开始了构建的新阶段。您可以选择性地从一个阶段复制构件到另一个阶段,将不需要的内容留在最终镜像之外。从而确保最终镜像尽可能精简。可以使得容器化的 Next.js 应用既高效又稳定地运行。

Next.js中的独立应用程序

在我们开始编写Dockerfile之前,我们需要了解Next框架的一个非常强大的功能。

理想情况下,当我们在Next中构建一个应用程序时,我们会继续依赖于node_modules,并进而依赖于package.json。这意味着即使在最终的镜像中,我们也需要导入所有的依赖项,以确保应用程序能够成功运行。

这就是Output File Tracing/ Standalone在NextJS中发挥作用的地方。Next.js可以自动创建一个独立的文件夹,仅复制生产部署所需的必要文件,包括node_modules中的选择文件。

要利用这个自动复制功能,您可以在您的 next.config.js 文件中启用它。

这将在 .next/standalone 创建一个文件夹,可以独立部署而无需安装 node_modules

此外,还提供了一个最小的 server.js 文件,可以用来替代 next start

这个最小服务器默认不会复制 public.next/static 文件夹。可以手动将这些文件夹复制到 standalone/publicstandalone/.next/static 文件夹中,之后 server.js 文件将自动为其提供服务。

Dockerfile

让我们一步一步地构建Dockerfile:

  1. 让我们创建我们的第一层 - dependencies ,在这里我们将解决所有的依赖关系并定义一个基础。您可以从可用列表中选择自己的基础。
vbnet 复制代码
FROM node:16-alpine AS dependencies

我们来添加一个本地包,设置工作目录,并复制 package.jsonpackage-lock.json 来定义依赖项。

sql 复制代码
RUN apk add --no-cache libc6-compat
WORKDIR /home/app
COPY package.json ./
COPY package-lock.json ./ 

一旦我们准备好了,我们就可以继续安装所需的依赖项。

css 复制代码
RUN npm i

我们已经添加了所有的依赖项。我们的第一层已经准备好了。

我们来创建第二层------ builder 。这一层的目标是生成我们最终要推送到最终镜像中的静态文件。

vbnet 复制代码
FROM node:16-alpine AS builder

接下来,我们需要将前一层的相关工件复制到 builder 中,并设置当前工作目录。

bash 复制代码
WORKDIR /home/app
COPY --from=dependencies /home/app/node_modules ./node_modules
COPY . . 

我们现在想要构建这个应用程序。

arduino 复制代码
RUN npm run build

这标志着第二层的结束。在这个阶段,我们拥有了静态文件,我们将把它们推送到我们的最终镜像中。

让我们创建我们的第三个也是最后一个层级 --- runner 。在这里,我们将推送静态文件,并尽量保持图像的大小较小。

我们还设置了工作目录,并将 NEXT_TELEMETRY_DISABLED 设置为 true 。这将确保我们选择退出Next的匿名数据收集。

bash 复制代码
FROM mhart/alpine-node:slim-14 AS runner
WORKDIR /home/app
ENV NEXT_TELEMETRY_DISABLED 1

然后我们将相关的工件放入图像中。这涉及到来自 .next/standalone 的独立文件、公共文件和静态文件。

vbnet 复制代码
COPY --from=builder /home/app/.next/standalone ./standalone
COPY --from=builder /home/app/public /home/app/standalone/public
COPY --from=builder /home/app/.next/static /home/app/standalone/.next/static

最后,我们暴露所需的端口,并记录下最终的命令来启动应用程序。

yaml 复制代码
EXPOSE 3000
ENV PORT 3000
CMD [ "node" , "./standalone/server.js" ] 

我们的Dockerfile大致如下:

vbnet 复制代码
FROM node:16-alpine AS dependencies
RUN apk add --no-cache libc6-compat
WORKDIR /home/app
COPY package.json ./
COPY package-lock.json ./
RUN npm iFROM node:16-alpine AS builder
WORKDIR /home/app
COPY --from=dependencies /home/app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED 1
ARG NODE_ENV
ENV NODE_ENV="${NODE_ENV}"
RUN npm run buildFROM mhart/alpine-node:slim-14 AS runner
WORKDIR /home/app
ENV NEXT_TELEMETRY_DISABLED 1
COPY --from=builder /home/app/.next/standalone ./standalone
COPY --from=builder /home/app/public /home/app/standalone/public
COPY --from=builder /home/app/.next/static /home/app/standalone/.next/static
EXPOSE 3000
ENV PORT 3000CMD [ "node" , "./standalone/server.js" ] 

结论

当我们为像Next这样的框架编写Dockerfile时,最常见的错误是:

  1. 将整个构建文件推送到容器中
  2. 将所有依赖项安装或复制到最终镜像中。
  3. 将所有的工件复制到最终层,包括源文件。

在我们决定将每个构建文件(无论是哪个框架)推送到镜像之前,我们需要了解其相关性。

通过对 next.config.jsDockerfile 进行这些更改,我成功将Docker镜像的大小从1.45 GB降低到仅有81.6 MB。

Size of the container with .next:

Size of the container with standalone:

相关推荐
小张是铁粉1 分钟前
docker学习二天之镜像操作与容器操作
学习·docker·容器
烟雨书信14 分钟前
Docker文件操作、数据卷、挂载
运维·docker·容器
IT成长日记18 分钟前
【Docker基础】Docker数据卷管理:docker volume prune及其参数详解
运维·docker·容器·volume·prune
这儿有一堆花24 分钟前
Docker编译环境搭建与开发实战指南
运维·docker·容器
LuckyLay24 分钟前
Compose 高级用法详解——AI教你学Docker
运维·docker·容器
Uluoyu32 分钟前
redisSearch docker安装
运维·redis·docker·容器
IT成长日记5 小时前
【Docker基础】Docker数据持久化与卷(Volume)介绍
运维·docker·容器·数据持久化·volume·
热爱生活的猴子5 小时前
阿里云服务器正确配置 Docker 国内镜像的方法
服务器·阿里云·docker
ItJavawfc6 小时前
RK-Android11-性能优化-限制App内存上限默认512m
性能优化·heapsize·heapgrowthlimit·虚拟机参数·内存上限
瓜子三百克6 小时前
七、性能优化
flutter·性能优化