Skip to content

Instantly share code, notes, and snippets.

@orimanabu
Last active April 28, 2024 14:47
Show Gist options
  • Save orimanabu/8fc59eccd9c8c6787314e8dabc03df8d to your computer and use it in GitHub Desktop.
Save orimanabu/8fc59eccd9c8c6787314e8dabc03df8d to your computer and use it in GitHub Desktop.
Kubernetes v1.30 User namespace PoC on Fedora 40

※ 本メモの更新版が 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

セットアップ

VMの作成

KVMホスト上に、Fedora Server 40の仮想マシンを3個作ります (control plane x 1, worker x 2)。

いろいろ準備

swap無効化

sudo touch /etc/systemd/zram-generator.conf

Firewalldの設定

sudo firewall-cmd --add-port 6443/tcp --permanent
sudo firewall-cmd --add-port 10250/tcp --permanent
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

kubelet ユーザーのuid/gid mappingの設定

(※ 検証をする上で、この手順は必ずしも実施しなくても構いません)

ユーザー 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のインストール

ここの情報にしたがって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

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

kubeadmの実行

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

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.hostUsersfalse を設定することで、user namespaceを使います。

(余談ですがCRI-Oは、Kubernetesが正式にuser namespaceに対応する前から、独自にuser namespaceを使えるようになっていました (Pod manifestの metadata.annotationsio.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で動いていることが確認できました。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment