「kwok」でKubernetesクラスターをシュミレーションする

2024年9月19日(木)
Daiki Hayakawa
第7回の今回は、大規模なKubernetesクラスターの検証を容易にする「kwok」について紹介します。

はじめに

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がインストールされている必要があります)。

1$ go install sigs.k8s.io/kwok/cmd/{kwok,kwokctl}@v0.6.0

インストールが完了したら、続けて下記のコマンドを実行してクラスターと3台のNodeを構築します。

1$ kwokctl create cluster
2$ kwokctl scale node --replicas=3

kwokctlには対象クラスター向けのkubectlやetcdctlなども含まれるので、今回はkwokctl経由でkubectlを使って行きましょう。

上記のコマンドを実行後kwokctl kubectl get nodeコマンドを実行すると、下記のように3台のNodeが作成されていることを確認できます。

1$ kwokctl kubectl get node
2NAME          STATUS   ROLES   AGE   VERSION
3node-000000   Ready    agent   7s    kwok-v0.6.0
4node-000001   Ready    agent   7s    kwok-v0.6.0
5node-000002   Ready    agent   7s    kwok-v0.6.0

Manage nodes and pods with kwokページのCreate a Nodeにあるように、Nodeを手動で作成することもできます。

実際に下記のようにして作成してみましょう。

コマンド実行後にkwokctl kubectl get nodeコマンドを実行してみると、下記のように手動で作成したkwok-node-0が作成されていることを確認できます。

1$ kwokctl kubectl get node
2NAME          STATUS   ROLES   AGE     VERSION
3kwok-node-0   Ready    agent   1m57s   kwok-v0.6.0
4node-000000   Ready    agent   5m      kwok-v0.6.0
5node-000001   Ready    agent   5m      kwok-v0.6.0
6node-000002   Ready    agent   5m      kwok-v0.6.0

それでは、引き続き下記のコマンドでPodを3個作成してみます。

1$ kwokctl scale pod --replicas=3

kwokctl kubectl get podコマンドを実行してみると、すぐにPodがRunningになっていることを確認できます。

1$ kwokctl kubectl get pod
2NAME         READY   STATUS    RESTARTS   AGE
3pod-000000   1/1     Running   0          4s
4pod-000001   1/1     Running   0          4s
5pod-000002   1/1     Running   0          4s

このように、簡単にNodeを作成し、作成したNodeでPodを動かしているのが確認できたかと思います。ちなみにkwokctl scale podコマンドで生成されるPodが下記のようなmanifestsになります(不要そうなフィールドは除去しています)。

01apiVersion: v1
02kind: Pod
03metadata:
04  labels:
05    kwok.x-k8s.io/kwokctl-scale: pod
06  name: pod-000000
07  namespace: default
08spec:
09  containers:
10  - image: busybox
11    imagePullPolicy: Always
12    name: container-0
13    volumeMounts:
14    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
15      name: kube-api-access-742pf
16      readOnly: true
17  tolerations:
18  - effect: NoExecute
19    key: node.kubernetes.io/not-ready
20    operator: Exists
21    tolerationSeconds: 300
22  - effect: NoExecute
23    key: node.kubernetes.io/unreachable
24    operator: Exists
25    tolerationSeconds: 300
26  volumes:
27  - name: kube-api-access-742pf
28    projected:
29      defaultMode: 420
30      sources:
31      - serviceAccountToken:
32          expirationSeconds: 3607
33          path: token
34      - configMap:
35          items:
36          - key: ca.crt
37            path: ca.crt
38          name: kube-root-ca.crt
39      - downwardAPI:
40          items:
41          - fieldRef:
42              apiVersion: v1
43              fieldPath: metadata.namespace
44            path: namespace

kwokで多くのリソースを作ってみる

それでは、実際に大量のNodeやPodを作成できるのか試してみます。まずは下記のコマンドを実行してNode数を増やしてみましょう。

1$ kwokctl scale node --replicas=20

kubectl get nodeコマンドを実行すると、node-000xxのNodeが20台に増えていることを確認できます。

01$ kwokctl kubectl get node --no-headers=true  | wc -l
02      21
03$ kwokctl kubectl get node
04NAME          STATUS   ROLES   AGE   VERSION
05kwok-node-0   Ready    agent   7m    kwok-v0.6.0
06node-000000   Ready    agent   10m   kwok-v0.6.0
07node-000001   Ready    agent   10m   kwok-v0.6.0
08node-000002   Ready    agent   10m   kwok-v0.6.0
09node-000003   Ready    agent   7s    kwok-v0.6.0
10node-000004   Ready    agent   7s    kwok-v0.6.0
11node-000005   Ready    agent   7s    kwok-v0.6.0
12node-000006   Ready    agent   7s    kwok-v0.6.0
13node-000007   Ready    agent   7s    kwok-v0.6.0
14node-000008   Ready    agent   7s    kwok-v0.6.0
15node-000009   Ready    agent   7s    kwok-v0.6.0
16node-000010   Ready    agent   7s    kwok-v0.6.0
17node-000011   Ready    agent   7s    kwok-v0.6.0
18node-000012   Ready    agent   7s    kwok-v0.6.0
19node-000013   Ready    agent   7s    kwok-v0.6.0
20node-000014   Ready    agent   7s    kwok-v0.6.0
21node-000015   Ready    agent   7s    kwok-v0.6.0
22node-000016   Ready    agent   7s    kwok-v0.6.0
23node-000017   Ready    agent   7s    kwok-v0.6.0
24node-000018   Ready    agent   7s    kwok-v0.6.0
25node-000019   Ready    agent   7s    kwok-v0.6.0

次に、下記のコマンドでPodもスケールしてみましょう。

1$ kwokctl scale pod --replicas=20

続けてkubectl get podコマンドを実行すると、Podが20台起動していることを確認できます。

01$ kubectl get node --no-headers=true  | wc -l
02      20
03$ kubectl get pod
04NAME         READY   STATUS    RESTARTS   AGE
05pod-000000   1/1     Running   0          2m54s
06pod-000001   1/1     Running   0          2m54s
07pod-000002   1/1     Running   0          2m54s
08pod-000003   1/1     Running   0          19s
09pod-000004   1/1     Running   0          19s
10pod-000005   1/1     Running   0          19s
11pod-000006   1/1     Running   0          19s
12pod-000007   1/1     Running   0          19s
13pod-000008   1/1     Running   0          19s
14pod-000009   1/1     Running   0          19s
15pod-000010   1/1     Running   0          19s
16pod-000011   1/1     Running   0          19s
17pod-000012   1/1     Running   0          19s
18pod-000013   1/1     Running   0          19s
19pod-000014   1/1     Running   0          19s
20pod-000015   1/1     Running   0          19s
21pod-000016   1/1     Running   0          19s
22pod-000017   1/1     Running   0          19s
23pod-000018   1/1     Running   0          19s
24pod-000019   1/1     Running   0          19s

先ほどは簡単に確認するためにNodeもPodも20台にスケールしましたが、手元の環境でも簡単に1000台程度にスケールさせることができました。

01# Node
02$ kwokctl scale node --replicas=1000
03No resource found, use default resource                                                   resource=node cluster=kwok
04Load resources                          counter=980 elapsed=4.2s resource=nodes replicas=1000 name=node cluster=kwok
05$ kubectl get node --no-headers=true  | wc -l
06    1001
07 
08# Pod
09$ kwokctl scale pod --replicas=1000
10No resource found, use default resource                                                                 resource=pod cluster=kwok
11Load resources                       counter=980 elapsed=2.9s namespace=default resource=pods replicas=1000 name=pod cluster=kwok
12$ kubectl get pod --no-headers=true  | wc -l
13    1000

Nodeについては少し待つだけで1,000台に増やすことができましたが、PodについてはDeploymentのreplicas変更を受けて徐々にスケールするようでした。

クラスターのクリーンアップ

これで基本的な動作が確認できたので、最後に作成したクラスターを削除してみます。削除するにはkwokctl delete clusterコマンドを実行するだけです。実行すると、下記のようにクラスターが削除されたことを確認できます。

1$ kwokctl delete cluster
2Cluster is stopping                                                                                           cluster=kwok
3Cluster is stopped                                                                                    elapsed=0.9s cluster=kwok
4Cluster is deleting                                                                                                  cluster=kwok
5Cluster is deleted

kwokのアーキテクチャ

このように、NodeやPodを仮想的に動作させる仕組みを持つkwokは、指定されたラベルやアノテーションが設定されたNodeを仮想的なNodeとして扱います。そして、仮想的なNodeにスケジュールされたPodに対し、kwokが管理するカスタムリソースであるStageリソースの条件をもとにPodのステータスをreadyに変更することで、Podが起動している状態をシミュレートする実装みになっているようでした。

また、その他Stageリソースを使ってNodeのステータス管理を行ったり、仮想的なNodeのNode Leaseを更新することでKubernetesから見て正常に動作しているNodeであると認識させるといったことも行っているようです。

Stageリソースは、例えば下記のようになっています。

01apiVersion: kwok.x-k8s.io/v1alpha1
02kind: Stage
03metadata:
04  name: node-initialize
05spec:
06  resourceRef:
07    apiGroup: v1
08    kind: Node
09  selector:
10    matchExpressions:
11    - key: '.status.conditions.[] | select( .type == "Ready" ) | .status'
12      operator: 'NotIn'
13      values:
14      - 'True'
15  next:
16    statusTemplate: |
17      {{ $now := Now }}
18      {{ $lastTransitionTime := or .metadata.creationTimestamp $now }}
19      conditions:
20      {{ range NodeConditions }}
21      - lastHeartbeatTime: {{ $now | Quote }}
22        lastTransitionTime: {{ $lastTransitionTime | Quote }}
23        message: {{ .message | Quote }}
24        reason: {{ .reason | Quote }}
25        status: {{ .status | Quote }}
26        type: {{ .type  | Quote}}
27      {{ end }}
28 
29      addresses:
30      {{ with .status.addresses }}
31      {{ YAML . 1 }}
32      {{ else }}
33      {{ with NodeIP }}
34      - address: {{ . | Quote }}
35        type: InternalIP
36      {{ end }}
37      {{ with NodeName }}
38      - address: {{ . | Quote }}
39        type: Hostname
40      {{ end }}
41      {{ end }}
42 
43      {{ with NodePort }}
44      daemonEndpoints:
45        kubeletEndpoint:
46          Port: {{ . }}
47      {{ end }}
48 
49      allocatable:
50      {{ with .status.allocatable }}
51      {{ YAML . 1 }}
52      {{ else }}
53        cpu: 1k
54        memory: 1Ti
55        pods: 1M
56      {{ end }}
57      capacity:
58      {{ with .status.capacity }}
59      {{ YAML . 1 }}
60      {{ else }}
61        cpu: 1k
62        memory: 1Ti
63        pods: 1M
64      {{ end }}
65 
66      {{ $nodeInfo := .status.nodeInfo }}
67      {{ $kwokVersion := printf "kwok-%s" Version }}
68      nodeInfo:
69        architecture: {{ or $nodeInfo.architecture "amd64" }}
70        bootID: {{ or $nodeInfo.bootID `""` }}
71        containerRuntimeVersion: {{ or $nodeInfo.containerRuntimeVersion $kwokVersion }}
72        kernelVersion: {{ or $nodeInfo.kernelVersion $kwokVersion }}
73        kubeProxyVersion: {{ or $nodeInfo.kubeProxyVersion $kwokVersion }}
74        kubeletVersion: {{ or $nodeInfo.kubeletVersion $kwokVersion }}
75        machineID: {{ or $nodeInfo.machineID `""` }}
76        operatingSystem: {{ or $nodeInfo.operatingSystem "linux" }}
77        osImage: {{ or $nodeInfo.osImage `""` }}
78        systemUUID: {{ or $nodeInfo.systemUUID `""` }}
79      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を使用したテストの例を見ることができますが、特に下記からパフォーマンステストやスケーラビリティのテストに使用されていることが伺えます。

また、Kubernetesにおけるe2eテストツールでの利用がサポートされていたり、kube-scheduler-simulatorというKubernetesのスケジューラーの動作をシュミレートするツールの内部でkwokが利用されているようです。

おわりに

今回は仮想的なNodeとPodを使用して、大量のNodeやPodを起動した際のシュミレートを行い、パフォーマンスやスケーラビリティのテストなどを簡単に行えるkwokについて紹介しました。

ソースコードを見ると「kubeletなしでNodeやPodをどのようにしてreadyな状態で管理するのか」といったKubernetesの仕組みに対する理解にも繋がり非常に面白いツールだと感じたので、ぜひみなさんも利用してみてください。

株式会社スリーシェイク Sreake事業部
Webサービスの開発やクラウドインフラの構築・運用、マネージドKubernetesサービスの開発を経てスリーシェイクにJoin。顧客システムへのKubernetes導入支援を中心に、インフラ構築・設計の支援を行っています。著書に「Kubeletから読み解くKubernetesのコンテナ管理の裏側」「Kube API Server ~Kubernetes API Serverの内部実装を見てみよう~」など。
---
スリーシェイクは、ITインフラ領域の技術力に強みをもつテクノロジーカンパニーです。SREコンサルティング事業「Sreake」では、AWS/Google Cloud/Kubernetesに精通したプロフェッショナルが技術戦略から設計・開発・運用を一貫してサポートしています。また、ノーコード型ETLツール「Reckoner」、フリーランスエンジニア特化型人材紹介サービス「Relance」、セキュリティサービス「Securify」を提供しています。
会社HP: https://3-shake.com/

連載バックナンバー

仮想化/コンテナ技術解説
第13回

「kubeshark」でKubernetesのトラフィックをリアルタイムに可視化する

2025/3/7
第13回の今回は、KubernetesのAPIトラフィックアナライザである「kubeshark」によるリアルタイムのトラフィック可視化について解説します。
仮想化/コンテナ技術解説
第12回

「OpenClarity」によるk8sワークロードの脆弱性スキャン

2025/2/21
第12回の今回は、セキュリティリスクを検出するOSSツール「OpenClarity」におけるKubernetesワークロードの脆弱性スキャンを解説します。
仮想化/コンテナ技術解説
第11回

「Longhorn」で分散ブロックストレージを簡単に管理する

2025/2/7
第11回の今回は、Kubernetes向けの分散ブロックストレージ「Longhorn」の特徴と導入方法、バックアップ・リストアの手順を解説します。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています