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

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

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 capability is overloaded; see Notes to kernel
              developers, below.

              * Perform a range of system administration operations
                including: quotactl(2), mount(2), umount(2),
                pivot_root(2), swapon(2), swapoff(2), sethostname(2),
                and setdomainname(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.AllocateResponse

 for _, req := range reqs.ContainerRequests {
  for _, id := range req.DevicesIDs {
   log.Printf("Allocate device: %s", id)
   if !deviceExists(devs, id) {
    return nil, 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 了。

责任编辑:武晓燕 来源: CS实验室
相关推荐

2021-07-21 20:24:29

Linux内核漏洞权限

2022-03-25 12:31:49

Linux根文件内核

2017-12-04 13:30:12

Linux文件系统链接

2019-01-29 10:43:59

Linux 系统 数据

2010-06-04 19:12:38

Hadoop文件系统

2020-07-22 14:53:06

Linux系统虚拟文件

2020-11-19 08:44:35

Linux

2011-12-26 15:51:36

用户访问访问管理

2011-01-13 14:10:30

Linux文件系统

2018-08-24 10:10:25

Linux文件系统技术

2019-09-20 10:04:45

Linux系统虚拟文件

2010-08-02 16:15:20

ibmdwLinux

2018-02-25 09:48:36

LinuxUbuntu文件系统

2016-10-10 09:42:23

Windows 10ReFS弹性文件

2013-05-27 14:46:06

文件系统分布式文件系统

2021-05-31 07:50:59

Linux文件系统

2022-02-24 09:39:22

Linux命令文件

2015-08-03 11:19:55

弹性文件系统AWS存储

2010-01-14 09:52:27

linux文件系统

2016-12-21 15:17:20

点赞
收藏

51CTO技术栈公众号