AWS/EC2 インスタンスのホスト名を固定する (Route53 使用 Ver)
こんにちは、R&D グループの y.kimura です。
今回は Amazon Web Service の EC2 に Route53 を利用して、
お好きなホスト名でアクセスできるようにしようというお話です。
AWS に固定ホスト名でアクセスするには、以下の方法が有名です。
- アクセスする端末の hosts ファイルを書き換える
メリット: 余計な費用が掛からない
デメリット: EC2 側の変更に追従するのが面倒
- EC2 の Elastic IPを割り当てる
メリット: 5 つまでなら無料で割り当てられる
デメリット: 5 つ以上 EC2 インスタンスに割り当てると、申請+追加料金が必要
- Route53 に登録する
メリット: EC2 インスタンス数の制限が無いに等しい
デメリット: 元々使用していれば問題ないですが、そうでない場合はRoute53 をこれだけのために利用する手間と費用がかかる
それぞれ一長一短ありますが、
私の関わっているプロジェクトで実際に採用している 3 番目の方法をご紹介します。
やることはいたって単純で、次の通りになります。
EC2インスタンスの起動/再起動時に次の処理を実行するスクリプトを走らせる。
- EC2インスタンス自身のメタデータを取得する
- メタデータ(もしくはタグ情報)を元にホスト名を決定する
- ホスト名を Route53 から削除する(古いホスト名)
- ホスト名を Route53 に登録する(レコード名=ホスト名 の CNAME に PublicDNS を登録)
EC2 の API は、様々な言語の SDK が用意されていますが、今回は Ruby の SDK を使用します。
Ruby 本体のバージョンは 2.1.1、AWS-SDK のバージョンは 2014 年 2 月時点での最新 (1.34.1) です。
Route53 の設定、EC2 インスタンスの作成は実行済み、
EC2 インスタンスの OS は CentOS6.4 64bit とします。
1. Ruby のインストール
rvm を使ってインストールするのが簡単ですね。せっかくなので現在の最新版の 2.1.1 をインストールします。
$ curl -L https://get.rvm.io | bash -s stable
一旦ログアウトして再度ログイン。rvm コマンドが使用できるようになります。
rvm で ruby 本体をインストールする前に、epel のリポジトリをインストール
# wget http://ftp-srv2.kddilabs.jp/Linux/distributions/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm
# rpm -Uvh epel-release-6-8.noarch.rpm
必要なパッケージをインストール
# yum install -y patch libyaml-devel libffi-devel glibc-headers gcc-c++ glibc-devel readline-devel zlib-devel openssl-devel autoconf automake libtool bison
ruby 本体のインストール
$ rvm install 2.1.1
続いて AWS-SDK のインストール
$ gem install --no-ri --no-doc aws-sdk
これで準備完了です。
2. スクリプトを設置
次の ruby スクリプトを ruby をインストールしたユーザの home に設置します。
hostname_to_route53.rb
#!/usr/bin/env ruby
require 'rubygems'
require 'aws-sdk'
## 設定項目
# AWS のアクセスキーIDとシークレットアクセスキー
ACCESS_KEY_ID = "アクセスキーID"
SECRET_ACCESS_KEY = "シークレットアクセスキー"
# EC2のリージョン=Asia Pacific/Tokyo
EC2_ENDPOINT = "ec2.ap-northeast-1.amazonaws.com"
# Route53のHosted-zone id
HOSTED_ZONE_ID = "Hosted Zone ID"
# ホスト名の元になるドメイン名
# 最後にピリオドを必ず付けること!
DOMAIN_NAME = "ec2.tricorn-labs.jp."
# TTL
TTL = 300
## 実行コード
SELF_META_URL = "http://169.254.169.254/latest/meta-data"
# EC2インスタンスのメタデータ
MY_PUBLIC_DNS = `curl #{SELF_META_URL}/public-hostname 2>/dev/null`
MY_PUBLIC_IP = `curl #{SELF_META_URL}/public-ipv4 2>/dev/null`
MY_INSTANCE_ID = `curl #{SELF_META_URL}/instance-id 2>/dev/null`
# APIキー、秘密鍵の設定
AWS.config({:access_key_id => ACCESS_KEY_ID, :secret_access_key => SECRET_ACCESS_KEY})
# APIキーに対応したアカウントのEC2インスタンスコレクション
ec2 = AWS::EC2.new(:ec2_endpoint => EC2_ENDPOINT)
# このEC2インスタンス情報の取得
my_ins = ec2.instances[MY_INSTANCE_ID]
# たまにメタデータの取得に失敗する 念のためチェック
raise "#{MY_INSTANCE_ID.to_s} was not found on your EC2" unless my_ins.exists?
sub_domain = my_ins.tags['sub-domain'] ? my_ins.tags['sub-domain'] : my_ins.instance_id
hostname = "#{sub_domain}.#{DOMAIN_NAME}"
r53 = AWS::Route53::Client.new()
rrsets = AWS::Route53::HostedZone.new(HOSTED_ZONE_ID).rrsets
rrset = rrsets[hostname, 'CNAME']
# レコードセットの削除
begin
cur_rec = rrset.resource_records[0][:value]
rescue => error
cur_rec = ''
end
if !cur_rec.empty? then
batch = AWS::Route53::ChangeBatch.new(HOSTED_ZONE_ID)
batch << AWS::Route53::DeleteRequest.new(hostname, 'CNAME', :ttl => TTL, :resource_records =>[{:value => cur_rec}])
batch.call
end
# レコードセットの登録
batch = AWS::Route53::ChangeBatch.new(HOSTED_ZONE_ID)
batch << AWS::Route53::CreateRequest.new(hostname, 'CNAME', :ttl => TTL, :resource_records => [{:value => MY_PUBLIC_DNS }])
batch.call
なお、設定するアクセスキーID・シークレットアクセスキーに対応する IAM ユーザの権限としては以下が割り当てられていれば動作します (不要なものもあるかもしれませんが)。
- ec2
- DescribeInstanceAttribute
- DescribeInstanceStatus
- DescribeInstances
- DescribeTags
- route53
- * (全ての権限)
では、スクリプトに実行権限を付けましょう
$ chmod 700 hostname_to_route53.rb
EC2インスタンスに、「sub-domain」というタグが設定されていると、
そちらをサブドメインとして使用します。
タグがない場合は、インスタンスID (i-XXXXXXXX) をサブドメインとして使用します。
スクリプト内の DOMAIN_NAME が 「ec2.tricorn-labs.jp」となっており、「sub-domain」タグに「web01」が設定されている EC2 インスタンスで実行した場合、Route53 に登録されるホスト名は、「web01.ec2.tricorn-labs.jp」となります。
※「tricorn-labs.jp」というドメインは実際には存在しません。
複数の EC2 インスタンスで使用する場合は、
「sub-domain」タグの値が重複しないよう注意してください。
さて試しに実行してみましょう。
$ ./hostname_to_route53.rb
マネジメントコンソールの Route53 で確認すると、新たにレコードが登録されていることがわかります。
CNAME に PublicDNS を登録するため、
同一リージョンの EC2 同士でホスト名を使用して通信した場合、
PrivateIP に変換されるため外部ネットワークを経由せずに通信することができます。
3. サーバ起動時に実行する
最後に、EC2 の PublicIP,PublicDNS が変更される
「Stop」⇒「Start」時に自動的にスクリプトを実行するようにします。
以下の様な init スクリプトを用意します。
ruby をインストールしたユーザを仮に ec2-user、スクリプトのパスを /home/${USER}/aws-tools/hostname_to_route53.rb としていますが、適宜変更して下さい。
r53
#!/bin/sh
#
# /etc/rc.d/init.d/r53
#
# Register hostname to Route53
#
# chkconfig: 345 44 56
#
USER=ec2-user
SCRIPT=/home/${USER}/aws-tools/hostname_to_route53.rb
start() {
su -l $USER -c "$SCRIPT"
return $?
}
stop()
{
return 0
}
restart() {
stop
start
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
*)
echo "Usage: $0 {start|stop|restart}"
RETVAL=2
esac
exit $RETVAL
/etc/init.d/ 以下に設置し、実行権限を付与した上で、chkconfig で起動時に実行されるよう登録します。
# mv r53 /etc/init.d/
# chown root:root /etc/init.d/r53
# chmod 700 /etc/init.d/r53
# chkconfig r53 on
さて、準備が出来ましたので試しに EC2 インスタンスを「Stop」アンド「Start」してみます。
しばらく待つと、Route53 にホスト名が登録されます!
このように、EC2 インスタンスの boot 時自動的に
Route53のレコードを更新するような仕組みを入れた AMI を作成しておけば、
PublicDNSは長すぎる!
IPだと覚えにくい!
うかつにインスタンスを停止できない!(するな)
などの悩みから解消されます。


ディスカッション
コメント一覧
まだ、コメントがありません