(正在探索)Docker Desktop for mac部署kind容器通过 SSH Tunnel 使用共享NFS卷

我在 (未成功)Docker Desktop for mac部署kind容器使用共享NFS卷 折腾了很久也没有成功,原因是 Docker Desktop for mac使用了Linux VM来运行Docker容器,导致和物理主机macOS隔离, Docker 捆绑挂载 到容器内部后无法作为文件系统NFS输出。

Docker Desktop for mac 端口转发(port forwarding) 实现了在物理主机 macOS 直接SSH登陆到 kind(本地docker模拟k8s集群) 运行的pod中的容器,那么,带来一种可能:

准备工作

部署 macOS系统NFS服务 共享

采用 macOS系统NFS服务 部署一个共享的 NFS 输出 docs :

  • macOS 物理主机上启动NFS服务:

macOS主机上启动NFS服
sudo nfsd enable
  • 检查nfs服务:

使用rpcinfo检查本机的portmapper
sudo rpcinfo -p

输出信息:

使用rpcinfo检查本机的portmapper的输出信息
   program vers proto   port
    100000    2   udp    111  rpcbind
    100000    3   udp    111  rpcbind
    100000    4   udp    111  rpcbind
    100000    2   tcp    111  rpcbind
    100000    3   tcp    111  rpcbind
    100000    4   tcp    111  rpcbind
    100024    1   udp    980  status
    100024    1   tcp   1021  status
    100021    0   udp    976  nlockmgr
    100021    1   udp    976  nlockmgr
    100021    3   udp    976  nlockmgr
    100021    4   udp    976  nlockmgr
    100021    0   tcp   1017  nlockmgr
    100021    1   tcp   1017  nlockmgr
    100021    3   tcp   1017  nlockmgr
    100021    4   tcp   1017  nlockmgr
    100003    2   udp   2049  nfs
    100003    3   udp   2049  nfs
    100003    2   tcp   2049  nfs
    100003    3   tcp   2049  nfs
    100011    1   udp    604  rquotad
    100011    2   udp    604  rquotad
    100011    1   tcp   1003  rquotad
    100011    2   tcp   1003  rquotad
    100005    1   udp    685  mountd
    100005    3   udp    685  mountd
    100005    1   tcp   1023  mountd
    100005    3   tcp   1023  mountd
  • 配置 /etc/exports :

配置物理主机 macOS/etc/exports 设置NFS输出目录
#如果不配置客户端的IP地址范围,似乎只有相同子网的客户端能够访问,跨网段NFS服务器会拒绝:
#  mount.nfs: mount(2): Permission denied
#  mount.nfs: access denied by server while mounting 172.22.0.12:/Users/huataihuang/docs/studio

# 配置过于简单,没有配置NFS客户端IP地址范围
#/Users/huataihuang/docs/studio -maproot=501:20

# 增加NFS客户端IP范围
/Users/huataihuang/docs/studio -maproot=501:20 -network 172.22.0.0 -mask 255.255.0.0

备注

-maproot=501:20 将远程root用户映射到本地用户 uid=501,gid=20 ,这样才能读写目录

  • 重启nfs服务:

重启 macOS 的nfs服务
sudo nfsd restart
  • 在网络中一台Linux主机上尝试挂载一次NFS卷验证:

    sudo mkdir /studio
    sudo mount -t nfs <macos_nfs_server_ip>:/Users/huataihuang/docs/studio /studio
    

如果没有异常挂载成功,则在 macOS 服务端检查共享:

检查服务器端输出挂载(已经在Linux挂载NFS)
showount -a

输出可以看到远程的NFS客户端IP地址以及挂载本机的卷信息:

All mounts on localhost:
192.168.6.200:/Users/huataihuang/docs/studio

准备就绪,接下来就可以结合 SSH Tunneling: 远程服务端口转发 将NFS输出给 Docker Desktop for mac 虚拟机中运行的 kind(本地docker模拟k8s集群) 容器挂载NFS了

SSH Tunneling: 远程服务端口转发 打通访问NFS

macOS NFS端口列表

端口

协议

说明

111

udp

rpcbind

111

tcp

rpcbind

980

udp

status

1021

tcp

status

976

udp

nlockmgr

1017

tcp

nlockmgr

2049

udp

nfs

2049

tcp

nfs

789

udp

rquotad

952

tcp

rquotad

615

udp

mountd

958

tcp

mountd

配置macOS物理主机用户目录 ~/.ssh/config 添加 dev-gw 远程服务器端口转发配置
Host *
    ServerAliveInterval 60
    ControlMaster auto
    ControlPath ~/.ssh/%h-%p-%r
    ControlPersist yes
    Compression yes

Host dev-gw
    HostName 127.0.0.1
    Port 122
    User admin
    IdentityFile ~/.ssh/id_rsa_fedora-dev
    RemoteForward 111 127.0.0.1:111
    RemoteForward 685 127.0.0.1:685
    RemoteForward 1003 127.0.0.1:1003
    RemoteForward 1017 127.0.0.1:1017
    RemoteForward 1021 127.0.0.1:1021
    RemoteForward 2049 127.0.0.1:2049

备注

中间转发服务器 dev-gw/etc/ssh/sshd_config 必须配置:

GatewayPorts yes

否则 SSH Tunneling: 远程服务端口转发 时,该服务器转发端口只监听回环地址 127.0.0.1 ,就会导致远程NFS客户端无法访问

测试

在Docker容器 ubuntu 测试客户端测试 Ubuntu NFS部署 :

  • 安装Ubuntu的NFS客户端:

Ubuntu安装NFS客户端
sudo apt install nfs-common
  • 挂载NFS服务器测试,注意,NFS服务器使用 dev-gwdocker network 中的网络IP 172.22.0.12 ,而服务器端的目录则是 macOS 物理主机前面配置的 macOS系统NFS服务 输出目录:

    mkdir /studio
    mount -t nfs 172.22.0.12:/Users/huataihuang/docs/studio /studio
    

这里有一个提示错误:

mount.nfs: rpc.statd is not running but is required for remote locking.
mount.nfs: Either use '-o nolock' to keep locks local, or start statd.
mount.nfs: Operation not permitted

修改参数,添加 -o nolock

mount -t nfs -o nolock 172.22.0.12:/Users/huataihuang/docs/studio /studio

此时提示错误:

mount.nfs: Operation not permitted

这个问题在 Docker容器使用NFS 已经排查过,是因为Docker容器安全限制,需要在运行容器时添加参数 --cap-add sys_admin

运行 fedora-dev-tini 容器命令添加 --cap-add sys_admin 实现容器内NFS挂载权限
docker run -itd -p 1122:22 --network kind \
        --cap-add sys_admin \
        --hostname fedora-dev-tini --name fedora-dev-tini fedora-dev-tini

测试挂载:

mkdir /stuido
mount -t nfs -o nolock,nfsvers=3,tcp 172.22.0.12:/Users/huataihuang/docs/studio /studio

非常奇怪,我在 dev-gw 服务器上观察发现没有任何连接到NFS相关监听端口,而 fedora-dev-tini 过了很久只显示一个输出:

mount.nfs: Connection refused
  • fedora-dev-tini 主机上检查 telnet 172.22.0.12 111 端口是打开的

  • 检查 rpcinfo

    # rpcinfo -p 172.22.0.12
    172.22.0.12: RPC: Remote system error - Connection refused
    

发现 dev-gw 拒绝了连接,但是在 dev-gw 上使用:

$ netstat -an
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 0.0.0.0:2049            0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:1017            0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:1021            0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:1003            0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:685             0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:111             0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.11:35477        0.0.0.0:*               LISTEN
...

可以看到端口是监听的,但是没有客户端连接

尝试 systemd NFS客户端

之前在 Docker容器使用NFS 中提到:

方法一:在容器内部安装 nfs-utils ,就如同常规到NFS客户端一样,在容器内部直接通过rpcbind方式挂载NFS共享输出,这种方式需要每个容器独立运行rpcbind服务,并且要使用 Docker systemd进程管理器 ,复杂且消耗较多资源。不过,优点是完全在容器内部控制,符合传统SA运维方式。

备注

kind(本地docker模拟k8s集群) 在node节点是使用了完整的 Systemd进程管理器 来实现的,所以部署 在Kubernetes中部署NFS 是满足节点提供 rpcbind 服务的。

对啊,要在Docker容器中测试NFS挂载,必须也是运行 Systemd进程管理器 的Docker容器

  • 重新部署 Fedora镜像 (内置 Systemd进程管理器 ) ,使用镜像是 fedora-ssh ,执行以下运行命令:

    docker run -itd --privileged=true -p 1122:22 --network kind \
        --hostname fedora-ssh --name fedora-ssh fedora-ssh
    

备注

Fedora 配置NFS方法采用 CentOS 7 配置NFS 相同客户端方式

  • 安装NFS客户端:

    dnf install nfs-utils
    
  • 果然,使用 Systemd进程管理器 的Docker容器,挂载时会自动启动 rpc-statd.service

    # mount -t nfs 172.22.0.12:/Users/huataihuang/docs/studio /studio
    Created symlink /run/systemd/system/remote-fs.target.wants/rpc-statd.service → /usr/lib/systemd/system/rpc-statd.service.
    mount.nfs: Protocol not supported
    

不过,还是报错:

mount.nfs: Protocol not supported

排查方法参考 mount.nfs: requested NFS version or transport protocol is not supported 添加 -vvvv 参数可以详细输出信息

# mount -t nfs -vvvv 172.22.0.12:/Users/huataihuang/docs/studio /studio
mount.nfs: timeout set for Thu Feb  2 00:01:08 2023
mount.nfs: trying text-based options 'vers=4.2,addr=172.22.0.12,clientaddr=172.22.0.13'
mount.nfs: mount(2): Protocol not supported
mount.nfs: trying text-based options 'vers=4,minorversion=1,addr=172.22.0.12,clientaddr=172.22.0.13'
mount.nfs: mount(2): Protocol not supported
mount.nfs: trying text-based options 'vers=4,addr=172.22.0.12,clientaddr=172.22.0.13'
mount.nfs: mount(2): Protocol not supported
mount.nfs: trying text-based options 'addr=172.22.0.12'
mount.nfs: prog 100003, trying vers=3, prot=6
mount.nfs: trying 172.22.0.12 prog 100003 vers 3 prot TCP port 2049
mount.nfs: prog 100005, trying vers=3, prot=17
mount.nfs: portmap query retrying: RPC: Unable to receive - Connection refused
mount.nfs: prog 100005, trying vers=3, prot=6
mount.nfs: trying 172.22.0.12 prog 100005 vers 3 prot TCP port 975
mount.nfs: portmap query failed: RPC: Remote system error - Connection refused
mount.nfs: Protocol not supported

通过 -vvvv 可以看到,客户端 portmap 会尝试 RPC ,失败:

mount.nfs: portmap query retrying: RPC: Unable to receive - Connection refused

然后尝试TCP端口 975 也失败(之前漏配置SSH):

mount.nfs: trying 172.22.0.12 prog 100005 vers 3 prot TCP port 975
mount.nfs: portmap query failed: RPC: Remote system error - Connection refused

例如,我改为 -o vers=3,tcp 就看到客户端会连接服务器端口:

mount.nfs: timeout set for Thu Feb  2 00:05:18 2023
mount.nfs: trying text-based options 'vers=3,tcp,addr=172.22.0.12'
mount.nfs: prog 100003, trying vers=3, prot=6
mount.nfs: trying 172.22.0.12 prog 100003 vers 3 prot TCP port 2049
mount.nfs: prog 100005, trying vers=3, prot=6
mount.nfs: trying 172.22.0.12 prog 100005 vers 3 prot TCP port 975
mount.nfs: portmap query failed: RPC: Remote system error - Connection refused
...

既然端口 975 少了,我重新修订 ~/.ssh/config 添加上 975端口,输出信息果然变化:

# mount -t nfs -vvvv -o vers=3,tcp 172.22.0.12:/Users/huataihuang/docs/studio /studio
mount.nfs: timeout set for Thu Feb  2 00:11:32 2023
mount.nfs: trying text-based options 'vers=3,tcp,addr=172.22.0.12'
mount.nfs: prog 100003, trying vers=3, prot=6
mount.nfs: trying 172.22.0.12 prog 100003 vers 3 prot TCP port 2049
mount.nfs: prog 100005, trying vers=3, prot=6
mount.nfs: trying 172.22.0.12 prog 100005 vers 3 prot TCP port 975
mount.nfs: mount(2): Permission denied
mount.nfs: access denied by server while mounting 172.22.0.12:/Users/huataihuang/docs/studio

看起来是被macOS的NFS服务器拒绝了

参考 Enabling network (NFS) shares in Mac OS X 看来需要设置macOS NFS服务器端的允许IP地址段

之前Linux物理服务器客户端测试,客户端IP地址和服务器端在同一个网段,似乎没有问题。但是这次是Docker容器,网段是 172.22.0.0/16 似乎就不行

修改 /etc/exports

/Users/huataihuang/docs/studio -maproot=501:20 -network 172.22.0.0 -mask 255.255.0.0

再次挂载NFS,就不报告 access denied 了,改为:

# mount -t nfs -vvvv -o vers=3,tcp 172.22.0.12:/Users/huataihuang/docs/studio /studio
mount.nfs: timeout set for Thu Feb  2 00:26:01 2023
mount.nfs: trying text-based options 'vers=3,tcp,addr=172.22.0.12'
mount.nfs: prog 100003, trying vers=3, prot=6
mount.nfs: trying 172.22.0.12 prog 100003 vers 3 prot TCP port 2049
mount.nfs: portmap query failed: RPC: Unable to receive - Connection reset by peer
mount.nfs: Connection reset by peer

portmap query failed: RPC: Unable to receive - Connection reset by peer 是什么意思呢?

mount.nfs: portmap query failed: RPC: Unable to receive - Connection refused in Centos 7 client 提供了一个排查思路:

rpcinfo -l <nfsserver> 100003 3

我这里实践:

# rpcinfo -l 172.22.0.12 100003 3
   program vers  tp_family/name/class    address               service
    100003  3    inet/udp/clts           172.22.0.12.8.1          nfs
    100003  3    inet/tcp/cots_ord       172.22.0.12.8.1          nfs

我想起来以前尝试过穿透防火墙设置NFS,需要特殊的固定端口配置,否则挂载时,服务器端和客户端协商会打开随机端口进行连接,此时就会被防火墙阻塞。这个问题在我的 SSH Tunneling 也存在

参考 NFS client mount fails behind firewall 看来还需要在服务器端做不少配置来来确保服务器端返回连接客户端不采用随机端口…

Fixing Ports Used by NFSv3 Server 介绍了Linxu上如何配置固定端口 /etc/sysconfig/nfs ,不知道有没有办法移植到 macOS 上

参考