[小ネタ] Route53 + CloudWatch Events + Lambda + Tagsを使って簡易DDNSを作る

自社でDIY的に作った仕組みが案外好評だったので記事にしてみました。
# 面倒くさいからとりあえずEIP使う を無くしたいだけだった
開発や検証用途でAWS上にInstanceを立ち上げた時、とりあえずブラウザにIP直打ちしたり、とりあえずSSHもconfig使わずにIP直打ちしてしまう経験ありませんか？？
私は壮絶にございました。

- Public IPはInstanceを起動する度に変わるから、とにかくやってられなくてEIP使っちゃう
- EIPは別に1個ならInstance起動中は0円だけど、停止してベッドでぐっすり寝てる時はじわじわ料金キテる。チリツモ
- Public IP変わってもDDNS的にドメインを自動的にアップデートしてくれるような仕組みがあればEIPなんぞ使わなくてもいいんだけど…

## 一般的な実現方法
前述したようなケースの場合、これまではEC2の「user-data」などを使ってcloud-initやシェルスクリプトでInstanceの作成/起動時にRoute53のレコードをUPSERTするような手法が一般的だったと勝手に解釈しています。

[参考:クラスメソッドさん]
cloud-initを使ってRoute53をPrivate Dynamic DNSにする
http://dev.classmethod.jp/cloud/aws/route53-private-dynamic-dns-use-cloudinit/

Jenkinsを導入したEC2インスタンスに常に同じ名前でアクセスする
http://dev.classmethod.jp/cloud/jenkins-ec2-name-upsert/

上記の例は多くの場合に役立ちますが、1つ問題があります。

**リソースが削除された時に、ドメインが削除されないという点です。**

スクリプトがInstance内部にある場合はTerminate時にレコードを消す事は容易ではありません。
そこで今回はこれを解決するために、今年の初めにアップデートされた「CloudWatch Events」とみんな大好きLambdaを使ってこの問題を解決したいと思います。

# CloudWatch Events とは
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/DeveloperGuide/WhatIsCloudWatchEvents.html
http://aws.typepad.com/aws_japan/2016/01/new-cloudwatch-events-track-and-respond-to-changes-to-your-aws-resources.html

詳細は上記のドキュメントを見てみてください。
何が嬉しいかと言うと、これまでEC2 Instanceのリソース状態の変化をシンプルに検知するにはCloudTrailなどを使うしかなかったのですが、CloudTrailはログが書き出されるまでに結構な時差があり、リアルタイムに検知することはできませんでした。

例. あるEC2のステータスがPendingからrunningに変わったのをトリガーに、すぐに任意の処理を走らせたい

これを見事に解決してくれるのがCloudWatch Eventsです。
EC2 Instanceであれば、
Pending, Running, Stopping, Stopped, Terminated
などの各状態変化をリアルタイムに検知して、
Lambda, SNS, SQS, Kinesis, Built-in traget
などにイベントを流す事ができます。

![スクリーンショット 2016-06-01 3.55.35.png](https://qiita-image-store.s3.amazonaws.com/0/19942/efb86d5c-5fe7-49be-ce14-bd590b619f7d.png)

上記の画像は全てのInstanceを対象として状態が「Terminated」、「Running」になった際に、Lambdaファンクションを発火させる設定例です。設定は、CloudWatch -> Events -> Rules からルールを作成していきます。

Lambdaファンクションには、

```json
{
  "id": "ee376907-2647-4179-9203-343cfb3017a4",
  "detail-type": "EC2 Instance State-change Notification",
  "source": "aws.ec2",
  "account": "123456789012",
  "time": "2015-11-11T13:30:34Z",
  "region": "us-east-1",
  "resources": [
    "arn:aws:ec2:us-east-1:123456789012:instance/i-abcd1111"
  ],
  "detail": {
    "instance-id": "i-abcd1111",
    "state": "running"
  }
}
```
のようなイベントが渡されるため、あとはLambdaから焼くなり煮るなり好きにすることができます。
今回の主目的は脱EIPでしたが、Route53のプライベートホストゾーンにも簡単に応用することができます。

# Labmda内での処理
さて、前述までの部分でLambdaにInstanceの状態変化をリアルタイムに伝える事ができるようになったので、後はLambdaで好きにコードを書いて処理するだけです。

観点としては、

- Instanceの作成/起動時にはInstanceのTagsを見て、Nameをドメイン名のprefixとして任意のAレコードをUPSERTで登録する
- Instanceの削除(停止を含めてもよい)時には、その逆にレコードを削除する処理を書く

ただ、注意点としては現状のCloudWatch Eventsのルールではあくまでもリージョン内全てのInstance or 特定のInstanceしか対象にできないので、
上記を参考に実装するとDDNSをする必要の無いInstanceまで不要なレコードが追加されてしまいます。

そこで、個人的にオススメしたいのは、ドメインのprefixとしてInstanceのNameを使うのではなく、
任意のTagを1つルール決めしておいて、Lambdaファンクション実行時に、そのInstanceにDDNS設定用のTagがある/ない を基準に処理を続ける/止める という風にするのが良いと思います。

具体的には、下記のようなイメージです。
![スクリーンショット 2016-06-01 4.14.10.png](https://qiita-image-store.s3.amazonaws.com/0/19942/6de68b63-bd53-34eb-145f-376a03443ef4.png)
この場合、Lambdaからタグ名「Domain」の存在を確かめる事で、レコードの追加の必要有無を判断できます。

# その他
いかがだったでしょうか。小ネタ中の小ネタですみません。
自社ではこれらの仕組みを使って、ドメインが必要な場合は構築時にTagを追加するという運用ルールを決めることで自動化を取り入れました。

プライベートホストゾーンにも簡単に応用できるので、
例えばTerraformでの構築時にTagを設定するだけで内部DNSの設定自動化までサクッと出来たりすると思います。