hidemium's blog

日々学んだことをアウトプットする。

govcを使ったvmdkイメージの移行

前回、govcを使ったフルクローンによるバックアップについて書きましたが、今回はgovcを使ってvmdkイメージの移行について試してみました。

hidemium.hatenablog.com

構成

vmdkイメージのダウンロード

今回の操作は、govcを使って仮想マシンのvmdkイメージを手元にダウンロードして、vmdkイメージをアップロードして別のVMとして作成します。

ovfファイルとしてダウンロードする方法が一番シンプルですが、動作を確認するためにあえて回り道をしつつ見ていきます。

govcのインストールと環境変数は前回と同様です。

govcでvmdkイメージをダウンロードするには以下のように実行します。

$ govc datastore.download -host hostname  -ds datastore "vm_name/vm_name-flat.vmdk" ./vm_name-flat.vmdk

ここでflat.vmdkファイルをしているのには理由があります。

データストアブラウザからだと、vmdkファイルしか見えませんが、ESXiにログインし、データストア内を見ると、flat.vmdkとvmdkファイルが存在することが分かります。

ls -lh
-rw-------    1 root     root       10.0G Jan 24 21:25 vm_name-flat.vmdk
-rw-------    1 root     root         527 Jan 24 21:27 vm_name.vmdk

xxx-flat.vmdkというファイルがダウンロードされますが、ファイルサイズがESXi内で見えるプロビジョニングサイズと同じサイズとなっていることが分かります。

flat.vmdkは、仮想マシンのデータ ディスクになります。実際のゲストOSのデータはこちらに格納されています。

vmdkは、ディスク記述子ファイルになります。関連するflat.vmdkに関する情報が記述されています。

VMDKフォーマットには複数の異なるサブフォーマットがあり、メタデータを外部記述子ファイルに保存するものと、メインデータとともに 1 つのファイルに埋め込むものがあります。

フラットイメージは事前にスペースを割り当てますが、スパースイメージは仮想マシンが書き込むにつれて増大するかたちになります。

次にgovcでflat.vmdkファイルをアップロードします。

$ govc import.vmdk -ds datastore -pool cluster-name/Resources ./vm_name-flat.vmdk new_vm_name
govc: vmdk: invalid format (must be streamOptimized)
The vmdk can be converted using one of:
  vmware-vdiskmanager -t 5 -r './vm_name-flat.vmdk' new.vmdk
  qemu-img convert -O vmdk -o subformat=streamOptimized './vm_name-flat.vmdk' new.vmdk

そうすると、streamOptimizedされたvmdkのフォーマットではないとアップロードできないと怒られます。

メッセージの通り、subformatにstreamOptimizedを指定してvmdkファイルを変換します。

$ qemu-img convert -O vmdk -o subformat=streamOptimized './vm_name-flat.vmdk' disk1.vmdk
$ ls -lh
-rw-r--r-- 1 user user 241M Jan 24 05:57 disk1.vmdk
-rw-rw-r-- 1 user user  10G Jan 23 09:31 vm_name-flat.vmdk

vmdkファイルのサイズがもともとはプロビジョニングサイズと同じサイズでしたが、変換することで実用量のサイズに変換されました。

streamOptimizedについては、こちらに記載がありました。

govcを使って、変換したvmdkファイルをアップロードします。

govcの引数には、今後作成するVM名と同じデータストア内のフォルダ名を指定しておきます。

$ govc import.vmdk -ds datastore -pool cluster-name/Resources ./disk1.vmdk new_vm_name

アップロードしたvmdkファイルを指定して、VMを作成します。

$ govc vm.create -m 2048 -c 2 -g ubuntu64Guest -host hostname -ds datastore -net network -on=false -disk="new_vm_name/disk1.vmdk" new_vm_name

vmdkをダウンロードする際に、flat.vmdkのファイルサイズでダウンロードされるため、事前にファイルサイズを小さくするには、ovfとしてダウンロードするのが一番シンプルそうではあります。

手元にvmdkファイルがあり、それを利用したい場合は、今回の方法が利用できるかもしれません。

govcを使った仮想マシンのバックアップ

前回、スナップショット技術やバックアップについて書きましたが、今回はgovcを使ってバックアップの取得について試してみました。

hidemium.hatenablog.com

構成

バックアップ

今回の操作は、govcを使って仮想マシンのクローンを用いたフルバックアップになります。

以下の手順でgovcのインストールを行います。また、合わせて環境変数を設定しておきます。

$ curl -L -o - "https://github.com/vmware/govmomi/releases/latest/download/govc_$(uname -s)_$(uname -m).tar.gz" | sudo tar -C /usr/local/bin -xvzf - govc
$ export GOVC_URL=https://<vcenter fqdn>/sdk
$ export GOVC_USERNAME="username"
$ export GOVC_PASSWORD="password"
$ export GOVC_INSECURE="1"
$ export VM="/Datacenter/vm/path/to/vm_name"
$ govc vm.info "${VM}"
Name:              vm_name
  Path:            /Datacenter/vm/path/to/vm_name
  Guest name:      Ubuntu Linux (64-bit)
  Memory:          2048MB
  CPU:             1 vCPU(s)
  Power state:     poweredOn
  Boot time:       
  IP address:   
  Host:            hostname

govcで同一のデータストアにクローンを行うには以下のように実行します。

$ govc vm.clone -vm $VM -on=false -host hostname -folder "/Datacenter/vm/path/to/folder" clone_vm_name
Cloning /Datacenter/vm/path/to/vm_name to /Datacenter/vm/path/to/folder/clone_vm_name...OK

電源オン状態でクローンをすると、スナップショットを自動で取得を行ったうえで、クローンを行ってくれます。

clone-temp-xxx という名称のスナップショットが自動で作成され、ゲストファイルシステムを静止するがオンになっており、ファイルシステムコンシステントとアプリケーションコンシステントに該当しそうです。

govcの -on オプションを false に指定することで、クローン先のVMは電源オフ状態のままとなります。

次に、govcで異なるデータストアにクローンを行うには以下のように実行します。クローン先のデータストアが異なるため、こちらはバックアップ相当になると思います。

$ govc vm.clone -vm $VM -on=false -host hostname -ds dest_datastore -folder "/Datacenter/vm/path/to/folder" clone_vm_name
Cloning /Datacenter/vm/path/to/vm_name to clone_vm_name...OK

次に、仮想マシンをダウンロードして、他の領域へ転送する想定でovaファイルをダウンロードします。

export.ovf を実行すると、ovfファイルとvmdkファイルがダウンロードされます。

$ govc export.ovf -vm $VM /path/to/folder/
Downloading vm_name-disk-0.vmdk... OK
$ ls -la /path/to/folder/vm_name
vm_name-disk-0.vmdk
vm_name.ovf

似たコマンドで datastore.download がありますが、こちらではvmdkの仮想ディスクの記述子データしかダウンロードされず、仮想ディスクの実際のデータは含まれません。

$ govc datastore.download -ds datastore folder_name/vm_name.vmdk vm_name.vmdk
$ cat vm_name.vmdk
# Disk DescriptorFile
version=1
encoding="UTF-8"
:

スナップショットを指定したバックアップ

以下のようにすると、指定したスナップショットからクローンを作成することができます。

$ govc snapshot.create -vm $VM snapshot_name
$ govc vm.clone -vm $VM -snapshot snapshot_name -on=false -host hostname -ds dest_datastore -folder "/Datacenter/vm/path/to/folder" clone_vm_name
Cloning /Datacenter/vm/path/to/vm_name to clone_vm_name...OK
$ govc snapshot.remove -vm $VM snapshot_name

バックアップではないですが、以下のようにするとリンククローンを作成することができます。

$ govc vm.clone -vm $VM -snapshot snapshot_name -link -on=false -host hostname -ds dest_datastore -folder "/Datacenter/vm/path/to/folder" clone_vm_name
Cloning /Datacenter/vm/path/to/vm_name to clone_vm_name...OK

リストア

フルクローンを使ったバックアップのため、リストアは指定したデータストアに対してもう一度クローンを実行するかたちで実施できます。

$ govc vm.clone -vm clone_vm_name -on=false -host hostname -ds dest_datastore -folder "/Datacenter/vm/path/to/folder" restore_vm_name
Cloning /Datacenter/vm/path/to/clone_vm_name to restore_vm_name...OK

ovfファイルからのリストアは、以下のように行います。

$ govc import.ovf -host hostname -ds datastore /path/to/folder/vm_name.ovf
Uploading vm_name-disk-0.vmdk... OK

First Class Disk (FCD)

govcコマンドを触っていると、First Class Disk (FCD) 関連のものがあることに気が付きます。

$ govc disk
  disk.create
  disk.ls
  disk.register
  disk.rm
  disk.snapshot.create
  disk.snapshot.ls
  disk.snapshot.rm
  disk.tags.attach
  disk.tags.detach

FCDのディスクを作成してみます。

$ govc disk.create -ds datastore -size 10G my-disk
Creating my-disk...OK
7584edf7-6bbd-4435-9762-609d32564d28
$ govc disk.ls -ds datastore  -l
7584edf7-6bbd-4435-9762-609d32564d28  my-disk  10.0G

試しに、スナップショットを作成してみます。

$ govc disk.snapshot.create -ds datastore 7584edf7-6bbd-4435-9762-609d32564d28 my-disk-snapshot
Snapshot 7584edf7-6bbd-4435-9762-609d32564d28...OK
d86d3465-8fa5-40c2-ac02-44ee3d15809b
$ govc disk.snapshot.ls -ds datastore 7584edf7-6bbd-4435-9762-609d32564d28
d86d3465-8fa5-40c2-ac02-44ee3d15809b  my-disk-snapshot

スナップショットからの切り戻しするサブコマンドは存在しなさそうでした。

後片付けでFCDのスナップショットとディスクを削除しておきます。

$ govc disk.snapshot.rm -ds datastore 7584edf7-6bbd-4435-9762-609d32564d28 d86d3465-8fa5-40c2-ac02-44ee3d15809b
Deleting d86d3465-8fa5-40c2-ac02-44ee3d15809b...OK
$ govc disk.rm -ds datastore 7584edf7-6bbd-4435-9762-609d32564d28
Deleting 7584edf7-6bbd-4435-9762-609d32564d28...OK

これらのコマンドの用途としては、CNSで作成した仮想ディスクのトラブルシュート周りで役に立ちそうなように見えています。

https://blogs.vmware.com/affiliates/vmware-cloud-native-storage-be-aware-of-dangling-virtual-disks-and-snapshots

スナップショット技術とVMwareのスナップショットとバックアップについて

コピーオンライト(Copy-On-Write)といったスナップショット技術とVMwareのスナップショットやバックアップについて整理をしてみたいと思います。

スナップショット技術

コピーオンライト(Copy-On-Write)スナップショット

CoWは、スナップショットデータ用に専用のストレージ領域が予約されます。

このストレージ領域にメタデータと元のデータブロックに変更が発生するたびに元データのコピーが作成されます。

ブロックを上書きする前に、その前の値を読み取って別の場所に書き込んでから、新しいデータを書き込む必要があり、書き込みごとに3つのIO操作(読み取り1回と書き込み2回)が使用されます。

このアプローチにはいくつか利点があり、新しいコピーが正常に書き込まれるまで、元のデータはそのまま残るため、データ破損のリスクが最小限に抑えられます。

リダイレクトオンライト (Redirect-on-Write) スナップショット

RoWは、更新時に元のデータをスナップショット領域にコピーはせずに、ブロックへの変更はブロックに関連図けられたポインタを別のブロックにリダイレクトし、そこにデータを書き込みます。

特定のスナップショットにアクセスしようとする場合、ブロック位置に対応するポインタを利用して、別の場所からブロックを取得します。

RoWでは、保護されたブロックを変更する際にIO操作がCoWに比べると書き込み1回に削減できます。

スプリットミラー スナップショット

スナップショットを作成中に同一のコピーを作成します。データコピー操作により、スナップショットの作成時に2倍のストレージ容量が消費され、時間がかかる場合があります。

バックグラウンドコピーを利用したCopy-On-Writeスナップショット

バックグラウンドコピーを利用したCopy-On-Writeでは、CoWの瞬時スナップショットデータを、バックグラウンドプロセスを利用して元の場所からスナップショット用ストレージにコピーする。これにより、元データのクローンまたはミラーが作成されます。

クラッシュコンシステントとアプリケーションコンシステント

スナップショットの整合性によって以下の種類があります。

  • クラッシュコンシステントは、仮想ディスクに既に書き込まれたデータのみを記録し、メモリ内のデータまたは保留中のI/O操作は含まれません。仮想マシンが正常なシャットダウンをせずに強制終了した状態と同じになります。
  • ファイルシステムコンシステントは、仮想ディスク上のデータに加えて、メモリ内のすべてのデータと保留中のI/O操作を記録します。
  • アプリケーションコンシステントは、メモリ上のデータ、及びすべてのトランザクションのスナップショットが取得されます。

VMwareスナップショットについて

VMwareのスナップショットを作成する際は、元の仮想ディスクの状態が維持され、新たに差分ディスクが作成されます。

スナップショットを取得すると、edit settingの画面では、VM名-000001.vmdkといった差分ディスクが接続された状態となります。

スナップショットはチェーン構造になっており、複数スナップショットを取得した場合、間の差分ディスクが破損すると、リストアができなくなります。

また、スナップショットを取得したり、統合する際に性能影響があります。

VMwareのスナップショットは、長期保存の想定ではなく、バージョンアップ直前の一時的なバックアップなどに利用されます。

差分ディスクへの書き込みは元の仮想ディスクのサイズまで最大拡張されます。

差分ディスクをスパースディスクと呼んでおり、コピーオンライトを利用しているようです。

vSphere VMFS データストアの概念と操作

スナップショットのオプションごとの違い

電源オンの場合、スナップショットのオプションを選択することができます。

  • 仮想マシンのメモリを含めるにチェックがない場合は、クラッシュコンシステントなスナップショットになります。電源オフの状態で静止点に戻ります。
  • 仮想マシンのメモリを含めるにチェックがある場合は、メモリの状態もスナップショットに含まれます。電源オンの状態で静止点に戻ります。おそらくファイルシステムコンシステント相当になるのではと思いますが、記述は見つけられませんでした。
  • ゲスト ファイル システムの静止は、仮想マシンのメモリを含めるがオフの時に選択できるオプションになっており、チェックをがある場合、進行中のプロセスを一時停止することで一貫性がある状態となり、ファイルシステムコンシステントとアプリケーションコンシステントになります。

VMware Knowledge Base

ゲスト ファイル システムの静止は、ゲストOS側での違いや対応が必要となり、WindowsではVSSを利用できるようにし、Linuxではスクリプトを配置することで、静止時に事前スクリプトを実行し、スナップショット取得後に事後スクリプトを実行することで、アプリケーションコンシステントなスナップショットを取ることができます。

VMware Knowledge Base

VMwareでのバックアップ

VMwareでバックアップをする方法はいくつかあります。

一番簡単なのは、クローンで仮想マシンをコピーをとっておくことです。

次に、VADP(vStorage APIs for Data Protection)という、バックアップ用のAPIを利用することでバックアップを取得することができます。Changed Block Tracking (CBT) と呼ばれる増分バックアップカニズムも提供されます。 こちらはバックアップ製品で利用されているものになり、LANを経由してバックアップを取得することができます。ただし、スナップショット機能を利用していることもあり、スナップショットを取得したり、統合する際に性能影響が出てしまいます。

VMware Knowledge Base

VADPで使われているVDDKについて前回記事にしてみました。

hidemium.hatenablog.com

VMwareのスナップショット単体の利用は、長期保存の想定ではなく、バージョンアップ直前の一時的なバックアップなどに利用されるため、長期のバックアップには向きません。

vSphere Replicationを使う方法もあり、こちらはスナップショットではなく、VMkernelのVSCSI filterを通じて、IOを途中で吸い出して転送しており、RPOを短くすることができるようです。

Failover and Remediation Techniques

参考

VDDKのサンプルコードを動かしてみる

VDDK (Virtual Disk Development Kit) は、VMDKの作成やアクセスを行うことができるC ライブラリになります。VDDKのサンプルコードを動かしてみたので、書いてみようと思います。

構成

  • vCenter 7.0 U3
  • ESXi 7.0 Update 3
  • VDDK 7.0.3.3
  • Ubuntu 22.04 (作業用VM)

VDDKとは

VDDKの中には以下のコンポーネントが含まれています。

  • 仮想ディスク ライブラリ。VMDK ファイルを操作するための C 関数呼び出しのセット
  • ディスク マウント ライブラリ。VMDK ファイル システムをリモート マウントするための C 関数呼び出しのセット
  • Visual Studio または GNU C コンパイラコンパイルできる C++ コード サンプル
  • PDF マニュアルとオンライン HTML リファレンス

仮想ディスク ライブラリには、以下の機能があります。

  • 仮想ディスク ファイルの作成、変換、拡張、最適化、縮小、および名前変更
  • REDO ログ (親子ディスク チェーン、つまりデルタ)の作成、VMDK ファイルの削除
  • VMDK ファイル内の任意の場所のデータへのランダムな読み取り/書き込みアクセス、メタデータを読み取り
  • SANやHotAddといった高度なトランスポートを使用したリモートの vSphere ストレージ接続

インストール

それでは、VDDKをインストールしてみます。

以下からVDDKをダウンロードします。

VMware Virtual Disk Development Kit (VDDK)

ダウンロードすると、以下のファイルを入手することができます。

  • VMware-vix-disklib-7.0.3-21933544.x86_64.tar.gz

以下のディレクトリに移動し、解凍を行います。

cd /u
sudo tar xzf ./VMware-vix-disklib-7.0.3-21933544.x86_64.tar.gz 

以下の設定を行い、共有ライブラリをシステムに認識させます。

sudo vi /etc/ld.so.conf.d/vmware-vix-disklib.conf 
/usr/lib/vmware-vix-disklib-distrib/lib64
sudo ldconfig

共有ライブラリのパスが通っていない場合、以下のようなエラーが出ます。

./vixDiskLibSample: error while loading shared libraries: libvixDiskLib.so.7.0.3: cannot open shared object file: No such file or directory

サンプルコードのあるディレクトリに移動します。

cd /usr/lib/vmware-vix-disklib-distrib/doc/samples/diskLib/
tree
.
├── Makefile
├── vixDiskLibSample
├── vixDiskLibSample.cpp
└── vixMntapiSample

usageを見てみます。

sudo ./vixDiskLibSample
Usage: vixdisklibsample.exe command [options] diskPath

List of commands (all commands are mutually exclusive):
 -create : creates a sparse virtual disk with capacity specified by -cap
 -redo parentPath : creates a redo log 'diskPath' for base disk 'parentPath'
 -info : displays information for specified virtual disk
 -dump : dumps the contents of specified range of sectors in hexadecimal
 -mount : mount the virtual disk specified
 -fill : fills specified range of sectors with byte value specified by -val
 -wmeta key value : writes (key,value) entry into disk's metadata table
 -rmeta key : displays the value of the specified metada entry
 -meta : dumps all entries of the disk's metadata
 -clone sourcePath : clone source vmdk possibly to a remote site
 -compress type: specify the compression type for nbd transport mode
 -readbench blocksize: Does a read benchmark on a disk using the 
specified I/O block size (in sectors).
 -writebench blocksize: Does a write benchmark on a disk using the
specified I/O block size (in sectors). WARNING: This will
overwrite the contents of the disk specified.
 -readasyncbench blocksize: Does an async read benchmark on a disk using the 
specified I/O block size (in sectors).
 -writeasyncbench blocksize: Does an async write benchmark on a disk using the
specified I/O block size (in sectors). WARNING: This will
overwrite the contents of the disk specified.
 -getallocatedblocks : gets allocated block list on a disk using the 
specified I/O block size (in sectors).
 -check repair: Check a sparse disk for internal consistency, where repair is a boolean value to indicate if a repair operation should be attempted.

各オプションについてイメージが付きづらいですが、vixDiskLibSampleに対応する関数とコールされるVDDKの関数の詳細はドキュメントに記載あります。

Command Functions - VMware Virtual Disk Development Kit Programming Guide (7.0.3)

vixDiskLibSampleを実行するために、vCenterのThumbprintが必要なため、govcで取得します。

govc about.cert
:
Thumbprints:                 
  SHA-256 Thumbprint:      <Thumbprint> 
  SHA-1 Thumbprint:        <Thumbprint> 

まずは、仮想マシンのVMDKの情報を取得してみます。

sudo ./vixDiskLibSample -info -host <vCenter IPアドレス> -user <username> -password <password> -vm "moref=<仮想マシンのmoid>" -thumb  <Thumbprint> "[Datastore] test/test.vmdk"

Disk[0] "[Datastore] test/test.vmdk is opened using transport mode "nbd".
capacity             = 209715200 sectors
logical sector size  = 512 bytes
physical sector size = 512 bytes
number of links      = 1
adapter type         = LsiLogic SCSI
BIOS geometry        = 0/0/0
physical geometry    = 13054/255/63
Transport modes supported by vixDiskLib: file:nbdssl:nbd
Disk[0] is closed.

VMDKの容量やトランスポートモードでNBDがサポートされることが確認できます。

次に、dumpというものを試し見ます。dumpは VixDiskLib_Read() を呼び出してセクタを取得し、セクタの内容を16 進数で出力します。

sudo ./vixDiskLibSample -dump -host <vCenter IPアドレス> -user <username> -password <password> -vm "moref=<仮想マシンのmoid>" -thumb  <Thumbprint> "[Datastore] test/test.vmdk"
Disk[0] "[Datastore] test/test.vmdk is opened using transport mode "nbd".
0000 : eb 63 90 00 00 00 00 00 00 00 00 00 00 00 00 00   .c..............
0010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0050 : 00 00 00 00 00 00 00 00 00 00 00 80 00 08 00 00   ................
0060 : 00 00 00 00 ff fa 90 90 f6 c2 80 74 05 f6 c2 70   ...........t...p
0070 : 74 02 b2 80 ea 79 7c 00 00 31 c0 8e d8 8e d0 bc   t....y|..1......
0080 : 00 20 fb a0 64 7c 3c ff 74 02 88 c2 52 bb 17 04   . ..d|<.t...R...
0090 : f6 07 03 74 06 be 88 7d e8 17 01 be 05 7c b4 41   ...t...}.....|.A
00a0 : bb aa 55 cd 13 5a 52 72 3d 81 fb 55 aa 75 37 83   ..U..ZRr=..U.u7.
00b0 : e1 01 74 32 31 c0 89 44 04 40 88 44 ff 89 44 02   ..t21..D.@.D..D.
00c0 : c7 04 10 00 66 8b 1e 5c 7c 66 89 5c 08 66 8b 1e   ....f..\|f.\.f..
00d0 : 60 7c 66 89 5c 0c c7 44 06 00 70 b4 42 cd 13 72   `|f.\..D..p.B..r
00e0 : 05 bb 00 70 eb 76 b4 08 cd 13 73 0d 5a 84 d2 0f   ...p.v....s.Z...
00f0 : 83 d0 00 be 93 7d e9 82 00 66 0f b6 c6 88 64 ff   .....}...f....d.
0100 : 40 66 89 44 04 0f b6 d1 c1 e2 02 88 e8 88 f4 40   @f.D...........@
0110 : 89 44 08 0f b6 c2 c0 e8 02 66 89 04 66 a1 60 7c   .D.......f..f.`|
0120 : 66 09 c0 75 4e 66 a1 5c 7c 66 31 d2 66 f7 34 88   f..uNf.\|f1.f.4.
0130 : d1 31 d2 66 f7 74 04 3b 44 08 7d 37 fe c1 88 c5   .1.f.t.;D.}7....
0140 : 30 c0 c1 e8 02 08 c1 88 d0 5a 88 c6 bb 00 70 8e   0........Z....p.
0150 : c3 31 db b8 01 02 cd 13 72 1e 8c c3 60 1e b9 00   .1......r...`...
0160 : 01 8e db 31 f6 bf 00 80 8e c6 fc f3 a5 1f 61 ff   ...1..........a.
0170 : 26 5a 7c be 8e 7d eb 03 be 9d 7d e8 34 00 be a2   &Z|..}....}.4...
0180 : 7d e8 2e 00 cd 18 eb fe 47 52 55 42 20 00 47 65   }.......GRUB .Ge
0190 : 6f 6d 00 48 61 72 64 20 44 69 73 6b 00 52 65 61   om.Hard Disk.Rea
01a0 : 64 00 20 45 72 72 6f 72 0d 0a 00 bb 01 00 b4 0e   d. Error........
01b0 : cd 10 ac 3c 00 75 f4 c3 00 00 00 00 00 00 00 00   ...<.u..........
01c0 : 02 00 ee ff ff ff 01 00 00 00 ff ff 7f 0c 00 00   ................
01d0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
01e0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
01f0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa   ..............U.

Disk[0] is closed.

createを使うと、vmdkの作成ができます。

また、fillをつかうと VixDiskLib_Write() を呼び出して、ディスク セクタを 1 で埋めることができます。

createとfillとdumpを組み合わせて、動作を見てみます。

sudo ./vixDiskLibSample  -create sample.vmdk  # VMDK作成
sudo ./vixDiskLibSample  -fill -val 1 sample.vmdk # ディスクセクタを1で埋める
sudo ./vixDiskLibSample  -fill -val 2 -start 1 -count 1 sample.vmdk # ディスクセクタを2で埋める
sudo ./vixDiskLibSample  -dump -start 0 -count 2 sample.vmdk # セクタの内容を表示
Disk[0] "sample.vmdk" is opened using transport mode "file".
0000 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0010 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0020 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0030 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0040 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0050 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0060 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0070 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0080 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0090 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
00a0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
00b0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
00c0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
00d0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
00e0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
00f0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0100 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0110 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0120 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0130 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0140 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0150 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0160 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0170 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0180 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
0190 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
01a0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
01b0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
01c0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
01d0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
01e0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................
01f0 : 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01   ................

0000 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0010 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0020 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0030 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0040 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0050 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0060 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0070 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0080 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0090 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
00a0 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
00b0 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
00c0 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
00d0 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
00e0 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
00f0 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0100 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0110 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0120 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0130 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0140 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0150 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0160 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0170 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0180 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
0190 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
01a0 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
01b0 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
01c0 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
01d0 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
01e0 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................
01f0 : 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02   ................

Disk[0] is closed.

vixDiskLibSampleコマンドはC++で書かれており、ソースコードはvixDiskLibSample.cppで確認することができます。

エラー関連

共有ライブラリ

共有ライブラリの設定後、aptを実行すると、以下のようなエラーが発生します。

sudo apt
apt: /usr/lib/vmware-vix-disklib-distrib/lib64/libstdc++.so.6: version `GLIBCXX_3.4.29' not found (required by /lib/x86_64-linux-gnu/libapt-private.so.0.0)
apt: /usr/lib/vmware-vix-disklib-distrib/lib64/libstdc++.so.6: version `GLIBCXX_3.4.29' not found (required by /lib/x86_64-linux-gnu/libapt-pkg.so.6.0)

VDDKの「libstdc++.so.6」がどのバージョンまでの GLIBCXX に対応しているか確認したところ、GLIBCXX_3.4.28までしか対応していないことが分かります。

strings /usr/lib/vmware-vix-disklib-distrib/lib64/libstdc++.so.6 | grep GLIBCXX
GLIBCXX_3.4
:
GLIBCXX_3.4.27
GLIBCXX_3.4.28

VDDKのバージョンを新しくするとこのあたりは変わってくるかもしれません。

/etc/ld.so.conf/ ではなく、環境変数でライブラリを指定する場合は、上記のようなエラーは発生しませんした。

export LD_LIBRARY_PATH=/usr/lib/vmware-vix-disklib-distrib/lib64

sudoで実行する場合、環境変数を渡すことができない場合があり、以下のようなエラーがでることがあります。

./vixDiskLibSample: error while loading shared libraries: libvixDiskLib.so.7.0.3: cannot open shared object file: No such file or directory

その場合は、以下のようにsudoに環境変数を指定して実行することもできます。

sudo env LD_LIBRARY_PATH=/usr/lib/vmware-vix-disklib-distrib/lib64 <command>

vixDiskLibSample

-thumbを指定しない場合、以下のエラーが表示されます。

サンプルコードはパラメーターのエラーが分かる程度で、どのパラメータが間違っているかは教えてくれません。

Error: [bora/apps/vixDiskLib/sampleProgram/vixDiskLibSample.cpp:1545]  3 One of the parameters was invalid

コンパイル

vixDiskLibSample.cppのコンパイルを試してみます。

G++のコンパイルに必要なパッケージをインストールします。G++ 10のパッケージもインストールしておきます。

sudo apt install build-essential
sudo apt install g++-10
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 11
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 10
sudo update-alternatives --config g++
# g++-10を選択します。

g++ 11でmakeした際に、以下のエラーメッセージが発生しコンパイルに失敗しました。

g++ --version
g++ (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ sudo make 
g++ -std=c++1y -lpthread -o vix-disklib-sample -I../../../include -L../../../lib64 vixDiskLibSample.cpp -ldl -lz -lsqlite3 -lcurl -lssl -lcrypto -lcares -lvixDiskLib
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `std::__exception_ptr::exception_ptr::exception_ptr(std::__exception_ptr::exception_ptr const&)':
vixDiskLibSample.cpp:(.text._ZNSt15__exception_ptr13exception_ptrC2ERKS0_[_ZNSt15__exception_ptr13exception_ptrC5ERKS0_]+0x36): undefined reference to `std::__exception_ptr::exception_ptr::_M_addref()'
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `std::__exception_ptr::exception_ptr::~exception_ptr()':
vixDiskLibSample.cpp:(.text._ZNSt15__exception_ptr13exception_ptrD2Ev[_ZNSt15__exception_ptr13exception_ptrD5Ev]+0x24): undefined reference to `std::__exception_ptr::exception_ptr::_M_release()'
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `__gnu_cxx::new_allocator<DiskIOPipeline::DiskInfo*>::allocate(unsigned long, void const*)':
vixDiskLibSample.cpp:(.text._ZN9__gnu_cxx13new_allocatorIPN14DiskIOPipeline8DiskInfoEE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorIPN14DiskIOPipeline8DiskInfoEE8allocateEmPKv]+0x49): undefined reference to `std::__throw_bad_array_new_length()'
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `__gnu_cxx::new_allocator<std::thread>::allocate(unsigned long, void const*)':
vixDiskLibSample.cpp:(.text._ZN9__gnu_cxx13new_allocatorISt6threadE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorISt6threadE8allocateEmPKv]+0x49): undefined reference to `std::__throw_bad_array_new_length()'
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `__gnu_cxx::new_allocator<DiskIOPipeline::DiskInfo>::allocate(unsigned long, void const*)':
vixDiskLibSample.cpp:(.text._ZN9__gnu_cxx13new_allocatorIN14DiskIOPipeline8DiskInfoEE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorIN14DiskIOPipeline8DiskInfoEE8allocateEmPKv]+0x49): undefined reference to `std::__throw_bad_array_new_length()'
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `__gnu_cxx::new_allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::allocate(unsigned long, void const*)':
vixDiskLibSample.cpp:(.text._ZN9__gnu_cxx13new_allocatorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE8allocateEmPKv]+0x49): undefined reference to `std::__throw_bad_array_new_length()'
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `__gnu_cxx::new_allocator<VixDiskLibBlock>::allocate(unsigned long, void const*)':
vixDiskLibSample.cpp:(.text._ZN9__gnu_cxx13new_allocatorI15VixDiskLibBlockE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorI15VixDiskLibBlockE8allocateEmPKv]+0x49): undefined reference to `std::__throw_bad_array_new_length()'
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `std::__atomic_futex_unsigned<2147483648u>::_M_load_and_test_until_steady(unsigned int, unsigned int, bool, std::memory_order, bool, std::chrono::duration<long, std::ratio<1l, 1l> >, std::chrono::duration<long, std::ratio<1l, 1000000000l> >)':
vixDiskLibSample.cpp:(.text._ZNSt23__atomic_futex_unsignedILj2147483648EE29_M_load_and_test_until_steadyEjjbSt12memory_orderbNSt6chrono8durationIlSt5ratioILl1ELl1EEEENS3_IlS4_ILl1ELl1000000000EEEE[_ZNSt23__atomic_futex_unsignedILj2147483648EE29_M_load_and_test_until_steadyEjjbSt12memory_orderbNSt6chrono8durationIlSt5ratioILl1ELl1EEEENS3_IlS4_ILl1ELl1000000000EEEE]+0x7c): undefined reference to `std::__atomic_futex_unsigned_base::_M_futex_wait_until_steady(unsigned int*, unsigned int, bool, std::chrono::duration<long, std::ratio<1l, 1l> >, std::chrono::duration<long, std::ratio<1l, 1000000000l> >)'
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `__gnu_cxx::new_allocator<ThreadData>::allocate(unsigned long, void const*)':
vixDiskLibSample.cpp:(.text._ZN9__gnu_cxx13new_allocatorI10ThreadDataE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorI10ThreadDataE8allocateEmPKv]+0x49): undefined reference to `std::__throw_bad_array_new_length()'
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `__gnu_cxx::new_allocator<unsigned long>::allocate(unsigned long, void const*)':
vixDiskLibSample.cpp:(.text._ZN9__gnu_cxx13new_allocatorImE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorImE8allocateEmPKv]+0x49): undefined reference to `std::__throw_bad_array_new_length()'
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `__gnu_cxx::new_allocator<std::_Fwd_list_node<std::future<std::shared_ptr<VixDisk> > > >::allocate(unsigned long, void const*)':
vixDiskLibSample.cpp:(.text._ZN9__gnu_cxx13new_allocatorISt14_Fwd_list_nodeISt6futureISt10shared_ptrI7VixDiskEEEE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorISt14_Fwd_list_nodeISt6futureISt10shared_ptrI7VixDiskEEEE8allocateEmPKv]+0x49): undefined reference to `std::__throw_bad_array_new_length()'
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `__gnu_cxx::new_allocator<std::_Sp_counted_ptr_inplace<std::mutex, std::allocator<std::mutex>, (__gnu_cxx::_Lock_policy)2> >::allocate(unsigned long, void const*)':
vixDiskLibSample.cpp:(.text._ZN9__gnu_cxx13new_allocatorISt23_Sp_counted_ptr_inplaceISt5mutexSaIS2_ELNS_12_Lock_policyE2EEE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorISt23_Sp_counted_ptr_inplaceISt5mutexSaIS2_ELNS_12_Lock_policyE2EEE8allocateEmPKv]+0x49): undefined reference to `std::__throw_bad_array_new_length()'
/usr/bin/ld: /tmp/ccOjIOGn.o: in function `__gnu_cxx::new_allocator<std::_Sp_counted_ptr_inplace<VixDisk, std::allocator<VixDisk>, (__gnu_cxx::_Lock_policy)2> >::allocate(unsigned long, void const*)':
vixDiskLibSample.cpp:(.text._ZN9__gnu_cxx13new_allocatorISt23_Sp_counted_ptr_inplaceI7VixDiskSaIS2_ELNS_12_Lock_policyE2EEE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorISt23_Sp_counted_ptr_inplaceI7VixDiskSaIS2_ELNS_12_Lock_policyE2EEE8allocateEmPKv]+0x49): undefined reference to `std::__throw_bad_array_new_length()'
/usr/bin/ld: /tmp/ccOjIOGn.o:vixDiskLibSample.cpp:(.text._ZN9__gnu_cxx13new_allocatorISt10_List_nodeIPhEE8allocateEmPKv[_ZN9__gnu_cxx13new_allocatorISt10_List_nodeIPhEE8allocateEmPKv]+0x49): more undefined references to `std::__throw_bad_array_new_length()' follow
collect2: error: ld returned 1 exit status
make: *** [Makefile:14: vix-disklib-sample] Error 1

vix-disklib-sampleのビルドは無事成功しましたが、vix-mntapi-sample側で以下のエラーが発生しました。

$ sudo make
g++ -std=c++1y -lpthread -o vix-disklib-sample -I../../../include -L../../../lib64 vixDiskLibSample.cpp -ldl -lz -lsqlite3 -lcurl -lssl -lcrypto -lcares -lvixDiskLib
g++ -std=c++1y -lpthread -o vix-mntapi-sample -DFOR_MNTAPI -I../../../include -L../../../lib64 vixDiskLibSample.cpp -ldl -lz -lsqlite3 -lcurl -lssl -lcrypto -lcares \
   -lfuse -lvixDiskLib -lvixMntapi
/usr/bin/ld: cannot find -lfuse: No such file or directory
collect2: error: ld returned 1 exit status
make: *** [Makefile:17: vix-mntapi-sample] Error 1

Ubuntu 22.04で libfuseが 3系がデフォルトになってしまっており、libfuse2をインストールする必要があります。

ldconfig -p | grep fuse
        libfuse3.so.3 (libc6,x86-64) => /lib/x86_64-linux-gnu/libfuse3.so.3
sudo apt install libfuse2
ldconfig -p | grep fuse
        libfuse3.so.3 (libc6,x86-64) => /lib/x86_64-linux-gnu/libfuse3.so.3
        libfuse.so.2 (libc6,x86-64) => /lib/x86_64-linux-gnu/libfuse.so.2
ls -la /usr/lib/x86_64-linux-gnu | grep fuse
lrwxrwxrwx  1 root root       18 Mar 23  2022 libfuse3.so.3 -> libfuse3.so.3.10.5
-rw-r--r--  1 root root   252088 Mar 23  2022 libfuse3.so.3.10.5
lrwxrwxrwx  1 root root       16 Mar 23  2022 libfuse.so.2 -> libfuse.so.2.9.9
-rw-r--r--  1 root root   260376 Mar 23  2022 libfuse.so.2.9.9

libfuse.soというファイル名でしたが、libfuse.so.2という末尾にバージョン情報が入ってしまい、修正が必要になります。

sudo ln -s libfuse.so.2 /usr/lib/x86_64-linux-gnu/libfuse.so
ls -la /usr/lib/x86_64-linux-gnu | grep fuse
lrwxrwxrwx  1 root root       18 Mar 23  2022 libfuse3.so.3 -> libfuse3.so.3.10.5
-rw-r--r--  1 root root   252088 Mar 23  2022 libfuse3.so.3.10.5
lrwxrwxrwx  1 root root       12 Dec 25 02:19 libfuse.so -> libfuse.so.2
lrwxrwxrwx  1 root root       16 Mar 23  2022 libfuse.so.2 -> libfuse.so.2.9.9
-rw-r--r--  1 root root   260376 Mar 23  2022 libfuse.so.2.9.9

こちらで無事vix-mntapi-sampleのコンパイルも完了しました。

sudo make
g++ -std=c++1y -lpthread -o vix-mntapi-sample -DFOR_MNTAPI -I../../../include -L../../../lib64 vixDiskLibSample.cpp -ldl -lz -lsqlite3 -lcurl -lssl -lcrypto -lcares \
   -lfuse -lvixDiskLib -lvixMntapi

参考

Ansibleとcloud-initを利用した仮想マシンのカスタマイズ

以前、AnsibleのVMwareモジュールとcloud-initを利用した仮想マシンのカスタマイズを書きましたが、今回はAnsibleを使ったcloud-initを利用した仮想マシンのカスタマイズ方法について書いてみたいと思います。

hidemium.hatenablog.com

hidemium.hatenablog.com

構成

  • vCenter 7.0 U3
  • ESXi 7.0 Update 3
  • Ubuntu 22.04 (テンプレートVM)

インストール

前回の手順と比べてAnsibleのバージョンがだいぶ新しくなっているため、再度インストール手順を載せておきます。

community.vmware を利用したAnsibleが実行できる環境を準備してみようと思います。

Ansible 7(ansible-core 2.14)以降をインストールする場合は、Python 3.9以降がインストールされている必要があります。

Releases and maintenance — Ansible Documentation

venvの環境をインストールします。(Ansibleの公式サイトでは、pipxによるインストール手順に変更されてました。)

$ python3 --version
Python 3.9.5
$ sudo apt install python3.9-venv
$ mkdir ansible-vmware
$ cd ansible-vmware
$ python3 -m venv venv (python3 -m venv [仮想環境ディレクトリ名])

venvというフォルダが作成されました。

仮想環境のアクティベートし、Ansibleをインストールします。

$ source venv/bin/activate
(venv) $ python -m pip install ansible

Collectionをインストールします。requirements.ymlを用意し、インストールしてみます。

(venv) $ vi requirements.yml
collections:
- name: community.vmware
(venv) $ ansible-galaxy collection install -r requirements.yml

collectionのフォルダ内にrequirements.txtがあり、こちらを利用し、pyvmomiとvSphere Automation Python SDKをインストールします。

(venv) $ python -m pip install -r ~/.ansible/collections/ansible_collections/community/vmware/requirements.txt
(venv) $ pip list
Package                            Version
---------------------------------- ---------
ansible                            8.5.0
ansible-core                       2.15.5
certifi                            2023.7.22
cffi                               1.16.0
charset-normalizer                 3.3.0
cryptography                       41.0.4
idna                               3.4
importlib-resources                5.0.7
Jinja2                             3.1.2
lxml                               4.9.3
MarkupSafe                         2.1.3
nsx-policy-python-sdk              4.1.0.1.0
nsx-python-sdk                     4.1.0.1.0
nsx-vmc-aws-integration-python-sdk 4.1.0.1.0
nsx-vmc-policy-python-sdk          4.1.0.1.0
packaging                          23.2
pip                                23.3
pkg_resources                      0.0.0
pycparser                          2.21
pyOpenSSL                          23.2.0
pyvmomi                            8.0.2.0
PyYAML                             6.0.1
requests                           2.31.0
resolvelib                         1.0.1
setuptools                         44.0.0
six                                1.16.0
urllib3                            2.0.6
vapi-common-client                 2.44.0
vapi-runtime                       2.44.0
vcenter-bindings                   4.2.0
vmwarecloud-aws                    1.64.0
vmwarecloud-draas                  1.23.0
vsphere-automation-sdk             1.86.0

前回のAnsibleの構成をもとに、cloud-initを利用した仮想マシンをカスタマイズするロールを書いていきます。

hidemium.hatenablog.com

まず、rolesに guest_cloud_init というロールを作成します。ロールの作成はansible-galaxyコマンドを利用します。

$ cd ~/ansible-vmware
$ cd roles
$ ansible-galaxy init guest_cloud_init
$ tree -L 2
.
└── guest_cloud_init
    ├── defaults
    │   └── main.yml
    ├── files
    ├── handlers
    │   └── main.yml
    ├── meta
    │   └── main.yml
    ├── README.md
    ├── tasks
    │   └── main.yml
    ├── templates
    ├── tests
    │   ├── inventory
    │   └── test.yml
    └── vars
        └── main.yml

taskのmain.ymlを編集します。テンプレートからのクローンと、仮想マシンメタデータとユーザデータを設定を行っています。

$ cd guest_cloud_init
$ vi task/main.yml
---
- name:  Clone a virtual machine from Linux template
  community.vmware.vmware_guest:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    datacenter: "{{ datacenter_name }}"
    validate_certs: no
    state: present
    template: "{{ template }}"
    folder: "/{{ datacenter_name }}/vm"
    name: "{{ vm_name }}"
    cluster: "{{ cluster_name }}"
  delegate_to: localhost

- name:  Set the metadata and userdata for the virtual machine
  community.vmware.vmware_guest:
    hostname: "{{ vcenter_hostname }}"
    username: "{{ vcenter_username }}"
    password: "{{ vcenter_password }}"
    validate_certs: no
    state: present
    name: "{{ vm_name }}"
    advanced_settings:
      - key: guestinfo.metadata
        value: "{{ lookup('template', 'metadata.j2') | b64encode }}"
      - key: guestinfo.metadata.encoding
        value: base64
      - key: guestinfo.userdata
        value: "{{ lookup('template', 'userdata.j2') | b64encode }}"
      - key: guestinfo.userdata.encoding
        value: base64
  delegate_to: localhost

templateのmetadata.j2を編集します。このようにすることで、仮想マシン名とゲストOS内のホスト名を同一にすることができます。テンプレート内ではネットワーク設定は固定としていますが、変数として定義も可能です。

$ vi template/metadata.j2
---
instance-id: {{ vm_name }}
local-hostname: {{ vm_name }}
hostname: {{ vm_name }}
network:
  version: 2
  ethernets:
    ens192:
      dhcp4: false
      addresses:
      - 192.168.1.2/24
      gateway4: 192.168.1.1
      nameservers:
        addresses:
        - 192.168.1.1

templateのuserdata.j2を編集します。

$ vi template/userdata.j2
---
#cloud-config

users:
  - default
  - name: username
    ssh_authorized_keys:
      - 公開鍵をペースト
    sudo: ALL=(ALL) NOPASSWD:ALL
    groups: sudo, wheel
    lock_passwd: true
    shell: /bin/bash
 
packages:
    - 必要なパッケージ名を追加

次に、taskの実行に必要な変数について homelab_vm グループの情報として取得できるように、group_vars配下にフォルダを作成して記載していきます。

$ cd ~/ansible-vmware/group_vars
$ mkdir homelab_vm
$ cd homelab_vm
$ vi main.yml
---
vcenter_hostname: vCenter Name
datacenter_name: Datacenter Name
cluster_name: Cluster Name
template: Template Name
vm_name: VM Name

playbookを実行できるか確認します。

$ ansible-playbook -i hosts pb_guest_cloud_init.yml
PLAY [homelab_vm] ************************************************************************************************************************************************************************

TASK [guest_cloud_init : Clone a virtual machine from Linux template] ******************************************************************************************************
changed: [VM name -> localhost]

TASK [guest_cloud_init : Set the metadata and userdata for the virtual machine] ******************************************************************************************
changed: [VM name -> localhost]

PLAY RECAP *****************************************************************************************************************************************************************
VM name : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

全体構成として以下のようになりました。

$ tree -L 4
.
├── ansible.cfg
├── group_vars
│   ├── all
│   │   ├── main.yml
│   │   └── vault.yml
│   └── homelab_vm
│       └── main.yml
├── host_vars
├── hosts
├── pb_guest_cloud_init.yml
├── requirements.yml
├── roles
│   └── guest_cloud_init
│       ├── defaults
│       │   └── main.yml
│       ├── files
│       ├── handlers
│       │   └── main.yml
│       ├── meta
│       │   └── main.yml
│       ├── README.md
│       ├── tasks
│       │   └── main.yml
│       ├── templates
│       │   ├── metadata.j2
│       │   └── userdata.j2
│       ├── tests
│       │   ├── inventory
│       │   └── test.yml
│       └── vars
│           └── main.yml
└── venv
    ├── bin
~ 中略 ~

VMが起動後に、ホスト名が変更されたことや、公開鍵認証を利用してsshでログインできることを確認します。

$ ssh username@192.168.1.2
$ hostnamectl

Ansibleのvmware_guestモジュールは、カスタマイズ仕様を使ったゲストOSの設定が可能ですが、cloud-initには対応はしていなかったため、このような方法でcloud-initが利用できるようになり、より自動化がしやすくなっていくのでは思います。

参考

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に該当します。

CPUID - Intel - WikiChip

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に該当します。

CPUID - Intel - WikiChip

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情報を表示する処理はこちらになるようです。

show_cpuinfo()

CPUID命令を実行してCPUのmodel名やfamily名を取得しているのはこのあたりのように見えます。

cpu_detect()

model nameはこのあたりで取得してそうに見えました。

get_model_name()

VMware仮想マシンで動作しているか判別する方法

Linuxカーネルの中に、vmware_platform()という関数があり、EAXに CPUID_VMWARE_INFO_LEAF を渡すと、hyper_vendor_idに VMwareVMware と返すことが分かります。

vmware_platform()

実際に、以下のコマンドで確認すると、ハイパーバイザーとしてVMwareが使われていることを確認できます。

$ lscpu
:
Hypervisor vendor:               VMware
:

参考

Cluster API Provider vSphereとArgo CDを使ったKubernetesクラスタの管理

前回、Cluster API Provider vSphereを使ってKubernetesクラスタを作成してみました。今回は、Cluster API Provider vSphereとArgo CDを使ってKubernetesクラスタを管理する方法を試してみたいと思います。

hidemium.hatenablog.com

構成

  • vCenter 7.0 U3
  • ESXi 7.0 Update 3
  • Kubespray
  • kubernetes v1.25.5
  • Cluster API v1.3.2
    • clusterctl v1.3.2
    • cluster-api-provider-vsphere v1.5.1
    • ubuntu-2004-kube-v1.25.5.ova
  • 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で管理できるようになるのではと思っています。

参考