运维
PythonJava前端数据库
Docker
Docker
  • Docker简介
  • 基本概念
    • 镜像
    • 容器
    • 仓库
  • 安装
  • 使用镜像
    • 获取镜像
    • 列出镜像
    • 删除镜像
    • 导入和导出
  • Dockerfile
    • 构建镜像
    • 多阶段构建
    • 构建多种系统架构支持的镜像
    • Dockerfile 命令介绍
      • COPY 复制文件
      • ADD 更高级的复制
      • CMD 容器启动命令
      • ENTPYPOINT 入口点
      • ENV 设置环境变量
      • ARG 构建参数
      • VOLUME 匿名卷
      • USER 指定当前用户
      • EXPOSE 暴露端口
      • WORKDIR 工作目录
      • SHELL 指令
      • LABEL 为镜像添加源数据
      • ONBUILD 为他人作嫁衣裳
  • 数据管理
    • 数据卷
    • 挂载主机目录
  • 操作容器
    • 启动、停止和删除
    • 进入容器
    • 导入和导出
    • 重启策略
  • 网络配置
    • 基本网络配置
    • 高级网络配置
  • Docker Buildx
    • 使用buildx构建镜像
    • 使用buildx构建多种系统架构镜像
  • Docker Compose
    • 简介
    • 简单使用
    • 多个配置文件
    • Compose网络配置
    • 控制启动服务启动顺序
    • 命令说明
  • Kubernetes
由 GitBook 提供支持
在本页
  • 多阶段构建之前的方案
  • 一、全部放入一个 Dockerfile
  • 二、分散到多个 Dockerfile
  • 多阶段构建方案
  • 为构建阶段命名
  • 只构建某一阶段的镜像

这有帮助吗?

  1. Dockerfile

多阶段构建

多阶段构建之前的方案

构建镜像最有挑战性之一的就是使镜像尽可能小。Dockerfile中的每一个指令都会向镜像添加新的层,在移动到下一个图层之前,你需要清理不再需要的历史遗留。要编写一个非常高效的Dockerfile,传统方式是采用shell或其它办法使层尽可能小,并确保每个层都能从上一层拿到需要的数据,并且不会多拿。

在 Docker v17.05 版本之前,我们在构建 Docker 镜像时,通常会采用两种方式:

一、全部放入一个 Dockerfile

一种方式是将所有的构建过程包含在一个Dockerfile中,包括项目及其依赖库的编译、测试、打包等流程,这里可能会带来一些问题:

  • 镜像层次过多、体积较大、部署时间变长。

  • 源代码存在泄露分险。

这里我们以Nest.js程序为例进行构建。

FROM node:18-slim

WORKDIR /app
COPY package.json pnpm-lock.json ./
RUN pnpm corepack && pnpm install
COPY . .
RUN pnpm build && rm -rf /app/src && rm -rf /app/test

ENTRYPOINT ["pnpm", "start"]

构建镜像

docker build --target builder -t username/imagename:tag

我们不难发现,在Dockerfile中我们还要执行清理工作,才能构建出符合生产的镜像。

REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
mynest       1.0       96fb19125f08   7 seconds ago   534MB

二、分散到多个 Dockerfile

另一种方式,就是我们事先在一个Dockerfile将项目及其依赖库编译测试打包好后,再将其拷贝到运行环境中,这种方式需要我们编写两个Dockerfile和一些额编译脚本才能将两个阶段自动整合起来,这种方式虽然可以规避第一种方式存在的分险,但是部署过程比较繁杂。

多阶段构建方案

为了解决以上问题,Docker v17.05 开始推出了多阶段构建(multistage builds)。使用多阶段构建我们就可以很容易的解决前面提到的问题,并且只需要一个Dockerfile就能完成所有工作。

在多阶段构建下,你可以在Dockerfile中使用多个FROM声明,每个FROM声明可以使用不同的基础镜像,并且每个FROM都使用一个全新的构建阶段。你可以选择性的将一个构建阶段的文件复制到另一个构建阶段,并删除你不想保留在最终镜像中的一切。例如:

# 第一阶段
FROM node:12.16.1-alpine3.11 as builder
WORKDIR /app
COPY package.json pnpm-lock.json ./
RUN pnpm corepack && pnpm install
COPY . .
RUN pnpm build

# 第二阶段
FROM node:12.16.1-alpine3.11
WORKDIR /app
COPY --from=0 /app/package.json /app/pnpm-lock.json ./
RUN pnpm corepack && pnpm install --prod
COPY --from=0 /app/dist ./dist
CMD ["node", "dist/index.js"]

上述Dockerfile示例中,采用了多阶段构建的写法,在一个Dockerfile中,完成了应用程序的编译以及构建,最终只保留了第二阶段的构建产物。

COPY --from=0 /app/package.json /app/package-lock.json ./表示从第一阶段的构建产物中拷贝文件到当前阶段。

现在只需要一个Dockerfile文件,就可以解决上述所有问题,我们只需要运行docker build来构建镜像即可。

docker build -t username/imagename:1.0 .

为构建阶段命名

默认情况下,构建阶段没有命名,使用它们的编号引用来它们,从第一个FROM以0开始计数。你也可以为每个构建阶段指定一个名称,给FROM指令添加as <NAME>为其构建阶段命名。

FROM node:12.16.1-alpine3.11 

# ......

COPY  /app/dist ./dist
# 第一阶段
FROM node:12.16.1-alpine3.11
WORKDIR /app
COPY package.json package-lock.json ./
RUN pnpm corepack && pnpm install
COPY . .
RUN pnpm build

# 第二阶段
FROM node:12.16.1-alpine3.11
WORKDIR /app

RUN pnpm corepack && pnpm install --prod

CMD ["node", "dist/index.js"]
# 第一阶段
FROM node:12.16.1-alpine3.11 
WORKDIR /app
COPY package.json package-lock.json ./
RUN pnpm corepack && pnpm install
COPY . .
RUN pnpm build

# 第二阶段
FROM node:12.16.1-alpine3.11
WORKDIR /app

RUN pnpm corepack && pnpm install --prod
COPY --from=builder /app/dist ./dist
CMD ["node", "dist/index.js"]

只构建某一阶段的镜像

我们可以使用as来为某一阶段命名,例如:

FROM node:18 as builder

当我们只想构建builder阶段的镜像时,增加--target=builder参数即可。

docker build --target builder -t username/imagename:tag
上一页构建镜像下一页构建多种系统架构支持的镜像

最后更新于2个月前

这有帮助吗?