ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Ubuntu 24.04 添加 Docker 仓库 GPG 密钥错误全解析

Ubuntu 24.04 添加 Docker 仓库 GPG 密钥错误全解析

升级到 Ubuntu 24.04(Noble)之后,我做的第一件事就是给测试机配上 Docker 官方仓库。按官网文档复制那几行命令,结果sudo apt update还没跑完,屏幕上就铺满了 GPG 密钥错误。这种报错在 Ubuntu 20.04、22.04 上并不常见,但到了 Noble 这一代,系统把仓库签名密钥的信任机制改成了更严格的模型,很多从旧版本升上来、或者习惯照抄旧教程的人都会撞进同一个坑:要么是一串NO_PUBKEY加十六进制编号,要么是gpg: no valid OpenPGP data found,还有一堆关于legacy trusted.gpg keyring的警告。这篇内容就把 Ubuntu 24.04 添加 Docker 官方仓库时遇到的 GPG 密钥缺失错误一次讲透:先说它为什么会出现,再给出一套完全可复现的正确流程,最后把高频报错逐条拆解,方便你对照排查。

1. 先弄清报错本质:apt 凭什么卡在 GPG 密钥上

1.1 一个典型报错现场

我先贴一段模拟的报错输出,关键指纹做了脱敏处理:

$ sudo apt update Hit:1 http://archive.ubuntu.com/ubuntu noble InRelease Hit:2 http://security.ubuntu.com/ubuntu noble-security InRelease Err:3 https://download.docker.com/linux/ubuntu noble InRelease The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 1A2B3C4D5E6F7A8B W: GPG error: https://download.docker.com/linux/ubuntu noble InRelease: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 1A2B3C4D5E6F7A8B E: The repository 'https://download.docker.com/linux/ubuntu noble InRelease' is not signed. E: apt cannot update the repository. Please check its configuration.

这段信息量其实很大。NO_PUBKEY表示 apt 在本地信任的密钥环境里找不到能匹配仓库签名所用的公钥;The repository is not signed是 apt 面对无法验证签名的仓库时的保守策略:宁可不更新,也不让未经身份验证的内容进入。最要命的是整个软件源更新会随之中断,其他仓库可能也会被一起拖住。如果你看到的是Err:3 ... public key is not available,问题几乎可以肯定出在密钥导入环节,而不是仓库地址写错。

1.2 apt 为什么坚持验证 GPG 签名

理解这个报错,得先清楚 apt 的签名验证机制。apt 更新软件源时,网络上拿到的是 Packages 索引文件。你当然不希望别人修改这个索引,让 apt 把恶意软件包装成正常包推给你。所以官方仓库会对索引的元数据文件做签名,签名用私钥生成,apt 用已经信任的公钥去验证。这就像收快递时快递员出示工作证,你验过证件才收件;没有对应的工作证,也就是没有公钥,apt 自然拒绝收件。

具体流程上,apt 会下载仓库的InRelease文件,里面带有 OpenPGP 签名,验证通过后才采用其中记录的哈希值去校验 Packages 索引。如果签名无法确认,整个仓库更新就处于不可信状态,apt 会直接报错并中断执行。所以 GPG 密钥缺失并不是 apt 的小脾气,而是它保护系统安全的基本手段。

1.3 Ubuntu 24.04 的信任模型变化

为什么这个错误在 Ubuntu 24.04 上格外多?因为 24.04 的仓库管理方式变了。旧版 Ubuntu 把第三方源的密钥放进全局信任库/etc/apt/trusted.gpg和/etc/apt/trusted.gpg.d/,用apt-key add导入,系统里所有软件源都会无条件信任这些密钥。但这种方式在安全上有个隐患:一旦某个第三方仓库密钥被攻破,攻击者可以拿它签名任何包,欺骗这台机器上的任意仓库。所以从 22.04 开始,apt-key就被标记为弃用,24.04 更是彻底走向了 deb822 格式的.sources文件,默认的/etc/apt/sources.list.d/ubuntu.sources就是一个典型例子。

新模型要求每个软件源通过Signed-By=明确指定自己信任的 keyring 文件,不再搞全局信任。这带来三个直接影响:

  • 旧教程里curl ... | sudo apt-key add -的写法在 24.04 上已经走不通,apt-key命令通常不可用;
  • 密钥要放到独立 keyring 文件里,常见位置是/etc/apt/keyrings/或/usr/share/keyrings/;
  • 源条目里必须写signed-by或Signed-By指向这个文件,apt 才会使用它。

正是这三处变化,让不少照着旧文章操作的人一头撞上 GPG 密钥错误。这也是为什么网上关于这个问题的求助贴突然变多——不是大家操作变差了,而是系统规则变了。

2. 一次正确添加 Docker 官方仓库的完整操作

2.1 先确认系统状态和依赖

开工之前,先确认系统版本,确保代号确实是 noble:

cat /etc/os-release

输出里能看到VERSION_CODENAME=noble就可以了。如果你系统还停在 jammy,那不是 Ubuntu 24.04,错误模型也会略有不同,先把系统升上来再继续。

接着安装必要工具:

sudo apt update sudo apt install -y ca-certificates curl gnupg

这三个包各司其职:ca-certificates提供 HTTPS 证书链,curl负责把 GPG 公钥文件从 Docker 官方仓库拉下来,gnupg则用来处理 OpenPGP 公钥的格式转换。缺了gnupg,后面执行gpg --dearmor会直接报 command not found;缺了curl,下载那一步就没法做。顺手把 ca-certificates 装上,也能避免后续出现证书类报错。

2.2 下载 GPG 密钥并转成二进制 keyring

这一步是避免NO_PUBKEY的关键。推荐命令如下:

sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg

几个点容易忽略。install -m 0755 -d确保目录存在且权限正确,避免后面写入时遇到问题。下载下来的原始内容是 ASCII armored 格式,开头是-----BEGIN PGP PUBLIC KEY BLOCK-----,而 apt 官方推荐把它转成二进制 keyring 文件,gpg --dearmor正是干这个的。最后chmod a+r很重要,apt 进程读取 keyring 文件时如果权限是600而只有某个用户可读,就会出现权限类报错,报错信息里通常带着permission denied字样。

步骤执行完,可以用下面这条命令验证文件已经是合法的 keyring:

sudo gpg --show-keys /etc/apt/keyrings/docker.gpg

输出会列出公钥算法、创建时间和完整指纹。指纹的内容建议与 Docker 官方文档公布的一致,确认你拿到的不是某个奇怪来源替换过的文件。这一步虽然多花十秒,但能省下后面大量排查时间。

2.3 在独立源文件中挂载 docker 仓库

不要把 Docker 源直接塞进/etc/apt/sources.list,而是放到独立的/etc/apt/sources.list.d/docker.list文件里。这样做有两个理由:一是便于排查和移除,二是单个仓库故障不会污染主 sources.list。24.04 也推荐这种模块化管理方式。

传统单行格式:

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu noble stable" | sudo tee /etc/apt/sources.list.d/docker.list

其中arch=$(dpkg --print-architecture)自动展开为 amd64 或 arm64,signed-by指向刚才生成的 keyring 文件,noble是发行版代号,stable是 Docker 仓库的组件名。

24.04 也完全支持 deb822 格式的.sources文件,可读性更好:

sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null <<'EOF' Types: deb URIs: https://download.docker.com/linux/ubuntu Suites: noble Components: stable Signed-By: /etc/apt/keyrings/docker.gpg EOF

两种写法效果等价。如果你系统的其他源已经在用.sources格式,那我建议统一用 deb822 风格,看起来更清晰,也更能融入 Ubuntu 24.04 的风格。

2.4 更新并验证安装结果

执行:

sudo apt update

正常情况会看到 docker 仓库的InRelease被 Get 下来,不再有签名错误。随后检查包是否可见:

apt-cache policy docker-ce

输出里如果能出现候选版本号,说明仓库添加成功,之后执行sudo apt install docker-ce docker-ce-cli containerd.io就能顺利装上。如果apt-cache policy里什么都查不到,大概率是源条目没有被 apt 读取,或者仓库地址本身有问题,继续看后面的章节。

3. 高频 GPG 错误逐个拆解与修复

3.1 NO_PUBKEY:密钥根本没有落进系统

这个报错最常见。除了完全没有执行密钥导入步骤外,还有几个隐藏原因:keyring 文件路径写错,源条目里的signed-by指向了不存在的文件;密钥文件虽然存在,但内容不是真正的公钥,而是一个 HTML 错误页或空文件;系统时间偏差太大,导致 apt 在验证签名时产生了异常表现。

排查步骤很简单:

ls -l /etc/apt/keyrings/docker.gpg sudo gpg --show-keys /etc/apt/keyrings/docker.gpg

如果ls显示文件不存在,就去检查是不是当初写错了目录名,比如写成了/etc/apt/keys而不是/etc/apt/keyrings。如果gpg --show-keys报 "No public key" 或类似提示,说明文件损坏,直接重新生成即可。

修复手段:

sudo rm -f /etc/apt/keyrings/docker.gpg curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg sudo apt update

这里提醒一句:不要只删 keyring 文件而忘了修正源文件里的signed-by路径。路径写错是NO_PUBKEY的温床。

3.2 gpg: no valid OpenPGP data found:下载到的内容是错的

这个错误出现在gpg --dearmor一步,意思是 gpg 收到的东西根本不是 ASCII armored 公钥。最常见的情况是 curl 成功返回了 HTTP 200,但内容是 HTML 页面,比如网关、防火墙的拦截页,或者 CDN 临时返回的错误页。curl -fsSL的-f只对 HTTP 错误码生效,不会拦截内容本身,所以 gpg 拿到 HTML 后照样会抱怨。

怎么确认?执行:

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | head -n 5

正常应该看到:

-----BEGIN PGP PUBLIC KEY BLOCK-----

如果看到<!DOCTYPE html>或一段 JSON,那就是网络出口有问题。这个场景多发生在某些办公网络、公共 WiFi 或网络服务商对 download.docker.com 做了特殊处理的环境里。处理办法:

  • 换个能正常访问该域名的网络出口,手机热点通常最快;
  • 稍等片刻重试,CDN 抖动过一会儿会恢复;
  • 找一台能正常下载 gpg 文件的机器,把文件合法传输回本机再使用。

这类问题排查到最后会发现根本不是 apt 的配置错误,而是拿不到正确的源头文件。别在那条命令上反复重试,先看内容。

3.3 Key is stored in legacy trusted.gpg keyring 警告

很多从旧版本升级到 24.04 的系统,以前用apt-key add导入过 Docker 密钥,升级后每次apt update都会刷出一条很长的警告,内容大致是:

Key is stored in legacy trusted.gpg keyring (/etc/apt/trusted.gpg), see the DEPRECATION section in apt-key(8) for details.

这个警告不直接影响安装,但说明系统处于一种不规范且存在风险的信任状态:密钥被全局信任,任何使用该密钥签名的包都能通过验证。修复的方向是把密钥搬到独立 keyring,并在 Docker 源条目里写signed-by。

实际操作时要注意,24.04 上apt-key del这种命令已经不可靠了。正确方向是:先把 Docker 源条目改成 2.3 里带signed-by的写法,再把原来全局信任的密钥文件移除。旧系统里密钥可能出现在/etc/apt/trusted.gpg.d/目录下,也可能直接写在/etc/apt/trusted.gpg这个二进制文件里。操作前先备份:

sudo cp -a /etc/apt/trusted.gpg /etc/apt/trusted.gpg.bak 2>/dev/null || true sudo cp -a /etc/apt/trusted.gpg.d /etc/apt/trusted.gpg.d.bak 2>/dev/null || true

备份后找出属于 docker 的密钥文件并删除,再按 2.2 和 2.3 重新导入和配置,警告就会消失。如果你确认这台机器上 Docker 密钥只存在于/etc/apt/trusted.gpg.d/docker.gpg,直接删除那个文件是最快的。

3.4 The repository ... does not have a Release file

这个报错虽然不直接带 GPG 字样,但经常和 GPG 问题混在一起出现。它通常意味着仓库 URL 指向的 dists 目录里,没有你要找的发行版代号。比如系统明明是 noble,但sources.list.d里写的是:

deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu focal stable

Docker 仓库明明支持 noble,你却被旧教程带着写了 focal,apt 去访问不存在的目录,自然找不到 Release 文件,于是判定仓库无效。

排查方式很直接:

curl -fsSL -o /dev/null -w "%{http_code}\n" https://download.docker.com/linux/ubuntu/dists/noble/Release

返回200说明 noble 在 Docker 仓库里存在;返回404说明代号写错。还有一个隐蔽场景:.sources文件里Suites: noble stable这种写法会把stable误当成套件名,语法上不成立。按 2.3 的模板写最省心,不要自己加料。

3.5 证书类错误:unknown authority 和证书失效

报错里如果出现Certificate is signed by an unknown authority,通常不是 GPG 密钥问题,而是系统信任链或时间问题。我排查这类报错的固定顺序:

date sudo timedatectl set-ntp true systemctl restart systemd-timesyncd sudo apt install --reinstall ca-certificates

如果系统时间比真实时间差了好几天,OpenPGP 签名验证会受公钥有效期影响而失败,apt 对 HTTPS 证书的校验也会一起异常。先校正时间再看签名错误是否消失。对于已经装有 ca-certificates 的机器,强制重装一遍还能把可能损坏的证书链文件补回来。

4. 不要盲猜,用一套流程把问题定位出来

4.1 快速核对 keyring 与仓库条目

遇到任何 GPG 错误,我都建议先做一次信息收集。最常看的三个位置是 keyring 文件、Docker 源文件和 apt 日志:

sudo gpg --show-keys /etc/apt/keyrings/docker.gpg cat /etc/apt/sources.list.d/docker.list sudo tail -n 30 /var/log/apt/term.log

gpg --show-keys确认密钥文件可解析;cat确认signed-by路径与实际一致;日志里能看到 apt 到底在哪一步失败。有时候错误已经写在日志里,只是apt update的终端输出把它吞掉了一部分。日志文件记录的信息比终端更完整,值得养成先看日志的习惯。

4.2 用 apt-cache 和 curl 交叉验证

如果仓库配置看着没问题,但apt update依旧报错,用 curl 直接访问源地址能快速区分是本地还是远端问题:

curl -fsSL https://download.docker.com/linux/ubuntu/dists/noble/Release | head -n 5 curl -fsSL https://download.docker.com/linux/ubuntu/gpg | head -n 3

第一个应该输出 Release 文件头部,第二个应该输出BEGIN PGP PUBLIC KEY BLOCK。任何一个不像预期,问题大概率在网络层或仓库地址本身。如果仓库访问正常,再清理一次 apt 的缓存状态:

sudo rm -rf /var/lib/apt/lists/*docker* 2>/dev/null sudo apt clean sudo apt update

清理 lists 目录有时能解决因缓存了错误签名元数据而导致的顽固报错。这类问题不是每次都出现,遇到一次就能体会到先清缓存的好处。

4.3 常见 GPG 错误速查表

报错关键字直接原因最快定位修复方向
NO_PUBKEY xxxx本地没有对应公钥gpg --show-keys /etc/apt/keyrings/docker.gpg重建并导入 keyring
no valid OpenPGP data下载到 HTML 或空文件curl .../gpg | head -n 5换网络出口重试
legacy trusted.gpg keyring密钥放进了全局信任区检查/etc/apt/trusted.gpg*迁移到独立 keyring
does not have a Release file代号或组件名错误curl -I .../dists/noble/Release修正源条目中的代号
unknown authorityCA 缺失或时间不准date+ 重装 ca-certificates校准时间并重装 CA 包
permission deniedkeyring 文件不可读ls -l .../docker.gpgchmod a+r修复

表格里是浓缩后的常见场景。实际排查时不要拘泥于单个报错文本,要把源条目、keyring 文件、网络环境、系统时间放在一起看。绝大多数情况下,一件事做好了——密钥正确导入到独立 keyring 并被signed-by引用——GPG 类错误会消失大半。

5. 关于密钥管理,我踩过几次坑之后的一些习惯

5.1 每个仓库一个专用 keyring,不要全局信任

我在很长一段时间里都习惯把所有第三方 GPG 密钥一股脑导进/etc/apt/trusted.gpg.d/,因为早年教程都这么写。后来改成独立 keyring 后,才真正意识到全局信任的风险边界有多大:一个被攻陷或误用的密钥,可以给这台机器上所有仓库的包开绿灯;而独立 keyring 配合signed-by,只是把这个仓库的信任范围圈在一个文件里。哪怕要维护 Docker 和其他多个第三方源,我也坚持每个源一个.gpg文件。后续发现某个第三方仓库不再维护,移除时也很干净:删掉 keyring 和源文件,什么都不剩。

5.2 升级系统后先确认上游仓库支持新代号

Ubuntu 发布新版本后,第三方仓库不一定立刻跟进。Ubuntu 24.04 发布时,多数主流软件都已经补上 noble 目录,但总有个别仓库会慢几周。如果你在apt update时看到仓库不可用的报错,先别急着怀疑自己的操作,拿 curl 去访问一下dists/noble/Release,返回 404 就是上游还没跟上来。这种情况只能等上游更新,或者暂时用旧版本源过渡,但不要在新系统里长期混用旧代号源,依赖解析迟早出问题。

5.3 把配置固化成脚本,避免重复踩坑

我现在装新机器的时候,会把密钥导入、源文件写入整理成一个简短脚本,每次直接运行:

sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu noble stable" | sudo tee /etc/apt/sources.list.d/docker.list sudo apt update

脚本里刻意不用旧的apt-key写法,也不把源塞进主sources.list,这样即使半年后再跑也没问题。有个小技巧可以让脚本在不同版本间通用:直接取当前系统的代号,而不是写死 noble。

CODENAME=$(. /etc/os-release && echo "$VERSION_CODENAME") echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $CODENAME stable" | sudo tee /etc/apt/sources.list.d/docker.list

只要上游仓库已经支持当前系统,这个脚本就能自动适配版本,省去每次手改代号的麻烦。这也是我在反复遇到 GPG 错误后,最终沉淀下来的习惯:与其每次修,不如把正确流程固定下来。

返回列表