技术博客

Docker 镜像管理基础:pull/push/tag/save/load 与镜像分层原理

全面讲解 Docker 镜像操作命令:docker pull/push/tag/save/load/rmi/inspect 的使用场景与参数详解,深入理解镜像分层存储(Union FS)结构、镜像 ID 与 Digest 的区别、多架构镜像(Multi-arch)原理,以及企业镜像管理的命名规范与版本策略。

Docker镜像Imagepullpushtag镜像层入门

Docker 镜像是容器的基础,掌握镜像管理是使用 Docker 的核心技能。本文从命令操作到底层原理,系统讲解镜像管理的完整知识体系。

镜像分层存储原理

在学命令之前,先理解镜像是如何存储的——这会让后续所有操作都更容易理解。

镜像分层示例(Nginx 镜像):

Layer 4: 622a1f2...  COPY nginx.conf(应用配置层)     8KB
Layer 3: 9f8a3c1...  RUN apt-get install nginx         45MB
Layer 2: 1a2b3c4...  RUN apt-get update                23MB
Layer 1: sha256:0d5  debian:12-slim 基础镜像           74MB
         ──────────────────────────────────
         合计:约 142MB(实际磁盘占用)

每一层都有:
  - 唯一的 SHA256 ID(内容寻址)
  - 只读属性(构建完成后不可修改)
  - diff 文件(相对上一层的变更)

分层的好处:

场景:服务器上有 3 个镜像:
  nginx:1.24  = debian:12 + nginx 1.24 层
  nginx:1.25  = debian:12 + nginx 1.25 层
  python:3.11 = debian:12 + python3.11 层

磁盘实际存储:
  debian:12 基础层    74MB  × 1(只存一份!)
  nginx 1.24 层       68MB
  nginx 1.25 层       69MB
  python3.11 层      112MB
  ─────────────────────────
  总计:323MB

如果没有分层(每个镜像完整存储):
  nginx:1.24   142MB
  nginx:1.25   143MB
  python:3.11  186MB
  ─────────────────────────
  总计:471MB

分层节省了约 30% 磁盘空间,网络传输同理(已有的层不重复下载)

一、docker pull — 拉取镜像

# 基础用法
docker pull nginx              # 默认拉取 latest 标签
docker pull nginx:1.25         # 指定标签
docker pull nginx:1.25-alpine  # alpine 轻量版

# 指定仓库地址
docker pull registry.cn-hangzhou.aliyuncs.com/library/nginx:1.25

# 指定 Digest(最精确,防止标签被覆盖)
docker pull nginx@sha256:a484819eb60211f5299034ac80f6a681b06f89e65866ce91f356ed7c72af059c

# 拉取所有标签(慎用,可能很大)
docker pull -a nginx

# 查看拉取进度
docker pull ubuntu:22.04
# ubuntu:22.04: Pulling from library/ubuntu
# 7b1a6ab2e44d: Pull complete   ← 每行是一个层
# Digest: sha256:77906da86b...  ← 镜像的唯一指纹
# Status: Downloaded newer image for ubuntu:22.04

镜像命名规则详解:

完整格式:[registry]/[namespace]/[name]:[tag]@[digest]

例子:
  nginx                          → docker.io/library/nginx:latest
  ubuntu:22.04                   → docker.io/library/ubuntu:22.04
  bitnami/redis:7.2              → docker.io/bitnami/redis:7.2
  registry.cn-hz.aliyuncs.com/library/nginx:1.25
  harbor.company.com/backend/api:v2.1.0

Tag 命名习惯:
  latest        → 最新版(不建议生产使用,版本不固定)
  1.25          → 主版本.次版本
  1.25.3        → 主版本.次版本.补丁版本
  1.25-alpine   → 带构建变体(alpine = 轻量)
  1.25.3-amd64  → 带架构信息

二、docker images — 查看本地镜像

# 列出所有镜像
docker images

# 输出示例:
# REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
# nginx        latest    a72860cb95fd   2 weeks ago    187MB
# nginx        1.25      605c77e624dd   8 months ago   141MB
# ubuntu       22.04     3b418d7b466a   6 weeks ago    77.9MB

# 过滤镜像
docker images nginx          # 只看 nginx 镜像
docker images --filter "dangling=true"  # 悬空镜像(<none>:<none>)

# 显示完整 Image ID(默认只显示前12位)
docker images --no-trunc

# 只显示 Image ID(常用于脚本)
docker images -q

# 格式化输出
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"

Image ID vs Digest:

# Image ID:镜像的本地标识(构建时生成)
docker images --format "{{.ID}}"
# a72860cb95fd   ← 12位短ID,实际是64位SHA256

# Digest:镜像内容的唯一指纹(从仓库拉取时的Hash)
docker images --digests
# REPOSITORY  TAG     DIGEST                            IMAGE ID       SIZE
# nginx       latest  sha256:a484819eb602...            a72860cb95fd   187MB

# 区别:
# Image ID 可能因为本地 tag 操作而不同
# Digest 是内容 Hash,全球唯一,不可变

三、docker tag — 标记镜像

tag 命令为镜像创建别名,不会复制数据,只是添加引用。

# 基础用法:给已有镜像打 tag
docker tag nginx:1.25 nginx:stable
docker tag nginx:1.25 harbor.company.com/infra/nginx:1.25

# 推送到私有仓库的完整流程:
# 1. 登录私有仓库
docker login harbor.company.com -u admin -p <password>

# 2. 打 tag(原镜像:tag → 目标仓库:tag)
docker tag nginx:1.25 harbor.company.com/infra/nginx:1.25
docker tag nginx:1.25 harbor.company.com/infra/nginx:stable

# 3. 推送
docker push harbor.company.com/infra/nginx:1.25
docker push harbor.company.com/infra/nginx:stable

# tag 不创建新镜像,只是引用:
docker images nginx
# REPOSITORY  TAG      IMAGE ID
# nginx       1.25     605c77e624dd  ← 同一个 Image ID
# nginx       stable   605c77e624dd  ← 同一个 Image ID

四、docker push — 推送镜像

# 推送到 Docker Hub
docker login  # 登录(只需一次)
docker push myusername/myapp:v1.0

# 推送到阿里云 ACR
docker login registry.cn-hangzhou.aliyuncs.com \
    -u <阿里云账> -p <>
docker push registry.cn-hangzhou.aliyuncs.com/myns/myapp:v1.0

# 推送所有同名 tag
docker push harbor.company.com/backend/api --all-tags

五、docker inspect — 查看镜像详情

# 查看镜像元数据(JSON 格式)
docker inspect nginx:1.25

# 常用字段提取
# 查看镜像层
docker inspect nginx:1.25 --format '{{json .RootFS.Layers}}' | jq .

# 查看暴露端口
docker inspect nginx:1.25 --format '{{json .Config.ExposedPorts}}'

# 查看环境变量
docker inspect nginx:1.25 --format '{{json .Config.Env}}' | jq .

# 查看镜像大小(字节)
docker inspect nginx:1.25 --format '{{.Size}}'

# 查看构建历史(每层做了什么)
docker history nginx:1.25
# IMAGE          CREATED       CREATED BY                               SIZE
# a72860cb95fd   2 weeks ago   CMD ["nginx" "-g" "daemon off;"]        0B
# <missing>      2 weeks ago   EXPOSE map[80/tcp:{}]                   0B
# <missing>      2 weeks ago   COPY /etc/nginx /etc/nginx              4.74kB
# <missing>      2 weeks ago   RUN /bin/sh -c apt-get install nginx    68.8MB

六、docker save / docker load — 导出与导入

用于在没有仓库的情况下传输镜像(离线环境、物理拷贝)。

# 导出镜像为 tar 包
docker save nginx:1.25 -o nginx-1.25.tar

# 导出多个镜像到一个 tar(打包批量传输)
docker save nginx:1.25 redis:7.2 ubuntu:22.04 | gzip > infra-images.tar.gz

# 查看 tar 包内容
tar tf nginx-1.25.tar
# manifest.json    ← 描述文件
# repositories     ← 镜像标签信息
# <layer_id>/layer.tar  ← 每一层的内容

# 导入 tar 包
docker load -i nginx-1.25.tar
# Loaded image: nginx:1.25

docker load < infra-images.tar.gz
# 支持 gzip 压缩格式

# 导入后验证
docker images nginx

save vs export 的区别:

docker save(镜像操作):
  保存镜像的完整分层结构和元数据
  可以 docker load 还原,保留所有 tag 和历史

docker export(容器操作):
  导出容器的文件系统快照(扁平化,无分层)
  用 docker import 还原,但会失去历史和元数据
  通常用于制作精简基础镜像

结论:备份/传输镜像用 save/load;导出容器文件系统用 export/import

七、docker rmi — 删除镜像

# 删除指定镜像
docker rmi nginx:1.24

# 删除多个
docker rmi nginx:1.24 nginx:1.23 ubuntu:20.04

# 强制删除(即使有容器在使用)
docker rmi -f nginx:latest  # 慎用!

# 删除所有悬空镜像(<none>:<none>)
docker image prune

# 删除所有未使用镜像(更激进,慎用)
docker image prune -a

# 批量删除指定名称的镜像
docker images nginx -q | xargs docker rmi

# 脚本:删除超过 30 天的镜像
docker images --format "{{.ID}}\t{{.CreatedAt}}" | \
    awk -v date="$(date -d '30 days ago' +%s)" \
    '$2 < date {print $1}' | \
    xargs -r docker rmi

八、多架构镜像(Multi-arch)

现代服务器有 x86_64 和 ARM(鲲鹏/飞腾)两种架构,多架构镜像让同一个 tag 在不同架构上自动拉取对应版本。

# 查看镜像支持的架构
docker manifest inspect nginx:latest | grep '"architecture"'
#   "architecture": "amd64"
#   "architecture": "arm64"
#   "architecture": "arm"
#   ... 等多种架构

# 在 ARM 机器上 pull nginx,自动获取 ARM 版本
# 在 x86 机器上 pull nginx,自动获取 x86 版本

# 手动指定架构(跨架构测试)
docker pull --platform linux/arm64 nginx:latest
docker pull --platform linux/amd64 nginx:latest

镜像管理最佳实践

1. 永远不要在生产使用 latest tag
   latest 随时可能被更新,导致"代码没变但行为变了"
   使用具体版本号:nginx:1.25.3

2. 建立镜像命名规范
   格式:registry/namespace/name:版本-环境
   示例:harbor.company.com/backend/api:v2.1.0-prod

3. 定期清理
   每周执行 docker image prune 清理悬空镜像
   配置 Harbor GC 策略,自动清理旧版本

4. 私有镜像必须鉴权
   docker login 的凭据存储在 ~/.docker/config.json
   生产环境使用 docker secret 或 K8s imagePullSecret

5. 信创/离线环境:用 save/load 而不是手动 pull
   提前准备好镜像包,部署时直接 docker load

小结

镜像管理的核心命令:pull(拉取)、push(推送)、tag(打标签)、save/load(离线传输)、rmi(删除)、inspect(查看详情)。

理解分层存储有两个关键点:层是内容寻址的(SHA256 Hash),同内容的层只存一份;镜像 ID 是本地标识,Digest 是全球唯一内容指纹。

下一篇讲容器生命周期管理:如何运行、查看、进入、停止和删除容器。