本文转载自:http://awkee.github.io/ 文章译者:yu hou
本节学习的内容是如何管理容器中的数据以及容器之间的数据,我们将要学习如下两个主要方式:
数据卷是一个或多个容器中专门设计的目录,它绕过了UnionFS,并且为数据持久化和数据共享提供了一些有帮助的功能:
数据卷用来持久化数据、独立于容器的生命周期。Docker不会在我们删除一个容器时自动的删除数据卷,也不会在数据卷没有被引用时被自动回收。
我们可以添一个数据卷到一个容器中,方法是在docker
create
或者docker
run
命令后使用-v
参数。 我们可以多次使用-v
参数来加载多个数据卷。下面是一个添加一个数据卷的示例:
$ docker run -d -P --name web -v /webapp training/webapp python app.py
此命令将会在web
容器中的/webapp目录下创建一个新的卷。
说明: 我们可以在Dockerfile中使用VOLUME指令添加一个或多个新的卷到镜像中。
我们可以通过执行docker inspect
指令来查看容器中挂载的卷信息 :
$ docker inspect web
输出信息大致如下,会提供容器的配置信息:
...
"Mounts": [
{
"Name": "fac362...80535",
"Source": "/var/lib/docker/volumes/fac362...80535/_data",
"Destination": "/webapp",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
]
...
其中Source
指定主机的目录,Destination
指定容器中的卷位置。如果有读写权限则显示RW
,并且为true
状态。
除了使用-v
参数创建新的卷,我们还可以将Docker引擎主机的目录挂载到容器中。
$ docker run -d -P --name web -v /src/webapp:/opt/webapp training/webapp python app.py
此命令挂载一个主机目录/src/webapp
到容器的/opt/webapp
目录。如果/opt/webapp
已经存在,/src/webapp
将会覆盖一层掩盖原始的目录内容,但是不会删除原始内容。当删除此挂载点,原始内容又会可以被访问了,此行为与mount
命令行为相同。
container-dir
必须是绝对路径,host-dir
可以是相对路径也可以是绝对路径,也可以是一个卷name
值。如果我们设置的host-dir
是绝对路径,Docker会绑定挂载指定的绝对路径(设置绝对路径不适合迁移);如果我们设置的是一个卷name
,Docker将会创建一个被命名为name
的卷。
命名卷的name
起名规则是以字母开始,后面可以是字母、数字、下划线( _ )、点( . )、减号( - )。
绝对路径是以斜线为开始,比如Windows平台下的C:\Users
目录需要写成/C/Users
,如下示例:
### OSX或者Linux平台
docker run -v /Users/<path>:/<container path> ...
### Windows平台
docker run -v /c/Users/<path>:/<container path> ...`
Docker 卷默认加载为读写模式,但是我们也可以指定为只读模式,方法如下:
docker run -d -P --name web -v /src/webapp:/opt/webapp:ro training/webapp python app.py
由于mount
函数的限制,移动主机上host-dir
的子目录可以为容器提供访问主机文件系统的权限,处于恶意的用户会利用此方法获取访问主机目录的权限。
说明;
host-dir
是依赖主机的,因此处于可移植性考虑不建议在Dockerfile
中挂载主机目录。毕竟主机目录不可能对于所有潜在的主机都是可用的。
Docker的一些卷插件允许我们挂载共享存储设备,比如iSCSI、NFS和FC。
使用共享卷的好处是他们都是独立于主机的。这就意味着只要我们安装了卷插件并且在容器启动时获取了访问权限,我们就可以在任何主机上使用他们来创建一个卷。
一种使用卷驱动器的方式是通过docker run
命令。卷驱动器创建一个命名卷,而不是路径。如下示例是使用flocker
驱动器创建了一个命名卷my-named-
volume
,使其可以挂载到了容器的/opt/webapp目录:
$ docker run -d -P \
--volume-driver=flocker \
-v my-named-volume:/opt/webapp \
--name web training/webapp python app.py
我们也可以使用docker volume create
命令在使用前创建一个卷。下面这个例子就是先创建一个命名卷,然后用到了容器中。
$ docker volume create -d flocker --name my-named-volume -o size=20GB
$ docker run -d -P \
-v my-named-volume:/opt/webapp \
--name web training/webapp python app.py
访问这里我们还可以获取更多的插件。
标签系统相SELinux一样,需要合适的标签来标记挂载到容器中的卷内容。没有标签,安全系统需要防止运行在容器中的进程使用其内容。默认Docker不会修改操作系统的标签集合。
如果想修改容器中的标签,我们需要在挂载卷时添加:z
或者:Z
后缀。这两个后缀告诉Docker重新标记共享卷上的文件对象。其中z
选项是告诉Docker两个容器共享这个卷的内容。Docker就会把卷内容标记为共享标签。共享卷标签允许所有的容器具有读写其内容权限。而Z
选项告诉Docker标记卷内容为私有非恭喜的标签。只有当前容器可以使用此私有卷。
同样使用-v
参数,但是这次挂载的是主机文件而不是主机目录。
$ docker run --rm -it -v ~/.bash_history:/root/.bash_history ubuntu /bin/bash
这个例子会把我们在容器shell中执行的命令记录在主机上的历史记录文件中,当我们删除容器后,这些记录会保存在主机的.bash_history文件中。
说明: 许多修改文件命令工具会导致inode变化,例如
vi
和sed --in-place
等。从Docker v1.1.0版本以后,这个修改操作会产生一个错误如"sed: cannot rename ./sedKdJ9Dy: Device or resource busy"。这种情况下我们如果想要修改挂载的文件的话,最简单的办法是挂载它所在的目录。
如果我们有些持久数据打算在多个容器之间共享,或者打算在非持久化的容器中使用,最好的办法是创建一个命名数据卷容器,然后从这个容器来挂载数据。
让我们来看个例子,我们创建一个没有运行应用的数据卷容器,它复用training/postgres
镜像,这样所有勇气将使用相同的层,节省磁盘空间。
$ docker create -v /dbdata --name dbstore training/postgres /bin/true
然后我们可以使用--volumes-from
参数把/dbdata
卷挂载到另一个容器中。
$ 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
容器上的文件是可见的。
我们可以额使用多个--volumes-from
参数从多个容器中融合数据卷。了解更多关于--volumes-from
信息点击 Mount volumes from container 链接查看run
命令参考文档。
如果我们删除挂载了卷的容器,包括dbstore
容器或者引用了dbstore
容器的db1
和db2
容器,这些容器相应的数据卷将会同时被删除。
想要从磁盘上删除一个卷 ,只能针对最后一个挂载了数据卷的容器显式地调用docker rm -v
命令,这种方式可使你在容器之间方便的更新和迁移数据。
说明: 如果在你删除一个容器时没有使用
-v
,Docker不会给你提示警告信息。我们将会得到dangling
卷;这个卷不被任何容器引用。我们可以使用docker volume ls -f dangling=true
来查找dangling卷,然后docker volume rm <volume name>
删除这些不需要的卷即可。
数据卷还可以用来备份、恢复或迁移数据。为此我们使用--volumes-from
参数来创建一个挂载数据卷的容器,像这样:
$ sudo docker run -d -v /dbdata --name dbdata training/postgres echo Data-only container for postgres
$ sudo docker run --volumes-from dbdata -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata
这里我们启动了一个挂载dbdata卷的新容器,并且挂载了一个本地目录作为/backup卷。最后,我们通过使用tar命令将dbdata卷的内容备份到容器中的/backup目录下的backup.tar文件中。当命令完成或者容器停止,我们会留下我们的dbdata卷的备份。
然后,你可以在同一容器或在另外的容器中恢复此数据。创建一个新的容器
$ sudo docker run -v /dbdata --name dbdata2 ubuntu /bin/bash
然后在新的容器中的数据卷里un-tar此备份文件。
$ sudo docker run --volumes-from dbdata2 -v $(pwd):/backup busybox tar xvf /backup/backup.tar
你可以对熟悉的目录应用此技术,来测试自动备份、迁移和恢复。
一个Docker数据卷当容器被删除后对数据进行持久化,我们可以创建命名卷和匿名卷,,命名卷有具体的源格式,如awesome:/bar
。匿名卷没有具体的源格式。当容器被删除后,我们要通知Docker引擎的守护进程清理匿名卷,我们需要在运行容器时添加--rm
选项 :
$ docker run --rm -v /foo -v awesome:/bar busybox top
此容器运行时创建了一个匿名卷/foo
,和命名卷awesome
,当容器停止运行自动删除后,匿名卷会被删除,而awesome
卷不会被删除掉。
多个容器可以共享一个或者多个数据卷。然而多个容器向同一个共享卷写数据会导致脏数据。确保我们的应用被设计的可以向共享数据存储中写数据。
数据卷可以被Docker宿主主机直接访问,这意味着我们可以使用普通的Linux工具来读写它们。在正常情况下,我们不建议这样直接访问,因为如果容器中的应用无法识别到我们直接访问的操作就会导致脏数据问题。
喜欢 (2)or分享 (0)