WebOS Goodies

WebOS の未来を模索する、ゲームプログラマあがりの Web 開発者のブログ。

WebOS Goodies へようこそ! WebOS はインターネットの未来形。あらゆる Web サイトが繋がり、共有し、協力して創り上げる、ひとつの巨大な情報システムです。そこでは、あらゆる情報がネットワーク上に蓄積され、我々はいつでも、どこからでも、多彩なデバイスを使ってそれらにアクセスできます。 WebOS Goodies は、さまざまな情報提供やツール開発を通して、そんな世界の実現に少しでも貢献するべく活動していきます。
Subscribe       

Amazon EC2 と Route 53 のはじめかた その 3 : Amazon Route 53 を試す

本日は「Amazon EC2 と Route 53 のはじめかた」 3 回シリーズの最後(3 回目)で、 Amazon Route 53 をご紹介します。ちなみに前 2 回の内容は以下のとおり。

  1. サインアップ
  2. Amazon EC2 を試す
  3. Amazon Route 53 を試す(この記事です)

Amazon Route 53 は比較的マイナーなサービスなのでご存じない方も多いかと思いますが、 Amazon クラウドでホストされた DNS サーバーです。その特徴はクラウドの利点を活かして高い信頼性と低コストを両立させていること。利用料金はひとつのゾーンごとに月額 $1 、さらに 100 万クエリーごとに $0.5 ととてもリーズナブル。 Amazon クラウドの信頼性を考えれば、驚異的なコストパフォーマンスです。もちろん、ほぼすべての DNS レコードが編集可能なので、必要な設定が行えないということもありません。

実は私が AWS のアカウントを取ったのも、 Value Domain の DNS サーバーでは DKIM (メールの送信ドメイン認証)の設定ができなかったことが切っ掛けです私の勘違いだったようです orz)。同様にレジストラのレンタル DNS サーバーを利用している方が多いと思いますが、 DNS はインターネットの根幹を司る重要なサービスです。これを機に Route 53 への移行を検討してみてはいかがでしょうか。

Amazone Route 53 の利用登録

第 1 回でも少し書きましたが、 Amazon EC2 の利用登録が既に済んでいるなら、 Amazon Route 53 の利用登録はとても簡単です。まず Amazon Route 53 のページを開き、左側にある「Sign Up For Amazon Route 53」という黄色いボタンをクリックします。

利用料金の説明と、支払情報が表示されるので、確認した後「ラインナップの完了」をクリックします。

これで利用登録は完了です。

もし他のサービスの利用登録をしていなければ(Route 53 が AWS で最初に使用するサービスであれば)、クレジットカード情報の登録や本人確認などがあるはずです。そちらの手順については第 1 回を参照してください。

なお、利用登録をしても、最初の Hosted Zone (Route 53 で管理するドメイン)を作成するまでは課金されません。これがクラウドサービスのいいところですね。

Route 53 API にアクセスする Ruby スクリプト

利用登録が済んだら、さっそく活用!・・・したいところですが、残念ながら Route 53 には EC2 のような便利な Management Console は用意されていません。 Web API を直接叩いて DNS レコードの作成・更新を行わなければいけないのですGoogle Spreadsheets で管理するためのスクリプト作りました!ヽ(゚∀゚)ノ

開発者サイトではそれらを行うためのツールやライブラリがいくつか公開されていますが、どうせきちんと管理するためには自分で自動化する必要があるでしょうから、 API の勉強も兼ねて Ruby のクライアントを書いてしまいました。以下、そのソースです。

#! /usr/bin/ruby

require 'openssl'
require 'net/https'
require 'uri'

ACCESS_KEY    = 'アクセスキー'
ACCESS_SECRET = 'シークレットアクセスキー'
DEBUG_MODE    = false

class Route53Client
  HOST = 'route53.amazonaws.com'
  PATH = '2010-10-01'

  def initialize(id, secret, debug_mode=false)
    @id     = id
    @secret = secret
    @debug  = debug_mode
  end

  def generate_auth_header(date)
    hmac = OpenSSL::HMAC.new(@secret, OpenSSL::Digest::SHA1.new)
    hmac.update(date)
    ['AWS3-HTTPS ',
     'AWSAccessKeyId=', @id, ',',
     'Algorithm=HmacSHA1,',
     'Signature=', [[hmac.digest].pack("m").gsub(/\s/u, '')]].join('')
  end

  def signed_request(method, uri, headers={}, body=nil)
    uri     = URI.join('https://' + HOST, PATH + '/' + uri) unless URI === uri
    headers = headers.dup
    date    = self.fetch_date()
    headers['x-amz-date']           = date
    headers['X-Amzn-Authorization'] = self.generate_auth_header(date)
    self.request(method, uri, headers, body)
  end

  def request(method, uri, headers={}, body=nil, debug=true)
    method           = method.upcase
    headers          = headers.dup
    http             = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl     = (uri.scheme == 'https')
    http.verify_mode = OpenSSL::SSL::VERIFY_NONE
    http.set_debug_output($stderr) if @debug && debug
    http.start do |http|
      if method == 'POST' || method == 'PUT'
        headers['Content-Length'] = (body ? body.length : 0).to_s
        headers['Content-Type']   = 'text/xml'
      end
      case method
      when 'GET'
        response = http.get(uri.request_uri, headers)
      when 'POST'
        response = http.post(uri.request_uri, body, headers)
      when 'PUT'
        response = http.put(uri.request_uri, body, headers)
      when 'DELETE'
        response = http.delete(uri.request_uri, headers)
      end

      if Net::HTTPSuccess === response
        response
      else
        body = response ? response.body : ''
        raise "Failed to send #{method} request to #{uri}\n#{body}"
      end
    end
  end

  def fetch_date()
    uri      = URI.parse("https://#{HOST}/date")
    response = self.request('GET', uri, {}, nil, false)
    response['Date']
  end
end

client = Route53Client.new(ACCESS_KEY, ACCESS_SECRET, DEBUG_MODE)

if $*.size >= 2 && /\A(?:GET|POST|PUT|DELETE)\z/i === $*[0].strip
  body     = $*[2] ? IO.read($*[2]) : nil
  response = client.signed_request($*[0].strip, $*[1].strip, {}, body)
  print response.body
  print "\n"
else
  print "Usage: r53.rb <GET|POST|PUT|DELETE> path [file]\n"
end

以下はこのコマンドを前提にしてアクセス方法をご紹介するので、パスの通っている場所に r53.rb として保存して、実行属性を設定しておいてください。そして、先頭で定義されている定数 ACCESS_KEY および ACCESS_SECRET に、後で取得するアクセスキーとアクセスシークレットを設定してください。

  1. 上記のソースをパスの通った場所に r53.rb という名前で保存する。
  2. 後で取得するアクセスキーおよびシークレットアクセスキーを、先頭で定義している定数 ACCESS_KEY および ACCESS_SECRET に設定する。
  3. r53.rb に実行属性を付加する。例 : chmod 700 r53.rb

ちなみに、 API の呼び出し方法を少しだけ解説すると、以下のような手順になります。

  1. https://route53.amazonaws.com/date に GET リクエストを投げ、 Date レスポンスヘッダから時刻文字列を取得する。
  2. 時刻文字列を HMAC で署名し、結果を Base64 で文字列化する。
  3. リクエストヘッダの x-amz-date に時刻文字列、 X-Amzn-Authorization に Base64 化した署名を設定し、必要な API エンドポイントにリクエストを投げる。

微妙に不安になるくらい簡単ですが、通信経路はすべて HTTPS が前提なので、これでいいのでしょう。たぶんきっと(^^;

アクセスキーを取得する

Route 53 の API にアクセスするには、「アクセスキー」というパスワードのようなものを取得する必要があります。まず AWS のサイト を開き、「アカウント」のメニューにある「セキュリティ証明書」をクリックしてください。

セキュリティ証明書のページが開き、その「アクセス証明書」の欄にアクセスキーが表示されています。シークレットアクセスキーについては、「表示」のリンクをクリックすることで表示されます。

これらの情報を、 r53.rb の先頭にある定数に設定しておいてください。

Hosted Zone の作成

準備ができたところで、自分の Hosted Zone を作成しましょう。 Hosted Zone とは Route 53 で管理するドメインの単位で、基本的にはレジストラに登録したドメインひとつにつきひとつの Hosted Zone を作成します。例えば、 webos-goodies.jp という Hosted Zone には www.webos-goodies.jp, app01.apps.webos-goodies.jp といったすべてのサブドメインが含まれますが、 www.webos-goodies.com は別の Hosted Zone (webos-goodies.com) になります。

また、課金の単位でもあるので、 Hosted Zone を作成すると課金が開始されます。といっても、前述のとおり極めて少額ですが。

それでは、 r53.rb を使って Hosted Zone を作ってみましょう。 Route 53 の API では、所定の XML を HTTP の POST メソッドで送信することですべての操作を行います。例えば webos-goodies.jp という Hosted Zone を作成するなら、以下の XML を送信します。

<?xml version="1.0" encoding="UTF-8"?>
<CreateHostedZoneRequest xmlns="https://route53.amazonaws.com/doc/2010-10-01/">
   <Name>webos-goodies.jp.</Name>
   <CallerReference>Add webos-goodies.jp</CallerReference>
</CreateHostedZoneRequest>

各タグの意味は以下のとおり。

タグ説明
Name作成する Hosted Zone のドメイン名(最後にピリオドを付ける)
CallerReferenceリクエストごとに異なる 128 バイト以下の任意の文字列

CallerReference がわかりづらいですが、リトライによる二重登録を防ぐもののようで、既に処理済みのリクエストと同じ CallerReference を持つリクエストは無視されます。そのほかに指定できるタグについては API リファレンスを参照してください。

上記の XML を適当なファイル名(ここでは ~/route53/createzone.xml と仮定)に保存し、以下のコマンドを実行すれば Hosted Zone が作成できます。

r53.rb POST hostedzone ~/route53/createzone.xml

レスポンス・ボディとして返された XML が表示されます(以下は読みやすいように改行しています)。

<?xml version="1.0"?>
<CreateHostedZoneResponse xmlns="https://route53.amazonaws.com/doc/2010-10-01/">
  <HostedZone>
    <Id>/hostedzone/Z21DW1QVGID6NG</Id>
    <Name>webos-goodies.jp.</Name>
    <CallerReference>Add webos-goodies.jp</CallerReference>
    <Config/>
  </HostedZone>
  <ChangeInfo>
    <Id>/change/C24LD0DUV5VOVE</Id>
    <Status>PENDING</Status>
    <SubmittedAt>2011-01-24T18:08:42.499Z</SubmittedAt>
  </ChangeInfo>
  <DelegationSet>
    <NameServers>
      <NameServer>ns-01.awsdns-00.com</NameServer>
      <NameServer>ns-500.awsdns-11.net</NameServer>
      <NameServer>ns-1112.awsdns-31.org</NameServer>
      <NameServer>ns-1600.awsdns-27.co.uk</NameServer>
     </NameServers>
  </DelegationSet>
</CreateHostedZoneResponse>

これで Hosted Zone が作成されました。レスポンスの XML のうち、 HostedZone/Id タグの /hostedzone/ 以降の部分が HostedZone の ID で、後ほど使うのでメモしておいてください。また、 NameServer タグでリストされているのが Hosted Zone に割り当てられた DNS サーバーなので、レジストラにこれらを登録することで実際のドメイン名の解決に Route 53 を利用できます。

DNS レコードの追加

Hosted Zone は追加できましたが、まだ DNS レコードが空の状態なので、次は必要な DNS レコードを追加します。これまた更新内容を XML として書かなくてはなりません。例えば webos-goodies.jp の A レコードの追加なら、以下の XML になります。

<?xml version="1.0" encoding="UTF-8"?>
<ChangeResourceRecordSetsRequest xmlns="https://route53.amazonaws.com/doc/2010-10-01/">
   <ChangeBatch>
      <Comment>Add A record for webos-goodies.jp.</Comment>
      <Changes>
        <Change>
          <Action>CREATE</Action>
          <ResourceRecordSet>
            <Name>webos-goodies.jp.</Name>
            <Type>A</Type>
            <TTL>14400</TTL>
            <ResourceRecords>
              <ResourceRecord>
                <Value>210.251.253.231</Value>
              </ResourceRecord>
            </ResourceRecords>
          </ResourceRecordSet>
        </Change>
      </Changes>
   </ChangeBatch>
</ChangeResourceRecordSetsRequest>

それぞれのタグの意味は・・・まあだいたいわかりますよね。詳細は API リファレンスを参照してください。なお、 Change タグを複数記述することで、複数の DNS レコードを一気に追加できます。変更がすべての DNS サーバーに行き渡るには時間がかかるので、必要な変更は一度に済ませたほうが良いのでしょう。

XML ファイル(ファイル名は ~/route53/adddnsrecord.xml と仮定)が用意できたら、以下のコマンドを実行することで DNS レコードが追加できます。

r53.rb POST hostedzone/<hostedzone id>/rrset ~/route53/adddnsrecord.xml

上記のうち、 <hostedzone id> となっている部分には Hosted Zone を作成したレスポンスの中に含まれていた ID を指定してください。前述の例なら "Z21DW1QVGID6NG" ですね。レスポンスとしては以下のような XML が返るはずです。

<?xml version="1.0"?>
<ChangeResourceRecordSetsResponse xmlns="https://route53.amazonaws.com/doc/2010-10-01/">
  <ChangeInfo>
    <Id>/change/CVSYPFWLOWW74</Id>
    <Status>PENDING</Status>
    <SubmittedAt>2011-01-24T18:31:07.726Z</SubmittedAt>
  </ChangeInfo>
</ChangeResourceRecordSetsResponse>

これで DNS レコードが変更できました。ただし、変更が実際に適用されるまでには時間がかかりますので、適用が完了したかどうかを確認するために以下のコマンドを実行します。

r53.rb GET change/<change id>

<change id> となっている部分には、レスポンスの ChangeInfo/Id タグの /change/ 以降の文字列を指定します。上記の例であれば "CVSYPFWLOWW74" ですね。レスポンスとしては以下のものが返ります。

<?xml version="1.0"?>
<GetChangeResponse xmlns="https://route53.amazonaws.com/doc/2010-10-01/">
  <ChangeInfo>
    <Id>/change/CVSYPFWLOWW74</Id>
    <Status>PENDING</Status>
    <SubmittedAt>2011-01-24T18:31:07.726Z</SubmittedAt>
  </ChangeInfo>
</GetChangeResponse>

Status タグが INSYNC ならば適用完了なのですが、ここでは PENDING となっているので、更新中ということです。通常であれば、 1 分もかからずに INSYNC になるはずです。

以上、 3 回にわたって Amazon EC2 と Route 53 をご紹介してきました。まだまだ活用できていると言うには程遠い状態ですが、それだけ使いごたえのあるサービスということでしょう。今後も継続してレポートしていきたいと思っているので、乞うご期待です。

関連記事

この記事にコメントする

Recommendations
Books
「Closure Library」の入門書です。
詳しくはこちらの記事をどうぞ!
Categories
Recent Articles