2. Docker镜像核心:镜像仓库、命名规范与唯一标识

聊完了镜像的分层结构,咱们得聊聊怎么找到、管理和引用这些镜像。说白了,就是三个问题:镜像放哪儿?怎么叫它?怎么区分不同的版本?

这三个问题,对应着今天要讲的三个核心概念:镜像仓库(Registry)、命名规范(Name:Tag)、以及镜像ID与Digest。嗯,这三样东西,你每天用Docker都会碰到,但很多人其实没完全搞明白。

2.1 镜像仓库(Registry)——镜像的“超级网盘”

镜像仓库,你可以把它想象成一个专门存Docker镜像的网盘。只不过这个网盘是分布式的,而且有严格的版本管理。

公共仓库 vs 私有仓库

  • Docker Hub:最常用的公共仓库。我刚开始用Docker时,基本全靠它。但后来发现,公司内部的项目不能随便往上传,于是开始搭私有仓库。
  • 私有仓库:比如 Harbor、Nexus、或者直接用 Docker Registry 官方镜像搭一个。我个人习惯用 Harbor,因为它带 Web UI 和权限管理,省心不少。

仓库地址的完整格式

你平时可能只写 nginx:latest,但完整的镜像地址其实是这样的:

[仓库地址]/[命名空间]/[镜像名]:[标签]
# 例如:
docker.io/library/nginx:latest
myregistry.example.com/myproject/myapp:v1.0

如果不写仓库地址,Docker 默认去 docker.io(也就是 Docker Hub)拉取。我在项目中遇到过有人把私有仓库的镜像推到 Docker Hub 上去了,结果死活拉不下来——就是因为没写仓库地址。

小技巧: 如果你在公司内网,记得配置 /etc/docker/daemon.json 里的 insecure-registries,否则 HTTPS 证书问题会让你抓狂。

2.2 镜像命名规范(Name:Tag)——别让你的镜像“裸奔”

镜像名 + 标签,就是镜像的“身份证号+姓名”。但很多人只写镜像名,不写标签,结果拉下来的版本完全不对。

标签的常见用法

标签写法 含义 风险
nginx:latest 最新版 每次拉取可能不同,生产环境慎用
nginx:1.21.5 具体版本号 稳定,推荐
nginx:1.21 大版本号 可能指向不同的小版本
myapp:20240301 日期标签 适合CI/CD流水线

我踩过的坑: 曾经有一次,生产环境的 CI 脚本里写的是 myapp:latest。结果某天开发团队推送了一个有 bug 的版本,标签也是 latest,生产环境自动拉取后直接崩溃。从那以后,我要求所有生产环境必须用 具体版本号 或 Git commit SHA 作为标签。

警告: 永远不要在生产环境使用 :latest 标签!你永远不知道它指向的是哪个版本。

2.3 镜像ID与Digest——真正的“指纹”

标签是可以变的,但镜像ID和Digest是唯一的。就像人的名字可以改,但指纹改不了。

镜像ID

每个镜像层都有一个唯一的 SHA256 哈希值,镜像ID 就是最顶层(也就是镜像的元数据层)的哈希值。你可以用 docker images 看到它:

$ docker images
REPOSITORY    TAG       IMAGE ID       CREATED       SIZE
nginx         latest    f0f8b8b8b8b8   2 weeks ago   187MB

这个 f0f8b8b8b8b8 就是镜像ID。但注意,它只取前12位,完整的哈希有64位。

Digest——更可靠的标识

镜像ID 可能会因为 docker tag 操作而改变?不会。但 Digest 是真正不可变的。它是对整个镜像清单(Manifest)的哈希值。

你可以这样查看 Digest:

$ docker images --digests nginx
REPOSITORY    TAG       DIGEST                                                                    IMAGE ID       CREATED       SIZE
nginx         latest    sha256:abc123...def456                                                  f0f8b8b8b8b8   2 weeks ago   187MB

或者用 docker inspect:

$ docker inspect nginx:latest | grep -A 10 "RepoDigests"
"RepoDigests": [
    "nginx@sha256:abc123...def456"
]

为什么 Digest 这么重要?

你想想看,如果有人把 nginx:latest 标签指向了一个恶意镜像,你拉下来就中招了。但如果你用 Digest 拉取:

docker pull nginx@sha256:abc123...def456

那无论标签怎么变,拉下来的永远是那个确定的镜像。我在做安全审计时,要求所有生产环境必须记录 Digest,而不是标签。这样即使镜像仓库被篡改,也能保证一致性。

核心要点:
  • 标签(Tag)是给人看的,方便记忆
  • 镜像ID 是给 Docker 引擎用的,用于本地识别
  • Digest 是给安全审计用的,保证不可篡改

2.4 知识体系图:镜像核心概念关系

下面这张图,帮你理清 Registry、Name:Tag、ID、Digest 之间的关系:

Docker 镜像仓库 (Registry) 命名空间 (Namespace) 例如:library / mycompany / myproject 镜像名 (Name) 例如:nginx / redis / myapp 标签 (Tag) 可变,例如:latest / v1.0 Digest (不可变) sha256:abc123... 唯一指纹 镜像ID (本地标识,前12位哈希) 例如:f0f8b8b8b8b8 Name:Tag 指向一个镜像版本,Digest 是它的不可变指纹,镜像ID 是本地存储的标识

2.5 实战建议

说了这么多,给你几个我实际工作中的建议:

  1. CI/CD 中永远用 Digest 记录版本:在构建完成后,用 docker images --digests 获取 Digest,写入部署配置文件。这样回滚时也能精确找到之前的版本。
  2. 私有仓库一定要配清理策略:我见过有人把测试镜像堆了几百个,磁盘直接爆了。用 Harbor 的话,可以设置保留最近 N 个版本。
  3. 标签命名要有规范:比如 项目名-环境-版本号,像 myapp-prod-v2.3.1。别用 test1、final 这种让人摸不着头脑的名字。
  4. 拉取镜像时指定平台:如果你在 ARM 机器上拉 x86 镜像,会报错。用 --platform linux/amd64 指定一下就好。
一个小命令: 想查看某个镜像的所有标签?用 curl 直接调 Registry API:
curl -s https://hub.docker.com/v2/repositories/library/nginx/tags | jq '.results[].name'

嗯,镜像仓库、命名规范、ID 和 Digest,这三样东西搞明白了,你就能在镜像管理上少踩很多坑。说白了,标签是给人看的,Digest 是给机器用的,镜像ID 是给 Docker 自己用的——各司其职。

可可精品资料库