Skip to content

第 2 章 镜像管理 ​

本章目标:掌握镜像的拉取、查看、标记、删除、导出导入,理解镜像分层与"写时复制"原理,并了解镜像仓库的运作方式。

1. 镜像是怎么来的 ​

镜像是分层打包的只读文件系统 + 元数据(配置、默认环境变量、入口命令等)。来源有三个:

  1. 公共仓库拉取:最常用的是 Docker Hub(docker.io),包含官方镜像与社区镜像;
  2. 私有仓库拉取:公司内部自建的 registry(如 Harbor、Docker Registry);
  3. 自己构建:用 Dockerfile 构建(第 6 章)。

镜像的完整名字格式:

text
[registry地址/][命名空间/]镜像名[:标签]
docker.io/library/nginx:1.27
└─────────┬────────┘  └──┬──┘  └─┬─┘
        registry       命名空间  标签(默认 latest)
  • docker.io/library/ 可以省略,docker pull nginx 等价于 docker pull docker.io/library/nginx:latest;
  • 命名空间 library 是官方镜像专用;社区镜像形如 bitnami/postgresql;
  • 标签(tag)默认是 latest,但它会随版本变动——生产环境应固定具体版本号。

2. 镜像的基本管理 ​

命令作用
docker pull <image>拉取镜像到本地
docker images(或 docker image ls)列出本地镜像
docker tag <镜像> <新名字:标签>打标签(复制引用,不复制数据)
docker rmi <image>删除镜像
docker image prune清理悬空镜像(dangling)
docker history <image>查看镜像的分层历史

一组完整的操作序列:

bash
docker pull nginx:alpine        # 拉取镜像
docker images                   # 查看本地镜像
docker tag nginx:alpine mynginx:v1   # 打一个自己的标签
docker rmi nginx:alpine           # 删除原标签
docker images | Select-String mynginx   # 标签仍在(同一个镜像)

要点:

  • tag 只是给同一个镜像增加一个引用(类似文件硬链接),不复制镜像数据;
  • rmi 删除的是标签/引用;只有镜像的所有标签都被删除、且没有容器引用时,镜像数据才会真正释放;
  • 删除被容器使用的镜像会报错:image is being used by running container,需要先删除(或停止)容器。

3. 镜像分层:为什么又省又快 ​

镜像由多层只读层叠加而成,每层都是文件系统的一次变更集合:

text
┌─────────────────────────┐  ← 可写层(只有容器有,容器删除即消失)
├─ 层 3:COPY 应用代码     │  ← 只读,build 时生成
├─ 层 2:安装依赖库        │  ← 只读
├─ 层 1:基础镜像层        │  ← 只读,如 alpine 的根文件系统
└─────────────────────────┘

由此引出三个关键推论:

  1. 复用:拉取新镜像时,如果本地已有相同的层,只下载缺失的层。多个镜像共用基础层,磁盘占用大幅下降。
  2. 构建缓存:Dockerfile 构建时层未变化则直接复用缓存(第 6 章详述)。
  3. 写时复制(copy-on-write):容器运行时并不复制整个镜像,而是在镜像之上叠加一层可写层;首次写入某个文件时才复制该文件到可写层。所以容器启动快、内存省,且多个容器共享同一镜像的只读层。

查看镜像的分层:

bash
docker history nginx:alpine

输出示例:

text
IMAGE          CREATED       CREATED BY                                      SIZE
a5a2e0f9e2a4   2 weeks ago   /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon off;"]   0B
...            ...          ...                                             ...

SIZE 为 0B 的层通常是纯元数据层(ENV、CMD、EXPOSE 等指令)。

第 1 章练习 3 的答案就在这:两个容器共享镜像的只读层,各自的改动写在自己独立的可写层里,互不可见——容器删除时它的可写层也随之消失,镜像不受任何影响。

4. 镜像的导出与导入 ​

离线环境或跨机器迁移镜像,有两对命令,别用混:

场景导出导入
迁移镜像(保留分层与元数据)docker save -o nginx.tar nginx:alpinedocker load -i nginx.tar
迁移容器文件系统(扁平化为单层,丢历史)docker export -o fs.tar <容器>docker import fs.tar <新镜像:tag>
bash
# 方式一:save / load(推荐,保留分层结构与历史)
docker save -o nginx.tar nginx:alpine
# 删除后 load 恢复,镜像分层与历史完整保留
docker rmi nginx:alpine && docker load -i nginx.tar
docker images | Select-String nginx

# 方式二:export / import(把正在运行的容器当前文件系统导出为单层镜像)
docker run -d --name tmp-nginx nginx:alpine
docker export -o fs.tar tmp-nginx
docker import fs.tar flat-nginx:v1
docker rm -f tmp-nginx

save/load 与 export/import 的区别:前者搬运的是镜像(分层、标签、历史都在,可离线 pull/push);后者搬运的是容器文件系统的快照(扁平一层,通常用于急救或把容器改装成镜像,一般不建议作为常规手段)。

5. 镜像仓库 ​

Docker Hub 是默认公共仓库,拉取无需登录,推送需要账号。发布镜像的流程:

bash
docker login                      # 输入 Docker Hub 用户名密码
docker tag myapp:v1 yourname/myapp:v1   # 打上带账号前缀的标签
docker push yourname/myapp:v1

自建私有 registry(局域网/离线内网):

bash
docker run -d --name registry -p 5000:5000 registry:2
docker tag nginx:alpine localhost:5000/nginx:alpine
docker push localhost:5000/nginx:alpine     # 推送到本地私有仓库
docker pull localhost:5000/nginx:alpine

localhost:5000 这种"带端口的前缀"会让 Docker 把它识别为 registry 地址而非命名空间。生产环境常用 Harbor 等带鉴权与漏洞扫描能力的仓库。

常见误区 ​

  • 误区:latest 标签 = 最新稳定版。 正确:latest 只是"未指定标签时默认的标签",镜像作者想指向谁就指向谁。生产环境务必固定版本号(如 nginx:1.27)。
  • 误区:docker rmi 删不掉就是镜像坏了。 正确:多半是还有容器引用它,docker ps -a 找到并先删容器。
  • 误区:在容器里装的东西会保存进镜像。 正确:容器内的改动只存在于可写层,容器一删就没了;要用它得重新构建(或 docker commit,但那是反模式)。
  • 误区:export 和 save 差不多。 正确:save 是镜像(分层+历史),export 是容器文件系统快照(扁平单层)。

小结 ​

  • 镜像 = 多层只读文件系统 + 元数据,来源:公共/私有仓库、自己构建。
  • 完整镜像名 = [registry/]命名空间/名字:标签,生产环境固定版本号,别依赖 latest。
  • 分层带来复用、构建缓存、写时复制三大收益;容器改动只进可写层,删除即消失。
  • 迁移镜像用 save/load;export/import 处理的是容器文件系统快照。
  • 推送/拉取私有仓库:docker login → docker tag 带仓库前缀 → docker push。

练习 ​

  1. 本机已有 nginx:latest 与 nginx:alpine,用 docker history 对比两者的层结构差异(提示:观察基础层与镜像体积差异,思考 alpine 为什么小)。
  2. 用 docker tag 给 nginx 打两个标签,rmi 掉其中一个,观察 docker images 中该镜像(IMAGE ID 相同)是否还在。
  3. 用 docker save 把 nginx 导出为 tar,删除镜像后再 docker load 恢复,确认 docker images 内容一致。
📖本文阅读--次|📊全站访问--次|👥访客--人