Amazon EC2 のスポットインスタンスを AWS Cloud9 から使用する

# 目的

* AWS Cloud9 (以降 Cloud9 とする) を使うためにかかる費用をとにかく安くする！！！
* 初回の環境構築手順は多少複雑でもよい。
* 環境構築後の日々の Cloud9 起動手順はできるかぎり簡単にする。


# Cloud9 の使用構成

どれだけ費用を削減できるのか比較するために、今回作成するオレオレ構成とは別に基本構成を考える。

* 基本構成
    * Environment Type を EC2 にして出来る限りデフォルト設定で作成した構成。
* オレオレ構成。
    * 費用が安くなるように工夫した構成。

## 基本構成

Environment Type を EC2 にして出来る限りデフォルト設定で作成した構成。

![cloud9-Page-3 (1).png](https://qiita-image-store.s3.amazonaws.com/0/219274/f7c82207-f718-2645-1026-51f68eb9c70f.png)

* 概要
    * t2.micro のオンデマンドインスタンス。
    * ルートデバイス用 EBS 8G。
    * AMI は 2019 年 3 月 28 日時点では Cloud9Default-2019-02-18T10-14 (ami-0f5f5c726eabf14a2) で任意変更はできない。
* メリット
    * 環境構築が楽。
    * 自動停止によってインスタンス停止を忘れても安心。
* デメリット
    * オンデマンドインスタンスなので高い。
    * ルートデバイス用の EBS 8G を維持し続ける必要があり、月あたり約１ドルの固定費が必要。
    * AMI を変更できない。 
    * 作成した EC2 はほぼ Cloud9 専用。

## オレオレ構成

費用が安くなるように工夫した構成。

![cloud9-Page-1.png](https://qiita-image-store.s3.amazonaws.com/0/219274/773ad43e-e682-f57b-4822-29f9c9d45818.png)

* 概要
    * EC2 のリージョン。
        * Cloud9 以外の目的での使用も考慮して EC2 は東京リージョンで運用。
    * EC2 のアベイラビリティーゾーン。
        * インスタンスと EBS のアベイラビリティーゾーンは同じにする必要あるので、スポットインスタンス作成時は要注意。
    * t2.micro のスポットインスタンス。
        * 基本構成との比較用に同じ t2.micro としている。
    * ルートデバイス用 EBS 8G。
        * スポットインスタンスを停止する際は削除する。
    * データ用 EBS 1G。
        * スポットインスタンスの停止に関係なく維持する。
    * 独自ドメイン。
        * 他目的で使用している独自ドメインを流用。
    * Route53
        * 他目的で使用している独自ドメインの管理で使用しているのを流用。
* メリット
    * EC2 を好きに用意できる。
* デメリット
    * 自動停止がない。
    * 日々の Cloud9 起動手順にスポットインスタンス作成という手順が増えている。


# 費用比較

## 費用単価

簡易化のために１ヶ月は 750 時間で計算する。

|項目|単価|Type=EC2構成|オレオレ構成|
|:-:|:-:|:-:|:-:|
|t2.micro (On-Demand)|0.0152 [USD/1H]|利用時間|─|
|t2.micro (Spot)|0.0046 [USD/1H]|─|利用時間|
|EBS 8G (root device)|0.00128 [USD/1H]|750時間|利用時間|
|EBS 1G (data)|0.00016 [USD/1H]|─|750時間|
|独自ドメイン|1.000 [USD/1M]|─|750時間|
|Route53 Hosted Zone|0.500 [USD/1M]|─|750時間|


## 利用時間に対する費用

![費用グラフ.png](https://qiita-image-store.s3.amazonaws.com/0/219274/49bad0c8-f4b3-8491-3123-c2e8a9246c71.png)

オレオレ1：独自ドメインと Route53 の費用を含めた場合。
オレオレ2：独自ドメインと Route53 の費用を含めない場合。

私個人としては独自ドメインおよび Route53 は別目的で運用しているものを流用するので、Cloud9 を使うためにかかる費用としては `オレオレ2` が該当。


# オレオレ構成の構築手順

## 初回の環境構築

![cloud9-Page-2.png](https://qiita-image-store.s3.amazonaws.com/0/219274/2a0ba9e9-3db7-690e-49d8-2692cd61bfd2.png)

* [EC2] データ用EBSを作成。
* [EC2] 初期設定用インスタンスを作成。スポットでもオンデマンドでもどちらでもいい。
* [EC2] データ用EBSを初期設定用インスタンスにアタッチ。
* [EC2] 初期設定用インスタンスにSSHログインしてマウント先ディレクトリ(例：`/workspace/home`)を作成。
* [EC2] `/workspace/home`にデータ用EBSをマウント。
* [EC2] ホームディレクトリがデータ用EBSになるようにCloud9作業用ユーザーを追加する。
    * `useradd -u 1003 -d /workspace/home/satoshi satoshi`
    * `usermod -a -G ec2-user satoshi`
    * `echo "satoshi ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/satoshi`
* [Route53] 初期設定用EC2のIPアドレスに対するAレコードを作成する。ホスト名は任意。
* [Cloud9] 設定を作成する。「Next Step」による完了はまだ。
    * UserはCloud9作業用ユーザーの名前を指定。
    * HostはRoute53に作成したAレコードのホスト名。
    * Environment pathはデータ用EBSのディレクトリになるように指定。例：`/workspace/home/satoshi/environment`
* [EC2] Cloud9の設定作成時に表示される`View public SSH key`をコピーして`/workspace/home/satoshi/.ssh/authorized_keys`に追記する。
* [Cloud9] 「Next Step」で進めていくとc9がインストールされていないからインストールしろみたいになるので、画面に従って進めていく。
* 以上で初期設定は完了。初期設定用インスタンスは削除してしまっていい。

※思い出しながら書いたので手順に抜けがあるかもしれない。

## スポットインスタンスの起動

スポットインスタンス作成時にユーザーデータを指定して色々と行う。

* データ用EBSが使用可能になるのを待つ。
* データ用EBSをアタッチ。
* データ用EBSのアタッチが完了してデバイスファイルが作成されるのを待つ。
* データ用EBSをマウント。
* マウントしたディレクトリを指定してCloud9の作業用ユーザーを追加。
* 作業用ユーザーのsudoを設定。
* Route53のレコードセットを更新。

```bash
#!/bin/bash
set -x
export AWS_DEFAULT_REGION=ap-northeast-1
VOLUME_ID=vol-xxxxxxxxxxxxxxxxx
ZONE_ID=ZXXXXXXXXXXXXX
RECODESET_NAME=c9.example.com.
IP=$(curl -s 169.254.169.254/latest/meta-data/public-ipv4)
INSTANCE_ID=$(curl -s 169.254.169.254/latest/meta-data/instance-id)
while :; do
    aws ec2 wait volume-available --volume-ids $VOLUME_ID && break
done;
aws ec2 attach-volume --volume-id $VOLUME_ID --instance-id $INSTANCE_ID --device /dev/xvdf
aws ec2 wait volume-in-use --volume-ids $VOLUME_ID
until [ -e /dev/xvdf ]; do
    sleep 1
done
mkdir -p /workspace/home
mount /dev/xvdf /workspace/home
#curl -sL https://rpm.nodesource.com/setup_10.x | bash -
#yum install -y gcc-c++ make
#yum install -y nodejs
#amazon-linux-extras install -y docker
useradd -u 1003 -d /workspace/home/satoshi satoshi
usermod -a -G ec2-user satoshi
echo "satoshi ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/satoshi
cat << _EOB_ > /tmp/changeset.json
{
    "Changes": [
        {
            "Action": "UPSERT",
            "ResourceRecordSet": {
                "Name": "${RECODESET_NAME}",
                "ResourceRecords": [
                    {
                        "Value": "${IP}"
                    }
                ],
                "TTL": 60,
                "Type": "A"
            }
        }
    ]
}
_EOB_
aws route53 change-resource-record-sets --hosted-zone-id $ZONE_ID --change-batch file:///tmp/changeset.json
ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
```

# 注意

力尽きた・・・。
思い出しながら書いてるため細かい情報が抜け落ちていそう。
Cloud9が東京リージョンにきたし、そのうち整理しなおして投稿しなおす。