如何使用 docker compose v3 在容器中直接挂载 NFS share/volume
How to directly mount NFS share/volume in container using docker compose v3
我有一个 v3 的组合文件,其中有 3 个服务 sharing/using 相同的卷。在使用 swarm 模式时,我们需要创建额外的容器和卷来管理我们在集群中的服务。
我打算使用 NFS 服务器,这样单个 NFS 共享将直接挂载到集群中的所有主机上。
我发现了以下两种方法,但需要在 docker 主机上执行额外的步骤 -
在主机上使用 "fstab" 或 "mount" 命令挂载 NFS 共享,然后将其用作 docker 服务的主机卷。
使用 Netshare 插件 - https://github.com/ContainX/docker-volume-netshare
有没有一种标准方法可以让我直接 use/mount NFS 共享使用 docker 编写 v3 仅执行 few/no 步骤(我知道 "nfs-common" 包是必需的无论如何)在 docker 主机上?
是的,您可以直接从撰写文件中引用 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
在发现大量未记录后,这是使用堆栈和 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:~#
此卷将在堆栈 运行 所在的任何群节点上创建(但不会销毁)。
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:~#
我的 AWS EFS 解决方案有效:
- Create EFS(别忘了在安全组打开NFS 2049端口)
安装nfs-common包:
sudo apt-get install -y nfs-common
检查您的 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 must be empty
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/
file 1.txt must exists
配置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]:/
我的问题已通过将驱动程序选项类型更改为 NFS4 得到解决。
volumes:
my-nfs-share:
driver: local
driver_opts:
type: "nfs4"
o: "addr=172.24.0.107,rw"
device: ":/mnt/sharedwordpress"
根据我需要如何使用音量,我有以下3个选项。
首先,您可以直接创建命名卷并将其用作组合中的外部卷,或用作docker run
或docker 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 run
和 docker service create
。这是一个相当长的选项,当您在另一个逗号分隔选项中嵌入一个逗号分隔选项时,您需要将一些引号(转义以便 shell 不会删除它们)传递给命令 运行.我倾向于将其用于需要访问 NFS 的 one-off 容器(例如,用于设置 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
是传递给挂载系统调用的选项。 mount 系统调用和 Linux 中的 mount 命令之间的一个区别是设备将 :
之前的部分移到了 addr
选项中。
nfsvers
用于设置NFS版本。这避免了延迟,因为 OS 首先尝试其他 NFS 版本。
当您使用 addr
时,addr
可能是一个 DNS 名称,而不仅仅是一个 IP 地址。如果您有多个 VPC 以及使用相同 DNS 名称的不同 NFS 服务器,或者如果您想在未来调整 NFS 服务器而不更新每个卷挂载,这将非常有用。
- 其他选项如
rw
(read-write) 可以传递给 o
选项。
device
字段是远程NFS服务器上的路径。前导冒号是必需的。这是 mount 命令如何将 IP 地址移动到系统调用的 addr
字段的产物。在将卷装入容器之前,该目录必须存在于远程主机上。
- 在
--mount
语法中,dst
字段是容器内的路径。对于命名卷,您可以在 docker run -v
命令中将此路径设置在卷装载的右侧(在短语法中)。
如果您在访问远程 NFS 卷时遇到权限问题,我遇到的一个常见原因是容器 运行 以 root 身份运行,NFS 服务器设置为 root squash(将所有 root 访问权限更改为 nobody用户)。您需要将容器配置为 运行 作为众所周知的 non-root UID,可以访问 NFS 服务器上的目录,或者在 NFS 服务器上禁用 root squash。
如果您也在使用 AutoFS,在 docker-compose
上您可以将 :shared
添加到所有路径,如下所示:
volumes:
- /some/nfs/mounted:/path:shared
感谢一位同事,我发现这是处理我的案例的更好方法。我们的用户出现错误 'too many symbolic links'...
干杯!
我有一个 v3 的组合文件,其中有 3 个服务 sharing/using 相同的卷。在使用 swarm 模式时,我们需要创建额外的容器和卷来管理我们在集群中的服务。
我打算使用 NFS 服务器,这样单个 NFS 共享将直接挂载到集群中的所有主机上。
我发现了以下两种方法,但需要在 docker 主机上执行额外的步骤 -
在主机上使用 "fstab" 或 "mount" 命令挂载 NFS 共享,然后将其用作 docker 服务的主机卷。
使用 Netshare 插件 - https://github.com/ContainX/docker-volume-netshare
有没有一种标准方法可以让我直接 use/mount NFS 共享使用 docker 编写 v3 仅执行 few/no 步骤(我知道 "nfs-common" 包是必需的无论如何)在 docker 主机上?
是的,您可以直接从撰写文件中引用 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
在发现大量未记录后,这是使用堆栈和 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:~#
此卷将在堆栈 运行 所在的任何群节点上创建(但不会销毁)。
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:~#
我的 AWS EFS 解决方案有效:
- Create EFS(别忘了在安全组打开NFS 2049端口)
安装nfs-common包:
sudo apt-get install -y nfs-common
检查您的 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 must be empty
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/
file 1.txt must exists
配置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]:/
我的问题已通过将驱动程序选项类型更改为 NFS4 得到解决。
volumes:
my-nfs-share:
driver: local
driver_opts:
type: "nfs4"
o: "addr=172.24.0.107,rw"
device: ":/mnt/sharedwordpress"
根据我需要如何使用音量,我有以下3个选项。
首先,您可以直接创建命名卷并将其用作组合中的外部卷,或用作docker run
或docker 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 run
和 docker service create
。这是一个相当长的选项,当您在另一个逗号分隔选项中嵌入一个逗号分隔选项时,您需要将一些引号(转义以便 shell 不会删除它们)传递给命令 运行.我倾向于将其用于需要访问 NFS 的 one-off 容器(例如,用于设置 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
是传递给挂载系统调用的选项。 mount 系统调用和 Linux 中的 mount 命令之间的一个区别是设备将:
之前的部分移到了addr
选项中。nfsvers
用于设置NFS版本。这避免了延迟,因为 OS 首先尝试其他 NFS 版本。
当您使用 addr
时,addr
可能是一个 DNS 名称,而不仅仅是一个 IP 地址。如果您有多个 VPC 以及使用相同 DNS 名称的不同 NFS 服务器,或者如果您想在未来调整 NFS 服务器而不更新每个卷挂载,这将非常有用。- 其他选项如
rw
(read-write) 可以传递给o
选项。 device
字段是远程NFS服务器上的路径。前导冒号是必需的。这是 mount 命令如何将 IP 地址移动到系统调用的addr
字段的产物。在将卷装入容器之前,该目录必须存在于远程主机上。- 在
--mount
语法中,dst
字段是容器内的路径。对于命名卷,您可以在docker run -v
命令中将此路径设置在卷装载的右侧(在短语法中)。
如果您在访问远程 NFS 卷时遇到权限问题,我遇到的一个常见原因是容器 运行 以 root 身份运行,NFS 服务器设置为 root squash(将所有 root 访问权限更改为 nobody用户)。您需要将容器配置为 运行 作为众所周知的 non-root UID,可以访问 NFS 服务器上的目录,或者在 NFS 服务器上禁用 root squash。
如果您也在使用 AutoFS,在 docker-compose
上您可以将 :shared
添加到所有路径,如下所示:
volumes:
- /some/nfs/mounted:/path:shared
感谢一位同事,我发现这是处理我的案例的更好方法。我们的用户出现错误 'too many symbolic links'...
干杯!