Docker - 容器数据卷

982 阅读6分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

什么是容器数据卷

docker 如果数据在容器中,那我们容器删除,数据就会丢失!需求:需求可以持久化。就好比我们使用MySQL,容器删除后,数据丢失了!数据之间可以有一个数据共享的技术,Docker 容器中产生的数据,同步到本地。这个就是卷技术,目录的挂载,将我们容器内容的目录,挂载到Linux上面。

image.png

总结:容器的持久化和同步操作,容器间也可以数据共享

使用数据卷

命令挂载(-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映射)。

image.png

删掉容器数据是否还在?

# 删除容器
$ 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后加orrw什么? 这个是对容器挂载路径设置权限,一旦设置了这个权限,容器对挂载出来的内容就有限定了

roreadonly)—— 只读

只能通过宿主机来操作,容器内部是无法操作的

rwreadwrite)—— 可读可写

默认是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
# 两个直接还是互通的

b2ed6513345d5a72dd83a7b240bbd2a.png

用途

多个容器间数据共享,比如多个mysql或多个redis之间

多个mysql 数据如何共享?

image.png

# 父容器
$ 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

结论

  • 容器之间可以做配置信息之间的传递,数据卷容器的生命周期,一直持续到没有容器使用为止。
  • 但是一旦持久化到了本地,这个时候本地的数据是不会删除的