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 がほぼ全てのコマンドについてるのでそれを利用する。

$ 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つ付けてみた
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で束ねるスクリプトにしてみた。

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形式で出力されるので、変数に入れておく。後々使えるので。

$ 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を付与する。

$ 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が反映されているかログインして確認する。

$ ssh -i ~/.ssh/XXXX.pem [email protected]


$ 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 とかパッケージをインストールしておく。
終わったらインスタンスを停止させる。

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

AMI作成

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

$ 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 : 記述を削除
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追記のみ。

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付与

さっきと同じように実行。

$ 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

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


以上