小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
什么是容器数据卷
docker 如果数据在容器中,那我们容器删除,数据就会丢失!需求:需求可以持久化。就好比我们使用MySQL,容器删除后,数据丢失了!数据之间可以有一个数据共享的技术,Docker 容器中产生的数据,同步到本地。这个就是卷技术,目录的挂载,将我们容器内容的目录,挂载到Linux上面。
总结:容器的持久化和同步操作,容器间也可以数据共享
使用数据卷
命令挂载(-v 主机目录:容器内目录)
# 启动一个Ubuntu镜像,将/home目录挂载到外部/home/ceshi
$ docker run -it -v /home/ceshi:/home ubuntu /bin/bash
# 验证
$ docker inspect b4b
#...
"Mounts": [
{
"Type": "bind",
"Source": "/home/ceshi", #主机内地址
"Destination": "/home", #容器内地址
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
#...
# 容器内home创建一个文件
$ touch home/test
# 服务器上查看
$ ls /home/ceshi/
test
# 服务器/home/ceshi创建一个文件
$ touch /home/ceshi/test01
# 容器内查看
$ ls home/
test test01
# 停止容器
$ docker stop b4b
# 服务器/home/ceshi创建一个文件
$ touch /home/ceshi/test02
# 启动容器
$ docker start b4b
# 进入容器
$ docker attach b4b
# 查看home
$ ls home/
test test01 test02
宿主机和容器内是互相绑定的,一方修改另一方也跟着修改。容器就是停止了,也可以修改。我们以后修改只需要在本地修改即可,容器内会自动同步
问题思考?
挂载数据如何存放,是否宿主机和容器分别保存一份,还是如何去存储的?
安装MySql
# 获取镜像,已经有了就不用获取
$ docker pull mysql:5.7
# dockerhub 官方示例
$ docker run --name some-mysql -v /my/own/datadir:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
# 运行容器,需要做数据挂载,安装启动mysql需要用户名密码
# -d 后台运行
# -p 端口映射
# -v 数据卷挂载
# -e 环境配置
# --name 名字
$ docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
# 查看挂载目录
$ ls /home/mysql/
conf data
# 启动成功后,我们使用`navicat`来测试一下,连接我们的ip + 3310(和容器内3306映射)。
删掉容器数据是否还在?
# 删除容器
$ docker rm -f mysql01
# 查看宿主机
$ ls /home/mysql/
conf data
将容器删除,挂载到本地的数据卷依旧没有丢失,就实现了容器的持久化功能
具名挂载和匿名挂载
# -v 容器内路径
# 启动nginx01,匿名挂载
$ docker run -d -P --name nginx01 -v /etc/nginx nginx
# 查看挂载所有卷的情况
$ docker volume ls
DRIVER VOLUME NAME
local e10b975bc9dbf6cf76c351832330e39b79f73a758dbd64486d008f0beef9f72e
# 这种就是匿名挂载,我们在-v 的时候 只写了容器内的路径,没有挂载的路径
# 启动nginx02,具名挂载
$ docker run -d -P --name nginx02 -v test-nginx:/etc/nginx nginx
# 查看挂载所有卷的情况
$ docker volume ls
DRIVER VOLUME NAME
local e10b975bc9dbf6cf76c351832330e39b79f73a758dbd64486d008f0beef9f72e
local test-nginx
# 通过 -v 卷名:容器内路径
# 查看卷位置
$ docker volume inspect test-nginx
[
{
"CreatedAt": "2021-09-12T21:53:47+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/test-nginx/_data",
"Name": "test-nginx",
"Options": null,
"Scope": "local"
}
]
所有的docker容器内的卷,没有指定目录的情况下是在/var/lib/docker/volumes内
我们可以通过具名挂载可以方便的找到我们的卷,大多数情况使用具名挂载
如何确定是具名还是匿名挂载,还是指定路径挂载?
- 匿名挂载:
-v 容器内路径 - 具名挂载:
-v 卷名:容器内路径 - 指定挂载:
-v 宿主机路径:容器内路径,宿主机路径/开始
拓展
有时候我们会发现一些镜像挂载命令-v后加or或rw什么?
这个是对容器挂载路径设置权限,一旦设置了这个权限,容器对挂载出来的内容就有限定了
ro(readonly)—— 只读
只能通过宿主机来操作,容器内部是无法操作的
rw(readwrite)—— 可读可写
默认是rw,宿主机和容器内都可以修改
Dockerfile挂载
什么是Dockerfile
Dockerfile 就是用来构建 docker 镜像的构建文件,命令脚本。通过这个脚本可以生成镜像,镜像是一层一层的,每个命令都是一层。
编写一个Dockerfile
# 创建一个dockerfile 文件,文件名字可以随机,建议Dockerfile
$ vim Dockerfile
# Dockerfile 内容 指令必须大写 这里面的每一条命令就是一层
FROM ubuntu
VOLUME ["volume01", "volume02"]
CMD echo "----end----"
CMD /bin/bash
# 保存退出
# 构建镜像
$ docker build -f Dockerfile -t zbc/ubuntu:1.0 .
Step 1/4 : FROM ubuntu
---> 1318b700e415
Step 2/4 : VOLUME ["volume01", "volume02"]
---> Running in 309b8ddda1cc
Removing intermediate container 309b8ddda1cc
---> 8e5e5a84be12
Step 3/4 : CMD echo "----end----"
---> Running in c6146e3c7e94
Removing intermediate container c6146e3c7e94
---> 93f472392dc6
Step 4/4 : CMD /bin/bash
---> Running in f475af85ea8b
Removing intermediate container f475af85ea8b
---> 7a307a77e7bf
Successfully built 7a307a77e7bf
Successfully tagged zbc/ubuntu:1.0
# 查看构建的镜像
$ docker images | grep ubuntu
zbc/ubuntu 1.0 7a307a77e7bf 50 seconds ago 72.8MB
# 运行
$ docker run -it zbc/ubuntu:1.0 /bin/bash
$ ls
bin dev home lib32 libx32 mnt proc run srv tmp var volume02
boot etc lib lib64 media opt root sbin sys usr volume01
# volume01,volume02 是我们生成镜像时候自动挂载的,因为我们什么都没有写是一个匿名挂载
$ docker inspect cd16d3407407
"Mounts": [
{
"Type": "volume",
"Name": "3b802fad624b4987cbcb3e10e3823ce10c7fbda24aad2241dbff414cda75af90",
"Source": "/var/lib/docker/volumes/3b802fad624b4987cbcb3e10e3823ce10c7fbda24aad2241dbff414cda75af90/_data",
"Destination": "volume02",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
},
{
"Type": "volume",
"Name": "3c3c710a3e08d2885f2c47bade0d11d4b6142e728f929d0556e5a4d5d397ddfc",
"Source": "/var/lib/docker/volumes/3c3c710a3e08d2885f2c47bade0d11d4b6142e728f929d0556e5a4d5d397ddfc/_data",
"Destination": "volume01",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
上述挂载镜像的方式很常见,因为我们通常会构建自己的镜像。假设狗拿镜像的时候没有挂载卷,要自动镜像挂载 -v 卷名: 容器内路径
数据卷容器
# 启动三个容器,使用我们刚才制作的docker镜像,注意Ubuntu没有前台应用,需要启动后ctrl+p+q退出
# 启动父容器
$ docker run -it --name ubuntu01 zbc/ubuntu:1.0 /bin/bash
# 查看容器内容,是否有我们挂载的volume01和volume02
$ ls
bin dev home lib32 libx32 mnt proc run srv tmp var volume02
boot etc lib lib64 media opt root sbin sys usr volume01
# 启动第二个容器,将数据挂载同步到父容器中
$ docker run -it --name ubuntu02 --volumes-from ubuntu01 zbc/ubuntu:1.0 /bin/bash
# 查看容器内容,是否有我们挂载的volume01和volume02
$ ls
bin dev home lib32 libx32 mnt proc run srv tmp var volume02
boot etc lib lib64 media opt root sbin sys usr volume01
# 测试 给ubuntu01 中 volume01 创建一个文件,查看ubuntu中是否存在
$ docker attach ubuntu01
$ touch /volume01/ubuntu_01_test
$ docker attach ubuntu02
$ ls /volume01
ubuntu_01_test
# 我们发现也ubuntu01 创建的内容进入了ubuntu02里面
# --volumes-from ubuntu01 就好比子类继承父类,子类就拥有父类的一些东西,ubuntu01 就是数据卷容器
# ubuntu03 是否可以共享 ubuntu02或ubuntu01的数据?
$ docker run -it --name ubuntu03 --volumes-from ubuntu01 zbc/ubuntu:1.0 /bin/bash
$ ls /volume01
ubuntu_01_test
# --volumes-from 只要通过它我们就可以实现容器的数据共享了
# 停止ubuntu01 会怎么样?停止后是不会对挂载数据有影响的
# 删除会怎么样?数据还会存在吗?
$ docker rm -f ubuntu01
$ docker attach ubuntu03
$ ls /volume01
ubuntu_01_test
# 我们发现数据依旧是存在的
# 使用docker inspect 查看ubuntu02和ubuntu03的区别
$ dockcer inspect ubunut02
"Mounts": [
{
"Type": "volume",
"Name": "b6f19722307651401d2d79722a2b93d191beba23872370dc2a7ab46e048a3eea",
"Source": "/var/lib/docker/volumes/b6f19722307651401d2d79722a2b93d191beba23872370dc2a7ab46e048a3eea/_data",
"Destination": "volume01",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
},
{
"Type": "volume",
"Name": "be5c33495e24b4351c77b4a42ab5bf5589d1bdf16228037c74b8ecbeb16d0cf2",
"Source": "/var/lib/docker/volumes/be5c33495e24b4351c77b4a42ab5bf5589d1bdf16228037c74b8ecbeb16d0cf2/_data",
"Destination": "volume02",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
$ dockcer inspect ubunut03
"Mounts": [
{
"Type": "volume",
"Name": "b6f19722307651401d2d79722a2b93d191beba23872370dc2a7ab46e048a3eea",
"Source": "/var/lib/docker/volumes/b6f19722307651401d2d79722a2b93d191beba23872370dc2a7ab46e048a3eea/_data",
"Destination": "volume01",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
},
{
"Type": "volume",
"Name": "be5c33495e24b4351c77b4a42ab5bf5589d1bdf16228037c74b8ecbeb16d0cf2",
"Source": "/var/lib/docker/volumes/be5c33495e24b4351c77b4a42ab5bf5589d1bdf16228037c74b8ecbeb16d0cf2/_data",
"Destination": "volume02",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
# 然后我们看删除ubuntu01后两个数据共享
$ docker exec -it ubuntu02 /bin/bash
$ touch /volume02/ubuntu_02_test
$ docker exec -it ubuntu02 /bin/bash
$ ls /volume02
ubuntu_02_test
# 两个直接还是互通的
用途
多个容器间数据共享,比如多个mysql或多个redis之间
多个mysql 数据如何共享?
# 父容器
$ docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 mysql:5.7
$ docker run -d -p 3311:3306 -e MYSQL_ROOT_PASSWORD=123456 --volumes-from mysql02 --name mysql01 mysql:5.7
结论
- 容器之间可以做配置信息之间的传递,数据卷容器的生命周期,一直持续到没有容器使用为止。
- 但是一旦持久化到了本地,这个时候本地的数据是不会删除的