大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
镜像仓库
这一课你将能做到
- 把自己构建的 Spring Boot 镜像打 tag 并推送到 Docker Hub / 阿里云 ACR,另一台机器一拉即用
- 用
registry:2在本地或内网搭一个私有镜像仓库并完成 push/pull - 建立正确的镜像版本管理习惯:命名空间、tag 规范、为什么禁止只用 latest
为什么要学
镜像构建在你自己的电脑上,但部署发生在服务器上——中间必须有一座桥。这座桥就是镜像仓库(Registry):CI 构建完推上去,任何环境拉下来就能跑,交付从「传 jar 包 + 部署文档」变成「pull 一个 tag」。
团队协作同样离不开它:你写的镜像同事要用、测试环境要拉、生产环境要回滚到上个版本——全部依赖仓库里的版本管理。国内企业普遍用阿里云 ACR 或自建 Harbor(Harbor 底层同样是 registry 的增强版),个人学习用 Docker Hub 足够。
先建立图像
镜像全名的完整结构,很多人第一次见:
[registry地址/][命名空间/]仓库名:标签
────────────────────────────────────
docker.io/library/mysql:8.0 ← docker pull mysql:8.0 的完整名字
registry.cn-hangzhou.aliyuncs.com/myteam/demo-app:1.0
localhost:5000/demo-app:1.0 ← 本地私有仓库
ghcr.io/myteam/demo-app:1.0 ← GitHub Container Registry
推拉的完整流程:
你的电脑 服务器
docker build ─→ docker tag ─→ docker push docker pull ─→ docker run
│ ▲
▼ │
Registry(存镜像、管版本)──────┘
没有 push,镜像只存在于你的机器上;没有 tag 规范,仓库很快变成垃圾场。
跟着做
步骤 1:注册并登录 Docker Hub
- 到
https://hub.docker.com注册账号(记下用户名,下面用<你的用户名>表示)。 - 本地登录:
docker login
# 输入用户名密码(或用 Access Token 更安全),看到 "Login Succeeded"
步骤 2:给镜像打规范的 tag
tag 必须带上你的命名空间,否则默认属于 library(官方保留):
docker tag demo-app:1.0 <你的用户名>/demo-app:1.0
docker images | findstr demo-app # 两个名字,同一个 IMAGE ID
步骤 3:推送到 Docker Hub
docker push <你的用户名>/demo-app:1.0
输出会逐层显示 Pushed / Layer already exists(复用过的层不重传,第 4 课分层的直接收益)。
步骤 4:在另一台机器验证拉取
删掉本地镜像再拉回来,模拟服务器行为:
docker rmi <你的用户名>/demo-app:1.0
docker pull <你的用户名>/demo-app:1.0
docker run -d --name demo-remote -p 8082:8080 <你的用户名>/demo-app:1.0
curl http://localhost:8082/actuator/health
到这里,「任何机器一拉即用」已经成立。
步骤 5:搭一个本地私有仓库
docker run -d --name local-registry -p 5000:5000 --restart always registry:2
向它推送(仓库地址是 localhost:5000):
docker tag demo-app:1.0 localhost:5000/demo-app:1.0
docker push localhost:5000/demo-app:1.0
如果报 http: server gave HTTP response to HTTPS client:registry:2 默认要求 HTTPS,本地需在 daemon.json 加 insecure 配置并重启引擎:
{
"insecure-registries": ["localhost:5000"]
}
验证私有仓库里的镜像可用:
curl http://localhost:5000/v2/_catalog # 列出仓库
curl http://localhost:5000/v2/demo-app/tags/list # 列出 tag
步骤 6:国内企业方案——阿里云 ACR 概览
- 阿里云控制台 → 容器镜像服务 → 个人实例(免费)→ 创建命名空间(如
myteam)。 - 创建镜像仓库(如
demo-app),得到地址registry.cn-hangzhou.aliyuncs.com/myteam/demo-app。 - 推送三件套:
docker login --username=你的阿里云账号 registry.cn-hangzhou.aliyuncs.com
docker tag demo-app:1.0 registry.cn-hangzhou.aliyuncs.com/myteam/demo-app:1.0
docker push registry.cn-hangzhou.aliyuncs.com/myteam/demo-app:1.0
内网机器(同 VPC 的 ECS)拉取走内网地址,速度接近本地磁盘。GHCR 用法同理:ghcr.io/<github用户名>/demo-app:1.0,用 GitHub PAT 登录。
讲清楚要点
- 镜像全名的寻址结构:
registry地址/命名空间/仓库名:tag。省略 registry 默认docker.io,省略命名空间默认library。理解这个结构,任何仓库(Hub/ACR/GHCR/Harbor)的用法都通了。 - tag 规范建议:
主版本.次版本.修订号(1.0.0)对应里程碑,配合 git commit 短哈希(1.0.0-a1b2c3d)保证可追溯;每个镜像多推一个1.0这样的滚动 tag 方便引用,但永远不要只用 latest。 - latest 的真实含义:它只是「最后一次被 docker push 时带上的名字」,既不保证最新,也不保证稳定。生产回滚场景里,
latest无法回答「线上跑的到底是哪个版本」。 - 登录凭据管理:
docker login的凭据存在本地凭据存储里;CI 上用 Access Token(Hub)或 RAM 账号(ACR)而非主账号密码,最小权限原则。 - 公库与私库的选择:开源项目用 Docker Hub 公库扩大影响力;商业代码一律私库(ACR/Harbor/GHCR 私有),公库泄漏业务 jar 等于泄漏代码——jar 可被反编译。
你可能会懵的地方
-
docker pull mysql为什么不需要完整地址? 省略时自动补全为docker.io/library/mysql:latest。官方镜像(如 mysql、redis、nginx)都在library命名空间下,个人镜像必须带自己的用户名。 -
push 报
denied: requested access to the resource is denied? 三种可能:没docker login;tag 的命名空间和登录账号不一致;对目标仓库无写权限。按顺序排查:先docker info看登录状态,再核对 tag 全名。 -
私有仓库和 Docker Hub 私有仓库有什么区别? 功能上都提供「仅授权可见」,差别在数据位置与网络:Hub 在境外有拉取限流与速度问题;自建 registry/Harbor 数据在自己机房,内网传输快、可控合规。企业里两者都可能有——开源依赖走 Hub 缓存,业务镜像走私库。
-
为什么推荐 tag 加上 git commit 哈希? 版本号(1.0.0)回答「这是哪一版」,commit 哈希回答「这一版对应哪行代码」。线上排障时拿镜像 tag 直接定位代码提交,是可追溯性的关键一环。
-
镜像能删吗?怎么删? Hub/ACR 网页端或 API 可删除 tag;自建 registry 删除需要先开启
REGISTRY_STORAGE_DELETE_ENABLED=true,再DELETE /v2/<name>/manifests/<digest>。习惯上更推荐「只推新不删旧 + 定期清理策略」,因为别的环境可能还在引用旧版本。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
docker push demo-app:1.0(无命名空间) | 被当作官方 library 仓库,无权限 | push <你的用户名>/demo-app:1.0 |
生产环境只引用 latest | 版本漂移,回滚无从谈起 | 固定语义化版本 + commit 哈希 tag |
| 把公司业务镜像推成公开仓库 | jar 可被任意下载反编译 | 私有仓库 + 访问控制 |
| 用主账号密码配 CI 登录 | 凭据泄漏影响面巨大 | 最小权限 Access Token / RAM 账号 |
| 自建 registry 忘配 insecure-registries | push 报 HTTPS 错误 | daemon.json 加配置或正确配 TLS |
| 镜像推完就不管 tag 清理 | 仓库膨胀,存储费用上升 | 保留最近 N 版 + 定期清理策略 |
卡点急救
- pull 限流
toomanyrequests:Docker Hub 匿名限流。docker login后重试;国内环境换阿里云 ACR 或配加速器(第 2 课)。 - push 极慢或中途断开:大镜像走境外网络不稳定。换 ACR(国内快)或压缩镜像体积(第 6 课多阶段构建)。
localhost:5000push 报 HTTPS 错误:daemon.json 的insecure-registries漏配或没重启引擎(systemctl restart docker/ Desktop Apply & Restart)。- 忘记登录账号对应哪个 token:
cat ~/.docker/config.json(Windows 在%USERPROFILE%\.docker\config.json)查看已登录的 registry 与凭据存储方式。 - 同事说拉不到你的镜像:让他核对完整镜像名(含命名空间)与仓库可见性(public/private),私有仓库需先登录且有权限。
面试常问
-
说一下你们项目的镜像版本管理策略? 构建时打两个 tag:语义化版本(或构建号)+ git commit 短哈希,推送到私有仓库;部署清单记录 digest;latest 仅本地开发使用。回滚 = 部署上一个版本 tag。
-
Docker Hub、Harbor、ACR 的区别? Docker Hub 是全球公共仓库(有限流);Harbor 是开源企业级私有仓库(含权限、漏洞扫描、镜像签名、复制同步,底层基于 registry);ACR 是阿里云托管服务(国内网络快,与阿里云 CI/CD 生态集成)。
-
CI/CD 中镜像如何流转? 流水线:拉代码 → 构建(多阶段 Dockerfile)→ 打 tag(版本+commit)→ push 私库 → 部署环境 pull 对应 tag → run。构建一次、多处部署同一镜像,是「交付物标准化」的落地。
-
为什么生产环境建议用 digest 部署? tag 可被重新推送覆盖,digest(sha256)由镜像内容唯一确定。以 digest 记录部署版本,能保证任何时间任何机器拉到的字节完全一致,审计可追溯。
自测三问
- 写出镜像全名的完整结构,并给出
docker pull redis:7实际请求的完整地址。 - 把
demo-app:1.0推送到阿里云 ACR 需要哪三步命令? - 为什么「只用 latest」在生产环境是事故隐患?举一个具体回滚场景说明。
小结一句话
仓库把镜像从「你电脑上的文件」变成「团队与环境的公共交付物」——理解全名寻址结构、守住 tag 规范,镜像才能真正流动起来。
延伸
- Docker Registry 官方文档
- 阿里云容器镜像服务 ACR
- 下一课预告:数据卷与挂载——解决容器化最经典的坑:容器删了,MySQL 数据没了。