1,了解Docker文件系统工作机制

    Docker镜像是由多个文件系统(只读层)叠加而成。当我们启动一个容器的时候,Docker会加载只读镜像层并在其上(译者注:镜像栈顶部)添加一个读写层。如果运行中的容器修改了现有的一个已经存在的文件,那该文件将会从读写层下面的只读层复制到读写层,该文件的只读版本仍然存在,只是已经被读写层中该文件的副本所隐藏。当删除Docker容器,并通过该镜像重新启动时,之前的更改将会丢失。在Docker中,只读层及在顶部的读写层的组合被称为Union File System(联合文件系统)。

    2,Volume

    为了能够保存(持久化)数据以及共享容器间的数据,Docker提出了Volume的概念。简单来说,Volume就是目录或者文件,它可以绕过默认的联合文件系统,而以正常的文件或者目录的形式存在于宿主机上。

    Volume可以将容器以及容器产生的数据分离开来,这样,当你使用docker rm my_container删除容器时,不会影响相关的数据。

    1,Volume优点
    volume在容器创建时就会初始化,在容器运行时就可以使用其中的文件。
    volume能在不同的容器之间共享和重用。
    对volume中数据的操作会马上生效。
    对volume中数据的操作不会影响到镜像本身。
    volume的生存周期独立于容器的生存周期,即使删除容器,volume仍然会存在,没有任何容器使用的volume也不会被Docker删除。

    2,从容器挂载volume
    在使用docker run或docker create创建新容器时,可以使用-v标签为容器添加volume:
    docker run -it —name vol_simple -v /data ubuntu /bin/bash

    这条指令在创建容器时会将容器中的/data目录作为一个volume挂载点。Docker会在宿主机
    /var/lib/docker/volumes/中创建一个以volume ID为名的目录,并将这个目录绑定挂载到容器内文件系统的volume挂载点上,这样Docker容器中的/data目录和宿主机的volume ID目录就是相同的了。

    如果镜像中不存在/data文件夹,容器启动后将创建一个名为/data的空文件夹;反之,如果镜像中存在/data文件夹,这个文件夹中的内容将全部被复制到宿主机中对应的文件夹中,并且根据容器中的文件为宿主机的文件设置合适的权限和所有者。

    查看指令:
    docker inspect vol_simple
    Docker数据卷 - 图1
    在Mounts的Source中可以查看文件夹的位置。

    3,从宿主机挂着volume
    在创建新容器的时候可以挂载一个主机上特定的目录到容器中:
    docker run -it —name vol_from_host -v /host/dir:/container/dir ubuntu /bin/bash

    使用上条命令将宿主机中的/host/dir文件夹作为一个volume挂载到容器中的/container/dir。/container文件夹必须使用绝对路径,/host/dir可以是绝对路径也可以是一个变量值。如果是变量值必须是以字母数据开头,然后是下划线、时间或连接符,例如a-z0-9。如果宿主机中不存在/host/dir,将创建一个空文件夹。在/host/dir文件夹中的所有文件或文件夹可以在容器的/container/dir文件夹下访问。如果进行中原本存在/container/dir文件夹,该文件夹里面的内容会被覆盖,但是不会删除已存在的内容。一旦挂载被删除,原来的文件是可以再次访问的。

    将单个文件作为volume挂载到容器:
    docker run -it —name vol_file -v /host/file:/container/file ubuntu /bin/bash

    使用上条命令将主机中的/host/file文件作为一个volume挂载到容器中的/container/file。文件必须使用绝对路径,如果主机中不存在/host/file,会提示错误。挂载后文件内容与宿主机中的文件一致,也就是说如果容器中原本存在/container/file,文件的原有内容将被覆盖。

    将主机上的文件或文件夹作为volume挂载时,可以使用:ro指定该volume为只读:
    docker run -it —name vol_from_host -v /host/dir:/container/dir:ro ubuntu /bin/bash

    在使用docker run或docker create创建新容器时,可以使用多个-v标签为容器添加多个volume。

    4,使用Dockerfile添加volume

    使用VOLUME指令向容器添加volume:
    VOLME /data
    在使用docker build命令生成镜像并且以该镜像启动容器时会挂载一个volume到/data。
    可以使用VOLUME指令添加多个volume:
    VOLUME [“/data1”,”/data2”]

    与使用docker run -v不同的是,VOLUME指令不能挂载主机中指定的文件夹。这是为了保证Dockerfile的可移植性,因为不能保证所有的宿主机都有对应的文件夹。

    5,共享volume
    在使用docker run或docker create创建新容器时,可以使用—volumes-from标签是的容器与已有的容器共享volume。
    docker run -it —name vol_use —volumes-from vol_simple ubuntu /bin/bash
    新创建的容器vol_use与之前创建的容器vol_simple共享volume。如果被共享的容器有多个volume,新容器也将有多个volume。如果新容器中存在数据卷文件夹,则会覆盖内容。

    与多个容器共享volume
    docker run -it —name vol_use —volumes-from vol_simple —volumes-from vol_simple1 ubuntu /bin/bash
    一个容器挂载了一个volume,即使这个容器停止运行,该volume仍然存在,其他容器也可以使用—volumes-from与这个容器共享volume。

    6,创建数据容器
    如果您有想要容器之间分享,或希望从非持久性容器使用一些持久的数据,最好创建一个名为数据卷容器,然后从它安装的数据。
    创建一个新的容器,不运行一个应用程序:
    docker create -v /dbdata —name dbstore training/postgres /bin/true
    使用多个容器共享这个容器volume
    docker run -d —volumes-from dbstore —name db1 training/postgres
    docker run -d —volumes-from dbstore —name db2 training/postgres

    在这种情况下,如果postgres镜像包含一个目录/dbdata,然后会在dbstore这个镜像中隐藏镜像postgres中的/dbdata。但是这些文件仅仅对dbstore是可见的。

    7,删除volume
    使用 docker rm删除容器并不会删除与volume对应的目录,这些目录会占据不必要的存储空间,即使手动删除,因为这些目录名称是无意义的随机字符串,所以操作十分麻烦。在删除容器时一并删除volume有两种方法:
    使用docker rm -v删除容器。
    在运行容器时使用docker run —rm,—rm标签会在容器停止运行时删除容器以及容器锁挂载的volume。

    注意:以上方式只能在对应volume是被最后一个容器使用时才会将其删除,如果容器的volume被多个容器共享,在删除最后一个共享他的容器时将其删除。
    如果volume是在创建时从宿主机中挂载的,无论对容器进行任何操作都不会导致其在宿主机中被删除,如果不需要这些文件,只能手动删除它们。

    8,备份、恢复和迁移volume
    备份volume方法:
    docker run —rm —volumes-from vol_simple -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /data

    该指令启动了一个临时的容器,这个容器挂载了两个volume,第一个volume与要备份的volume共享,第二个volume将宿主机的当前目录挂载到容器的/backup下。容器运行后将要备份的内容(/data文件夹)备份到/backup/data.tar,然后删除容器,备份后的data.tar就留在了当前目录。

    还原备份
    docker run -it —name vol_bck -v /data ubuntu /bin/bash
    docker run —rm —volumes-from vol_bck -v $(pwd):/backup ubuntu bash -c “cd /data && tar xvf /backup/data.tar —strip 1”

    首先运行了一个新容器座位数据恢复的目标。第二行指令启动了一个临时容器,这个容器挂载了两个volume,第一个volume与要恢复的volume共享,第二个volume将宿主机的当前目录挂载到容器的/backup下。由于之前备份的data.tar在当前目录下,那么它在容器中的/dataup也能访问到,容器启动后将这个存档文件中的/data恢复到根目录下,然后删除容器。