Docker Swarm如何实现卷共享?

136

Docker Swarm可以管理两种类型的存储:

volumebind

尽管Docker文档不建议使用bind,因为它会在每个Swarm Node上创建一个本地目录与任务之间的绑定,但是关于volume实现没有提到,因此我不明白卷是如何在任务之间共享的?

  • Docker Swarm如何在节点之间共享卷?
  • 卷保存在哪里(在管理器上?如果有多个管理器呢?)
  • 如果它在不同的机器和不同的网络上运行,节点之间是否会出现问题?
  • 它是否会创建VPN?

1
Swarm是否共享卷?大约一年前我处理过docker swarm,但我认为swarm不负责在节点之间共享卷。如果您希望节点共享相同的卷,则必须使用诸如Azure VolumeDriver之类的卷插件。 - Munchkin
对我们而言有效的方法是在节点之间使用lsync... - Jose Pato
5个回答

100

Swarm Mode本身对卷不会做任何不同的处理,它在容器运行的节点上运行您提供的任何卷挂载命令。如果您的卷挂载是局部于该节点的,则数据将保存在该节点上。没有内置的功能可以自动在节点之间移动数据。

有一些软件基础的分布式存储解决方案,例如GlusterFS、Rook、Ceph和Longhorn。其中许多都专注于与Kubernetes集成,在Swarm中无法使用。

典型的结果是您需要在应用程序中管理存储的复制(例如etcd和其他raft算法),或者您在外部存储系统上执行挂载(希望它具有自己的高可用性)。挂载外部存储系统有两个选项,块或基于文件。基于块的存储(例如EBS)通常具有更高的性能,但仅限于在单个节点上挂载。为此,通常需要第三方卷插件驱动程序,以使您的Docker节点可访问该块存储。基于文件的存储(例如EFS)具有较低的性能,但更便携,并且可以同时在多个节点上挂载,这对于复制服务非常有用。

最常见的基于文件的网络存储是NFS(这与EFS使用的协议相同)。您甚至可以在没有任何第三方插件驱动程序的情况下挂载它。Docker提供的不幸命名为“本地”的卷插件驱动程序使您可以使用驱动程序选项传递任何值,并且如果没有选项,则默认将卷存储在docker目录/var/lib/docker/volumes中。通过选项,您可以将其传递给NFS参数,并且它甚至会对NFS主机名执行DNS查找(通常情况下您无法使用NFS执行此操作)。以下是使用本地卷驱动程序挂载NFS文件系统的不同方法的示例:

  # create a reusable volume
  $ docker volume create --driver local \
      --opt type=nfs \
      --opt o=nfsvers=4,addr=192.168.1.1,rw \
      --opt device=:/path/to/dir \
      foo

  # or from the docker run command
  $ docker run -it --rm \
    --mount type=volume,dst=/container/path,volume-driver=local,volume-opt=type=nfs,\"volume-opt=o=nfsvers=4,addr=192.168.1.1\",volume-opt=device=:/host/path \
    foo

  # or to create a service
  $ docker service create \
    --mount type=volume,dst=/container/path,volume-driver=local,volume-opt=type=nfs,\"volume-opt=o=nfsvers=4,addr=192.168.1.1\",volume-opt=device=:/host/path \
    foo

  # inside a docker-compose file
  ...
  volumes:
    nfs-data:
      driver: local
      driver_opts:
        type: nfs
        o: nfsvers=4,addr=192.168.1.1,rw
        device: ":/path/to/dir"
  ...

如果您使用结尾处的组合文件示例,请注意,卷的更改(例如更新服务器路径或地址)在现有命名卷中存在时不会反映出来。 您需要重命名卷或删除它,以允许swarm重新创建具有新值的卷。

我在大多数NFS使用中看到的另一个常见问题是启用服务器上的“root squash”。这会导致权限问题,当作为root运行的容器尝试将文件写入卷时。 您还将面临类似的UID / GID权限问题,其中容器UID / GID是需要具有写入卷权限的权限,这可能需要在NFS服务器上调整目录所有权和权限。


非常好的回答,谢谢。不过你提到的9个缩写词中,我只知道3-4个(GA、EE、HA、EBS、EFS、NFS、DNS、UID、GID)。能否给它们定义一下,这会很有用。 - Lee
1
其中一些引用已经过时,所以我将它们删除了。EBS和EFS是AWS服务,如果您不熟悉AWS,请忽略它们。理解NFS、UID和GID是设置网络存储解决方案的先决条件,并且最好通过文档和博客文章来完成。 - BMitch
据我所了解,MinIO是一种可以自行托管的EBS,类似于AWS服务中的那种,用于存储对象而不是原始文件(也许你不会在回答中提到以免更加混淆)。 - undefined

88
你所询问的是一个常见问题。卷数据及其功能由卷驱动程序管理。就像你可以使用不同的网络驱动程序,如overlaybridgehost,你也可以使用不同的卷驱动程序。
Docker和Swarm只提供标准的local驱动程序。它没有Swarm意识,并且会在您的服务任务安排在哪个节点上时为您的数据创建新的卷。这通常不是您想要的。
你需要一个第三方驱动程序插件,它具有Swarm意识,并确保你为服务任务创建的卷在正确的时间出现在正确的节点上。选项包括使用"Docker for AWS/Azure"及其附带的CloudStor驱动程序,或流行的开源REX-Ray解决方案。
有许多第三方卷驱动程序,您可以在Docker Store上找到。

1
Hadoop可以作为这样一个共享卷吗? - stackit

22

我们本地托管的Swarm解决方案如下:每个工作节点都安装了一个nfs-share,由我们的文件服务器提供在/mnt/docker-data下。当我在服务组合文件中定义卷时,我将设备设置为/mnt/docker-data下某个路径,例如:

volumes:
  traefik-logs:
    driver: local
    driver_opts:
      o: bind
      device: /mnt/docker-data/services/traefik/logs
      type: none

使用这种解决方案,Docker会在部署服务的每个节点创建卷,并且令人惊讶的是已经存在数据,因为它是通过卷在其他节点上使用的相同路径。

如果您仔细查看节点文件系统,则会发现将我的文件服务器挂载到/var/lib/docker/volumes下,如下所示:

root@node-3:~# df -h
Dateisystem                                                                                                   Größe Benutzt Verf. Verw% Eingehängt auf
[...]
fs.mydomain.com:/srv/shares/docker-data/services/traefik/logs                                 194G    141G   53G   73% /var/lib/docker/volumes/traefik_traefik-logs/_data

这个在混合群集中能工作吗?例如,Windows节点。 - rantlr
我不这么认为。 - Tommaso Mazza

12

我对AWS EFS的解决方案,已经能够正常工作:

  1. 创建EFS(不要忘记在安全组中打开NFS端口2049)
  2. 安装nfs-common软件包:

    sudo apt-get install -y nfs-common

  3. 检查您的EFS是否正常工作:

    mkdir efs-test-point
    sudo chmod go+rw efs-test-point
    sudo mount -t nfs -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport [YOUR_EFS_DNS]:/ efs-test-point
    touch efs-test-point/1.txt
    sudo umount efs-test-point/
    ls -la efs-test-point/

    目录必须为空

    sudo mount -t nfs -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport [YOUR_EFS_DNS]:/ efs-test-point

    ls -la efs-test-point/

    文件1.txt必须存在

  4. 配置docker-compose.yml文件:

    services:
      sidekiq:
        volumes:
          - uploads_tmp_efs:/home/application/public/uploads/tmp
      ...
    volumes:
      uploads_tmp_efs:
        driver: local
        driver_opts:
          type: nfs
          o: addr=[YOUR_EFS_DNS],nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2
          device: [YOUR_EFS_DNS]:/


2
默认情况下,Swarm将始终寻找本地卷驱动程序,因此最好的方法是:
  1. 创建NFS共享,即yum -y install nfs-utils
  2. 在/etc/exports中导出如下所示的内容:/root/nfshare 192.168.1.0/24(rw,sync,no_root_squash)
  3. 打开所需端口,在我的情况下,我执行了以下操作:firewall-cmd --permanent --add-service mountd; firewall-cmd --permanent --add-service rpc-bind; firewall-cmd --permanent --add-service nfs; firewall-cmd --zone=public --permanent --add-port 2049/tcp
  4. 在Docker工作节点上挂载新创建的共享,然后
  5. docker service create --name my-web --replicas 3 -p 80:80 --mount 'type=volume,source=nfshare,target=/usr/share/nginx/html/,volume-driver=local,volume-opt=type=nfs,volume-opt=device=:/root/nfshare,"volume-opt=o=addr=192.168.1.8,rw"' nginx:latest
  6. 在上面的示例中,我在192.168.1.8主机上创建了nfshare并使用/etc/exports文件进行了导出
  7. 启动守护进程:systemctl start nfs-server rpcbind & systemctl enable nfs-server rpcbind
  8. exportfs -r以使更改生效
  9. /root/nfshare有我的自己的index.html
  10. 仔细检查卷驱动程序条目,它也可以是外部的,而且它对我起作用了
  11. 更多参考https://docs.docker.com/storage/volumes/

我不喜欢答案中提到的所有NFS解决方案。这会造成单点故障。要么使用分布式文件系统,例如GlusterFS,要么尝试摆脱对分布式卷的需求(例如使用支持共享文件的数据库,例如MongoDB中的GridFS)。 - Csongor Fagyal
2
我喜欢GridFS的想法。对我来说,GlusterFS在部署到Digital Ocean节点时会不断断开和重置所有内容,是一个完全的glusterf*。这是一个巨大的单点故障。我看了一下GridFS,并读到了以下内容:“如果您需要原子方式更新整个文件的内容,请勿使用GridFS。”因此,如果您计划更新文件,例如SSL证书,必须有更好的选择... - Jimbo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接