当前位置:嗨网首页>书籍在线阅读

05-容器与持久化数据

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

13.2.2 容器与持久化数据

在容器中持久化数据的方式推荐采用卷。

总体来说,用户创建卷,然后创建容器,接着将卷挂载到容器上。卷会挂载到容器文件系统的某个目录之下,任何写到该目录下的内容都会写到卷中。即使容器被删除,卷与其上面的数据仍然存在。

如图13.1所示,Docker卷挂载到容器的 /code 目录。任何写入 /code 目录的数据都会保存到卷当中,并且在容器删除后依然存在。

84.png

图13.1 Docker卷挂载到容器的 `/code` 目录

图13.1中, /code 目录是一个Docker卷。容器其他目录均使用临时的本地存储。卷与目录 /code 之间采用带箭头的虚线连接,这是为了表明卷与容器是非耦合的关系。

1.创建和管理容器卷

Docker中卷属于一等公民。抛开其他原因,这意味着卷在API中拥有一席之地,并且有独立的 docker volume 子命令。

使用下面的命令创建名为 myvol 的新卷。

$ docker volume create myvol

默认情况下,Docker创建新卷时采用内置的 local 驱动。恰如其名,本地卷只能被所在节点的容器使用。使用 -d 参数可以指定不同的驱动。

第三方驱动可以通过插件方式接入。这些驱动提供了高级存储特性,并为Docker集成了外部存储系统。图13.2展示的就是外部存储系统被用作卷存储。驱动集成了外部存储系统到Docker环境当中,同时能使用其高级特性。

85.png

图13.2 外部存储接入Docker

截至本书撰写时,已经存在25种卷插件,涵盖了块存储、文件存储、对象存储等。

  • 块存储: 相对性能更高,适用于对小块数据的随机访问负载。目前支持Docker卷插件的块存储例子包括HPE 3PAR、Amazon EBS以及OpenStack块存储服务(Cinder)。
  • 文件存储: 包括NFS和SMB协议的系统,同样在高性能场景下表现优异。支持Docker卷插件的文件存储系统包括NetApp FAS、Azure文件存储以及Amazon EFS。
  • 对象存储: 适用于较大且长期存储的、很少变更的二进制数据存储。通常对象存储是根据内容寻址,并且性能较低。支持Docker卷驱动的例子包括Amazon S3、Ceph以及Minio。

现在卷已经创建成功,读者可以通过 docker volume ls 命令进行查看,还可以使用 docker volume inspect 命令查看详情。

$ docker volume ls
DRIVER             VOLUME NAME
local              myvol
$ docker volume inspect myvol
[
    {
        "CreatedAt": "2018-01-12T12:12:10Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/myvol/_data",
        "Name": "myvol",
        "Options": {},
        "Scope": "local"
    }
]

inspect 命令输出中有几点很有意思。Driver和Scope都是 local 。这意味着卷使用默认 local 驱动创建,只能用于当前Docker主机上的容器。 Mountpoint 属性说明卷位于Docker主机上的位置。在本例中卷位于Docker主机的 /var/lib/docker/volumes/myvol/_data 目录。在Windows Docker主机上对应内容为 Mountpoint": "C:\\ProgramData\\Docker\\ volumes\\myvol\\_data

使用 local 驱动创建的卷在Docker主机上均有其专属目录,在Linux中位于 /var/lib/docker/volumes 目录下,在Windows中位于 C:\ProgramData\Docker\volumes 目录下。这意味着可以在Docker主机文件系统中查看卷,甚至在Docker主机中对其进行读取数据或者写入数据操作。在第9章中就有一个示例——复制某个文件到Docker主机的卷目录下,在容器该卷中立刻就能看到对应的文件。

读者可以在Docker服务以及容器中使用 myvol 卷了。例如,可以在 docker container run 命令后增加参数 --flag 将卷挂载到新建容器中。稍后通过几个例子进行说明。

有两种方式删除Docker卷。

  • docker volume prune
  • docker volume rm

docker volume prune 会删除未装入到某个容器或者服务的 所有卷 ,所以 谨慎使用docker volume rm 允许删除指定卷。两种删除命令都不能删除正在被容器或者服务使用的卷。

因为没有使用 myvol 卷,所以请通过 prune 命令进行删除。

$ docker volume prune
WARNING! This will remove all volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Volumes:
myvol
Total reclaimed space: 0B

恭喜,现在已经完成创建、查看以及删除卷的操作了。上述操作均未涉及容器,这也验证了卷是独立的这一特性。

到目前,读者已经了解了卷的创建、列表、查看以及删除命令。此外,还可以通过在Dockerfile中使用 VOLUME 指令的方式部署卷。具体的格式为 VOLUME <container-mount-point 。但是,在Dockerfile中无法指定主机目录。这是因为主机目录通常情况下是相对主机的一个目录,意味着这个目录在不同主机间会变化,并且可能导致构建失败。如果通过Dockerfile指定,那么每次部署时都需要指定主机目录。

2.演示卷在容器和服务中的使用

现在读者已经了解了卷相关的基本命令,接下来看一下如何在容器和服务中使用卷。

接下来的内容是基于某个没有卷的系统,演示内容适用于Linux和Windows。

使用下面的命令创建一个新的独立容器,并挂载一个名为 bizvol 的卷。

Linux示例如下。

$ docker container run -dit --name voltainer \
    --mount source=bizvol,target=/vol \
    alpine

Windows示例如下。

所有的Windows示例都在PowerShell中执行,请注意反引号(`)用于将命令拆至多行。

> docker container run -dit --name voltainer `
    --mount source=bizvol,target=c:\vol `
    microsoft/powershell:nanoserver

即使系统中没有叫作 bizvol 的卷,命令也应该能够成功运行。这里引出了很有意思的一点。

  • 如果指定了已经存在的卷,Docker会使用该卷。
  • 如果指定的卷不存在,Docker会创建一个卷。

在当前示例中, bizvol 这个卷并不存在,所以Docker新建一个卷并挂载到新容器内部。这意味着读者可以通过 docker volume ls 命令看到该卷。

$ docker volume ls
DRIVER              VOLUME NAME
local               bizvol

尽管容器和卷各自拥有独立的生命周期,Docker也不允许删除正在被容器使用的卷。

$ docker volume rm bizvol
Error response from daemon: unable to remove volume: volume is in use -
[b44 d3f82...dd2029ca]

目前卷是空的。执行 exec 连接到容器并向卷中写入一部分数据。示例引用的是Linux,如果读者使用Windows示例,则需要将 docker container exec 命令结尾的 sh 替换为 pwsh.exe 。其他命令在Linux和Windows上面均可以生效。

$ docker container exec -it voltainer sh
/# echo "I promise to write a review of the book on Amazon" > /vol/file1
/# ls -l /vol
total 4
-rw-r--r-- 1 root root 50 Jan 12 13:49 file1
/# cat /vol/file1
I promise to write a review of the book on Amazon

输入 exit 命令返回到Docker主机Shell中,然后使用下面命令删除容器。

$ docker container rm voltainer -f
voltainer

即使容器被删除,卷依旧存在。

$ docker container ls -a
CONTAINER ID     IMAGE    COMMAND    CREATED    STATUS
$ docker volume ls
DRIVER              VOLUME NAME
local               bizvol

由于卷仍然存在,因此可以进入到其在主机的挂载点并查看前面写入的数据是否还在。

在Docker主机的终端上执行下面的命令。第一条命令会证明文件依然存在,第二条命令展示了文件的内容。

如果是Windows示例,一定要使用 C:\ProgramData\Docker\volumes\bizvol\_data 目录。

$ ls -l /var/lib/docker/volumes/bizvol/_data/
total 4
-rw-r--r-- 1 root root 50 Jan 12 14:25 file1
$ cat /var/lib/docker/volumes/bizvol/_data/file1
I promise to write a review of the book on Amazon

太棒了,卷和数据都还在。

甚至将 bizvol 挂载到一个新的服务或者容器都是可以的。下面的命令会创建一个名为 hellcat 的新Docker服务,并且将 bizvol 挂载到该服务副本的 /vol 目录。

$ docker service create \
  --name hellcat \
  --mount source=bizvol,target=/vol \
  alpine sleep 1d
overall progress: 1 out of 1 tasks
1/1: running [====================================>]
verify: Service converged

上述命令没有指定--replicas参数,所以服务只会部署一个副本。找到Swarm集群中运行了该服务的节点。

$ docker service ps hellcat
ID         NAME         NODE     DESIRED STATE   CURRENT STATE
l3nh...    hellcat.1    node1    Running         Running 19 seconds ago

在本例中,副本运行在node1节点上。登录到node1节点,然后获取服务副本容器ID。

node1$ docker container ls
CTR ID     IMAGE            COMMAND     STATUS       NAMES
df6..a7b   alpine:latest    "sleep 1d"  Up 25 secs   hellcat.1.l3nh...

注意,容器的名称包括了 service-namereplica-number 以及 replica-ID ,采用句号分隔。

登录到该容器并检查数据是否在 /vol 中。在exec例子中会使用服务副本的容器ID。如果读者使用Windows示例,记得将 sh 替换为 pwsh.exe

node1$ docker container exec -it df6 sh
/# cat /vol/file1
I promise to write a review of the book on Amazon

这样卷中保存了原始数据,并且在新容器中也可以使用。