EC2 のスポット価格の 平均値, 標準偏差, 最大値を AWS-CLI を使って計算して、スポットで起動するかを判断しよう！



背景
-------

　最近,東京リージョンのc3.8xlargeのスポット価格の変動が激しいことご存知ですか？下記の画像は2015年４月27日にスクショした、c3.8xlarge の Pricing History ですが、このように大きく価格が変動しています。そのため、スポットを使うべきか、オンデマンドを使うべきか、他のインスタンスを使うべきかなど悩むことが多くなりました。

![Kobito.tZHil1.png](https://qiita-image-store.s3.amazonaws.com/0/73207/1ea45b5a-a1e7-d9fe-e9da-21988315827f.png "Kobito.tZHil1.png")


課題
--------

　スポットインスタンスが使えそうか、使えなさそうか、一瞬で判断したいのですが、マネージメントコンソール上からだと、スポット価格を見るまでの操作が面倒です。またc3.8xlarge と c4.8xlargeなど異なるインスタンスタイプで比較することができないといった問題がありました。一方、 AWS CLIを使って 'aws ec2 describe-spot-price-history' コマンドをたたいても、JSONが大量にでるで〜るでツライだけでした。

解決策
----------

　パッと一目で、「このインスタンスはスポットで使えねー」など判断したいということで、discribeコマンドで得られるJSONをフィルタリングし、スポット価格の
　
- 平均
- 標準偏差
- 最大値
- 最小値

を計算するスクリプトを作ることにします！

- 価格のバラツキは標準偏差を見ればよいですね！
- スポットインスタンスのリクエスト時に設定する Maximum price の値は最大値を見ればよいですね！



シェルスクリプトで実装してみた
-----------------------------


　世界中のc4,c3.8xlargeのスポット価格を比較できると面白いかも・・ということで、全体の流れとしては下記のようにしました。
　
1. `aws ec2 describe-regions` でリージョン取得
2. `AWS_DEFAULT_REGION`の設定
3. `aws ec2 describe-availability-zones` で Availability Zone を取得
4. `aws ec2 describe-spot-price-history` で Pricing History の JSON データを取得
5. jq で データをフィルタリング後、awkで統計処理

　これで、世界中の Region と Availability Zone を自動で取得し順次出力することができます。尚、Pricing History の期間は、現時点から１週間前までのデータを処理しています。

以下実装したシェルスクリプトです。Macから動作確認しています。ちなみに、GNUのdateコマンドは、オプションが異なりますので、適宜読み替えてください。

```bash:script
#!/bin/bash

#
# INITIAL CONDITION
#

INSTANCE="c4.8xlarge c3.8xlarge"
DAYS=7
START=`date -u -v-${DAYS}d +"%Y-%m-%dT%H:%M:%S"`
END=`date -u +"%Y-%m-%dT%H:%M:%S"`


#
# SOLVER
#

MY_SOLVER()
{
MY_INS=$1
MY_AZ=$2
MY_START=$3
MY_END=$4

aws ec2 describe-spot-price-history \
--instance-types ${MY_INS} \
--product-description "Linux/UNIX (Amazon VPC)" \
--start-time ${MY_START} \
--end-time ${MY_END} \
--availability-zone ${MY_AZ} \
| jq -r '.SpotPriceHistory[] | "\(.Timestamp)\t\(.SpotPrice)"' | \
sed s/.000Z//g | CALC ${MY_INS} ${MY_AZ} 
}

CALC()
{
awk -v instance=$1 -v az=$2 '
    BEGIN{
        i     = 0;
        sum   = 0;
    }
    {
        price[i] = $2;
        sum += $2;
        i++;
    } 
    END{
        imax = i;
		if (imax != 0) {
        	ave   = sum / imax;
            sumdx = 0;
        	max   = ave;
        	min   = ave;

        	for (i = 0; i < imax; i++) {
        	    val = price[i];
        	    dx  = val - ave;
        	    dx2 = dx * dx;
        	    sumdx += dx2;
        	    if (val > max) max = val;
        	    if (val < min) min = val;
        	}
        	sigma = sqrt(sumdx / imax);
        	printf("%16s%20s%10.3f%10.3f%10.3f%10.3f\n",
        	           instance, az, ave, sigma, max, min);
		} else {
			printf("%16s%20s%40s\n",
                       instance, az, "--- NO DATA ---");
		}
    }'
}

#
# AWS CLI DESCRIBE REGIONS AND AVAILABILITY-ZONE 
#

MY_REGION()
{
aws ec2 describe-regions | jq -r ".Regions[] | .RegionName"
}

MY_AVAILABILITY_ZONE()
{
aws ec2 describe-availability-zones --region $1 \
    | jq -r ".AvailabilityZones[] | .ZoneName"
}

#
# MAIN
#

printf "%16s%20s%10s%10s%10s%10s\n" "InstanceType" "AZ" "average" "stdev" "max" "min"
for TMP_INS in `echo "${INSTANCE}"`
do
	for REGION in `MY_REGION`
	do
		export AWS_DEFAULT_REGION=${REGION}
		for TMP_AZ in `MY_AVAILABILITY_ZONE ${REGION}`
		do
			MY_SOLVER ${TMP_INS} ${TMP_AZ} ${START} ${END}
		done
	done
done
```


世界の c4.8xlarge, c3.8xlarge スポット価格を比較してみた
-----------------------------------------------


先ほどのスクリプトを実際に動かしてみました！

### 動作確認を実施した環境

　今回、動作確認したバージョンです。オイラの環境だと最新の1.7.24において`describe-spot-price-history` は動作しませんでした。

```bash:command
aws --version
```

```bash:result
aws-cli/1.7.1 Python/2.7.6 Darwin/14.1.0
```


### 実行結果

　2015年4月27日の実行結果を以下に示します。データは左から、インスタンスタイプ、 AZ(Availability Zone), Pricing Historyの平均、標準偏差、最大、最小値となっています。データを取得期間は4月27日から７日前までの 1 Week です。
　東京リージョンの c3.8xlarge の Pricing History は冒頭で紹介しましたが、データからも ap-northeast-1c の価格変動の大きさが読み取れます。 平均 : 0.879, 標準偏差 : 1.668, 最大値 : 10.000 と なっており、同じ AZ の c4.8xlarge と比較すると、標準偏差は16倍大きく、最大値に限ってはおよそ30倍です。これではスポットで使えない（泣）
　
　なぜか世界中で、c4のほうがc3より空いており、スポットで使うならc4のほうがオススメということもわかりました。c4のほうがオンデマンド価格が高いからですかね？c4.8xlargeはhaswellアーキテクチャでメモリはDDR4採用で帯域幅広くなっているし、c3.8xlargeとの価格差以上の性能でると思うんですけど・・・なぜだ！


```bash:result
    InstanceType                  AZ   average     stdev       max       min
      c4.8xlarge       eu-central-1a     0.340     0.007     0.362     0.326
      c4.8xlarge       eu-central-1b     0.324     0.005     0.350     0.321
      c4.8xlarge          sa-east-1a                         --- NO DATA ---
      c4.8xlarge          sa-east-1b                         --- NO DATA ---
      c4.8xlarge          sa-east-1c                         --- NO DATA ---
      c4.8xlarge     ap-northeast-1a     0.324     0.002     0.337     0.320
      c4.8xlarge     ap-northeast-1c     0.323     0.001     0.333     0.321
      c4.8xlarge          eu-west-1a     0.363     0.159     2.112     0.324
      c4.8xlarge          eu-west-1b     0.368     0.219     2.112     0.325
      c4.8xlarge          eu-west-1c     0.742     0.729     3.000     0.340
      c4.8xlarge          us-east-1a     0.424     0.840     7.424     0.258
      c4.8xlarge          us-east-1b     0.311     0.350     7.424     0.258
      c4.8xlarge          us-east-1d     0.262     0.004     0.350     0.259
      c4.8xlarge          us-east-1e     0.336     0.048     1.000     0.280
      c4.8xlarge          us-west-1a     0.306     0.218     2.208     0.257
      c4.8xlarge          us-west-1c     0.329     0.229     3.000     0.265
      c4.8xlarge          us-west-2a     0.352     0.306     1.856     0.257
      c4.8xlarge          us-west-2b     0.480     0.485     3.100     0.258
      c4.8xlarge          us-west-2c     0.563     0.492     2.100     0.263
      c4.8xlarge     ap-southeast-2a     0.336     0.017     0.400     0.320
      c4.8xlarge     ap-southeast-2b     0.537     0.474     2.432     0.320
      c4.8xlarge     ap-southeast-1a     0.388     0.162     2.432     0.327
      c4.8xlarge     ap-southeast-1b     0.426     0.214     2.432     0.343
      c3.8xlarge       eu-central-1a     0.640     0.605     2.732     0.335
      c3.8xlarge       eu-central-1b     2.732     0.000     2.732     2.732
      c3.8xlarge          sa-east-1a     0.323     0.004     0.346     0.320
      c3.8xlarge          sa-east-1b     0.320     0.000     0.320     0.320
      c3.8xlarge          sa-east-1c     0.340     0.019     0.399     0.321
      c3.8xlarge     ap-northeast-1a     0.677     0.891     9.600     0.344
      c3.8xlarge     ap-northeast-1c     0.879     1.668    10.000     0.327
      c3.8xlarge          eu-west-1a     0.354     0.491     8.000     0.320
      c3.8xlarge          eu-west-1b     0.333     0.210     4.000     0.320
      c3.8xlarge          eu-west-1c     0.332     0.239     9.600     0.320
      c3.8xlarge          us-east-1a     0.277     0.354     9.600     0.256
      c3.8xlarge          us-east-1b     0.358     0.562     5.500     0.256
      c3.8xlarge          us-east-1d     0.273     0.149     3.000     0.256
      c3.8xlarge          us-east-1e     0.343     0.405     9.600     0.257
      c3.8xlarge          us-west-1a     0.751     0.773     2.732     0.411
      c3.8xlarge          us-west-1c     9.600     0.000     9.600     9.600
      c3.8xlarge          us-west-2a     0.576     1.151    15.000     0.359
      c3.8xlarge          us-west-2b     1.256     2.313     9.600     0.346
      c3.8xlarge          us-west-2c     1.403     2.817    10.000     0.272
      c3.8xlarge     ap-southeast-2a     0.421     0.354     4.000     0.324
      c3.8xlarge     ap-southeast-2b     0.506     0.898     9.600     0.321
      c3.8xlarge     ap-southeast-1a     0.362     0.032     0.504     0.322
      c3.8xlarge     ap-southeast-1b     0.436     0.198     3.024     0.328
```



### 半端ない us-west-1a の c3.8xlarge Pricing History 

　あまりにも目立つ"us-west-1a"のc3.8xlarge、平均 : 9.6, 標準偏差 : 0.0, 最大値 : 9.6 ... つまり、$9.6 で一定ということでハンパないっす。マネージメントコンソールからチェックしてみても予想通りっ！この感じだと当分スポットでは使えそうにありません(泣)

![Kobito.UC1eLD.png](https://qiita-image-store.s3.amazonaws.com/0/73207/940bd10f-96d0-dcde-1247-ff54f98d5a0d.png "Kobito.UC1eLD.png")




スクリプトのカスタマイズ
-----------------------

### 確認したいインスタンスタイプの変更するとき

確認したいインスタンスタイプにcc2.8xlargeを追加する場合は下記のように変更してください。

```bash:before
INSTANCE="c4.8xlarge c3.8xlarge"
```

```bash:after
INSTANCE="c4.8xlarge c3.8xlarge cc2.8xlarge"
```

### 確認したいリージョンを限定するとき

Regionを us-east-1 と ap-northeast の2つに限定したい場合は下記のようにしてください

```bash:before
MY_REGION()
{
aws ec2 describe-regions | jq -r ".Regions[] | .RegionName"
}
```

```bash:after
MY_REGION()
{
echo "us-east-1 ap-northeast-1"
}
```


今後やりたいこと
===========

下記、時間があればやるかも・・

- ここ1週間で、設定した閾値を何回超えるかみたいなアルゴリズムもいれたいです。これはポアソン分布なのかな？こちらのほうが、もっと定量的な予測ができて、まわりを説得する材料なんかになると思います
- AMLに突っ込んで、スポット価格を予測はやってみたいですね。これ誰かやってくれないかなぁ〜