docker pull nginx 一行代码背后的秘密 — 省略域名时它会去哪里? 🐳

“为什么我只输入了 nginx,镜像就被拉取下来了?”

— 这个初学者最先好奇的问题,

蕴含着Docker注册表的核心哲学。

>

🎯 本文涵盖内容

  • Docker镜像引用(reference)的完整格式和省略规则
  • nginx → docker.io/library/nginx:latest 的三阶段扩展机制
  • 纯净Docker Engine是否可以更改默认注册表 (剧透: 不能)
  • 通过registry-mirrors和Podman的unqualified-search-registries进行规避的实战方法
  • 短名称(short name)带来的安全威胁 — 域名抢注(typosquatting)

📌 为什么这个主题很重要

每天敲打几十次的 docker pull nginx。它如此自然,以至于你可能从未怀疑过。然而,这一行代码实际上是经过三次自动校正的结果。如果你不知道这些校正规则,在以下情况下会立即受阻:

  • 需要将镜像来源统一到公司内部私有注册表时
  • 需要在Docker Hub访问受限的环境中部署时,例如中国或中东
  • 需要规避Docker Hub的拉取速率限制 (匿名用户每小时100次) 时
  • 需要防御域名抢注攻击 (类似名称的恶意镜像) 时

也就是说,这个机制不仅仅是一个“便利功能”,而是与运营、安全、成本交织在一起的重要设计点。

🔍 镜像引用的完整格式

Docker镜像引用遵循以下结构:

[REGISTRY[:PORT]/]NAMESPACE/REPOSITORY[:TAG|@DIGEST]
组成部分 说明 省略时的默认值
REGISTRY 存储镜像的注册表域名 docker.io
NAMESPACE 组织·用户·特殊命名空间 library (仅限docker.io)
REPOSITORY 镜像仓库名称 (不可省略)
TAG 版本·变体标识符 latest

因此,当Docker守护进程内部处理 docker pull nginx 这一行代码时,会依次发生以下三个阶段的自动校正:

nginx
  ↓ (1) 레지스트리 생략 → docker.io 붙임
docker.io/nginx
  ↓ (2) 네임스페이스 생략 → library 붙임
docker.io/library/nginx
  ↓ (3) 태그 생략 → latest 붙임
docker.io/library/nginx:latest

实际上,当你执行 docker pull nginx 时,最后一行会显示 docker.io/library/nginx:latest。Docker会友好地告诉你:“我实际上是这样解释的。”

library 命名空间的真实身份

这里有一个常见的误解。library 命名空间是docker.io注册表专用的特殊规则,而不是通用约定。这意味着此规则不适用于Quay.io或GCR等其他注册表。

# 只有在 docker.io 上才会发生 library 自动补全
docker pull nginx                       # ✅ docker.io/library/nginx:latest
docker pull quay.io/nginx                # ❌ 不会变为 quay.io/library/nginx
docker pull quay.io/bitnami/nginx        # ✅ 必须明确指定命名空间

因此,从docker.io以外的注册表拉取镜像时,必须使用包含命名空间的完整路径(FQIN, Fully-Qualified Image Name)

🔧 可以更改默认注册表吗?

结论如下:

运行时默认注册表更改备注

运行时默认注册表 更改 备注
Docker Engine (纯净版) ❌ 不可能 硬编码为docker.io
Docker Engine + registry-mirrors 🔶 仅可规避 docker.io的直通镜像
Podman / CRI-O / Buildah ✅ 可能 unqualified-search-registries
RHEL系列补丁Docker ✅ 可能 ADD_REGISTRY, BLOCK_REGISTRY 选项 (遗留)

1) 纯净Docker Engine — 默认注册表无法更改

上游Docker不提供将默认注册表更改为其他位置的选项。Docker Hub始终是最终的搜索目的地。这是有意为之的设计,是一种保守的选择,旨在减少名称冲突和攻击面。

2) registry-mirrors — 并非“更改”,而是“插入到前面”的方式

相反,Docker允许你注册一个Docker Hub的直通缓存镜像(pull-through mirror)。你可以按如下方式修改 /etc/docker/daemon.json

{
  "registry-mirrors": [
    "https://mirror.gcr.io",
    "https://registry.mycompany.internal"
  ]
}

然后重启守护进程。

sudo systemctl restart docker

此设置的工作原理如下:

  1. 当收到 docker pull nginx 请求时,
  2. Docker守护进程首先按顺序检查已注册的镜像,
  3. 如果镜像中存在该镜像,则从那里拉取,
  4. 如果不存在,最终会去docker.io

也就是说,默认值并非被“替换”,而是被“插入到前面”。Docker Hub作为最终后端的事实并未改变。这是在韩国、中国等Docker Hub访问缓慢或不稳定的地区常用的模式。

3) Podman — 真正可以更改默认注册表

另一方面,Podman、Buildah、CRI-O系列可以通过 /etc/containers/registries.conf 完全自由配置。

# /etc/containers/registries.conf

# 搜索短名称(例如:nginx)的注册表列表 — 按顺序搜索
unqualified-search-registries = [
  "registry.mycompany.internal",
  "quay.io",
  "docker.io"
]

# 短名称解析模式
# 如果是 "enforcing",当模糊时强制用户选择
short-name-mode = "enforcing"

# 为 docker.io 添加镜像
[[registry]]
prefix = "docker.io"
location = "docker.io"

  [[registry.mirror]]
  location = "mirror.mycompany.internal:5000"

这样设置后,执行 podman pull nginx 时,会从 registry.mycompany.internal 开始依次搜索。这比Docker灵活得多。这种差异也是RHEL·OpenShift生态系统选择Podman而非Docker作为默认的几个原因之一。

⚠️ 短名称带来的安全威胁 — 域名抢注(Typosquatting)

能够更改默认注册表很方便,但反过来也是一个陷阱。如果在 unqualified-search-registries 中注册了多个注册表,那么镜像来自哪个注册表就会变得模糊不清。

例如,如果搜索顺序是 ["registry1.com", "registry2.com"],而攻击者先在 registry1 上传了一个名为 nginx 的恶意镜像,那么即使真正的 nginxregistry2 上,registry1 上的恶意镜像也会被优先拉取。这就是短名称抢注(short-name squatting)攻击。

3条防御原则

  1. 使用FQIN: 在生产环境中,始终明确指定注册表、命名空间和标签,例如 docker.io/library/nginx:1.27.3
  2. 固定摘要(Digest): 更严格的做法是固定哈希值,例如 nginx@sha256:72297...。确保镜像完全一致。
  3. Short-name-mode = “enforcing” (仅限Podman): 当短名称模糊时,配置为强制用户确认选择。

✅ 总结

docker pull nginx 这一看似普通的一行代码,实际上是经过注册表 → 命名空间 → 标签三次自动校正,最终扩展为 docker.io/library/nginx:latest 的结果。这个规则是Docker Hub这个特定注册表的惯例,并非容器世界通用的语法。

核心要点总结如下:

  • 纯净Docker Engine无法更改默认注册表 — 只能通过 registry-mirrors 在前面插入缓存。
  • Podman系列可以通过 unqualified-search-registries 完全自由配置。
  • 便利性的代价是模糊性 — 在生产环境中,务必养成使用FQIN或固定摘要的习惯。

如果你对下一步感兴趣,建议按以下顺序学习:Docker Hub的拉取速率限制规避策略构建Harbor·ECR等私有注册表基于Sigstore的镜像签名验证(cosign)、以及CI/CD流水线中的摘要固定自动化



Comments

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注