EVCモードで仮想マシンにCPU機能がどのように公開されるか追ってみる
vSphere環境のクラスタ内に異なるCPU世代のサーバーがあった場合に、異なるCPU世代のサーバー間でvMotionができるようにするため、Enhanced vMotion Compatibility (EVC)という機能があります。EVCを利用した場合に、ゲストOS上でどのようにCPU機能が公開されているのか追ってみたので書いてみようと思います。
構成
- vCenter 7.0 U3
- ESXi 7.0 Update 3
- Intel NUC NUC7i3BNH
- Intel(R) Core(TM) i3-7100U CPU @ 2.40GHz
- Ubuntu 20.04
CPUの仮想化について
仮想マシンは、CPUの仮想化とハードウェアの仮想化によって実現しています。
CPUの仮想化は、ソフトウェアベースのCPU仮想化とハードウェアアシストによるCPU仮想化という方法があります。
ソフトウェアベースのCPU仮想化は、仮想マシン上で動作するOSが、実際のハードウェアリソースを直接アクセスする代わりに、仮想化ソフトウェア(ハイパーバイザー)を介してリソースにアクセスします。ソフトウェアベースのCPU仮想化は、ハードウェアリソースに依存しないため、柔軟性がありますが、パフォーマンスの低下が課題となる場合があります。
ハードウェアアシストによるCPU仮想化は、ハイパーバイザーをサポートするハードウェア機能を使用して仮想化を実現する方法です。この方式では、ハイパーバイザーはハードウェア機能を使用して、仮想マシン上で実行されるOSが直接ハードウェアリソースにアクセスできるようにします。
仮想化にハードウェア アシストを使用すると、コードを変換する必要がなくなります。その結果、システム呼び出しやトラップを多用するワークロードが、ネイティブに近い速度で実行されます。
Intel CPUで利用可能なハードウェアアシストによるCPU仮想化は、Intel VT-x(Virtualization Technology)と呼ばれています。
Intel VT-xは、仮想マシンの動作に必要なCPUリソースを割り当てるための仮想マシンモニター(VMM)と連携することによって、CPUの仮想化を実現します。
VMMと仮想マシン
VMMは、仮想マシンを制御するソフトウェアであり、CPUの仮想化を実現するために必要です。VMMは、仮想マシンの制御と監視を行い、仮想マシンがハードウェアにアクセスするための仮想的なインターフェースを提供します。
CPUのモードには、リング(Ring)と呼ばれる階層があり、0から3のレベルに分かれています。リング0は最も特権の高いレベルであり、オペレーティングシステムカーネルなどのシステムソフトウェアが実行されます。一方、リング3は最も特権の低いレベルであり、一般的なアプリケーションソフトウェアが実行されます。
VMMと仮想マシンの切り替えは、CPUのモードを変更することで実現されます。
VMMはVMX rootモードで動作し、仮想マシンはVMX non-rootモードで動作します。
VMX rootモードは、VMMが動作する特権モードであり、物理CPUに直接アクセスすることができます。このモードでは、VMMが全ての物理リソースをコントロールでき、仮想マシンの作成、削除、スケジューリングなどの制御を行うことができます。
VMX non-rootモードは、仮想マシンが動作する非特権モードです。このモードでは、仮想マシンが割り当てられた仮想リソース(CPU、メモリ、デバイスなど)にアクセスすることができます。
VMMは、VMX non-rootモードで動作する仮想マシンの動作を監視し、割り込みなどにより必要に応じて「VM Exit」という命令を発行し、CPUはVMX non-rootモードからVMX rootモードに切り替えられ、以降の処理はVMMが行います。
VMMは必要な処理が終了すると、またVMX non-rootモードに戻すため、「VM Entry」という命令を発行し、CPUに動作モードを切り替えさせます。
このモードの切り替えにはオーバーヘッドが伴いますが、Intel VT-xの仮想化支援機能によりVMMと仮想マシンの切り替えを高速化しています。
仮想CPUについて
VMMは、仮想マシンに提供するCPU情報を仮想デバイスとしてエミュレートすることで実現されます。具体的には、VMMは仮想マシンに対して、物理CPUの情報を模擬した仮想CPUを提供します。
この仮想CPUは、ハードウェアアシストによるCPU仮想化が使用されている場合は、実際の物理CPUによってバックエンドで実行されます。
仮想CPUは、物理CPUと同様に、プロセッサの機能を提供するレジスタを持ちます。VMMは、仮想マシンに提供するCPU情報を設定するために、これらのレジスタを制御します。たとえば、VMMは、仮想CPUのレジスタに物理CPUの識別子を設定したり、仮想CPUが使用できる拡張命令セットを制限することができます。
EVCについて
Enhanced vMotion Compatibility (EVC)は、vSphere環境のクラスタ内に異なるCPUのサーバーがあった場合に、異なるCPU世代を搭載したサーバー間でvMotionができるようにするためのクラスタの機能になります。
EVCは、仮想マシンが動作するCPU世代の最低限の世代を特定し、仮想CPUが使用できるCPUの機能を制限することで、異なるCPU世代を搭載したサーバー間を移行できるようになります。
vSphere6.7以降、仮想ハードウェア バージョン 14 以降では、仮想マシンごとにEVCを設定できるようになっています。
ゲストOSからCPU情報の取得方法
Linuxカーネルは、/proc/cpuinfoファイルでCPU情報を提供します。このファイルには、物理CPUの情報とともに、仮想CPUの情報が含まれています。
CPU情報を確認するには、CPUID命令を利用します。
CPUID命令は、CPUの機能やパフォーマンスを調べるために使用される命令です。CPUID命令を実行すると、EAXレジスタに値を設定し、CPUID命令を実行すると、EAXレジスタの値に基いて特定の機能を実行します。
EAXレジスタに1を設定しCPUID命令を実行すると、EAXにプロセッサのファミリータイプやモデルなどの情報(プロセッサ・シグネチャ)、EBXとEDXとECXに機能フラグを返してくれます。
EAXに格納される情報のフォーマットは以下のとおりです。
3:0 - ステッピング 7:4 - モデル 11:8 - ファミリー 13:12 - プロセッサタイプ 19:16 - 拡張モデル 27:20 - 拡張ファミリー
では、ゲストOS上で、CPUID命令を使ったCPU情報を取得してみます。EVCを設定しない状態でまずは見てみます。
PythonにCPUID命令を実行できる、cpuidというパッケージがあるため、こちらを利用します。
インタラクティブモードで、以下のように実行してみます。
$ python3 -m venv venv
$ source /home/username/project_directory/venv/bin/activate
(venv) $ pip install cpuid
(venv) $ python
Python 3.8.10 (default, Nov 14 2022, 12:59:47)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cpuid
>>> eax, ebx, ecx, edx = cpuid.cpuid(1) # EAXレジスタに1を設定
>>> print(f"EAX: {eax:08x}")
EAX: 000806e9
>>> print(f"EBX: {ebx:08x}")
EBX: 00010800
>>> print(f"ECX: {ecx:08x}")
ECX: fffa3203
>>> print(f"EDX: {edx:08x}")
EDX: 0f8bfbff
000806e9をフォーマットに変換すると以下のようになります。
stepping id = 0x9 (9) model = 0xe (14) family = 0x6 (6) processor type = primary processor (0) extended model = 0x8 (8) extended family = 0x0 (0)
| Extended Family | Family | Extended Model | Model | Stepping | |
|---|---|---|---|---|---|
| 0 | 0x6 | 0x8 | 0xE | 0x9 | Family 6 Model 142 Stepping 9 |
modelはExtended ModelとModelを合わせた8Eの142になります。
Modelの142はKaby Lakeに該当します。
cpuinfoの値は以下のようになります。
modelが142と表示されることが分かります。
$ cat /proc/cpuinfo processor : 0 vendor_id : GenuineIntel cpu family : 6 model : 142 model name : Intel(R) Core(TM) i3-7100U CPU @ 2.40GHz stepping : 9 microcode : 0x84 cpu MHz : 2399.999 cache size : 3072 KB physical id : 0 siblings : 1 core id : 0 cpu cores : 1 apicid : 0 initial apicid : 0 fpu : yes fpu_exception : yes cpuid level : 22 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault invpcid_single pti ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid rdseed adx smap clflushopt xsaveopt xsavec xgetbv1 xsaves arat arch_capabilities bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs itlb_multihit srbds mmio_stale_data retbleed bogomips : 4799.99 clflush size : 64 cache_alignment : 64 address sizes : 45 bits physical, 48 bits virtual power management:
EVC環境でのCPU情報の見え方
クラスタのEVCをHaswellに変更してゲストOSの状態を確認してみます。
(venv) $ python
Python 3.8.10 (default, Nov 14 2022, 12:59:47)
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cpuid
>>> eax, ebx, ecx, edx = cpuid.cpuid(1)
>>> print(f"EAX: {eax:08x}")
EAX: 000306f0
>>> print(f"EBX: {ebx:08x}")
EBX: 00010800
>>> print(f"ECX: {ecx:08x}")
ECX: fffa3203
>>> print(f"EDX: {edx:08x}")
EDX: 0f8bfbff
000306f0をフォーマットに変換すると以下のようになります。
stepping id = 0x0 (0) model = 0xf (15) family = 0x6 (6) processor type = primary processor (0) extended family = 0x0 (0) extended model = 0x3 (3)
| Extended Family | Family | Extended Model | Model | Stepping | |
|---|---|---|---|---|---|
| 0 | 0x6 | 0x3 | 0xF | 0 | Family 6 Model 63 Stepping 0 |
Modelの63はHaswellに該当します。
cpuinfoの値は以下のようになります。
modelが63と表示されることが分かります。
$ cat /proc/cpuinfo processor : 0 vendor_id : GenuineIntel cpu family : 6 model : 63 model name : Intel(R) Core(TM) i3-7100U CPU @ 2.40GHz stepping : 0 microcode : 0x84 cpu MHz : 2399.999 cache size : 3072 KB physical id : 0 siblings : 1 core id : 0 cpu cores : 1 apicid : 0 initial apicid : 0 fpu : yes fpu_exception : yes cpuid level : 13 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc cpuid pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm cpuid_fault invpcid_single pti ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid xsaveopt arat arch_capabilities bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs itlb_multihit mmio_stale_data retbleed bogomips : 4799.99 clflush size : 64 cache_alignment : 64 address sizes : 45 bits physical, 48 bits virtual power management:
機能フラグにも違いが生じていることが分かります。
EVCにより、仮想CPUのモデル名や機能を制限されていることを確認できました。
また、vCenterのクラスタのVMware EVCの画面を見ると、CPU 機能セットに以下の設定があり、こちらの値で上書きされていることが分かります。EVCのVal:6やVal:0x3fはEAXの値と一致することが分かります。
| 名前 | キー | 値 |
|---|---|---|
| CPU ファミリ | cpuid.FAMILY | Val:6 |
| CPU モデル | cpuid.MODEL | Val:0x3f |
| CPU ステッピング | cpuid.STEPPING | Val:0 |
EVCのキーや値を見ていると、EVCで上書きしているのは、modelやfamily、機能フラグで、model nameは上書きしていないようでした。
vSphere6.5ではCPUID機能フラグという画面がありましたが、vSphere7.0では表示が変わったようです。
Linuxカーネルでの動作
Linuxカーネルでの/proc/cpuinfoのCPU情報の表示する処理について見ていきます。CPU情報を表示する処理はこちらになるようです。
CPUID命令を実行してCPUのmodel名やfamily名を取得しているのはこのあたりのように見えます。
model nameはこのあたりで取得してそうに見えました。
VMwareの仮想マシンで動作しているか判別する方法
Linuxカーネルの中に、vmware_platform()という関数があり、EAXに CPUID_VMWARE_INFO_LEAF を渡すと、hyper_vendor_idに VMwareVMware と返すことが分かります。
実際に、以下のコマンドで確認すると、ハイパーバイザーとしてVMwareが使われていることを確認できます。
$ lscpu : Hypervisor vendor: VMware :
参考
Cluster API Provider vSphereとArgo CDを使ったKubernetesクラスタの管理
前回、Cluster API Provider vSphereを使ってKubernetesクラスタを作成してみました。今回は、Cluster API Provider vSphereとArgo CDを使ってKubernetesクラスタを管理する方法を試してみたいと思います。
構成
- vCenter 7.0 U3
- ESXi 7.0 Update 3
- Kubespray
- kubernetes v1.25.5
- Cluster API v1.3.2
- Argo CD v2.6.0
Argo CDについて
Argo CD は、Kubernetes のための宣言的な GitOps 継続的デリバリーツールです。GitOpsパターンに従って、望ましいアプリケーション状態を定義するための信頼できる情報源(source of truth)として Git リポジトリを使用します。
Argo CDで利用できるファイルは以下のようなものがあります。
- kustomize
- helmチャート
- jsonnetファイル
- manifestsファイル
Gitリポジトリに、アプリケーションの定義ファイルを用意しておくことで、クラスタに自動デプロイすることができます。
Cluster APIとArgo CDを組み合わせて使うことで、Kubernetesクラスタの管理とアプリケーションの管理をGitリポジトリで一元管理することができるようになります。
Argo CDのインストール
前回作成した、management clusterに以下のようにArgo CDをインストールします。
$ kubectl create namespace argocd $ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
以下のように、Argo CD CLI をダウンロードします。
$ curl -sSL -o argocd-linux-amd64 https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64 $ sudo install -m 555 argocd-linux-amd64 /usr/local/bin/argocd $ rm argocd-linux-amd64
デフォルトだと、Argo CD API サーバーは外部 IPで公開されていないため、API サーバーにアクセスするためにIngressを設定します。
$ vi ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-server-ingress
namespace: argocd
annotations:
kubernetes.io/ingress.class: "nginx"
kubernetes.io/tls-acme: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
tls:
- hosts:
- argocd.kube.home.lab
secretName: ingress-tls
rules:
- host: argocd.kube.home.lab
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
name: https
$ kubectl apply -f ingress.yaml
ブラウザから https://argocd.kube.home.lab にアクセスできることを確認します。
ブラウザでパスワードが求めれるため、以下のコマンドでパスワードを取得します。
$ kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo
以下のように、CLI を使用してログインします。
$ argocd login argocd.kube.home.lab
動作確認
Argo CDのリポジトリにguestbookというアプリケーションが用意されているため、こちらを使って動作確認を行ってみます。
以下のコマンドでアプリケーションの登録を行ってみます。
$ argocd app create guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path guestbook --dest-server https://kubernetes.default.svc --dest-namespace default
guestbookのアプリケーションは自動同期の設定がされていないため、アプリケーションの登録後に以下のように同期を行います。同期を行うことで、リポジトリからマニフェストを取得し、kubectl applyマニフェストを実行します。
$ argocd app sync guestbook
Health StatusがHealthyとなっており、正常に同期されたことが分かります。
$ argocd app get guestbook Name: argocd/guestbook Project: default Server: https://kubernetes.default.svc Namespace: default URL: https://argocd.kube.home.lab/applications/guestbook Repo: https://github.com/argoproj/argocd-example-apps.git Target: Path: guestbook SyncWindow: Sync Allowed Sync Policy: <none> Sync Status: Synced to (53e28ff) Health Status: Healthy
プライベートリポジトリの登録
Argo CDを利用するために、Gitリポジトリが必要なため、プライベートリポジトリとしてBitbucketのリポジトリを用意します。
BitbucketのPersonal settingsでSSH keysにsshの公開鍵を登録しておきます。
以下のように、sshの秘密鍵を利用し、プライベートリポジトリを登録します。
$ argocd repo add git@bitbucket.org:<username>/argocd-apps.git --ssh-private-key-path ~/.ssh/id_rsa
リポジトリにマニフェストファイルを追加
$ tree
.
├── app
│ └── workload-nginx-app.yaml
├── capi-cluster
│ └── cluster.yaml
├── management
│ └── capi-cluster-app.yaml
├── README.md
└── workload
└── nginx
├── deployment.yaml
└── service.yaml
ファイルについて解説していきます。
こちらは、Argro CDのApplicationを追加するマニフェストファイルになります。動作確認では、argocdコマンドで行っていましたが、マニフェストファイルでも管理することができます。management clusterにデプロイする形となります。
$ vi management/capi-cluster-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: capi-cluster
namespace: argocd
spec:
destination:
namespace: default
server: 'https://kubernetes.default.svc'
source:
path: capi-cluster
repoURL: 'git@bitbucket.org:<username>/argocd-apps.git'
targetRevision: HEAD
project: default
syncPolicy:
automated: {}
capi-cluster/cluster.yamlには、clusterctl で生成したworkload clusterのcluster.yamlの中身をそのまま貼り付けます。
$ clusterctl generate cluster vsphere-quickstart \
--infrastructure vsphere \
--kubernetes-version v1.25.5 \
--control-plane-machine-count 1 \
--worker-machine-count 3 > cluster.yaml
こちらは、動作確認用のnginxのサービスをworkload clusterへデプロイするための、Argro CDのApplicationのマニフェストファイルになります。
serverには、workload clusterのcontrole planeのエンドポイントのURLを指定しておきます。
$ vi app/workload-nginx-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: nginx
namespace: argocd
spec:
destination:
namespace: default
server: 'https://10.0.50.40:6443'
source:
path: workload/nginx
repoURL: 'git@bitbucket.org:<username>/argocd-apps.git'
targetRevision: HEAD
project: default
syncPolicy:
automated: {}
動作確認用のnginxのサービスの設定になります。
$ vi workload/nginx/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
ports:
- containerPort: 80
$ vi workload/nginx/service.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: nginx
name: nginx
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
workload clusterのインストール
以下のコマンドで、workload clusterをインストールするための、Argo CDのApplicationを設定します。
$ kubectl apply -f management/capi-cluster-app.yaml
Argo CDのUIかコマンドでCluster APIによりworkload clusterのインストールされている様子を確認します。
$ argocd app get argocd/capi-cluster
capi-clusterのステータスがProgressingのままとなっていますが、こちらはCNIのインストールが完了していたいためになります。
workload clusterにCNIのインストールを行います。
まずは、workload clusterにアクセスするためのconfigファイルを以下のように生成します。
$ clusterctl get kubeconfig vsphere-quickstart > vsphere-quickstart.kubeconfig
以下のようにcalicoをインストールします。
$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/calico.yaml
capi-clusterのステータスがHealthyとなれば、workload clusterのインストールの完了となります。
$ argocd app get argocd/nginx Name: argocd/capi-cluster Project: default Server: https://kubernetes.default.svc Namespace: default URL: https://argocd.kube.home.lab/applications/capi-cluster Repo: git@bitbucket.org:<username>/argocd-apps.git Target: HEAD Path: capi-cluster SyncWindow: Sync Allowed Sync Policy: Automated Sync Status: Synced to HEAD (1c29848) Health Status: Healthy
ArgoCDのUI上だと以下のように見えます。

workload clusterでの動作確認
ArgoCDでworkload clusterを扱えるようにするために、clusterの登録を行います。
workload clusterのコンテキスト名を取得します。
$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl config get-contexts CURRENT NAME CLUSTER AUTHINFO NAMESPACE * vsphere-quickstart-admin@vsphere-quickstart vsphere-quickstart vsphere-quickstart-admin
以下のようにArgoCDにworkload clusterを新クラスタとして登録します。
$ argocd cluster add vsphere-quickstart-admin@vsphere-quickstart --insecure --kubeconfig ./vsphere-quickstart.kubeconfig
以下のコマンドで、動作確認用のnginxのサービスをworkload clusterへデプロイするための、Argro CDのApplicationを設定します。
$ kubectl apply -f app/workload-nginx-app.yaml
nginxのステータスがHealthyとなれば、nginxのインストールの完了となります。
$ argocd app get argocd/nginx Name: argocd/nginx Project: default Server: https://10.0.50.40:6443 Namespace: default URL: https://argocd.kube.home.lab/applications/nginx Repo: git@bitbucket.org:<username>/argocd-apps.git Target: HEAD Path: workload/nginx SyncWindow: Sync Allowed Sync Policy: Automated Sync Status: Synced to HEAD (1c29848) Health Status: Healthy
おわりに
ArgoCDを使ってCluster APIを使ったKubernetesクラスタの管理を試してみました。ArgoCDへ作成したworkload clusterのクラスタ登録をどうやって自動で行うかなど課題がありますが、作りこんでいけば、CNIのインストールや、vSphere CSIの再インストールなども自動化して、Gitで管理できるようになるのではと思っています。
参考
Cluster API Provider vSphereでvSphere CSI ControllerがCrashLoopBackOffとなる事象を追ってみる
前回、Cluster API Provider vSphereを使ってKubernetesクラスタを作成してみました。今回は、Kubernetesクラスタを作成した際にvSphere CSI ControllerがCrashLoopBackOffとなる事象について原因を追ってみたので書いてみようと思います。
構成
- vCenter 7.0 U3
- ESXi 7.0 Update 3
- Kubespray
- kubernetes v1.25.5
- Cluster API v1.3.2
- vSphere CPI v1.18.1
- vSphere CSI v2.1.0
- kubernetes v1.25.5はサポートされない
発生した事象について
前回、Cluster APIを使ってKubernetesクラスタを作成し、calicoのインストールが完了後のpodの状態がこちらです。vsphere-csi-controller が CrashLoopBackOff となっています。
$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl get pod -A NAMESPACE NAME READY STATUS RESTARTS AGE kube-system calico-kube-controllers-74677b4c5f-m77w8 1/1 Running 0 16m kube-system calico-node-7bcpz 1/1 Running 0 16m kube-system calico-node-w7fs9 1/1 Running 0 16m kube-system calico-node-wckhz 1/1 Running 0 16m kube-system calico-node-zlc5b 1/1 Running 0 16m kube-system coredns-565d847f94-chn5z 1/1 Running 0 21m kube-system coredns-565d847f94-dn4zq 1/1 Running 0 21m kube-system etcd-vsphere-quickstart-d7r7k 1/1 Running 0 21m kube-system kube-apiserver-vsphere-quickstart-d7r7k 1/1 Running 0 21m kube-system kube-controller-manager-vsphere-quickstart-d7r7k 1/1 Running 0 21m kube-system kube-proxy-br9fn 1/1 Running 0 20m kube-system kube-proxy-cvfzq 1/1 Running 0 20m kube-system kube-proxy-qxlj2 1/1 Running 0 20m kube-system kube-proxy-rmzww 1/1 Running 0 21m kube-system kube-scheduler-vsphere-quickstart-d7r7k 1/1 Running 0 21m kube-system kube-vip-vsphere-quickstart-d7r7k 1/1 Running 0 21m kube-system vsphere-cloud-controller-manager-ctkr7 1/1 Running 0 20m kube-system vsphere-cloud-controller-manager-hl5w2 1/1 Running 0 20m kube-system vsphere-cloud-controller-manager-r9dhk 1/1 Running 0 20m kube-system vsphere-csi-controller-5d6484644d-nqwlh 4/5 CrashLoopBackOff 8 (37s ago) 21m kube-system vsphere-csi-node-h9p2x 3/3 Running 0 21m kube-system vsphere-csi-node-hbgsl 3/3 Running 0 20m kube-system vsphere-csi-node-tr99v 3/3 Running 0 20m kube-system vsphere-csi-node-xk2jq 3/3 Running 0 20m
ログの確認
まずは、vsphere-csi-controllerの状態について以下のコマンドで確認してみます。ログを見ると、以下のようなエラーが発生します。エラーは、vCenter名の証明書が不明な認証局によって署名されているためです。デフォルトでは、vCenterは証明書を発行する認証局(VMCA)があり、自己署名証明書を作成しています。
$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl logs vsphere-csi-controller-5d6484644d-nqwlh -c vsphere-csi-controller -n kube-system level=fatal msg="grpc failed" error="Post https://vcsa-1.home.lab:443/sdk: x509: certificate signed by unknown authority"
自己署名証明書を使うオプションががあり、csi-vsphere.conf で insecure-flag を利用する必要があります。
Create a Kubernetes Secret for vSphere Container Storage Plug-in
Cluster APIを使ってKubernetesクラスタで、vSphere CSIのcsi-vsphere.confの設定がどのようになっているかを確認するには、以下のコマンドを実行します。
$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl edit secret csi-vsphere-config -n kube-system # Please edit the object below. Lines beginning with a '#' will be ignored, # and an empty file will abort the edit. If an error occurs while saving this file will be # reopened with the relevant failures. # apiVersion: v1 data: csi-vsphere.conf: <設定値> kind: Secret metadata: creationTimestamp: "<date>" name: csi-vsphere-config namespace: kube-system resourceVersion: "3908" uid: 2b96cb9c-5444-425a-a625-f1b6c4ca5754 type: Opaque
csi-vsphere.confにbase64でエンコードされた設定値が入っており、値を確認してみます。設定値を見ると、 insecure-flag が設定されていないことが分かります。
$ echo "設定値" | base64 -d [Global] cluster-id = "default/vsphere-quickstart" [VirtualCenter "vcsa-1.home.lab"] user = "vCenter username" password = "vCenter password" datacenters = "datacenter name" [Network] public-network = "portgroup name"
csi-vsphere.confの定義ファイルに、 insecure-flag = "true" を追加し、base64でデコードします。
$ vi csi-vsphere.conf [Global] cluster-id = "default/vsphere-quickstart" [VirtualCenter "vcsa-1.home.lab"] insecure-flag = "true" user = "vCenter username" password = "vCenter password" datacenters = "datacenter name" [Network] public-network = "portgroup name" $ base64 csi-vsphere.conf <出力値>
base64でデコードした値をsecretのcsi-vsphere.confに入力し、保存します。
$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl edit secret csi-vsphere-config -n kube-system # Please edit the object below. Lines beginning with a '#' will be ignored, # and an empty file will abort the edit. If an error occurs while saving this file will be # reopened with the relevant failures. # apiVersion: v1 data: csi-vsphere.conf: <値を入れ替え> kind: Secret metadata: creationTimestamp: "<date>" name: csi-vsphere-config namespace: kube-system resourceVersion: "3908" uid: 2b96cb9c-5444-425a-a625-f1b6c4ca5754 type: Opaque
podを再起動し、vsphere-csi-controllerが起動してくることを確認します。
$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl rollout restart deploy vsphere-csi-controller -n kube-system
再起動後のpodの状態はこちらです。
$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl get pod -A NAMESPACE NAME READY STATUS RESTARTS AGE kube-system calico-kube-controllers-74677b4c5f-87sdx 1/1 Running 0 66m kube-system calico-node-26nls 1/1 Running 0 66m kube-system calico-node-kgnzr 1/1 Running 0 66m kube-system calico-node-qhmm6 1/1 Running 0 66m kube-system calico-node-rpsbk 1/1 Running 0 66m kube-system coredns-565d847f94-drphk 1/1 Running 0 68m kube-system coredns-565d847f94-qc66x 1/1 Running 0 68m kube-system etcd-vsphere-quickstart-9nzbv 1/1 Running 0 68m kube-system kube-apiserver-vsphere-quickstart-9nzbv 1/1 Running 0 68m kube-system kube-controller-manager-vsphere-quickstart-9nzbv 1/1 Running 0 68m kube-system kube-proxy-9ntsx 1/1 Running 0 68m kube-system kube-proxy-dqrpj 1/1 Running 0 67m kube-system kube-proxy-v5j24 1/1 Running 0 67m kube-system kube-proxy-zqp7h 1/1 Running 0 67m kube-system kube-scheduler-vsphere-quickstart-9nzbv 1/1 Running 0 68m kube-system kube-vip-vsphere-quickstart-9nzbv 1/1 Running 0 68m kube-system vsphere-cloud-controller-manager-4fmp2 1/1 Running 0 67m kube-system vsphere-cloud-controller-manager-r7sq7 1/1 Running 0 67m kube-system vsphere-cloud-controller-manager-z6dcx 1/1 Running 0 67m kube-system vsphere-csi-controller-d695747dc-qhjqs 5/5 Running 0 35s kube-system vsphere-csi-node-98z65 3/3 Running 0 67m kube-system vsphere-csi-node-jjx8r 3/3 Running 0 67m kube-system vsphere-csi-node-rfg6w 3/3 Running 0 67m kube-system vsphere-csi-node-stlbj 3/3 Running 0 68m
起動はできましたが、vsphere-csi-controllerのバージョンを確認してみると、v2.1.0を利用していることが分かります。
VMware vSphere Container Storage Plug-inのリリースノートを見ると、v2.1.0はKubernetes v1.19までしかサポートしていないことが分かります。
VMware vSphere Container Storage Plug-in 2.1 Release Notes
この問題については、こちらのissueでも議論されているようでした。
vSphere CSI Controllerがデプロイされる動作
Cluster API Provider vSphereで、vSphere CSI Controllerが自動的にデプロイされている理由としては、ClusterResourceSetが利用されています。
ClusterResourceSetは、workload clusterを作成した際に、workload clusterに追加のコンポーネントを自動的にインストールできるようにする実験的な機能です。
CNIを毎回コマンドでインストールしているため、自動でインストールされると便利ではあります。
ClusterResourceSetの設定内容について確認してみようと思います。
workload clusterを作成する際に、cluster.yamlを作成しましたが、こちらの中身を見てみます。
$ clusterctl generate cluster vsphere-quickstart \
--infrastructure vsphere \
--kubernetes-version v1.25.5 \
--control-plane-machine-count 1 \
--worker-machine-count 3 > cluster.yaml
ClusterResourceSetのリソースの中に、csi-vsphere-configがあることが分かります。csi-vsphere-configの中身には、insecure-flag が設定されていないことが分かります。
$ vi cluster.yaml
---
apiVersion: addons.cluster.x-k8s.io/v1beta1
kind: ClusterResourceSet
metadata:
labels:
cluster.x-k8s.io/cluster-name: vsphere-quickstart
name: vsphere-quickstart-crs-0
namespace: default
spec:
clusterSelector:
matchLabels:
cluster.x-k8s.io/cluster-name: vsphere-quickstart
resources:
- kind: Secret
name: vsphere-csi-controller
- kind: ConfigMap
name: vsphere-csi-controller-role
- kind: ConfigMap
name: vsphere-csi-controller-binding
- kind: Secret
name: csi-vsphere-config
- kind: ConfigMap
name: csi.vsphere.vmware.com
- kind: ConfigMap
name: vsphere-csi-node
- kind: ConfigMap
name: vsphere-csi-controller
- kind: Secret
name: cloud-controller-manager
- kind: Secret
name: cloud-provider-vsphere-credentials
- kind: ConfigMap
name: cpi-manifests
:
---
apiVersion: v1
kind: Secret
metadata:
name: csi-vsphere-config
namespace: default
stringData:
data: |
apiVersion: v1
kind: Secret
metadata:
name: csi-vsphere-config
namespace: kube-system
stringData:
csi-vsphere.conf: |+
[Global]
cluster-id = "default/vsphere-quickstart"
[VirtualCenter "vcsa-1.home.lab"]
user = "vCenter username"
password = "vCenter password"
datacenters = "datacenter name"
[Network]
public-network = "portgroup name"
type: Opaque
type: addons.cluster.x-k8s.io/resource-set
cluster.yamlのcsi-vsphere-configを書き換えても、vsphere-csi-controllerが起動してくることが分かりました。
また、csi-vsphere-configにthumbprintオプションを追加し、vCenterのサーバー証明書のthumbprintやvCenterのCAのthumbprintを指定してみましたが、エラーは解消しませんでした。
現状は、csi-vsphere-configを書き換えても、vSphere CSIはサポートされない組み合わせとなってしまうため、ClusterResourceSetを使う場合でもバージョンの更新が課題になってきそうに見えています。
Continuous ClusterResourceSetStrategy · Issue #4807 · kubernetes-sigs/cluster-api · GitHub
また、clusterctl.yamlで EXP_CLUSTER_RESOURCE_SET を false にしても、Taintの設定上podが起動してこないため、現状は一度vSphere CSIを削除し、最新バージョンのvSphere CSIをインストールし直さないといけないようには見えており、他にいい方法がないか気になっています。
参考
KubernetesクラスタにvSphere CPIとvSphere CSIをインストールする
前回は、Cluster APIを使ってvSphere上にKubernetesクラスタを作成してみました。今回は、vSphere CPIとvSphere CSIのインストールを試してみたいと思います。
構成
- vCenter 7.0 U3
- ESXi 7.0 Update 3
- Kubespray
- kubernetes v1.25.5
- vSphere CPI v1.25.0
- vSphere CSI v2.7.0
vSphere CPIとvSphere CSIについて
vSphere CSI は、KubernetesのPersistentVolume(PV)にてvSphere ストレージを利用するための、Container Storage Interface(CSI)プラグインになります。vSphere CSIを使うと、PVは仮想マシンの1つの仮想ディスクとしてworkerノードに接続されます。また、vSphere CSIで作成された仮想ディスクはFirst Class Disk(FCD)と呼ばれ、仮想マシンに接続しないと管理ができなかった通常の仮想ディスクと異なり、EBSのように独立して仮想ディスクの管理をすることができます。。FCDは、vSAN、VMFS、NFS、vVols データストアで利用でき、ReadWriteOnceのPVを作成することができます。vSANファイル共有を使うと、ReadWriteManyのPVを作成することができるようです。ストレージポリシーも利用でき、ディスクごとの可用性設定やIOコントロールをすることができます。
また、vSphere CSIは、vSphereのCloud Provider Interface (CPI) とともに使用されます。Controller Managerは、Kubernetesのコントローラをまとめて実行するコンポーネントです。Cloud Controller Managerは、Kubernetesで使用したいリソースと任意のクラウドで使用しているリソースの連携を行うCloud Providerを動作するためのコンポーネントです。In-Tree内のCloud Providerは、 vSphere Cloud Provider (VCP)と呼ばれていましたが、In-Tree外のCloud ProviderはvSphere CPIと呼ばれるようになりました。vSphere CPIは、vCenter Serverに接続し、VM、ディスクなどのインフラストラクチャに関する情報をKubernetes API にマッピングすることができます。
vSphere CPIのインストールの事前準備
vSphere CPIのインストールをする際に、vsphere-cloud-controller-managerがノードのリソースが少ない場合、起動してこないため、ノードのスペックを2vCPU、4GBメモリに変更しておきます。
Kubernetesクラスタのmasterノードとworkerノードの仮想マシンに対して、disk.EnableUUIDを有効にします。また、仮想ハードウェアバージョンは15以上、Paravirtual SCSIコントローラーが必要なようですが、すでに設定済みのためスキップします。
$ for VMNAME in kube-master0{1..3}; do
echo ${VMNAME}
export VM="/Datacenter/vm/k8s/$VMNAME"
govc vm.change -vm $VM -e="disk.enableUUID=1"
done
$ for VMNAME in kube-worker0{1..2}; do
echo ${VMNAME}
export VM="/Datacenter/vm/k8s/$VMNAME"
govc vm.change -vm $VM -e="disk.enableUUID=1"
done
masterノードとworkerノードに node.cloudprovider.kubernetes.io/uninitialized=true:NoSchedule というTaintを設定します。
Taintはノードに配置したくないPodの条件を指定し、Tolerationで容認がない場合は、Taintを設定したノードにPodが配置されない状態となります。
Taintを設定前の状態はこちらになります。
$ kubectl describe nodes | egrep "Taints:|Name:" Name: kube-master01 Taints: node-role.kubernetes.io/master:NoSchedule Name: kube-master02 Taints: node-role.kubernetes.io/master:NoSchedule Name: kube-master03 Taints: node-role.kubernetes.io/master:NoSchedule Name: kube-worker01 Taints: <none> Name: kube-worker02 Taints: <none>
ノードにTaintを設定します。
$ for VMNAME in kube-master0{1..3}; do
echo $VMNAME
kubectl taint node $VMNAME node.cloudprovider.kubernetes.io/uninitialized=true:NoSchedule
done
$ for VMNAME in kube-worker0{1..2}; do
echo $VMNAME
kubectl taint node $VMNAME node.cloudprovider.kubernetes.io/uninitialized=true:NoSchedule
done
Taintの設定後の状態はこちらです。masterノードは設定前と変わっていないように見えました。
$ kubectl describe nodes | egrep "Taints:|Name:" Name: kube-master01 Taints: node-role.kubernetes.io/master:NoSchedule Name: kube-master02 Taints: node-role.kubernetes.io/master:NoSchedule Name: kube-master03 Taints: node-role.kubernetes.io/master:NoSchedule Name: kube-worker01 Taints: node.cloudprovider.kubernetes.io/uninitialized=true:NoSchedule Name: kube-worker02 Taints: node.cloudprovider.kubernetes.io/uninitialized=true:NoSchedule
vSphere CPIのインストール
vsphere-cloud-controller-manager.yamlをダウンロードします
$ VERSION=1.25 $ wget https://raw.githubusercontent.com/kubernetes/cloud-provider-vsphere/release-$VERSION/releases/v$VERSION/vsphere-cloud-controller-manager.yaml
vsphere-cloud-controller-manager.yamlにて、vCenterなどの情報を書き換えます。
$ vi vsphere-cloud-controller-manager.yaml
apiVersion: v1
kind: Secret
metadata:
name: vsphere-cloud-secret
labels:
vsphere-cpi-infra: secret
component: cloud-controller-manager
namespace: kube-system
# NOTE: this is just an example configuration, update with real values based on your environment
stringData:
10.0.10.5.username: "vCenter username"
10.0.10.5.password: "vCenter password"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: vsphere-cloud-config
labels:
vsphere-cpi-infra: config
component: cloud-controller-manager
namespace: kube-system
data:
# NOTE: this is just an example configuration, update with real values based on your environment
vsphere.conf: |
# Global properties in this section will be used for all specified vCenters unless overriden in VirtualCenter section.
global:
port: 443
# set insecureFlag to true if the vCenter uses a self-signed cert
insecureFlag: true
# settings for using k8s secret
secretName: vsphere-cloud-secret
secretNamespace: kube-system
# vcenter section
vcenter:
vcsa-1.home.lab:
server: 10.0.10.5
user: <vCenter username>
password: <vCenter password>
datacenters:
- <datascenter name>
vSphere CPIをインストールします。
$ kubectl apply -f vsphere-cloud-controller-manager.yaml
インストールに成功すると、podはこのようになりました。
$ kubectl get pod -A kube-system vsphere-cloud-controller-manager-9zczs 1/1 Running 0 155m kube-system vsphere-cloud-controller-manager-fcs65 1/1 Running 0 155m kube-system vsphere-cloud-controller-manager-r5wfk 1/1 Running 0 155m
vsphere-cloud-controller-manager の起動が失敗している場合は、以下のコマンドで原因を確認します。
$ kubectl describe pod vsphere-cloud-controller-manager -n kube-system
vSphere CSIのインストール
vmware-system-csi というネームスペースを作成します。
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/vsphere-csi-driver/v2.7.0/manifests/vanilla/namespace.yaml
masterノードに対して、Taintを設定します。
for VMNAME in kube-master0{1..3}; do
echo $VMNAME
kubectl taint nodes $VMNAME node-role.kubernetes.io/control-plane=:NoSchedule
done
Taintの設定後の状態はこちらです。
kubectl describe nodes | egrep "Taints:|Name:" Name: kube-master01 Taints: node-role.kubernetes.io/control-plane:NoSchedule Name: kube-master02 Taints: node-role.kubernetes.io/control-plane:NoSchedule Name: kube-master03 Taints: node-role.kubernetes.io/control-plane:NoSchedule Name: kube-worker01 Taints: node.cloudprovider.kubernetes.io/uninitialized=true:NoSchedule Name: kube-worker02 Taints: node.cloudprovider.kubernetes.io/uninitialized=true:NoSchedul
vSphere CSIのためのcsi-vsphere.confという定義ファイルを作成します。
$ vi csi-vsphere.conf [Global] cluster-id = "cluster-1" [VirtualCenter "vcsa-1.home.lab"] insecure-flag = "true" user = "vCenter username" password = "vCenter password" port = "443" datacenters = "datacenter name"
csi-vsphere.confからsecretを作成します。
$ kubectl create secret generic vsphere-config-secret --from-file=csi-vsphere.conf --namespace=vmware-system-csi
vSphere CSIをインストールします。
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/vsphere-csi-driver/v2.7.0/manifests/vanilla/vsphere-csi-driver.yaml
vSphere CSIがインストールされたことを確認します。
$ kubectl get deployment --namespace=vmware-system-csi NAME READY UP-TO-DATE AVAILABLE AGE vsphere-csi-controller 3/3 3 3 141m
インストールに成功すると、podはこのようになりました。
$ kubectl get pod -A kube-system vsphere-cloud-controller-manager-9zczs 1/1 Running 0 155m kube-system vsphere-cloud-controller-manager-fcs65 1/1 Running 0 155m kube-system vsphere-cloud-controller-manager-r5wfk 1/1 Running 0 155m vmware-system-csi vsphere-csi-controller-68d96d8597-2cfcf 7/7 Running 0 141m vmware-system-csi vsphere-csi-controller-68d96d8597-5dszx 7/7 Running 0 141m vmware-system-csi vsphere-csi-controller-68d96d8597-82mcm 7/7 Running 0 141m vmware-system-csi vsphere-csi-node-b9ztz 3/3 Running 0 141m vmware-system-csi vsphere-csi-node-cshf7 3/3 Running 0 141m vmware-system-csi vsphere-csi-node-dqc79 3/3 Running 0 141m vmware-system-csi vsphere-csi-node-fp2zd 3/3 Running 0 141m vmware-system-csi vsphere-csi-node-qnfx6 3/3 Running 0 141m
動作確認
動作確認のため、PersistentVolumeを作成していきます。
ストレージポリシーの設定が必要なため、前回作成した Normal-Storage-Policy というストレージポリシーを利用します。IOPS制限は外しておきます。
StorageClassの定義を作成し、インストールします。定義ファイルの中で先ほど用意したストレージポリシーを指定しておきます。
$ vi example-sc.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: example-sc
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: csi.vsphere.vmware.com
parameters:
storagepolicyname: "Normal-Storage-Policy"
csi.storage.k8s.io/fstype: "ext4"
$ kubectl apply -f example-sc.yaml
PVCの定義を作成し、インストールします。
$ vi example-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: example-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: example-sc
$ kubectl apply -f example-pvc.yaml
PVとPVCが作成できていることを確認します。
$ kubectl get pv,pvc NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE persistentvolume/pvc-6c1bc194-bbc0-4e26-b77c-3b2412960200 5Gi RWO Delete Bound default/example-pvc example-sc 16s NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE persistentvolumeclaim/example-pvc Bound pvc-6c1bc194-bbc0-4e26-b77c-3b2412960200 5Gi RWO example-sc 17s
vCenterを見ると、クラウドネイティブストレージ>コンテナ ボリュームにPVの情報が表示されていることが分かります。
さらにデータストアブラウザを見ると、First Class Disk用のフォルダのfcdフォルダ内にPVの実態となる、vmdkファイルが作成されいてることをが分かります。
上記の定義ファイルだと、workerノードにFCDのvmdkはまだアタッチされていない状態となるようです。
参考
Cluster APIを使ってvSphereにKubernetesクラスタを作成する
今まではKubesprayを使ってKubernetesのインストールを行ってきました。今回は、Cluster APIを使ったKubernetesクラスタを作成を試してみました。
構成
- vCenter 7.0 U3
- ESXi 7.0 Update 3
- Kubespray
- kubernetes v1.25.5
- Cluster API v1.3.2
Cluster APIについて
Cluster APIは、Kubernetesの宣言型の機能を利用し、Kubernetesクラスタの作成や構成、管理をするプロジェクトになります。Kubesprayでは、masterノートやworkerノードの展開はgovcなど自身で行ってきましたが、Cluster APIではその作業も任せることができます。Cluster APIはパブリッククラウドなど様々なプラットフォームに対応しており、vSphereにも対応しており、vSphereの実装として、Cluster API Provider vSphere(CAPV)があります。CAPVはTanzu Kubernetes Grid(TKG)の中でも利用されているようです。
Cluster APIを利用すると、Kubernetesクラスタを管理するmanagement clusterと、management clusterで管理されたワークロードを実行するworkload clusterが作成されます。
workload cluster用のネットワークの準備
workload clusterはDHCPでIPアドレスを取得するため、workload clusterが接続するポートグループにDHCPでIPアドレスを取得できるようにしておきます。homelabでは、EdgeRouterのDHCP機能でIPアドレスを払い出ししています。
マシンイメージの展開
CAPV用のマシンイメージが配布されており、 ubuntu-2004-kube-v1.25.5.ova をダウンロードし、vCenter上にデプロイします。
homelabでの利用なので、マシンイメージをそのまま利用しますが、production環境では独自ビルドが推奨なようです。
マシンイメージのビルド用のpackerテンプレートはこちらで管理されていました。
image-builder/images/capi/packer/ova at master · kubernetes-sigs/image-builder · GitHub
Cluster APIでマシンイメージを展開する際に、デフォルトだとlinked cloneが利用されるため、以下のようにlinked cloneを利用するためにスナップショットをとってから、テンプレート化しておきます。
$ govc snapshot.create -vm ubuntu-2004-kube-v1.25.5 root $ govc vm.markastemplate ubuntu-2004-kube-v1.25.5
haproxyのマシンイメージについては、kube-vipを使うように変更となったため、非推奨となってようなので、利用はしていません。
management clusterのインストール
通常の手順ですと、kindを利用して、docker上にKubernetesをインストールして、bootstrap clusterを作成し、そこにclusterctlを使ってmanagement clusterをインストールします。
今回は、KubesprayですでにKubernetesクラスタがあるため、Kubesprayで作成したKubernetesクラスタにmanagement clusterをインストールしていきます。
clusterctlをインストールします。
$ curl -L https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.3.2/clusterctl-linux-amd64 -o clusterctl $ sudo install -o root -g root -m 0755 clusterctl /usr/local/bin/clusterctl $ clusterctl version
clusterctlを実行するために、以下のように ~/.cluster-api/clusterctl.yaml に定義ファイルを用意します。
$ vi ~/.cluster-api/clusterctl.yaml ## -- Controller settings -- ## VSPHERE_USERNAME: "vCenterのログインユーザ" VSPHERE_PASSWORD: "パスワード" ## -- Required workload cluster default settings -- ## VSPHERE_SERVER: "vCenterのFQDN" VSPHERE_DATACENTER: "データセンター名" VSPHERE_DATASTORE: "データストア名" VSPHERE_NETWORK: "DHCPが利用可能なポートグループ名" VSPHERE_RESOURCE_POOL: "<クラスタ名 or リソースプール名>/Resources" VSPHERE_FOLDER: "vm" VSPHERE_TEMPLATE: "ubuntu-2004-kube-v1.25.5" CONTROL_PLANE_ENDPOINT_IP: "workload clusterのkubectlで接続するコントロールプレーンのIPアドレス" VIP_NETWORK_INTERFACE: "eth0" VSPHERE_TLS_THUMBPRINT: "vCenterのthumbprint" EXP_CLUSTER_RESOURCE_SET: "true" VSPHERE_SSH_AUTHORIZED_KEY: "sshのための公開鍵" VSPHERE_STORAGE_POLICY: ""
vCenterのthumbprintは、vCenterにsshでログインし、以下のコマンドで取得します。
# openssl x509 -in /etc/vmware-vpx/ssl/rui.crt -fingerprint -sha1 -noout
vCenterのログイン情報や、workload clusterを作成するために必要なリソースプールやデータストア名などを入力します。
上記の設定で困ったことは、 CONTROL_PLANE_ENDPOINT_IP と VIP_NETWORK_INTERFACE の設定になります。
CONTROL_PLANE_ENDPOINT_IP はkube-vipで利用するworkload clusterのコントロールプレーンのIPアドレスになり、DHCPのIP帯以外のstaticなIPアドレスを指定する必要があります。ここのIPアドレスの指定を間違えると、コントロールプレーンにアクセスできないとエラーが出て、workload clusterの作成に失敗します。
$ kubectl logs capi-kubeadm-control-plane-controller-manager-785c94c5d4-sh9fg -n capi-kubeadm-control-plane-system -f "Failed to update KubeadmControlPlane Status" err="failed to create remote cluster client: failed to create cluster accessor: error creating dynamic rest mapper for remote cluster \"default/vsphere-quickstart\": Get \"https://10.0.50 .30:6443/api?timeout=10s\": x509: certificate signed by unknown authority (possibly because of \"crypto/rsa: verification error\" while trying to verify candidate authority certificate \"kubernetes\")" controller="kubeadmcontrolplane" controllerGroup="controlplane.cluster.x-k8s.io" controllerKind="KubeadmControlPlane" KubeadmControlPlane="default/vsphere-quickstart" namespace="default" name="vsphere-quickstart" reconcileID=fa7159b4-97ff-45cd-b1ba-29a4d5f11548 Cluster="default/vsphere-quickstart"
VIP_NETWORK_INTERFACE は、コントロールプレーンのkube-vipで利用するインターフェイスの設定となりますが、公式のマシンイメージでは、インターフェイスにeth0を利用しており、サンプルではens192だったため、コントロールプレーンにアクセスできず、workload clusterの作成に失敗します。
~/.cluster-api/clusterctl.yaml が出来たところで、以下のコマンドで、management clusterのインストールを行います。
$ clusterctl init --infrastructure vsphere Fetching providers Installing cert-manager Version="v1.10.1" Waiting for cert-manager to be available... Installing Provider="cluster-api" Version="v1.3.2" TargetNamespace="capi-system" Installing Provider="bootstrap-kubeadm" Version="v1.3.2" TargetNamespace="capi-kubeadm-bootstrap-system" Installing Provider="control-plane-kubeadm" Version="v1.3.2" TargetNamespace="capi-kubeadm-control-plane-system" Installing Provider="infrastructure-vsphere" Version="v1.5.1" TargetNamespace="capv-system" Your management cluster has been initialized successfully! You can now create your first workload cluster by running the following: clusterctl generate cluster [name] --kubernetes-version [version] | kubectl apply -f -
以下のような、podが起動してきます。
$ kubectl get pod -A NAMESPACE NAME READY STATUS RESTARTS AGE capi-kubeadm-bootstrap-system capi-kubeadm-bootstrap-controller-manager-78c76cd689-flgtb 1/1 Running 0 3d capi-kubeadm-control-plane-system capi-kubeadm-control-plane-controller-manager-785c94c5d4-sh9fg 1/1 Running 0 3d capi-system capi-controller-manager-8665f47486-thlh4 1/1 Running 0 3d capv-system capv-controller-manager-dfb77fcc6-tz7t8 1/1 Running 0 2d19h cert-manager cert-manager-74d949c895-v4g52 1/1 Running 0 3d cert-manager cert-manager-cainjector-d9bc5979d-ktwrz 1/1 Running 0 3d cert-manager cert-manager-webhook-84b7ddd796-fdj8l 1/1 Running 0 3d
workload clusterのインストール
次に、workload clusterのインストールをしていきます。
workload clusterのKubernetsのバージョンや、コントロールプレーンのVM数、wokerノードのVM数を指定して、cluster.yamlを生成します。
$ clusterctl generate cluster vsphere-quickstart \
--infrastructure vsphere \
--kubernetes-version v1.25.5 \
--control-plane-machine-count 1 \
--worker-machine-count 3 > cluster.yaml
必要に応じて、cluster.yamlを編集します。
以下のコマンドで、workload clusterのインストールを行います。
$ kubectl apply -f cluster.yaml
以下のコマンドで、workload clusterが起動してきかたを確認できます。10分ぐらいで立ち上がってくるように見えています。
$ kubectl get machines NAME CLUSTER NODENAME PROVIDERID PHASE AGE VERSION vsphere-quickstart-md-0-5c5849c5b9-5wbv8 vsphere-quickstart vsphere-quickstart-md-0-5c5849c5b9-5wbv8 vsphere://42080fa2-7dab-7285-444a-7b0ecc66a35e Running 7m15s v1.25.5 vsphere-quickstart-md-0-5c5849c5b9-95595 vsphere-quickstart vsphere-quickstart-md-0-5c5849c5b9-95595 vsphere://4208d8b7-8297-c452-26f9-e7061d023e8a Running 7m15s v1.25.5 vsphere-quickstart-md-0-5c5849c5b9-s4rz9 vsphere-quickstart vsphere-quickstart-md-0-5c5849c5b9-s4rz9 vsphere://4208246c-a347-1f39-51c3-3907b502f884 Running 7m15s v1.25.5 vsphere-quickstart-sxfqt vsphere-quickstart vsphere-quickstart-sxfqt vsphere://4208aad8-d2e7-42bf-00ac-70511419ff77 Running 7m14s v1.25.5
workload clusterにアクセスするためのconfigファイルを以下のように生成します。
$ kubectl get secret/vsphere-quickstart-kubeconfig -o json \ | jq -r .data.value \ | base64 --decode \ > ./vsphere-quickstart.kubeconfig
workload clusterのノードを確認すると、NotReadyとなっています。こちらはCNIがインストールされていないため、利用ができない状態となっています。
$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl get node NAME STATUS ROLES AGE VERSION vsphere-quickstart-md-0-5c5849c5b9-5wbv8 NotReady <none> 10m v1.25.5 vsphere-quickstart-md-0-5c5849c5b9-95595 NotReady <none> 10m v1.25.5 vsphere-quickstart-md-0-5c5849c5b9-s4rz9 NotReady <none> 10m v1.25.5 vsphere-quickstart-sxfqt NotReady control-plane 11m v1.25.5
以下のようにcalicoをインストールします。
$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/calico.yaml
Readyになったことを確認し、workload clusterのインストールは完了になります。
$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl get node NAME STATUS ROLES AGE VERSION vsphere-quickstart-md-0-5c5849c5b9-5wbv8 Ready <none> 28h v1.25.5 vsphere-quickstart-md-0-5c5849c5b9-95595 Ready <none> 28h v1.25.5 vsphere-quickstart-md-0-5c5849c5b9-s4rz9 Ready <none> 28h v1.25.5 vsphere-quickstart-sxfqt Ready control-plane 28h v1.25.5
calicoのインストールが完了すると、podの状態はこのようになっていました。vsphere-csi-controller が CrashLoopBackOff となっている原因について別途書いてみようと思います。
$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl get pod -A NAMESPACE NAME READY STATUS RESTARTS AGE kube-system calico-kube-controllers-74677b4c5f-m77w8 1/1 Running 0 16m kube-system calico-node-7bcpz 1/1 Running 0 16m kube-system calico-node-w7fs9 1/1 Running 0 16m kube-system calico-node-wckhz 1/1 Running 0 16m kube-system calico-node-zlc5b 1/1 Running 0 16m kube-system coredns-565d847f94-chn5z 1/1 Running 0 21m kube-system coredns-565d847f94-dn4zq 1/1 Running 0 21m kube-system etcd-vsphere-quickstart-d7r7k 1/1 Running 0 21m kube-system kube-apiserver-vsphere-quickstart-d7r7k 1/1 Running 0 21m kube-system kube-controller-manager-vsphere-quickstart-d7r7k 1/1 Running 0 21m kube-system kube-proxy-br9fn 1/1 Running 0 20m kube-system kube-proxy-cvfzq 1/1 Running 0 20m kube-system kube-proxy-qxlj2 1/1 Running 0 20m kube-system kube-proxy-rmzww 1/1 Running 0 21m kube-system kube-scheduler-vsphere-quickstart-d7r7k 1/1 Running 0 21m kube-system kube-vip-vsphere-quickstart-d7r7k 1/1 Running 0 21m kube-system vsphere-cloud-controller-manager-ctkr7 1/1 Running 0 20m kube-system vsphere-cloud-controller-manager-hl5w2 1/1 Running 0 20m kube-system vsphere-cloud-controller-manager-r9dhk 1/1 Running 0 20m kube-system vsphere-csi-controller-5d6484644d-nqwlh 4/5 CrashLoopBackOff 8 (37s ago) 21m kube-system vsphere-csi-node-h9p2x 3/3 Running 0 21m kube-system vsphere-csi-node-hbgsl 3/3 Running 0 20m kube-system vsphere-csi-node-tr99v 3/3 Running 0 20m kube-system vsphere-csi-node-xk2jq 3/3 Running 0 20m
スモークテスト
動作確認を行います。
テスト用のpodを2台作成し、pod間で疎通が取れるか確認します。
$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl run myshell1 -it --rm --image busybox -- sh / # hostname -i 192.168.136.2 / # ping 192.168.82.67 PING 192.168.82.67 (192.168.82.67): 56 data bytes 64 bytes from 192.168.82.67: seq=0 ttl=62 time=0.615 ms 64 bytes from 192.168.82.67: seq=1 ttl=62 time=3.218 ms ^C --- 192.168.82.67 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.615/1.916/3.218 ms
$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl run myshell2 -it --rm --image busybox -- sh / # hostname -i 192.168.82.67 / # ping 192.168.136.2 PING 192.168.136.2 (192.168.136.2): 56 data bytes 64 bytes from 192.168.136.2: seq=0 ttl=62 time=0.918 ms 64 bytes from 192.168.136.2: seq=1 ttl=62 time=0.316 ms ^C --- 192.168.136.2 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.316/0.617/0.918 ms
podの状態は以下の通りです。
$ KUBECONFIG=vsphere-quickstart.kubeconfig kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES myshell1 1/1 Running 0 50s 192.168.136.2 vsphere-quickstart-md-0-5c5849c5b9-5wbv8 <none> <none> myshell2 1/1 Running 0 28s 192.168.82.67 vsphere-quickstart-md-0-5c5849c5b9-95595 <none> <none>
おわりに
KubesprayだとKubernetesクラスタのインストールに30分ぐらいかかりましたが、Cluster APIだと10分程度で利用ができるので、効率がよさそうでした。ただ、Kubesprayだとクラスタの初期設定(CNIやMetalLB)もある程度対応できたり、マシンイメージのビルドが楽だったり、構築中の動作やどのような設定を行っているかが分かりやすいということもあり、homelabだとKubesprayのほうが使いやすそうには見えています。用途によって使い分けるのがよさそうに見えました。
参考
PromethusとGrafanaを使ったRook Cephの監視
前回は、Rookのダッシュボードのインストールを試してみました。今回は、Rook Cephを監視するためのPromethusとGrafanaのインストールを試してみました。
構成
- vCenter 7.0 U3
- ESXi 7.0 Update 3
- Ubuntu 22.04 (テンプレートVM)
- open-vm-tools 11.3.5
- cloud-init 22.2
- Kubespray
- kubernetes v1.25.5
- MetalLB v0.12.1
- ingress-nginx v1.5.1
- CoreDNS
- EdgeRouter X
- Rook Ceph v1.10.9
Promethusのインストール
promethus operatorをインストールします。
LATEST=$(curl -s https://api.github.com/repos/prometheus-operator/prometheus-operator/releases/latest | jq -cr .tag_name)
curl -sL https://github.com/prometheus-operator/prometheus-operator/releases/download/${LATEST}/bundle.yaml | kubectl create -f -
promethus operatorが起動したことを確認します。
$ kubectl get pod NAME READY STATUS RESTARTS AGE prometheus-operator-57df45d67c-69f6z 1/1 Running 0 34s
Serviceをtype: LoadBalancerにするために、以下のように修正します。
$ cd rook/deploy/examples/monitoring $ vi prometheus-service.yaml - type: NodePort + type: LoadBalancer
Rookクラスターを監視し、メトリックを定期的に収集するサービスモニターを作成します。
$ kubectl create -f service-monitor.yaml $ kubectl create -f prometheus.yaml $ kubectl create -f prometheus-service.yaml
Prometheusが起動していることを確認します。
$ kubectl -n rook-ceph get pod,service NAME READY STATUS RESTARTS AGE pod/prometheus-rook-prometheus-0 2/2 Running 0 53s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/prometheus-operated ClusterIP None <none> 9090/TCP 54s service/rook-prometheus LoadBalancer 10.233.11.124 10.0.50.203 9090:30900/TCP 45s
ブラウザから、 http://10.0.50.203:9090/ にアクセスし、PrometheusのWebコンソールが開けることを確認します。
Grafanaのインストール
Grafanaのhelmチャートをダウンロードします。
$ helm repo add grafana https://grafana.github.io/helm-charts $ helm repo update
ingressが利用できるように、以下のようにvalues.yamlファイルを作成し、Grafanaのインストールを行います。
$ vi values.yml
ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true"
labels: {}
path: /
hosts:
- grafana.kube.home.lab
tls:
- secretName: ingress-tls
hosts:
- grafana.kube.home.lab
$ helm install my-release grafana/grafana -f values.yml
Grafanaが起動してきたことを確認します。
$ kubectl get pod,svc,ingress NAME READY STATUS RESTARTS AGE pod/my-release-grafana-6fc6cd88d4-x8tnr 1/1 Running 0 88m pod/prometheus-operator-57df45d67c-69f6z 1/1 Running 0 143m NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/my-release-grafana ClusterIP 10.233.19.102 <none> 80/TCP 94m service/prometheus-operator ClusterIP None <none> 8080/TCP 143m NAME CLASS HOSTS ADDRESS PORTS AGE ingress.networking.k8s.io/my-release-grafana <none> grafana.kube.home.lab 10.0.50.201 80, 443 94m
以下のようにGrafanaにログインするためのパスワードを取得します。
$ kubectl get secret --namespace default my-release-grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo
ブラウザから、 https://grafana.kube.home.lab にアクセスし、ユーザはadminと上記のパスワードでログインします。
Grafanaの設定
GrafanaのConfigurationから、Add data sourceでデータソースを追加します。
Prometheusをクリックし、URLに http://10.0.50.203:9090/ を入力し、Save & testをクリックします。
DashboardsのNew>importをクリックし、Import via grafana.comに以下のリンク先にある、Dashbord ID copied!をクリックし、クリップボードに入った値をペーストします。
Ceph - Clusterの場合は以下のようなダッシュボードが表示されます。

リソースの削除
動作確認ができたため、リソースを削除しておきます。
$ helm uninstall my-release $ kubectl delete -f service-monitor.yaml $ kubectl delete -f prometheus.yaml $ kubectl delete -f prometheus-service.yaml $ kubectl delete -f https://raw.githubusercontent.com/coreos/prometheus-operator/v0.62.0/bundle.yaml
参考
ストレージポリシーによるIOPS制限をした仮想マシンのfioによるベンチマーク
前回は、仮想マシンで構築したRook Cephから払い出したPersistentVolumeについてfioを使ってパフォーマンスのベンチマークを取ってみました。その際に比較のために仮想マシンのベンチマークを取ってみました。今回は、仮想マシンの状態の比較として、ストレージポリシーによるIOPS制限をかけた場合の仮想マシンの挙動を確認してみます。
構成
- vCenter 7.0 U3
- ESXi 7.0 Update 3
- Ubuntu 22.04 (テンプレートVM)
- open-vm-tools 11.3.5
- cloud-init 22.2
ストレージポリシーについて
vSphereの機能のなかで、Storage Policy Based Management (SPBM)というストレージレイヤーを抽象化する機能があります。
SPBMには、 Storage I/O ControlのSIOC v1とSIOC v2が存在します。
SIOC v1は、ストレージ I/Oのレイテンシを監視し、I/Oが競合した場合に、仮想マシンの共有値に基づいて、データストア全体のディスクスケジューラを使用して、I/Oを調整します。
SIOC v2は、vSphere 6.5から導入され、基盤となるストレージに関係なく、仮想マシンのI/O パスに直接アクセスできる vSphere APIs for I/O Filtering (VAIO)を使用して仮想マシン単位で制御を行うことができます。
ストレージポリシーでホスト ベースのサービスを指定することで、SIOC v2を利用することができ、IOPSの制限と予約と共有を行うことができます。
ストレージポリシーによるSIOC v2の設定
ストレージポリシーによって仮想マシンにIOPS制限をかけてみます。
タグを作成します。
タグのカテゴリに Storage Disk Type をつけ、データストアとデータストアクラスタにチェックをいれます。
Storage Disk Type カテゴリ配下に、 NVMe というタグを作成します。
データストアのインベントリに移動し、ストレージポリシーを適用したいデータストアのタグに NVMe を付与します。
ポリシーおよびプロファイル>ストレージ ポリシー コンポーネントにて、作成をクリックします。
名前に Normal-IOPS-limit などの名前を入れます。
IOPS の制限に3000、IOPS の予約に1、IOPS のシェア数に1000の値を入れ、保存します。
ポリシーおよびプロファイル>仮想マシン ストレージ ポリシーにて、作成をクリックします。
名前に Normal-Storage-Policy などの名前を入れます。
ホスト ベースのサービスでホスト ベースのルールの有効化にチェックを入れ、データストア固有のルールにタグ ベースの配置ルールを有効化にチェックを入れます。
ホスト ベースのサービスで、ストレージ ポリシー コンポーネントの使用で先ほど作成した Normal-IOPS-limit をします。
タグ ベースの配置で、タグ カテゴリに Storage Disk Type 、タグに NVMe を指定して、保存します。
仮想マシンへのストレージポリシーの適用
ストレージポリシーの作成ができたため、仮想マシンにストレージポリシーを適用します。
前回作成したUbutuの仮想マシンの仮想マシン ストレージ ポリシーの編集から Normal-Storage-Policy を選択し、保存します。
fioによるパフォーマンスのベンチマーク
前回と同様、複数のブロックサイズと複数のI/Oパターンのタイプで測定するため、以下のようなコマンドで実行してみました。128kのブロックサイズを追加しています。
direct=1でnon-buffered I/Oを利用し、iodepth=32でキュー中に待機させるIOリクエストの最大数を32に指定、ioengine=libaioでIOを発生させる方法としてLinux ネイティブの非同期 I/Oを指定、numjobs=8でスレッド数を8に指定、group_reportingで複数スレッドの結果をまとめてレポート、output-format=jsonで計測結果をjson形式で出力という設定を行っています。
# for BS in 4k 8k 16k 64k 128k 1m; do
for RW in write randwrite read randread; do
fio -filename=/mnt/rook-vol1/test -direct=1 -iodepth=32 -rw=$RW -ioengine=libaio -bs=$BS -size=1G -numjobs=8 -runtime=30 -name=32-$RW-$BS --group_reporting --output-format=json | \
jq -cr '[."global options".iodepth,."global options".bs, ."global options".rw,
.jobs[0].read.iops, .jobs[0].write.iops,
.jobs[0].read.bw, .jobs[0].write.bw]|@csv'
done
done
こちらが、ストレージポリシーで3000IOPSの制限をかけたUbuntuの仮想マシンで測定したfioのパフォーマンスのベンチマーク結果です。

前回の結果も載せておきます。

ブロックサイズが64kまではきれいに3000IOPSで制限されることが分かります。
帯域の制限は、IO数に32KBを加重することで計算されます。今回は3000IOPSのため、3000x32KB=93.75MB/秒の帯域幅の IOPS 制限が設定されます。
以前はIOPSを制限する際に32KBで正規化されていましたが、vSphere6.5以降は正規化されず、ブロックサイズに関係なくIOPSの値で制限されるようになっています。
そのため、ブロックサイズが128kでは、帯域の制限が先にあたり、IOPSが3000より小さい値になったと考えられます。