本节摘要:docker tag 给镜像挂仓库引用名,docker push 把镜像发运到仓库,内容摘要(digest)则是签收时的核对凭据。本节讲清 tag 是改名不是复制的本质、引用名的构成规则、推送输出里层挂载的含义,以及为什么 CI 与生产都改用摘要而不用标签引用镜像。
某天凌晨发布后,有同事问:为什么同一个镜像在仓库里有好几个名字?它们各自占空间吗?删一个名字会删掉镜像吗?这些问题全指向同一条命令——tag。它是仓库部使用频率最高、误解也最深的词条,本节把它与 push 串成完整链路讲透。
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG] docker push [OPTIONS] NAME[:TAG]
$ docker images myweb REPOSITORY TAG IMAGE ID CREATED SIZE myweb 0.1 8c4e21ab77d2 2 days ago 112MB $ docker tag myweb:0.1 registry.local:5000/myweb:0.1 $ docker tag myweb:0.1 registry.local:5000/myweb:1 $ docker images | grep 8c4e21ab77d2 myweb 0.1 8c4e21ab77d2 2 days ago 112MB registry.local:5000/myweb 0.1 8c4e21ab77d2 2 days ago 112MB registry.local:5000/myweb 1 8c4e21ab77d2 2 days ago 112MB
三个引用名,同一个 IMAGE ID——tag 没有复制任何数据,只是在镜像的内容之上又挂了一块门牌。所以删掉其中一个名字(rmi 输出 Untagged)不占空间也不伤内容,只有最后一个名字被摘掉时层才真正可回收。理解了这一点,"给镜像换个仓库名就能推"这句话才真正落地:push 认的是引用名里的仓库地址部分,tag 就是把地址写进名字的工序。
引用名的完整构成值得背下来:[仓库地址/]命名空间/名字[:标签]。带地址(如 registry.local:5000/...)就推往自建仓库,不带则默认公共仓库;命名空间在公共仓库是账号或组织名。4.2 推送前的 tag 工序,写的正是这个规则。
$ docker push registry.local:5000/myweb:0.1 The push refers to repository [registry.local:5000/myweb] c4d1a9e2b3f0: Pushed 9c1b6e4a2f0d: Mounted from library/mybase 8a3e1c9d4b2a: Mounted from library/mybase 0.1: digest: sha256:8b42de6f... size: 1159
逐行读。Pushed 表示该层从本机上传;Mounted from 是仓库侧的妙处——这层内容仓库里已经有了(比如来自之前推过的基础镜像),直接引用现成数据,不用再传。所以第二次推同系镜像通常快得惊人:变的只有应用层,基础层全挂载。这正是贰部分层思想在仓库侧的回响。
⚠️ 常见坑:push 被拒报 denied: requested access to the resource is denied,多数时候不是权限故障,而是引用名里的仓库地址或命名空间写错——它想推去你没有权限的地方。先 docker images 核对引用名,再 docker login 对应仓库,最后重推。
$ docker images --digests registry.local:5000/myweb REPOSITORY TAG DIGEST IMAGE ID registry.local:5000/myweb 0.1 sha256:8b42de6f... 8c4e21ab77d2 $ docker pull registry.local:5000/myweb@sha256:8b42de6f...
标签是可变的——同名标签今天与昨天可以指向不同内容;摘要是内容的哈希,内容变则摘要变,不可伪造。两者的差别在发布场景里是事故级的:生产机写 myweb:1,仓库侧有人重新推了同标签,下次拉取拿到的就是另一个镜像;写摘要引用,拉到的永远是你验过的那份。CI 归档产物、生产部署清单,推荐一律用摘要引用。

链路上每一步对应的命令此前都已就位:build 在贰部、tag 与 push 在本节、pull 在贰部、摘要核对在本节。把这条链跑熟,镜像的发布事务就没有黑箱了。
把开篇同事的问题收尾:同一镜像挂了多个名字后执行 rmi,删的是名字还是镜像?现场演示:
$ docker rmi registry.local:5000/myweb:1 Untagged: registry.local:5000/myweb:1 $ docker rmi myweb:0.1 Error response from daemon: conflict: unable to delete 8c4e21ab77d2 (must be forced) - image is referenced in multiple repositories
第一条输出只有 Untagged——摘牌,层数据原地不动。第二条报 conflict:这个 IMAGE ID 还挂在 registry.local:5000/myweb:0.1 名下,守护进程拒绝误删,除非 --force。规则一句话:rmi 默认只删名字,轮到镜像的最后一个名字被摘时才动层数据。--force 留给明确知道自己在做什么的时刻,一般它都不该出现。
再补一条 CI 实践:push 之后立即用 docker inspect --format '{{index .RepoDigests 0}}' 取回执摘要写进部署清单。日后生产对账,清单上的摘要与仓库现值逐字比对,就能确认线上跑的正是验收过的那份——比按标签部署再翻日志求证省心得多。
镜像的进出港手续到此齐备。叁部的容器删了数据就没了?伍部数据部专治这个不服:卷、绑定挂载与临时盘。