JSON形式ファイルでのEC2インスタンス起動


同じ役割の複数台のEC2インスタンスを構成する際、AMIを独自に作ることがあると思う。
それをJSON形式の定義ファイルを用いて実現させてみる。

### シナリオ
1. Amazon Linux AMI からEC2インスタンスA起動
2. 共通設定とか投入したEC2インスタンスAから元となるAMI(BaseAMI)を作成
3. BaseAMIから新たにEC2インスタンスBを起動
4. EC2インスタンスBに設定投入

このうち、1〜3までをやってみた。4とかはChefとかでやると思うので割愛。


### 1. Amazon Linux AMI からEC2インスタンスA起動
#### 雛形出力
最近のAWS CLIのVer(1.6とか1.7以降?)では、JSON形式出力用のオプション `--generate-cli-skeleton` がほぼ全てのコマンドについてるのでそれを利用する。

```bash
$ aws ec2 run-instances --generate-cli-skeleton > run-instances_base.json
```

#### JSONファイル修正
出力された雛形に対して必要な設定を追加していく。
最小限の設定として、以下を設定。IPアドレスの他、EBSを3つ付けてみた。
SecurityGroupは名称だと上手くいかないのでIDで設定。

* ImageId : ami-4985b048 [Amazon Linux AMI 2014.09.1 (HVM)]
* KeyName : 自分ので
* InstanceType : t2.micro
* AvailabilityZone : 好きなAZで
* SecurityGroupIds : アクセスできるSGで
* SubnetId : 好きなSubnetで
* PrivateIpAddress : 好きなIPで
* BlockDeviceMappings : 3つ付けてみた

```json:run-instances_base.json
{
    "ImageId": "ami-4985b048",
    "KeyName": "YOUR_KEY_NAME",
    "InstanceType": "t2.micro",
    "Placement": {
        "AvailabilityZone": "ap-northeast-1X"
    },
    "SecurityGroupIds": [
        "sg-XXXXXXXX"
    ],
    "SubnetId": "subnet-XXXXXXXX",
    "PrivateIpAddress": "10.X.X.X",
    "BlockDeviceMappings": [
        {
            "DeviceName": "/dev/xvda",
            "Ebs": {
                "VolumeSize": 30
            }
        },
        {
            "DeviceName": "/dev/xvdba",
            "Ebs": {
                "VolumeSize": 10
            }
        },
        {
            "DeviceName": "/dev/xvdbb",
            "Ebs": {
                "VolumeSize": 10
            }
        }
    ]
}
```

#### userdata作成
EC2インスタンス起動時にホスト名を設定させたい、という場合にはuserdataと言う名のシェルスクリプトを実行させることができる。
16KB制限らしいが、そこまで長大なものは書かないので大丈夫かと。
今回はホスト名の設定及びhosts追記と、2つのEBSをLVMで束ねるスクリプトにしてみた。

```bash:userdata_base.sh
#!/bin/bash
hostname test-instance-a
sed -i -e "s/HOSTNAME=\(.*\)/HOSTNAME=test-instance-a/" /etc/sysconfig/network
echo "10.X.X.X    test-instance-a" >> /etc/hosts
/sbin/pvcreate /dev/sdb[ab]
/sbin/vgcreate /dev/vgtest /dev/sdb[ab]
/sbin/lvcreate -l 100%FREE -n lvtest vgtest
/sbin/mkfs.ext4 -j /dev/vgtest/lvtest
/bin/mkdir -p /export/test
/bin/mount -t ext4 /dev/vgtest/lvtest /export/test
echo "/dev/vgtest/lvtest /export/test ext4 defaults 0 0" >> /etc/fstab
```

#### EC2インスタンスA起動
作成したJSONファイルとuserdataを引数に指定する。`file://` というスキーマが必要。
実行に成功すると、結果ばぶわぁーっとJSON形式で出力されるので、変数に入れておく。後々使えるので。

```bash
$ RET=$(aws ec2 run-instances --cli-input-json file://run-instances_base.json --user-data file://userdata_base.sh)
```

#### Tag付与
`run-instances` 実行時に合わせて設定する方法が不明なので、ちょっとメンドイ方法で設定。
まず、上記の `run-instances` 実行結果から、`InstanceId` を抽出して、その `InstanceId` を引数にしてTagを付与する。

```bash
$ INSTANCE_ID=$(echo ${RET} | python -m json.tool | grep "InstanceId" | awk -F: '{print $2}' | sed -e 's/\"//g' -e 's/,//g')

$ aws ec2 create-tags --resource ${INSTANCE_ID} --tags Key=Name,Value=test-instance-a
```


### 2. 共通設定とか投入したEC2インスタンスAから元となるAMI(BaseAMI)を作成
#### userdataの設定確認
先ほどのuserdataが反映されているかログインして確認する。

```bash
$ ssh -i ~/.ssh/XXXX.pem ec2-user@10.X.X.X


$ sudo hostname
test-instance-a

$ sudo df -h /export/test
Filesystem                 Size  Used Avail Use% Mounted on
/dev/mapper/vgtest-lvtest   20G   45M   19G   1% /export/test
```

#### 設定
他に設定したいものがあれば。
適当に `yum update` とかパッケージをインストールしておく。
終わったらインスタンスを停止させる。

```bash
$ sudo yum -y install dstat git nginx expect
$ sudo yum -y update
$ sudo shutdown -h now
```

#### AMI作成
インスタンスが停止したら、先ほど `run-instances` した時の `InstanceId` を指定して作成。

```bash
$ aws ec2 create-image --instance-id ${INSTANCE_ID} --name test-base-ami
{
    "ImageId": "ami-ac564aad"
}
```

AMIが出来上がるまでちょっと時間が掛かる。

### 3. BaseAMIから新たにEC2インスタンスBを起動
#### JSONファイル修正
さっき使ったのをベースに修正。変更点は以下。

* ImageId : さっきの `create-image` 実行結果
* KeyName : 自分ので
* InstanceType : t2.micro
* AvailabilityZone : 好きなAZで
* SecurityGroupIds : アクセスできるSGで
* SubnetId : 好きなSubnetで
* PrivateIpAddress : 好きなIPで
* BlockDeviceMappings : 記述を削除

```json:run-instances_b.json
{
    "ImageId": "ami-XXXXXXXX",
    "KeyName": "YOUR_KEY_NAME",
    "InstanceType": "t2.micro",
    "Placement": {
        "AvailabilityZone": "ap-northeast-1X"
    },
    "SecurityGroupIds": [
        "sg-XXXXXXXX"
    ],
    "SubnetId": "subnet-XXXXXXXX",
    "PrivateIpAddress": "10.X.X.Z"
}
```

#### userdata作成
今回はホスト名の設定及びhosts追記のみ。

```bash:userdata_b.sh
#!/bin/bash
hostname test-instance-b
sed -i -e "s/HOSTNAME=\(.*\)/HOSTNAME=test-instance-b/" /etc/sysconfig/network
echo "10.X.X.Z    test-instance-b" >> /etc/hosts
```


#### EC2インスタンスB起動 & Tag付与
さっきと同じように実行。

```bash
$ RET=$(aws ec2 run-instances --cli-input-json file://run-instances_b.json --user-data file://userdata_b.sh)

$ INSTANCE_ID=$(echo ${RET} | python -m json.tool | grep "InstanceId" | awk -F: '{print $2}' | sed -e 's/\"//g' -e 's/,//g')

$ aws ec2 create-tags --resource ${INSTANCE_ID} --tags Key=Name,Value=test-instance-b
```

あとはインスタンスが起動するのを待つのみ。
まぁ確認は割愛で。

---
以上