Docker Image(镜像)使用指南
- Docker Image 介绍
- 镜像的组成:分层存储体系
- 镜像的构建:Dockerfile 与
docker build - 镜像的存储与分发:Registry
- 深入底层:镜像的内容
- 优化技巧
- Docker Image 常用指令
- Docker Image 综合实践
Docker Image 介绍
一个 Docker 镜像是一个只读的模板,它包含了创建 Docker 容器所需的所有指令和文件系统内容。
我们可以通过以下方式来理解它:
- 面向对象编程类比:镜像就像是类,而容器就是这个类实例化出来的对象。一个类可以创建多个对象,同样,一个镜像可以运行出多个容器。
- 操作系统安装盘类比:镜像就像是
.iso光盘镜像文件,而容器就是使用这个光盘安装好并正在运行的操作系统。光盘是静态的、只读的,而安装好的系统是可运行的、可写的。
需要注意的是,不要将这些比喻中的“类”和“操作系统”的创建方式与 Docker 镜像和容器的创建方式混淆,他们的实现机制是完全不同的。
Docker Image 具有以下特性:
- 只读:镜像一旦创建,其内容就不会改变。
- 分层存储:镜像由一系列只读层组成,这是 Docker 的核心技术之一。
- 内容寻址:现代 Docker 使用内容哈希(SHA256)来唯一标识镜像和其每一层,确保内容的一致性,为什么需要使用哈希,这就涉及到了
Dockerfile的构建过程以及底层的存储机制,后续文章会详细讲解,这里主要是了解Docker Image。
镜像的组成:分层存储体系
这是理解镜像最关键、最精髓的部分。
什么是分层?
Docker 镜像并非一个巨大的单体文件,而是由一系列层堆叠而成的。每一层代表了 Dockerfile 中的一条指令。
举个例子,一个简单的 Dockerfile:
FROM ubuntu:20.04 # 层 1: 拉取基础镜像层
COPY . /app # 层 2: 添加当前目录文件到镜像的 /app 目录
RUN make /app # 层 3: 使用 make 构建应用
CMD python /app/app.py # 层 4: 设置容器启动时的默认命令
这个 Dockerfile 构建的镜像至少包含 4 层(实际上基础镜像 ubuntu:20.04 本身也是由多层组成的)。
分层的好处
共享和复用:
- 如果两个不同的镜像都基于
ubuntu:20.04,那么你的主机上只需要存储一份ubuntu:20.04的层。这极大地节省了磁盘空间和网络传输带宽。 - 当你构建新镜像时,如果某一层(例如
RUN apt-get update)没有变化,Docker 会直接使用本地缓存的现有层,使得构建过程非常迅速。
- 如果两个不同的镜像都基于
减少磁盘占用:
- 容器在运行时,会在镜像的所有只读层之上,添加一个可写的容器层。所有对运行中容器的文件修改(如创建、删除、更改文件)都发生在这个薄薄的可写层中。这消除了对底层只读层的复制,使得容器启动极其轻量和快速。
高效分发:
- 当你
docker push或docker pull一个镜像时,Docker 引擎只会传输你本地没有的层。如果你更新了应用代码(只修改了COPY这一层),你只需要重新推送或拉取变化的那一层,而不是整个镜像。
- 当你
镜像的构建:Dockerfile 与 docker build
镜像通常通过一个名为 Dockerfile 的文本文件来定义,并使用 docker build 命令构建在,这里只做一个简单的了解,大致看看即可,后续会详细讲解。
Dockerfile 主要指令:
FROM: 指定基础镜像,所有镜像的起点。RUN: 在镜像层中执行命令(如安装软件包)。COPY&ADD: 将文件从构建上下文复制到镜像中。CMD&ENTRYPOINT: 指定容器启动时运行的命令。EXPOSE: 声明容器运行时监听的端口。ENV: 设置环境变量。WORKDIR: 设置后续指令的工作目录。
构建过程:
当我们运行 docker build -t my-app:latest . 时:
- Docker 客户端将构建上下文(通常是
.指定的当前目录)发送给 Docker 守护进程。 - 守护进程逐条执行 Dockerfile 中的指令。
- 每执行一条指令,就会创建一个新的镜像层(如果该指令修改了文件系统)。
- 所有指令执行完毕后,最终生成一个标记为
my-app:latest的镜像。
镜像的存储与分发:Registry
镜像需要被存储和共享,这通过 Registry 实现。
- Docker Hub: 默认的公共 Registry,就像 GitHub 对于代码一样。你可以从中拉取官方镜像(如
nginx,python)和社区镜像。 - 其他公共 Registry: Harbor、Google Container Registry、Amazon ECR、Azure Container Registry 等。
- 私有 Registry: 你可以搭建自己的私有 Registry(使用 Docker 官方提供的
registry镜像或其他企业级方案如 Harbor),用于存储公司内部的私有镜像。
镜像名称结构: [registry-url]/[username]/[repository]:[tag]
registry-url: 默认为docker.io(Docker Hub)。username: 在 Docker Hub 或其他 Registry 上的用户名或组织名。repository: 镜像仓库名,通常代表一个项目或应用。tag: 标签,用于区分同一镜像的不同版本(如latest,v1.0,dev)。最佳实践是避免使用浮动的latest标签,而使用明确的版本号。
常用命令:
docker pull nginx:alpine: 从 Registry 拉取镜像。docker push my-company.com/dev/my-app:v1.2: 推送镜像到私有 Registry。docker images或docker image ls: 列出本地镜像。docker rmi <image_id>: 删除本地镜像。
深入底层:镜像的内容
一个镜像不仅仅是文件的集合,它还包含一些重要的元数据(JSON 格式):
- 镜像索引: 用于多架构镜像(如同时支持
amd64和arm64),它指向不同平台对应的具体镜像清单。 - 镜像清单: 描述了一个特定平台镜像的配置文件和层信息。
- 镜像配置: 包含了该镜像的详细元数据,如:
- 创建时间、作者
- 容器运行时使用的配置(如环境变量、工作目录、入口点命令、暴露的端口)
- 该镜像所包含的所有层的哈希值(
rootfs.diff_ids)
- 层文件: 每个层都是一个压缩包(
tar.gz),包含了文件系统的变化。
我们可以通过 docker inspect <image_name> 命令来查看镜像的配置信息。
优化技巧
使用
.dockerignore文件: 排除构建上下文中不必要的文件(如node_modules,.git),加速构建过程并减小镜像体积。选择小巧的基础镜像: 优先选择 Alpine Linux 等小型镜像,可以极大减小最终镜像体积和安全攻击面。
多阶段构建: 这是构建高效、小体积镜像的黄金法则。在一个 Dockerfile 中使用多个
FROM指令。你可以在一个阶段(“构建阶段”)使用包含编译器等重型工具的镜像来构建应用,然后在另一个阶段(“运行阶段”)只复制构建好的二进制文件到一个非常精简的基础镜像中。# 阶段一:构建阶段 FROM golang:1.19 AS builder WORKDIR /app COPY . . RUN go build -o myapp . # 阶段二:运行阶段 FROM alpine:latest COPY /app/myapp /usr/local/bin/ CMD ["myapp"]合并 RUN 指令: 将多个
RUN指令用&&连接成一个,减少镜像层数(虽然层数限制现在已不是大问题,但仍能减少一些元数据开销)。明确的标签: 为生产环境镜像使用语义化版本号(如
v1.2.3)或 Git 提交哈希,而不是默认的latest。
Docker Image 常用指令
docker images
功能
列出本地存储的 Docker 镜像。语法
docker images [OPTIONS] [REPOSITORY[:TAG]]别名
docker image ls docker image list关键参数
-a, --all:显示所有镜像(包括中间镜像层,默认情况下会过滤掉中间镜像层)--digests:显示镜像的摘要信息-f, --filter:根据条件过滤显示结果--format:使用 Go 模板格式化输出内容--no-trunc:显示完整的镜像信息(不截断输出)-q, --quiet:只显示镜像 ID
使用示例
# 列出本地所有镜像 docker images # 列出特定的镜像(ubuntu 仓库的所有版本) docker images ubuntu # 显示所有镜像(包括中间层) docker images -a # 只显示镜像ID docker images -q # 显示完整的镜像信息(不截断) docker images --no-trunc # 使用过滤器显示特定标签的镜像 docker images -f "reference=nginx:*"
docker tag
功能
为本地镜像创建新的标签,通常用于为镜像添加仓库地址前缀,为推送镜像到仓库做准备。语法
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]别名
docker image tag使用示例
# 为本地镜像添加新的标签 docker tag ubuntu:22.04 myregistry.com/myubuntu:22.04 # 为镜像添加版本标签 docker tag nginx nginx:1.23.3 # 为镜像添加多个标签 docker tag myapp:latest myregistry.com/myapp:v1.0 docker tag myapp:latest myregistry.com/myapp:latest # 推送到私有仓库前的标签准备 docker tag local-image:tag your-private-registry.com/username/repository:tag注意事项
docker tag并不会创建新的镜像,只是为现有镜像添加了一个新的引用名称- 同一个镜像可以有多个标签,它们共享相同的镜像 ID
- 删除一个标签不会删除镜像本身,只有当所有标签都被删除时,镜像才会被真正删除
- 在推送镜像到仓库前,必须使用
docker tag为镜像添加包含仓库地址的完整名称
docker pull
功能:从镜像仓库拉取(下载)镜像或仓库到本地。这是获取和运行容器的基础。
语法:
docker pull [OPTIONS] NAME[:TAG|@DIGEST]
NAME:镜像名称,格式通常为[仓库地址/]用户名/仓库名。TAG:镜像标签(版本)。如果省略,默认为latest。DIGEST:镜像的数字摘要,用于精确指定某个版本的镜像,比 TAG 更唯一、安全。
示例:
# 从 Docker Hub 拉取最新的 Ubuntu 镜像
docker pull ubuntu
# 拉取指定标签的 Nginx 镜像(版本为 1.25-alpine)
docker pull nginx:1.25-alpine
# 从私有仓库拉取镜像
docker pull myregistry.com:5000/myapp:v1.0
# 通过镜像摘要拉取,确保内容绝对一致
docker pull ubuntu@sha256:abc123...
注意事项:
- 总是明确指定标签(如
:alpine,:v1.0)而非依赖默认的latest,以确保环境的一致性,通过官网中的tag复制对应仓库对应版本的镜像拉取命令是一个好习惯。
docker push
功能:将本地的镜像标签推送到镜像仓库。这是分享和部署自定义镜像的关键步骤。
语法:
docker push [OPTIONS] NAME[:TAG]
- 推送前,必须先用
docker tag命令将本地镜像命名为符合目标仓库规范的名称(仓库地址/用户名/仓库名:标签)。
示例:
# 1. 首先,为本地镜像打上符合仓库规范的标签
docker tag my_local_image:latest your_username/your_repo:v1.0
# 2. 然后,推送到仓库
docker push your_username/your_repo:v1.0
# 推送到私有仓库
docker push myregistry.com:5000/your_repo:v1.0
注意事项:
- 推送前必须确保已通过
docker login登录且有相应权限。 - 镜像推送是分层的操作的,如果仓库中已存在相同的层,则不会重复上传,节省时间和带宽。
docker rmi
功能:删除本地的一个或多个 Docker 镜像。释放磁盘空间或清理不再需要的镜像。
语法:
docker rmi [OPTIONS] IMAGE [IMAGE...]
IMAGE:要删除的镜像名称或 ID。
示例:
# 删除单个镜像
docker rmi ubuntu:22.04
# 删除多个镜像
docker rmi nginx:1.23.3 myapp:latest
注意事项:
- 只有在没有容器使用该镜像时,才能成功删除镜像。
- 可以使用
-f选项强制删除正在使用的镜像,但这可能会导致相关容器出现问题。
docker save、docker load
功能:docker save 用于将一个或多个镜像保存为 tar 文件,方便迁移或备份;docker load 则用于从 tar 文件中加载镜像。
语法:
docker save [OPTIONS] IMAGE [IMAGE...] > image.tar
docker load [OPTIONS] < image.tar
IMAGE:要保存或加载的镜像名称或 ID。OPTIONS:可选参数,如-o指定输出文件。
示例:
# 保存镜像为 tar 文件
docker save -o myimage.tar myapp:latest
# 从 tar 文件加载镜像
docker load -i myimage.tar
注意事项:
docker save保存的是镜像的所有层和元数据,生成的 tar 文件可以在其他 Docker 主机上使用docker load加载。- 使用
docker save和docker load可以方便地在没有网络连接的环境中迁移镜像。
docker image inspect
功能
显示镜像的详细信息,包括元数据、配置信息、层信息等。语法
docker image inspect [OPTIONS] IMAGE [IMAGE...]关键参数
-f, --format:使用给定的 Go 模板格式化输出
使用示例
# 查看指定镜像的详细信息 docker image inspect nginx:1.23.3 # 查看多个镜像的信息 docker image inspect ubuntu:22.04 alpine:latest # 使用格式输出只查看镜像的架构信息 docker image inspect -f '{{.Architecture}}' nginx:1.23.3 # 查看镜像的创建时间 docker image inspect -f '{{.Created}}' nginx:1.23.3 # 查看镜像的所有层(Layer) docker image inspect -f '{{.RootFS.Layers}}' nginx:1.23.3
docker history
功能
查看镜像的历史记录,包括每一层的创建时间、作者、命令等信息。语法
docker history [OPTIONS] IMAGE关键参数
-H, --human=false:以人类可读的格式显示大小--no-trunc:显示完整的输出(不截断)--quiet:只显示镜像 ID
使用示例
# 查看指定镜像的历史记录 ┌─[root@VM-16-15-debian] - [/home/ljx/docker_test/namespace_test/test_dir2] - [1634] └─[$] docker history nginx:stable-alpine3.21-perl [12:45:03] IMAGE CREATED CREATED BY SIZE COMMENT 0b65b64c7d04 4 months ago RUN /bin/sh -c set -x && apkArch="$(cat … 38.4MB buildkit.dockerfile.v0 <missing> 4 months ago RUN /bin/sh -c set -x && apkArch="$(cat … 36.3MB buildkit.dockerfile.v0 <missing> 4 months ago ENV NJS_RELEASE=1 0B buildkit.dockerfile.v0 <missing> 4 months ago ENV NJS_VERSION=0.8.10 0B buildkit.dockerfile.v0 <missing> 4 months ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0 <missing> 4 months ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0 <missing> 4 months ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0 <missing> 4 months ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B buildkit.dockerfile.v0 <missing> 4 months ago COPY 30-tune-worker-processes.sh /docker-ent… 4.62kB buildkit.dockerfile.v0 <missing> 4 months ago COPY 20-envsubst-on-templates.sh /docker-ent… 3.02kB buildkit.dockerfile.v0 <missing> 4 months ago COPY 15-local-resolvers.envsh /docker-entryp… 389B buildkit.dockerfile.v0 <missing> 4 months ago COPY 10-listen-on-ipv6-by-default.sh /docker… 2.12kB buildkit.dockerfile.v0 <missing> 4 months ago COPY docker-entrypoint.sh / # buildkit 1.62kB buildkit.dockerfile.v0 <missing> 4 months ago RUN /bin/sh -c set -x && addgroup -g 101… 4.05MB buildkit.dockerfile.v0 <missing> 4 months ago ENV DYNPKG_RELEASE=1 0B buildkit.dockerfile.v0 <missing> 4 months ago ENV PKG_RELEASE=1 0B buildkit.dockerfile.v0 <missing> 4 months ago ENV NGINX_VERSION=1.28.0 0B buildkit.dockerfile.v0 <missing> 4 months ago LABEL maintainer=NGINX Docker Maintainers <d… 0B buildkit.dockerfile.v0 <missing> 4 months ago CMD ["/bin/sh"] 0B buildkit.dockerfile.v0 <missing> 4 months ago ADD alpine-minirootfs-3.21.4-x86_64.tar.gz /… 7.82MB buildkit.dockerfile.v0 # 以人类可读的格式查看历史记录 ┌─[root@VM-16-15-debian] - [/home/ljx/docker_test/namespace_test/test_dir2] - [1633] └─[$] docker history -H nginx:stable-alpine3.21-perl IMAGE CREATED CREATED BY SIZE COMMENT 0b65b64c7d04 4 months ago RUN /bin/sh -c set -x && apkArch="$(cat … 38.4MB buildkit.dockerfile.v0 <missing> 4 months ago RUN /bin/sh -c set -x && apkArch="$(cat … 36.3MB buildkit.dockerfile.v0 <missing> 4 months ago ENV NJS_RELEASE=1 0B buildkit.dockerfile.v0 <missing> 4 months ago ENV NJS_VERSION=0.8.10 0B buildkit.dockerfile.v0 <missing> 4 months ago CMD ["nginx" "-g" "daemon off;"] 0B buildkit.dockerfile.v0 <missing> 4 months ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0 <missing> 4 months ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0 <missing> 4 months ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B buildkit.dockerfile.v0 <missing> 4 months ago COPY 30-tune-worker-processes.sh /docker-ent… 4.62kB buildkit.dockerfile.v0 <missing> 4 months ago COPY 20-envsubst-on-templates.sh /docker-ent… 3.02kB buildkit.dockerfile.v0 <missing> 4 months ago COPY 15-local-resolvers.envsh /docker-entryp… 389B buildkit.dockerfile.v0 <missing> 4 months ago COPY 10-listen-on-ipv6-by-default.sh /docker… 2.12kB buildkit.dockerfile.v0 <missing> 4 months ago COPY docker-entrypoint.sh / # buildkit 1.62kB buildkit.dockerfile.v0 <missing> 4 months ago RUN /bin/sh -c set -x && addgroup -g 101… 4.05MB buildkit.dockerfile.v0 <missing> 4 months ago ENV DYNPKG_RELEASE=1 0B buildkit.dockerfile.v0 <missing> 4 months ago ENV PKG_RELEASE=1 0B buildkit.dockerfile.v0 <missing> 4 months ago ENV NGINX_VERSION=1.28.0 0B buildkit.dockerfile.v0 <missing> 4 months ago LABEL maintainer=NGINX Docker Maintainers <d… 0B buildkit.dockerfile.v0 <missing> 4 months ago CMD ["/bin/sh"] 0B buildkit.dockerfile.v0 <missing> 4 months ago ADD alpine-minirootfs-3.21.4-x86_64.tar.gz /… 7.82MB buildkit.dockerfile.v0
可以看出来,docker history 是自带 -H 选项的,默认就是以人类可读的格式显示历史记录。
docker image prune
Docker 用久了,磁盘空间容易不足,docker image prune 💾 正是用来清理未使用的镜像,帮助释放磁盘空间的命令。我来为你详细讲解一下它的用法和注意事项。
理解“未使用的镜像”
Docker 对未使用的对象(如镜像、容器、卷和网络)采取保守的清理策略,除非明确要求,否则通常不会自动删除它们,这可能会导致 Docker 占用额外的磁盘空间。docker image prune 主要清理两类镜像:
- 悬空镜像 (Dangling Images) :这是
docker image prune默认清理的对象。它们是没有标签(Tag)且未被任何容器引用的镜像,通常是由于构建新镜像或重新打标签而产生的中间层或旧镜像。 - 所有未使用的镜像 :使用
-a或--all参数时,此命令会删除所有未被任何容器引用的镜像,无论其是否有标签。这包括了那些你有名有姓但当前没有任何容器使用的镜像(例如,旧版本的my-app:v1.0)。
命令语法和常用选项
docker image prune 的基本语法是:
docker image prune [OPTIONS]
常用的 [OPTIONS] 包括:
| 选项 | 作用 | 示例 |
|---|---|---|
-a, --all |
删除所有未被容器引用的镜像,而不仅仅是悬空镜像。 | docker image prune -a |
-f, --force |
强制删除,不显示确认提示。在脚本中常用。 | docker image prune -f |
--filter |
使用过滤表达式来限制要修剪的镜像。 | docker image prune -a --filter "until=24h" |
常用操作示例
安全清理悬空镜像(推荐日常使用)
这是最安全的做法,因为它只清理那些明确不再需要的“悬空”镜像。docker image prune执行后,Docker 会列出待删除的镜像并请求确认(除非使用
-f强制跳过)。清理所有未使用的镜像(彻底清理,需谨慎)
这个命令会删除所有未被任何容器引用的镜像,包括有标签但未被使用的。docker image prune -a注意:此操作可能会删除一些你以后可能还会用到的镜像,下次使用前需要重新拉取或构建。
按条件过滤清理镜像
你可以使用--filter来精确控制要删除的镜像。例如,删除所有创建时间超过 24 小时的未使用镜像:docker image prune -a --filter "until=24h"或者,删除所有创建时间超过 7 天的未使用镜像:
docker image prune -a --filter "until=168h"强制清理(用于脚本)
如果你希望在脚本中自动执行清理,而不被交互提示打断,可以加上-f或--force选项。docker image prune -f # 强制删除悬空镜像 docker image prune -a -f # 强制删除所有未使用的镜像
注意事项
数据无价,操作前确认 :在执行
docker image prune -a前,建议先确认哪些镜像会被删除。可以使用以下命令查看所有悬空镜像:docker images -f dangling=true若要查看所有镜像及其依赖关系,需仔细评估。
被容器使用的镜像不会被删除 :无论容器是否正在运行,只要存在容器(包括已停止的容器)引用了该镜像,该镜像就不会被
prune删除。这是 Docker 的数据保护机制。
清理操作的建议流程
一个比较安全的清理操作流程可以参考:
停止所有容器(如果希望清理更彻底):
docker container stop $(docker container ls -aq)执行系统级清理(根据需求选择是否加
--volumes):docker system prune -af --volumes重启需要的服务(例如使用 Docker Compose):
docker compose up -d
查看磁盘空间
在清理前后,你可以使用以下命令查看 Docker 的磁盘使用情况,了解清理效果:
docker system df
Docker Image 综合实践
离线迁移镜像
在日常开发、部署或环境迁移中,我们经常会遇到这样的场景:生产服务器无法访问外部网络( air-gapped environment)、需要快速部署一个特定版本的环境,或者只是想将开发好的应用镜像简单地“复制”到另一台机器上。这时,Docker 镜像的离线迁移就成了一项必备技能。
这里小编将通过一个完整的实验,带你一步步实践如何将一台主机上的 Docker 镜像打包、传输并加载到另一台主机上。
实验概述
实验目标:将主机 1(Source Host) 上的一个 busybox 镜像,完整地迁移到主机 2(Target Host) 上。
模拟环境:
- 主机 1:IP 为
[某内网IP],我们以root用户操作。 - 主机 2:IP 为
82.156.255.140,我们以ljx用户操作。 - (注:本次实验为演示方便,在同一台机器上模拟了两个主机的角色,但操作流程与真实双机环境完全一致)
使用工具:
docker save/docker loadscp(Secure Copy)
第一步:在主机 1 上查找并打包镜像
首先,我们需要在源主机上找到想要迁移的镜像。
查找镜像:
使用docker images配合grep命令精确查找我们需要的busybox镜像及其唯一的 Image ID。docker images | grep busybox输出:
busybox stable-glibc 1827167fde90 2 years ago 4.42MB我们记下这个关键的 Image ID:
1827167fde90。打包镜像:
使用docker save命令将镜像打包成一个单一的.tar文件。这个文件包含了镜像的所有层(Layers)、元数据(Metadata)和标签(Tags)信息。docker save -o myimage.tar 1827167fde90-o myimage.tar:指定输出文件名为myimage.tar。1827167fde90:指定要打包的镜像 ID。你也可以使用镜像名:标签(如busybox:stable-glibc)。
至此,一个完整的 Docker 镜像包
myimage.tar就准备好了。
第二步:将镜像包传输到主机 2
我们使用 scp 命令,通过 SSH 协议安全地将文件从主机 1 拷贝到主机 2 的指定目录。这是跨主机操作的关键一步。
scp myimage.tar ljx@82.156.255.140:/home/ljx/docker_test/image_test/
myimage.tar:要传输的文件。ljx@82.156.255.140:以ljx用户身份登录到 IP 为82.156.255.140的主机 2。:/home/ljx/docker_test/image_test/:指定主机 2 上的目标路径。
如果是首次连接,会提示验证主机指纹,输入 yes 继续即可,然后输入用户 ljx 的密码。
第三步:在主机 2 上加载并验证镜像
现在,我们切换到主机 2 上进行操作。
验证文件已接收:
首先确认文件已成功传输并位于正确的目录下。ll ~/docker_test/image_test/输出:
total 4560 -rw------- 1 ljx ljx 4668416 Aug 30 16:21 myimage.tar可以看到,一个约 4.5MB 的
myimage.tar文件已经存在。加载镜像:
使用docker load命令将打包文件还原为 Docker 镜像。docker load -i myimage.tar输出:
Loaded image ID: sha256:1827167fde90df99d9341a27fbce2b445550eb2b18105e03f98102f00c0ec35e恭喜!输出中的
Loaded image ID表明镜像已成功加载到主机 2 的 Docker 镜像库中。验证镜像:
最后,让我们检查一下镜像是否真的可以正常使用。docker images | grep 1827167fde90 # 或者直接运行一个新的容器来测试 docker run -it --rm 1827167fde90 echo "Hello from the migrated image!"如果能看到镜像列表和成功的输出,就证明整个离线迁移实验大功告成!
通过 docker save 和 docker load 进行镜像离线迁移,是一种非常可靠、通用的方法。它的优点是:
- 环境无关:不依赖任何镜像仓库(如 Docker Hub、Harbor)。
- 简单直接:命令易于理解和记忆。
- 完整性:完美保留镜像的所有层和历史。
注意事项:
- 权限问题:在目标主机上执行
docker load时,确保当前用户有操作 Docker 的权限(通常需要加入docker用户组)。 - 存储空间:确保传输前后两台主机都有足够的磁盘空间。
- 网络安全:使用
scp传输时,数据是加密的,保证了镜像的安全性。 - 标签丢失:有时
docker save通过 Image ID 打包可能会丢失原有的镜像名和标签(Tag),加载后镜像可能变为<none>:<none>。此时可以使用docker tag <image_id> <new_name>:<new_tag>为其重新打 tag。更推荐使用docker save -o myimage.tar busybox:stable-glibc这种镜像名:标签的方式来保存,可以保留所有信息。
镜像存储的压缩与共享
在容器镜像的分发过程中,存储压缩与共享机制对于提升效率、节省带宽至关重要。本次实验通过操作同一镜像的不同版本,直观展示了这一机制的工作原理。
从远程仓库拉取标签为 v1.0 的镜像到本地
docker pull crpi-x6zeb1ynyh83ir4y.cn-hangzhou.personal.cr.aliyuncs.com/liujiaxuan/busybox_by_liujiaxuan:v1.0
拉取过程显示完整的下载流程,包括分层信息的获取:
v1.0: Pulling from liujiaxuan/busybox_by_liujiaxuan
Digest: sha256:800a83edaed8daab01f81f408912d121d175066900dd422bdcb6c8c91dbb3268
Status: Downloaded newer image for crpi-x6zeb1ynyh83ir4y.cn-hangzhou.personal.cr.aliyuncs.com/liujiaxuan/busybox_by_liujiaxuan:v1.0
查看本地镜像信息,注意到解压后的镜像大小为 4.42MB
docker images | grep busybox
输出结果显示本地存储的实际大小:
crpi-x6zeb1ynyh83ir4y.cn-hangzhou.personal.cr.aliyuncs.com/liujiaxuan/busybox_by_liujiaxuan v1.0 1827167fde90 2 years ago 4.42MB
busybox stable-glibc 1827167fde90 2 years ago 4.42MB
为演示共享机制,删除本地镜像后重新拉取
# 细节:删除是先去标签再删除
┌─[root@VM-16-15-debian] - [~] - [1687]
└─[$] docker rmi 1827167fde90 -f
Untagged: crpi-x6zeb1ynyh83ir4y.cn-hangzhou.personal.cr.aliyuncs.com/liujiaxuan/busybox_by_liujiaxuan:v1.0
Untagged: crpi-x6zeb1ynyh83ir4y.cn-hangzhou.personal.cr.aliyuncs.com/liujiaxuan/busybox_by_liujiaxuan@sha256:800a83edaed8daab01f81f408912d121d175066900dd422bdcb6c8c91dbb3268
Untagged: busybox:stable-glibc
Untagged: busybox@sha256:fea9e0f09e8cbbe7b2d2ca2ebb6e8da1e2e1d7c6ca7a4cf297eb2fcf5afda5ed
Deleted: sha256:1827167fde90df99d9341a27fbce2b445550eb2b18105e03f98102f00c0ec35e
Deleted: sha256:b4cb8796a924c1fe5cf7031b67a551c63f9236c5cb0e0d51af962285ae361db7
┌─[root@VM-16-15-debian] - [~] - [1688]
└─[$] docker pull crpi-x6zeb1ynyh83ir4y.cn-hangzhou.personal.cr.aliyuncs.com/liujiaxuan/busybox_by_liujiaxuan:v1.0
v1.0: Pulling from liujiaxuan/busybox_by_liujiaxuan
2dc65973bc71: Pull complete
Digest: sha256:800a83edaed8daab01f81f408912d121d175066900dd422bdcb6c8c91dbb3268
Status: Downloaded newer image for crpi-x6zeb1ynyh83ir4y.cn-hangzhou.personal.cr.aliyuncs.com/liujiaxuan/busybox_by_liujiaxuan:v1.0
crpi-x6zeb1ynyh83ir4y.cn-hangzhou.personal.cr.aliyuncs.com/liujiaxuan/busybox_by_liujiaxuan:v1.0
重新拉取时观察到完整的分层下载过程。
创建新版本标签并推送到仓库
docker tag crpi-x6zeb1ynyh83ir4y.cn-hangzhou.personal.cr.aliyuncs.com/liujiaxuan/busybox_by_liujiaxuan:v1.0 crpi-x6zeb1ynyh83ir4y.cn-hangzhou.personal.cr.aliyuncs.com/liujiaxuan/busybox_by_liujiaxuan:v2.0
docker push crpi-x6zeb1ynyh83ir4y.cn-hangzhou.personal.cr.aliyuncs.com/liujiaxuan/busybox_by_liujiaxuan:v2.0
推送过程显示分层已存在,无需重复上传:
The push refers to repository [crpi-x6zeb1ynyh83ir4y.cn-hangzhou.personal.cr.aliyuncs.com/liujiaxuan/busybox_by_liujiaxuan]
b4cb8796a924: Layer already exists
v2.0: digest: sha256:800a83edaed8daab01f81f408912d121d175066900dd422bdcb6c8c91dbb3268 size: 527
通过仓库管理界面查看,可以发现实际存储的镜像数据大小仅为 2.172MB,显著小于本地解压后的 4.42MB:
这一差异体现了镜像存储的压缩机制:镜像在传输和存储时采用压缩格式(2.172MB),而在本地运行时需要解压为可用的文件系统格式(4.42MB)。同时,基于相同镜像层(Digest 相同)的多个标签可以共享存储空间,推送新版本时只需上传元数据信息,大幅节省了网络带宽和存储成本。
这种设计将压缩解压的计算代价分散到各个客户端,虽然单个用户需要承担少量的计算开销,但整体上显著减轻了网络基础设施的压力,实现了”积少成多”的优化效果。