はじめに
このエントリーは俺得メモなのでなんですかこれって思った人は読まなくて大丈夫です。
Kubernetesのコンテナランタイムを自由に選べるようにする「RuntimeClass」をもっと色んな人に使ってもらうべくこの記事を書いています(突然の矛盾)。
今回の記事のゴールは以下のとおりです
- kubeadmを使ってKubernetesクラスターを作る(執筆時点では1.21)
- containerdをメインのCRIランタイムとして設定し、OCIランタイムにはruncとrunsc(gVisor)を入れて自由に選択できるようにする
- そのためのRuntimeClass
というわけで早速やっていきましょう。
前提準備
- まっさらなUbuntu 20.04のマシン
containerdのインストール
cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf overlay br_netfilter EOF sudo modprobe overlay sudo modprobe br_netfilter # Setup required sysctl params, these persist across reboots. cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf net.bridge.bridge-nf-call-iptables = 1 net.ipv4.ip_forward = 1 net.bridge.bridge-nf-call-ip6tables = 1 EOF # Apply sysctl params without reboot sudo sysctl --system # (Install Docker CE) ## Set up the repository: ### Install packages to allow apt to use a repository over HTTPS apt-get update && apt-get install -y \ apt-transport-https ca-certificates curl software-properties-common gnupg2 # Add Docker's official GPG key: curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key --keyring /etc/apt/trusted.gpg.d/docker.gpg add - # Add the Docker apt repository: add-apt-repository \ "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \ stable" ## Install containerd sudo apt-get update && sudo apt-get install -y containerd.io
これでcontainerdはとりあえず入ります。途中で入ってるカーネルパラメーターの調整は、Kubernetesの公式ドキュメントを参考にしています。
containerd-shim-runsc-v1のインストール
CRIランタイム(containerd)とOCIランタイム(runc, gVisor)が連携するために必要な「shim」を入れて上げる必要があるので、追加で入れておきます。
## Install gVisor shim for containerd ( set -e ARCH=$(uname -m) URL=https://storage.googleapis.com/gvisor/releases/release/latest/${ARCH} wget ${URL}/runsc ${URL}/runsc.sha512 \ ${URL}/containerd-shim-runsc-v1 ${URL}/containerd-shim-runsc-v1.sha512 sha512sum -c runsc.sha512 \ -c containerd-shim-runsc-v1.sha512 rm -f *.sha512 chmod a+rx runsc containerd-shim-runsc-v1 sudo mv runsc containerd-shim-runsc-v1 /usr/local/bin )
これはgVisorの公式ドキュメントから引用。
containerdの設定を変更する
containerd config default
を使うとデフォルトの設定値が全部表示されるので、それを所定の場所に保存します。ついでにKubernetesで必要な設定もちらほらいれておきましょう。
sudo mkdir -p /etc/containerd sudo containerd config default > /etc/containerd/config.toml if grep -q SystemdCgroup "/etc/containerd/config.toml"; then echo "Config found, skip rewriting..." else sed -i -e "/^ \[plugins\.\"io\.containerd\.grpc\.v1\.cri\"\.containerd\.runtimes\.runc\.options\]$/a\ SystemdCgroup \= true\n \[plugins\.\"io\.containerd\.grpc\.v1\.cri\"\.containerd\.runtimes\.runsc\]\n runtime_type \= \"io\.containerd\.runsc\.v1\"" /etc/containerd/config.toml sed -i -e 's/shim_debug \= false/shim_debug \= true/g' /etc/containerd/config.toml fi
これをやると主にこんな感じの設定が入るはず。
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes] [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] runtime_type = "io.containerd.runc.v2" runtime_engine = "" runtime_root = "" privileged_without_host_devices = false base_runtime_spec = "" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SystemdCgroup = true [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc] runtime_type = "io.containerd.runsc.v1" [plugins."io.containerd.runtime.v1.linux"] shim = "containerd-shim" runtime = "runc" runtime_root = "" no_shim = false shim_debug = true
これを見るとruncがデフォルトランタイムになってgVisor用の設定をいれて、ついでにKubernetes用にCgroupの設定をいじってる感じ。これが終わったらcontainerdのプロセスを立ち上げてあげます(今回は念の為再起動しています)。
# Restart containerd sudo systemctl restart containerd
Kubernetes向けのセットアップとパッケージのインストール
一部インストールに向けた必須項目と、KubernetesでprotectKernelDefaults
を有効にするためにいくつかのカーネルパラメーターをいじっています。
# Set Kubernetes kernel params cat <<EOF | tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 vm.overcommit_memory = 1 vm.panic_on_oom = 0 kernel.panic = 10 kernel.panic_on_oops = 1 kernel.keys.root_maxkeys = 1000000 kernel.keys.root_maxbytes = 25000000 EOF sysctl --system # Install kubeadm apt-get update && apt-get install -y apt-transport-https curl curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - cat <<EOF | tee /etc/apt/sources.list.d/kubernetes.list deb https://apt.kubernetes.io/ kubernetes-xenial main EOF apt-get update apt-get install -y kubelet kubeadm kubectl apt-mark hold kubelet kubeadm kubectl
kubeadm用コンフィグの設定とクラスターの作成
トークンは設定しなくても動的に生成できますが今回はめんどいので静的にトークンを仕込んじゃいます。再現性重視。criSocket
にランタイムを設定したり、cgroupDriver
やprotectKernelDefaults
の設定などを気持ち入れています。
# Set kubeadm config cat > ~/init_kubelet.yaml <<EOF apiVersion: kubeadm.k8s.io/v1beta2 kind: InitConfiguration bootstrapTokens: # DO NOT USE THE STATIC TOKEN IN PRODUCTION - token: "9a08jv.c0izixklcxtmnze7" description: "kubeadm bootstrap token" ttl: "24h" nodeRegistration: criSocket: "/var/run/containerd/containerd.sock" --- apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration cgroupDriver: "systemd" protectKernelDefaults: true EOF # Create a Kubernetes cluster kubeadm init --config init_kubelet.yaml
クラスターアクセス用にkubeconfigの取得をしてCNIでCiliumを入れる
ついでにワークロードをコントロールプレーンで動かすためにtaintを解除しています。今回はシングルクラスター想定なので・・・。ワーカーノードが別に存在する場合はやらなくてもよし。
mkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config chown $(id -u):$(id -g) $HOME/.kube/config # Setup CNI curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash helm repo add cilium https://helm.cilium.io/ helm install cilium cilium/cilium --namespace kube-system # Untaint master node to schedule workloads kubectl taint nodes --all node-role.kubernetes.io/master-
RuntimeClassの設定をしてワークロードを動かしてみる
gVisor用のRuntimeClassリソースを適用してみます。設定ファイルの中にあるhandlerってやつはCRIランタイムの設定ファイル(containerdの場合は/etc/containerd/config.toml
)で設定したハンドラーの名前を入れるので今回はrunscになります。
一応言うとplugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc
の部分ね。
cat <<EOF | kubectl apply -f - apiVersion: node.k8s.io/v1beta1 kind: RuntimeClass metadata: name: gvisor handler: runsc EOF cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: nginx-gvisor spec: runtimeClassName: gvisor containers: - name: nginx image: nginx EOF cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: nginx spec: containers: - name: nginx image: nginx EOF
実際にPodが動いてるか確認してみる。RuntimeClassを指定したPodは、RuntimeClassをRespectするので、もしまともに動かなかったらエラーになります。
$ kubectl get pod nginx-gvisor -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-gvisor 1/1 Running 0 118s 10.0.0.6 ubuntu-s-4vcpu-8gb-sgp1-01 <none> <none>
$ kubectl get pod nginx -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx 1/1 Running 0 5s 10.0.0.131 ubuntu-s-4vcpu-8gb-sgp1-01 <none> <none>
動作確認
$ kubectl exec -it nginx-gvisor -- dmesg [ 0.000000] Starting gVisor... [ 0.346752] Granting licence to kill(2)... [ 0.454322] Creating bureaucratic processes... [ 0.736069] Accelerating teletypewriter to 9600 baud... [ 1.184759] Letting the watchdogs out... [ 1.296606] Reading process obituaries... [ 1.364670] Checking naughty and nice process list... [ 1.467231] Generating random numbers by fair dice roll... [ 1.957884] Segmenting fault lines... [ 2.217956] Synthesizing system calls... [ 2.526996] Preparing for the zombie uprising... [ 2.808670] Ready!
わーい!
とりあえず動かした手順だけ雑に貼ってるのでちょっと読み物としてはアレですが、これでcontainerd+gVisorが動いたので僕は嬉しいです。
なお、これの実現にあたっては@IanMLewisに大変助けていただきました。ありがとうございます。
では、今日はこのへんで。