https://blog.csdn.net/dream_broken/article/details/52314993

1.什么是数据卷volume

为了了解什么是Docker Volume,首先我们需要明确Docker内的文件系统是如何工作的。Docker镜像被存储在一系列的只读层。当我们开启一个容器,Docker读取只读镜像并添加一个读写层在顶部。如果正在运行的容器修改了现有的文件,该文件将被拷贝出底层的只读层到最顶层的读写层。在读写层中的旧版本文件隐藏于该文件之下,但并没有被不破坏 - 它仍然存在于镜像以下。当Docker的容器被删除,然后重新启动镜像时,将开启一个没有任何更改的新的容器 - 这些更改会丢失。此只读层及在顶部的读写层的组合被Docker称为Union File System(联合文件系统)。
为了能够保存(持久)数据以及共享容器间的数据,Docker提出了Volumes的概念。很简单,volumes是目录(或者文件),它们是外部默认的联合文件系统或者是存在于宿主文件系统正常的目录和文件。

2.为什么使用数据卷volume

Docker的镜像是由一系列的只读层组合而来,当启动一个容器的时候,Docker加载镜像的所有只读层,并在最上层加入一个读写层。这个设计使得Docker可以提高镜像构建、存储和分发的效率,节省了时间和存储空间,然而也存在如下问题。

(1)容器中的文件在宿主机上存在形式复杂,不能在宿主机上很方便的对容器中的文件进行访问

(2)多个容器之间的数据无法共享

(3)当删除容器时,容器产生的数据将丢失

为了解决这些问题,Docker引入了数据卷(volume)机制。volume是存在一个或多个容器中的特定文件或文件夹,这个目录能够独立于联合文件系统的形式在宿主机中存在,并为数据的共享与持久提供一下便利。

(1)volume在容器创建时就初始化,在容器运行时就可以使用其中的文件

(2)volume能在不同的容器之间共享和重用

(3)对volume中的数据的操作会马上生效

(4)对volume中数据操作不会影响到镜像本身

(5)volume的生存周期独立于容器的生存周期,即使删除容器,volume仍然会存在,没有任何容器使用的volume也不会被Docker删除

3.如何使用数据卷

3.1 从容器挂载volume(-v /path)

在使用docker run创建新容器的时候,可以使用-v 标签为容器添加数据卷volume,以下用法是从容器中的某个文件夹创建volume,如果容器中指定的文件夹不存在,会自动生成

在上面的概念中,有说道,宿主机应该会有一个文件夹绑定挂载到容器中的volume挂载点,那默认的宿主机上的文件夹在哪呢,使用docker inspect命令,查看下容器详情(CRT令起一个SSH终端)

  1. [root@localhost ~]# docker inspect volume-test01
  2. [
  3. {
  4. "Id": "81a74152e6f45a3f780ac7cdc37c9a089814f9a70aad1d27747093ca3c3dae3e",
  5. "Created": "2016-08-25T07:48:55.942949334Z",
  6. "Path": "/bin/bash",
  7. "Args": [],
  8. "State": {
  9. "Status": "running",
  10. "Running": true,
  11. "Paused": false,
  12. "Restarting": false,
  13. "OOMKilled": false,
  14. "Dead": false,
  15. "Pid": 10199,
  16. "ExitCode": 0,
  17. "Error": "",
  18. "StartedAt": "2016-08-25T07:48:56.777918888Z",
  19. "FinishedAt": "0001-01-01T00:00:00Z"
  20. },
  21. "Image": "sha256:4fd21defa24c8c07b3689b267a63d563ca0e26ef931b329fc3f3d46efb5bba2d",
  22. "ResolvConfPath": "/var/lib/docker/containers/81a74152e6f45a3f780ac7cdc37c9a089814f9a70aad1d27747093ca3c3dae3e/resolv.conf",
  23. "HostnamePath": "/var/lib/docker/containers/81a74152e6f45a3f780ac7cdc37c9a089814f9a70aad1d27747093ca3c3dae3e/hostname",
  24. "HostsPath": "/var/lib/docker/containers/81a74152e6f45a3f780ac7cdc37c9a089814f9a70aad1d27747093ca3c3dae3e/hosts",
  25. "LogPath": "",
  26. "Name": "/volume-test01",
  27. "RestartCount": 0,
  28. "Driver": "devicemapper",
  29. "MountLabel": "system_u:object_r:svirt_sandbox_file_t:s0:c47,c332",
  30. "ProcessLabel": "system_u:system_r:svirt_lxc_net_t:s0:c47,c332",
  31. "AppArmorProfile": "",
  32. "ExecIDs": null,
  33. "HostConfig": {
  34. "Binds": null,
  35. "ContainerIDFile": "",
  36. "LogConfig": {
  37. "Type": "journald",
  38. "Config": {}
  39. },
  40. "NetworkMode": "default",
  41. "PortBindings": {},
  42. "RestartPolicy": {
  43. "Name": "no",
  44. "MaximumRetryCount": 0
  45. },
  46. "VolumeDriver": "",
  47. "VolumesFrom": null,
  48. "CapAdd": null,
  49. "CapDrop": null,
  50. "Dns": [],
  51. "DnsOptions": [],
  52. "DnsSearch": [],
  53. "ExtraHosts": null,
  54. "GroupAdd": null,
  55. "IpcMode": "",
  56. "Links": null,
  57. "OomScoreAdj": 0,
  58. "PidMode": "",
  59. "Privileged": false,
  60. "PublishAllPorts": false,
  61. "ReadonlyRootfs": false,
  62. "SecurityOpt": null,
  63. "UTSMode": "",
  64. "ShmSize": 67108864,
  65. "ConsoleSize": [
  66. 0,
  67. 0
  68. ],
  69. "Isolation": "",
  70. "CpuShares": 0,
  71. "CgroupParent": "",
  72. "BlkioWeight": 0,
  73. "BlkioWeightDevice": null,
  74. "BlkioDeviceReadBps": null,
  75. "BlkioDeviceWriteBps": null,
  76. "BlkioDeviceReadIOps": null,
  77. "BlkioDeviceWriteIOps": null,
  78. "CpuPeriod": 0,
  79. "CpuQuota": 0,
  80. "CpusetCpus": "",
  81. "CpusetMems": "",
  82. "Devices": [],
  83. "KernelMemory": 0,
  84. "Memory": 0,
  85. "MemoryReservation": 0,
  86. "MemorySwap": 0,
  87. "MemorySwappiness": -1,
  88. "OomKillDisable": false,
  89. "PidsLimit": 0,
  90. "Ulimits": null
  91. },
  92. "GraphDriver": {
  93. "Name": "devicemapper",
  94. "Data": {
  95. "DeviceId": "29",
  96. "DeviceName": "docker-253:0-101330881-9cb32851050b1707022b475489686582a272d883a56a8ff52f3344f56b65639f",
  97. "DeviceSize": "10737418240"
  98. }
  99. },
  100. "Mounts": [
  101. {
  102. "Name": "5ddd734c53a38a78a9f739157c63074b4aff736d4045925616d7753402304137",
  103. "Source": "/var/lib/docker/volumes/5ddd734c53a38a78a9f739157c63074b4aff736d4045925616d7753402304137/_data",
  104. "Destination": "/opt/vol-data",
  105. "Driver": "local",
  106. "Mode": "",
  107. "RW": true,
  108. "Propagation": ""
  109. }
  110. ],
  111. "Config": {
  112. "Hostname": "81a74152e6f4",
  113. "Domainname": "",
  114. "User": "",
  115. "AttachStdin": true,
  116. "AttachStdout": true,
  117. "AttachStderr": true,
  118. "Tty": true,
  119. "OpenStdin": true,
  120. "StdinOnce": true,
  121. "Env": null,
  122. "Cmd": [
  123. "/bin/bash"
  124. ],
  125. "Image": "test/mycentos:v1.0",
  126. "Volumes": {
  127. "/opt/vol-data": {}
  128. },
  129. "WorkingDir": "",
  130. "Entrypoint": null,
  131. "OnBuild": null,
  132. "Labels": {}
  133. },
  134. "NetworkSettings": {
  135. "Bridge": "",
  136. "SandboxID": "c73841812aab480cf8e6a02071e36951dceb002d7b36f7dc0b38ccd9db833ba5",
  137. "HairpinMode": false,
  138. "LinkLocalIPv6Address": "",
  139. "LinkLocalIPv6PrefixLen": 0,
  140. "Ports": {},
  141. "SandboxKey": "/var/run/docker/netns/c73841812aab",
  142. "SecondaryIPAddresses": null,
  143. "SecondaryIPv6Addresses": null,
  144. "EndpointID": "3e492b611d9bd2c202c8a6c8fe4b4a755393545348b1aeb4e60995609dabb07c",
  145. "Gateway": "172.17.0.1",
  146. "GlobalIPv6Address": "",
  147. "GlobalIPv6PrefixLen": 0,
  148. "IPAddress": "172.17.0.2",
  149. "IPPrefixLen": 16,
  150. "IPv6Gateway": "",
  151. "MacAddress": "02:42:ac:11:00:02",
  152. "Networks": {
  153. "bridge": {
  154. "IPAMConfig": null,
  155. "Links": null,
  156. "Aliases": null,
  157. "NetworkID": "54001aaff29a231c9b2fe83459805d99dce0c21d6e2719f9b11bc90d8fe2f9c9",
  158. "EndpointID": "3e492b611d9bd2c202c8a6c8fe4b4a755393545348b1aeb4e60995609dabb07c",
  159. "Gateway": "172.17.0.1",
  160. "IPAddress": "172.17.0.2",
  161. "IPPrefixLen": 16,
  162. "IPv6Gateway": "",
  163. "GlobalIPv6Address": "",
  164. "GlobalIPv6PrefixLen": 0,
  165. "MacAddress": "02:42:ac:11:00:02"
  166. }
  167. }
  168. }
  169. }
  170. ]

注意看Mounts节点(Docker的版本用的是1.10.3),数据卷的使用,类似于 Linux 下对目录或文件进行 mount。

  1. "Mounts": [
  2. {
  3. "Name": "5ddd734c53a38a78a9f739157c63074b4aff736d4045925616d7753402304137",
  4. "Source": "/var/lib/docker/volumes/5ddd734c53a38a78a9f739157c63074b4aff736d4045925616d7753402304137/_data",
  5. "Destination": "/opt/vol-data",
  6. "Driver": "local",
  7. "Mode": "",
  8. "RW": true,
  9. "Propagation": ""
  10. }
  11. ]


     当我们在容器的volume上操作时,宿主机上对应的文件是否也会跟着变动呢?测试一下,在容器的volume上创建一个文件test.txt,然后查看宿主机是不是也会同步存在

经测试,当容器上的volume有变动时,宿主机也会跟着变动,那反过来呢?经测试也是一样的。不管是容器挂载点发生变动还是宿主机对挂载目录进行操作,令一方都会跟着变动。

利用docker commit生成新镜像,然后docker run -it 运行新镜像,发现容器挂载目录下没有任何文件了。说明生成新镜像时,是不保存挂载文件的。

3.2从宿主机挂载volume(-v /host-path:/container-path)

将主机的文件或文件夹作为volume挂载时,可以用多个 -v标签为容器添加多个volume,还可以使用:ro指定该volume为只读。注意:如果容器中指定的挂载目录存在相同的文件时,会被宿主机覆盖掉

在宿主机上建立了/opt/vol-01和/opt/vol-02挂载点,分别和容器中的/opt/vol-test-1和/opt/vol-test-2对应,前者权限默认读写,后者只能读,用docker inspect

当在容器的vol-test-2上新建操作时,会提示只读.在宿主机上,2个挂载点新增,修改,删除操作都OK,但是在容器中居然2个都不行,按理说应用是第二个vol-test-2不行才对,不知是哪里出问题了。

利用docker commit生成新镜像,然后docker run -it 运行新镜像,发现容器挂载目录下没有任何文件了。说明生成新镜像时,是不保存挂载文件的。

3.3使用Dockerfile添加volume

使用VOLUME指令向容器添加volume

VOLUME /data

多个时VOLUME ["/data1","/data2"]

这种情况和第一个中情况docker run -v /data是一样的。注意,dockerfile中使用volume是不能和第二种方法那样挂载宿主机中指定的文件夹。这时为了保证Dockerfile的可移植性,因为不能保证所有的宿主机都有对应的文件夹。

需要注意的是,在Dockerfile中使用VOLUME指令后,如果尝试对这个volume进行修改,这些修改指令都不会生效,比如下面例子,尝试添加一个文件,并修改文件并改变文件所有权限

  1. FROM test/mycent:v1.0
  2. RUN useradd foo
  3. VOLUME /data
  4. RUN touch /data/x
  5. RUN chown -R foo:foo /data

通过该Dockerfile创建镜像并启动容器后,该容器中存在用户foo,并且能看到在/data挂载的volume,但是/data文件夹的所有者并没有被改变为foo,而且/data下也没有/data/x文件。但是如果顺序反过来,先建文件,先授权,再挂载volume,那就得到期待的结果。

4.共享volume/数据卷容器(--volumes-from)

如果你有一些持续更新的数据需要在容器之间共享,最好创建数据卷容器。数据卷容器,其实就是一个正常的容器,专门用来提供数据卷供其它容器挂载的。

先创建一个名为dbdata的数据卷容器,专门共其他容器挂载。

在/opt/dbdata下创建了一个文件db.properties。

再启动2个容器,a,b都a--volumes-from dbdata,b --volumes-from a.

可以看到vol_a,vol_b容器的/opt/dbdata下都有db.properties文件。在vol_b的/opt/dbdata下创建文件vol_b.txt时,容器dbdata,vol_a也都同时同步了。

对dbdata,vol_a,vol_b使用命令docker inspect时,发现他们的Mounts下的Source都是一样的,说明它们都绑定到宿主机的同一个目录,所以当某个容器的挂载修改时,其他容器也看到了同样的效果

如果挂载源有多个时,可以使用多个--volumes-from,如

docker run -it --name vol_use --volumes-from vol_a --volumes-from vol_b test/mycentos:v1.0 /bin/bash.

如果一个容器挂在了volume,即使容器停止了运行,该volume仍然存在,其他容器仍然可以继续--volumes-from它。

5.删除volume

如果创建容器时挂载了volume,在/var/lib/docker/volumes/04b003b21b873157433deffbaf08bb0c89d234d3ec3c6576fdd7b61f5d41163e/_data下会生成相应的文件(路径,不同版本,不同操作系统会有所不同,具体可以用docker inspect查看容器具体信息),当删除容器时,宿主机上的挂载目录时不会删除的,并且目录名称是随机字符,不知意义,所以在删除容器时,需要妥善处理容器的volume。删除容器时一并删除volume有2中方法

(1)docker rm -v 删除容器。就是删除容器时,加上-v

(2)docker run --rm .就是启动容器的时候加上--rm,那么当容器运行停止时会自动删除容器以及容器所挂载的volume

上面创建了容器dbdata,vol_a,vol_b,vol_c,现在使用docker rm -v看看有什么效果。

vol_c是--volumes-from dbdata的,删除vol_c时,宿主机的挂载目录仍存在,没删掉,猜测那是因为还有其他容器在连着或者说是dbdata -v的时候创建的。那现在删除dbdata容器试试看(vol_a是--volumes-from dbdata的,vol_a还没删除,验证下能否删除dbdata)。

发现dbdata删除时,宿主机那目录仍然存在,同时也说明哪怕vol_a是--volumes-from dbdata的,vol_a还没删除,那也没影响。同时也说明只要还有一个容器在挂载这宿主机的目录,那宿主机的目录就不会删除。那接下来,把所有容器都删除,再看结果。

可以看到,当最后一个容器删除后,宿主机那volume目录终于删除了.

6.备份、恢复或迁移volume

上面有测试过,当使用docker commit等手段生成新镜像,然后再启动镜像生成新容器时,原先volume目录下的文件不见了,可以生成新镜像时,并没有把volume下的文件一起打包生成镜像。

volume作为数据的载体,在很多情况下需要对其中的数据进行备份、迁移,或是从已有数据恢复。一个很容易想到的方法就是用docker inspect命令查找到volume对应宿主机上对应的那个目录位置,然后复制其中内容或使用tar打包。当这些笨拙的做法并不值得推荐,因为查找主机上文件夹后再操作容易出错,也不适合脚本的自动化执行。

备份volume可以使用以下方法

docker run --rm --volumes-from dbdata -v $(pwd):/backup test/mycentos:v1.0 tar cvf /back/data.tar /data

这行指令启动了一个临时的容器,这个容器挂载了两个volume,第一个volume与要备份的volume共享,第二个volume将宿主机的当前目录(也可以绝对路径)挂载到容器的/backup下。容器运行后将要备份的容器(/data)备份到/backup/data.tar,然后删除容器,备份后的data.tar就留在了当前目录。操作验证一下

居然报错了,说没有权限。进入容器-it时,是docker随机生成一个用户的,至于怎样给该用户授权,以后再研究吧。

docker学习5--docker数据卷(volume)的更多相关文章

  1. Docker数据卷Volume实现文件共享、数据迁移备份(三)--技术流ken

    前言 前面已经写了两篇关于docker的博文了,在工作中有关docker的基本操作已经基本讲解完了.相信现在大家已经能够熟练配置docker以及使用docker来创建镜像以及容器了.本篇博客将会讲解如 ...

  2. Docker之数据卷Volume(七)

    一.简介   Docker数据卷(volume)机制.volume是存在于一个或多个容器中的特定文件或文件夹,这个目录以独立于联合文件系统的形式在宿主机中存在,并为数据的共享与持久化提供便利. 1)v ...

  3. docker数据卷(volume)

    1.什么是数据卷volume https://blog.csdn.net/dream_broken/article/details/52314993 为了了解什么是Docker Volume,首先我们 ...

  4. docker——数据卷volume:文件共享

    volume——如何让容器中的一个目录与宿主机的一个目录进行绑定,实现容器与宿主机之间的文件共享? 数据卷volume功能特性 数据卷:是一个可供一个或多个容器使用的特殊目录,实现让容器中的一个目录和 ...

  5. Docker数据卷Volume实现文件共享、数据迁移备份(三)

    数据卷volume功能特性 数据卷 是一个可供一个或多个容器使用的特殊目录,实现让容器中的一个目录和宿主机中的一个文件或者目录进行绑定.数据卷 是被设计用来持久化数据的对于数据卷你可以理解为NFS中的 ...

  6. 基于Ceph分布式集群实现docker跨主机共享数据卷

    上篇文章介绍了如何使用docker部署Ceph分布式存储集群,本篇在此基础之上,介绍如何基于Ceph分布式存储集群实现docker跨主机共享数据卷. 1.环境准备 在原来的环境基础之上,新增一台cen ...

  7. Docker 使用指南 (四)—— 数据卷的使用

    一.数据卷的使用 有时候需要使用数据库,但是又希望它的数据能保存在本地,Docker中提供了数据卷可以供你方便的操作数据.数据卷是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用 ...

  8. <Docker学习>5. docker数据管理

    当我们创建了一个tomcat容器,如何简单部署一个web应用?如何将war包放入到容器中?也就是说怎么样把文件从宿主机中 "放入" 到容器中? docker cp命令可以将宿主机本 ...

  9. Docker学习之Docker容器基本使用

    Docker学习之Docker容器基本使用 新建容器并启动 命令格式:docker run --options repository:tag 后台运行 命令格式:-d 已存在的容器相关操作 启动:do ...

随机推荐

  1. vue二次实战(二)

    https://www.cnblogs.com/jellify/p/9522477.html install的弹出框中输入sublimeTmpl,找到sublimeTmpl这个插件后回车 Vue路由 ...

  2. String类内存空间详解

    java.lang.String类内存问题详解 字符串理解的难点在于其在堆内存空间上的特殊性,字符串String对象在堆内存上有两种空间: 字符串池(String pool):特殊的堆内存,专门存放S ...

  3. java类型的小知识List 等

    List 复制之 浅拷贝与深拷贝 详细连接https://blog.csdn.net/never_tears/article/details/79067245 java中判断字符串是否为数字的方法的几 ...

  4. windos安装maven

    1.下载好maven压缩包,并解压到相应位置,本次安装在D: 2.配置环境变量 MAVEN_HOME=D:\apache-maven-3.0.5 path=%MAVEN_HOME% 3.生成maven ...

  5. WPF TextBox控件中文字实现垂直居中

    TextBox纵向长度比较长但文字字体比较小的时候,在输入时就会发现文字不是垂直居中的. 而使用中我们发现,TextBox虽然可以设置文字的水平对齐方式,但却没有相应的属性让我们来调节他的垂直对齐方式 ...

  6. 避免MQ消息重发的简单实现思路

    一.MQ消息发送 一.MQ消息发送 1.发送端MQ-client(消息生产者:Producer)将消息发送给MQ-server: 2.MQ-server将消息落地: 3.MQ-server回ACK给M ...

  7. 只要访问url地址 那么容器就会根据地址进行对象的创建

    只要访问url地址 那么容器就会根据地址进行对象的创建

  8. Qt evenFilter()与installEvenFilter()

    1, eventFilter 函数中实现事件过滤器.请注意:该函数在 QObject 类中声明为一个虚函数,因此只能由 QObject 的子类继承使用. 2, installEventFilter函数 ...

  9. Python中的numpy模块解析

    numpy 1.  创建对象 维度(dimensions):轴 轴的个数:秩(rank) Numpy最重要的一个特点就是其N维数组对象(即ndarray) 创建数组最简单的函数就是用array函数: ...

  10. 牛客寒假算法训练1 D 欧拉(容斥)

    1 #include<bits/stdc++.h> using namespace std; ; typedef long long ll; int p[maxn],a[maxn]; ll ...