1.前言

 在上一篇文章 《"三剑客”之Swarm集群架构、集群管理 、服务管理》中,大家已了解swarm集群管理以及如何管理swarm集群中的服务。试想一下,如果swarm集群中运行了mysql、nginx等服务,这些服务的数据如果没有挂载到宿主机中,那么容器一旦停止运行,那就意味着数据丢失。

 有什么方法可以解决swarm集群中运行的服务能够数据持久化呢?我们可以通过volme、bind、nfs等方法来实现swarm集群应用数据持久化,其实也和docker数据持久化的形式是一样的,下面通过几个案例一起来了解一下。

2.环境说明

本文的环境还是用上次的swarm集群环境,不过这一次多增加一台nfs服务器,如果你没有这么多服务器,可以把其中一台agent节点替换成nfs服务器也一样。

服务器 角色 运行服务 系统版本
172.18.18.32 Manager docker 17.12.0-ce、nfs客户端 centos7.4 x64
172.18.18.33 agent01 docker 17.12.0-ce 、nfs客户端 centos7.4 x64
172.18.18.34 agent02 docker 17.12.0-ce 、nfs客户端 centos7.4 x64
172.18.18.90 nfs docker 17.12.0-ce 、nfs服务端 centos7.4 x64

3.通过volume实现数据持久化

3.1 volume说明

卷是绕过联合文件系统的一个或多个容器内的特定目录。 卷被设计为保持数据,与容器的生命周期无关。 因此,Docker在删除容器时不会自动删除卷,也不会“垃圾收集”不再由容器引用的卷。 也称为:数据卷。

需要更详细了解volume可参考官方文档:https://docs.docker.com/storage/volumes/

3.2 使用格式

#docker service create \
--replicas 1或2或3... \
--name SERVICE-NAME \
--mount type=volume,src=<VOLUME-NAME>,dst=<CONNTAINER-PATH> \
<IMAGE>

3.3 案例演示

1、接下来,我们在Manager节点上,创建数据卷nginx-vol,并把卷挂载到新建的nginx服务中(/usr/share/nginx/html/),此目录为nginx容器默认首页的目录,nginx用的是docker hub的官方镜像。

[root@Manager ~]# docker service  create --replicas 2 --name web_test --mount type=volume,src=nginx-vol,dst=/usr/share/nginx/html/ nginx 
u2rhq6pn3hwf3wosmahtvy10u
overall progress: 2 out of 2 tasks 
1/2: running   [==================================================>] 
2/2: running   [==================================================>] 
verify: Service converged 

参数说明:

  • --replicas:创建2个nginx副本;
  • --name:自定义名称为web_test;
  • --mount:挂载类型为volume;
  • src:src挂载源为nginx-vol,相当于执行了docker volume nginx-vol;
  • dst:dst挂载目的路径为nginx容器中的路径/usr/share/nginx/html;
  • nginx:通过nginx镜像来运行;

2、查看一下创建的nginx副本

[root@Manager ~]# docker service ps web_test 
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
xcpbgz473trl        web_test.1          nginx:latest        Manager             Running             Running 32 seconds ago                       
qxvmgvgijtyn        web_test.2          nginx:latest        agent01             Running             Running 2 minutes ago 

可以看出创建的nginx 2个副本分别在Manager和agnet01节点上运行了。

3、查看数据卷、挂载的数据
我们通过docker volumes ls可以看到nginx-vol数据卷也自动创建了。

[root@Manager ~]# docker volume ls
DRIVER              VOLUME NAME
local               nginx-vol

在通过docker volume inspect nginx-vol可以看到nginx-vol卷自动挂载到了/var/lib/docker/volumes/nginx-vol/_data目录下

[root@Manager ~]# docker volume  inspect nginx-vol 
[
    {
        "CreatedAt": "2018-03-26T15:03:43+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/nginx-vol/_data",
        "Name": "nginx-vol",
        "Options": {},
        "Scope": "local"
    }
]

然后,查看/var/lib/docker/volumes/nginx-vol/data目录下的数据同步了,从下面结果可以得到,nginx副本中/usr/share/nginx/html目录下的数据已经同步到了宿主机nginx-vol数据卷中了:

[root@Manager /]# ls /var/lib/docker/volumes/nginx-vol/_data
50x.html  index.html

最后,在nginx-vol数据卷中随便创建个文件来验证是否会同步到nginx副本中:

[root@Manager /]# cd /var/lib/docker/volumes/nginx-vol/_data
[root@Manager _data]# touch a.txt

进入副本查看,之前创建了2个副本,我就随便进入1个副本查看,发现在宿主机nginx-vol卷中创建的a.txt已经同步到了副本的 /usr/share/nginx/html/目录下:

[root@Manager _data]# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
92453ec5728f        nginx:latest        "nginx -g 'daemon of…"   5 minutes ago       Up 5 minutes        80/tcp              web_test.2.gkt08hfffztue90pwfsgj5wn3

[root@Manager _data]# docker exec -it 92453ec5728f bash
root@92453ec5728f:/# ls /usr/share/nginx/html/ 
50x.html  a.txt  index.html

4.通过bind实现数据持久化

4.1 bind说明

与卷相比,(bind)绑定挂到具有有限的功能。 当您使用绑定挂载时,宿主机上的文件或目录被挂载到容器中。 文件或目录由宿主机上的完整路径或相对路径引用(宿主机上的文件或目录要存在)。
相比之下,当您使用卷时,会在宿主机上的Docker存储目录中创建一个新目录,并且Docker会管理该目录的内容。该文件或目录不需要已经存在于Docker宿主机上。 如果它尚不存在,它会根据需求创建。 绑定挂载非常高效,但它们依赖于具有特定目录结构的主机的文件系统。

详细了解可参考官方文档:https://docs.docker.com/storage/bind-mounts/

4.2 使用格式

1、读写挂载格式

#docker service create \
--replicas 1或2或3... \
--name SERVICE-NAME \
--mount type=bind,src=<VOLUME-NAME>,dst=<CONNTAINER-PATH> \
<IMAGE>

2、只读挂载格式

#docker service create \
--replicas 1或2或3... \
--name SERVICE-NAME \
--mount type=bind,src=<VOLUME-NAME>,dst=<CONNTAINER-PATH>,ro  \
<IMAGE>

是不是发现和volume挂载一样的,只不过在type挂载类型变为bind而以,只读挂载还需要在上ro选项,默认为读写挂载,不需要加rw参数。

4.3 案例演示

1、我们还是在Manager节点上,创建数据挂载目录/data/nginx,也是挂载到新建的nginx服务中(/usr/share/nginx/html/)。需要注意的是,我们需要在swarm集群所有节点上(Manager、agent)都要创建/data/nginx目录,因为Manager在创建nginx副本的时候是随机分配的。

[root@Manager ~]# mkdir -p /data/nginx/
[root@agent01 ~]# mkdir -p /data/nginx/
[root@agnet02 ~]# mkdir -p /data/nginx/

[root@Manager ~]#docker service  create --replicas 2 --name web_test02 --mount type=bind,src=/data/nginx/,dst=/usr/share/nginx/html/ nginx 

参数和volume挂载类似的,不做详细的说明了,大家一看就能明白。

2、查看一下创建的nginx副本

[root@Manager ~]# docker service ps web_test02 
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE           ERROR               PORTS
c2k6dnbzqct2        web_test02.1        nginx:latest        agent01             Running             Running 3 minutes ago                       
89gn5fjxzicr        web_test02.2        nginx:latest        agnet02             Running             Running 3 minutes ago     

可以看到,创建的2个副本根据默认策略分别分配到了agnet01和agnet02节点上。

3、进入agnet01或agnet02节点上查看下

[root@agent01 ~]# docker ps -l
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
f2dbadb50332        nginx:latest        "nginx -g 'daemon of…"   5 minutes ago       Up 5 minutes        80/tcp              web_test02.1.c2k6dnbzqct2s2pftjed6ftvy
[root@agent01 ~]# docker exec -it f2dbadb50332 bash
root@f2dbadb50332:/# ls /usr/share/nginx/html/

在agent01节点上,进入副本后发现 /usr/share/nginx/html/目录为空,这是正常的,刚才我们在创建/data/nginx目录时只是空目录,并没有数据,所以现在来制造点数据验证一下。

4、制造数据

[root@agent01 ~]# cd /data/nginx/
[root@agent01 nginx]# touch  bind.txt
[root@agent01 nginx]# docker exec -it f2dbadb50332 bash
root@f2dbadb50332:/# ls /usr/share/nginx/html/
bind.txt

在agent01宿主机/data/nginx/目录中新建了个文件,是不是发现副本中的目录也同步了;有些人可能会问了,那我是不是也要在agent02上也需要手动创建数据,如果还有其它节点(agent03、agnet04...等等),那岂不是每台swarm节点上要手动创建,多麻烦啊。接下来,我们通过nfs来解决这个麻烦,只需要搭建nfs服务器,swarm所有节点为nfs客户端。这样只需要在nfs服务端上创建数据即可。

5.通过nfs实现数据持久化

NFS(Network File System)即网络文件系统,是FreeBSD支持的文件系统中的一种,它允许网络中的计算机之间通过TCP/IP网络共享资源。在NFS的应用中,本地NFS的客户端应用可以透明地读写位于远端NFS服务器上的文件,就像访问本地文件一样。

5.1 环境部署

nfs服务器:创建/nfs目录
nfs客户端:不需要在nfs客户端提前mount,在Manager上创建副本的时候指定参数自动mount到nfs服务端

1、nfs服务端部署

[root@nfs ~]# yum -y install nfs-utils
[root@nfs ~]# mkdir /nfs
[root@nfs ~]# vim /etc/exports
/nfs 172.18.18.0/24(rw,sync,no_root_squash)
[root@nfs ~]# systemctl  restart nfs

nfs参数说明:

  • /nfs: nfs上的共享目录;
  • 172.18.18.0/24:网络中可以访问这个NFS输出目录的主机;
  • rw:共享目录的权限,rw 是可读写的权限,只读的权限是ro;
  • sync:同步,数据更安全,速度慢;
  • async:异步,速度快,效率高,安全性低;
  • no_root_squash:NFS 服务共享的目录的属性, 如果用户是root, 对这个目录就有root的权限;

2、nfs客户端部署
在swarm集群所有节点安装部署:

[root@Manager ~]#  yum -y install nfs-utils
[root@agent01 ~]#  yum -y install nfs-utils
[root@agent02 ~]#  yum -y install nfs-utils

5.2 使用格式

#docker service create \
--replicas 1或2或3... \
--name myservice \
--mount \
'type=volume,src=<VOLUME-NAME>,dst=<CONTAINER-PATH>, \
volume-driver=local,volume-opt=type=nfs,volume-opt=device=<nfs-server>:<nfs-path>, \
"volume-opt=o=addr=<nfs-address>,vers=4,soft,timeo=180,bg,tcp,rw"'  \
<IMAGE>

5.3 案例演示

1、我们还是在Manager上创建nginx副本来演示,同样把nfs共享目录:/nfs挂载到副本的(/ysr/share/nginx/html)目录中。

[root@Manager ~]# docker service create --replicas 3 --name web-nfs --mount 'type=volume,src=nfs-vol,dst=/usr/share/nginx/html,volume-driver=local,volume-opt=type=nfs,volume-opt=device=:/nfs,"volume-opt=o=addr=172.18.18.90,vers=4,soft,timeo=180,bg,tcp,rw"'  nginx 

参数说明:

  • --replicas:创建3个nginx副本;
  • --name:自定义命名为web-nfs;
  • type=volume:必须为volume挂载,不能用bind的挂载方式;
  • src:src挂载源为nginx-vol,相当于执行了docker volume nginx-vol;
  • dst:dst挂载目的路径为nginx容器中的路径/usr/share/nginx/html;
  • volume-opt=type=nfs:指定挂载卷类型为nfs模式;
  • volume-opt=device=:/nfs:这是nfs服务器共享目录的名称;
  • "volume-opt=o=addr=172.18.18.90:指定nfs服务器地址;
  • vers=4,soft,timeo=180,bg,tcp,rw:指定nfs版本号、超时时间、后台挂载、协议可选TCP/UDP、读写权限等信息;
  • nginx:通过nginx镜像来运行;

2、查看副本运行信息

[root@Manager ~]# docker service ps web-nfs 
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
q67t67ucdba2        web-nfs.1           nginx:latest        agent01             Running             Running 12 minutes ago                       
3cs5jh8chegz        web-nfs.2           nginx:latest        Manager             Running             Running 10 minutes ago                       
0hykerf7n3a4        web-nfs.3           nginx:latest        agnet02             Running             Running 13 minutes ago                       

3个nginx副本分别分配到了swarm集群的3个节点上。

3、df 查看文件挂载信息,省略无用信息

[root@Manager ~]# df -h
Filesystem               Size  Used Avail Use% Mounted on
...
:/nfs                     50G  9.6G   41G  20% /var/lib/docker/volumes/nfs-vol/_data
...

你在其它几个agnet节点上df查看也同样有这些信息。

4、回到nfs服务端,创建测试数据

[root@nfs nfs]# cd /nfs/
[root@nfs nfs]# touch  nfs.html

5、进入swarm中所有节点,查看数据是否同步
进入Manager查看:

[root@Manager ~]# docker ps -l
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
f176e66dc9f2        nginx:latest        "nginx -g 'daemon of…"   14 minutes ago      Up 14 minutes       80/tcp              web-nfs.2.3cs5jh8chegz4ncid8qd2as8q

[root@Manager ~]# docker exec -it f176e66dc9f2 bash
root@f176e66dc9f2:/# ls /usr/share/nginx/html/
50x.html  index.html  nfs.html

进入agnet01查看,agent02就不演示了,大家自己查看一下:

[root@agent01 ~]# docker ps -l
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
b9b7dcc5bdfa        nginx:latest        "nginx -g 'daemon of…"   17 minutes ago      Up 17 minutes       80/tcp              web-nfs.1.q67t67ucdba2c7b5srkm9b58n

[root@agent01 ~]# docker exec -it b9b7dcc5bdfa bash
root@b9b7dcc5bdfa:/# ls /usr/share/nginx/html/
50x.html  index.html  nfs.html

6、用docker volume ls验证数据卷

[root@Manager ~]# docker volume ls
DRIVER              VOLUME NAME
local               nfs-vol
local               nginx-vol

[root@agent01 ~]# docker volume ls
DRIVER              VOLUME NAME
local               nfs-vol
local               nginx-vol

7、通过rm删除副本验证

[root@Manager ~]# docker service  rm web-nfs 
[root@Manager ~]#df -h

我们把刚才创建的副本删除掉,然后通过df -h查看挂载的文件,发现也跟着不在了,其它节点也一样不存在了;然后你回到nfs服务端查看/nfs共享目录,数据是不变的。这就表现了,通过nfs挂载,副本被删除,不影响nfs服务端的源数据。只要数据在nfs服务端,就可实现数据的持久化。

小结

在使用挂载类型上,一般优先建议使用volume(卷)挂载,不要使用bind(绑定)挂载,卷是持久化由Docker容器生成和使用的数据的首选机制。 虽然绑定挂载依赖于主机的目录结构,但卷由Docker完全管理。 与绑定安装相比,卷有几个优点:

  • 与绑定挂载相比,卷更容易备份或迁移。
  • 您可以使用Docker CLI命令或Docker API管理卷。
  • 卷在Linux和Windows容器上均可使用。
  • 卷可以在多个容器之间更安全地共享。
  • 卷驱动程序允许您在远程主机或云提供程序上存储卷,加密卷的内容或添加其他功能。
  • 新卷的内容可以由容器预先填充。

本文的内容就到此结束,喜欢我的文章,请点击最上方右角处的《关注》!!!