13
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

VPC内 Private DNSに TAGを使って自身の名前を自動登録する

Last updated at Posted at 2016-02-07

VPCを利用した開発環境

VPC と Route53 の Private DNS をうまく組み合わせると、内部的にはまったく同一の開発環境を複数立てることが出来て便利です。Route53 の Private DNS は、同じ名前の Hosted Zone をいくつでも作る事が出来るので、例えば、"dev.example.com" と "demo.example.com" 用にそれぞれ VPC を作りつつ、VPC内部のホストは Private DNSを利用して どちらも "webapp.example.com.internal" という名前をつけた Internal ELB経由でアクセスする といったことが可能になります。

しかし、実際に内部のホストにログインして何かをしようと思うと、デフォルトで着いている ip-xx-xx-xx みたいな名前ではツライ。。といって、固定の webapp-01.example.com.internal とかつけると、AutoScaling のときなどに困ります。DHCPのアドレスなどを利用して、webapp-123.dev.example.com.internal などの名前を起動時に自動で Private DNS に登録して欲しいところ。

インスタンス起動時に Route53 にホスト名を自動登録する

このあたりについては、すでにいろいろな方が書かれているので、下記などを参考にすれば出来るかと思います。

ただ、上記だと、停止時にDNS登録を消してくれないので、どんどん登録が増えてしまいます。下記などを参考に、停止時に自動で DNS登録を削除することも忘れずに...

下記みたいな感じのものを /etc/init.d/dns-ctrl.sh として配置する感じですね。
で、問題は、/var/lib/instance_config で、環境変数を用意してあげるところです。

dns-ctrl.sh
#!/bin/bash                                                                                                        
# chkconfig: 2345 90 10                                                                                            
# description: add this instance to Route53                                                                        

source /var/lib/instance_config

LAST_IP=`echo ${IP_ADDRESS} | cut -d "." -f 4`
HOST_NAME=`echo ${HOST_NAME_BASE}-${LAST_IP}`
# for Full IP                                                                                                      
# HOST_NAME=`echo ${HOST_NAME_BASE}-${IP_ADDRESS} | sed -e 's/\./\-/g'`                                            

name="dns-ctrl"
lock_file="/var/lock/subsys/${name}"

case "$1" in
    start)
        touch ${lock_file}
        BATCH_JSON='{                                                                                                      
          "Changes": [                                                                                                     
            { "Action": "UPSERT",                                                                                          
              "ResourceRecordSet": {                                                                                       
                "Name": "'${HOST_NAME}'.'${VPC_ENV}'.'${DOMAIN_NAME}'",                                                    
                "Type": "A",                                                                                               
                "TTL" : 60,                                                                                                
                "ResourceRecords": [                                                                                       
                  { "Value": "'${IP_ADDRESS}'" }                                                                           
                ]                                                                                                          
              }                                                                                               
            }                                                                                                              
          ]                                                                                                                
        }'
        aws route53 change-resource-record-sets --hosted-zone-id ${HOSTED_ZONE_ID}  --change-batch "${BATCH_JSON}"
        ;;

    stop)
        rm -f ${lock_file}
        BATCH_JSON='{                                                                                                      
          "Changes": [                                                                                                     
            { "Action": "DELETE",                                                                                          
              "ResourceRecordSet": {                                                                                       
                "Name": "'${HOST_NAME}'.'${VPC_ENV}'.'${DOMAIN_NAME}'",                                                    
                "Type": "A",                                                                                               
                "TTL" : 60,                                                                                                
                "ResourceRecords": [                                                                                       
                  { "Value": "'${IP_ADDRESS}'" }                                                                           
                ]                                                                                                          
              }                                                                                                            
            }                                                                                                              
          ]                                                                                                                
        }'
        aws route53 change-resource-record-sets --hosted-zone-id ${HOSTED_ZONE_ID}  --change-batch "${BATCH_JSON}"
        ;;
esac

exit

Hosted Zone を判別する

さて、今回、困ったのがこの部分。Route53 に自動登録をする際には、登録先の Hosted Zone IDを指定する必要があります。同じ AMI を利用してインスタンスを立ち上げつつ、デプロイされた環境に応じて登録して欲しいので、何らかの方法で この Hosted Zone IDを自動で取得する必要がありました。

ところが、Route53の list-hosted-zones を使って Hosted Zone のリストを取得してみても、どのVPCに紐づいているのかわからないのです。名前で判断しようにも、同じ 内部ドメイン名で作っているので判別しようがありません。

逆に、VPC側の attributes を取得しても、Hosted Zone がわかりません。

どうやら、Hosted Zone の詳細のところにしか、VPCの情報はないようです。

ということで、ちょっと面倒ですが、Hosted Zoneのリストを取得し、それぞれの Hosted Zone情報を取得した上で、自身のインスタンスが配置された VPC ID と比較することで、ようやく 登録先の Hosted Zone を判別することが出来ました。

ruby でのサンプルコードは下記のようになります。

sample.rb
#! /usr/bin/env ruby                                                                                               
require 'aws-sdk'
require 'net/http'

base_uri = URI.parse 'http://instance-data'
instance_id = az = ""

Net::HTTP.start(base_uri.host, base_uri.port) { |http|
    instance_id = http.get('/latest/meta-data/instance-id').body
    az = http.get('/latest/meta-data/placement/availability-zone').body
}
region = az[0..-2]

ec2 = Aws::EC2::Client.new(region: region)
instance = ec2.describe_instances({instance_ids:[instance_id]}).reservations.first.instances.first

route53 = Aws::Route53::Client.new(region: region)
hosted_zones = route53.list_hosted_zones.hosted_zones
hosted_zone_id = ""

hosted_zones.each do | hosted_zone |
  hosted_zone_id = hosted_zone.id
  if (vpcs = route53.get_hosted_zone(id: hosted_zone_id).vp_cs[0])
    if (vpcs.vpc_id) && (vpcs.vpc_id == instance.vpc_id)
      break;
    end
  end
end

hosted_zone_id = hosted_zone_id.gsub("/hostedzone/","")
puts "Hosted Zone ID: " + hosted_zone_id;

タグから 環境名やRoleを取得する

上記の sample.rb に加えて、下記のように VPCのタグから環境名、インスタンスのタグから Role を取得し、インスタンスのプライベートIPアドレスをあわせれば、DNS登録に必要な情報が揃います。

sample.rb
vpc = ec2.describe_vpcs(vpc_ids: [instance.vpc_id]).vpcs[0]
env = ""
vpc.tags.each do | tag |
  if (tag.key == 'env')
    env = tag.value
  end
end

role = ""
instance.tags.each do | tag |
  if (tag.key == 'role')
    role = tag.value
  end
end

puts "Env: " + env
puts "Role: " + role
puts "IP: " + instance.private_ip_address

まとめ

上記のような方法で、起動時に自動で Private DNSの情報や タグ名などを取得した上で、Route 53 に登録したり、シェルプロンプトを切り替えることが出来るようになります。

ついでに、踏み台サーバに fumidai.dev.example.com みたいな名前をつけておいたうえで、.ssh/config に下記みたいな定義を加えておけば、"ssh webapp-123.dev.example.com.internal" みたいな感じで、プライベートサブネットのインスタンスにログイン出来て便利です。

.ssh/config
Host *.dev.example.com.internal
     ProxyCommand ssh fumidai.dev.example.com nc %h 22

Host *.demo.example.com.internal
     ProxyCommand ssh fumidai.demo.example.com nc %h 22

いやぁ〜 便利な世の中になったもんですね!

13
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?