如何为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:

相关推荐
Zl1597531597532 小时前
k8s基础环境部署
云原生·容器·kubernetes
陌殇殇殇4 小时前
使用GitLab CI构建持续集成案例
运维·ci/cd·云原生·容器·kubernetes·gitlab
技术钱4 小时前
docker简介
运维·docker·容器
roman_日积跬步-终至千里4 小时前
【docker】docker常见命令
运维·docker·容器
tangdou3690986559 小时前
Docker系列-超级详细教你Linux安装并使用docker compose,如何使用docker-compose安装sqlserver
docker·容器·sql server
tangdou3690986559 小时前
手把手非常详细图文并茂教你 Docker 部署 SQL Server
docker·容器·sql server
裴云飞9 小时前
鸿蒙性能优化之布局优化
性能优化·harmonyos
福大大架构师每日一题19 小时前
20.1 分析pull模型在k8s中的应用,对比push模型
云原生·容器·kubernetes
飘逸高铁侠1 天前
docker export/import 和 docker save/load 的区别
docker·容器·eureka
大宇进阶之路1 天前
docker运行arm64架构的镜像、不同平台镜像构建
docker·微服务·架构