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 mynginx23. 两者区别
表格
| 命令 | 作用 | 适用场景 |
|---|---|---|
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.tar3. 两对命令对比(重点,别搞混)
表格
| 对比项 | export / import | save / 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=always或unless-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:用的镜像