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だと覚えにくい!
うかつにインスタンスを停止できない!(するな)
などの悩みから解消されます。
ディスカッション
コメント一覧
まだ、コメントがありません