Kubernetes v1.30でBetaになったUser Namespaceを動かしてみる
はじめに
Kubernetes v1.30でBetaになったUser NamespaceをFedora 40, CentOS Stream 9, Ubuntu 24.04で試してみました (本記事は、gistに書いたメモの改訂版です)。
下記のドキュメントに沿って環境を作って検証しました。CRI-O、crunを入れて、/etc/sub[ug]id
をちゃんと設定すれば、たぶんすんなり動くと思います。
本記事は、2024-04-27時点の情報をもとにしています。
現時点では、User Namespaceを試すには下記が必要になります。
- Linuxカーネル v6.3 (tmpfsのidmapマウント)
- CentOS Stream 9のカーネルのベースバージョンはこれよりも古いですが、User Namespace自体はうまく動いているように見えました。おそらく必要な機能がバックポートされているからだと思います (これかな...)
- crun v1.9以降 (v1.13以降が推奨)
- runc v1.1.zは現時点では未対応
- CRI-O v1.25以降
- containerd v1.7は、k8s v1.27〜v1.30のUser Namespaceには未対応
セットアップ
VMの作成
KVMホスト上に、仮想マシンを必要なだけ作ります。
以下の検証では、作った順番の都合で、結果的に
- control plane: Fedora 40
- worker: Fedora 40, CentOS Stream 9, Ubuntu 24.04
という構成になりました。
いろいろ準備
swap無効化
Fedora 40の場合:
sudo touch /etc/systemd/zram-generator.conf
CentOS Stream 9、Ubuntu 24.04の場合
/etc/fstabのswapの行をコメントアウトして、swapoff -a
を実行します。
Firewalldの設定
Fedora 40、CentOS Stream 9の場合のみ実施します。Ubuntu 24.04の場合はこの手順はスキップしてください。
Kubernetesで必要なポートの開放
sudo firewall-cmd --add-port 6443/tcp --permanent
sudo firewall-cmd --add-port 10250/tcp --permanent
sudo firewall-cmd --reload
Antreaで使用するポートの開放
後述のとおり、CNIプラグインとしてAntreaを使用しました。必要と思われるポートをあらかじめ開けておきます。
sudo firewall-cmd --permanent --add-port 10349/tcp
sudo firewall-cmd --permanent --add-port 10350/tcp
sudo firewall-cmd --permanent --add-port 10351/tcp
sudo firewall-cmd --permanent --add-port 10351/udp
sudo firewall-cmd --permanent --add-port 6081/udp
sudo firewall-cmd --permanent --add-port 443/tcp
sudo firewall-cmd --reload
net.ipv4.ip_forward
の設定
echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/98-k8s.conf
sudo sysctl -p --system
/run/systemd/resolve/resolv.conf
の作成
CentOS Stream 9はsystemd-resolved
を使っていないせいか、このファイルがないためにkubeadm joinに失敗します。
正しい対応がよくわかっていませんが、とりあえず下記を実行しておくとうまくいきました。
sudo mkdir -p /run/systemd/resolve
sudo ln -s /etc/resolv.conf /run/systemd/resolve/resolv.conf
kubelet
ユーザーのuid/gid mappingの設定
(※ 検証をする上で、この手順は必ずしも実施しなくても構いません)
ユーザー kubelet
を作成し、ここ
に記載されているように 65536 * 110
個のUIDを使えるようにします。
sudo useradd kubelet
デフォルト設定だと、使えるUIDは65536個です (下記はFedora 40での出力例です。ori
はインストール時に作成したユーザー名です)。
$ cat /etc/subuid
ori:524288:65536
kubelet:589824:65536
kubelet
ユーザーが使えるUIDを 65536 * 110
個に増やしておきます。マッピングの開始番号(sub[gu]idの2つ目のカラム、上記例だと 589824
のところ)が65536の倍数である必要がありますが、CentOS Stream 9、Ubuntu 24.04の場合は、そうなっていない可能性があります。必要に応じて、開始番号も調整してください。本検証では、Fedora 40で作ったときの番号をコピペして、CentOS Stream 9およびUbuntu 24.04の場合も下記の値を使用しました。
$ cat /etc/subuid
ori:524288:65536
kubelet:589824:7208960
Kubernetesのインストール
ここの情報にしたがってKubernetesおよびCRI-Oのrpmをインストールします。
CRI-Oはk8sと同じバージョンを入れる必要がありますが、v1.30のCRI-Oはまだ正式には出ていないため、開発中のものを使います。
Fedora 40、CentOS Stream 9の場合
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
CRI-Oのインストール
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
Ubuntu 24.04の場合
ここのとおりにやりました。
まず前提となるパッケージを入れます。
sudo apt-get update
sudo apt-get install -y software-properties-common curl
次にリポジトリの設定をします。24.04から形式が変わったようですが、混在できるようなので古いままでやりました。
# k8s repo
KUBERNETES_VERSION=v1.30
curl -fsSL https://pkgs.k8s.io/core:/stable:/$KUBERNETES_VERSION/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/$KUBERNETES_VERSION/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list
# crio repo
PROJECT_PATH=prerelease:/main
curl -fsSL https://pkgs.k8s.io/addons:/cri-o:/$PROJECT_PATH/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/cri-o-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/cri-o-apt-keyring.gpg] https://pkgs.k8s.io/addons:/cri-o:/$PROJECT_PATH/deb/ /" | sudo tee /etc/apt/sources.list.d/cri-o.list
k8s, cri-oを入れます。
sudo apt-get update
sudo apt-get install -y cri-o kubelet kubeadm kubectl podman
crunやその他依存関係で必要そうなパッケージもインストールしたいので、podmanもインストールしています (podman自体は直接必要なわけではありません)。
最後に、サービスの起動設定をしておきます。
sudo systemctl enable kubelet
sudo systemctl enable crio
sudo systemctl start crio
kubeadmの実行
UserNamespacesSupport
のfeature gateを有効にしたconfigファイルを作ります。
cat <<EOF > kubeadm.config
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
apiServer:
extraArgs:
feature-gates: "UserNamespacesSupport=true"
networking:
serviceSubnet: 10.96.0.0/24
podSubnet: 10.244.0.0/16
---
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プラグインを入れます。この検証では、Antreaを入れました。
kubectl apply -f https://github.com/antrea-io/antrea/releases/download/v1.13.4/antrea.yml
※ 最初Ciliumで検証していたのですが、Ubuntu 24.04でCiliumがうまく動かなかったため(issue)、Antreaに変えました。
User Namespaceの検証
ここのサンプルを使います。
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
を設定することで、Pod内のコンテナプロセスが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-fedora-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/*/[ug]id_map
を見ると、User NamespaceにおけるUIDのマッピングが確認できます。
$ kubectl exec pod/testpod -- cat /proc/self/uid_map
0 4980736 65536
コンテナ内のUID 0以降65536個のUIDが、ホスト上のUID 4980736以降の65536個にマッピングされていることがわかります。
※ User Namespaceを使用しない場合は、通常 /proc/*/[ug]id_map
の2つ目のカラムが 0
になります
最後に該当workerノード (k8s-fedora-wk2
) 上で、このsleepプロセスをpsしてみます。まずホスト上でのPIDを調べます。
ssh k8s-fedora-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-fedora-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 4980736で動いていることが確認できました。
※ User Namespaceを使用しない場合は、ここのPIDがコンテナプロセスのPIDと同じ (このコンテナの場合は 0
) になります。
Discussion