非特权 Pod 如何运行用户态文件系统

本文主要讲解了非特权 Pod 如何运行用户态文件系统,主要需要给与挂载所需权限即 CAP_SYS_ADMIN 并将宿主机的块设备 /dev/fuse 挂载进 Pod 中,其中挂载块设备需要做一些开发工作,实现一个 Device Plugin。然后就可以愉快地在非特权 Pod 中运行 FUSE daemon 了。
首页 新闻资讯 行业资讯 非特权 Pod 如何运行用户态文件系统

FUSE(filesystem in userspace)是指用户态的文件系统。通过 FUSE 内核模块的支持,开发者只需要根据 FUSE 提供的接口实现具体的文件操作就可以实现一个文件系统,FUSE 包含一个内核模块和一个用户空间守护进程(FUSE daemon)。内核模块加载时被注册成 Linux 虚拟文件系统的一个 FUSE 文件系统驱动,此外还注册了一个 /dev/fuse 的块设备。FUSE daemon 通过 /dev/fuse 读取请求,并将结果写入 /dev/fuse,这个 FUSE 设备就充当了 FUSE daemon 与内核通信的桥梁。

在 Kubernetes 环境中,如果需要在 Pod 中运行 FUSE daemon,通常是将其设置为特权容器。当 Pod 为特权时,自然所有的权限都会有,甚至也直接享用宿主机的设备。但非特权 Pod 想要运行用户态的文件系统有点困难,主要需要两点:

  1. 挂载权限;

  2. 对 /dev/fuse 设备的读写权限;

本篇文章主要讲解在没有特权的情况下,如何在 Pod 中运行用户态文件系统。

挂载权限

首先,mount 属于管理级别的系统调用,需要 CAP_SYS_ADMIN 权限,参考 capability 文档:

CAP_SYS_ADMIN
              Note: this capabilityisoverloaded;see Notestokernel
              developers,below.*Perform a rangeofsystem administration operations
                including: quotactl(2),mount(2),umount(2),pivot_root(2),swapon(2),swapoff(2),sethostname(2),andsetdomainname(2);

CAP_SYS_ADMIN 可以在 Pod 的 .securityContext.capabilities 中设置,如下:

securityContext:
      capabilities:add:-SYS_ADMIN

其次,有的系统开启了 Linux 内核安全模块 AppArmor,默认的 AppArmor 配置也是没有 mount 权限的,需要额外配置 mount 权限,如下:

#include <tunables/global>profile app flags=(attach_disconnected,mediate_deleted){#include <abstractions/base>mount,umount,capability sys_admin,...}

在每台节点上配置好之后,在 pod 中加入 container.apparmor.security.beta.kubernetes.io/app: localhost/app 的注解,以声明使用这份 AppArmor 配置,详细信息可以参考《如何使用 AppArmor 限制应用的权限》。

FUSE 设备

一个用户态的文件系统包含一个内核模块和一个用户空间 daemon 进程。内核模块加载时被注册成 Linux 虚拟文件系统的一个 fuse 文件系统驱动。此外,还注册了一个 /dev/fuse 的块设备。该块设备作为 fuse daemon 进程与内核通信的桥梁。而对于用户空间的 fuse daemon 来说,访问 /dev/fuse 设备是至关重要的。

在 Kubernetes 环境中,如果要将宿主机的某个块设备挂载进 pod 中,可以使用 Device Plugins。而 Device Plugins 需要第三方服务自己提供,实现起来也比较简单。

对于 FUSE 设备的 Device Plugins 来说,社区也有很多实现,不过都大同小异,只需要在 Device Plugins 接口 Allocate 中将宿主机的 /dev/fuse 目录挂载进容器的 /dev/fuse 并给与 rwm 权限即可。比如:

func(m*FuseDevicePlugin)Allocate(ctx context.Context,reqs*pluginapi.AllocateRequest)(*pluginapi.AllocateResponse,error){
 devs :=m.devs
 var responses pluginapi.AllocateResponsefor_,req :=range reqs.ContainerRequests {for_,id :=range req.DevicesIDs {
   log.Printf("Allocate device: %s",id)if!deviceExists(devs,id){returnnil,fmt.Errorf("invalid allocation request: unknown device: %s",id)}
  }
  response :=new(pluginapi.ContainerAllocateResponse)response.Devices=[]*pluginapi.DeviceSpec{
   {
    ContainerPath:"/dev/fuse",HostPath:"/dev/fuse",Permissions:"rwm",},}

  responses.ContainerResponses=append(responses.ContainerResponses,response)}return&responses,nil
}

上述 Device Plugins 的完整代码实现详见zwwhdls/node-device-plugin。

在 Pod 中使用只需要在 resources 中申明即可,比如:

apiVersion: v1
kind: Pod
metadata:
  name: fuse
spec:
  containers:-name: test
      image: centos
      command:["sleep","infinity"]resources:
        limits:
          hdls.me/fuse:"1"requests:
          hdls.me/fuse:"1"

总结

本文主要讲解了非特权 Pod 如何运行用户态文件系统,主要需要给与挂载所需权限即 CAP_SYS_ADMIN 并将宿主机的块设备 /dev/fuse 挂载进 Pod 中,其中挂载块设备需要做一些开发工作,实现一个 Device Plugin。然后就可以愉快地在非特权 Pod 中运行 FUSE daemon 了。

44    2024-11-07 09:38:43    Pod CAP 特权