最近在玩 aws ec2 第一個一定會碰到的問題就是 ip 都是動態的,每次開機都不一樣。造成大部分的佈署方式會有問題,一般都是用動態 dns 來解決,原本想自己架 bind 或 djbdns ,但是架好以後還要處理動態 dns 更新的機制,於是把想法動到價格低廉的 route 53 身上,他有完整的 restful api 應該很符合我的需求。
找到這個 script 是利用 http://checkip.dyndns.com 來抓取自己 ip 再更新 route 53 的 A record 。
不過 ec2 的 public dns 有一個特性,從外面解會解到 public ip ,但是從裡面解會解到 private ip ,同一個 availability zone 用 private ip 互連是不多收費的。
如果設 A record 使用 public 連線,就沒有這個優勢了,所以我改用 CNAME 指到 ec2 的 public dns。然後原本取得 ip 的部分也改用 aws 取 meta-data 就可以了。
具體作法,可以開一個 subdomain 例如 ec2.hsatac.net ,然後把這個 subdomain delegate 給我們的 route 53 來解析。在 DNS 的部分新增一筆 NS ec2.hsatac.net
然後 server 設定為 route 53 給的那幾組即可。
修改過後的 script 如下:
#!/bin/sh
# Setup dynamic dns on Route 53 for aws ec2 (CNAME)
#
# Modified from Johan Lindh's script
#
# Script requirements:
#
# wget
# grep
# sed
# dig
# cut
# openssl
# base64
#
# Most if not all of these come standard on *nix distros.
#
# The domain and host name to update
# and the desired TTL of the record
Domain=your.domain.net
Hostname=`hostname`
NewTTL=600
# The Amazon Route 53 zone ID for the domain
# and the Amazon ID and SecretKey. Remember to
# ensure that this file can't be read by
# unauthorized people!
ZoneID=Z1234567890
AmazonID=A1234567890
SecretKey=GR$WYTJ%Y$@GY%J$%GY@H
# Enter the URL used to check extern IP
CheckIPURL='http://169.254.169.254/latest/meta-data/public-hostname'
# Enter some static text that immediately preceeds the current IP in the HTML output
# Note that you'll probably need to look at the actual HTML code to find this
CheckIPText='Current IP Address:'
###############################################################
# You should not need to change anything beyond this point
###############################################################
# Find an authoritative AWS R53 nameserver so we get a clean TTL
AuthServer=$(dig NS $Domain | grep -v ';' | grep -m 1 awsdns | grep $Domain | cut -f 6)
if [ "$AuthServer" = "" ]; then
echo The domain $Domain has no authoritative Amazon Route 53 name servers
exit 1
fi
# Get the record and extract its parts
Record=$(dig @$AuthServer A $Hostname.$Domain | grep -v ";" | grep "$Hostname\.$Domain")
OldType=$( echo $Record | cut -d ' ' -f 4 )
OldTTL=$( echo $Record | cut -d ' ' -f 2 )
OldIP=$( echo $Record | cut -d ' ' -f 5 | sed s/.$//)
# Make sure it is an A record (could be CNAME)
if [ "$Record" != "" -a "$OldType" != "CNAME" ]; then
echo $Hostname.$Domain has a $OldType record, expected 'CNAME'
exit 1
fi
# Retrieve the current IP
CurrentIP=$(wget "$CheckIPURL" -o /dev/null -O /dev/stdout)
# Changeset preamble
Changeset=""
Changeset=$Changeset"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
Changeset=$Changeset"<ChangeResourceRecordSetsRequest xmlns=\"https://route53.amazonaws.com/doc/2010-10-01/\">"
Changeset=$Changeset"<ChangeBatch><Comment>Update $Hostname.$Domain</Comment><Changes>"
if [ "$OldIP" != "" ]; then
# Add a DELETE request to the changeset
Changeset=$Changeset"<Change>"
Changeset=$Changeset"<Action>DELETE</Action>"
Changeset=$Changeset"<ResourceRecordSet>"
Changeset=$Changeset"<Name>$Hostname.$Domain.</Name>"
Changeset=$Changeset"<Type>CNAME</Type>"
Changeset=$Changeset"<TTL>$OldTTL</TTL>"
Changeset=$Changeset"<ResourceRecords>"
Changeset=$Changeset"<ResourceRecord>"
Changeset=$Changeset"<Value>$OldIP</Value>"
Changeset=$Changeset"</ResourceRecord>"
Changeset=$Changeset"</ResourceRecords>"
Changeset=$Changeset"</ResourceRecordSet>"
Changeset=$Changeset"</Change>"
fi
# Add CREATE request to the changeset
Changeset=$Changeset"<Change>"
Changeset=$Changeset"<Action>CREATE</Action>"
Changeset=$Changeset"<ResourceRecordSet>"
Changeset=$Changeset"<Name>$Hostname.$Domain.</Name>"
Changeset=$Changeset"<Type>CNAME</Type>"
Changeset=$Changeset"<TTL>$NewTTL</TTL>"
Changeset=$Changeset"<ResourceRecords>"
Changeset=$Changeset"<ResourceRecord>"
Changeset=$Changeset"<Value>$CurrentIP</Value>"
Changeset=$Changeset"</ResourceRecord>"
Changeset=$Changeset"</ResourceRecords>"
Changeset=$Changeset"</ResourceRecordSet>"
Changeset=$Changeset"</Change>"
# Close the changeset
Changeset=$Changeset"</Changes>"
Changeset=$Changeset"</ChangeBatch>"
Changeset=$Changeset"</ChangeResourceRecordSetsRequest>"
if [ "$OldIP" != "$CurrentIP" ]; then
# Get the date at the Amazon servers
CurrentDate=$(wget -q -S https://route53.amazonaws.com/date -O /dev/null 2>&1 | grep Date | sed 's/.*Date: //')
# Calculate the SHA1 hash and required headers
Signature=$(echo -n $CurrentDate | openssl dgst -binary -sha1 -hmac $SecretKey | base64)
DateHeader="Date: "$CurrentDate
AuthHeader="X-Amzn-Authorization: AWS3-HTTPS AWSAccessKeyId=$AmazonID,Algorithm=HmacSHA1,Signature=$Signature"
# Submit request
Result=$(wget -nv --header="$DateHeader" --header="$AuthHeader" --header="Content-Type: text/xml; charset=UTF-8" --post-data="$Changeset" -O /dev/stdout -o /dev/stdout https://route53.amazonaws.com/2010-10-01/hostedzone/$ZoneID/rrset)
if [ "$?" -ne "0" ]; then
echo "Failed to update $Hostname.$Domain: "$Result
fi
fi