主要内容
管理Docker中的数据
在容器中创建以及修改的文件默认都被保存在容器的可写层,这意味着:
- 当容器停止运行,容器中的数据不再可用,里面的数据难以被其他容器重用。当容器被删除,当中的数据会一并消失
- 容器中的数据与运行容器的环境紧耦合,难以被迁移
- 往容器中的可写层写数据,需要通过storage driver管理文件系统,性能被削弱
为了解决上述问题,Docker提供了两种储存数据的方式:volumes和bind mounts,它们和容器松耦合,即使容器停止甚至被删除,这些数据依然得以保留。
我们所说的数据一般有两种状态:可变的和不变的,也称为动态数据和静态数据。
- 动态数据 如数据库产生的数据,会随着时间的改变而改变
- 静态数据 如网站的html文件等,发布后就不会改变的
静态数据保存在镜像层中是没有问题的,因为它们不变,因此我们可以把这些数据打包进镜像文件,但像数据库,我们就应该使用Docker提供的储存方案保存它们。
使用volumes
volumes是一个保存数据的目录,通常位于/var/lib/docker/volumes/
目录下,并由Docker管理。其他非Docker进程不应该修改这个目录下面的数据。
volumes可以分为匿名volume和命名volume,除了名字不同外,它们的行为一致。
- 匿名volume在容器运行的时候由Docker创建,每个匿名volume都有一个唯一的ID
- 由于匿名volume没有名称,因此在挂载的时候不需要指定名称
- 命名volume可以自己创建,也可以由Docker创建
- 命名volume在挂载的时候需要指定名称
使用命名volume
volume是一个目录,位于/var/lib/docker/volumes
下面,我们可以使用docker volume
命令创建和管理这些volume。volume可以同时被多个容器挂载。
可以通过-v(--volume)
参数或者--mount
参数把volume挂载到容器中,不过需要注意的是:–mount参数在17.06版本之前只能够在swarm service环境中使用,17.06或之后的版本–mount也能够在非集群环境中挂载volume。
以mysql镜像为例子,下面的例子创建了一个命名volume:
1 2 3 4 5 6 7 8 |
docker run --name mysql-server -t \ -v mysql:/var/lib/mysql \ # 创建了名为mysql的volume -e MYSQL_DATABASE="test" \ -e MYSQL_USER="test" \ -e MYSQL_PASSWORD="test" \ -e MYSQL_ROOT_PASSWORD="root" \ -d mysql:5.7 |
如果Docker发现指定名称的volume还不存在,会先帮我们创建出来。这个volume被映射到容器的/var/lib/mysql
目录。
使用ls观察目录结构:
1 2 3 4 |
[root@dellcentos7 ~]# ls /var/lib/docker/volumes/mysql/_data/ auto.cnf ca.pem client-key.pem ibdata1 ib_logfile1 mysql private_key.pem server-cert.pem sys ca-key.pem client-cert.pem ib_buffer_pool ib_logfile0 ibtmp1 performance_schema public_key.pem server-key.pem test |
可以看到,mysql中的数据被存放到mysql这个volume的_data目录下面。
使用docker volume命令
docker volume
命令有几个子命令,可以方便地创建和管理volumes。
1 2 3 |
[root@dellcentos7 ~]# docker volume create inspect ls prune rm |
之前的例子中的volume是由Docker创建的,现在我们通过docker volume create
命令手动创建volume。
1 2 |
[root@dellcentos7 ~]# docker volume create mysql |
然后使用docker volume ls
查看
1 2 3 4 5 |
[root@dellcentos7 ~]# docker volume ls DRIVER VOLUME NAME ...... local mysql |
创建好volume之后,可以像之前一样运行容器
1 2 3 4 5 6 7 8 |
docker run --name mysql-server -t \ -v mysql:/var/lib/mysql \ # 使用预先创建的volume -e MYSQL_DATABASE="test" \ -e MYSQL_USER="test" \ -e MYSQL_PASSWORD="test" \ -e MYSQL_ROOT_PASSWORD="root" \ -d mysql:5.7 |
无论是docker为我们创建volume还是我们使用docker volume create
手动创建,它们的行为一致。
使用匿名volume
匿名volume的使用和命名volume一样,但是挂载的时候不需要指定名称,匿名volume只能够由Docker创建。
1 2 3 4 5 6 7 8 |
docker run --name mysql-server -t \ -v /var/lib/mysql \ #没有指定volume名称,使用匿名volume -e MYSQL_DATABASE="test" \ -e MYSQL_USER="test" \ -e MYSQL_PASSWORD="test" \ -e MYSQL_ROOT_PASSWORD="root" \ -d mysql:5.7 |
那么怎样知道容器挂载的是哪一个volume呢?可以使用docker inspect
命令:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
"Mounts": [ { "Type": "volume", "Name": "caefe74dc6d8e3d0a012e829be8b7729bd5effdb7816b732d4b46aaf8706ea0e", "Source": "/var/lib/docker/volumes/caefe74dc6d8e3d0a012e829be8b7729bd5effdb7816b732d4b46aaf8706ea0e/_data", "Destination": "/var/lib/mysql", "Driver": "local", "Mode": "", "RW": true, "Propagation": "" } ] |
docker inspect
命令用于查阅容器的元数据,其中就包括volume的使用信息。
挂载其他容器的volume
使用--volumes-from
选项,可以从一个容器挂载另一个容器中的volume,非常适合在容器间共享数据。
1 2 3 4 5 6 |
[root@dellcentos7 ~]# docker run --volumes-from mysql-server -it busybox /bin/sh / # ls /var/lib/mysql/ auto.cnf client-cert.pem ib_logfile0 ibtmp1 private_key.pem server-key.pem ca-key.pem client-key.pem ib_logfile1 mysql public_key.pem sys ca.pem ib_buffer_pool ibdata1 performance_schema server-cert.pem test |
使用--volumes-from
选项挂载的volume挂载点都一样,这里是/var/lib/mysql
。
Data-Packed volume
data-packed volume类似上面的例子,也是从其他的容器挂载volume,不同的是,data-packed volume中的数据是镜像创建的过程中打包进去的,保存在镜像层,也就是说,data-packed volume中的数据适合放置静态数据。
1 2 3 4 |
FROM busybox ADD data.tar /data VOLUME["/data"] |
然后其他容器就可以使用--volumes-from
挂载这个容器提供的volumes。
删除volume
可以使用docker volume rm
命令或者docker volume prune
命令删除volume
- rm用于删除指定volume,即使这些volume目前被某个容器引用
- prune用于删除那些孤儿volume,即没有任何容器挂载的volume
使用bind mounts
和volumes不同,bind mounts可以挂载宿主机文件系统中的任意目录,bind mounts的随意性会造成一定的安全隐患,例如容器可以修改系统的重要文件。
挂载目录
开发人员在开发的过程中为了方便测试,可以把宿主机中的项目路径挂载进容器,这样容器就可以随时读取项目文件。下面的例子把java项目路径挂载进容器的tomcat目录。
1 2 3 4 5 6 7 8 9 |
[root@centos7-server ~]# ls anaconda-ks.cfg testpro [root@centos7-server ~]# ls testpro/ index.html [root@centos7-server ~]# docker run --name tomcat \ > -v /root/testpro/:/usr/local/tomcat/webapps/testpro \ # 把本地的文件夹挂载到容器的指定目录 > -p 8080:8080 \ > -it -d tomcat |
挂载之后,在宿主机中对文件的任何修改都会马上反应在容器当中。
挂载文件
bind mounts方式不仅可以挂载目录,也可以把文件挂载进容器
例如把/etc/hosts文件挂载到容器
1 2 3 |
docker run --name busybox \ -v /etc/hosts:/etc/hosts:ro # ro表示read only |
挂载文件的时候需要特别注意:如果要挂载的本地文件不存在,那么docker会自动帮你创建,但是,创建出来的是一个目录,而不是文件,如果不注意的话,很可能会造成错误。
总结
无论是volumes还是bind mounts,都有一些地方需要注意
- 挂载volume进容器的时候,如果容器中的挂载点有数据,则会把这些数据复制到宿主机的挂载点中
- volumes位置固定在
/var/lib/docker/volumes
下面,这是一个优点,也是一个缺点,优点是不会影响宿主机的文件系统,比较安全;缺点是失去了灵活性 - 挂载bind mounts的时候,宿主机的目录会覆盖容器中的同名目录,结果是容器中的原数据不可见,直到bind mounts卸载
- bind mounts可以是宿主机的任意目录,会造成一定的安全问题,但它非常灵活
使用共享储存设备
为了容错设计,应用和数据一般都不会放置在同一主机中,这就要求容器具备挂载远程volume的能力。但很遗憾,docker默认不具备这个能力,如果确实有这个需求,可以通过docker的插件实现,具体可参考官方链接:volume plugins
volumes位置固定的缺点限制了它的灵活性,但是volumes配合bind mounts却可以工作得很好,以glusterfs分布式文件系统为例子:
- glusterfs服务器向外提供了data目录用于数据保存
- Docker Host主机把这个远程data目录挂载到了自己的/mnt目录
- Docker Host中使用其中一个容器以bind mounts的方式挂载本地/mnt目录,间接使用了远程目录
- 其他容器通过–volumes-from的方式使用glusterfs client容器中的volume
转载请注明:Pure nonsense » Docker数据储存