EP6 容器的数据卷

81 阅读8分钟

EP6 容器的数据卷

什么是数据卷?

Docker将运行环境所需的所有东西都打包到容器里,容器运行时产生的所有数据/文件,在容器销毁后都会和容器一起销毁。为了能持久化数据以及共享容器间的数据,Docker提出了Volume的概念。 卷(Volume)就是目录或文件,存在于一个或多个容器中,由Docker挂载到容器,但卷不属于联合文件系统(Union FileSystem),因此能够绕过UFS提供一些用于持续存储或共享数据的特性。

卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此Docker不会在容器删除时删除其挂载的数据卷

数据卷的特点:

  1. 数据卷可在容器之间共享或重用数据
  2. 卷中的更改可以直接生效
  3. 数据卷中的更改不会包含在镜像的更新中
  4. 数据卷的生命周期一直持续到没有容器使用它为止

数据挂载方式

目前Docker提供了三种不同的方式将数据从宿主机挂载到容器中:

  1. volumesDocker管理宿主机文件系统的一部分,默认位于 /var/lib/docker/volumes 目录中;(最常用的方式
  2. bind mounts:意为着可以存储在宿主机系统的任意位置;(比较常用的方式
  3. tmpfs:挂载存储在宿主机系统的内存中,而不会写入宿主机的文件系统;(一般都不会用的方式

image.png bind mount在不同的宿主机系统时不可移植的,比如WindowsLinux的目录结构是不一样的,bind mount所指向的host目录也不能一样。所以bind mount不能出现在Dockerfile中,因为这样Dockerfile就不可移植了。

使用bind mounts挂载数据

bind mounts挂载命令: docker run -it -v 宿主机目录:容器目录

  1. 在宿主机创建一个文件夹,文件夹中添加一个文件

    % pwd
    /Users/chenrui/work/temp/dockerStudy/volume
    % touch testVolume.txt
    % ls
    testVolume.txt
    
  2. 运行一个nginx容器,挂载volume目录到nginx/usr/share/nginx/html目录

    % docker run -it -d --name=volume_nginx -p 8081:80  -v /Users/chenrui/work/temp/dockerStudy/volume:/usr/share/nginx/html nginx:1.22.0
    6acefa1d972c8e4b2c8827cb7be48aef8bfc127bde251006e4d886bce05e2aaa
    
  3. 进入容器查看/usr/share/nginx/html目录,发现该目录没有任何文件(默认情况下该目录应该有nginx默认的index页面)

    % docker exec -it volume_nginx bash
    root@6acefa1d972c:/# cd /usr/share/nginx/html/
    root@6acefa1d972c:/usr/share/nginx/html# ls
    root@6acefa1d972c:/usr/share/nginx/html#
    
  4. 此时,在宿主机的work/temp/dockerStudy/volume目录下添加文件,再查看容器内发现在宿主机添加的文件同步到了容器内;同时在容器内操作文件,宿主机的文件也会同步修改

    # 宿主机
    dockerStudy % touch testVolume.txt
    dockerStudy % ls
    testVolume.txt
    # 容器内
    root@6acefa1d972c:/usr/share/nginx/html# ls
    testVolume.txt
    
    # 容器:修改文件
    root@6acefa1d972c:/usr/share/nginx/html# echo "aaaa" > testVolume.txt
    # 宿主机
    volume % cat testVolume.txt
    aaaa
    

    如果希望挂载的文件在容器内只读可以通过在挂在目录后面添加:ro来设置目录只读(默认是rw)。eg. docker run -v 宿主机目录:容器目录:or

  5. 通过inspect查看卷绑定状态

% docker inspect volume_nginx
....
"Mounts": [
    {
        "Type": "bind",
        "Source": "/Users/chenrui/work/temp/dockerStudy/volume",
        "Destination": "/usr/share/nginx/html",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    }
],
....

使用bind mounts方式挂载目录时,无论宿主机目录是否为空,Docker都会用宿主机目录覆盖容器挂载目录;这样会造成一些问题,比如:我们需要挂载的目录本身是有数据的,这些数据是容器启动的依赖项;此时使用bind mounts方式挂载就会导致启动容器时出现错误。遇到这种情况就需要使用容器卷来挂载数据了。

volumes常用命令

  1. 创建容器卷

    # docker volume create demo_vol
    demo_vol
    
  2. 查看容器卷

image.png

  1. 查看数据卷详情

    [root@localhost ~]# docker volume inspect demo_vol
    [
        {
            "CreatedAt": "2022-08-27T15:33:32+08:00",
            "Driver": "local",
            "Labels": {},
            "Mountpoint": "/var/lib/docker/volumes/demo_vol/_data",
            "Name": "demo_vol",
            "Options": {},
            "Scope": "local"
        }
    ]
    

    "Mountpoint": "/var/lib/docker/volumes/demo_vol/_data"可以看出数据卷在宿主机的位置

注意:在MacOs中,Docker是运行在单独的虚拟机上的,所以docker创建的Volume并不在mac的主机上而是在Docker虚拟机中.如果要在MacOs中查看容器卷可以使用nsenter1镜像

    % docker run -it --privileged --pid=host --rm justincormack/nsenter1
    / # cd /var/lib/docker/volumes/demo_vol/_data/
    /var/lib/docker/volumes/demo_vol/_data # ls
    /var/lib/docker/volumes/demo_vol/_data #
    

4. 删除容器卷

    % docker volume ls
    DRIVER    VOLUME NAME
    local     demo_vol
    chenrui@chenruideMacBook-Pro ~ % docker volume rm demo_vol
    demo_vol
    chenrui@chenruideMacBook-Pro ~ % docker volume ls
    DRIVER    VOLUME NAME

5. 清理容器卷

    % docker volume prune
    WARNING! This will remove all local volumes not used by at least one container.
    Are you sure you want to continue? [y/N] y
    Deleted Volumes:
    23a8ad685b0218b19523576f80e07484b8aef1a6afcb0eb2b9c999bbd592c353
    6724bf4ba886185f2cb364396f335507faf5c67cf61d71e117c62645c21e107d
    toolkit_mongodb
    2964084adbf7186f7e1bd15ed92fe81270f2cda20ac7597d47209d322460671e
    cc4e3783e9d2a1cdcdfc8ba077d45d9637158595f796d261dd6903865072d932
    97b327e9b51756e90f5322801dd35227d85d20b67549f022cbb723e2c75788fe
    f7ef3dde0a15cfb4b82dd8f8fcb1b47b2c9f38bd1dfac88e62505ee3ddd3ace2
    316da39969e32004756be4b13a6b033eecb812de58ee901d857ced2df6335496

    Total reclaimed space: 315MB
    chenrui@chenruideMacBook-Pro ~ % docker volume ls
    DRIVER    VOLUME NAME

使用volumes挂载数据

docker run提供两种方式绑定容器卷

-v/--volume,由(:)分隔的三个字段组成,卷名:容器路径:选项列表。选项列表可以ro/rw。

--mount,由多个键值对组成,由,分隔,每个由一个<key=<value>>元组组成。
type,值可以为 bind,volume,tmpfs。
source,对于命名卷,是卷名。对于匿名卷,这个字段被省略。可能被指定为 source 或 src。
destination,文件或目录将被挂载到容器中的路径。可以指定为 destination,dst 或 target。

启动容器并绑定卷

% docker run -it -d --name=volume_nginx -p 8081:80  -v ng_vol:/usr/share/nginx/html nginx:1.22.0
c756f40d4dd95b8bd4f99922c3e54e0ad8613ccc6253e9a4c521f1f3e7934938

通过inspect可以看到已经绑定了容器卷 转存失败,建议直接上传图片文件 查看卷可以看到ng_vol已经创建了

% docker volume ls
DRIVER    VOLUME NAME
local     ng_vol
% docker volume inspect ng_vol
[
    {
        "CreatedAt": "2022-08-27T08:40:06Z",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/ng_vol/_data",
        "Name": "ng_vol",
        "Options": null,
        "Scope": "local"
    }
]

查看宿主机中卷目录,发现这个卷中已经有了两个html文件,这是因为当我们的容器卷是空的,而挂载的容器目录非空时,docker会将容器挂载目录下的数据初始化到容器卷中

% docker run -it --privileged --pid=host --rm justincormack/nsenter1
/ # cd /var/lib/docker/volumes/ng_vol/_data
/var/lib/docker/volumes/ng_vol/_data # ls
50x.html    index.html

进入容器修改内容

% docker exec -it volume_nginx bash
root@c756f40d4dd9:/# cd /usr/share/nginx/html/
root@c756f40d4dd9:/usr/share/nginx/html# echo "cy4ever" > ./helloDocker.txt
root@c756f40d4dd9:/usr/share/nginx/html# cat helloDocker.txt
cy4ever

在宿主机查看卷目录

% docker run -it --privileged --pid=host --rm justincormack/nsenter1
/ # cd /var/lib/docker/volumes/ng_vol/_data
/var/lib/docker/volumes/ng_vol/_data # ls
50x.html    index.html
/var/lib/docker/volumes/ng_vol/_data # ls
50x.html         helloDocker.txt  index.html
/var/lib/docker/volumes/ng_vol/_data # cat helloDocker.txt
cy4ever

具名挂载和匿名挂载

具名挂载

具名挂载,就是指定文件夹名称,区别于指定路径挂载,这里的指定文件夹名称是在Docker指定的默认数据卷路径下的。 上文中「使用volumes挂在数据」就是使用的具名挂载,eg. -v 卷名称:容器挂载目录

匿名挂载

匿名挂载就是在指定数据卷的时候,不指定容器路径对应的主机路径,这样对应映射的主机路径就是默认的路径/var/lib/docker/volumes/中自动生成一个随机命名的文件夹。 匿名挂载不需要指定卷名称,docker会自动生成一个随机命名的卷名称,eg. -v 容器挂载目录

image.png