指尖上的记忆指尖上的记忆
首页
  • 基础
  • Laravel框架
  • Symfony框架
  • 基础
  • Gin框架
  • 基础
  • Spring框架
  • 命令
  • Nginx
  • Ai
  • Deploy
  • Docker
  • K8s
  • Micro
  • RabbitMQ
  • Mysql
  • PostgreSsql
  • Redis
  • MongoDb
  • Html
  • Js
  • 前端
  • 后端
  • Git
  • 知识扫盲
  • Golang
🌟 gitHub
首页
  • 基础
  • Laravel框架
  • Symfony框架
  • 基础
  • Gin框架
  • 基础
  • Spring框架
  • 命令
  • Nginx
  • Ai
  • Deploy
  • Docker
  • K8s
  • Micro
  • RabbitMQ
  • Mysql
  • PostgreSsql
  • Redis
  • MongoDb
  • Html
  • Js
  • 前端
  • 后端
  • Git
  • 知识扫盲
  • Golang
🌟 gitHub

关于Dockerfile多阶段构建: 示例 Dockerfile

FROM node:hydrogen-alpine AS builder

ARG GITLAB_TOKEN

# update and install dependency for build stage
RUN apk update && apk upgrade
RUN apk add git

# copy source code into image, note .dockerignore
WORKDIR /app
COPY . .

# create temp npmrc with npm auth token, install dependencies, prune for production
RUN rm -rf ./node_modules/*
RUN npm cache clean --force
RUN cp /app/.npmrc.local /app/.npmrc
RUN npm ci
RUN npm run postinstall
RUN rm /app/.npmrc
RUN npm run build
RUN npm prune --production

FROM node:hydrogen-alpine

WORKDIR /app

# copy from build stage
COPY --from=builder /app/package.json ./
COPY --from=builder /app/package-lock.json ./
COPY --from=builder /app/ecosystem.config.cjs ./
COPY --from=builder /app/.output ./.output
COPY --from=builder /app/node_modules ./node_modules
# COPY --from=builder /app/ci-scripts/entrypoint.sh ./entrypoint.sh

# RUN apk add bash
# RUN chmod +x /app/entrypoint.sh
# ENTRYPOINT ["/bin/bash", "/app/entrypoint.sh"]

# install PM2 process manager
RUN npm install pm2 -g
# install git and nginx
# RUN apk add nginx
# COPY --from=builder /app/nginx-template.conf /etc/nginx/templates/nginx-template.conf

ENV NITRO_PORT=3000
ENV NITRO_HOST=0.0.0.0

EXPOSE 3000

# start app with PM2 process manager
CMD [ "pm2-runtime", "/app/ecosystem.config.cjs"]

脚本分析: 这个 Dockerfile 使用了多阶段构建(multi-stage build)的方式,这是一个常见的 Docker 构建模式。它包含两个主要阶段:

  1. 构建阶段(Builder Stage):

    • 这个阶段用于构建应用程序。它使用 node:hydrogen-alpine 作为基础镜像,执行以下操作:
      • 安装构建所需的依赖项,如 git。
      • 复制源代码到镜像中。
      • 安装 npm 依赖并执行构建命令。
      • 优化最终的依赖项,只保留生产所需的包。
    • 这个阶段的输出是一个优化过的构建结果,包括应用程序代码和生产依赖项。
  2. 运行阶段(Runtime Stage):

    • 这个阶段用于运行应用程序。它同样使用 node:hydrogen-alpine 作为基础镜像,但不再包含构建时的工具和依赖项。
    • 它从构建阶段复制所需的文件,如 package.json、package-lock.json、应用程序输出文件和 node_modules 文件夹。
    • 安装 pm2 进程管理器,用于运行应用程序。
    • 设置运行时的环境变量,如 NITRO_PORT 和 NITRO_HOST。
    • 最终,使用 pm2-runtime 命令启动应用程序。

这种多阶段构建的好处是:

  1. 优化镜像大小: 构建阶段包含了所有构建依赖项,但在运行阶段,只有应用程序运行所需的文件和依赖项被复制到最终镜像中,减小了镜像体积。
  2. 分离构建和运行: 构建和运行阶段被分离,使得每个阶段都能专注于自己的任务,提高了灵活性和可维护性。

总之,这个 Dockerfile 采用了多阶段构建的模式,将构建和运行阶段分离,以优化最终 Docker 镜像的大小和结构。

多阶段构建中 AS builder的作用: 在多阶段构建中使用 AS <name> 语法是可选的,但通常被认为是一种最佳实践。

在这个 Dockerfile 中,使用 AS builder 是为了给第一个构建阶段命名为 "builder"。这样做有以下好处:

  1. 可读性: 给每个阶段起名字可以让 Dockerfile 更加清晰易懂。在后续的 COPY --from=builder 语句中,可以清楚地知道它是从哪个阶段复制文件。

  2. 复用性: 如果在 Dockerfile 中需要多次引用同一个构建阶段,使用命名可以让引用更加简单直观。

  3. 可维护性: 当 Dockerfile 变得复杂时,给构建阶段命名可以让代码更加模块化和可读。这有助于理解 Dockerfile 的结构和工作原理。

所以虽然 AS <name> 语法不是必须的,但它通常被认为是一种良好的实践,可以提高 Dockerfile 的可读性和可维护性。但如果你的 Dockerfile 非常简单,不使用命名也是可以的。

怎么知道是Runtime阶段: 在多阶段构建的 Dockerfile 中,通常最后一个 FROM 语句所定义的阶段就是运行时阶段(Runtime Stage)。

让我们来看看这个 Dockerfile 的结构:

  1. 第一个 FROM 语句使用 node:hydrogen-alpine 作为基础镜像,并标记为 AS builder。这是构建阶段(Builder Stage)。
  2. 最后一个 FROM 语句同样使用 node:hydrogen-alpine 作为基础镜像。这个没有任何 AS 标记,所以它就是最终的运行时阶段(Runtime Stage)。

通常情况下,多阶段构建的最后一个阶段就是运行时阶段。这个阶段只包含应用程序运行所需的内容,而不包含构建工具和依赖项。

所以在这个 Dockerfile 中,当看到最后一个 FROM 语句使用 node:hydrogen-alpine 作为基础镜像时,我们就可以确定这就是运行时阶段。

总的来说,通过观察 Dockerfile 中 FROM 语句的顺序和是否有 AS 标记,可以很容易地识别出构建阶段和运行时阶段。最后一个 FROM 语句定义的就是最终的运行时阶段。

RUN npm ci的作用: RUN npm ci 是这个 Dockerfile 中的一个重要命令,它的作用是安装应用程序的依赖项。让我们更详细地解释这条命令:

  1. RUN: 这是 Dockerfile 中的一个关键词,它表示在构建镜像过程中需要执行的命令。

  2. npm ci: 这是 npm 命令行工具提供的一个子命令。ci 代表 "clean install",它与常见的 npm install 命令有一些区别:

    • npm ci 会严格按照 package-lock.json 文件中的版本信息安装依赖项,而不会自动升级依赖的版本。这有助于确保构建的可重复性。
    • npm ci 会首先删除 node_modules 文件夹,然后从头开始安装依赖项。这确保了一个干净的依赖安装环境。
    • npm ci 比 npm install 更快,因为它省略了一些额外的检查和处理步骤。

综合起来,在 Dockerfile 的构建阶段使用 RUN npm ci 可以确保应用程序的依赖项被可靠地安装,并且构建过程更加高效和可重复。这有助于确保最终生成的 Docker 镜像的一致性和可靠性。

这个脚本在执行 ci 之前先执行了 RUN rm -rf ./node_modules/*的作用: 在运行 npm ci 命令之前,Dockerfile 中有一行 RUN rm -rf ./node_modules/*。这个命令的作用是:

  1. 删除 - rm 命令用于删除文件或目录。
  2. 递归删除 - -rf 选项表示递归地删除目录及其内容。
  3. ./node_modules/* - 这个路径指的是删除 /app/node_modules 目录下的所有内容。

为什么要在运行 npm ci 之前先删除 node_modules 目录呢?主要有以下几个原因:

  1. 清理环境 - 删除旧的 node_modules 文件夹可以确保在安装依赖时从一个干净的环境开始,避免遗留的依赖项干扰新的安装过程。

  2. 强制重新安装 - 通过删除 node_modules,强制 npm ci 命令从头开始安装所有依赖项,而不是尝试使用缓存的依赖项。这有助于确保完整和正确的依赖安装。

  3. 镜像大小优化 - 在多阶段构建中,删除旧的 node_modules 可以减小最终 Docker 镜像的体积,因为只有新安装的生产依赖项会被复制到运行时镜像中。

总之,这行 RUN rm -rf ./node_modules/* 命令是为了确保在安装依赖项之前,构建环境是干净和一致的,有助于提高构建过程的可靠性和最终镜像的质量。是的,你注意到了这个细节。在运行 npm ci 命令之前,Dockerfile 中有一行 RUN rm -rf ./node_modules/*。这个命令的作用是:

  1. 删除 - rm 命令用于删除文件或目录。
  2. 递归删除 - -rf 选项表示递归地删除目录及其内容。
  3. ./node_modules/* - 这个路径指的是删除 /app/node_modules 目录下的所有内容。

为什么要在运行 npm ci 之前先删除 node_modules 目录呢?主要有以下几个原因:

  1. 清理环境 - 删除旧的 node_modules 文件夹可以确保在安装依赖时从一个干净的环境开始,避免遗留的依赖项干扰新的安装过程。

  2. 强制重新安装 - 通过删除 node_modules,强制 npm ci 命令从头开始安装所有依赖项,而不是尝试使用缓存的依赖项。这有助于确保完整和正确的依赖安装。

  3. 镜像大小优化 - 在多阶段构建中,删除旧的 node_modules 可以减小最终 Docker 镜像的体积,因为只有新安装的生产依赖项会被复制到运行时镜像中。

总之,这行 RUN rm -rf ./node_modules/* 命令是为了确保在安装依赖项之前,构建环境是干净和一致的,有助于提高构建过程的可靠性和最终镜像的质量。