以非root用户身份运行 Kubernetes 节点组件

FEATURE STATE: Kubernetes v1.22 [alpha]

这个文档描述了怎样不使用 root 特权,而是通过使用 用户命名空间 去运行 Kubernetes 节点组件(例如 kubelet、CRI、OCI、CNI)。

这种技术也叫做 rootless 模式(Rootless mode)

准备开始

您的 Kubernetes 服务器版本必须不低于版本 1.22. 要获知版本信息,请输入 kubectl version.

使用 Rootless 模式的 Docker/Podman 运行 Kubernetes

kind

kind 支持使用 Rootless 模式的 Docker 或者 Podman 运行 Kubernetes。

请参阅使用 Rootless 模式的 Docker 运行 kind

minikube

minikube 也支持使用 Rootless 模式的 Docker 运行 Kubernetes。

请参阅 Minikube 文档中的 docker 驱动页面。

它不支持 Rootless 模式的 Podman。

直接在主机上运行 Rootless 模式的 Kubernetes

K3s

K3s 实验性支持了 Rootless 模式。

请参阅使用 Rootless 模式运行 K3s 页面中的用法.

Usernetes

Usernetes 是 Kubernetes 的一个参考发行版, 它可以在不使用 root 特权的情况下安装在 $HOME 目录下。

Usernetes 支持使用 containerd 和 CRI-O 作为 CRI 运行时。 Usernetes 支持配置了 Flannel (VXLAN)的多节点集群。

关于用法,请参阅 Usernetes 仓库

手动部署一个在用户命名空间运行 kubelet 的节点

本节提供在用户命名空间手动运行 Kubernetes 的注意事项。

创建用户命名空间

第一步是创建一个 用户命名空间

如果你正在尝试使用用户命名空间的容器(例如 Rootless 模式的 Docker/Podman 或 LXC/LXD) 运行 Kubernetes,那么你已经准备就绪,可以直接跳到下一小节。

否则你需要通过传递参数 CLONE_NEWUSER 调用 unshare(2),自己创建一个命名空间。

用户命名空间也可以通过如下所示的命令行工具取消共享:

在取消命名空间的共享之后,你也必须对其它的命名空间例如 mount 命名空间取消共享。

在取消 mount 命名空间的共享之后,你需要调用 chroot() 或者 pivot_root(), 但是你必须在这个命名空间内挂载可写的文件系统到几个目录上。

请确保这个命名空间内(不是这个命名空间外部)至少以下几个目录是可写的:

  • /etc
  • /run
  • /var/logs
  • /var/lib/kubelet
  • /var/lib/cni
  • /var/lib/containerd (参照 containerd )
  • /var/lib/containers (参照 CRI-O )

创建委派 cgroup 树

除了用户命名空间,你也需要有一个版本为 cgroup v2 的可写 cgroup 树。

如果你在一个采用 systemd 机制的主机上使用用户命名空间的容器(例如 Rootless 模式的 Docker/Podman 或 LXC/LXD)来运行 Kubernetes,那么你已经准备就绪。

否则你必须创建一个具有 Delegate=yes 属性的 systemd 单元,来委派一个具有可写权限的 cgroup 树。

在你的节点上,systemd 必须已经配置为允许委派。更多细节请参阅 Rootless 容器文档的 cgroup v2 部分。

配置网络

节点组件的网络命名空间必须有一个非本地回路的网卡。它可以使用 slirp4netnsVPNKitlxc-user-nic(1) 等工具进行配置。

Pod 的网络命名空间可以使用常规的 CNI 插件配置。对于多节点的网络,已知 Flannel (VXLAN、8472/UDP) 可以正常工作。

诸如 kubelet 端口(10250/TCP)和 NodePort 服务端口之类的端口必须通过外部端口转发器 (例如 RootlessKit、 slirp4netns 或 socat(1)) 从节点网络命名空间暴露给主机。

你可以使用 K3s 的端口转发器。更多细节请参阅 在 Rootless 模式下运行 K3s

配置 CRI

kubelet 依赖于容器运行时。你需要部署一个容器运行时(例如 containerd 或 CRI-O), 并确保它在 kubelet 启动之前已经在用户命名空间内运行。

containerd 1.4 开始支持在用户命名空间运行 containerd 的 CRI 插件。

在用户命名空间运行 containerd 需要在 /etc/containerd/containerd-config.toml 文件包含以下配置:

version = 2

[plugins."io.containerd.grpc.v1.cri"]
# 禁用 AppArmor
  disable_apparmor = true
# 忽略配置 oom_score_adj 时的错误
  restrict_oom_score_adj = true
# 禁用 hugetlb cgroup v2 控制器(因为 systemd 不支持委派 hugetlb controller)
  disable_hugetlb_controller = true

[plugins."io.containerd.grpc.v1.cri".containerd]
# 如果内核 >= 5.11 , 也可以使用 non-fuse overlayfs, 但需要禁用 SELinux
  snapshotter = "fuse-overlayfs"

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
# 我们使用的 cgroupfs 已经被 systemd 委派,所以我们不使用 SystemdCgroup 驱动
# (除非你在命名空间内运行了另一个 systemd)
  SystemdCgroup = false

CRI-O 1.22 开始支持在用户命名空间运行 CRI-O。

CRI-O 必须配置一个环境变量 _CRIO_ROOTLESS=1

也推荐使用 /etc/crio/crio.conf 文件内的以下配置:

[crio]
  storage_driver = "overlay"
# 如果内核 >= 5.11 , 也可以使用 non-fuse overlayfs, 但需要禁用 SELinux
  storage_option = ["overlay.mount_program=/usr/local/bin/fuse-overlayfs"]

[crio.runtime]
# 我们使用的 cgroupfs 已经被 systemd 委派,所以我们不使用 "systemd" 驱动
# (除非你在命名空间内运行了另一个 systemd)
  cgroup_manager = "cgroupfs"

配置 kubelet

在用户命名空间运行 kubelet 必须进行如下配置:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
featureGates:
  KubeletInUserNamespace: true
# 我们使用的 cgroupfs 已经被 systemd 委派,所以我们不使用 "systemd" 驱动
# (除非你在命名空间内运行了另一个 systemd)
cgroupDriver: "cgroupfs"

KubeletInUserNamespace 特性门控被启用时, kubelet 会忽略节点内由于配置如下几个 sysctl 参数值而可能产生的错误。

  • vm.overcommit_memory
  • vm.panic_on_oom
  • kernel.panic
  • kernel.panic_on_oops
  • kernel.keys.root_maxkeys
  • kernel.keys.root_maxbytes.

在用户命名空间内, kubelet 也会忽略任何由于打开 /dev/kmsg 而产生的错误。 这个特性门控也允许 kube-proxy 忽略由于配置 RLIMIT_NOFILE 而产生的一个错误。

KubeletInUserNamespace 特性门控从 Kubernetes v1.22 被引入, 标记为 "alpha" 状态。

通过挂载特制的 proc 文件系统,也可以在不使用这个特性门控的情况下在用户命名空间运行 kubelet,但这不受官方支持。

配置 kube-proxy

在用户命名空间运行 kube-proxy 需要进行以下配置:

apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: "iptables" # or "userspace"
conntrack:
# 跳过配置 sysctl 的值 "net.netfilter.nf_conntrack_max"
  maxPerCore: 0
# 跳过配置 "net.netfilter.nf_conntrack_tcp_timeout_established"
  tcpEstablishedTimeout: 0s
# 跳过配置 "net.netfilter.nf_conntrack_tcp_timeout_close"
  tcpCloseWaitTimeout: 0s

注意事项

  • 大部分“非本地”的卷驱动(例如 nfsiscsi)不能正常工作。 已知诸如 localhostPathemptyDirconfigMapsecretdownwardAPI 这些本地卷是能正常工作的。

  • 一些 CNI 插件可能不正常工作。已知 Flannel (VXLAN) 是能正常工作的。

更多细节请参阅 rootlesscontaine.rs 站点的 Caveats and Future work 页面。

另请参见

Items on this page refer to third party products or projects that provide functionality required by Kubernetes. The Kubernetes project authors aren't responsible for those third-party products or projects. See the CNCF website guidelines for more details.

You should read the content guide before proposing a change that adds an extra third-party link.

最后修改 November 12, 2021 at 1:52 PM PST : [zh]translate. fix issue #30217 (#30403) (efbbe7f18)