※ 本メモの更新版が https://zenn.dev/orimanabu/articles/k8s-v130-user-namespace-poc にあります
k8s v1.30でBetaになったuser namespaceをFedora 40で試してみました。
下記のドキュメントに沿って環境を作って検証します。
2024-04-25時点の情報をもとにしています。
現時点では、user namespaceを試すには下記が必要になります。
- Linuxカーネル v6.3 (tmpfsのidmapマウント)
- crun v1.9以降 (v1.13以降が推奨)
- runc v1.1.zは現時点では未対応
- CRI-O v1.25以降
- containerd v1.7は、k8s v1.27〜v1.30のuser namespaceには未対応
本検証では、下記を使用しました。
$ cat /etc/fedora-release
Fedora release 40 (Forty)
$ uname -r
6.8.7-300.fc40.x86_64
$ rpm -q kubeadm kubelet cri-o crun
kubeadm-1.30.0-150500.1.1.x86_64
kubelet-1.30.0-150500.1.1.x86_64
cri-o-1.30.0~dev-150500.75.1.x86_64
crun-1.14.4-1.fc40.x86_64
KVMホスト上に、Fedora Server 40の仮想マシンを3個作ります (control plane x 1, worker x 2)。
sudo touch /etc/systemd/zram-generator.conf
sudo firewall-cmd --add-port 6443/tcp --permanent
sudo firewall-cmd --add-port 10250/tcp --permanent
sudo firewall-cmd --reload
echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/98-k8s.conf
sudo sysctl -p --system
(※ 検証をする上で、この手順は必ずしも実施しなくても構いません)
ユーザー kubelet
を作成し、ここ
に記載されているように 65536 * 110
個のUIDを使えるようにします。
sudo useradd kubelet
デフォルト設定だと、使えるUIDは65536個です。
$ cat /etc/subuid
ori:524288:65536
kubelet:589824:65536
kubelet
ユーザーが使えるUIDを 65536 * 110
個に増やします。
sudo sed -i.orig -E -e 's/^(kubelet:[0-9]+):65536/\1:7208960/' /etc/subuid
sudo sed -i.orig -E -e 's/^(kubelet:[0-9]+):65536/\1:7208960/' /etc/subgid
$ cat /etc/subuid
ori:524288:65536
kubelet:589824:7208960
ここの情報にしたがってKubernetesおよびCRI-Oのrpmをインストールします。 CRI-Oはk8sと同じバージョンを入れる必要がありますが、v1.30のCRI-Oはまだ正式には出ていないため、開発中のものを使います。
まずk8sのyumリポジトリを設定します。
KUBERNETES_VERSION=v1.30
cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/$KUBERNETES_VERSION/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/$KUBERNETES_VERSION/rpm/repodata/repomd.xml.key
EOF
パッケージをインストールします。
sudo dnf install -y kubelet kubeadm kubectl
サービスを起動設定をします(サービスの起動は kubeadm init
の中でやってくれます)。
sudo systemctl enable kubelet
まずyumリポジトリを設定します。
PROJECT_PATH=prerelease:/main
cat <<EOF | sudo tee /etc/yum.repos.d/cri-o.repo
[cri-o]
name=CRI-O
baseurl=https://pkgs.k8s.io/addons:/cri-o:/$PROJECT_PATH/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/addons:/cri-o:/$PROJECT_PATH/rpm/repodata/repomd.xml.key
EOF
パッケージをインストールします。
sudo dnf install -y cri-o
もしここで error: Verifying a signature using certificate DE15B14486CD377B9E876E1A234654DA9A296436 (isv:kubernetes OBS Project <isv:kubernetes@build.opensuse.org>)
みたいなエラーが出たら、
/var/cache
以下にダウンロードされているrpmファイルを直接インストールして乗り切ります (Fedoraで起こる気がします、が原因は追いかけてません...)。具体的には、 sudo find /var/cache -name 'cri-o.*.rpm'
して出てきたrpmファイルを sudo rpm -ivh --nosignature
で入れます。
CRI-Oが必要とするコマンドがいくつかあるので、それをインストールします。雑にやるならpodmanを入れると依存関係で入れてくれます(podman自体はCRI-Oを使用するのに必要はありませんが)。
sudo dnf install -y podman
もし真面目に必要なものだけ入れるなら、これくらいを入れておけば大丈夫だと思います。
sudo dnf install container-selinux crun conmon containers-common shadow-utils-subid iproute-tc
サービスを起動します。
sudo systemctl start crio
sudo systemctl enable crio
UserNamespacesSupport
のfeature gateを有効にしたconfigファイルを作ります。
cat <<EOF > kubeadm.config
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
apiServer:
extraArgs:
feature-gates: "UserNamespacesSupport=true"
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
featureGates:
UserNamespacesSupport: true
EOF
control planeノードでkubeadmを実行します。
sudo kubeadm init --config=kubeadm.config
正常終了したら、画面の出力どおりにkubeconfigをホームディレクトリにコピーして、workerノード上で kubeadm join
を実行します。
最後に好きなCNIプラグインを入れます。この検証では、ciliumを入れました。
CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)
CLI_ARCH=amd64
if [ "$(uname -m)" = "aarch64" ]; then CLI_ARCH=arm64; fi
curl -L --fail --remote-name-all https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}
sha256sum --check cilium-linux-${CLI_ARCH}.tar.gz.sha256sum
sudo tar xzvfC cilium-linux-${CLI_ARCH}.tar.gz /usr/local/bin
rm cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}
cilium install
ここのサンプルを使います。
cat <<EOF > user-namespaces-stateless.yaml
apiVersion: v1
kind: Pod
metadata:
name: testpod
spec:
hostUsers: false
containers:
- name: shell
command: ["sleep", "infinity"]
image: debian
spec.hostUsers
に false
を設定することで、user namespaceを使います。
(余談ですがCRI-Oは、Kubernetesが正式にuser namespaceに対応する前から、独自にuser namespaceを使えるようになっていました (Pod manifestの metadata.annotations
に io.kubernetes.cri-o.userns-mode: "auto"
を入れておくとuser namespaceを使います)。Kubernetes側で正式にuser namespaceに対応したので、この拡張は今後はdeprecateされるのではないかと思われます。)
デプロイしたらこんな感じになります。
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
testpod 1/1 Running 0 24m 10.0.2.208 k8s-129-wk2.fedora-amd.home <none> <none>
コンテナ内では、UID 0でプロセスが動いています。
$ kubectl exec testpod -- lsfd -p 1 | head -n 2
COMMAND PID USER ASSOC MODE TYPE SOURCE MNTID INODE NAME
sleep 1 root exe --- REG overlay 0 1255122 /usr/bin/sleep
$ kubectl exec testpod -- id
uid=0(root) gid=0(root) groups=0(root)
/proc/*/uid_map
を見ると、user namespaceにおけるUIDのマッピングが確認できます。
$ kubectl exec pod/testpod -- cat /proc/self/uid_map
0 4980736 65536
コンテナ内のUID 0以降65536個のUIDが、ホスト上のUID 4980736以降の65536個にマッピングされていることがわかります。
最後に該当workerノード (k8s-129-wk2
) 上で、このsleepプロセスをpsしてみます。まずホスト上でのPIDを調べます。
ssh k8s-129-wk2 systemd-cgls -u kubepods-besteffort-pod$(kubectl get pod testpod -o json | jq -r .metadata.uid | tr '-' '_').slice
Unit kubepods-besteffort-pode33ed7f7_0c89_42a5_9956_94a0a1761b23.slice (/kubepods.slice/kubepods-besteffort.slice/kubepods-besteffort-pode33ed7f7_0c89_42a5_9956_94a0a1761b23.slice):
└─crio-7089c8a0f865cf3857f0e4bf493daaee2716607661abbb50a090473ae6ceecbc.scope …
└─container
└─9247 sleep infinity
コンテナ内のsleepプロセスのホスト上のPIDは9247であることがわかります。
$ ssh k8s-129-wk2 ps u -p 9247
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
4980736 9247 0.0 0.0 2484 1156 ? Ss 15:47 0:00 sleep infinity
PID 9247のsleepは、/proc/self/uid_mapで確認したUIDで動いていることが確認できました。