Docker(五)数据管理

时间:2023-02-10 17:30:02

在容器中管理数据主要有两种方式:

  • 数据卷(Data volumes
  • 数据卷容器(Data volumes containers

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

  1. 容器中的文件在宿主机上存在形式复杂,不能再宿主机上很方便地对容器中的文件进行访问。
  2. 多个容器之间的数据无法共享。
  3. 当删除容器时,容器产生的数据将丢失。

为了解决上面的这些问题,Docker引入了数据卷(volume)机制。

数据卷

数据卷是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:

  1. 数据卷在容器创建时就会初始化,在容器运行时就可以使用其中的文件
  2. 数据卷可以在容器之间共享和重用
  3. 对数据卷的修改会立马生效
  4. 对数据卷的更新,不会影响镜像
  5. volume的生存周期独立于容器的生存周期,即使删除容器,volume仍然会存在,没有任何容器使用volume也不会被Docker删除。
  6. 卷会一直存在,直到没有容器使用

数据卷的使用,类似于 Linux 下对目录或文件进行 mount。

使用 -v 标记来创建一个数据卷并挂载到容器里

-v /data -v src:dst #可以挂载单个文件

创建一个数据卷:

在用 docker run 命令的时候,使用 -v 标记来创建一个数据卷并挂载到容器里。在一次 run 中多次使用可以挂载多个数据卷。

创建一个 web 容器,并加载一个数据卷到容器的 /webapp 目录。

docker run -it --rm --name web -v /webapps centos    #--rm 退出时删除容器

注意:也可以在 Dockerfile 中使用 VOLUME 来添加一个或者多个新的卷到由该镜像创建的任意容器。

查看web容器的挂载目录
# docker inspect --format={{.Config.Volumes}} web
map[/webapps:{}]

查看web容器的挂载的详细信息
# docker inspect --format={{.Mounts}} web 
[{volume b809c68dc433e17f83f7ab61d77aed4c7407cb4c5ffdae555c5728662a9dc446 /var/lib/docker/volumes/b809c68dc433e17f83f7ab61d77aed4c7407cb4c5ffdae555c5728662a9dc446/_data /webapps local  true }]
#可以看到volume ID,以及volume ID的绝对路径,以及容器的挂载点目录,是本机挂载的形式,是挂载可读写状态。

挂载一个主机目录作为数据卷:

-v标记也可以指定挂载一个本地主机的目录到容器中去,这个功能在进行测试的时候十分方便,比如用户可以放置一些程序到本地目录中,来查看容器是否正常工作。本地目录的路径必须是绝对路径,如果目录不存在 Docker会自动为你创建它。如果镜像中原本存在/opt文件夹,该文件夹下原有的内容将被删除,以保持与宿主机中的文件夹一致。

docker run -it --rm --name web -v /usr/local/src:/opt/ centos ##挂载本地的/usr/local/src到容器的/opt目录下

注意:Dockerfile中不支持这种用法,这是因为Dockerfile是为了移植和分享用的。然而,不同操作系统的路径格式不一样,所以目前还不能支持。

Docker 挂载数据卷的默认权限是读写,用户也可以通过 :ro 指定为只读。

docker run -it --rm --name web -v /usr/local/src:/opt:ro centos   #rw|ro
# docker run -it --rm --name web -v /usr/local/src:/opt:ro centos
# docker run -it --rm --name web1 -v /usr/local/src:/opt/ centos
# docker inspect --format={{.Mounts}} web 
[{bind  /usr/local/src /opt  ro false rprivate}]
# docker inspect --format={{.Mounts}} web1
[{bind  /usr/local/src /opt   true rprivate}]       

挂载一个本地主机文件作为数据卷:
-v 标记也可以从主机挂载单个文件到容器中

docker run --rm -it -v ~/.bash_history:/.bash_history ubuntu /bin/bash

这样就可以记录在容器输入过的命令了。

注意:如果直接挂载一个文件,很多文件编辑工具,包括 vi 或者 sed –in-place ,可能会造成文件inode的改变,所以最简单的办法就直接挂载文件的父目录。

使用Dockerfile添加数据卷:

VOLUME /data

在使用docker build命令生成镜像并且以该镜像启动容器时会挂载一个volume到/data。如果镜像中存在/data文件夹,这个文件夹中的内容将全部被复制到宿主机中对应的文件夹中,并且根据容器中的文件设置合适的权限和所有者。

类似地,可以使用VOLUME指令添加多个volume:

VOLUME ["/data1","data2"]

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

数据卷容器

--volumes-from

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

首先,创建一个命名的数据卷容器 dbdata:

docker run -dit --name dbdata -v /usr/local/src/:/dbdata centos

然后,在其他容器中使用 --volumes-from 来挂载 dbdata 容器中的数据卷:

docker run -dit --volumes-from dbdata --name db1 centos
docker run -dit --volumes-from dbdata --name db2 centos

容器db1和db2会共享容器dbdata的数据卷,也就是说三个容器的/dbdata目录下都会有宿主机/usr/local/src/目录下的所有文件

还可以使用多个 –volumes-from 参数来从多个容器挂载多个数据卷。 也可以从其他已经挂载了数据卷的容器来挂载数据卷:

docker run -dit --volumes-from db1 --name db3 centos

注意:使用 --volumes-from 参数所挂载数据卷的容器自己并不需要保持在运行状态。

如果有一些数据,比如配置文件、数据文件等,要与多个容器之间共享,一种常见的做法就是创建一个数据容器,其他容器与之共享volume。然后这个数据容器就可以停止运行避免浪费资源了。

如果删除了挂载的容器(包括 dbdata、db1 和 db2),数据卷并不会被自动删除。如果要删除一个数据卷,必须在删除最后一个还挂载着它的容器时使用docker rm -v 命令来指定同时删除关联的容器。 这可以让用户在容器之间升级和移动数据卷。

利用数据卷容器来备份、恢复、迁移数据卷

可以利用数据卷对其中的数据进行进行备份、恢复和迁移。
volume作为数据的载体,在很多情况下需要对其中的数据进行备份、迁移,或是从已有数据恢复。
docker run -it --name test -v /data/db centos为例,该容器在/data/db挂载了一个volume。如果需要将这里的数据备份,一个很容易想到的方法就是使用docker inspect命令查找到/data/db在宿主机上对应的文件夹位置,然后复制其中内容或是使用tar进行打包;同样地,如果需要恢复某个volume中的数据,可以查找到volume对应的文件夹,将数据复制进这个文件夹或是使用tar从文档文件中恢复。

首先先准备一个数据卷容器:

# docker run -it --name test -v /data/db centos
[root@dda9d6200f79 /]# cd /data/db/
[root@dda9d6200f79 db]# ls
[root@dda9d6200f79 db]# echo 111 >> test1
[root@dda9d6200f79 db]# echo 222 >> test2 
[root@dda9d6200f79 db]# ls
test1  test2
[root@dda9d6200f79 db]# exit

备份:

docker run --rm --volumes-from test -v /docker/backup/test:/backup --name worker_backup centos tar cvf /backup/backup.tar /data/db

先用--rm启动一个临时的容器worker_backup, 然后将test的volume共享给worker_backup,然后将本地的/docker/backup/test目录挂载到 worker_backup 的 /backup 目录上,然后将 worker_backup 容器上的 /data/db 打包到 worker_backup 容器的/backup/目录下包名叫做backup.tar。然后因为是临时容器,容器做完操作就消亡了。

在宿主机上查看备份数据:

# ls /docker/backup/test/
backup.tar

恢复:
如果要恢复数据到一个容器,首先创建一个带有数据卷的容器 test2

docker run -dit -v /data/db --name test2 centos

然后创建另一个容器,挂载 test2 的容器,并使用 untar 解压备份文件到挂载的容器卷中:

# docker run --rm --volumes-from test2 -v /docker/backup/test/:/backup centos tar xvf /backup/backup.tar
data/db/
data/db/test1
data/db/test2

如果启动不了的话可以进到容器中手动解压

先搞了个新容器作为数据恢复的目标,第二行指令启动了一个临时容器,这个容器挂载了两个volume,第一个volume与要恢复的volume共享,第二个volume将宿主机刚才的备份目录挂载到容器的/backup下,然后将这个存放文件中的backup.tar恢复到根目录下,然后执行结束后,临时容器就消失了,恢复后的数据就在test2的volume中了。

删除数据卷

如果创建容器时,从容器中挂载了volume,在/var/lib/docker/volumes下会生成与volume对应的目录,使用docker rm删除容器并不会删除与volume对应的目录,这些目录会占据不必要的存储空间,即使可以手动删除,因为这些目录名称是无意义的随机字符串,要知道它们是否与未被删除的容器对应也十分麻烦。所以在删除容器时需要对容器的volume妥善处理。在删除容器时,一并删除volume有以下两种方法:

-第一种方法(docker rm -v):

# docker inspect --format={{.Mounts}} test 
[{volume 861a3030682f25272824eabe524a8d783a614e56a60b844e2b18ebbc894b006c /var/lib/docker/volumes/861a3030682f25272824eabe524a8d783a614e56a60b844e2b18ebbc894b006c/_data /data/db local  true }]    

# docker rm -v test

第二种方法(docker run --rm):
在运行容器时,使用docker rm --rm--rm标签会在容器停止时删除容器以及容器所挂载的volume。

需要注意的是,以上方法只能在对应volume是被最后一个容器使用时才会将其删除,如果容器的volume被多个容器共享,在删除最后一个共享它的容器时将其删除。

如果volume是在创建容器时从宿主机中挂载的,无论对容器进行任何操作都不会导致其在宿主机被删除,如果不需要这些文件,只能手工删除它们。