쿠버네티스 노드 컴포넌트를 비root 사용자로 실행하기

쿠버네티스 노드 컴포넌트를 비root 사용자로 실행하기 (Running Kubernetes Node Components as a Non-root User)

기능 상태: Kubernetes v1.37부터 Beta.

이 문서는 kubelet, CRI, OCI, CNI 같은 쿠버네티스 노드 컴포넌트를 root 권한 없이, 사용자 네임스페이스를 사용해 실행하는 방법을 설명해요. 이 기술은 루트리스 모드(rootless mode)라고도 불려요.

참고: 이 문서는 쿠버네티스 노드 컴포넌트(그리고 따라서 파드)를 비root 사용자로 실행하는 방법을 설명해요. 단순히 파드를 비root 사용자로 실행하는 방법을 찾고 있다면 SecurityContext를 참고하세요.

출처: 문서

본문

시작하기 전에 (Before you begin)

쿠버네티스 서버가 최소 1.22 버전이어야 해요. 버전을 확인하려면 kubectl version을 입력하세요.

  • Cgroup v2 활성화
  • 사용자 세션으로 systemd 활성화
  • 호스트 Linux 배포판에 따라 몇 가지 sysctl 값 구성
  • 권한 없는 사용자가 /etc/subuid/etc/subgid에 나열되어 있는지 확인
  • KubeletInUserNamespace 기능 게이트 활성화

Rootless Docker/Podman/nerdctl 안에서 쿠버네티스 실행하기 (Running Kubernetes inside Rootless Docker/Podman/nerdctl)

kind

kind는 Rootless Docker, Rootless Podman, 또는 Rootless nerdctl 안에서 쿠버네티스를 실행하는 것을 지원해요. Running kind with Rootless Docker를 참고하세요.

minikube

minikube도 Rootless Docker 또는 Rootless Podman 안에서 쿠버네티스를 실행하는 것을 지원해요. Minikube 문서를 참고하세요.

  • Rootless Docker
  • Rootless Podman

Usernetes

참고: 이 항목은 쿠버네티스 자체가 아닌 서드파티 프로젝트나 제품을 링크해요. 자세한 내용 보기.

Usernetes도 Rootless Docker, Rootless Podman, 또는 Rootless nerdctl 안에서 쿠버네티스를 실행하는 것을 지원해요. kind와 minikube와 달리 Usernetes는 VXLAN을 사용해 여러 Docker/Podman/nerdctl 노드로 클러스터를 구성하는 것을 지원해요. 사용법은 Usernetes 문서를 참고하세요.

쿠버네티스 안에서 사용자 네임스페이스 파드로 쿠버네티스 실행하기 (Running Kubernetes inside Kubernetes, as user namespace pods)

Usernetes

참고: 서드파티 프로젝트 링크.

Usernetes는 Kubernetes-in-Docker 모드에 더해 Kubernetes-in-Kubernetes 모드를 지원해요. Kubernetes-in-Kubernetes 모드는 내부 클러스터의 노드를, 사용자 네임스페이스가 활성화된(hostUsers: false) 외부 클러스터에서 실행되는 파드로 만들어요. 사용법은 Usernetes 문서를 참고하세요.

권한 없는 컨테이너 안에서 쿠버네티스 실행하기 (Running Kubernetes inside Unprivileged Containers)

sysbox

참고: 서드파티 프로젝트 링크.

Sysbox는 오픈소스 컨테이너 런타임("runc"과 비슷)으로, Linux 사용자 네임스페이스로 격리된 권한 없는 컨테이너 안에서 Docker와 Kubernetes 같은 시스템 레벨 워크로드를 실행하는 것을 지원해요. 자세한 내용은 Sysbox Quick Start Guide: Kubernetes-in-Docker를 참고하세요.

Sysbox는 Cgroup v2와 KubeletInUserNamespace 기능 게이트 없이 권한 없는 컨테이너 안에서 쿠버네티스를 실행하는 것을 지원해요. 이것은 컨테이너 안에 특별히 조작된 /proc/sys 파일시스템을 노출하고 여러 다른 고급 OS 가상화 기술을 사용함으로써 해요.

호스트에서 직접 Rootless 쿠버네티스 실행하기 (Running Rootless Kubernetes directly on a host)

K3s

참고: 서드파티 프로젝트 링크.

K3s는 실험적으로 rootless 모드를 지원해요. 사용법은 Running K3s with Rootless mode를 참고하세요.

Usernetes (until 2023)

첫 세대의 Usernetes는 호스트에서 직접 rootless 쿠버네티스를 실행하는 것을 지원했어요. 이 모드는 2023년에 보관됐어요. 보관된 정보는 Usernetes 저장소의 gen1 브랜치를 참고하세요.

사용자 네임스페이스에서 kubelet을 실행하는 노드를 수동으로 배포하기 (Manually deploy a node that runs the kubelet in a user namespace)

이 섹션은 사용자 네임스페이스에서 쿠버네티스를 수동으로 실행하기 위한 힌트를 제공해요.

참고: 이 섹션은 쿠버네티스 배포판 개발자가 읽기 위한 것이지, 최종 사용자가 읽기 위한 것이 아니에요.

사용자 네임스페이스 만들기 (Creating a user namespace)

첫 단계는 사용자 네임스페이스를 만드는 것이에요. Rootless Docker/Podman 또는 LXC/LXD 같은 사용자 네임스페이스 컨테이너에서 쿠버네티스를 실행하려 한다면 준비된 것이므로 다음 하위 섹션으로 갈 수 있어요. 그렇지 않으면 CLONE_NEWUSERunshare(2)를 호출해 사용자 네임스페이스를 직접 만들어야 해요. 사용자 네임스페이스는 unshare(1), RootlessKit, become-root 같은 명령줄 도구로도 unshare할 수 있어요.

사용자 네임스페이스를 unshare한 후에는 mount namespace 같은 다른 네임스페이스도 unshare해야 해요. mount namespace를 unshare한 후 chroot()pivot_root()를 호출할 필요는 없지만, 네임스페이스의 여러 디렉터리에 쓰기 가능한 파일시스템을 마운트해야 해요. 최소한 다음 디렉터리는 네임스페이스 안에서(네임스페이스 밖이 아니라) 쓰기 가능해야 해요.

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

위임된 cgroup 트리 만들기 (Creating a delegated cgroup tree)

사용자 네임스페이스에 더해 cgroup v2로 쓰기 가능한 cgroup 트리도 필요해요.

참고: 노드 컴포넌트를 사용자 네임스페이스에서 실행하기 위한 쿠버네티스 지원은 cgroup v2가 필요해요. cgroup v1은 지원되지 않아요.

systemd 기반 호스트의 Rootless Docker/Podman 또는 LXC/LXD에서 쿠버네티스를 실행하려 한다면 준비된 것이에요. 그렇지 않으면 쓰기 권한이 있는 cgroup 트리를 위임하기 위해 Delegate=yes 속성의 systemd 유닛을 만들어야 해요. 노드에서 systemd가 위임을 허용하도록 이미 구성되어야 해요. 자세한 내용은 Rootless Containers 문서의 cgroup v2를 참고하세요.

네트워크 구성하기 (Configuring network)

참고: 서드파티 프로젝트 링크.

노드 컴포넌트의 네트워크 네임스페이스에 non-loopback 인터페이스가 있어야 해요. 이는 예를 들어 slirp4netns, VPNKit, 또는 lxc-user-nic(1)으로 구성할 수 있어요. 파드의 네트워크 네임스페이스는 일반 CNI 플러그인으로 구성할 수 있어요. 다중 노드 네트워킹에는 Flannel(VXLAN, 8472/UDP)이 동작하는 것으로 알려져 있어요.

kubelet 포트(10250/TCP)와 NodePort 서비스 포트 같은 포트는 RootlessKit, slirp4netns, socat(1) 같은 외부 포트 포워더로 노드 네트워크 네임스페이스에서 호스트로 노출해야 해요. K3s의 포트 포워더를 사용할 수 있어요. Running K3s in Rootless Mode에서 자세히 확인할 수 있어요. 구현은 k3s의 pkg/rootlessports 패키지에서 찾을 수 있어요.

CRI 구성하기 (Configuring CRI)

kubelet은 컨테이너 런타임에 의존해요. containerd나 CRI-O 같은 컨테이너 런타임을 배포하고, kubelet이 시작되기 전에 그것이 사용자 네임스페이스 안에서 실행되고 있는지 확인해야 해요.

containerd의 CRI 플러그인을 사용자 네임스페이스에서 실행하는 것은 containerd 1.4부터 지원돼요. 사용자 네임스페이스 안에서 containerd를 실행하려면 다음 구성이 필요해요.

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 컨트롤러 위임을 지원하지 않기 때문)
  disable_hugetlb_controller = true

[plugins."io.containerd.grpc.v1.cri".containerd]
# non-fuse overlayfs 사용도 가능 (kernel >= 5.11), SELinux 비활성화 필요
  snapshotter = "fuse-overlayfs"

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
# systemd가 위임한 cgroupfs를 사용하므로 SystemdCgroup 드라이버를 사용하지 않음
# (네임스페이스 안에서 다른 systemd를 실행하지 않는 한)
  SystemdCgroup = false

구성 파일의 기본 경로는 /etc/containerd/config.toml이에요. 경로는 containerd -c /path/to/containerd/config.toml로 지정할 수 있어요.

사용자 네임스페이스에서 CRI-O를 실행하는 것은 CRI-O 1.22부터 지원돼요. CRI-O는 환경 변수 _CRIO_ROOTLESS=1이 설정되어야 해요. 다음 구성도 권장돼요.

[crio]
  storage_driver = "overlay"
# non-fuse overlayfs 사용도 가능 (kernel >= 5.11), SELinux 비활성화 필요
  storage_option = ["overlay.mount_program=/usr/local/bin/fuse-overlayfs"]

[crio.runtime]
# systemd가 위임한 cgroupfs를 사용하므로 "systemd" 드라이버를 사용하지 않음
# (네임스페이스 안에서 다른 systemd를 실행하지 않는 한)
  cgroup_manager = "cgroupfs"

구성 파일의 기본 경로는 /etc/crio/crio.conf이에요. 경로는 crio --config /path/to/crio/crio.conf로 지정할 수 있어요.

kubelet 구성하기 (Configuring kubelet)

사용자 네임스페이스에서 kubelet을 실행하려면 다음 구성이 필요해요.

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
featureGates:
  KubeletInUserNamespace: true
# systemd가 위임한 cgroupfs를 사용하므로 "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" 상태로 도입됐어요.

이 기능 게이트 없이 사용자 네임스페이스에서 kubelet을 실행하는 것도 특별히 조작된 proc 파일시스템을 마운트함으로써(가능하지만, Sysbox가 하는 방식) 가능하지만 공식적으로 지원되지는 않아요.

kube-proxy 구성하기 (Configuring kube-proxy)

사용자 네임스페이스에서 kube-proxy를 실행하려면 다음 구성이 필요해요.

apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: "iptables" # 또는 "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

주의 사항 (Caveats)

  • nfsiscsi 같은 대부분의 "비로컬" 볼륨 드라이버는 동작하지 않아요. local, hostPath, emptyDir, configMap, secret, downwardAPI 같은 로컬 볼륨은 동작하는 것으로 알려져 있어요.
  • 일부 CNI 플러그인은 동작하지 않을 수 있어요. Flannel(VXLAN)은 동작하는 것으로 알려져 있어요.

이에 대한 더 많은 내용은 rootlesscontaine.rs 웹사이트의 Caveats and Future work 페이지를 참고하세요.

함께 보기 (See Also)

  • rootlesscontaine.rs
  • Rootless Containers 2020 (KubeCon NA 2020)
  • Running kind with Rootless Docker
  • Usernetes
  • Running K3s with rootless mode
  • KEP-2033: Kubelet-in-UserNS (aka Rootless mode)

더 알아보기 (Learn more)