AWS-cli + route53でElasticIPを節約したお話

この記事はリクルートライフスタイル [Advent Calendar 2015](http://qiita.com/advent-calendar/2015/recruitlifestyle) の20日目です。
ホットペッパービューティーで開発陣の周りをお手伝いしている、岡田賢治です。

# はじめに
RLSにお世話になりまして、約半年が過ぎました。元は、オンプレサーバのサバ管で、いろいろスクリプトを書いていました。
こちらの環境で、AWSで作業することが非常に多くなりました。AWSは全くの素人だったので、ググりながらの日々が続きました。

作業内容としては、主にサーバの構築。サービスを提供するものや、サービスの検証・サービスのミラー等、様々なサーバを用意します。
サーバのインスタンスが増えるに当たり、増えるものと減るものが出てきます。
### 増えるもの
　インスタンス数
　ディスクの使用量
### 減るもの
　ElasticIPの残り数

# Elastic IP
AWSを利用するに当たり、最初に面食らったのがElasticIPでした。インスタンスの設定では、内部管理のプライベートIPの他、外部からアクセスできるグローバルIPを割り当てることが出来ます。
しかし、このグローバルIPはインスタンスの起動毎に変わるIP＝Public IPになります。
「どうやれば固定できる・・・」で、発見したのがElasic IP（の使用方法）でした。

## 減ってゆくElastic IPの残り
Elastic IPは、有償でAWSから借り受けるものであり、どの利用者も無尽蔵に利用できるというわけではないと思います。もちろん正式サービスに利用するのであればいいのですが、当方のように「検証環境」とか「ミラー環境」を用意する身としては、常に在庫があるわけではありません。
すると何が始まるか・・・？検証環境のインスタンスを起動するたびに、Route53へAレコードを登録しなくてはいけなくなります。それを忘れて「繋がらない・・・」とハマったことも何度も・・・

## 要は何ができればいいの？
そもそもElastic IPを利用する理由は「インスタンス起動時に常に同じIPが割あたっている保証がほしい」からなわけです。
なぜ保証がほしいか？といえば「DNS（のAレコード）に、そう登録してあるから」なわけです。

ここでひらめいたのが、AWSのWebAPIを利用する方法です。
・そのインスタンスに割り振られたPublic IP（≠Elastic IP）は、取得ができる。
・DNS/Route53のAレコードは、WebAPIにより登録ができる。

となれば、「常に同じIPじゃなくても、起動時に割あたったPublic IPがそのままRoute53に登録してあるFQDNに割あたってくれればいいんじゃね？」となります。

# スクリプトへの反映

そこで考えたスクリプトが以下の2つです。

```bash:r53.sh
#!/bin/bash

export PATH=/usr/local/sbin:/usr/local/bin:${PATH}

#変数設定
INSTANCEID=XXXXXXXXX
HOSTEDZONEID=XXXXXXXXX


# PublicIPが有効になるまで2分待つ
sleep 120 ;

cd /root

# AWS CLIを使って、PublicIPを取得
PUBLICIP=`aws ec2 describe-instances --instance-ids ${INSTANCEID} | jq ".Reservations[0].Instances[0].NetworkInterfaces[0].PrivateIpAddresses[0].Association.PublicIp"`

#PublicIPは、"XXX.XXX.XXX.XXX"の形式なので、XXX.XXX.XXX.XXXに変換する
PUBLICIP=`echo ${PUBLICIP} | sed -e 's/"//g'`

# 設定ファイルset.jsonを削除
if [ -f set.json ]; then
        rm set.json ;
fi

# テンプレートファイルを元にset.jsonを作成
sed -e "s/PUBLICIP/${PUBLICIP}/" set.json.tmpl > set.json

# Route 53情報を書き換える
aws route53 change-resource-record-sets --hosted-zone-id ${HOSTEDZONEID} --change-batch file://set.json

exit ;
```
ここでは、
- 16行目でaws ec2 describe-instancesコマンドを利用して、対象インスタンスの情報を取得しています。対象インスタンスは6行目で指定したINSTANCEIDに設定したインスタンスIDです。
- ただしそのコマンドでは、起動直後にはインスタンス情報を取得できません（正確には、PublicIPが割当たるまでの時間差の関係で、PublicIPが取得できません）。有効になるまで一定時間待つ必要があります。ここでは120秒=2分としました(11行目)
- JSON形式で値が返りますので、jqコマンドとsedを使ってCIDR形式のIPアドレスに変換します(19行目)。
- AWS Route53に送るJSONデータを作成します(26行目)。テンプレートファイルset.json.tmplを元にして、set.jsonファイルを作成しています。
- 出来上がったset.jsonファイルを使って、Route53の情報を書き換えます(29行目)。

次にテンプレートファイルset.json.tmplですが、

```JSON:set.json.tmpl
{
  "Comment": "create A record",
  "Changes": [
    {
      "Action": "UPSERT",
      "ResourceRecordSet": {
        "Name": "XXXX.YYYY.ZZZZ.",
        "Type": "A",
        "TTL": 300,
        "ResourceRecords": [
          {
            "Value": "PUBLICIP"
          }
        ]
      }
    }
  ]
}
```
- Route53に登録するFQDNは"XXXX.YYYY.ZZZZ"です。
- PUBLICIPの文字列が、r53.shの中のsedコマンドでCIDR形式のIPアドレスに置き換わります(r53.sh:29行目)。

# 補足情報
上記スクリプトを動かすには、以下の条件が必要です。
- AWSのコマンド操作ツール群、aws-cliがインストールされていること。
- jqがインストールされていること。
- AWS/EC2に、インスタンスの情報取得が可能であるアクセス権（キー・シークレットキー等）を保持していること。
- AWS/Route53に、更新可能なアクセス権とそれに付随するHOSTZONEID情報を保持していること。

# まとめ
プログラミングやら何やらいろいろやっていますが、自分のプログラミングの起源はUNIX/Linuxのサーバ管理で使った、スクリプト群が起源であることを最近痛感しています。
最近では、それがWebAPI/RestAPIになったりJSON形式が出てきたり、DevOpsという言葉が生まれたりと、昔を懐かしんでばかりもいられないな、というのが実情です。
仕事では、引き続き他のツール群との連携を取っていくつもりです。
プライベートでは・・・OpenStackをやっているので、AWSの比較をAPIを含めながら行っていきたいな、と考えています。

長い文章を読んでいただき、ありがとうございました。それでは。