2015年6月18日木曜日

AWSのインスタンスタイプの変更をスケジューリングしインスタンスタイプを最適化する

こんにちは、井下です。

梅雨入りしたという割には日差しの強い日が多いのですが、昨年も一昨年も同じようなことを感じていたので、梅雨とはそういうものなんだと思うようにしました。

はじめに

以前のブログでインスタンスタイプの最適化を図ろうという内容をご紹介しましたが、今回は"いつ"インスタンスタイプを上げるべきか(=スペックを上げるべきか)が分かっていて、その手順を自動化したい!というケースでの解決方法をご紹介します。

特に、決まったシーズンだけ、手動でインスタンスのスケールアップをしているという方は、有効にご活用いただけます。


構成はインスタンスタイプ変更バッチを用意し、その実行をA-AUTO 50(もしくは他のジョブ管理ツール)で制御する、下記のようにシンプルな構成です。



ここからはインスタンスタイプ変更バッチのサンプルと、A-AUTO 50への登録方法について記載します。



インスタンスタイプ変更バッチ


処理概要

指定したインスタンスのインスタンスタイプを変更します。EC2の仕様上、インスタンスが停止している状態でなければ、インスタンスタイプは変更できないため、インスタンスが起動している場合は一度インスタンスを停止し、インスタンスタイプの変更後に再開させます。
なお、本バッチの役割はインスタンスタイプの変更であるため、実行時にインスタンスが停止していた場合、インスタンスタイプを変更した後、インスタンスを再開させることはありません。

パラメータ

第1パラメータ(必須):リージョンコード
第2パラメータ(必須):インスタンスID
第3パラメータ(必須):※変更したいインスタンスタイプ名

※インスタンスタイプ名は、AWSマネジメントコンソールから確認できる名前を指定します。  例)t2.micro、m3.medium

リターンコード

0 : 正常終了しました
1 : 現在のインスタンスタイプと、パラメータで指定したインスタンスタイプが同じのため、インスタンスタイプの変更処理を行わずに終了しました
4 : 必須パラメータが不足しています
8 : EC2 API Tools実行に必要な環境変数に誤りがある、またはパラメータのリージョンコード、インスタンスIDが不正です
10: インスタンスの停止に失敗しました
12: パラメータのインスタンスタイプが不正です
14: インスタンスタイプの変更に失敗しました
16: インスタンスの再開に失敗しました
18: ElasticIPの割り当てに失敗しました

サンプルバッチ

@echo off
rem input parameters
rem %1 region code
rem %2 instance id
rem %3 instance type

set EC2_HOME=C:\ec2-api-tools-1.7.3.2
set PATH=%PATH%;%EC2_HOME%\bin
set EC2_URL=https://ec2.%1.amazonaws.com
set JAVA_HOME="C:\Program Files\Java\jre1.8.0_25"
set AWS_ACCESS_KEY=XXXXXXXXXXXXXXXXXXXXXXXX
set AWS_SECRET_KEY=XXXXXXXXXXXXXXXXXXXXXXXX
set INSTANCE_ID=%2
set INSTANCE_TYPE=%3

set RC=0
set CHECK_LOOP=3
set START_AND_STOP_SLEEP_SEC=30
set CHANGE_INSTANCE_TYPE_SLEEP_SEC=5

echo ### %date% %time% EC2 instance type change batch started. region=%1,instace id=%2,instance type=%3.

rem パラメータチェック
if not "%1"=="" if not "%2"=="" if not "%3"=="" set PARAMS_COUNT=TRUE
if not "%PARAMS_COUNT%"=="TRUE" goto PARAM_INVALID

rem インスタンスタイプの確認
call :CHECK_INSTANCE_TYPE

rem 既に変更しようとしたインスタンスタイプの場合
if "%CHECKED_INTSTANCE_TYPE%"=="0" goto ALREADY_SELECETED_INSTANCE_TYPE

rem インスタンスが起動していれば、INSTANCE_RUNNING_FLAGにTRUEをセット
for /F "usebackq tokens=1" %%c in (`call ec2-describe-instances %INSTANCE_ID% --filter "instance-state-name=running"`) do if not "%%c"=="" set INSTANCE_RUNNING_FLAG=TRUE

rem インスタンスタイプが起動している場合
if "%INSTANCE_RUNNING_FLAG%"=="TRUE" (
  echo ### %date% %time% instance is running. will stop.

  rem 現状のIPアドレスの取得
  for /F "usebackq tokens=2" %%e in (`call ec2-describe-addresses --filter "instance-id=%INSTANCE_ID%"`) do set ELASTIC_IP=%%e

  rem インスタンスの停止
  call :STOP
) else (
  echo ### %date% %time% instance is not running.
)

rem インスタンスタイプの変更
call ec2-modify-instance-attribute %INSTANCE_ID% --instance-type %INSTANCE_TYPE%

if not "%ERRORLEVEL%"=="0" goto INSTANCE_TYPE_INVALID

sleep %CHANGE_INSTANCE_TYPE_SLEEP_SEC%

rem インスタンスタイプの変更確認
call :CHECK_INSTANCE_TYPE
if "%CHECKED_INTSTANCE_TYPE%"=="1" goto INSTANCE_TYPE_CHANGE_FAILED

echo ### %date% %time% instance type change successful.

rem インスタンスタイプの変更前が起動中だった場合
if "%INSTANCE_RUNNING_FLAG%"=="TRUE" (
  echo ### %date% %time% will restart instance.

  rem インスタンスの再開
  call :RESTART

  rem 元々ElasticIPが割り当てられていた場合、再度割り当てる
  if not "%ELASTIC_IP%"=="" call :ASSOCIATE_ADDRESS
) else (
  echo ### %date% %time% skip instance restart process, because instance was not running.
)

goto END


rem インスタンスの停止処理
:STOP
call ec2-stop-instances %INSTANCE_ID%

for /L %%L in (1,1,%CHECK_LOOP%) do (
  echo ### %date% %time% instance status check count=%%L.

  rem A-AUTO 50で提供しているsleepモジュールを使用。秒数を指定して実行する
  sleep %START_AND_STOP_SLEEP_SEC%

  for /F "usebackq tokens=1" %%s in (`ec2-describe-instances %INSTANCE_ID% --filter "instance-state-name=stopped"`) do if not "%%s"=="" (
    goto :EOF
  )

  echo ### %date% %time% instance status was not stopped.
)

goto STOP_FAILED

rem インスタンスの再開処理
:RESTART
call ec2-start-instances %INSTANCE_ID%

for /L %%L in (1,1,%CHECK_LOOP%) do (
  echo ### %date% %time% instance status check count=%%L.
  rem A-AUTO 50で提供しているsleepモジュールを使用。秒数を指定して実行する
  sleep %START_AND_STOP_SLEEP_SEC%

  for /F "usebackq tokens=1" %%r in (`call ec2-describe-instances %INSTANCE_ID% --filter "instance-state-name=running"`) do if not "%%r"=="" (
    goto :EOF
  )

  echo ### %date% %time% instance status was not running.
)

goto RESTART_FAILED

rem インスタンスタイプの確認処理
:CHECK_INSTANCE_TYPE
setlocal
for /F "usebackq tokens=3" %%i in (`call ec2-describe-instance-attribute %INSTANCE_ID% --instance-type`) do (
  if "%%i"=="%INSTANCE_TYPE%" (
  rem 引数のインスタンスタイプと現在のインスタンスタイプが合致した場合、CHECKED_INTSTANCE_TYPEに0をセットする
  endlocal & set CHECKED_INTSTANCE_TYPE=0
  ) else (
  rem 引数のインスタンスタイプと現在のインスタンスタイプが合致しなかった場合、CHECKED_INTSTANCE_TYPEに1をセットする
  endlocal & set CHECKED_INTSTANCE_TYPE=1
  )
)

rem ec2-describe-instance-attributeでエラーが発生した場合
if "%CHECKED_INTSTANCE_TYPE%"=="" (
  goto ACCESS_ERROR
)

goto :EOF

rem ElasticIPの割り当て処理
:ASSOCIATE_ADDRESS
echo ### %date% %time% associate Elastic IP
call ec2-associate-address -i %INSTANCE_ID% %ELASTIC_IP%

if not "%ERRORLEVEL%"=="0" (
  goto ASSOCIATE_ADDRESS_FAILED
)

goto :EOF

:ALREADY_SELECETED_INSTANCE_TYPE
echo ### %date% %time% stopped change process because Instance type was the same.
set RC=1
goto END

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

:ACCESS_ERROR
echo ### %date% %time% instance access error.
set RC=8
goto END

:STOP_FAILED
echo ### %date% %time% instance stop failed.
set RC=10
goto END

:INSTANCE_TYPE_INVALID
echo ### %date% %time% instance type invalid.
set RC=12
goto END

:INSTANCE_TYPE_CHANGE_FAILED
echo ### %date% %time% instance type change failed.
set RC=14
goto END

:RESTART_FAILED
echo ### %date% %time% instance restart failed.
set RC=16
goto END

:ASSOCIATE_ADDRESS_FAILED
echo ### %date% %time% associate Elastic IP failed.
set RC=18
goto END

:END
echo ### %date% %time% EC2 instance type change batch ended(RC=%RC%).
exit %RC%

バッチ補足


1.環境変数

本バッチでは、EC2 API Toolsを利用するため、サンプルバッチ内で赤字になっている6つの環境変数を、実行環境に合わせて設定します。
設定方法については、以前のブログ(4.2章)に記載しているので、そちらをご参照ください。

2.制約事項

本バッチはEC2の仕様上、下記の制約があります。実行前に制約を満たしているかをご確認ください。
(2015年6月時点での仕様のため、それ以降の仕様変更によって制約も変化することをご了承ください)
  • インスタンスタイプの変更方法は、インスタンスのルートデバイスタイプによって異なります。本バッチはルートデバイスタイプが"Amazon EBS ボリューム"の場合の方法を採っているため、それ以外のルートデバイスタイプの動作は保証しません
    • AWSマネジメントコンソールで、インスタンスの"Root device type"が"ebs"となっていれば、ルートデバイスタイプが"Amazon EBS ボリューム"であることを確認できます
  • 指定できるインスタンスタイプは、インスタンスのOSによって異なります。本バッチで指定・変更できるインスタンスタイプも、前述の仕様に準じたものです。
    • 指定できるインスタンスタイプは、AWSマネジメントコンソールから確認できる。

A-AUTO 50への登録方法

A-AUTO 50でインスタンスタイプ変更バッチの実行をスケジューリングする手順について説明します。
なお、以下のような利用想定ケースで登録を行うものとします。

  • 平時のインスタンスタイプは"m3.medium"で十分
  • 11月1日からはインスタンスタイプを"m3.large"にしたい
  • 1月の最終日にはインスタンスタイプを"m3.medium"に戻したい
※毎月決まった日(期間)にインスタンスタイプを変更する等他のスケジューリングも可能です

この場合、"11月1日にインスタンスタイプをm3.largeにするネットワークスケジュール"(いつ・何をやるかの組合せ)と、"1月の最終日にインスタンスタイプをm3.mediumにするネットワークスケジュール"を作成します。

ネットワークスケジュールを作成するために、スケジュール情報とジョブネットワーク情報を作成し、それらを関連付けます。

1.スケジュール情報の登録

A-AUTO 50により、いつ実行するかを定義します。
"11月1日"に実行するスケジュールは、下記のように登録してください。
  • スケジュール
    • スケジュールID:任意の入力が可能ですが、インスタンスタイプ変更バッチの実行スケジュールだと分かるような名前にすることをお勧めします。
    • ホリデーID:定義済みのホリデーID「OPSHOLT2」を選択します。なお、いつが休日にあたるかは指定したホリデーIDによって定義されています。会社固有の休日や、祝日などを考慮したい場合、こちらの9章を参考にしてカレンダーを作成してください。(OPSHOLT2は土曜日と日曜日を休日としています)
    • 処理パターン:「カレンダー」を選択します
    • 処理サイクル:「毎年」を選択します
    • シフトパターン:「当日」を選択します。これでジョブの実行日が休日であろうと、ジョブを実行します。
  • 標準処理日
    • 標準処理日:「2015年11月1日」を指定します




"1月の最終日"に実行するスケジュールは、下記のように登録してください。
  • スケジュール
    • スケジュールID:任意の入力が可能ですが、インスタンスタイプ変更バッチの実行スケジュールだと分かるような名前にすることをお勧めします。
    • ホリデーID:定義済みのホリデーID「OPSHOLT2」を選択します。なお、いつが休日にあたるかは指定したホリデーIDによって定義されています。会社固有の休日や、祝日などを考慮したい場合、こちらの9章を参考にしてカレンダーを作成してください。(OPSHOLT2は土曜日と日曜日を休日としています)
    • 処理パターン:「カレンダー」を選択します
    • 処理サイクル:「毎年」を選択します
    • シフトパターン:「当日」を選択します。これでジョブの実行日が休日であろうと、ジョブを実行します。
  • 標準処理日
    • 標準処理日:「2016年1月最終日」を指定します



2.ジョブネットワーク情報の登録

A-AUTO 50により、何のジョブを実行するのかを定義します。
インスタンスタイプ変更バッチでインスタンスタイプを"m3.large"にするジョブネットワークは、下記のように登録してください。
  • ネットワーク属性1
    • ネットワークID:任意の入力が可能ですが、インスタンスタイプ変更バッチを実行することが分かるような名前にすることをお勧めします。
    • スタート時刻:インスタンスタイプ変更バッチを実行する時刻を指定します。


  • ジョブ
    • ジョブ番号:001~999の任意の入力が可能ですが、インスタンスタイプ変更バッチのみを実行する場合は特に数字を気にする必要はありません。(複数のジョブ実行する場合の実行順序の設定です)
    • ジョブコード:インスタンスタイプ変更バッチのジョブ名を指定します。なお、8桁まで入力が可能です。例として、インスタンスタイプ変更バッチを「EC2ITCHN.bat」とした場合、「EC2ITCHN」と入力します。
    • マックスリターンコード:「2」を指定します。ここで指定した数値未満の値であれば、正常終了と見なします。
    • ノーマル実行時のジョブ引渡しパラメータ:インスタンスタイプ変更バッチの実行時のパラメータを指定します。1つ目のパラメータ、2つ目のパラメータはインスタンスの情報を入力し、3つ目のパラメータに"m3.large"を入力します。
    • リラン実行時のパラメータ :ノーマル実行時の引渡しパラメータと同様の入力をしてください。こちらは異常終了時からの再実行時の入力パラメータとなります。

なお、インスタンスタイプ変更バッチでインスタンスタイプを"m3.large"にするジョブネットワークは、"ジョブ"の"ノーマル実行時のジョブ引渡しパラメータ"および"リラン実行時のパラメータ"の3つ目のパラメータを"m3.large"にすることで登録できます。

3.ネットワークスケジュール情報の登録

前述の手順で作成したスケジュールとジョブネットワークを関連付けます。
ネットワークスケジュール画面から、ネットワークに関連付けたいスケジュールをそれぞれ選択します。




以上で、A-AUTO 50を利用してAWSのインスタンスタイプの変更を自動化することができました。



0 件のコメント:

コメントを投稿