赛事档案库 YQHL Competition Archive 返回主站
DIAG

镜像 tag 诊断

唯一一道没有攻击面的题目:按上传记录引用镜像时缺少 tag,导致容器无法启动,考察对镜像坐标的基本认知。

方向诊断
难度 Easy
分值50 分
题号5 / 20
项目 内容
分类 / 难度 诊断 / Easy
分值 50
类型 容器启动故障排查(镜像引用缺 tag)
题面 「引用上传记录(无 tag)验证容器能否启动」

1. 这是一道什么样的题

DIAG 是本次比赛中唯一一道没有攻击面的题目。它给的不是一个能访问的服务,而是一个现象:某个环境按上传记录启动时,容器起不来。

题面只有一句话:

引用上传记录(无 tag)验证容器能否启动

关键信息全在这句话里:

  • 引用上传记录 —— 指的是按当初上传镜像时记录的镜像名去引用,而不是按完整坐标;
  • 无 tag —— 记录里只有仓库名 + 镜像名,没有 :标签;
  • 验证容器能否启动 —— 现象是启动失败。

结合分值只有 50,可以判断这是一道"看一眼就懂、不懂就卡住"的常识题。

2. 复现现象

在目标节点上,按"上传记录"里的名字去引用同一个镜像 —— 也就是不带 tag:

$ docker images | grep check-01
lj-check-01-trap:v1         42206e37b9e4        148MB         37.7MB   U

$ docker run -d --name diagtest lj-check-01-trap
Unable to find image 'lj-check-01-trap:latest' locally
docker: Error response from daemon: error from registry: ...

观察这行自动补全:

Unable to find image 'lj-check-01-trap:latest' locally

我们输入的是 lj-check-01-trap,Docker 自作主张补上了 :latest。

3. 根因

Docker 镜像的完整坐标是 [registry/][namespace/]repository[:tag|@digest]。tag 是可选部分,但省略时的语义不是"随便哪个版本",而是默认补 :latest。

于是发生了这个链条:

记录里的镜像名:  lj-check-01-trap
        │
        ▼  Docker 补全
实际查找的引用:  lj-check-01-trap:latest
        │
        ▼  本地镜像表里只有 :v1
本地找不到 → 转向远程仓库拉取
        │
        ▼  仓库里没有 :latest(或拉取失败)
启动失败

用 docker images 印证:本地只有 lj-check-01-trap:v1,没有 :latest。

$ docker images | grep check-01
lj-check-01-trap:v1         42206e37b9e4        148MB         37.7MB

而 :latest 这个标签在语义上并无特殊地位 —— 它只是一个普通标签名。构建时如果不显式 -t name:latest,就不会产生它。所以"上传了镜像"和"本地存在 :latest“是两件事。

顺带一个常见误判:删掉 :v1 并不会让 :latest 出现,反而会一起报错 ——

$ docker rmi lj-check-01-trap
Error response from daemon: No such image: lj-check-01-trap:latest

这也反向证明了 docker rmi 同样会做 :latest 补全。

4. 结论

容器起不来的直接原因是:引用时省略了 tag,Docker 将其补全为 :latest,而本地只有 :v1(远程也无 :latest),镜像解析失败。

5. 修复

最小改动 —— 补上 tag:

docker run -d --name app lj-check-01-trap:v1

如果确实需要 latest 语义(例如平台按无 tag 引用):

# 给现有版本打上 :latest
docker tag lj-check-01-trap:v1 lj-check-01-trap:latest

# 或者用 tag 指向不可变摘要,避免 latest 漂移
docker tag lj-check-01-trap@sha256:42206e37b9e4... lj-check-01-trap:latest

从根上修 —— 记录侧不要存裸仓库名:

  • 镜像上传记录里永远存完整坐标(name:tag)或摘要(name@sha256:...);
  • 生产环境优先用 digest 而非 tag 引用,因为 tag 可被覆盖,digest 不可变;
  • 若平台需要"最新"语义,构建流水线里显式打 :latest 并同时打版本 tag,别指望 Docker 自动生成。

校验一下:

$ docker inspect --format '{{<.RepoTags>}}' lj-check-01-trap:v1
[lj-check-01-trap:v1]

如果在 RepoTags 里看不到 latest,就一定不能用裸名引用。

6. 顺带记住的几个坑

写法 Docker 实际解释
nginx docker.io/library/nginx:latest
myrepo/app docker.io/myrepo/app:latest
registry:5000/app registry:5000/app:latest(端口不会被误判为 tag)
app@sha256:... 按摘要引用,tag 被忽略
app:v1 docker.io/library/app:v1

注意第三行:很多人以为 registry:5000/app 里的 :5000 是 tag —— 不会,Docker 只在最后一个 / 之后的 : 才当 tag 分隔符。所以 registry:5000/app 会被补成 registry:5000/app:latest。

7. 小结

这道题考的不是技巧,而是对镜像引用规则的确切记忆:

  • tag 省略 ⇒ 默认 :latest;
  • :latest 只是一个普通标签,不是"最新版本"的保证;
  • 上传记录里丢了 tag,等于把一个能启动的镜像变成了无法解析的引用。

一句话答案:镜像引用缺 tag,Docker 补成 :latest,而实际镜像只有 :v1,因此找不到镜像、容器启动失败 —— 补上 :v1 即可。

(本题无 flag,判定依据为诊断结论是否正确。)