Fusicきばんブログ

Fusic基盤ユニットの非公式技術ブログ

複数AWSアカウントのRoute53で管理されている複数HostedZoneの複数DNSレコードをうまく変更する

基盤ユニットの小山 ( @k1LoW ) です。

そろそろ PHPカンファレンス福岡2017ですね!

弊社Fusicでは前日に会社会議室を開放してクーラーの効いているWiFiスポットを提供しますので、是非ご利用下さい。 Fusicは天神にありますしアクセスも良いです。福岡の開発会社をみるいい機会ですよ!

fusic.co.jp

さらにカンファレンスの次の日は無限ビール(?)を開催します!

fusic.doorkeeper.jp

楽しみですね!


さて、以前ちょっと面倒なDNS設定変更作業がありましたので、その作業手順をメモしておこうと思います。

主にRubyの話です。

DNS設定変更作業内容

1つ1つの作業はただのDNSレコードの設定変更だったのですが、少し面倒で

  • 複数のAWSアカウントのRoute53で管理されているHostedZone
  • HostedZoneも複数
  • HostedZone内の変更対象のDNSレコードも複数
  • それぞれ変更内容が異なる
  • DNSレコードの先にはそれぞれWebサーバが存在。設定変更後も同じWebサーバにリクエストが通る前提

というものを、(Webサイトが存在するので)できるだけ短時間に実施する必要がありました。

結果として、Codenize.toolsによるコード化ができ、+Rubyスクリプトでの事前のテストで変更内容の確認ができたので心理的ストレスなく当日を迎えられました。

作業内容

1. AWSリソース操作の権限をつなげる

対象Route53が複数のAWSアカウントにまたがっていたので、まずはAWSリソースの操作権限を1つのIAM Userにつなげました。

メインのAWSアカウントでIAM Userを作成し、それ以外のアカウントにはAssume Role用のIAM Roleを作成して連携させています。

~/.aws/credentials~/.aws/config は以下のようになりました。

# ~/.aws/credentials
[change-task-user-main]
aws_access_key_id = XXXXXXXXXXXXXXXXXXX
aws_secret_access_key = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ~/.aws/config
[profile change-task-role-a]
role_arn = arn:aws:iam::11111111111111:role/change-task
source_profile = change-task-user-main

[profile change-task-role-b]
role_arn = arn:aws:iam::222222222222:role/change-task
source_profile = change-task-user-main

[profile change-task-role-c]
role_arn = arn:aws:iam::333333333333:role/change-task
source_profile = change-task-user-main

2. DNSの設定をCodenizeする

Route53の設定は特にコードでの管理がされていませんでしたので、まずはコードで管理できるようにします。

Codenize.toolsの1つであるRoadworkerを利用します。

RoadworkerはRoute53の設定をコード化して管理運用するツールです。

Codenize.toolsの各ツールにはエクスポート機能があるのでいつでもコード化できます。

以下のようにRoute53の設定をRoadworkerのexport機能でエクスポートしました。

$ AWS_PROFILE=change-task-user-main bundle exec roadwork -e -o Routefile.main
$ AWS_PROFILE=change-task-role-a bundle exec roadwork -e -o Routefile.a
$ AWS_PROFILE=change-task-role-b bundle exec roadwork -e -o Routefile.b
$ AWS_PROFILE=change-task-role-c bundle exec roadwork -e -o Routefile.c

3. CodenizeしたDNS設定を使って、別のAWSアカウントでテスト環境を構築する

エクスポートしたRoutefileを使って別のAWSアカウントでRoute53のテスト環境を構築します。

$ AWS_PROFILE=change-task-test bundle exec roadwork -a -f Routefile.main
$ AWS_PROFILE=change-task-test bundle exec roadwork -a -f Routefile.a
$ AWS_PROFILE=change-task-test bundle exec roadwork -a -f Routefile.b
$ AWS_PROFILE=change-task-test bundle exec roadwork -a -f Routefile.c

これで本番環境のDNS環境(本番DNS)とテスト用のDNS環境(テストDNS)ができました。

4. HTTPリクエストのテストを作成する

本番環境NS、テスト環境NS、そして(念の為)GoogleのNSを使ったHTTPリクエストのテストを作成します。

NSの変更は Resolv::DefaultResolver#replace_resolvers で切り替えました。

HTTPリクエストのテストは取得したHTMLに期待する文字列が含まれているかどうかの簡単なテストです。

ざっくりですが以下のような感じで構築しました。

require 'spec_helper'

dns = {
  production: 'XXX.XXX.XXX.XXX',
  test: 'YYY.YYY.YYY.YYY',
  google: '8.8.8.8'
}

specs = [
  { fqdn: 'example.com', url: 'http://example.com', expected: 'Production Site' },
  { fqdn: 'www.example.com', url: 'http://www.example.com', expected: 'Production Site' },
  { fqdn: 'example.jp', url: 'https://example.jp', expected: '本番サイト' },
  { fqdn: 'blog.example.org', url: 'blog.example.org', expected: 'Production Blog' }
]

dns_req_spec(dns, specs)
# spec_helper.rb
def dns_req_spec(dns, specs)
  dns.each_with_index do |d, idx|
    specs.each do |spec|
      describe "#{spec[:fqdn]} #{idx} DNS" do
        before(:context) do
          resolver = Resolv::DNS.new(nameserver: [d])
          Resolv::DefaultResolver.replace_resolvers([Resolv::Hosts.new, resolver])
        end
        it "#{spec[:url]} http access" do
          connection = Faraday.new spec[:url] do |conn|
            conn.use FaradayMiddleware::FollowRedirects
            conn.adapter :net_http
          end
          res = connection.get '/'
          expect(res.body.force_encoding('UTF-8')).to include(spec[:expected])
        end
      end
    end
  end
end

これで、本番環境NS、テスト環境NS、GoogleのNSの3つにおいて正常にHTTPリクエストが通るかどうかのテストができました。 (正確には対象DNSレコードがCNAMEの場合はテストできませんが)

(ちなみに、Route53のDNSレコードの設定が反映されたかどうかのテストであれば roadwork -t で実施できます。今回はテストDNSというものがあったので利用していません。)

5. テストDNSのDNSレコードの設定を変更する

テストDNSを今回の変更内容に合わせて変更します。

Routefileを編集してRoadworkerの apply オプションで一気に適用できるから便利ですね。

[Edit Routefile.*]
$ AWS_PROFILE=change-task-test bundle exec roadwork -a -f Routefile.main
$ AWS_PROFILE=change-task-test bundle exec roadwork -a -f Routefile.a
$ AWS_PROFILE=change-task-test bundle exec roadwork -a -f Routefile.b
$ AWS_PROFILE=change-task-test bundle exec roadwork -a -f Routefile.c

先ほど4で作成したHTTPリクエストテストを実行して、DNSレコード設定変更後も正常にHTTPリクエストが通ることを確認します。

6. 本番DNSのDNSレコードの設定を変更する

既に検証はテストDNSで終わっているので、先ほど修正したRoutefileを本番のRoute53にそれぞれ apply していけば完了です。

$ AWS_PROFILE=change-task-user-main bundle exec roadwork -a -f Routefile.main
$ AWS_PROFILE=change-task-role-a bundle exec roadwork -a -f Routefile.a
$ AWS_PROFILE=change-task-role-b bundle exec roadwork -a -f Routefile.b
$ AWS_PROFILE=change-task-role-c bundle exec roadwork -a -f Routefile.c

再度、先ほど4で作成したHTTPリクエストテストを実行して、DNSレコード設定変更後も正常にHTTPリクエストが通ることを確認します。

これで完了です。

まとめ

インフラの設定をコード化(Codenize)することによってその後の作業を効率化、さらに作業漏れの軽減ができました。

Rubyで「リゾルバを変更する」という処理を書けることで、事前のテスト検証も実施できました。

今回のDNSがAPIで設定変更可能なRoute53でなかったと思うとちょっとツラいですね。

もし同じようなDNS設定変更があった場合の、何かの参考になれば幸いです。