Docker可以支持把一个宿主机上的目录挂载到镜像里。

docker run -it -v /home/dock/Downloads:/usr/Downloads ubuntu64 /bin/bash

通过-v参数,冒号前为宿主机目录,必须为绝对路径,冒号后为镜像内挂载的路径。  


现在镜像内就可以共享宿主机里的文件了。

默认挂载的路径权限为读写。如果指定为只读可以用:ro

docker run -it -v /home/dock/Downloads:/usr/Downloads :ro  ubuntu64 /bin/bash

docker还提供了一种高级的用法。叫数据卷。

数据卷:“其实就是一个正常的容器,专门用来提供数据卷供其它容器挂载的”。感觉像是由一个容器定义的一个数据挂载信息。其他的容器启动可以直接挂载数据卷容器中定义的挂载信息。
看示例:

docker run -v /home/dock/Downloads:/usr/Downloads  --name dataVol ubuntu64 /bin/bash

创建一个普通的容器。用--name给他指定了一个名(不指定的话会生成一个随机的名子)。

再创建一个新的容器,来使用这个数据卷。

docker run -it  --volumes-from  dataVol ubuntu64 /bin/bash


--volumes-from用来指定要从哪个数据卷来挂载数据。 

如何在Docker容器内外互相拷贝数据?
从容器内拷贝文件到主机上

docker cp <containerId>:/file/path/within/container /host/path/target  

从主机上拷贝文件到容器内

1.用-v挂载主机数据卷到容器内

docker run -v /path/to/hostdir:/mnt $container 

在容器内拷贝 
 
cp /mnt/sourcefile /path/to/destfile 

2.直接在主机上拷贝到容器物理存储系统 

A. 获取容器名称或者id :

$ docker ps 

B. 获取整个容器的id

$ docker inspect -f  '{{.Id}}' 步骤A获取的名称或者id 

C. 在主机上拷贝文件:

$ sudo cp path-file-host /var/lib/docker/aufs/mnt/FULL_CONTAINER_ID/PATH-NEW-FILE   

或者  

$ sudo cp path-file-host /var/lib/docker/devicemapper/mnt/123abc<<id>>/rootfs/root  

例子:
$ docker ps 
 
CONTAINER ID   IMAGE  COMMAND    CREATED   STATUS    PORTS    NAMES 
 
d8e703d7e303  solidleon/ssh:latest   /usr/sbin/sshd -D           cranky_pare 
 
docker inspect -f  '{{.Id}}'  cranky_pare 
or   
$ docker inspect -f  '{{.Id}}' d8e703d7e303 
 
d8e703d7e3039a6df6d01bd7fb58d1882e592a85059eb16c4b83cf91847f88e5 
 
$ sudo cp file.txt /var/lib/docker/aufs/mnt/**d8e703d7e3039a6df6d01bd7fb58d1882e592a85059eb16c4b83cf91847f88e5 

3.用输入输出符

docker run -i ubuntu /bin/bash -c 'cat > /path/to/container/file' < /path/to/host/file/  

或者

docker exec -it <container_id> bash -c 'cat > /path/to/container/file' < /path/to/host/file/  


Docker容器启动的时候,如果要挂载宿主机的一个目录,可以用-v参数指定。


譬如我要启动一个CentOS容器,宿主机的/test目录挂载到容器的/soft目录,可通过以下方式指定:


docker run -it -v /test:/soft centos /bin/bash


这样在容器启动后, 容器内会自动创建/soft的目录。通过这种方式,我们可以明确一点,即-v参数中,冒号":"前面的目录是宿主机目录,后面的目录是容器内目录


貌似简单,其实不然,下面我们来验证一下:


一、 容器目录不可以为相对路径


[root@localhost ~]# docker run -it -v /test:soft centos /bin/bash

invalid value "/test:soft" for flag -v: soft is not an absolute path

See 'docker run --help'.


直接报错,提示soft不是一个绝对路径,所谓的绝对路径,必须以下斜线“/”开头。


二、 宿主机目录如果不存在,则会自动生成

如果宿主机中存在/test目录,首先删除它

[root@localhost ~]# rm -rf /test

[root@localhost ~]# ls /

bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var


启动容器


[root@localhost ~]#  docker run -it -v /test:/soft centos /bin/bash

[root@a487a3ca7997 /]# ls

bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  soft  srv  sys  tmp  usr  var


查看宿主机,发现新增了一个/test目录


[root@localhost ~]# ls /
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  test  tmp  usr  var


三、宿主机的目录如果为相对路径呢?


这次,我们换个目录名test1试试


# docker run -it -v test1:/soft centos /bin/bash


再到宿主机上查看是否新增了一个/test1目录,结果没有,是不是因为我用的是相对路径,所以生成的test1目录在当前目录下,结果发现还是没有。那容器内的/soft目录挂载到哪里去了?

通过docker inspect命令,查看容器“Mounts”那一部分,我们可以得到这个问题的答案。


    "Mounts": [
        {
            "Name": "test1",
            "Source": "/var/lib/docker/volumes/test1/_data",
            "Destination": "/soft",
            "Driver": "local",
            "Mode": "z",
            "RW": true
        }
    ],

可以看出,容器内的/soft目录挂载的是宿主机上的 /var/lib/docker/volumes/test1/_data目录

原来, 所谓的相对路径指的是/var/lib/docker/volumes/,与宿主机的当前目录无关。

四、如果只是-v指定一个目录,这个又是如何对应呢?

启动一个容器

[root@localhost ~]# docker run -it -v /test2 centos /bin/bash

[root@ea24067bc902 /]# ls

bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  test2  tmp  usr  var

同样 使用docker inspect命令查看宿主机的挂载目录


  "Mounts": [
        {
            "Name": "96256232eb74edb139d652746f0fe426e57fbacdf73376963e3acdb411b3d73a",
            "Source": "/var/lib/docker/volumes/96256232eb74edb139d652746f0fe426e57fbacdf73376963e3acdb411b3d73a/_data",
            "Destination": "/test2",
            "Driver": "local",
            "Mode": "",
            "RW": true
        }
    ],

可以看出,同3中的结果类似,只不过,它不是相对路径的目录名,而是 随机生成的一个目录名

五、如果在容器内修改了目录的属主和属组,那么对应的挂载点是否会修改呢?

首先开启一个容器,查看容器内/soft目录的属性

[root@localhost ~]# docker run -it -v /test:/soft centos /bin/bash

[root@b5ed8216401f /]# ll -d /soft/

drwxr-xr-x 2 root root 6 Sep 24 03:48 /soft/

查看宿主机内/test目录的属性

[root@localhost ~]# ll -d /test/
drwxr-xr-x 2 root root 6 Sep 24 11:48 /test/

在容器内新建用户,修改/soft的属主和属组

[root@b5ed8216401f /]# useradd victor

[root@b5ed8216401f /]# chown -R victor.victor /soft/

[root@b5ed8216401f /]# ll -d /soft/

drwxr-xr-x 2 victor victor 6 Sep 24 03:48 /soft/

再来看看宿主机内/test目录的属主和属组是否会发生变化?

[root@localhost ~]# ll -d /test/

drwxr-xr-x 2 mycat mycat 6 Sep 24 11:48 /test/

竟然变为mycat了。。。

原来,这个与UID有关系,UID,即“用户标识号”,是一个整数,系统内部用它来标识用户。一般情况下它与用户名是一一对应的。

首先查看容器内victor对应的UID是多少,

[root@b5ed8216401f /]#  cat /etc/passwd | grep victor

victor:x:1000:1000::/home/victor:/bin/bash

victor的UID为1000,那么宿主机内1000对应的用户是谁呢?

[root@localhost ~]#  cat /etc/passwd |grep 1000

mycat:x:1000:1000::/home/mycat:/bin/bash

可以看出,宿主机内UID 1000对应的用户是mycat。


六、容器销毁了,在宿主机上新建的挂载目录是否会消失?


在这里,主要验证两种情况:

​一、指定了宿主机目录,即 -v /test:/soft。

​二、没有指定宿主机目录,即-v /soft


第一种情况:


[root@localhost ~]# rm -rf /test    --首先删除宿主机的/test目录

[root@localhost ~]# ls /    --可以看到,宿主机上无/test目录

bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

[root@localhost ~]#  docker run -it --name=centos_test -v /test:/soft centos /bin/bash  --启动容器,为了删除方便,我用--name参数指定了容器的名字

[root@82ad7f3a779a /]# exit
exit
[root@localhost ~]# docker rm centos_test  --删除容器
centos_test
[root@localhost ~]# ls /  --发现 /test目录依旧存在

bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  test  tmp  usr  var


可以看出, 即便容器销毁了,新建的挂载目录不会消失。进一步也可验证,如果宿主机目录的属主和属组发生了变化,容器销毁后,宿主机目录的属主和属组不会恢复到挂载之前的状态。


第二种情况,通过上面的验证知道, 如果没有指定宿主机的目录,则容器会在/var/lib/docker/volumes/随机配置一个目录,那么我们看看这种情况下的容器销毁是否会导致相应目录的删除

首先启动容器


[root@localhost ~]#  docker run -it --name=centos_test -v /soft centos /bin/bash

[root@6b75579ec934 /]# exit
exit

通过docker inspect命令查看容器在宿主机上生成的挂载目录


    "Mounts": [
        {
            "Name": "b53164cb1c9f1917788638692fb22ad11994cf1fbbc2461b6c390cd3e10ea301",
            "Source": "/var/lib/docker/volumes/b53164cb1c9f1917788638692fb22ad11994cf1fbbc2461b6c390cd3e10ea301/_data",
            "Destination": "/soft",
            "Driver": "local",
            "Mode": "",
            "RW": true
        }
    ],

 
对应的是/var/lib/docker/volumes/b53164cb1c9f1917788638692fb22ad11994cf1fbbc2461b6c390cd3e10ea301/_data目录

销毁容器,看目录是否存在


[root@localhost ~]# docker rm centos_test
centos_test
[root@localhost ~]# ll /var/lib/docker/volumes/b53164cb1c9f1917788638692fb22ad11994cf1fbbc2461b6c390cd3e10ea301
total 0
drwxr-xr-x 2 root root 6 Sep 24 14:25 _data


发现该目录依旧存在,即便重启了docker服务,该目录依旧存在


[root@localhost ~]# systemctl restart docker

[root@localhost ~]# ll /var/lib/docker/volumes/b53164cb1c9f1917788638692fb22ad11994cf1fbbc2461b6c390cd3e10ea301
total 0
drwxr-xr-x 2 root root 6 Sep 24 14:25 _data

七、挂载宿主机已存在目录后,在容器内对其进行操作,报“Permission denied”。

可通过两种方式解决:

1>  关闭selinux。

临时关闭:# setenforce 0


永久关闭: 修改/etc/sysconfig/selinux文件,将SELINUX的值设置为disabled。

2> 以特权方式启动容器

指定--privileged参数


如:#  docker run -it --privileged -v /test:/soft centos /bin/bash

​​
​一、通过docker run命令


1、运行命令:docker run --name test -it -v /home/xqh/myimage:/data ubuntu  /bin/bash


其中的  -v 标记 在容器中设置了一个挂载点 /data(就是容器中的一个目录),并将主机上的 /home/xqh/myimage 目录中的内容关联到 /data下。


这样在容器中对/data目录下的操作,还是在主机上对/home/xqh/myimage的操作,都是完全实时同步的,因为这两个目录实际都是指向主机目录。


2、运行命令:docker run --name test1 -it -v /data ubuntu /bin/bash


上面-v的标记只设置了容器的挂载点,并没有指定关联的主机目录。 这时docker会自动绑定主机上的一个目录。通过docker inspect 命令可以查看到


xqh@ubuntu:~/myimage$  docker inspect test1
[
{
    "Id": "1fd6c2c4bc545163d8c5c5b02d60052ea41900a781a82c20a8f02059cb82c30c",
.............................
    "Mounts": [
        {
            "Name": "0ab0aaf0d6ef391cb68b72bd8c43216a8f8ae9205f0ae941ef16ebe32dc9fc01",
            "Source": "/var/lib/docker/volumes/0ab0aaf0d6ef391cb68b72bd8c43216a8f8ae9205f0ae941ef16ebe32dc9fc01/_data",
            "Destination": "/data",
            "Driver": "local",
            "Mode": "",
            "RW": true
        }
    ],
...........................

上面 Mounts下的每条信息记录了容器上一个挂载点的信息,"Destination" 值是容器的挂载点,"Source"值是对应的主机目录。

可以看出这种方式对应的 主机目录是自动创建的,其目的不是让在主机上修改,而是让多个容器共享。

 二、通过dockerfile创建挂载点

上面介绍的通过docker run命令的-v标识创建的挂载点只能对创建的容器有效。

通过dockerfile的 VOLUME 指令可以在镜像中创建挂载点,这样只要通过该镜像创建的容器都有了挂载点。

还有一个区别是, 通过 VOLUME 指令创建的挂载点,无法指定主机上对应的目录,是自动生成的

#test
FROM ubuntu
MAINTAINER hello1
VOLUME ["/data1","/data2"]

上面的dockfile文件通过VOLUME指令指定了两个挂载点 /data1 和 /data2.

我们通过docker inspect 查看通过该dockerfile创建的镜像生成的容器,可以看到如下信息

    "Mounts": [
        {
            "Name": "d411f6b8f17f4418629d4e5a1ab69679dee369b39e13bb68bed77aa4a0d12d21",
            "Source": "/var/lib/docker/volumes/d411f6b8f17f4418629d4e5a1ab69679dee369b39e13bb68bed77aa4a0d12d21/_data",
            "Destination": "/data1",
            "Driver": "local",
            "Mode": "",
            "RW": true
        },
        {
            "Name": "6d3badcf47c4ac5955deda6f6ae56f4aaf1037a871275f46220c14ebd762fc36",
            "Source": "/var/lib/docker/volumes/6d3badcf47c4ac5955deda6f6ae56f4aaf1037a871275f46220c14ebd762fc36/_data",
            "Destination": "/data2",
            "Driver": "local",
            "Mode": "",
            "RW": true
        }
    ],

可以看到两个挂载点的信息。

三、容器共享卷(挂载点)

docker run --name test1 -it myimage /bin/bash

上面命令中的 myimage是用前面的dockerfile文件构建的镜像。 这样容器test1就有了 /data1 和 /data2两个挂载点。


下面我们创建另一个容器可以和test1共享 /data1 和 /data2卷 ,这是在 docker run中使用  --volumes-from标记,如:

可以是来源不同镜像,如:

docker run --name test2 -it  --volumes-from test1  ubuntu  /bin/bash

也可以是同一镜像,如:

docker run --name test3 -it  --volumes-from test1  myimage  /bin/bash

上面的三个容器 test1 , test2 , test3 均有 /data1 和 /data2 两个目录,且目录中内容是共享的,任何一个容器修改了内容,别的容器都能获取到。

四、最佳实践:数据容器

如果多个容器需要共享数据(如持久化数据库、配置文件或者数据文件等),可以考虑创建一个特定的数据容器,该容器有1个或多个卷。

其它容器通过--volumes-from 来共享这个数据容器的卷。

因为容器的卷本质上对应主机上的目录,所以这个数据容器也不需要启动。


如: docker run --name dbdata myimage echo "data container"
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐