2015年5月29日金曜日

AWSのリソース使用状況レポートを自動生成しインスタンスタイプの最適化を図る【カスタムメトリクスデータ送信編】

こんにちは、井下です。

新人研修の講師の時期が近づいてきて、資料の見直しと更新をしていますが、1年も経つと既に古い技術になっていたり、新しい概念が出てきたりして、更新する部分が意外と多いものだとしみじみと感じています。

はじめに

さて、今回から「AWSのメトリクスを自動で収集・レポート作成し、インスタンスタイプの最適化を図る」というテーマで3回ほどブログを掲載することを予定しています。

意識されている方は多いと思いますが、EC2インスタンスの課金体系は基本的に 稼働時間 × インスタンスタイプ になっています。
つまり、できるだけ使わない時間は動かさずに、できるだけ無駄のないスペックのインスタンプタイプを選択することが、コストの最適化に繋がるということになります。

稼働時間に関しては、先月書いたインスタンスの起動・停止の自動化によって、無駄を省けるようになると思いますが、インスタンプタイプはどうでしょうか?

個人利用や検証であれば、最小のmicroインスタンスを利用しても問題は起きにくいですが、外部公開したり、業務に直結するインスタンスであれば、どれを選ぶか吟味しなくてはなりません。
また、インスタンスタイプを選択した後になってから、当初とは事情が異なってスペックが足りなくなったり、持て余してしまうことも考えられないことではありません。

インスタンスタイプが適切であるかを判断するためには、CPU使用率やメモリ使用率などのリソースの状況を確認すると思います。
また、リソースの状況は一時的なものではなく、例えば1日のリソースの監視結果など、ある程度の期間のものを確認したくなるのではないでしょうか。

そんな要望に応えるサービスとして、AWSから「CloudWatch」というサービスが提供されています。

実はインスタンスを作成すると、自動でCloudWatchが次の10個の項目について監視を開始しており、その結果はAWS マネージメントコンソールから確認することができます。(監視する項目は「メトリクス」と呼びます)

  • CPUUtilization(CPU使用率)
  • DiskReadOps(ディスク読み取り回数)
  • DiskWriteOps(ディスク書き込み回数)
  • DiskReadBytes(ディスク読み取りバイト数)
  • DiskWriteBytes(ディスク書き込みバイト数)
  • NetworkIn(ネットワーク受信バイト数)
  • NetworkOut(ネットワーク送信バイト数)
  • StatusCheckFailed_Instance(過去1分のインスタンスステータスチェックの成功可否)
  • StatusCheckFailed_System(過去1分のシステムステータスチェックの成功可否)
  • StatusCheckFailed(「StatusCheckFailed_Instance」と「StatusCheckFailed_System」の組み合わせ)

ここまで聞くと、「それじゃあ、気になったときにCloudWatchを確認すればいいじゃないか」と思うかもしれませんが、2つ問題があります。

1つ目は、例えばインスタンスのメモリ使用率を確認しようとしても、CloudWatchのメトリクスとして監視されていないので、メモリ使用率を確認することができません。
2つ目は、CloudWatchで保持できるデータは過去2週間までになっていることです。長期間や過去の実績をAWSマネジメントコンソールから確認することはできません。

それらの問題に対しては、AWSから提供されているCloudWatch用のコマンドラインツール「CloudWatch Command Line Tools」を使うことによって解決します。

「CloudWatch Command Line Tools」では、主に記録されている各メトリクスのデータ取得および、利用者側独自に定義するメトリクス(カスタムメトリクス)のデータを送信(記録)することができます。

1つ目の問題に対しては、監視対象となる環境から何らかの方法でデータを取得し、CloudWatchにカスタムメトリクスとして送信(記録)することで、AWS マネージメントコンソールから送信された独自のカスタムメトリクスのデータを確認することができます。
2つ目の問題に対しては、日次でデータを取得(エクスポート)することで、過去のデータを保管することができます。

今回から3回に渡って、「CloudWatch Command Line Tools」とA-AUTO 50、またOSSのEmbulkを利用して、1月分のメトリクスを自動で収集・レポート作成し、インスタンスタイプの見直しに活用できるようにします。

今回、収集・レポート作成の対象とするメトリクスは以下の7つとしています。
メトリクス 単位 メトリクスの種類
CPU使用率%標準メトリクス
空メモリー容量Mbyteカスタムメトリクス
メモリー使用率%カスタムメトリクス
ロードアベレージカスタムメトリクス
仮想メモリー使用率%カスタムメトリクス
ネットワーク受信バイト数Mbyte標準メトリクス
ネットワーク送信バイト数Mbyte標準メトリクス


全体的の構成図は次のようになります。


バッチとしては5つ(バッチ1のシェル版として、シェル1を用意しています)作成し、各々が決められた周期で実行することで、Excelレポートを自動生成できます。

各バッチの処理は下記のようになっています。
バッチ名 処理内容 実行周期
バッチ1インスタンス内でデータを取得し、カスタムメトリクスとしてCloudWatchへ送信します。1分ごと
シェル1バッチ1のシェル版です。Linuxのカスタムメトリクスを取得・送信したい場合は、こちらを利用します。1分ごと
バッチ2 前日分の標準メトリクス・カスタムメトリクスのデータを取得します。 日次
バッチ3 バッチ2で取得したデータをまとめ、OSSのEmbulkを利用して整形します。 月次
バッチ4 バッチ3で整形されたデータから、レポートとしてExcelのグラフを作成します。 月次

※掲載するバッチファイル内のCloudWatchやEmbulkは2015年5月現在のインターフェースを基に記載しています。提供元でインターフェースが変更されるとことがありますので、最新の情報は各提供元でご確認ください。

今回はバッチ1・シェル1について説明し、バッチ2、3、4については後日のブログにて説明します。



CloudWatch Command Line Tools利用における準備

バッチ1・シェル1ではCloudWatch Command Line Toolsを利用していますが、そのために幾つか準備をする必要があります。

準備①:リソースの利用状況を計測したいインスタンスに、CloudWatch Command Line Toolsをダウンロードする

こちらからCloudWatch Command Line Toolsをダウンロードし、任意のフォルダで解凍します。

準備②:CloudWatchの利用権限を持ったIAMユーザのクレデンシャルファイルを用意する

既存のIAMユーザもしくは新規のIAMユーザを作成し、「CloudWatchFullAccess」権限を付与した後、アクセスキーIDとシークレットキーを以下のように指定し、任意のファイル名で保存してください。(ここで保存したファイルは「クレデンシャルファイル」と呼びます)

クレデンシャルファイル
AWSAccessKeyId=アクセスキーID
AWSSecretKey=シークレットキー

※後述のサンプルバッチでは、"credentials"というファイル名で利用しています。
※IAMユーザの作成方法ならびに権限付与の方法は、インスタンスの起動・停止の自動化のブログの手順「4.1.1 アクセスキーID、シークレットアクセスキーID」の手順を参考にして下さい。

準備③:Javaのインストール

CloudWatch Command Line Toolsは実行にJavaを必要とするため、未インストールであれば、Javaをインストールします。
なお、必要とするJavaのバージョンは1.5以上です。

準備④:CloudWatch Command Line Toolsの動作確認

実際にCloudWatch Command Line Toolsが動作するかを確認します。
まず、コマンドプロンプトから下記の環境変数をセットします。

set AWS_CLOUDWATCH_HOME=CloudWatch Command Line Toolsの配置先パス
set PATH=%PATH%;%AWS_CLOUDWATCH_HOME%\bin
set JAVA_HOME=Javaのホームディレクトリ
set AWS_CREDENTIAL_FILE=準備②で作成したクレデンシャルファイルのパス

その後、下記のコマンドを実行します。

C:\~>mon-version

次のように表示されていれば、CloudWatch Command Line Toolsを利用する準備が完了しています。

指定されたファイルが見つかりません。
指定されたファイルが見つかりません。
Amazon CloudWatch CLI version w.x.y.z (API 2010-08-01)

指定されたファイルが見つかりません。

※「指定されたファイルが見つかりません」と表示されますが、動作には影響ありません。
  コマンドの内部で呼んでいる「echo-helper.cmd」ファイルの日本語対応がされていないため発生する問題です。


バッチ1(カスタムメトリクスデータ送信)


処理概要

下記の4つのカスタムメトリクスに対するその時点のリソース使用状況を取得し、ClodWatchへ送信(記録)します。
  • 空メモリー容量
  • メモリー使用率
  • 仮想メモリー使用率
  • ロードアベレージ

パラメータ

第1パラメータ :リージョンコード
第2パラメータ :インスタンスID

※実行する環境にPoweShell バージョンが3.0以上がインストールされている環境であれば、PoweShellを介してリージョンコードとインスタンスIDを自動取得することができるため、パラメータを省略することができます。

リターンコード

0 : 正常終了しました
4 : パラメータに不正があります
8 : 送信するカスタムメトリクスのデータ取得に失敗しました
12:CloudWatchへのカスタムメトリクスデータ送信(記録)に失敗しました

サンプルバッチ

@echo off

rem input parameters
rem %1 region code
rem %2 instance id

set REGION=%1
set INSTANCEID=%2

rem CloudWatchAPI need environment variables
set AWS_CLOUDWATCH_HOME="C:\CloudWatch-1.0.20.0"
set PATH=%PATH%;%AWS_CLOUDWATCH_HOME%\bin
set JAVA_HOME="C:\Program Files\Java\jre1.8.0_25"
set AWS_CREDENTIAL_FILE="XXXXXXX\credentials"

rem Constants
set RC=999
set LOADAVINT=10
set DATEFORMAT=[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]
set LOADAVTHD=99
set IGNORESTR="指定されたファイルが見つかりません。"

rem start setlocal
setlocal enabledelayedexpansion

rem get metas

rem check REGION and INSTANCEID result set
if "!REGION!"=="" if "!INSTANCEID!"=="" set INSTANCE_AUTO_GET=TRUE
if not "!REGION!"=="" if not "!INSTANCEID!"=="" set INTANCE_INPUT=TRUE

if "!INSTANCE_AUTO_GET!"=="TRUE" (

  rem get REGION and INSTANCEID from own meta data
  for /F "usebackq tokens=1" %%i in (`"powershell -Command Invoke-RestMethod -Uri "http://169.254.169.254/latest/meta-data/instance-id""`) do set INSTANCEID=%%i
  for /F "usebackq tokens=1" %%r in (`"powershell -Command Invoke-RestMethod -Uri "http://169.254.169.254/latest/meta-data/placement/availability-zone""`) do set REGION=%%r

  rem REGION tail a-z delete
  for /F "usebackq" %%c in (`"echo !REGION:~-1,1! | findstr [a-z]"`) do if not "%%c"=="" set REGION=!REGION:~0,-1!
 
  rem check get meta datas result set
  if not "!REGION!"=="" if not "!INSTANCEID!"=="" if not "!REGION!"=="+" if not "!INSTANCEID!"=="+" set GETMETAS=TRUE
  if not "!GETMETAS!"=="TRUE" (
    goto GET_METAS_FAILED
  )
) else if not "!INTANCE_INPUT!"=="TRUE" (
  goto PARAM_INVALID
)

rem set variables
set AWS_CLOUDWATCH_URL=http://monitoring.%REGION%.amazonaws.com
set PADDING_TIME=%time: =0%
set TMP_FILE=%~dp0sendMet_%INSTANCEID%_%DATE:~-10,4%%DATE:~-5,2%%DATE:~-2%%PADDING_TIME:~0,2%%PADDING_TIME:~3,2%%PADDING_TIME:~6,2%%PADDING_TIME:~9,2%.txt


rem get load average

set TYPRF=
for /F "usebackq delims=, tokens=2" %%t in (`"typeperf -sc %LOADAVINT% -si 1 "\System\Processor Queue Length" | findstr /r /C:"%DATEFORMAT%""`) do (
  rem get and add loadaverage
  set TYPRF=%%t
  set TYPRF=!TYPRF:"=!
  rem "
  set TYPRF=!TYPRF:.=!
  set TYPRF=!TYPRF:~0,3!
  set /a LOADAVSUM=!TYPRF!+LOADAVSUM
)

set /a LOADAVTMP=!LOADAVSUM!/%LOADAVINT%

rem LOADAV to decimal
if !LOADAVTMP! GTR %LOADAVTHD% (
  set LOADAV=!LOADAVTMP:~0,-2!.!LOADAVTMP:~-2,2!
) else (
  set LOADAVTMP=000!LOADAVTMP!
  set LOADAV=!LOADAVTMP:~-3,-2!.!LOADAVTMP:~-2,2!
)

rem get memories

set SYSINFO=
for /F "usebackq delims=MB" %%s in (`"systeminfo | findstr "メモリ""`) do (
  set SYSINFO=%%s
  if not "!SYSINFO:物理メモリの合計=!" == "!SYSINFO!" (
    for /F "usebackq tokens=2" %%m in (`echo %%s`) do set ALMEM=%%m
  ) else if not "!SYSINFO:利用できる物理メモリ=!" == "!SYSINFO!" (
    for /F "usebackq tokens=2" %%m in (`echo %%s`) do set AVMEM=%%m
  ) else if not "!SYSINFO:仮想メモリ: 最大サイズ=!" == "!SYSINFO!" (
    for /F "usebackq tokens=3" %%m in (`echo %%s`) do set ALSWMEM=%%m
  ) else if not "!SYSINFO:仮想メモリ: 使用中=!" == "!SYSINFO!" (
    for /F "usebackq tokens=3" %%m in (`echo %%s`) do set USWMEM=%%m
  )
)

rem delete comma
set ALMEM=%ALMEM:,=%
set AVMEM=%AVMEM:,=%
set ALSWMEM=%ALSWMEM:,=%
set USWMEM=%USWMEM:,=%

rem percentage calculation
set /a MEMUSAGE = (ALMEM - AVMEM) * 100 / ALMEM

rem total swap memory check
if "!ALSWMEM!"=="0" (
  set /a SWMEMUSAGE = 0
) else (
  set /a SWMEMUSAGE = USWMEM * 100 / ALSWMEM
)

rem send metrics

call mon-put-data --metric-name AvailableMemory --namespace "A-AUTO Metrics" --dimensions "InstanceId=!INSTANCEID!" --value !AVMEM! --unit "Megabytes" 2> !TMP_FILE!
for /F "usebackq" %%m in (`"findstr /X /V %IGNORESTR% "!TMP_FILE!""`) do (
  call :SEND_METRICS_FAILED AvailableMemory !AVMEM!
)

call mon-put-data --metric-name MemoryUsage --namespace "A-AUTO Metrics" --dimensions "InstanceId=!INSTANCEID!" --value !MEMUSAGE! --unit "Percent" 2> !TMP_FILE!
for /F "usebackq" %%m in (`"findstr /X /V %IGNORESTR% "!TMP_FILE!""`) do (
  call :SEND_METRICS_FAILED MemoryUsage !MEMUSAGE!
)

call mon-put-data --metric-name SwapMemory --namespace "A-AUTO Metrics" --dimensions "InstanceId=!INSTANCEID!" --value !SWMEMUSAGE! --unit "Percent" 2> !TMP_FILE!
for /F "usebackq" %%m in (`"findstr /X /V %IGNORESTR% "!TMP_FILE!""`) do (
  call :SEND_METRICS_FAILED SwapMemory !SWMEMUSAGE!
)

call mon-put-data --metric-name LoadAverage --namespace "A-AUTO Metrics" --dimensions "InstanceId=!INSTANCEID!" --value !LOADAV! --unit "Count" 2> !TMP_FILE!
for /F "usebackq" %%m in (`"findstr /X /V %IGNORESTR% "!TMP_FILE!""`) do (
  call :SEND_METRICS_FAILED LoadAverage !LOADAV!
)

set RC=0
echo %date% %time% AvailableMemory=!AVMEM!, MemoryUsage=!MEMUSAGE!, SwapMemory=!SWMEMUSAGE!, LoadAverage=!LOADAV!
goto END

:PARAM_INVALID
echo %date% %time% parameter invalid.
set RC=4
goto END

:GET_METAS_FAILED
echo %date% %time% get meta datas failed.
set RC=8
goto END

:SEND_METRICS_FAILED
echo %date% %time% %1 send failed. %1=%2.
set RC=12
goto END

:END
if exist !TMP_FILE! del !TMP_FILE!
exit %RC%

バッチ補足


1.環境変数

CloudWatch Command Line Toolsを利用するためには、前述したように環境変数に設定が必要です。
実行する環境に合わせて、サンプルバッチ内の下記の環境変数の書き換えを行ってください。
 •AWS_CLOUDWATCH_HOME :CloudWatch Command Line Toolsの配置先パス
 •JAVA_HOME               :Javaの配置先パス
 •AWS_CREDENTIAL_FILE    :CloudWatchの利用権限を持ったユーザのクレデンシャルファイル

2.メトリクスデータの取得

カスタムメトリクスのデータ取得は、すべてOSで用意されているコマンドを利用しています。
ロードアベレージは"typeperf"コマンド、その他3つのカスタムメトリクスについては"systeminfo"コマンドを用いて取得しています。

【注意】"systeminfo"コマンドが返却する結果は言語に依存して異なります。このため、サンプルバッチは日本語OSで実行する前提の内容となっています。このため日本語OSでない場合は、言語に合わせてバッチファイルの内容を変更する必要があります。

3.実行方法とタイミング

カスタムメトリクスでより正確なデータを収集するためには、短い間隔でバッチ①を実行してください。
※1分間隔で実行するのが良いかと考えます。

一定間隔での自動実行には、A-AUTO 50の"時間間隔起動"を利用して、1分毎に実行させることができます。A-AUTO 50であれば、リモートライセンスの利用で複数のインスタンスをまとめて管理スケジュールすることが可能です。また、送信エラーが発生した場合にエラーを通知させることも可能となります。
タスクスケジューラを利用される場合は、1つのタスクの最低間隔が5分となっているため、タスクを5つ登録して実行する必要があります。

送信したカスタムメトリクスデータの確認

バッチ1(シェル1)で、送信(記録)したカスタムメトリクスデータが正しく記録されているかは、
AWSマネージメントコンソールのCloudWatchを選択し、画面左下のセレクトボックスの「A-AUTO Metrics」を選択することで、4つのカスタムメトリクスを表示・確認できます。




シェル1(カスタムメトリクスデータ送信・Linux用)


処理概要

バッチ1と同じです。

パラメータ

なし(リージョンコード、インスタンスIDはLinuxのコマンドで自動取得します)

リターンコード

0 : 正常終了しました
8 : 送信するカスタムメトリクスのデータ取得に失敗しました
12: CloudWatchへのカスタムメトリクスデータ送信(記録)に失敗しました

サンプルシェル

#!/bin/sh

end() {
  exit $1
}

get_metas_failed() {
  echo `date +"%Y/%m/%d %I:%M:%S"` get meta datas failed.
  end "8"
}

send_metrics_failed() {
  echo `date +"%Y/%m/%d %I:%M:%S"` $1 send failed. $1=$2.
  end "12"
}

INSTANCEID=`curl -s http://169.254.169.254/latest/meta-data/instance-id`
REGION=`curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone | sed -e "s/[a-z]$//g"`

if [ -z "$INSTANCEID" -o -z "$REGION" ]; then
  get_metas_failed
fi

export AWS_CLOUDWATCH_HOME="/opt/CloudWatch-2010-08-01"
export PATH=$PATH:$AWS_CLOUDWATCH_HOME/bin
export AWS_CLOUDWATCH_URL=http://monitoring.$REGION.amazonaws.com
export JAVA_HOME="/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.45-30.b13.el7_1.x86_64/jre"
export AWS_CREDENTIAL_FILE="XXXXXXXXX/credentials"

LOADAV=`uptime | awk '{print $(NF - 1)}' | sed -e "s/,$//g"`
AVMEM=`free -m| grep Mem | awk '{print $4}'`
ALMEM=`free -m| grep Mem | awk '{print $2}'`
AVSWMEM=`free -m| grep Swap | awk '{print $4}'`
ALSWMEM=`free -m| grep Swap | awk '{print $2}'`

MEMUSAGE=`expr \( $ALMEM - $AVMEM \) \* 100 / $ALMEM `
if [ "$ALSWMEM" -eq 0 ]; then
  SWMEMUSAGE=0
else
  SWMEMUSAGE=`expr \( $ALSWMEM - $AVSWMEM \) \* 100 / $ALSWMEM `
fi

mon-put-data --metric-name AvailableMemory --namespace "A-AUTO Metrics" --dimensions "InstanceId=$INSTANCEID" --value $AVMEM --unit "Megabytes"
if [ "$?" -ne 0 ]; then
  send_metrics_failed "AvailableMemory" "$AVMEM"
fi

mon-put-data --metric-name MemoryUsage --namespace "A-AUTO Metrics" --dimensions "InstanceId=$INSTANCEID" --value $MEMUSAGE --unit "Percent"
if [ "$?" -ne 0 ]; then
  send_metrics_failed "MemoryUsage" "$MEMUSAGE"
fi

mon-put-data --metric-name SwapMemory --namespace "A-AUTO Metrics" --dimensions "InstanceId=$INSTANCEID" --value $SWMEMUSAGE --unit "Percent"
if [ "$?" -ne 0 ]; then
  send_metrics_failed "SwapMemory" "$SWMEMUSAGE"
fi

mon-put-data --metric-name LoadAverage --namespace "A-AUTO Metrics" --dimensions "InstanceId=$INSTANCEID" --value $LOADAV --unit "Count"
if [ "$?" -ne 0 ]; then
  send_metrics_failed "LoadAverage" "$LOADAV"
fi

echo `date +"%Y/%m/%d %I:%M:%S"` AvailableMemory=$AVMEM, MemoryUsage=$MEMUSAGE, SwapMemory=$SWMEMUSAGE, LoadAverage=$LOADAV

end "0"


シェル補足


1.環境変数

CloudWatch Command Line Toolsを利用するため、下記の環境変数を実行環境に合わせて書き換えを行ってください。
 •AWS_CLOUDWATCH_HOME :CloudWatch Command Line Toolsの配置先パス
 •JAVA_HOME               :Javaの配置先パス
 •AWS_CREDENTIAL_FILE    :CloudWatchの利用権限を持ったユーザのクレデンシャルファイル

2.メトリクスのデータの取得

カスタムメトリクスのデータ取得は、すべてOSで用意されているコマンドを利用しています。
ロードアベレージは"uptime"コマンド、その他の3つカスタムメトリクスについては"free"コマンドを用いて取得しています。

3.実行方法とタイミング

前述のA-AUTO 50リモートライセンスの繰り返し実行を利用するか、cronで1分間隔で実行して下さい。


次回は今回のバッチ(シェル)で送信したメトリクスをデータ収集・整形します。


0 件のコメント:

コメントを投稿