見出し画像

TWSNMPでVMware仮想基盤を監視する

TWSNMPにVMwareの仮想基盤を監視する機能をつけた話です。パッケージやライブラリのサンプルプログラムはシンプルな方がよいという話も書いておきます。今朝、猫に3時に起こされたのですが一週間の疲れで起きられず、かみさんが代わりにお世話してました。今は昼近くですが一度、猫が甘えにきて何か言ってましたが今は寝ています。

VMware仮想基盤の監視機能

いつの間にか社内システムや開発環境に実物のパソコンやサーバーを使うことは少なくってきました。ほとんど仮想マシンです。ですから、沢山の仮想マシンが動作する仮想基盤はとても重要な存在です。仮想基盤がダウンすれば影響は甚大です。そこでTWSNMPにも仮想基盤を監視する機能をつけようと思いました。
監視する項目は、

・CPUの使用量
・メモリの使用量
・データストア(ハードディスク)の使用量
・仮想マシンの稼働状況

に対応することにしました。仮想基盤に搭載しているCPUのうち仮想マシンが使用している割合を周波数で計算した値にしました。例えば仮想基盤のCPUが2GHzで8コアならば、最大利用可能な周波数は16GHzです。稼働している仮想マシンがトータル8GHz利用していれば50%ということです。メモリとデータストアは単純に使用割合で監視できます。仮想マシンの稼働状況は登録している仮想マシンのうち電源の入っている仮想マシンの割合でよいと思います。これらの値を定期的に取得してTWSNMPのAIに分析させれば何かわかるかもしれません。楽しみです。

VMwareの仮想基盤から情報を取得する方法

VMwareの仮想基盤の管理にはAPIが用意されています。これを使うためのライブラリやパッケージをVMware社がGit HUBで公開しています。

ありがたいことです。TWSNMPはGO言語で開発しているのでGO言語のためのパッケージを見てみます。

Projects using govmomiの説明を読むと最近流行りの技術で使われていることがわかります。もちろん、これを使うことにしました。

サンプルを探せ

さて、使用するパッケージは見つかったのですが、機能が多すぎて私のやりたいことのために使える関数がどこにあるのかさっぱりわかりません。ドキュメントのリンクとかを見ましたが、なかなか見つかりません。「困った」
こういう場合はソースコードの中のサンプルコードを探すようにしています。今回のパッケージのソースコードには、それらしいディレクトリがありました。

画像1

中を探してソースコードを読んで見るとありました。

画像2

CPUとメモリはhosts(緑)、データストアはdatastore(赤),仮想マシンの情報はvirtualmachines(黄色)の中のサンプルコードに取得方法が書いてありました。一歩前進。

サンプルコードが分かりにくい

せっかく見つけた手がかりのサンプルコードですが、凝った作りになっていてあまり理解しやすい感じではありません。hostsの中のコードは、

func main() {
	examples.Run(func(ctx context.Context, c *vim25.Client) error {

		// Create a view of HostSystem objects
		m := view.NewManager(c)

		v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{"HostSystem"}, true)
		if err != nil {
			return err
		}

		defer v.Destroy(ctx)

		// Retrieve summary property for all hosts
		// Reference: http://pubs.vmware.com/vsphere-60/topic/com.vmware.wssdk.apiref.doc/vim.HostSystem.html
		var hss []mo.HostSystem
		err = v.Retrieve(ctx, []string{"HostSystem"}, []string{"summary"}, &hss)
		if err != nil {
			return err
		}

		// Print summary per host (see also: govc/host/info.go)

		tw := tabwriter.NewWriter(os.Stdout, 2, 0, 2, ' ', 0)
		fmt.Fprintf(tw, "Name:\tUsed CPU:\tTotal CPU:\tFree CPU:\tUsed Memory:\tTotal Memory:\tFree Memory:\t\n")

		for _, hs := range hss {
			totalCPU := int64(hs.Summary.Hardware.CpuMhz) * int64(hs.Summary.Hardware.NumCpuCores)
			freeCPU := int64(totalCPU) - int64(hs.Summary.QuickStats.OverallCpuUsage)
			freeMemory := int64(hs.Summary.Hardware.MemorySize) - (int64(hs.Summary.QuickStats.OverallMemoryUsage) * 1024 * 1024)
			fmt.Fprintf(tw, "%s\t", hs.Summary.Config.Name)
			fmt.Fprintf(tw, "%d\t", hs.Summary.QuickStats.OverallCpuUsage)
			fmt.Fprintf(tw, "%d\t", totalCPU)
			fmt.Fprintf(tw, "%d\t", freeCPU)
			fmt.Fprintf(tw, "%s\t", (units.ByteSize(hs.Summary.QuickStats.OverallMemoryUsage))*1024*1024)
			fmt.Fprintf(tw, "%s\t", units.ByteSize(hs.Summary.Hardware.MemorySize))
			fmt.Fprintf(tw, "%d\t", freeMemory)
			fmt.Fprintf(tw, "\n")
		}

		_ = tw.Flush()

		return nil
	})
}

VMwareの仮想基盤に接続する部分などが別のパッケージになっています。取得した情報を表示する部分も表示を整形するパッケージを使っています。もう少しシンプルな方が見やすいと思います。

サンプルコードを再構成

自分の理解を深めるためにサンプルコードを(自分にとって)分かりやすく再構成しました。

func main() {
    // 接続先のURLを作る
	u, err := soap.ParseURL("https://user:pass@127.0.0.1:8989/sdk")
	if err != nil {
		log.Panic(err)
	}
    // タイムアウトのためのcontextを作る
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
   // 接続する
	client, err := govmomi.NewClient(ctx, u, true)
	if err != nil {
		log.Panic(err)
	}
   // ホスト情報を取得する
	if err := hosts(ctx, client.Client); err != nil {
		log.Println(err)
	}
	if err := ds(ctx, client.Client); err != nil {
		log.Println(err)
	}
	if err := vms(ctx, client.Client); err != nil {
		log.Println(err)
	}
}

func hosts(ctx context.Context, c *vim25.Client) error {
	m := view.NewManager(c)
	v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{"HostSystem"}, true)
	if err != nil {
		return err
	}
	defer v.Destroy(ctx)
	var hss []mo.HostSystem
	err = v.Retrieve(ctx, []string{"HostSystem"}, []string{"summary"}, &hss)
	if err != nil {
		return err
	}
  // ホスト情報の表示
	fmt.Printf("Name:\tUsed CPU:\tTotal CPU:\tFree CPU:\tUsed Memory:\tTotal Memory:\tFree Memory:\t\n")
	for _, hs := range hss {
		totalCPU := int64(hs.Summary.Hardware.CpuMhz) * int64(hs.Summary.Hardware.NumCpuCores)
		freeCPU := int64(totalCPU) - int64(hs.Summary.QuickStats.OverallCpuUsage)
		freeMemory := int64(hs.Summary.Hardware.MemorySize) - (int64(hs.Summary.QuickStats.OverallMemoryUsage) * 1024 * 1024)
		fmt.Printf("%s\t", hs.Summary.Config.Name)
		fmt.Printf("%d\t", hs.Summary.QuickStats.OverallCpuUsage)
		fmt.Printf("%d\t", totalCPU)
		fmt.Printf("%d\t", freeCPU)
		fmt.Printf("%s\t", (units.ByteSize(hs.Summary.QuickStats.OverallMemoryUsage))*1024*1024)
		fmt.Printf("%s\t", units.ByteSize(hs.Summary.Hardware.MemorySize))
		fmt.Printf("%d\t", freeMemory)
		fmt.Printf("\n")
	}
	return nil
}

見やすくなりました。これで試験してうまくいったらTWSNMPに組み込めます。

安心して使えるテスト環境が必要

このプログラムをテストするにはVMwareの仮想基盤(ESXiなど)が必要です。最初から実際に使っている環境で試験するのはちょっと怖いのでお試しで使える環境が必要です。幸いなことに使用しているGO言語のパッケージには、APIのシュミュレータがありました。

vcsim - A vCenter and ESXi API based simulator

これを使ってテストしました。


開発のための諸経費(機材、Appleの開発者、サーバー運用)に利用します。 ソフトウェアのマニュアルをnoteの記事で提供しています。 サポートによりnoteの運営にも貢献できるのでよろしくお願います。