「kwok」でKubernetesクラスターをシュミレーションする
はじめに
3-shakeのSreake事業部に所属する早川(@bells17)です。第7回目の今回は、大規模なKubernetesクラスターの検証を容易にする「kwok」について紹介します。
kwokとは“Kubernetes WithOut Kubelet”の略で、実際のKubernetes Nodeではなく仮想的なNodeやPodを使用することで、1,000台以上のNodeのあるKubernetesクラスターを手元環境で手軽にセットアップして検証を行えるツールキットです。
つまり、kwokを使用することで実際のNodeを用意することなく大規模なKubernetesクラスターの検証を部分的に行えるようになります。
kwokを試してみる
「実際のNodeを使用しない」と言われてもピンとこないと思うので、まずは試してみましょう。
kwokは「kwok」と「kwokctl」の2つのコマンドラインツールによって構成されているので、はじめにインストールを行います(kwokコマンドが仮想的なNodeやPodが動作してるかのように振る舞うためのアプリケーション本体、kwokctlがkwokを便利に使うためのコマンドラインツールとなっています)。
Installationページを見ると、下記の3つの方法でインストールできます。
- Package Manager
go install
コマンド- バイナリのダウンロード
ここでは、go install
コマンド使ってインストールを行います。今回はv0.6.0を使用するため、下記のコマンドでインストールしてください(事前にGoがインストールされている必要があります)。
$ go install sigs.k8s.io/kwok/cmd/{kwok,kwokctl}@v0.6.0
インストールが完了したら、続けて下記のコマンドを実行してクラスターと3台のNodeを構築します。
$ kwokctl create cluster $ kwokctl scale node --replicas=3
kwokctlには対象クラスター向けのkubectlやetcdctlなども含まれるので、今回はkwokctl経由でkubectlを使って行きましょう。
上記のコマンドを実行後kwokctl kubectl get node
コマンドを実行すると、下記のように3台のNodeが作成されていることを確認できます。
$ kwokctl kubectl get node NAME STATUS ROLES AGE VERSION node-000000 Ready agent 7s kwok-v0.6.0 node-000001 Ready agent 7s kwok-v0.6.0 node-000002 Ready agent 7s kwok-v0.6.0
Manage nodes and pods with kwokページのCreate a Nodeにあるように、Nodeを手動で作成することもできます。
実際に下記のようにして作成してみましょう。
$ kwokctl kubectl apply -f [<https://raw.githubusercontent.com/kubernetes-sigs/kwok/v0.6.0/site/static/examples/node.yaml>](<https://github.com/kubernetes-sigs/kwok/blob/v0.6.0/site/static/examples/node.yaml>)
コマンド実行後にkwokctl kubectl get node
コマンドを実行してみると、下記のように手動で作成したkwok-node-0
が作成されていることを確認できます。
$ kwokctl kubectl get node NAME STATUS ROLES AGE VERSION kwok-node-0 Ready agent 1m57s kwok-v0.6.0 node-000000 Ready agent 5m kwok-v0.6.0 node-000001 Ready agent 5m kwok-v0.6.0 node-000002 Ready agent 5m kwok-v0.6.0
それでは、引き続き下記のコマンドでPodを3個作成してみます。
$ kwokctl scale pod --replicas=3
kwokctl kubectl get pod
コマンドを実行してみると、すぐにPodがRunningになっていることを確認できます。
$ kwokctl kubectl get pod NAME READY STATUS RESTARTS AGE pod-000000 1/1 Running 0 4s pod-000001 1/1 Running 0 4s pod-000002 1/1 Running 0 4s
このように、簡単にNodeを作成し、作成したNodeでPodを動かしているのが確認できたかと思います。ちなみにkwokctl scale pod
コマンドで生成されるPodが下記のようなmanifestsになります(不要そうなフィールドは除去しています)。
apiVersion: v1 kind: Pod metadata: labels: kwok.x-k8s.io/kwokctl-scale: pod name: pod-000000 namespace: default spec: containers: - image: busybox imagePullPolicy: Always name: container-0 volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/serviceaccount name: kube-api-access-742pf readOnly: true tolerations: - effect: NoExecute key: node.kubernetes.io/not-ready operator: Exists tolerationSeconds: 300 - effect: NoExecute key: node.kubernetes.io/unreachable operator: Exists tolerationSeconds: 300 volumes: - name: kube-api-access-742pf projected: defaultMode: 420 sources: - serviceAccountToken: expirationSeconds: 3607 path: token - configMap: items: - key: ca.crt path: ca.crt name: kube-root-ca.crt - downwardAPI: items: - fieldRef: apiVersion: v1 fieldPath: metadata.namespace path: namespace
kwokで多くのリソースを作ってみる
それでは、実際に大量のNodeやPodを作成できるのか試してみます。まずは下記のコマンドを実行してNode数を増やしてみましょう。
$ kwokctl scale node --replicas=20
kubectl get node
コマンドを実行すると、node-000xx
のNodeが20台に増えていることを確認できます。
$ kwokctl kubectl get node --no-headers=true | wc -l 21 $ kwokctl kubectl get node NAME STATUS ROLES AGE VERSION kwok-node-0 Ready agent 7m kwok-v0.6.0 node-000000 Ready agent 10m kwok-v0.6.0 node-000001 Ready agent 10m kwok-v0.6.0 node-000002 Ready agent 10m kwok-v0.6.0 node-000003 Ready agent 7s kwok-v0.6.0 node-000004 Ready agent 7s kwok-v0.6.0 node-000005 Ready agent 7s kwok-v0.6.0 node-000006 Ready agent 7s kwok-v0.6.0 node-000007 Ready agent 7s kwok-v0.6.0 node-000008 Ready agent 7s kwok-v0.6.0 node-000009 Ready agent 7s kwok-v0.6.0 node-000010 Ready agent 7s kwok-v0.6.0 node-000011 Ready agent 7s kwok-v0.6.0 node-000012 Ready agent 7s kwok-v0.6.0 node-000013 Ready agent 7s kwok-v0.6.0 node-000014 Ready agent 7s kwok-v0.6.0 node-000015 Ready agent 7s kwok-v0.6.0 node-000016 Ready agent 7s kwok-v0.6.0 node-000017 Ready agent 7s kwok-v0.6.0 node-000018 Ready agent 7s kwok-v0.6.0 node-000019 Ready agent 7s kwok-v0.6.0
次に、下記のコマンドでPodもスケールしてみましょう。
$ kwokctl scale pod --replicas=20
続けてkubectl get pod
コマンドを実行すると、Podが20台起動していることを確認できます。
$ kubectl get node --no-headers=true | wc -l 20 $ kubectl get pod NAME READY STATUS RESTARTS AGE pod-000000 1/1 Running 0 2m54s pod-000001 1/1 Running 0 2m54s pod-000002 1/1 Running 0 2m54s pod-000003 1/1 Running 0 19s pod-000004 1/1 Running 0 19s pod-000005 1/1 Running 0 19s pod-000006 1/1 Running 0 19s pod-000007 1/1 Running 0 19s pod-000008 1/1 Running 0 19s pod-000009 1/1 Running 0 19s pod-000010 1/1 Running 0 19s pod-000011 1/1 Running 0 19s pod-000012 1/1 Running 0 19s pod-000013 1/1 Running 0 19s pod-000014 1/1 Running 0 19s pod-000015 1/1 Running 0 19s pod-000016 1/1 Running 0 19s pod-000017 1/1 Running 0 19s pod-000018 1/1 Running 0 19s pod-000019 1/1 Running 0 19s
先ほどは簡単に確認するためにNodeもPodも20台にスケールしましたが、手元の環境でも簡単に1000台程度にスケールさせることができました。
# Node $ kwokctl scale node --replicas=1000 No resource found, use default resource resource=node cluster=kwok Load resources counter=980 elapsed=4.2s resource=nodes replicas=1000 name=node cluster=kwok $ kubectl get node --no-headers=true | wc -l 1001 # Pod $ kwokctl scale pod --replicas=1000 No resource found, use default resource resource=pod cluster=kwok Load resources counter=980 elapsed=2.9s namespace=default resource=pods replicas=1000 name=pod cluster=kwok $ kubectl get pod --no-headers=true | wc -l 1000
Nodeについては少し待つだけで1,000台に増やすことができましたが、PodについてはDeploymentのreplicas変更を受けて徐々にスケールするようでした。
クラスターのクリーンアップ
これで基本的な動作が確認できたので、最後に作成したクラスターを削除してみます。削除するにはkwokctl delete cluster
コマンドを実行するだけです。実行すると、下記のようにクラスターが削除されたことを確認できます。
$ kwokctl delete cluster Cluster is stopping cluster=kwok Cluster is stopped elapsed=0.9s cluster=kwok Cluster is deleting cluster=kwok Cluster is deleted
kwokのアーキテクチャ
このように、NodeやPodを仮想的に動作させる仕組みを持つkwokは、指定されたラベルやアノテーションが設定されたNodeを仮想的なNodeとして扱います。そして、仮想的なNodeにスケジュールされたPodに対し、kwokが管理するカスタムリソースであるStageリソースの条件をもとにPodのステータスをreadyに変更することで、Podが起動している状態をシミュレートする実装みになっているようでした。
また、その他Stageリソースを使ってNodeのステータス管理を行ったり、仮想的なNodeのNode Leaseを更新することでKubernetesから見て正常に動作しているNodeであると認識させるといったことも行っているようです。
Stageリソースは、例えば下記のようになっています。
apiVersion: kwok.x-k8s.io/v1alpha1 kind: Stage metadata: name: node-initialize spec: resourceRef: apiGroup: v1 kind: Node selector: matchExpressions: - key: '.status.conditions.[] | select( .type == "Ready" ) | .status' operator: 'NotIn' values: - 'True' next: statusTemplate: | {{ $now := Now }} {{ $lastTransitionTime := or .metadata.creationTimestamp $now }} conditions: {{ range NodeConditions }} - lastHeartbeatTime: {{ $now | Quote }} lastTransitionTime: {{ $lastTransitionTime | Quote }} message: {{ .message | Quote }} reason: {{ .reason | Quote }} status: {{ .status | Quote }} type: {{ .type | Quote}} {{ end }} addresses: {{ with .status.addresses }} {{ YAML . 1 }} {{ else }} {{ with NodeIP }} - address: {{ . | Quote }} type: InternalIP {{ end }} {{ with NodeName }} - address: {{ . | Quote }} type: Hostname {{ end }} {{ end }} {{ with NodePort }} daemonEndpoints: kubeletEndpoint: Port: {{ . }} {{ end }} allocatable: {{ with .status.allocatable }} {{ YAML . 1 }} {{ else }} cpu: 1k memory: 1Ti pods: 1M {{ end }} capacity: {{ with .status.capacity }} {{ YAML . 1 }} {{ else }} cpu: 1k memory: 1Ti pods: 1M {{ end }} {{ $nodeInfo := .status.nodeInfo }} {{ $kwokVersion := printf "kwok-%s" Version }} nodeInfo: architecture: {{ or $nodeInfo.architecture "amd64" }} bootID: {{ or $nodeInfo.bootID `""` }} containerRuntimeVersion: {{ or $nodeInfo.containerRuntimeVersion $kwokVersion }} kernelVersion: {{ or $nodeInfo.kernelVersion $kwokVersion }} kubeProxyVersion: {{ or $nodeInfo.kubeProxyVersion $kwokVersion }} kubeletVersion: {{ or $nodeInfo.kubeletVersion $kwokVersion }} machineID: {{ or $nodeInfo.machineID `""` }} operatingSystem: {{ or $nodeInfo.operatingSystem "linux" }} osImage: {{ or $nodeInfo.osImage `""` }} systemUUID: {{ or $nodeInfo.systemUUID `""` }} phase: Running
このリソースで、kwokに下記のような指示を出すことにより対象リソースを更新しています。
- どのリソースが(.spec.resourceRef)
- どの条件の際に(.spec.selector)
- どのようなステータスに設定されるのか?(.spec.next.statusTemplate)
これにより、NodeやPodの状態をコントロールして仮想的なNodeやPodとして動作するような状態を構築しています。今回のようにkwokctlを使用して環境を構築した際は実際にはStageリソースは作られず、組み込みのStageリソースが使用されます。
一方、kwokはDeploy kwok in a Clusterページにあるように、既存のクラスターに対してデプロイすることも可能です。
Stageリソースは、こういったケースで使用されるようになっているようです。
kwokの利用用途
kwokは元々はfake-kubeletとfake-k8sというリポジトリが元となっており、現在はKubernetesコミュニティのsig-schedulingのサブプロジェクトとして開発されているようです。
IntroductionページのUser Storiesを見ると、下記のような主にパフォーマンスに対するテストを中心としたユースケースが想定されているようです。
- 多数のNodeとPodを使用したkube-schedulerのテスト
- CRD Controllerの開発
- control planeのパフォーマンステスト
Adoptersページを見ると実際にkwokを使用したテストの例を見ることができますが、特に下記からパフォーマンステストやスケーラビリティのテストに使用されていることが伺えます。
- https://aptakube.com/blog/load-testing-kubernetes-clients-without-breaking-the-bank
- https://medium.com/@sunyanan.choochotkaew1/going-beyond-limits-scalability-test-ci-for-kubernetes-cni-operator-with-simulated-cluster-ed53e772dfa5
- https://github.com/headlamp-k8s/headlamp/blob/main/docs/development/testing.md#load-testing-headlamp
また、Kubernetesにおけるe2eテストツールでの利用がサポートされていたり、kube-scheduler-simulatorというKubernetesのスケジューラーの動作をシュミレートするツールの内部でkwokが利用されているようです。
おわりに
今回は仮想的なNodeとPodを使用して、大量のNodeやPodを起動した際のシュミレートを行い、パフォーマンスやスケーラビリティのテストなどを簡単に行えるkwokについて紹介しました。
ソースコードを見ると「kubeletなしでNodeやPodをどのようにしてreadyな状態で管理するのか」といったKubernetesの仕組みに対する理解にも繋がり非常に面白いツールだと感じたので、ぜひみなさんも利用してみてください。
連載バックナンバー
Think ITメルマガ会員登録受付中
全文検索エンジンによるおすすめ記事
- Project CalicoをKubernetesで使ってみる:構築編
- Kubernetesの基礎
- KubernetesのWorkloadsリソース(その1)
- 「Inspektor Gadget」でKubernetesクラスタをデバッグする
- Oracle Cloud Hangout Cafe Season7 #1「Kubnernetes 超入門」(2023年6月7日開催)
- Kubernetes環境を構築して、実際にコンテナを動かしてみよう
- 「kind」でローカル環境にKubernetesクラスターを構築する
- KubernetesのマニフェストをMagnumで実行する
- Kubernetes上のコンテナをIngressでインターネットに公開するまで
- KubernetesのWorkloadsリソース(その2)