首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
SkyPilot fuse-proxy 深度解析:在无特权 Kubernetes 容器中安全挂载 FUSE 文件系统
📅 2026/9/16 9:01:30
✍️ 爱科研究院
👁 阅读 3,247
SkyPilot fuse-proxy 深度解析在无特权 Kubernetes 容器中安全挂载 FUSE 文件系统【免费下载链接】skypilotThe AI Compute Platform for frontier teams. SkyPilot turns fragmented AI compute into one AI supercomputer, so frontier AI teams build custom intelligence faster.项目地址: https://gitcode.com/GitHub_Trending/sk/skypilot导读SkyPilot 的fuse-proxy是 addons 目录下的一个独立 Go 组件它通过“客户端伪装 特权代理服务”的架构让运行在 Kubernetes 普通 Pod 中的 SkyPilot 应用如 gcsfuse、rclone、blobfuse 等 FUSE 适配器无需 root 权限与SYS_ADMINcapability 即可完成 FUSE 挂载。本文以 addons/fuse-proxy/README.md 为骨架结合该仓库内的 Go 源码、DaemonSet 清单与 SkyPilot 的集成代码讲解其工作原理、部署步骤、两种代理模式fusermount-shim 与 fusermount-wrapper以及底层实现的源码级细节。读完本文你将理解 FUSE 在容器化环境中的权限困境、fuse-proxy 的解决思路并能独立完成该组件的构建、部署与应用集成。背景为什么容器里挂 FUSE 是个难题在 Kubernetes 中向容器挂载 FUSE 文件系统如 gcsfuse、s3fs、blobfuse、rclone通常需要两类特权挂载操作本身需要权限fusermount需要访问/dev/fuse设备并在目标 mount namespace 中执行mount(2)系统调用这要求进程具有 root 权限或SYS_ADMINcapability设备访问受限即使容器能以 root 运行很多集群的安全策略如 PodSecurity Admission、自定义准入控制器会禁止授予SYS_ADMIN导致 mount 失败。SkyPilot 的目标是在任意 Kubernetes 集群上运行 AI 工作负载其任务往往需要挂载存储卷例如通过 FUSE 挂载对象存储或共享文件系统。为了让 SkyPilot Pod 保持“非特权”状态同时仍然能够使用 FUSESkyPilot 引入了fuse-proxy组件。从源码注释可以看到其设计意图addons/fuse-proxy/cmd/fusermount-shim/main.gofusermount-shim是一个二进制它替换非特权容器中的fusermount二进制并将 fusermount 请求转发到运行在特权容器中的 fusermount server。核心思想就是让非特权 Pod 里的挂载请求外包给节点上一个特权代理进程来完成。架构总览客户端 Shims 特权 Daemonfuse-proxy由两部分组成见 addons/fuse-proxy/README.md组件角色运行位置fusermount-shim伪装fusermount二进制拦截并转发 fusermount 调用SkyPilot 应用容器非特权fusermount-wrapper包装直接使用libfuse的 FUSE 适配器预挂载后把 fd 交给适配器SkyPilot 应用容器非特权fusermount-server在调用者的 mount namespace 中执行真正的fusermount并通过SCM_RIGHTS传回已挂载的 fdKubernetes 节点上的特权 DaemonSet Pod客户端与服务端通过Unix Domain Socket通信因此 SkyPilot Pod 与 DaemonSet Pod 必须共享宿主机上的一个目录默认/var/run/fusermount该目录同时承载server.sockUnix Socket 通信端点fusermount-shim/fusermount-wrapper服务端在启动时会把这两个二进制复制到共享目录中供应用容器通过符号链接引用见 addons/fuse-proxy/cmd/fusermount-server/main.go。共享目录路径可以通过环境变量FUSERMOUNT_SHARED_DIR覆盖某些 FUSE 实现如 gcsfuse不会把环境变量传递给 fusermount 二进制因此代码中保留了常量路径/var/run/fusermount作为兜底见 addons/fuse-proxy/pkg/common/common.go。通信协议要点客户端与服务端的请求/响应结构定义在 addons/fuse-proxy/pkg/common/common.go// Request represents a fusermount request type Request struct { Args []string json:args } // Response represents a fusermount response type Response struct { Success bool json:success Error string json:error,omitempty }一次完整的挂载调用过程见 addons/fuse-proxy/pkg/client/client.go为客户端打开自己的 mount namespaceos.Open(/proc/self/ns/mnt)得到 ns fd客户端把{Args}请求 JSON 序列化后连同 ns fd 一起通过SendMsgSCM_RIGHTS发给服务端服务端在调用者的 mount namespace 中执行真实fusermount服务端把已挂载的/dev/fusefd 通过SendMsg传回客户端客户端把 fd 通过_FUSE_COMMFD交给上层的 FUSE 适配器。服务端在启动时监听server.sock并把 socket 权限设为0666以允许所有用户连接见 addons/fuse-proxy/pkg/server/server.go。两种代理模式根据 FUSE 适配器的挂载方式fuse-proxy提供两种模式。Proxy Modefusermount-shim适用于 gcsfuse 等对于通过调用fusermount二进制完成挂载的适配器典型代表是 gcsfuse使用fusermount-shim模式。其流程为FUSE 适配器进程在 Sky 容器中启动执行挂载命令由于 PATH 中的fusermount已被符号链接到fusermount-shim适配器实际执行的是 shimshim 解析参数将请求转发给特权 Daemon PodDaemon Pod 识别调用者的 mnt namespace用nsenter --mountns在调用者的 namespace 中执行真正的fusermount完成挂载Daemon Pod 通过 Unix Socket 把挂载好的文件描述符传回 FUSE 适配器适配器拿到 fd 后开始正常读写。README 中的时序图完整描绘了这一过程shim 的实现细节addons/fuse-proxy/cmd/fusermount-shim/main.go 完整实现了与libfuse中fusermount命令行兼容的参数解析-h/-V/-u/-q/-z/-o。关键点通过-u判断是挂载还是卸载卸载请求同样转发给服务端执行挂载时shim 从环境变量_FUSE_COMMFD读取调用方FUSE 实现传入的通信 fd待服务端返回挂载成功的 fd 后将其通过该 comm fd 送回给调用方如果环境变量_FUSE_COMMFD未设置会直接 panic 报错见 addons/fuse-proxy/pkg/common/common.go。服务端的挂载处理addons/fuse-proxy/pkg/server/server.go 中的handleFusermount是核心逻辑用nsenter --mount/proc/self/fd/nsFd进入调用者的 mount namespace调用ensureFuseDevice确保 namespace 中存在/dev/fuse若不存在则用mknod以字符设备major10minor229创建并chmod 666见 server.go通过socketpair创建 fd 传递通道把其中一个 socket fd 作为ExtraFilesfd 3传给 fusermount并设置环境变量_FUSE_COMMFD3执行nsenter ... fusermount argsfusermount会通过 comm fd 把挂载好的/dev/fusefd 传回服务端服务端再把该 fd 经客户端连接传回调用者。服务端收到 fd 后会主动关闭自己持有的副本避免fuse_dev_release计数不归零导致 FUSE 连接无法销毁见 server.go 的注释这是一个针对 rclone 挂载在进程退出时请求卡死的问题修复。同时服务端收到 ns fd 后也会及时关闭防止持有 mnt namespace 引用而阻塞临时存储清理见 server.go。客户端与服务端的挂载/卸载判断服务端通过 addons/fuse-proxy/pkg/server/flags.go 中的isUmount判断请求是否为卸载只要参数中出现以-开头且包含u的参数或显式出现--unmount即视为卸载操作此时无需回传 fd。Wrapper Modefusermount-wrapper适用于 blobfuse 等对于直接使用libfuse挂载 FUSE 设备的适配器典型代表是 Azure Blob Storage 的blobfusefusermount-shim会失效原因在于见 READMElibfuse只有在 mount 操作失败时才会回退到fusermount而open /dev/fuse系统调用本身在容器内就要求 root 权限因此在回退发生之前挂载进程已经失败了。fusermount-wrapper的解决方案是addons/fuse-proxy/cmd/fusermount-wrapper/main.go在启动 FUSE 适配器之前调用fusermount-server完成挂载把挂载好的 fd 传给 FUSE 适配器进程libfuse会发现已挂载的 fd 并直接使用libfuse在fuse_lowlevel.c中有对应发现逻辑。wrapper 会把适配器命令中的占位符默认{}替换为/dev/fd/N并通过ExtraFiles把挂载好的 fd 注入子进程这样libfuse就会直接复用该 fd 而不再尝试打开/dev/fuse见 main.go。wrapper 的命令行参数Usage: fusermount-wrapper -m /path/to/mount-point -- FUSE_IMPL_CMD [FUSE_IMPL_FLAGS] {}参数说明默认值-m, --mount-pointFUSE 设备的挂载点路径必填-p, --placeholderFUSE 实现命令中要替换的占位符{}-o, --options挂载选项空-d, --daemonize将 FUSE 适配器进程 daemon 化false其他实现细节fd 注入wrapper 使用cmd.Run()而非syscall.Exec执行适配器命令确保ExtraFiles能正确传递给子进程见 main.go失败清理如果适配器进程启动失败wrapper 会尝试执行fusermount -u mount-point卸载已挂载的设备并以相同的退出码退出见 main.godaemonize 模式-d模式下父进程监控子进程若 5 秒内子进程未退出则判定就绪并返回 0该超时对齐了 azure blobfuse2 的挂载超时见 main.go 的注释说明从代码注释看这是一种hacky的就绪判断官方已标注 TODO 计划改为向 server 查询挂载状态卸载职责wrapper 本身不负责卸载因为libfuse卸载时会正常回退到fusermount3最终由fusermount-shim转发处理见 main.go。部署与使用1. 构建 server 镜像在addons/fuse-proxy目录下执行docker build . -t fusermount-server:latestDockerfile 使用多阶段构建基于golang:1.23-alpine编译三个二进制fusermount-server、fusermount-shim、fusermount-wrapper最终镜像基于alpine:3.19安装util-linux提供nsenter和fuse包并以 root 运行。2. 以特权 DaemonSet 部署 server仓库提供了完整的部署清单 sky/provision/kubernetes/manifests/fusermount-server-daemonset.yaml要点如下privileged: trueDaemonSet Pod 运行在特权模式这是它能在其他容器的 mount namespace 中执行nsenter、创建/dev/fuse设备的前提Tolerations容忍所有节点的NoSchedule和NoExecute污点保证每个节点都有 server Pod共享目录hostPath挂载/var/run/fusermountDirectoryOrCreate同时通过环境变量FUSERMOUNT_SHARED_DIR/var/run/fusermount告知 server 共享目录位置资源与探针请求 50m CPU / 50Mi 内存并通过livenessProbe定期检查server.sock是否存在test -S /var/run/fusermount/server.sockinitialDelaySeconds: 10、periodSeconds: 5、failureThreshold: 10。构建后的镜像被推送到berkeleyskypilot/fusermount-server:0.2.2部署清单中引用的镜像 tag。SkyPilot 通过 GitHub Actions 工作流.github/workflows/fusermount-server-image.yaml自动构建并发布该镜像。3. 在应用 Pod 中挂载共享目录应用 Pod 需要挂载与 DaemonSet 相同的宿主机目录spec: containers: - name: main volumeMounts: - mountPath: /var/run/fusermount name: fusermount-shared-dir volumes: - hostPath: path: /var/run/fusermount type: DirectoryOrCreate name: fusermount-shared-dir注意Pod 与 DaemonSet 必须位于同一节点才能通过宿主机目录通信。SkyPilot 内部会确保这一点共享目录路径常量定义在 sky/provision/kubernetes/fuse.py 中注释明确指向本 README。4. 用 shim 伪装fusermount二进制在 init-container 或应用容器的 init 脚本中执行以下步骤# Install fuse2 and fuse3 apt-get install -y fuse fuse3 # Locate the original fusermount binary. FUSERMOUNT_PATH$(which fusermount) if [ -z $FUSERMOUNT_PATH ]; then echo Error: fusermount binary not found exit 1 fi FOURSMOUNE3_PATH$(which fusemount3) if [ -z $FOURSMOUNE3_PATH ]; then echo Error: fusemount3 binary not found exit 1 fi # The -original suffix is crucial: the server will enter the mnt namespace of application container and find the original fusermount binary # by searching fusermount-original executable in PATH. cp -p $FUSERMOUNT_PATH ${FUSERMOUNT_PATH}-original # Mask the fusermount/fusemount3 binary by the shim provided by fusermount-server. ln -sf /var/run/fusermount/fusermount-shim $FUSERMOUNT_PATH ln -sf /var/run/fusermount/fusermount-shim $FOURSMOUNE3_PATH为什么-original后缀至关重要服务端在nsenter进入应用容器的 mount namespace 后需要调用真正的 fusermount 来执行挂载。由于 PATH 中的fusermount已被符号链接替换为 shim服务端通过-fusermount-path参数默认/bin/fusermount-original见 addons/fuse-proxy/cmd/fusermount-server/main.go寻找原始二进制——如果你把原始二进制复制为${FUSERMOUNT_PATH}-original则其路径形如/bin/fusermount-original或/usr/bin/fusermount-original。源码中的 TODO 注释也承认这是hacky做法服务端假定调用者容器中的特定知识fusermount 路径未来计划寻找更优雅的方案。SkyPilot 中的自动化集成SkyPilot 实际使用时的 Pod 侧设置远比上述手写脚本健壮。在 sky/provision/kubernetes/fuse.py 中get_fusermount_shim_setup_command生成了完整的设置命令其增强点包括使用command -v而非whichRHEL/UBI/Rocky 等最小化镜像不含which并用|| true保证错误信息可读对fusermount3使用|| true容忍其缺失缺失时回退到fusermount3位于${FUSERMOUNT_PATH}3的路径同时把fusermount-wrapper也链接到/bin/fusermount-wrapper以支持 blobfuse2 等 libfuse 适配器内置wait_for_fusermount等待逻辑最长 60 秒如果 DaemonSet 刚刚部署或节点刚启动、server Pod 尚未就绪等待其把 shim 二进制复制到共享目录处理 hostPath 以noexec挂载的情况此时符号链接方式执行 shim 会失败脚本会退化为直接把 shim 二进制cp到容器内并执行fusermount -V验证。构建与开发辅助仓库为开发者提供了 Go 项目的常规开发命令见 addons/fuse-proxy/Makefilemake fmt # go fmt ./... make vet # go vet ./... make tidy # go mod tidy go mod verify make gofumpt # 安装并运行 mvdan.cc/gofumpt make reviewable # fmt vet tidy gofumpt并检查 git diff 是否干净go.mod中的依赖体现了关键技术选型k8s.io/klog/v2日志、spf13/pflag命令行解析、sevlyar/go-daemondaemon 化以及pfnet-research/meta-fuse-csi-plugin/pkg/utilSCM_RIGHTSfd 传递的底层工具函数。致敬与设计渊源README 的 Acknowledgements 明确指出本组件受 k8s-fuse-csi-plugin 启发——fd 传递、namespace 识别等底层机制pkg/util包直接复用了该项目的实现。这一设计选择让 fuse-proxy 能够聚焦于 SkyPilot 特有的容器侧集成与适配器兼容性问题而不是重新发明内核接口。小结fuse-proxy以客户端 shim 拦截 特权 server 代理执行 SCM_RIGHTS fd 传递的组合拳解决了 Kubernetes 非特权容器使用 FUSE 的核心矛盾fusermount-shim面向调用fusermount的适配器gcsfuse 等对上层完全透明fusermount-wrapper面向直接使用libfuse的适配器blobfuse 等通过预挂载 fd 注入绕过对/dev/fuse的 root 访问需求服务端负责在正确的 mount namespace 中执行真实挂载并处理/dev/fuse设备创建、权限设置、fd 生命周期等关键细节。这一组件让 SkyPilot 可以在不放松 Pod 安全策略的前提下保持存储卷挂载能力的完整性——既符合最小权限的安全最佳实践又不牺牲对象存储等 FUSE 后端的易用性。需要进一步探索时可以从 fuse-proxy 源码目录 的三个cmd入口、pkg 的实现以及 SkyPilot 侧的 fuse.py 集成逻辑入手形成完整的技术闭环。【免费下载链接】skypilotThe AI Compute Platform for frontier teams. SkyPilot turns fragmented AI compute into one AI supercomputer, so frontier AI teams build custom intelligence faster.项目地址: https://gitcode.com/GitHub_Trending/sk/skypilot创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/9/16 9:01:30
开发者如何用Reels短剧高效传播技术内容
2026/9/16 9:01:30
WSL2 原生 Docker Engine 安装与踩坑指南:告别 Docker Desktop 的性能损耗
2026/9/16 9:01:30
多时间尺度源储荷协调调度三层模型与Matlab linprog实现
2026/9/16 9:46:48
Simulink零基础实现卷积编码与Viterbi译码
2026/9/16 9:46:48
研究生必备:8款实测有效的降AI率工具与使用策略
2026/9/16 9:46:48
BDMA.zip_BDMA固件解析:嵌入式系统中伪ZIP格式的DMA加速配置包
2026/9/16 9:46:48
医疗智能客服系统架构设计与AI应用实践
2026/9/16 9:46:48
Spring容器管理Bean的三种核心方式详解
2026/9/16 9:41:48
Arduino UNO Q热成像+路径规划实现山火隔离带智能生成
2026/9/16 0:00:15
嵌入式三大高薪赛道:车规功能安全、RISC-V固件架构、边缘AI部署
2026/9/16 0:00:15
Zephyr 移植指南:SAM R34 Xplained Pro(samr34_xpro)评估板支持与 LoRa 开发实战
2026/9/16 0:00:15
纯HTML+SVG图解工具:出版级架构图的语义化生成方案
2026/9/15 13:08:25
拯救者Y7000黑屏故障排查与维修实战指南
2026/9/16 7:38:03
AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验
2026/9/16 1:54:57
Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化