如何使用 docker compose v3 直接在容器中挂载 NFS 共享/卷



>我有一个带有 v3 的撰写文件,其中有 3 个服务共享/使用相同的卷。在使用 swarm 模式时,我们需要创建额外的容器和卷来管理整个集群中的服务。

我计划使用 NFS 服务器,以便将单个 NFS 共享直接挂载到群集中的所有主机上。

我找到了以下两种方法,但它需要在 docker 主机上执行额外的步骤 -

  • 在主机上使用"fstab"或"mount"命令挂载NFS共享,然后将其用作docker服务的主机卷。

  • 使用网共享插件 - https://github.com/ContainX/docker-volume-netshare

有没有一种标准方法,我可以在 docker 主机上仅执行几个/没有步骤(我知道无论如何都需要"nfs-common"包)直接使用 docker compose v3 使用 NFS 共享?

在发现这是大量未记录的之后,这是使用堆栈和 docker 组合挂载 NFS 卷的正确方法。

最重要的是您需要使用version: "3.2"或更高。如果你不这样做,你就会有奇怪和不明显的错误。

第二个问题是,当卷的定义发生变化时,卷不会自动更新。这可能会导致您陷入一个兔子洞,认为您的更改不正确,而这些更改只是尚未应用。确保docker rm VOLUMENAME可能位于的任何位置,因为如果卷存在,则不会对其进行验证。

第三个问题更像是 NFS 问题 - 如果 NFS 文件夹不存在,则不会在服务器上创建该文件夹。这就是 NFS 的工作方式。 在执行任何操作之前,您需要确保它存在。

(不要删除"soft"和"nolock",除非您确定自己知道自己在做什么 - 如果您的 NFS 服务器消失,这将阻止 docker 冻结)

下面是一个完整的示例:

[root@docker docker-mirror]# cat nfs-compose.yml
version: "3.2"
services:
rsyslog:
image: jumanjiman/rsyslog
ports:
- "514:514"
- "514:514/udp"
volumes:
- type: volume
source: example
target: /nfs
volume:
nocopy: true
volumes:
example:
driver_opts:
type: "nfs"
o: "addr=10.40.0.199,nolock,soft,rw"
device: ":/docker/example"

[root@docker docker-mirror]# docker stack deploy --with-registry-auth -c nfs-compose.yml rsyslog
Creating network rsyslog_default
Creating service rsyslog_rsyslog
[root@docker docker-mirror]# docker stack ps rsyslog
ID                  NAME                IMAGE                       NODE                DESIRED STATE       CURRENT STATE                     ERROR               PORTS
tb1dod43fe4c        rsyslog_rsyslog.1   jumanjiman/rsyslog:latest   swarm-4             Running             Starting less than a second ago
[root@docker docker-mirror]#

现在,在 swarm-4 上:

root@swarm-4:~# docker ps
CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS              PORTS               NAMES
d883e0f14d3f        jumanjiman/rsyslog:latest   "rsyslogd -n -f /e..."   6 seconds ago       Up 5 seconds        514/tcp, 514/udp    rsyslog_rsyslog.1.tb1dod43fe4cy3j5vzsy7pgv5
root@swarm-4:~# docker exec -it d883e0f14d3f df -h /nfs
Filesystem                Size      Used Available Use% Mounted on
:/docker/example          7.2T      5.5T      1.7T  77% /nfs
root@swarm-4:~#

此卷将在运行堆栈的任何 swarm 节点上创建(但不会销毁)。

root@swarm-4:~# docker volume inspect rsyslog_example
[
{
"CreatedAt": "2017-09-29T13:53:59+10:00",
"Driver": "local",
"Labels": {
"com.docker.stack.namespace": "rsyslog"
},
"Mountpoint": "/var/lib/docker/volumes/rsyslog_example/_data",
"Name": "rsyslog_example",
"Options": {
"device": ":/docker/example",
"o": "addr=10.40.0.199,nolock,soft,rw",
"type": "nfs"
},
"Scope": "local"
}
]
root@swarm-4:~#

根据我需要如何使用音量,我有以下 3 个选项。

首先,您可以直接创建命名卷,并将其用作撰写中的外部卷,或在docker rundocker service create命令中用作命名卷。

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

接下来,有--mount语法 适用于docker rundocker service create.这是一个相当长的选项,当您在另一个逗号分隔选项中嵌入逗号分隔选项时,您需要将一些引号(转义,以便 shell 不会删除它们)传递给正在运行的命令。我倾向于将其用于需要访问 NFS 的一次性容器(例如,用于设置 NFS 目录的实用程序容器):

# 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=nfs.example.com",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=nfs.example.com",volume-opt=device=:/host/path 
foo

最后,您可以在撰写文件中定义命名卷。执行此操作时的一个重要注意事项是,名称卷仅创建一次,并且不会使用任何更改进行更新。因此,如果您需要修改命名卷,则需要为其指定一个新名称。

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

在以下每个示例中:

  • 类型设置为nfs,而不是nfs4。这是因为 docker 在addr字段上提供了一些不错的功能,但仅适用于nfs类型。
  • o是传递给挂载系统调用的选项。Linux 中的挂载 syscall 和挂载命令之间的一个区别是,设备具有:移动到addr选项之前的部分。
  • nfsvers用于设置 NFS 版本。这样可以避免延迟,因为操作系统会先尝试其他 NFS 版本。
  • 当您使用type=nfs时,addr可能是 DNS 名称,而不仅仅是 IP 地址。如果您有多个 VPC 具有使用相同 DNS 名称的不同 NFS 服务器,或者如果您想在将来调整 NFS 服务器而不更新每个卷挂载,则非常有用。
  • 其他选项(如rw(读写))可以传递给o选项。
  • device字段是远程 NFS 服务器上的路径。前导冒号是必需的。这是 mount 命令如何将 IP 地址移动到系统调用的addr字段的工件。在将卷装载到容器中之前,远程主机上必须存在此目录。
  • --mount语法中,dst字段是容器内的路径。对于命名卷,您可以在docker run -v命令的卷装载右侧设置此路径(采用简短语法)。

如果您在访问远程 NFS 卷时遇到权限问题,我遇到的一个常见原因是容器以 root 身份运行,NFS 服务器设置为 root squash(将所有 root 访问权限更改为 none 用户)。您需要将容器配置为作为有权访问 NFS 服务器上目录的已知非根 UID 运行,或者禁用 NFS 服务器上的根挤压。

是的,您可以直接从撰写文件中引用 NFS:

volumes:
db-data:
driver: local
driver_opts:
type: nfs
o: addr=$SOMEIP,rw
device: ":$PathOnServer"

以类似的方式,您可以在每个主机上创建一个 nfs 卷。

docker volume create --driver local --opt type=nfs --opt o=addr=$SomeIP,rw --opt device=:$DevicePath --name nfs-docker

我的 AWS EFS 解决方案,适用于:

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

    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/

    directory 必须为空

    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 文件:

    服务业: 锡德基克: 卷: - uploads_tmp_efs:/首页/应用程序/公共/上传/TMP ... 卷: uploads_tmp_efs: 驱动程序:本地 driver_opts: 类型: NFS o: addr=[YOUR_EFS_DNS],nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 设备: [YOUR_EFS_DNS]:/

通过将驱动程序选项类型更改为 NFS4 解决了我的问题。

volumes:
my-nfs-share:
driver: local
driver_opts:
type: "nfs4"
o: "addr=172.24.0.107,rw"
device: ":/mnt/sharedwordpress"

如果您也使用 AutoFS,则可以在docker-compose上向所有路径添加:shared,如下所示:

volumes:
- /some/nfs/mounted:/path:shared

多亏了一位同事,我发现这是处理我案件的更好方法。我们的用户遇到错误,指出"符号链接太多"......

干杯!

最新更新