Docker 系统学习
约 8552 字大约 29 分钟
2025-10-01
Docker 在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。使得 Docker 技术比虚拟机技术更为轻便、快捷。
相关信息
Docker 本身不是容器技术,它是容器技术的一个管理工具。
"在容器的基础上进行封装"这句话的意思是:
容器技术的底层:在 Linux 系统里,早就有了实现隔离的基础技术,比如 Namespace 和 Cgroups。这就像已经有了砖块、水泥这些盖房子的原材料。
Docker 的封装:Docker 做的事情,就是把这些复杂的底层技术打包成一个简单好用的工具。它提供了标准化的镜像、一键启动、网络管理等功能。这就像用现成的材料,提供了一套可以快速盖好房子的"乐高积木"或"模板"。
所以,Docker 是让创建和管理容器变得极其简单的工具,而不是容器技术本身。
传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;
而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。
更高效的利用系统资源
由于容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,Docker 对系统资源的利用率更高。无论是应用执行速度、内存损耗或者文件存储速度,都要比传统虚拟机技术更高效。因此,相比虚拟机技术,一个相同配置的主机,往往可以运行更多数量的应用。
更快速的启动时间
传统的虚拟机技术启动应用服务往往需要数分钟,而 Docker 容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。
一致的运行环境
开发过程中一个常见的问题是环境一致性问题。由于开发环境、测试环境、生产环境不一致,导致有些 bug 并未在开发过程中被发现。而 Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现 这段代码在我机器上没问题啊 这类问题。
持续交付和部署
对开发和运维(DevOps )人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。
使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过 Dockerfile 来进行镜像构建,并结合 持续集成(Continuous Integration) 系统进行集成测试,而运维人员则可以直接在生产环境中快速部署该镜像,甚至结合 持续部署(Continuous Delivery/Deployment) 系统进行自动部署。
而且使用 Dockerfile 使镜像构建透明化,不仅仅开发团队可以理解应用运行环境,也方便运维团队理解应用运行所需条件,帮助更好的生产环境中部署该镜像。
更轻松的迁移
由于 Docker 确保了执行环境的一致性,使得应用的迁移更加容易。Docker 可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。
更轻松的维护和扩展
Docker 使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。此外,Docker 团队同各个开源项目团队一起维护了一大批高质量的 官方镜像 ,既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本。
对比传统虚拟机总结
| 特性 | 容器 | 虚拟机 |
|---|---|---|
| 启动 | 秒级 | 分钟级 |
| 硬盘使用 | 一般为 MB | 一般为 GB |
| 性能 | 接近原生 | 弱于 |
| 系统支持量 | 单机支持上千个容器 | 一般几十个 |
基本概念
Docker 包括三个基本概念:镜像(Image),容器(Container),仓库(Repository),理解了这三个概念,就理解了 Docker 的整个生命周期。
Docker 镜像
镜像即根文件系统:Docker 镜像本质上是一个轻量级的、独立的软件包,包含了运行一个应用所需的完整环境:程序、库、资源、配置以及参数(如环境变量)。它相当于一个 Linux 操作系统的 root(根文件夹) 文件系统。
镜像是静态的:镜像内容在构建完成后就不可更改,不包含任何动态数据。
核心特性:分层存储
- 目的:为了解决镜像体积庞大的问题,借鉴了 Union FS 技术。
- 工作原理:镜像由多个只读的层(Layer)叠加而成。构建时,每一层都是在前一层的基础上进行修改。每一层构建完成后就不会再改变。
- 关键优势:
- 空间效率与复用:不同的镜像可以共享相同的基础层,节省存储空间和下载时间。
- 可追溯性与缓存:层的设计使得镜像的构建过程清晰,并且可以利用缓存加速构建。
- 重要注意事项:在构建镜像的时候如果对文件进行了修改(如删除),事实上非真正删除底层数据,而是在当前层标记为删除,在分发使用的时候这个删除的文件(层)依旧占用空间。因此,在构建镜像时,应确保每一层只添加必要的内容,并及时清理临时文件,以避免镜像臃肿。
简单来说,Docker 镜像是一个通过分层技术实现的、静态的、包含完整运行环境的文件系统,这种分层结构使其非常高效和灵活。
Docker 容器
容器与镜像的关系:容器是镜像的运行时实例。这种关系类似于面向对象编程中的类和对象。镜像是静态的、不可变的定义,而容器是动态的、可操作的实体。
容器的本质:容器的实质是一个进程,但是一个具有隔离特性的进程。它运行在独立的命名空间中,拥有自己的根文件系统、网络配置、进程空间和用户ID空间,从而与宿主机和其他容器隔离,提供了安全、独立的运行环境。
容器的存储层:
- 容器基于镜像的分层之上,创建一个临时的、可读写的容器存储层。
- 该存储层的生命周期与容器完全一致:容器被删除,存储层中的数据也会丢失。
数据持久化的最佳实践:
- 核心原则:容器存储层应保持无状态化,不应向其写入需要持久化的数据。
- 正确方法:必须使用数据卷 或绑定宿主目录来实现数据持久化。
- 优势:这些方式的数据读写直接作用于宿主(或网络存储),性能更好、稳定性更高,并且生命周期独立于容器,容器删除后数据依然存在。
简单来说,Docker 容器是镜像的运行实例,是一个隔离的进程。为了实现数据持久化,必须遵循最佳实践,使用数据卷而非容器内部的临时存储层。
Docker Registry
- Registry:一个镜像仓库服务,其中可以管理多个仓库。
- Repository:一个仓库对应一个项目或软件(如
ubuntu),其中存放了该软件的不同版本。 - Tag:标签用于区分同一仓库中的不同版本(如
18.04,latest)。完整的镜像地址格式为<仓库名>:<标签>。
Registry 的两种主要类型:
- 公开服务
- 功能:提供公共的镜像存储和分发。用户可以免费上传、下载公开镜像。
- 代表:
- Docker Hub:默认的官方仓库,拥有大量高质量官方镜像。
- 其他:Quay.io、GitHub Container Registry (ghcr.io), Google Container Registry 等。
- 国内特色:由于网络原因,国内用户常使用加速器(如阿里云、DaoCloud加速器)或国内的公有仓库(如网易云、阿里云镜像库)来提升下载速度。
- 私有服务
- 功能:用户在本地或私有云环境下搭建,用于存储和分发内部使用的私有镜像。
- 搭建方式:
- Docker Registry:官方提供的基础镜像,仅包含核心API服务,无图形界面。
- 第三方软件:如 Harbor 和 Nexus,提供图形界面、用户管理、安全扫描等高级功能
一句话总结:Docker Registry 是镜像的“应用商店”,公开服务如同官方应用市场,而私有服务则如同企业内部私有的应用市场,用于安全地管理自有应用。
使用 Docker 镜像
Docker 运行容器前需要本地存在对应的镜像,如果本地不存在该镜像,Docker 会从镜像仓库下载该镜像。
获取镜像
从 Docker 镜像仓库获取镜像的命令是 docker pull。其命令格式为:
docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]具体的选项可以通过 docker pull --help 命令看到
docker pull --platform linux/arm64 ubuntu:20.04--platform 后面可以指定:
操作系统/架构,例如:
linux/amd64→ x86_64 架构linux/arm64→ ARM 64 位架构linux/arm/v7→ ARM 32 位架构
ubuntu:20.04 → 要拉取的镜像
docker manifest inspect ubuntu:20.04会列出镜像支持的 所有平台和架构
Docker 镜像仓库地址:地址的格式一般是 <域名/IP>[:端口号]。默认地址是 Docker Hub docker.io 。
仓库名:如之前所说,这里的仓库名是两段式名称,即 <用户名>/<软件名>。如果不给出用户名,则默认为 library,也就是官方镜像。
$ docker pull mysql:8.0
8.0: Pulling from library/mysql
a803e7c4b030: Pull complete
c3c8d5a7b6da: Pull complete
2b9f5f4d0fdb: Pull complete
5b93ad8d3a0e: Pull complete
a03e6b85c0e6: Pull complete
f07fa83e6c38: Pull complete
e6f8c35cb7d9: Pull complete
e6691f90c1d9: Pull complete
4e5bdf37d774: Pull complete
a1a0e7c2f2c1: Pull complete
8b0f6a7dae38: Pull complete
Digest: sha256:eb9c7e6fae0e7c90e3f66a7b2b4d1e2f3b4b5d6c7a8b9a0c1d2e3f4a5b6c7d8
Status: Downloaded newer image for mysql:8.0
docker.io/library/mysql:8.0命令解析
docker pull mysql:8.0:拉取官方mysql镜像的8.0标签版本。- 仓库地址:未指定地址,默认从 Docker Hub 下载。
- 完整镜像名:输出最后一行显示为
docker.io/library/mysql:8.0,这验证了它来自 Docker Hub 的官方library仓库。
核心特性:分层存储的直观体现
- 输出中显示了多行
Pull complete,每一行代表镜像的一个层被成功下载。 - MySQL 镜像比 Ubuntu 镜像更复杂,因此层数也更多(本例中有11层)。这印证了镜像是由多个只读层联合组成的。
安全与一致性:数字摘要
Digest: sha256:eb9c7****c7d8- 这是镜像内容的唯一、安全的哈希值。只要镜像内容不变,这个摘要就不会变。它可以用来精确验证你拉取的镜像是否与官方发布的镜像完全一致,防止镜像被篡改。
镜像的维护与更新
- 如果你在不同时间拉取同一个标签(如
mysql:8.0)的镜像,看到的层ID和摘要可能会不同。 - 这是因为维护者会更新镜像(例如,打上安全补丁),然后用相同的标签重新发布。这确保了用户总能获取到该版本最新、最安全的镜像。
通过 docker pull mysql:8.0 的命令输出,我们清晰地看到了 Docker 镜像从默认的 Docker Hub 仓库被分层下载的过程,并通过唯一的数字摘要来保证内容的一致性和安全性,这是镜像工作机制的完美演示。
运行镜像
docker run -d --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -p 3306:3306 mysql:8.0
# c710212735c9d64b6d18bd0b26f6bfeae78db0f52df22d8a3764b46d2d6c6f33命令解析:
- docker run:核心命令,用于创建并启动一个新容器。
- -d:以分离模式 运行容器,即让容器在后台运行。这是运行服务器类应用(如数据库、Web服务器)的标准方式。
- --name some-mysql:为容器指定一个自定义名称
some-mysql,便于后续管理(如启动、停止、查看日志),否则Docker会随机生成一个名字。 - -e MYSQL_ROOT_PASSWORD=my-secret-pw:-e 参数用于设置环境变量。这是 MySQL 镜像的强制要求,通过它来设置
root用户的密码。这是配置容器化应用的核心方式。 - -p 3306:3306:端口映射。将宿主机的
3306端口映射到容器内部的3306端口。这样,你才能通过宿主机IP和端口访问容器内运行的MySQL服务。 - mysql:8.0:指定使用的镜像名称和标签。
- 长串哈希值:命令执行后返回的是容器的唯一ID,表示容器已成功在后台启动。
docker run -it --rm ubuntu:18.04 bash- -it:这是两个参数的组合,用于获取一个交互式终端。
-i:保持标准输入打开,允许你与容器交互。-t:分配一个伪终端,让你像在真实服务器上一样操作。
- --rm:一个非常实用的参数。表示容器退出后自动删除。这适合临时性的测试任务,避免留下大量停止的容器占用磁盘空间。
- ubuntu:18.04:指定用于创建容器的基础镜像。
- bash:在容器启动后要执行的命令,这里表示启动一个 Bash shell。
进入容器:命令执行后,会切换到容器内部的 Shell 提示符(如 root@e7009c6ce357:/#)。
执行命令:在容器内可以执行任何命令,例如 cat /etc/os-release,确认了容器内运行的是 Ubuntu 18.04 系统。
退出容器:使用 exit 命令退出 Bash shell,容器随之停止。由于使用了 --rm 参数,该容器也会被自动删除。
docker run -it --rm 是一个经典的测试命令组合,它能让你快速进入一个临时容器进行操作,退出后容器自动清理,非常方便快捷。
| 命令 | 作用 | 是否创建新容器 | 使用场景 |
|---|---|---|---|
docker run -it | 启动容器并进入交互 | 是 | 想从镜像新建并进入一个容器时 |
docker exec -it | 在容器里执行命令 | 否 | 容器已存在并在运行时,想进去看看、调试、跑命令 |
列出镜像
docker image ls
# docker images(快捷方式)REPOSITORY TAG IMAGE ID CREATED SIZE列表包含了 仓库名、标签、镜像 ID、创建时间 以及 所占用的空间。
Docker Hub 上的大小:显示的是 压缩后的镜像体积(网络传输大小),用于下载或上传时参考。
docker image ls 显示的大小:显示 本地展开后的镜像各层占用的总空间,更准确地反映磁盘占用。
镜像实际占用空间:
Docker 镜像是 多层结构(layered),可以复用相同的基础层。
多个镜像共享相同层时,每层只保存一次。
因此 不同镜像大小之和 ≠ 实际硬盘占用,实际占用通常更小。
查看实际空间占用,使用命令:
docker system dfImages:所有镜像占用空间
Containers:所有容器占用空间
Local Volumes:卷占用空间
Build Cache:构建缓存占用空间
Reclaimable:可回收空间(未使用的镜像层、卷等)
root@armbian:~# docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 11 7 1.383GB 687.4MB (49%)
Containers 7 7 122.4MB 0B (0%)
Local Volumes 3 1 4.559MB 0B (0%)
Build Cache 0 0 0B 0B还可以根据仓库名列出镜像,比如显示所有 ubuntu 仓库的镜像:
docker image ls ubuntu仓库名 + 标签列出特定镜像
docker image ls ubuntu:18.04使用过滤器 --filter / -f
按时间过滤
列出 在某个镜像之后创建的镜像:
docker image ls -f since=mongo:3.2列出 在某个镜像之前创建的镜像:
docker image ls -f before=mongo:3.2
按标签过滤
如果镜像构建时定义了 LABEL,可以用标签筛选:
docker image ls -f label=com.example.version=0.1docker image ls 默认会输出一个完整的表格,适合直接查看整体情况。
docker image ls -q 只输出 镜像 ID,一行一,用途:常与其他命令配合,如删除镜像:
docker rmi $(docker image ls -q -f dangling=true可以通过过滤条件筛选需要的镜像,例如:
docker image ls -f dangling=true -q只输出 虚悬镜像(dangling image) 的 ID。 常见过滤条件:dangling=true/false、before=<image>、reference=<repo> 等。
自定义输出通过 --format 参数 + Go 模板语法,适合更灵活的场景,比如:
- 自定义列内容
docker image ls --format "{{.ID}}: {{.Repository}}"只显示 ID 和仓库名。
自定义表格
docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}"效果与默认类似,但可以指定自己想要的列,且带标题行。
常见可用的字段有:.ID,.Repository,.Tag,.Digest,.Size,.CreatedSince,.CreatedAt
总结要点
-q→ 精简模式,常用于脚本和批处理操作。--filter/-f→ 精确筛选目标对象(镜像、容器、网络等都支持)。--format→ 使用 Go 模板自定义输出,方便人读或者机器解析。
删除镜像
如果要删除本地的镜像,可以使用 docker image rm 命令,其格式为:
docker image rm [选项] <镜像>
# docker image rm redis:latest nginx:latest mongo:3.2
# docker image rm 5f515359c7f8 05a60462f8ba fe9198c04d62删除所有悬空镜像(无标签镜像)
docker image rm $(docker images -f "dangling=true" -q)删除所有仓库名为 redis 的镜像:
docker image rm $(docker image ls -q redis)或者删除所有在 mongo:3.2 之前的镜像:
docker image rm $(docker image ls -q -f before=mongo:3.2)<镜像> 可以是 镜像短 ID、镜像长 ID、镜像名 或者 镜像摘要。
镜像摘要(Digest)是基于镜像内容生成的全局唯一哈希,用于保证内容一致性和安全验证。
- 镜像 ID 和 摘要 Digest 都是基于 SHA256 的哈希
- ID 更多用于本地管理和层标识
- Digest 用于内容唯一性验证和远程镜像一致性检查
查看长ID:
docker image ls --no-truncdocker image inspect ubuntu:18.04查询单个镜像的详细信息,输出中也会有长ID
Untagged 和 Deleted
镜像的唯一标识
- 镜像由 ID 和 摘要 唯一确定
- 一个镜像可以有多个标签(Repository:Tag)
删除标签 → Untagged
- 使用
docker image rm <镜像>删除的是 标签 - 如果镜像还有其它标签指向它,命令只会 取消指定标签(Untagged)
- 镜像本身仍然存在,直到所有标签都被取消
删除镜像 → Deleted
- 当镜像 没有任何标签 且 没有被其它镜像依赖,才会触发真正的删除(Deleted)
- Docker 会从上层向基础层依次判断哪些层可以删除
镜像层依赖
- 镜像是多层结构(UnionFS)
- 如果某层被其它镜像或容器依赖,该层不会被删除
- 直到 没有任何镜像或容器依赖该层,才会真正释放存储
容器依赖
- 如果有容器基于该镜像(即使未运行),镜像也不能删除
- 删除镜像前应确保相关容器已经删除
悬空镜像
<none> <none> 00285df0df87 2 days ago 532 MB悬空镜像就是 没有名字/标签的镜像,常见于镜像更新、构建未打 tag 或手动删除 tag 后遗留的旧镜像。它占用空间但无法直接通过名字访问,可以安全清理。
悬停镜像的产生
假设你第一次拉取 ubuntu:18.04:
docker pull ubuntu:18.04本地生成一个镜像 A
tag 指向 A:
ubuntu:18.04 -> 镜像 A (ID: abc123)此时没有悬空镜像。
官方推送了新版 ubuntu:18.04,你再拉取:
docker pull ubuntu:18.04Docker 下载新版镜像 B(ID: def456)
tag ubuntu:18.04 现在指向 B
镜像 A 失去 tag,变成 <none>:<none>
结果:
<none>:<none> -> 镜像 A (旧版本)
ubuntu:18.04 -> 镜像 B (新版本)镜像 A 仍在本地,只是没有名字了 → 悬空镜像。
docker build .如果在构建镜像的时候没有使用 -t 给镜像打名字,构建完成的镜像也会是 <none>:<none>。
这种情况和旧镜像失去 tag 是一样的结果。
悬空镜像本身没有名字和直接用途,大多数情况下已经没有价值,清理它可以节省磁盘空间,只有你明确想保留的,才需要重新打 tag 保存。
docker image prunedocker image prune 的作用是 清理本地未使用的 Docker 镜像,特别是那些 悬空镜像(dangling images),释放磁盘空间。
默认只删除 dangling 镜像(即 <none>:<none> 的镜像),删除前会有提示,需要确认
-f 或 --force 不再提示,直接删除
-a 或 --all 删除 所有未被容器使用的镜像,不仅仅是悬空镜像,注意:如果某个镜像没有 tag 或未被容器使用,会被删除。
还可以按 过滤条件删除,比如,删除 24 小时前未使用的镜像:
docker image prune -a --filter "until=24h"中间层镜像
Docker 构建镜像时会生成 中间层镜像,用于 加速构建和重复利用资源。
每一层对应 Dockerfile 的一个命令(RUN / COPY / ADD 等),上层镜像会依赖下层中间层。
docker image ls 只显示 顶层镜像(有 tag 的镜像)。
如果想显示 包括中间层在内的所有镜像 docker image ls -a
你会看到很多 无标签的镜像(<none>)。
这些无标签镜像大多是 中间层,是其他镜像的依赖。
管理注意事项
- 不要删除这些中间层镜像,否则依赖它们的上层镜像可能会出错。
- 这些镜像 不会重复占用磁盘空间,因为 Docker 会复用相同层。
- 当删除顶层镜像后,依赖的中间层如果没有其他引用,才会被连带清理。
总结:中间层镜像是 Docker 的内部实现细节,用于构建和复用,不必手动管理。只要保留顶层镜像,Docker 会自动管理中间层的存储和复用。
镜像快照
docker commit 用于 将容器当前状态保存为一个新的镜像,相当于把容器“拍快照”变成可复用的镜像。
把容器改动保存成镜像
- 当你在容器里安装了软件、修改了配置、添加了文件,想保留这些改动
- 使用
docker commit就可以生成一个新的镜像,后续可以用它启动新容器
生成自定义镜像
- 可以基于官方镜像快速做定制,而不必每次都写 Dockerfile
docker commit [OPTIONS] <容器ID或名称> <新镜像名>:<标签>-a:作者信息
-m:提交说明(commit message)
--change:修改 Dockerfile 指令(如 ENV、CMD、EXPOSE 等)
当我们运行一个容器的时候(如果不使用卷的话),我们做的任何文件修改都会被记录于容器存储层里。而 Docker 提供了一个 docker commit 命令,可以将容器的存储层保存下来成为镜像。换句话说,就是在原有镜像的基础上,再叠加上容器的存储层,并构成新的镜像。以后我们运行这个新镜像的时候,就会拥有原有容器最后的文件变化。
注
慎用 docker commit
镜像容易臃肿
docker commit会把容器当前状态- 直接打包成新镜像。
- 容器里除了你想修改的文件外,还有许多临时文件、缓存、日志、安装包残留等,都会被打包进去。
- 每次 commit 都会增加一层,这些无关内容会长期存在于镜像中,无法清理。
黑箱操作,不可追踪
- commit 是对容器文件系统的快照,不记录具体的命令和操作步骤。
- 除了制作人,别人几乎无法复现镜像的构建过程。
- 即使是制作人,过一段时间后也很难记起具体操作,维护成本高。
分层机制导致问题积累
- Docker 镜像的分层是不可变的,每一层都是只读的。
- commit 只会在当前层增加修改记录,不会删除上一层的内容。
- 结果是镜像越来越大,即使你删除了容器里的文件,底层层依然占空间。
维护困难
- 黑箱镜像不利于团队协作。
- 后期修改或调试时,很难知道镜像是如何生成的。
容器历史记录
docker history 是 Docker 提供的一个命令,用来 查看镜像(image)每一层的历史信息。它可以帮助你了解镜像是如何构建的,每一层的大小,以及每一层对应的命令。
docker history [OPTIONS] IMAGE| 选项 | 说明 |
|---|---|
-H, --human | 以可读格式显示大小(默认是字节) |
--no-trunc | 不截断输出,显示完整信息 |
-q, --quiet | 只显示层ID,不显示其他信息 |
docker history nginx:latest
IMAGE CREATED CREATED BY SIZE COMMENT
cfc29b0b0b1d 3 weeks ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemo… 0B
<missing> 3 weeks ago /bin/sh -c #(nop) EXPOSE 80 0B
<missing> 3 weeks ago /bin/sh -c #(nop) STOPSIGNAL SIGTERM 0B
<missing> 3 weeks ago /bin/sh -c apt-get update && apt-get install… 21.6MB
<missing> 3 weeks ago /bin/sh -c #(nop) LABEL maintainer=NGINX D… 0B- IMAGE:这一层的镜像ID。
- CREATED:这一层创建的时间。
- CREATED BY:执行创建这一层的命令。
- SIZE:这一层的大小。
- COMMENT:可选备注信息。
主要用于:
分析镜像大小,可以找出哪些层占用空间大,从而优化镜像。
追踪镜像构建历史,了解镜像是如何一步步构建出来的,特别是从别人提供的镜像中学习 Dockerfile 的写法。
排查问题,如果某个镜像层有问题,可以通过 docker history 确认具体命令和层信息。
当你执行 docker commit 时,Docker 会在镜像上 增加一层(类似新的“快照”层)。
docker history 可以显示这个镜像的 所有层历史,包括用 docker commit 创建的层。
所以,如果你对某个镜像做过 commit,然后查看 docker history,就会看到对应的那一层。
docker history 只能显示 每层的创建命令、时间和大小,它不会直接告诉你是哪个容器做的 commit,但你可以通过 commit 的时候加标签或注释 来标记:
docker commit -m "修改配置文件" mycontainer myimage:latest然后在 docker history myimage:latest 中,你就能看到 COMMENT 列显示 "修改配置文件",知道这层是你 commit 的。
更改容器
docker run --name webserver -d -p 80:80 nginxdocker exec -it webserver bash
root@3729b97e8226:/# echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
root@3729b97e8226:/# exit
exit修改了容器的文件,也就是改动了容器的存储层。我们可以通过 docker diff 命令看到具体的改动。
查看容器差异
docker diff [容器]docker diff 就是用来查看容器相对于镜像的 文件系统改动,它会输出三种类型的变化标记:
A (Added):新增了一个文件或目录
C (Changed):修改了一个已有的文件或目录(内容变了,或者元数据变了,比如权限、时间戳)
D (Deleted):删除了一个文件或目录
Dockerfile
操作容器
导入和导出
Docker 还提供了 docker save 和 docker load 命令,用以将镜像保存为一个文件,然后传输到另一个位置上,再加载进来。这是在没有 Docker Registry 时的做法,现在已经不推荐,镜像迁移应该直接使用 Docker Registry,无论是直接使用 Docker Hub 还是使用内网私有 Registry 都可以。
docker save -o <保存文件名>.tar <镜像名>:<标签>
docker load -i <保存文件名>.tar文件名可以自定义为任意名称甚至任意后缀名,但文件的本质都是归档文件,但是推荐使用 .tar 后缀名以示区别。
注意:如果同名则会覆盖(没有警告)
若使用 gzip 压缩:
docker save <镜像名>:<标签> | gzip > <保存文件名>.tar.gz然后我们将 <保存文件名>.tar.gz 文件复制到了到了另一个机器上,可以用下面这个命令加载镜像:
docker load -i <保存文件名>.tar.gz镜像的实现原理
分层结构
- 每个镜像由很多只读层组成(每个层对应 Dockerfile 里的一条指令)。
- 层与层之间叠加,最终形成完整镜像。
联合文件系统(UnionFS / OverlayFS)
- 技术上通过一种叫 UnionFS(Docker 常用 OverlayFS)的文件系统,把多层目录“合并”为一个目录。
- 上层可以覆盖下层的同名文件,看起来就像一个整体。
可写层
- 容器启动时,镜像层是只读的,Docker 会在顶层加一个可写层,所有的修改都写在这一层。
- 镜像本身不变,因此能保证复用和一致性。
好处
- 节省空间:多个镜像可以共享相同的底层层。
- 快速构建:只修改或新增的部分会形成新的一层。
- 可维护:删除容器不会影响镜像。
Docker 镜像是由多层只读层叠加组成的,运行时再加一个可写层,借助 OverlayFS 看起来像一个完整文件系统,从而实现增量构建、复用和轻量化。
Docker Hub
可以通过执行 docker login 命令交互式的输入用户名及密码来完成在命令行界面登录 Docker Hub。
可以通过 docker logout 退出登录。
你可以通过 docker search 命令来查找官方仓库中的镜像,并利用 docker pull 命令来将它下载到本地。
在查找的时候通过 --filter=stars=N 参数可以指定仅显示收藏数量为 N 以上的镜像。
用户也可以在登录后通过 docker push 命令来将自己的镜像推送到 Docker Hub。
docker tag ubuntu:18.04 username/ubuntu:18.04
docker push username/ubuntu:18.04数据管理
数据卷
数据卷 是一个可供一个或多个容器使用的特殊目录,它绕过 UnionFS,可以提供很多有用的特性:
Docker 卷(Volume) 是挂载在容器内的 宿主机目录(或者由 Docker 管理的卷路径)。
容器对卷的读写操作,实际上是在操作宿主机上的真实文件系统。
因此,无论在容器内还是宿主机上修改卷里的文件,都会立即生效,不需要重启容器。
即使容器删除,卷里的数据依然保留在宿主机上,下次启动新容器时可以继续使用,但是要注意挂载路径必须一致。
创建数据卷
docker volume create [选项] <卷名>选项 常用的有:
-d, --driver:指定卷的驱动(默认是local)--label:添加标签--opt:传递驱动特定选项
卷名:自定义卷的名字,如果不指定,Docker 会生成一个随机名字
查看数据卷
查看所有的 数据卷
docker volume ls查看指定 数据卷 的信息
docker volume inspect <卷名>挂载数据卷
在用 docker run 命令的时候,使用 --mount 标记来将 数据卷 挂载到容器里。在一次 docker run 中可以挂载多个 数据卷。
下面创建一个名为 web 的容器,并加载一个 数据卷 到容器的 /usr/share/nginx/html 目录。
docker run -d -P \
--name web \
# -v <卷名>:/usr/share/nginx/html \
--mount source=<卷名>,target=/usr/share/nginx/html \
nginx:alpine删除数据卷
docker volume rm <卷名>数据卷 是被设计用来持久化数据的,它的生命周期独立于容器,Docker 不会在容器被删除后自动删除 数据卷,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的 数据卷。
如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使用 docker rm -v 这个命令。
无主的数据卷可能会占据很多空间,要清理请使用以下命令
docker volume prune挂载目录
挂载一个主机目录作为数据卷,使用 --mount 标记可以指定挂载一个本地主机的目录到容器中去。
docker run -d -P \
--name web \
# -v /src/webapp:/usr/share/nginx/html \
--mount type=bind,source=/src/webapp,target=/usr/share/nginx/html \
nginx:alpine上面的命令加载主机的 /src/webapp 目录到容器的 /usr/share/nginx/html目录。
这个功能在进行测试的时候十分方便,比如用户可以放置一些程序到本地目录中,来查看容器是否正常工作。
本地目录的路径必须是绝对路径,并且使用 --mount 参数时如果本地目录不存在,Docker 会报错。
Docker 挂载主机目录的默认权限是 读写,用户也可以通过增加 readonly 指定为 只读。
docker run -d -P \
--name web \
# -v /src/webapp:/usr/share/nginx/html:ro \
--mount type=bind,source=/src/webapp,target=/usr/share/nginx/html,readonly \
nginx:alpine加了 readonly 之后,就挂载为 只读 了。如果你在容器内 /usr/share/nginx/html 目录新建文件,会显示如下错误
/usr/share/nginx/html # touch new.txt
touch: new.txt: Read-only file system