チビども、はやくせんか!
皆さんご存じスタジオジブリの映画である千と千尋の神隠しに登場する釜爺。
普段は薬草の調合をしたり、風呂釜の日を管理したりと油屋の縁の下の力持ちです。
そんな釜爺がkamajiとなってkubernetes/k8sのクラスタの管理をしてくれるようになっています。
このkamajiについての説明とハンズオンのログを残しておこうと思います。
【参考】
k8sについて
kamajiとは?
要点としては、
- 既存のk8sクラスタ上にユーザテナントのcontrol planeの機能を展開できる
- 既存のk8sクラスタと展開したcontrol planeを含むクラスタは別物
- control planeの機能は既存k8sクラスタ上ではk8sの1リソースとして扱うことができる
- control planeの機能は複数展開可能
- k8sのAPIを公開してnodeとの通信を実施
- 展開したcontrol planeを含むクラスタのnode一覧にはcontrol planeのノードは存在しない
- nodeに相当するホストは別途用意する必要あり
- control planeとはk8s APIを用いて通信
何が嬉しいの?
- k8s上のリソースを作成するのみでcontrol planeの構築が済むため楽
- kubeadmでクラスタを構築するケースと比較してcontrol plane用のホストを構築しなくて済む
- 複数のクラスタを作成する際にはk8s上でのリソースを作成するのみでOK
- オンプレ/プライベートクラウド上など、managed k8sが使いにくい環境でmanaged k8sのような仕組みを提供できる選択肢の1つになりうる
- 提供側はkamajiシステム関連のコンテナおよびそれが動作している既存k8sクラスタの管理/利用側は展開されるcontrol plane機能リソースおよびnode相当のホストの管理のように所掌分界点を設けることが可能
- 複数の利用者間でそれぞれクラスタが割り当てられており、分離されている
- control plane部分は共用リソースに載っているのでリソース消費量等には注意が必要
ハンズオン
元手順
用意するもの
- k8sクラスタ: 1つ
- 投稿者はシングルノードのk3sクラスタを利用
- node用ホスト: 1つ(仮想マシン/物理マシン問わず)
※OSはUbuntu24.04を使用
構成
今回の構成ではテナントクラスタのcontrol planeにあるk8s APIをMetalLB+type LoadBalancerで公開する
事前準備
k8s API公開のためのMetalLBの構築
-
以下コマンドを実行
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.11/config/manifests/metallb-native.yaml
-
以下のconfigmapを適用
cat <<EOF > metallb-config.yaml apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: my-ip-pool namespace: metallb-system spec: addresses: - 192.168.1.100-192.168.1.110<<使用する環境に応じて修正>> --- apiVersion: metallb.io/v1beta1 kind: L2Advertisement metadata: name: my-l2-advertisement namespace: metallb-system spec: ipAddressPools: - my-ip-pool EOF kubectl apply -f metallb-config.yaml
(optional)k3sで構築している人で楽をしたい人向け
k3sにはKlipperLBがバインドされているためそれを有効化することで上記を代替することが可能
k3sのインストールすることでデフォルトでKlipperLBが有効化される
ただし、この場合kamaji control planeリソースを6443/tcpで公開することはできないので注意(k3sクラスタのAPI公開ポートと重複が起こるため)
kamajiシステムリソース作成
helmインストール
OSおよびCPUアーキテクチャに応じてlinux
,amd64
を適宜変更すること
$ wget https://get.helm.sh/helm-v3.16.4-linux-amd64.tar.gz
$ tar xvf helm-v3.16.4-linux-amd64.tar.gz
$ mv linux-amd64/helm /usr/local/bin/helm
Cert Managerインストール
$ helm repo add jetstack https://charts.jetstack.io
$ helm repo update
$ helm install \
cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.11.0 \
--set installCRDs=true
以下のような出力があることを確認
$ kubectl -n cert-manager get po
NAME READY STATUS RESTARTS AGE
cert-manager-86844d9d7-ldqdr 1/1 Running 0 2d5h
cert-manager-cainjector-6d5f558c69-7hcck 1/1 Running 0 2d5h
cert-manager-webhook-bd76f6cf9-8f72z 1/1 Running 0 2d5h
Kamaji Controllerインストール
$ helm repo add clastix https://clastix.github.io/charts
$ helm repo update
$ helm install kamaji clastix/kamaji -n kamaji-system --create-namespace
以下のような出力があることを確認
$ kubectl -n kamaji-system get pods
NAME READY STATUS RESTARTS AGE
kamaji-etcd-0 1/1 Running 0 50s
kamaji-etcd-1 1/1 Running 0 60s
kamaji-etcd-2 1/1 Running 0 90s
kamaji-7949578bfb-lj44p 1/1 Running 0 12s
テナントクラスタ作成
テナントコントロールプレーン作成
kamajiのquickstartに記載されている手順
以下の環境変数が必要- TENANT_NAMESPACE
- TENANT_NAME
- TENANT_VERSION
- TENANT_PORT=6443
- TENANT_DOMAIN
- TENANT_SVC_CIDR
- TENANT_POD_CIDR
- TENANT_DNS_SERVICE
- TENANT_PROXY_PORT
投稿者は以下の環境変数ファイルをセット
$ cat << EOF > env_kamaji-test
TENANT_NAMESPACE=kamaji-test
TENANT_NAME=kamaji-test
TENANT_VERSION=v1.29.1
TENANT_PORT=6443
TENANT_DOMAIN=kamaji.192.168.11.241.nip.io
TENANT_SVC_CIDR=10.44.0.0/16
TENANT_POD_CIDR=10.45.0.0/16
TENANT_DNS_SERVICE=10.44.0.10
TENANT_PROXY_PORT=8132
EOF
$ export $(cat env_kamaji-test | xargs)
$ cat > ${TENANT_NAMESPACE}-${TENANT_NAME}-tcp.yaml <<EOF
apiVersion: kamaji.clastix.io/v1alpha1
kind: TenantControlPlane
metadata:
name: ${TENANT_NAME}
namespace: ${TENANT_NAMESPACE}
labels:
tenant.clastix.io: ${TENANT_NAME}
spec:
dataStore: default
controlPlane:
deployment:
replicas: 3
additionalMetadata:
labels:
tenant.clastix.io: ${TENANT_NAME}
extraArgs:
apiServer: []
controllerManager: []
scheduler: []
resources:
apiServer:
requests:
cpu: 250m
memory: 512Mi
limits: {}
controllerManager:
requests:
cpu: 125m
memory: 256Mi
limits: {}
scheduler:
requests:
cpu: 125m
memory: 256Mi
limits: {}
service:
additionalMetadata:
labels:
tenant.clastix.io: ${TENANT_NAME}
serviceType: LoadBalancer
kubernetes:
version: ${TENANT_VERSION}
kubelet:
cgroupfs: systemd
admissionControllers:
- ResourceQuota
- LimitRanger
networkProfile:
port: ${TENANT_PORT}
certSANs:
- ${TENANT_NAME}.${TENANT_DOMAIN}
serviceCidr: ${TENANT_SVC_CIDR}
podCidr: ${TENANT_POD_CIDR}
dnsServiceIPs:
- ${TENANT_DNS_SERVICE}
addons:
coreDNS: {}
kubeProxy: {}
konnectivity:
server:
port: ${TENANT_PROXY_PORT}
resources:
requests:
cpu: 100m
memory: 128Mi
limits: {}
EOF
$ kubectl -n ${TENANT_NAMESPACE} apply -f ${TENANT_NAMESPACE}-${TENANT_NAME}-tcp.yaml
投稿者は以下のymlファイルを用意
前述のとおりk3sでkKlipperLBを使用している場合はk3sですでに6443ポートを使用している場合があるため16443ポートなどに適宜変更してください。
kamaji公式のマニュフェストとは異なりkonectivityはブランクとしてます。
---
apiVersion: v1
kind: Namespace
metadata:
name: kamaji-test
---
apiVersion: kamaji.clastix.io/v1alpha1
kind: TenantControlPlane
metadata:
name: kamaji-test
namespace: kamaji-test
labels:
tenant.clastix.io: kamaji-test
spec:
dataStore: default
controlPlane:
deployment:
replicas: 3
additionalMetadata:
labels:
tenant.clastix.io: kamaji-test
extraArgs:
apiServer: []
controllerManager: []
scheduler: []
resources:
apiServer:
requests:
cpu: 250m
memory: 512Mi
limits: {}
controllerManager:
requests:
cpu: 125m
memory: 256Mi
limits: {}
scheduler:
requests:
cpu: 125m
memory: 256Mi
limits: {}
service:
additionalMetadata:
labels:
tenant.clastix.io: kamaji-test
serviceType: LoadBalancer
kubernetes:
version: "v1.29.12"
kubelet:
cgroupfs: systemd
admissionControllers:
- ResourceQuota
- LimitRanger
networkProfile:
port: 6443
certSANs:
- kamaji-test.kamaji.192.168.11.241.nip.io
serviceCidr: 10.44.0.0/16
podCidr: 10.45.0.0/16
dnsServiceIPs:
- 10.44.0.10
addons:
coreDNS: {}
kubeProxy: {}
konnectivity: {}
以下コマンドでデプロイ
kubectl apply -f kamaji-test-kamaji-test-tcp.yaml
しばらく経つと以下のようなリソースが作成されているはず
kamajiのcontrol planeリソースがv1.29.12かつ192.168.11.152:6443をエンドポイントとして作成されていることがわかる。
$ kubectl -n kamaji-test get tenantcontrolplanes.kamaji.clastix.io
NAME VERSION STATUS CONTROL-PLANE ENDPOINT KUBECONFIG DATASTORE AGE
kamaji-test v1.29.12 Ready 192.168.11.152:6443 kamaji-test-admin-kubeconfig default 96s
上記のcontrol planeリソースの実体は以下のpod/service
$ kubectl -n kamaji-test get all
NAME READY STATUS RESTARTS AGE
pod/kamaji-test-549cfc69c9-2lt2n 4/4 Running 0 89s
pod/kamaji-test-549cfc69c9-q56dw 4/4 Running 0 89s
pod/kamaji-test-549cfc69c9-wvdv4 4/4 Running 0 89s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kamaji-test LoadBalancer 10.43.135.152 192.168.11.152 6443:32641/TCP,8132:31207/TCP 2m15s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/kamaji-test 3/3 3 3 90s
NAME DESIRED CURRENT READY AGE
replicaset.apps/kamaji-test-549cfc69c9 3 3 3 89s
replicaset.apps/kamaji-test-89c59cf8f 0 0 0 90s
これでcontrol planeの作成は完了。
テナントノード作成
以下の手順は中期個所を除き原則テナントノード上で実施
containerdのインストール
いつも通りの手順
なお、投稿者はcgroupのインターフェイス設定をsystemdにしていないせいで躓いていた。
-
カーネルパラメータ周辺の設定 k8sドキュメント
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF sudo modprobe overlay sudo modprobe br_netfilter # この構成に必要なカーネルパラメーター、再起動しても値は永続します cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF # 再起動せずにカーネルパラメーターを適用 sudo sysctl --system
-
containerd/runc/criのダウンロード
$ wget https://github.com/containerd/containerd/releases/download/v1.7.24/containerd-1.7.24-linux-amd64.tar.gz $ wget https://github.com/opencontainers/runc/releases/download/v1.2.3/runc.amd64 $ wget https://github.com/containernetworking/plugins/releases/download/v1.6.1/cni-plugins-linux-amd64-v1.6.1.tgz
-
バイナリ配置
$ sudo tar Cxzvf /usr/local containerd-1.7.24-linux-amd64.tar.gz $ sudo install -m 755 runc.amd64 /usr/local/sbin/runc $ sudo mkdir -p /opt/cni/bin $ sudo tar Cxzvf /opt/cni/bin cni-plugins-linux-amd64-v1.6.1.tgz
-
systemdファイル設置
$ wget https://raw.githubusercontent.com/containerd/containerd/main/containerd.service $ sudo mkdir -p /usr/local/lib/systemd/system $ sudo mv containerd.service /usr/local/lib/systemd/system
-
containerd設定ファイル設置
$ sudo mkdir /etc/containerd $ containerd config default > config.toml $ sudo mv config.toml /etc/containerd/config.toml $ sudo vi /etc/containerd/config.toml
config.toml
内SystemdCgroup = false
をSystemdCgroup = true
に修正修正前[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] BinaryName = "" CriuImagePath = "" CriuPath = "" CriuWorkPath = "" IoGid = 0 IoUid = 0 NoNewKeyring = false NoPivotRoot = false Root = "" ShimCgroup = "" SystemdCgroup = false
修正後[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] BinaryName = "" CriuImagePath = "" CriuPath = "" CriuWorkPath = "" IoGid = 0 IoUid = 0 NoNewKeyring = false NoPivotRoot = false Root = "" ShimCgroup = "" SystemdCgroup = true
-
containerdスタート
$ sudo systemctl daemon-reload $ sudo systemctl enable --now containerd.service $ sudo systemctl status containerd.service
kubeadm/kubectl/kubeletのインストール
k8sの公式ドキュメントのとおりに実施
- apt更新
$ sudo apt-get update $ sudo apt-get install -y apt-transport-https ca-certificates curl gpg
- gpg鍵追加
$ curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/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:/v1.29/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
- パッケージインストール
$ sudo apt-get update $ sudo apt-get install -y kubelet kubeadm kubectl $ sudo apt-mark hold kubelet kubeadm kubectl
テナントノードのクラスタへの参加
以下手順のうち項番1,2のみマネジメントクラスタ上で実施
-
マネジメントクラスタ 上で以下のコマンドを実行
$ kubectl get secrets -n kamaji-test kamaji-test-admin-kubeconfig -o json \ | jq -r '.data["admin.conf"]' \ | base64 --decode \ > kamaji-test-kamaji-test.kubeconfig
-
kamaji-test-kamaji-test.kubeconfig
ファイルをテナントノードに転送 -
テナントノード 上で以下のコマンドを実行
$ $(echo "sudo ")$(kubeadm --kubeconfig=kamaji-test-kamaji-test.kubeconfig token create --print-join-command)
$ export KUBECONFIG=kamaji-test-kamaji-test.kubeconfig $ kubectl get node NAME STATUS ROLES AGE VERSION gt01-kamaji-test-kw01 NotReady <none> 1m v1.29.12
この時点だとCNIがデプロイされていないので
NotReady
となる -
flannelデプロイ
$ wget https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml $ vi kube-flannel.yml
kamaji-test-kamaji-test-tcp.yaml
を作成した際にpodCidr
を10.45.0.0/16
としたのでflannelのマニュフェストも修正(84行目)修正前net-conf.json: | { "Network": "10.244.0.0/16", "EnableNFTables": false, "Backend": { "Type": "vxlan" } }
修正後net-conf.json: | { "Network": "10.45.0.0/16", "EnableNFTables": false, "Backend": { "Type": "vxlan" } }
-
デプロイ&確認
デプロイ$ kubectl apply -f kube-flannel.yml namespace/kube-flannel created serviceaccount/flannel created clusterrole.rbac.authorization.k8s.io/flannel created clusterrolebinding.rbac.authorization.k8s.io/flannel created configmap/kube-flannel-cfg created daemonset.apps/kube-flannel-ds created
node確認
$ kubectl get node NAME STATUS ROLES AGE VERSION gt01-kamaji-test-kw01 Ready <none> 17m v1.29.12
pod確認(kube-flannel)
すべてRunningであることを確認NAME READY STATUS RESTARTS AGE kube-flannel-ds-pgd97 1/1 Running 0 90s
pod確認(kube-system)
すべてRunningであることを確認$ kubectl -n kube-system get po NAME READY STATUS RESTARTS AGE coredns-7f988d7799-r4fgk 1/1 Running 0 41m coredns-7f988d7799-zzv4g 1/1 Running 0 41m konnectivity-agent-jtvzz 1/1 Running 0 59s kube-proxy-hsbzk 1/1 Running 0 17m
動作確認
試しにnginx用のデプロイメントを作成
$ kubectl create deployment nginx --image nginx --replicas 3
deployment.apps/nginx created
$ kubectl get po
NAME READY STATUS RESTARTS AGE
nginx-7854ff8877-q2h5t 1/1 Running 0 18s
nginx-7854ff8877-szzdg 1/1 Running 0 18s
nginx-7854ff8877-wf4xl 1/1 Running 0 18s
問題なくpodが作成された
終わりに
ここまでkamajiを用いたマルチテナントでのmanaged k8s環境の構築およびユーザサイドで利用するテナントクラスタの作成方法について手順を追ってみました。
粗削りではあるもののひとまず利用可能なクラスタは作成することができました。
実用に落とし込んでいく上では
- テナントクラスタのcontrol planeのk8s APIエンドポイントをingessで公開
- テナントクラスタのcontrol planeのetcdのデータ保存の永続化
- kamajiのシステムリソース上のデータ保存の永続化
についても確認/詰める必要がある部分に見えているのでその部分も追加で調査をしていきたいと思います。
ここまでお読みいただきありがとうございました。