今天和小伙伴们,聊一个工作中遇到的小问题~
背景
公司项目是基于容器化架构设计,具体业务也拆成了多个微服务及对应了多个docker image镜像。研发环境中会频繁进行升级image镜像,导致出现很多image为 <none>
命名的镜像,这种也称为悬空镜像。
本篇文章将展示几种现象会导致这种情况发生。
下文演示环境,版本信息如下
- Centos8
- docker server: 23.0.1
- docker client: 20.10.17
- containerd: 1.6.18
- runc version: 1.1.4
现象一
前提条件: 在同环境中两个镜像名称:tag
都相同,image层数据内容不一致,覆盖后则会出现 <none>
标志。
注意: 如果仅是镜像名称:tag相同则不会覆盖,也不会出现 <none> 标志。 因为image层数据没有发生变化,docker认为是同一个镜像。
shell
[root@k8s-host docker]# cat > Dockerfile << EOF
> FROM alpine:latest
> ADD test.txt /root
> EOF
[root@k8s-host docker]# echo 1 > test.txt
[root@k8s-host docker]# docker build -t alpine1:v1.0 .
[+] Building 0.1s (7/7) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 134B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/alpine:latest 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 96B 0.0s
=> CACHED [1/2] FROM docker.io/library/alpine:latest 0.0s
=> [2/2] ADD test.txt /root 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:aa416b5c47216b392755ba2b4ad08f5531f3aa86c103744e261e49ce7a354021 0.0s
=> => naming to docker.io/library/alpine1:v1.0 0.0s
[root@k8s-host docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
alpine1 v1.0 aa416b5c4721 30 seconds ago 7.34MB
该镜像是基于alpine:latest镜像进行二次编译
Dockerfile中仅增加 test.txt 用来区分层内容
Dockerfile
FROM alpine:latest
ADD test.txt /root
上述步骤中执行 docker images
可以看到 alpine1:v1.0 已经存在镜像ID aa416b5c4721
的容器镜像。
下面继续编译镜像,名字与 alpine1:v1.0 相同, 仅仅将Dockerfile中 test.txt 文件内容修改。
进行第二次加载镜像,此时系统已存在相同的镜像名称。
shell
[root@k8s-host docker]# echo 2 > test.txt
[root@k8s-host docker]# docker build -t alpine1:v1.0 .
[+] Building 0.1s (7/7) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 134B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/alpine:latest 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 96B 0.0s
=> CACHED [1/2] FROM docker.io/library/alpine:latest 0.0s
=> [2/2] ADD test.txt /root 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:e4159797272a190d20856475858bab23d11e64d6bdd9a57ca8a85ad3c3a17087 0.0s
=> => naming to docker.io/library/alpine1:v1.0 0.0s
[root@k8s-host docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
alpine1 v1.0 e4159797272a 3 seconds ago 7.34MB
<none> <none> aa416b5c4721 6 minutes ago 7.34MB
原来的 alpine1:v1.0 镜像ID为 aa416b5c4721 的docker镜像目前属于 none 标记,这是因为有了新的镜像名称相同造成。虽然是none标记,如果之前有引用的容器仍然可以使用。
注意:如果将 alpine1:v1.0 镜像ID为 e4159797272a 的docker镜像删除, 下面的none标记也不会发生变化。只有当时的 Dockerfile中的 ADD 文件重新docker build -t alpine1:v1.0编译,此处的 none 标记才能显示正常。
现象二
docker save 时镜像没有指定镜像名称:tag,而是使用的镜像ID。
shell
[root@k8s-host docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
alpine1 v1.0 e4159797272a 3 seconds ago 7.34MB
[root@k8s-host docker]# docker save e4159797272a | gzip > alpine1.docker
[root@k8s-host docker]# docker rmi e4159797272a
Untagged: alpine1:v1.0
Deleted: sha256:e4159797272a190d20856475858bab23d11e64d6bdd9a57ca8a85ad3c3a17087
[root@k8s-host docker]# docker load < alpine1.docker
Loaded image ID: sha256:e4159797272a190d20856475858bab23d11e64d6bdd9a57ca8a85ad3c3a17087
[root@k8s-host docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> e4159797272a 21 minutes ago 7.34MB
操作中,将alpine1:v1.0镜像没有按照镜像:tag方式 save,而是使用 docker save e4159797272a
镜像ID格式保存, 仅仅微小的变化会导致镜像加载时的效果不同。
当执行完 docker load < alpine1.docker
加载镜像后发现,此时的镜像ID还是原来没有变化,而镜像名称:TAG 都变成none。
注意:如果 docker save 导出镜像时不指定 镜像名称:tag 这种方式,在docker load 加载镜像时会丢失镜像名称和tag标记。
现象三
正运行的容器所引用的镜像,将其强行删除镜像docker images会出现none标记
shell
[root@k8s-host docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
alpine4 v1.0 7ea0f12a3321 7 hours ago 7.34MB
[root@k8s-host docker]# docker rmi alpine4:v1.0
Error response from daemon: conflict: unable to remove repository reference "alpine4:v1.0" (must force) - container 945e9f47d833 is using its referenced image 7ea0f12a3321
[root@k8s-host docker]# docker rmi -f alpine4:v1.0
Untagged: alpine4:v1.0
[root@k8s-host docker]# docker ps -a | grep alpine
945e9f47d833 7ea0f12a3321 "/bin/sh -c 'tail -f..." 54 seconds ago Up 53 seconds alpine4
[root@k8s-host docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 7ea0f12a3321 7 hours ago 7.34MB
操作中执行 docker rmi -f alpine4:v1.0
命令后,仅删除了镜像:tag,并没有删除镜像数据层。
可以看到只有镜像名称:tag 变成了none,实际的镜像还是存在的。因为目前 945e9f47d833 容器中引用到了 7ea0f12a3321 镜像是无法使用 docker rmi -f
删除的。
扩展内容
尤其在测试、研发环境中会出现很多 <none>
标记的镜像,占用较多资源。 在docker cli工具中可以使用如下命令来清理未使用的镜像。
shell
$ docker system prune
注意:该命令包含删除所有未使用的容器、网络、镜像(悬空的和未引用的),以及卷(可选)。
也可单独删除所有无名的镜像(悬空镜像)
shell
$ docker rmi $(docker images -f "dangling=true" -q)
技术文章持续更新,请大家多多关注呀~~
搜索微信公众号,关注我【 帽儿山的枪手 】