Docker 进阶笔记(存储 + 镜像分层 + 数据卷|纠错 + 案例 + 注释版)

📅 2026/7/8 6:47:52 👁️ 阅读次数 📝 编程学习
Docker 进阶笔记(存储 + 镜像分层 + 数据卷|纠错 + 案例 + 注释版)

一、创建容器的两种方式:docker run vs docker create

1. docker run(最常用,创建并启动)

一条命令完成:创建容器 + 启动容器

bash

运行

# 完整格式 docker run -dit --name 容器名 -p 宿主机端口:容器端口 镜像名:版本 [启动命令] # 案例:创建并启动一个nginx容器 docker run -dit --name mynginx -p 8080:80 nginx:latest

参数解释:

  • -d:后台守护进程运行
  • -i:保持标准输入打开
  • -t:分配伪终端
  • --name:给容器起名字
  • -p:端口映射

2. docker create(只创建,不启动)

只创建容器,容器处于停止状态,需要手动docker start才会运行。

bash

运行

# 完整格式 docker create --name 容器名 -p 宿主机端口:容器端口 镜像名:版本 # 案例:只创建不启动 docker create --name mynginx2 -p 8081:80 nginx:latest # 手动启动 docker start mynginx2

3. 两者区别

表格

命令作用适用场景
docker run创建 + 启动,一步到位99% 场景都用这个
docker create只创建不启动需要提前准备好容器、稍后再启动的特殊场景

二、docker commit:把容器的改动保存为新镜像

作用

你在容器里装了软件、改了配置,想把这个状态保存成一个新的镜像,以后直接用这个镜像启动容器,不用再重新装一遍。

命令格式

bash

运行

docker commit -p 容器名/ID 新镜像名:版本
  • -p:commit 时暂停容器,保证数据一致性

实战案例

bash

运行

# 1. 先进入容器,装个vim(模拟你对容器做了修改) docker exec -it mynginx /bin/bash apt update && apt install -y vim exit # 2. 把修改后的容器保存成新镜像 docker commit -p mynginx mynginx-vim:v1 # 3. 查看本地镜像,能看到新生成的 mynginx-vim:v1 docker images # 4. 用新镜像启动容器,进去直接就有vim了,不用再装 docker run -dit --name test2 mynginx-vim:v1

注意事项

  • commit 适合临时保存、测试用,生产环境推荐用 Dockerfile 构建镜像,更规范、可追溯
  • commit 保存的是容器可写层的变化,基于原镜像增量生成新镜像

三、容器导出导入 vs 镜像导出导入(两对命令别搞混)

1. 容器导出导入:export /import

作用

运行中的容器整个导出成一个压缩包,可以传到别的机器上导入成镜像。

导出容器

bash

运行

# 格式:docker export -o 导出的包名 容器名/ID docker export -o mynginx.tar mynginx

注意:export 导出的是容器的文件系统,会丢失镜像的历史层、启动命令、元数据,导入后只是一个光秃秃的文件系统镜像。

导入容器包成镜像

bash

运行

# 格式:docker import 包名 新镜像名:版本 docker import mynginx.tar mynginx-import:v1

导入后直接变成镜像,不是容器,需要用 docker run 启动。

2. 镜像导出导入:save /load

作用

镜像完整导出成压缩包,传到别的机器上完整导入,保留所有镜像层、元数据、启动命令。

导出镜像

bash

运行

# 格式:docker save -o 导出的包名 镜像名:版本 docker save -o nginx.tar nginx:latest
导入镜像包

bash

运行

# 格式:docker load -i 包名 docker load -i nginx.tar

3. 两对命令对比(重点,别搞混)

表格

对比项export / importsave / load
操作对象容器镜像
导出内容容器文件系统(单层)完整镜像(所有层 + 元数据)
丢失信息丢失启动命令、镜像历史、环境变量等完整保留,什么都不丢
导入后变成镜像(需要自己指定启动命令)还原成和原来一模一样的镜像
文件大小更小更大
适用场景迁移容器状态(了解即可,不常用)镜像迁移、离线部署(生产常用)

记忆:容器用 export,镜像用 save。生产环境镜像迁移一律用 save/load。


四、写时复制(CoW)与镜像分层原理

1. 什么是写时复制 Copy-on-Write

镜像作为容器的基础环境,是只读的。 所有容器共用同一份镜像文件,容器启动时在镜像上面加一层可写层

  • 读文件:直接从镜像层读
  • 写文件:先把文件从镜像层复制到可写层,然后在可写层修改
  • 好处:多个容器共用镜像,节省大量磁盘空间,启动快

2. 联合文件系统四层结构(OverlayFS)

Docker 用 OverlayFS 实现联合挂载,一共四层目录:

表格

层级名称读写性作用
第 1 层lowerdir只读镜像层,所有镜像文件都在这里,可以有多个 lowerdir(镜像有多少层就有多少个)
第 2 层upperdir可读可写容器层,容器运行产生的所有修改都存在这一层
第 3 层merged统一视图给用户看的合并视图,把 lowerdir 和 upperdir 的文件合并展示,upperdir 里有的就展示 upperdir 的,没有的展示 lowerdir 的
第 4 层workdir工作目录内部临时工作目录,文件拷贝、原子操作时用,用户不用管

通俗理解

  • lowerdir = 公共课本,大家都能看,不能改
  • upperdir = 你自己的笔记本,你可以在上面写东西
  • merged = 你看书的视角,书上有的直接看,你记了笔记的地方看你的笔记
  • workdir = 草稿纸,写东西时先打草稿再誊上去

验证命令

bash

运行

# 查看容器的分层信息,能看到UpperDir、LowerDir、MergedDir、WorkDir docker inspect 容器名 | grep -A 10 GraphDriver

五、数据卷 -v 参数详解(持久化存储)

为什么需要数据卷

容器的可写层(upperdir)数据是临时的,容器删了数据就没了。 数据卷把容器里的数据存到宿主机上,容器删了数据还在,实现持久化。

-v 参数的三种用法

用法 1:bind 绑定挂载(绝对路径模式)

把宿主机的指定目录和容器目录绑定。

bash

运行

# 格式:docker run -v 宿主机绝对路径:容器内路径 docker run -dit --name mynginx -v /root/html:/usr/share/nginx/html -p 8080:80 nginx

特点:

  • 宿主机路径必须是绝对路径
  • 宿主机目录不存在会自动创建
  • 你完全知道数据存在宿主机哪个位置,方便管理
  • 适合:配置文件、网站代码、日志等需要手动管理的文件
用法 2:volume 卷存储(具名卷模式)

Docker 自己在宿主机上找个地方存数据,你只需要给卷起个名字。

bash

运行

# 格式:docker run -v 卷名:容器内路径 docker run -dit --name mynginx -v nginx-html:/usr/share/nginx/html -p 8080:80 nginx

特点:

  • 冒号前面是卷名(不是路径,没有斜杠开头)
  • Docker 自动管理存储位置,一般在/var/lib/docker/volumes/
  • 卷不存在会自动创建,已存在就直接用
  • 适合:数据库数据、应用产生的数据等不需要手动找路径的文件
用法 3:匿名卷(不推荐,了解即可)

只写容器路径,不写宿主机的部分,Docker 自动创建一个随机名字的卷。

bash

运行

docker run -dit --name mynginx -v /usr/share/nginx/html nginx

特点:

  • 卷名是随机哈希,不好找、不好管理
  • 容器删了卷还在,但你不知道哪个是哪个
  • 基本没人用,了解就行

bind 和 volume 对比

表格

对比项bind 绑定挂载volume 卷存储
-v 写法绝对路径:容器路径卷名:容器路径
存储位置你指定的任意目录Docker 统一管理(/var/lib/docker/volumes)
权限控制受宿主机目录权限影响Docker 统一管理,权限问题少
可移植性差,换机器路径可能不一样好,只认卷名
适用场景配置文件、代码、日志数据库数据、应用数据

六、docker volume 卷管理命令

常用命令

bash

运行

# 1. 创建一个卷 docker volume create 卷名 # 案例 docker volume create mydata # 2. 查看所有卷 docker volume ls # 3. 查看卷的详细信息(能看到存在宿主机哪个路径) docker volume inspect 卷名 # 案例 docker volume inspect mydata # 输出里的 Mountpoint 就是真实存储路径 # 4. 删除一个卷(容器在用的卷删不掉,要先删容器) docker volume rm 卷名 # 5. 清理所有没被使用的卷(释放空间) docker volume prune

实战案例

bash

运行

# 1. 先创建一个卷 docker volume create mysql-data # 2. 启动mysql容器,把数据存在这个卷里 docker run -d --name mysql -v mysql-data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7 # 3. 进去建个库、插点数据(模拟业务数据) docker exec -it mysql mysql -uroot -p123456 create database testdb; exit # 4. 删掉容器(模拟容器挂了) docker rm -f mysql # 5. 重新启动一个mysql容器,挂载同一个卷 docker run -d --name mysql2 -v mysql-data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7 # 6. 进去看,之前的testdb还在!数据没丢 docker exec -it mysql2 mysql -uroot -p123456 show databases;

七、--restart 容器开机自启策略

作用

设置容器的重启策略,Docker 服务重启、服务器重启后,容器要不要自动起来。

四种策略

表格

策略值作用
no不自动重启(默认)
always总是自动重启,不管什么原因退出的都重启
on-failure只有异常退出(退出码非 0)时才重启
unless-stopped总是重启,除非你手动 stop 了它

实战案例

bash

运行

# 启动容器时设置开机自启 docker run -dit --name mynginx --restart=always -p 8080:80 nginx # 给已经存在的容器修改重启策略 docker update --restart=always 容器名

怎么选

  • 生产环境重要服务:--restart=alwaysunless-stopped
  • 临时测试容器:默认 no 就行
  • 批处理任务(跑完就结束的):on-failure

八、完整的容器创建命令(带所有常用参数)

bash

运行

docker run -dit \ --name 容器名 \ -p 宿主机端口:容器端口 \ -v 宿主机路径/卷名:容器内路径 \ -e 环境变量名=值 \ --restart=always \ 镜像名:版本 \ [启动命令]

完整案例:启动一个带持久化的 nginx

bash

运行

docker run -dit \ --name myweb \ -p 80:80 \ -v /root/web:/usr/share/nginx/html \ -v /root/nginx.conf:/etc/nginx/nginx.conf \ --restart=always \ nginx:1.24

参数逐个解释:

  • -dit:后台交互式运行
  • --name myweb:容器名叫 myweb
  • -p 80:80:宿主机 80 端口映射到容器 80
  • -v /root/web:/usr/share/nginx/html:网站目录挂载到宿主机
  • -v /root/nginx.conf:/etc/nginx/nginx.conf:配置文件挂载到宿主机,改配置不用进容器
  • --restart=always:开机自动重启
  • nginx:1.24:用的镜像