Dockerfile多阶段构建(一个 Dockerfile 导入多个镜像)

Docker多阶段构建允许在一个Dockerfile中使用多个FROM指令,实现编译环境和运行环境的分离,有效减小最终镜像大小。通过这种方式,可以在不影响运行时功能的情况下排除不必要的编译工具。

Docker 17.05版本以后,支持了多阶段构建,允许一个Dockerfile 中出现多个 FROM 指令。多阶段构建的作用是什么呢?

Docker的镜像内容中,并非只是一个文件,而是有依赖关系的层级结构,后面以前一层为基础,可以理解成类似 git 每一次 commit 都是一个层。 Dockerfile 中的大多数指令都会生成一个层。

多个 FROM 指令时,最后生成的镜像,仍以最后一条 FROM 为准,之前的 FROM 会被抛弃,那么之前的FROM 又有什么意义呢?

在后面的 FROM 指令中, 能够将前置阶段中的文件拷贝到后边的阶段中,这就是多阶段构建的最大意义。

最大的使用场景是将编译环境和运行环境分离。

比如,我们需要构建一个Go语言程序,那么就需要用到go命令等编译环境,我们的Dockerfile可能是这样的:

# Go语言环境基础镜像
FROM golang:1.10.3

# 将源码拷贝到镜像中
COPY server.go /build/

# 指定工作目录
WORKDIR /build

# 编译镜像时,运行 go build 编译生成 server 程序
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOARM=6 go build -ldflags '-w -s' -o server

# 指定容器运行时入口程序 server
ENTRYPOINT ["/build/server"]

基础镜像golang:1.10.3是非常庞大的,因为其中包含了所有的Go语言编译工具和库,而运行时候我们仅仅需要编译后的server程序就行了,不需要编译时的编译工具,最后生成的大体积镜像就是一种浪费。 可以再加一个构建步骤,创建第二个 Dockerfile:

最后将编译接口拷贝到镜像中就行了,那么Dockerfile的基础镜像并不需要包含Go编译环境:

# 不需要Go语言编译环境
FROM scratch

# 将编译结果拷贝到容器中
COPY server /server

# 指定容器运行时入口程序 server
ENTRYPOINT ["/server"]
提示:scratch 是内置关键词,并不是一个真实存在的镜像。 FROM scratch 会使用一个完全干净的文件系统,不包含任何文件。 因为Go语言编译后不需要运行时,也就不需要安装任何的运行库。FROM scratch可以使得最后生成的镜像最小化,其中只包含了 server 程序。

而使用 docker 的多阶段构建特性,一个 Dockerfile 就可以完成如上的工作。

# 编译阶段
FROM golang:1.10.3

COPY server.go /build/

WORKDIR /build

RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOARM=6 go build -ldflags '-w -s' -o server

# 运行阶段
 FROM scratch

# 从编译阶段的中拷贝编译结果到当前镜像中
COPY --from=0 /build/server /

ENTRYPOINT ["/server"]

COPY 指令的--from=0 参数,从前边的阶段中拷贝文件到当前阶段中,多个FROM语句时,0代表第一个阶段。除了使用数字,我们还可以给阶段命名,比如:

# 编译阶段 命名为 builder
FROM golang:1.10.3 as builder

# ... 省略

# 运行阶段
FROM scratch

# 从编译阶段的中拷贝编译结果到当前镜像中
COPY --from=builder /build/server /

更为强大的是,COPY --from 不但可以从前置阶段中拷贝,还可以直接从一个已经存在的镜像中拷贝。比如,

   FROM ubuntu:16.04
    
   COPY --from=quay.io/coreos/etcd:v3.3.9 /usr/local/bin/etcd /usr/local/bin/

直接将etcd镜像中的程序拷贝到了我们的镜像中,这样在生成程序镜像时,就不需要源码编译etcd了,直接将官方编译好的程序文件拿过来就行了。

<think>好的,我现在需要帮用户解决在构建Docker镜像时出现的GPG公钥缺失导致的签名验证错误。用户提到错误是NO_PUBKEY,涉及Debian Bookworm的InRelease签名未验证。首先,我得回顾用户之前的对话和提供的引用,确保解决方案符合他们的实际情况。 首先,用户之前的问题是关于Debian Bookworm系统中多个GPG密钥缺失的问题,我已经给出了一些解决方案,包括安装gnupg、使用循环添加密钥、现代方法替代apt-key等。现在用户在Docker构建过程中遇到类似的问题,需要调整这些方法以适应Dockerfile的环境。 接下来,我需要考虑Dockerfile的特殊性。在Docker构建过程中,不能像在普通系统中那样交互式地运行命令,所以需要确保所有步骤都能在非交互模式下完成。例如,使用apt-get install时可能需要加上-y参数,或者处理gpg命令可能需要的额外选项。 用户提供的引用中,引用[2]提到在docker内执行apt update报错,涉及Docker官方仓库的密钥7EA0A9C3F273FCD8,而用户现在的问题是关于Debian Bookworm的InRelease,可能与之前的密钥不同,但解决方法类似。引用[3]显示用户尝试使用apt-key但失败,因为缺少dirmngr,这提示在Docker镜像中可能需要先安装相关的软件包,如dirmngr,以确保gpg命令能正常工作。 现在,我需要将之前的解决方案适配到Dockerfile中。可能的步骤包括: 1. 确保在Dockerfile中安装必要的工具,如gnupg、dirmngr,因为这些可能在基础镜像中默认未安装。 2. 在运行apt-get update之前,先添加缺失的GPG公钥。 3. 使用适合Docker的非交互式方法,避免提示或用户输入。 4. 根据用户之前的回答,可能需要使用现代的方法,如将密钥放入/etc/apt/keyrings/目录,并在sources.list中引用。 另外,用户提到的错误是NO_PUBKEY,所以需要确定具体的密钥ID。用户的问题中涉及的是Debian Bookworm的仓库,可能需要添加对应的密钥。根据之前的回答,密钥如0E98404D386FA1D9(Docker)、54404762BBB6E853(Debian安全仓库)等,可能需要包含这些。 在Dockerfile中,步骤可能如下: - 安装gnupg和dirmngr:因为有些基础镜像可能没有这些工具,导致gpg命令失败。 - 使用gpg --batch --keyserver来获取密钥,避免交互式问题。 - 将密钥导出到适当的位置,如/etc/apt/keyrings/,并在sources.list.d中配置对应的仓库,使用signed-by参数指向密钥文件。 同时,要处理多个密钥的情况,可能需要循环添加,或者针对每个缺失的密钥单独处理。例如,在用户提供的错误中,NO_PUBKEY 0E98404D386FA1D9等,需要确保这些密钥都被正确添加。 此外,需要注意Docker镜像的层次结构,尽量合并RUN指令以减少层数,提高构建效率。例如,将安装软件包、添加密钥、更新apt等步骤合并到一个RUN指令中,利用&&和换行符来连接命令。 还要考虑缓存问题,有时候apt-get update需要与添加密钥分开,或者在添加密钥后立即更新,但需要确保顺序正确。例如,先添加密钥,再执行apt-get update,否则可能仍然无法验证签名。 最后,验证步骤也很重要,在Dockerfile中可能需要检查apt-get update是否成功,或者在构建过程中捕获错误。但通常,如果正确添加了密钥,后续的apt-get install应该能正常工作。 总结,解决方案需要: 1. 安装必要的工具(gnupg, dirmngr)。 2. 非交互式获取并添加缺失的GPG密钥。 3. 配置APT源,使用signed-by指向密钥文件。 4. 更新包列表并安装所需软件。</think>### Dockerfile构建时解决GPG密钥缺失的完整方案 #### 技术原理 Docker镜像构建过程中出现`NO_PUBKEY`错误,是由于APT仓库的GPG公钥未正确导入。签名验证流程采用$RSA/SHA256$算法,密钥长度$4096-bit$,密钥指纹通过OpenPGP证书传递[^1]。传统`apt-key`命令在容器环境中易出现依赖缺失问题[^3]。 --- ### 分步解决方案 #### 1. 基础镜像优化(Dockerfile) ```dockerfile FROM debian:bookworm # 安装必要组件(必须包含dirmngr) RUN apt-get update && apt-get install -y --no-install-recommends \ gnupg2 \ dirmngr \ ca-certificates \ && rm -rf /var/lib/apt/lists/* ``` #### 2. 密钥注入方法(推荐方案) ```dockerfile # 使用现代密钥管理方式(Debian 11+标准) RUN mkdir -p /etc/apt/keyrings \ && gpg --batch --keyserver hkps://keyserver.ubuntu.com --recv-keys 0E98404D386FA1D9 54404762BBB6E853 BDE6D2B9216EC7A8 \ && gpg --batch --yes --output /etc/apt/keyrings/debian-bookworm.gpg --export 0E98404D386FA1D9 \ && gpg --batch --yes --output /etc/apt/keyrings/debian-security.gpg --export 54404762BBB6E853 BDE6D2B9216EC7A8 # 配置APT源(带signed-by参数) RUN echo "deb [signed-by=/etc/apt/keyrings/debian-bookworm.gpg] http://deb.debian.org/debian bookworm main" > /etc/apt/sources.list \ && echo "deb [signed-by=/etc/apt/keyrings/debian-security.gpg] http://deb.debian.org/debian-security bookworm-security main" >> /etc/apt/sources.list ``` #### 3. 构建验证步骤 ```dockerfile # 强制更新软件源 RUN apt-get update -o Acquire::AllowInsecureRepositories=false \ && apt-get install -y your-package ``` --- ### 关键技术解析 1. **密钥获取优化**: - `--batch`参数消除交互提示 - `hkps://`协议强制加密连接 - 多密钥合并导出减少文件数量 2. **安全强化配置**: ```dockerfile # 禁用不安全仓库 RUN echo 'APT::Get::AllowUnauthenticated "false";' > /etc/apt/apt.conf.d/99verify ``` 3. **Docker构建缓存处理**: ```bash # 若需强制刷新密钥,构建时添加--no-cache docker build --no-cache -t your-image . ``` --- ### 异常处理指南 | 错误类型 | 解决方案 | |-----------------------------|------------------------------------------------------------------------| | `gpg: keyserver receive failed` | 增加`--keyserver hkp://pgp.mit.edu:80`备用服务器 | | `NO_PUBKEY`残留 | 执行`RUN rm -rf /var/lib/apt/lists/*`清除缓存后再试 | | 证书过期 | 更新基础镜像:`FROM debian:bookworm-20240515`(带最新安全补丁) | --- ### 安全最佳实践 1. 密钥指纹验证示例: ```dockerfile RUN echo "0E98404D386FA1D9:6D67 DC44 3B8D A34D 41F2 C3E9 0E98 404D 386F A1D9" | gpg --verify-fingerprint ``` 2. 最小化密钥范围: ```dockerfile # 仅对特定仓库授权 RUN echo "deb [signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian bookworm stable" > /etc/apt/sources.list.d/docker.list ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值