Azure Cloud ServiceのVMでページングファイルがCドライブ(永続ストレージ)に配置されている件でトラブった経緯と調査、解決のために行ったことについて記載します。

運用環境

Azure Cloud Serviceで、ExtraSmallのVM2個で運用しています。osFamily、osVersionは未指定です。

OSはWindows Server 2012 R2 Datacenter。メモリは768MBです。

VMで処理が固まる

1年ほど運用してきましたが、最近処理が増えたのか、処理が途中で止まる現象が発生し出しました。

具体的には、

  • メモリを多く使うような箇所でログが途切れている
  • 物理メモリの使用率は常に100%近くで、仮想メモリも1~2GB使っている

ここで思ったのは、「ExtraSmallはそろそろきついか。。」、「もしかしてメモリリークしてる??」でした。メモリリークについてはメモリ使用量が増え続けてないので原因候補から外しました。

「じゃあ、性能不足か。予算が、、」、しかしメモリ使用量は物理、仮想合わせても3GB程度、仮想メモリは4GB確保しているので、まだ余裕はあるはず、この状況で処理が止まるなんてことあるだろうか?う~ん。

ここでふと、処理負荷時にディスク使用率が100%に張り付いているのを見つけました。HDDをそんなに酷使するような処理は行っていないので、なにがそんなに負荷を与えるの?と思いました。そういえばAzureのVMには永続ストレージと一時ストレージがあって、永続ストレージはものすごく遅いんだっけ、もしかして永続ストレージを使っているから重い?、とさらに調査してみることに。

VMにリモートデスクトップでログインしてパフォーマンスモニターで処理負荷時の様子を眺めました。すると以下のことがわかりました。

  • CPU負荷は問題ない
  • メモリ使用量もまだ余裕ある
  • Cドライブのディスク負荷がヤバイ

処理が止まる原因はCドライブの過負荷のようです。しかもCドライブに負荷をかけているのはpagefile.sys。

「そうか、物理メモリが少ないから常にページングファイルでガリガリやってる状態で、処理が立て込むとついには固まる、と。」

状況がわかってきました。

各ドライブの速度測定

一方で、VMには3つのドライブがあって、Cドライブ以外の負荷はまったくないような状況です。ここで疑問に思ったのはCドライブは永続ストレージ?それとも一時ストレージ?ということでした。もし永続ストレージなら改善の余地があるかもしれません。そこでネットを調べたところ、AzureのVMはDドライブが一時ストレージという記載がありますが、Cloud ServiceのVMにも当てはまるのか、情報が少なくはっきりしません。そこで実際に計測してみることにしました。

以下が実際のベンチマーク結果です。

Cドライブ

-----------------------------------------------------------------------
CrystalDiskMark 5.1.2 x64 (C) 2007-2016 hiyohiyo
                           Crystal Dew World : http://crystalmark.info/
-----------------------------------------------------------------------
* MB/s = 1,000,000 bytes/s [SATA/600 = 600,000,000 bytes/s]
* KB = 1000 bytes, KiB = 1024 bytes

   Sequential Read (Q= 32,T= 1) :    43.511 MB/s
  Sequential Write (Q= 32,T= 1) :    56.066 MB/s
  Random Read 4KiB (Q= 32,T= 1) :     1.225 MB/s [   299.1 IOPS]
 Random Write 4KiB (Q= 32,T= 1) :     1.082 MB/s [   264.2 IOPS]
         Sequential Read (T= 1) :    74.436 MB/s
        Sequential Write (T= 1) :    68.354 MB/s
   Random Read 4KiB (Q= 1,T= 1) :     0.547 MB/s [   133.5 IOPS]
  Random Write 4KiB (Q= 1,T= 1) :     0.811 MB/s [   198.0 IOPS]

  Test : 500 MiB [C: 34.8% (6.9/20.0 GiB)] (x5)  [Interval=5 sec]
  Date : 2016/07/28 8:07:01
    OS : Windows Server 2012 R2 Datacenter (Full installation) [6.3 Build 9600] (x64)

Dドライブ

-----------------------------------------------------------------------
CrystalDiskMark 5.1.2 x64 (C) 2007-2016 hiyohiyo
                           Crystal Dew World : http://crystalmark.info/
-----------------------------------------------------------------------
* MB/s = 1,000,000 bytes/s [SATA/600 = 600,000,000 bytes/s]
* KB = 1000 bytes, KiB = 1024 bytes

   Sequential Read (Q= 32,T= 1) :  1397.509 MB/s
  Sequential Write (Q= 32,T= 1) :   880.550 MB/s
  Random Read 4KiB (Q= 32,T= 1) :    68.634 MB/s [ 16756.3 IOPS]
 Random Write 4KiB (Q= 32,T= 1) :    92.339 MB/s [ 22543.7 IOPS]
         Sequential Read (T= 1) :  1697.640 MB/s
        Sequential Write (T= 1) :  1372.134 MB/s
   Random Read 4KiB (Q= 1,T= 1) :    27.629 MB/s [  6745.4 IOPS]
  Random Write 4KiB (Q= 1,T= 1) :    28.783 MB/s [  7027.1 IOPS]

  Test : 500 MiB [D: 34.0% (10.9/32.0 GiB)] (x5)  [Interval=5 sec]
  Date : 2016/07/28 8:13:56
    OS : Windows Server 2012 R2 Datacenter (Full installation) [6.3 Build 9600] (x64)

Eドライブ

-----------------------------------------------------------------------
CrystalDiskMark 5.1.2 x64 (C) 2007-2016 hiyohiyo
                           Crystal Dew World : http://crystalmark.info/
-----------------------------------------------------------------------
* MB/s = 1,000,000 bytes/s [SATA/600 = 600,000,000 bytes/s]
* KB = 1000 bytes, KiB = 1024 bytes

   Sequential Read (Q= 32,T= 1) :   424.361 MB/s
  Sequential Write (Q= 32,T= 1) :   351.871 MB/s
  Random Read 4KiB (Q= 32,T= 1) :    78.930 MB/s [ 19270.0 IOPS]
 Random Write 4KiB (Q= 32,T= 1) :    71.282 MB/s [ 17402.8 IOPS]
         Sequential Read (T= 1) :   365.320 MB/s
        Sequential Write (T= 1) :   334.481 MB/s
   Random Read 4KiB (Q= 1,T= 1) :    13.171 MB/s [  3215.6 IOPS]
  Random Write 4KiB (Q= 1,T= 1) :    18.136 MB/s [  4427.7 IOPS]

  Test : 500 MiB [E: 41.4% (635.0/1534.9 MiB)] (x5)  [Interval=5 sec]
  Date : 2016/07/28 8:23:47
    OS : Windows Server 2012 R2 Datacenter (Full installation) [6.3 Build 9600] (x64)

この結果からDドライブが一時ストレージであることがわかりました。

なぜ永続ドライブに配置するのか

「そもそもなぜページングファイルを永続ストレージに配置するのか。ページングファイルは揮発性の情報だから、永続化する必要はないはずなのに。。単なるミス?それとももともとこのような仕様?」

MSに問い合わせてみようと思いましたが、インシデント代がもったない。。不具合という確証もないし。。

そこでMSDNフォーラムに質問してみました。

https://social.msdn.microsoft.com/Forums/ja-JP/ece39ae4-7f2f-4e4a-bb77-cc0c342743be/cloud-serviceextrasmall-vmpagefilesysc?forum=windowsazureja

ページングファイルの場所をDドライブにしてみる

試しに手動にてページングファイルの場所をDドライブにして様子を見てみると、メモリ使用量は相変わらずですが、ディスク負荷はほぼ解消しました。

OK。これで解決しそうです。

ページングファイルの配置変更を自動化できる?

しかし、Cloud ServiceのVMはOSが自動更新されます。これによりページングファイルもCドライブに戻ってしまいます。OSが自動更新されてもページングファイルをDドライブにできるような方法はないものか。。

質問の回答ばかりに頼っていてもしょうがないので、自分で回避方法を探してみることにしました。

Cloud Servieにはスタートアップタスクという仕組みがあり、アプリケーション配置時にバッチスクリプトなどを実行することができます。既にNewRelicのインストールなどを行っていますし、ここでページングファイルの配置変更ができれば解決しそうです。

しかしページングファイルの設定変更にはOS再起動が必要です。できるのか。ネットで調べてみたところ、スタートアップタスクでOS再起動している人がいました。これはできるかも!やってみよう!

ページングファイルの配置変更スクリプト

やってみた結果、できました。

以下がページングファイルの配置変更スクリプトをスタートアップタスクとして登録する手順です。

1) ServiceDefinition.csdefにスタートアップタスクを登録します。

<ServiceDefinition ...>
  <WorkerRole ...>
    <Startup>
      <Task commandLine="changePagefileLoc.cmd" executionContext="elevated" taskType="simple"/>
    </Startup>
  </WorkerRole>
</ServiceDefinition>

2) changePagefileLoc.cmdをロールプロジェクト直下に追加します。

「出力ディレクトリにコピー」を「常にコピー」にします。
文字コードはASCII。

@echo off
DATE /T >> "%TEMP%\StartupLog.txt" 2>&1
TIME /T >> "%TEMP%\StartupLog.txt" 2>&1
ECHO Starting up changePagefileLoc.cmd. >> "%TEMP%\StartupLog.txt" 2>&1

PowerShell -ExecutionPolicy Unrestricted .\changePagefileLoc.ps1 >> "%TEMP%\StartupLog.txt" 2>&1
EXIT /B 0

3) changePagefileLoc.ps1をロールプロジェクト直下に追加します。

「出力ディレクトリにコピー」を「常にコピー」にします。
文字コードはUTF-8(BOM付き)。

Write-Output "==================="
Write-Output "Change pagefile location start"
$CurrentPageFile = Get-WmiObject -Query "select * from Win32_PageFileSetting"
Write-Output "C drive page file: $CurrentPageFile"
if ($CurrentPageFile -and $CurrentPageFile.Name.ToLower() -eq "c:\pagefile.sys") {
    $computer = Get-WmiObject Win32_computersystem -EnableAllPrivileges
    $computer.AutomaticManagedPagefile = $false
    $computer.Put()
    $CurrentPageFile.delete()

	Set-WmiInstance -Class Win32_PageFileSetting -Arguments @{name="d:\pagefile.sys"; InitialSize = 0; MaximumSize = 0} -EnableAllPrivileges | Out-Null
	$PageFile = Get-WmiObject Win32_PageFileSetting -Filter "SettingID='pagefile.sys @ d:'"
    $PageFile.InitialSize = 4096
    $PageFile.MaximumSize = 4096
    [Void]$PageFile.Put()

	$PageFile = Get-WmiObject Win32_PageFileSetting -Filter "SettingID='pagefile.sys @ d:'"
	Write-Output "D drive page file: $PageFile"

    Write-Output "The computer will restart for the changes to take effect."
	Restart-Computer
}
Write-Host "Change pagefile location end"

まとめ

当面の回避策が見つかってよかったです。しかしなぜこのような仕様になっているのかは謎のままです。

ネットを探しても出てこない症状。可能性があるとすれば最近発生した不具合?、もし元々このような仕様だったとしたら、Cloud Serviceを使っている人はずっとこのような構成のOSを使わされていたということになります。

物理メモリが多いVMサイズではあまり問題になる状況はないかもしれませんが、ひとたびメモリ使用量が物理メモリを超えると、ページングによりCドライブが過負荷になり、しまいにはディスクアクセスエラーになり処理落ちするわけです。

ExtraSmallのように常にメモリ不足のような場合は致命的な問題です。まぁExtraSmallは開発・テスト用とされていますからね。素直に上のサイズ使えということですかね。

MSの人はこのことに気付いているのか、何か理由があってそうしているのか、聞いてみたいです。そういう窓口ってないんですかね。