本記事はvExperts Advent Calendar 2016への寄稿も兼ねております。是非アドベントカレンダーの他の記事もお楽しみください。当社からは私とSEの工藤が寄稿します。
本記事の原文はもともとPernixData社のTechnical Support Engineer (TSE)で、現在はPernixData社のNutanix社による買収でNutanix社のSr. Systems Reliability Engineerとして活動を継続しているGuido Hagemann氏によるものです。
VMworld EMEAに参加した際に初めてお会いしましたが、Guidoさんはサポート担当ですので、時間によってはサポートコールを取ってくれて話やメールをした間柄です。
原文を参照したい方はVMware ESXi locking and how to kill a frozen VMをご確認ください。情報は原文の投稿時のままの情報ですので、現時点では投稿時の情報と製品とで差異が出ている場合があります。
当社のNutanix社製品についてはこちら。VMware社製品についてはこちら。
このブログの記事はフリーズした仮想マシンの強制停止方法についての内容です。テクニカルサポートとして勤務していると「何かがおかしい」としか言いようのないようなケースに出くわすことが有ります。これはストレージの問題であったり、特定のESXiで動作中のプロセスが他の多くの理由からファイルをロックしているということだったりします。この記事では様々なトラブルシューティング、例えばどのホストがロックを取得しているか、APD(オールパスダウン)が起こってはいないか、PDL(Parmanent Device Loss - 恒久的なデバイスの喪失)が起こっていないかなどのテクニックに続けて、どのように仮想マシンを強制停止するか様々な方法を体系立ててご紹介していきます。
まず最初は仮想マシンがどのように動作しているかを見てみましょう。以下の図はESXiの内部の一般的なプロセスを説明したものです:
![Fig143 Fig143]()
図1: ESXi内部のプロセス
これらのプロセスはそれぞれが別々のコンテキスト(context)から来ているため、別々のグループに分類することが出来ます。左上では仮想マシンがユーザーワールドで動作しているのがわかります。続いて幾つかのホストプロセスが動作していますが、ここでは2つだけを例に取り上げています: Hostdとシェルです。
ESXi シェル
ESXiがLinuxベースのものではないということを理解しておくことが重要です。これは最近ではあまり耳にしなくなりましたが、ほんの少しまでは多くの人々がESXiをLinuxベースだと勘違いして色々と試行錯誤したという話をしていました。理由は単に大抵のシェルコマンド(grep, less, more, ps, catなど)が利用でき、SSHでリモートアクセスしたときの挙動が似ているからというだけだったりするのです。ESXiがLinuxベースではないのだったら、どうしてこのようなLinux/Unix由来のコマンドが使えるんだい?それはとても簡単です。単にVMwareが「Busybox」と呼ばれるソフトウェア実装を利用することにしたからです。これはVMwareによる軽量なシェル実装で、典型的なシェルコマンドの実行をおこないます。BusyBoxはユーザーワールドのプロセスとしてVMKernel上で動作します。つまり、基本的にはWindows上のcygwinに似たようなものです。さて、これで何故コマンドがシェルの中で動作するのかがわかりました。Busyboxのおかげです。
"ps"(process status/プロセスの状態)コマンドを利用すると、特定の仮想マシンのサブコンポーネントがどのような状態かよくわかります。仮想マシンがフリーズしてしまった時、以下の2つを知ることが重要です:
フリーズした仮想マシンを強制停止する
フリーズした仮想マシンの強制停止の方法は多くあります。以下では4つの例を挙げてどのように仮想マシンを強制停止するかをご紹介します。例を上げながら、強制停止する方法をどのように適切なデータで特定しながら行うのかご紹介していきます。どのツールを利用するのが良い、悪いはなく、私は単に何が可能なのか、そのヴァリエーションをご紹介したいと思います。強制終了のパートのあと、更に、VMFSとNFSのロック機構についてもいくらかご紹介致します。
シェルツール (ps と kill)
esxcli
vim-cmd
ESXtop
“PS”を“KILL”と組み合わせる
仮想マシンの状態を知るために、以下の"ps"コマンドを利用します(この例では仮想マシン名はam1ifvh029です)。ご覧のとおり、この仮想マシンは8つのvCPUを持つことがわかります。これは8つのvmm0~7、そして同じくvmx-vcpu-0~7と8つの仮想スレッドがmksとsvgaのスレッド以外に存在することからわかります。最後の列はグループID(GID)を示しており、これがメインとなるプロセスです。
~ # ps -jv | egrep "WID|am1ifvh029"
WID CID WorldName GID
645172 0 vmm0:am1ifvh029 645137
645174 0 vmm1:am1ifvh029 645137
645175 0 vmm2:am1ifvh029 645137
645176 0 vmm3:am1ifvh029 645137
645177 0 vmm4:am1ifvh029 645137
645178 0 vmm5:am1ifvh029 645137
645180 0 vmm6:am1ifvh029 645137
645181 0 vmm7:am1ifvh029 645137
645219 645137 vmx-vthread-13:am1ifvh029 645137
645220 645137 vmx-mks:am1ifvh029 645137
645221 645137 vmx-svga:am1ifvh029 645137
645222 645137 vmx-vcpu-0:am1ifvh029 645137
645223 645137 vmx-vcpu-1:am1ifvh029 645137
645224 645137 vmx-vcpu-2:am1ifvh029 645137
645225 645137 vmx-vcpu-3:am1ifvh029 645137
645226 645137 vmx-vcpu-4:am1ifvh029 645137
645227 645137 vmx-vcpu-5:am1ifvh029 645137
645228 645137 vmx-vcpu-6:am1ifvh029 645137
645229 645137 vmx-vcpu-7:am1ifvh029 645137
プロセスを終了させるためにはkillコマンドを-9オプションとともに利用します。もしもkillコマンドの別のオプションについて詳しく知りたい場合にはこちらをご参照ください。基本的には-9はカーネルがプロセス自身に何も通知を行わずに終了を行うという意味になります。これは理論的にはプロセスが何をしているかによってはデータを喪失する可能性があり、終了方法ではもっともハードなものになります。もちろん最初は"kill -1"(ハングアップシグナルをプロセスに送る)、"kill -2"(CTRL+Cと同じ)を試した後に行うのが良いでしょう。
~ # kill -9 645137
"kill"コマンドはそれが出来てしまう場合には何も確認を返してこずにプロセスを終了させてしまいます。psコマンドでもう一度プロセスの動作を確認すると、プロセスIDがなくなっているという事になります。
“ESXCLI VM PROCESS”を使う
ESXi上ではesxcliコマンドでハイパーバイザーの低レベルなインフラストラクチャの管理を行うことが出来ます。この先にご紹介するvim-cmdコマンドとは異なり、これは完全にESXiのその下のインフラストラクチャにフォーカスしたコマンドです。このコマンドはただひとつのコマンド(esxcli)に見えますが、様々なネームスペースを利用した広範なサブコマンドをもっています。ありがたく、また以前のesxcfg-コマンドよりも優れていることには、これはツリー階層構造にまとめられていることです。シェルにコマンドを入力し、いつでも利用可能なすべてのオプションを参照することが出来ます。このVMware KB: 2012964でよく使う組み合わせのコマンドを見つけることが可能で、esxcliとvim-cmdとPowerCLIがどのように違っているのかを見ることが出来ます。プロセスを停止させる前にそのプロセスがどの状態になっているのかということを確認してください。esxcliについてはSteve Jin氏によって書かれた素晴らしいブログの記事もあります。
~ # esxcli vm process list | grep -i -A 4 am1ifvh029
am1ifvh029
World ID: 645172
Process ID: 0
VMX Cartel ID: 645137
UUID: 42 21 23 10 79 c5 62 80-9b 06 74 21 81 9a fc 57
Display Name: am1ifvh029
Config File: /vmfs/volumes/55883a14-21a51000-d5e9-001b21857010/am1ifvh029/am1ifvh029.vmx
仮想マシンを強制停止するには"World ID"を利用しなくてはなりません。Worldを強制停止するには別のオプション(--type または -t)が用意されています:
~ # esxcli vm process kill -t=soft -w "645172"
なにもオプションを指定しない場合、標準では"soft"にて実行されます。"hard"または"force"を試してみてください。見ての通り、最初の例で"ps"コマンドで見たメインのプロセスIDとワールドIDは必ず同じものになります。これはいつもvmm0のIDです。
“VIM-CMD”を利用して仮想マシンを強制停止する
もう一つの仮想マシンの状態を確認して、停止を行うコマンドはvim-cmdです。これは「hostd」上に実装されており、ESXiとhostdとが統合されているAPIとほとんど同じように利用することが出来ます。vim-cmdは多くの運用タスクにも利用することが可能です。Steve Jin氏によるもう一つのesxcli同様に素晴らしい記事は
こちら。
ESXi内部のvim-cmdは/bin/vim-cmdに格納されており、これ自身は実際にはhostdへのシンボリックリンクです:
~ # ls -l /bin/vim-cmd
lrwxrwxrwx 1 root root 10 Mar 4 2016 /bin/vim-cmd -> /bin/hostd
vim-cmdにはいくつかのサブコマンドが有ります。それが何であるかを知るためには単にvim-cmdとシェルに打ち込めばすみます:
~ # vim-cmd
Commands available under /:
hbrsvc/ internalsvc/ solo/ vmsvc/
hostsvc/ proxysvc/ vimsvc/ help
見ての通り、7つのサブコマンド(とhelp)があります。それぞれが何のためにあるのかが分かりますし、ESXiの内部にどれだけの機能やオプションが取り込まれているのかを想像することも出来ます。svc(サービス)を取り除きたいのであれば、基本的にそれぞれのコマンドを利用します:hbr、internal、solo、vm、host、proxy、vimそしてhelpです。実際にはinternalsvcはほんとうの意味のESXiの内部APIではないということは覚えておいてください。
今は仮想マシンについて何らかの作業をしようとしていますので、"vmsvc"コマンドを使うということになります。vim-cmd vmsvcと打ち込むことで以下の結果を得られます :
~ # vim-cmd vmsvc
Commands available under vmsvc/:
acquiremksticket get.snapshotinfo
acquireticket get.spaceNeededForConsolidation
connect get.summary
convert.toTemplate get.tasklist
convert.toVm getallvms
createdummyvm gethostconstraints
destroy login
device.connection logout
device.connusbdev message
device.ctlradd power.getstate
device.ctlrremove power.hibernate
device.disconnusbdev power.off
device.diskadd power.on
device.diskaddexisting power.reboot
device.diskremove power.reset
device.getdevices power.shutdown
device.toolsSyncSet power.suspend
device.vmiadd power.suspendResume
device.vmiremove queryftcompat
devices.createnic reload
get.capability setscreenres
get.config snapshot.create
get.config.cpuidmask snapshot.dumpoption
get.configoption snapshot.get
get.datastores snapshot.remove
get.disabledmethods snapshot.removeall
get.environment snapshot.revert
get.filelayout snapshot.setoption
get.filelayoutex tools.cancelinstall
get.guest tools.install
get.guestheartbeatStatus tools.upgrade
get.managedentitystatus unregister
get.networks upgrade
get.runtime
今回は仮想マシンの強制終了ですから、実際のvmidの状態を見る必要があります:
~ # vim-cmd vmsvc/getallvms | grep -i 'vmid\|am1ifvh028' | awk '{print $1,$2}'
Vmid Name
4 am1ifvh028
現在の電源状態を得る場合には以下のコマンドを使います:
~ # vim-cmd vmsvc/power.getstate 4
さて、結果として、仮想マシンが動作しているというアウトプットが得られました。
Retrieved runtime info
Powered on
vim-cmdを利用して仮想マシンの電源を切るには以下のコマンドを実行します:
~ # vim-cmd vmsvc/power.off 4
ESXtopを利用して仮想マシンを強制停止する
以下のコマンドを利用してesxtopユーティリティを起動します。
- esxtopを実行する(esxtopはCPU表示で起動します、"c"を押すことで別の表示からCPUリソース利用状況の画面へ戻ってくることが出来ます)
- "Shift+v"を押すことで、仮想マシンの表示へと限定することが出来ます。これによって時々多くのプロセスが表示されてしまい、仮想マシンについて全く見えないということを防ぎ、可読性が良くなります。
"f"をおして、表示されるリストのフィールドを表示します。
- "c"をおして、"Leader World ID"の列を追加します。これはどの仮想マシンを強制停止するのか見極めるために必要です。
- 名前とLeader World ID(LWID)から目的の仮想マシンを特定します。
"k"を押します。
- そうすると強制停止するWorld(WID)を聞かれます。ステップ5のLWIDを入力し、エンターを押します。
- 数秒の後、プロセスが消えてなくなります。
ESXiのロック機構
ですが、上のすべての選択肢が役に立たなかったらどうしたら良いのでしょうか?偶然に、他のホストが仮想マシンをロックしてしまったのでしょうか? もしも仮想マシンが応答しているものの、表示はされており、アクセス不能状態になっている場合には仮想マシンが現在動作しているホストがロックを保持している事になります。この場合、上のすべての方法は動作しません。たまたま、他のホストがロックをまだ保持したままになっているのです。過去に仮想マシンがどのホストで動作していたかということを知ることは常に重要な事です。以下のコマンドでどこに仮想マシンが登録されていたかということをvmware.logsから知ることが出来ます。
~ # find /vmfs/volume -name <vmname>
/vmfs/volumes/<DatastoreUUID>/<vmname>
findコマンドのあとは、当該のディレクトリへと移動するか、grepで検索を行います:
# grep -i hostname vmware*
vmware-188.log:2016-08-11T14:45:26.065Z| vmx| I120: Hostname=am1ifvh004
vmware-189.log:2016-08-25T14:10:15.054Z| vmx| I120: Hostname=am1ifvh003
vmware-190.log:2016-09-02T01:39:45.934Z| vmx| I120: Hostname=am1ifvh003
vmware-191.log:2016-09-13T05:31:17.699Z| vmx| I120: Hostname=am1ifvh003
vmware-192.log:2016-09-13T15:55:42.495Z| vmx| I120: Hostname=am1ifvh003
vmware-193.log:2016-10-07T15:59:35.317Z| vmx| I120: Hostname=am1ifvh004
vmware.log:2016-10-10T17:04:38.627Z| vmx| I120: Hostname=am1ifvh003
仮想マシンが2016-10-10T17:04:38.627Zから am1ifvh003 ホストで動作していることがわかります。
どのデータストアで仮想マシンが動作していた家を見つけるもう一つの方法は既にご紹介したesxcliコマンドです。クラスタ内のデータストアにアクセス可能なホストのうちの一つから以下の例を使って、どこに仮想マシンが登録されているのか見ていきます。vCenterがダウンしている場合には仮想マシンが最後にどこにいたのかを知るためには上の例を使ってください。.vmxファイルがどこにあるのかは、このファイル自身がESXiからの.lckファイルになっているので2通りの方法があります:
~ # esxcli vm process list | grep -i -A 4 <vmname> | grep -i 'Config File' | awk '{print $3}'
--> /vmfs/volumes/<DatastoreUUID>/<vmname>/<vmname>.vmx
~ # lsof | grep -i <vmname>.vmx.lck | awk '{print $NF}'
--> /vmfs/volumes/<DatastoreUUID>/<vmname>/<vmname>.vmx.lck
VMFSのロック機構の説明
最初はチェックしたい仮想マシンのディレクトリへと移動し、誰がロックを保持しているのかを見ます。
~# cd /vmfs/volumes/<DatastoreName/<UUID>/<vmname>/
vmkfstools -D を利用して2つのことを確認します:
どのMACアドレスがロックを保持しているか
そのファイルがどのオフセットを保持しているか
~# vmkfstools -D <vmname>.vmx.lck
Lock [type 10c00001 offset 189607936 v 46492, hb offset 3723264
gen 3377, mode 1, owner 57f7c8e2-8f5d86e3-efc8-001b21857010 mtime 110695
num 0 gblnum 0 gblgen 0 gblbrk 0]
Addr <4, 438, 118>, gen 46491, links 1, type reg, flags 0, uid 0, gid 0, mode 600
len 0, nb 0 tbz 0, cow 0, newSinceEpoch 0, zla 4305, bs 8192
最初の簡単な方はOwner IDの最後の部分001b21857010を確認することです。これはロックを保持しているホストのNICの一つのMACアドレスに関連しています。"esxcli network nic list"コマンドを利用して、誰がそのNICを保持しているのかを調べることが出来ますし、c#のvSphereクライアント、Webクライアント、もしくはシェルでも誰が<vmname>.vmx.lck ファイルのオーナーなのかを確認できます。
~# esxcli network nic list | awk '{print $1,$8}
Name Status
------ -----------------
vmnic0 38:63:bb:3f:19:48
vmnic1 38:63:bb:3f:19:49
vmnic2 38:63:bb:3f:19:4a
vmnic3 38:63:bb:3f:19:4b
vmnic4 00:1b:21:85:70:10
vmnic5 00:1b:21:85:70:11
2つ目のオプションはowner IDがゼロとして表示された際に利用するものです。この場合、<vmname>.vmx.lckファイルのオフセットを利用します。以下のコマンドを利用してください:
~# hexdump -C /vmfs/volumes/<datastore>/.vh.sf -n 512 -s <offset>
データストアは仮想マシンが動作しているデータストアです、ですから、一段戻ってデータストアレベルで実行してください。オフセットの値は以前のコマンド(上では 3723264)です。
アウトプットの16進数のオフセット(黄色にハイライトしてあります)を利用してESX/ESXiホストのMACアドレスとロックの状態を調べることが出来ます:
~# hexdump -C /vmfs/volumes/<datastore>/.vh.sf -n 512 -s <3723264>
0038d000 02 ef cd ab 00 d0 38 00 00 00 00 00 31 0d 00 00 |......8.....1...|
0038d010 00 00 00 00 fa 0f e1 f5 ee 00 00 00 e2 c8 f7 57 |...............W|
0038d020 e3 86 5d 8f c8 ef 00 1b 21 85 70 10 81 d1 0c 01 |..].....!.p.....|
0038d030 0e 00 00 00 3d 04 00 00 00 00 00 00 00 00 00 00 |....=...........|
0038d040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
0038d200
7番目から12番目までのバイトがMACアドレスです :00 1b 21 85 70 10
それから"esxcli network nic list"を利用してそのオーナーを調べます。これが特定のホストの物理NICのものになります。こうして、仮想マシンが他のホストに登録されており、ロックされていることが確認できればロックを保持しているホストへと仮想マシンを再度移動させて、仮想マシンを起動させることが出来ます。DRSをマニュアルにしておくということを忘れないで下さい。そうしておかないと仮想マシンは他のホストで間違って起動されてしまいます。ここまでの全てができなかった場合、最終的な手段としてはロックを保持しているホストの再起動になります。
NFSのロック機構の説明
NFSの場合には誰がロックを保持しているのか問うことを確かめるにはちょっと事情が異なります。これはファイルベースのプロトコルですから、当たり前のことです。
仮想マシンのディレクトリへと移動します。( "esxcli vm process list"コマンドなどで同様にどこに仮想マシンがいるか見つけられます)
VMFSとは異なり、ファイルでのすべての操作は対応する .lckファイルに対してのものになります。VMDKの数が少ない仮想マシンの場合でも、そこそこの数の .lckファイルが表示されます。ですからどれが.vmx.lckなのかを見つけなくてはいけません。".lck-3409000000000000"を例として取り上げましょう。
~# ls -lA | grep .lck-
-rwxrwxr-x 1 root root 84 Oct 19 13:29 .lck-3409000000000000
-rwxrwxr-x 1 root root 84 Oct 19 13:29 .lck-3d01000000000000
-rwxrwxr-x 1 root root 84 Oct 19 13:29 .lck-4801000000000000
-rwxrwxr-x 1 root root 84 Oct 19 13:29 .lck-5301000000000000
-rwxrwxr-x 1 root root 84 Oct 19 13:29 .lck-5e01000000000000
-rwxrwxr-x 1 root root 84 Oct 19 13:29 .lck-6901000000000000
-rwxrwxr-x 1 root root 84 Oct 19 13:29 .lck-7401000000000000
-rwxrwxr-x 1 root root 84 Oct 19 13:29 .lck-7f01000000000000
-rwxrwxr-x 1 root root 84 Oct 19 13:29 .lck-8a01000000000000
-rwxrwxr-x 1 root root 84 Oct 19 13:29 .lck-9501000000000000
-rwxrwxr-x 1 root root 84 Oct 19 13:29 .lck-a001000000000000
-rwxrwxr-x 1 root root 84 Oct 19 13:29 .lck-ab01000000000000
-rwxrwxr-x 1 root root 84 Oct 19 13:29 .lck-e201000000000000
-rwxrwxr-x 1 root root 84 Oct 19 13:29 .lck-f208000000000000
hexdumpコマンドで各.lckファイルのホスト名を調べなくてはなりません。
~# hexdump -C .lck-3409000000000000
00000000 fd 79 97 00 00 00 00 00 23 01 cd ab ff ff ff ff |.y......#.......|
00000010 01 00 00 00 61 6d 31 69 66 76 68 30 30 33 00 00 |....am1ifvh003..|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000030 00 00 00 00 57 ac 79 10 71 18 c3 9e f3 16 00 1b |....W.y.q.......|
00000040 21 85 70 10 00 00 00 00 00 00 00 00 00 00 00 00 |!.p.............|
00000050 00 00 00 ff |....|
00000054
上の例では、このロックファイルがam1ifvh003で保持されています。これでどのホストがこの.lckファイルを持っているのことがわかりました。しかし、
.lck-3409000000000000がどのファイルのためのロックなのかがわかりません。今度はエンディアンを以下の図にあるようにひっくり返さなければなりません。
![Fig144 Fig144]()
図2: ビッグエンディアンからリトルエンディアンへの翻訳
次のステップは16進数から10進数への変換です。今回のサンプルでは、エンディアンは必要のない0で多く埋め尽くされています。翻訳はこの通り: 0x934 = 2356 (10進数)
さて、続いて以下のコマンドでどのinodeを参照しているのかを調べます:
~# stat * | grep -B2 2356 | grep File
File: am1ifpt002.vmx.lck
つまり、最初のロックファイルは<vmname>.vmx.lck ファイルということになります。
このコマンドを活用して、自動的に同じことをESXi上で行うことも出来ます。(この例では、エンディアンをひっくり返す必要もなくなります):
~# stat * | grep -B2 `v2=$(v1=.lck-3409000000000000;echo ${v1:13:2}${v1:11:2}${v1:9:2}${v1:7:2}${v1:5:2});printf "%d\n" 0x$v2` | grep File
File: am1ifpt002.vmx.lck
結論
仮想マシンがフリーズしてしまうのには数多くの理由が考えられます。誰がロックファイルを保持しているのかを様々な方法で見つけ出すことができれば、仮想マシンをどのように強制終了させるのか、フリーズした仮想マシンの問題をどのように解決するのか、多くの場合の糸口を見つけることが出来ます。もちろん、最初にご説明したようにストレージシステムやSCSI予約の問題、ストレージシステムのバグによる嘘のinode番号、などが原因ということも有ります。私の記事が気に入った、もっといい提案や推奨したい方法があるなど、いつでもお気軽にご連絡ください。
記事担当者: マーケティング本部 三好哲生 (@miyo4i)
今回はvExpertのAdvent Calenderということで、普段は絶対に訳さないようなテッキーな内容をお届け致しました。実は結構このあたり、お客様で発生したトラブルでPernixDataが悪さをしているという嫌疑をかけられ、その原因調査などで実際に使ったテクニックなんかも含まれていたりして。
いずれにしても、仮想マシンのフリーズ、あまり出くわしたくない事象ですが、万が一出くわしてしまった場合、盲に再起動するのではなく原因を調べ、再発を防ぐために何らか情報が欲しいものです。今回の情報はおそらく他にはないレベルでこれを説明した内容でしょう。Guidoさん、いつもありがとう!