This article is a guest post by Guy Rosen, CEO of Onavo and author of the Jack of All Clouds blog. Guy was one of the first people to produce hard numbers on cloud adoption for site hosting, and he continues to publish regular updates to this research in his State of the Cloud series. These days he runs his startup Onavo which uses the cloud to offer smartphone users a way to slash overpriced data roaming costs. In this article, Guy provides another technique to track changes to your dynamic cloud services automatically, possible now that AWS has released Route 53, DNS services. Take it away, Guy.
While one of the greatest things about EC2 is the way you can spin up, stop and start instances to your heart’s desire, things get sticky when it comes to actually connecting to an instance. When an instance boots (or comes up after being in the Stopped state), Amazon assigns a pair of unique IPs (and DNS names) that you can use to connect: a private IP used when connecting from another machine in EC2, and a public IP is used to connect from the outside. The thing is, when you start and stop dozens of machines daily you lose track of these constantly changing IPs. How many of you have found, like me, that each time you want to connect to a machine (or hook up a pair of machines that need to communicate with each other, such as a web and database server) you find yourself going back to your EC2 console to copy and paste the IP?
This morning I got fed up with this, and since Amazon launched their new Route 53 service I figured the time was ripe to make things right. Here’s what I came up with: a (really) small script that takes your EC2 instance list and plugs it into DNS. You can then refer to your machines not by their IP but by their instance ID (which is preserved across stops and starts of EBS-backed instances) or by a user-readable tag you assign to a machine (such as “webserver”).
Here’s what you do:
- Sign up to Amazon Route 53.
- Download and install cli53 from https://github.com/barnybug/cli53 (follow the instructions to download the latest Boto and dnspython)
- Set up a domain/subdomain you want to use for the mapping (e.g., ec2farm.mycompany.com):
Set it up on Route53 using cli53:
./cli53.py create ec2farm.mycompany.comUse your domain provider’s interface to set Amazon’s DNS servers (reported in the response to the
createcommand)Run the following script (replace any details and paths, emphasized in bold, with your own):
#!/bin/tcsh -f set root=`dirname $0` setenv EC2_HOME /usr/local/ec2-api-tools setenv EC2_CERT $root/ec2_x509_cert.pem setenv EC2_PRIVATE_KEY $root/ec2_x509_private.pem setenv AWS_ACCESS_KEY_ID myawsaccesskeyid setenv AWS_SECRET_ACCESS_KEY mysecretaccesskey$EC2_HOME/bin/ec2-describe-instances | \ perl -ne '/^INSTANCE\s+(i-\S+).*?(\S+\.amazonaws\.com)/ \ and do { $dns = $2; print "$1 $dns\n" }; /^TAG.+\sShortName\s+(\S+)/ \ and print "$1 $dns\n"' | \ perl -ane 'print "$F[0] CNAME $F[1] --replace\n"' | \ xargs -n 4 $root/cli53/cli53.py \ rrcreate -x 60 ec2farm.mycompany.com
Voila! You now have DNS names such as i-abcd1234.ec2farm.mycompany.com that point to your instances. To make things more helpful, if you add a tag called ShortName to your instances it will be picked up, letting you create names such as dbserver2.ec2farm.mycompany.com. The script creates CNAME records, which means that you will automatically get internal EC2 IPs when querying inside EC2 and public IPs from the outside.
Put this script somewhere, run it in a cron - and you’ll have an auto-updating DNS zone for your EC2 servers.
Short disclaimer: the script above is a horrendous one-liner that roughly works and uses many assumptions, it works for me but no guarantees.
Comments
Boaz Ziniman —
Brian Armstrong —
This will give you entries like i-abcd1234.us-west-1.ec2farm.mycompany.com and i-abce1235.us-east-1.ec2farm.mycompany.com
set region=
ec2-metadata -z | cut -d ' ' -f 2 | sed -r -e 's/[a-z]+$//g'$EC2_HOME/bin/ec2-describe-instances –region “$region” |
perl -ne ‘/^INSTANCE\s+(i-\S+).*?(\S+.amazonaws.com)/
and do { $dns = $2; print “$1 $dns\n” }; /^TAG.+\sShortName\s+(\S+)/
and print “$1 $dns\n”’ |
perl -ane ‘print “$F[0].’$region’ CNAME $F[1] –replace\n”’
Gene Vayngrib —
Can you explain how CNAME helps get internal EC2 IP when querying inside EC2. We are now forced to do /etc/hosts modifications to achieve the effect you describe. Thank you.
shlomo —
Good question.
When queried from within EC2 by DNS name, public EC2 IP addresses resolve to the private IP address of the associated instance. This is by design, so there’s no reason to duplicate this functionality with /etc/hosts - which is harder to maintain when something changes.
See Eric Hammond’s article on this for a more detailed explanation: http://alestic.com/2009/06/ec2-elastic-ip-internal
BTW, Guy said that. And he’s correct.
Gene Vayngrib —
shlomo —
Can you remove all manually-added entries from your /etc/hosts file, run the pings, and paste in the commands you’re running and their output here?
Gene Vayngrib —
ubuntu@ip-10-250-67-32:~$ ping vulcanbase.lablz.com PING vulcanbase.lablz.com (10.250.67.32) 56(84) bytes of data. 64 bytes from vulcanbase.lablz.com (10.250.67.32): icmp_seq=1 ttl=64 time=0.032 ms 64 bytes from vulcanbase.lablz.com (10.250.67.32): icmp_seq=2 ttl=64 time=0.025 ms ^C — vulcanbase.lablz.com ping statistics — 2 packets transmitted, 2 received, 0% packet loss, time 1001ms rtt min/avg/max/mdev = 0.025/0.028/0.032/0.006 ms
here I went and commented out a line in /etc/hosts for vulcanbase.lablz.com#
ubuntu@ip-10-250-67-32:
$ sudo nano /etc/hosts ubuntu@ip-10-250-67-32:$ ping vulcanbase.lablz.com PING vulcanbase.lablz.com (75.101.163.142) 56(84) bytes of data. ^C — vulcanbase.lablz.com ping statistics — 6 packets transmitted, 0 received, 100% packet loss, time 5004msthanks for your help
shlomo —
How is the DNS entry for vulcanbase.lablz.com defined?
Gene Vayngrib —
shlomo —
You need to define the DNS record as a CNAME alias pointing to the DNS name of the Elastic IP address. Otherwise it will resolve to the address provided explicitly by the A record: the public IP address.
Gene Vayngrib —
shlomo —
As of today, Route 53 is pretty much standard DNS wrapped in an API. It doesn’t offer anything new for this scenario, so you’d need to use a CNAME alias in your own DNS zone.
Brian Armstrong —
It also includes the code from above to have region-specific entries for the DNS.
Sorry it runs in bash rather than tcsh.
#!/bin/bash root=
dirname $0region=ec2-metadata -z | cut -d' ' -f2 | sed -r -e 's/[a-z]+$//g'ec2-describe-instances –region “$region” “$instid” | perl -ne ‘/^INSTANCE\s+(i-\S+).*?(\S+.amazonaws.com)/ and do { $dns = $2; print “$1 $dns\n” }; /^TAG.+\sShortName\s+(\S+)/ and do { foreach my $tag (split(/,/, $1)) { print “$tag $dns\n” } }’ | perl -ane ‘print “$F[0].’$region’ CNAME $F[1]\n”’ | xargs -n 3 $root/cli53/cli53.py rrcreate –replace -x 60 ec2farm.mycompany.comjim soho —
shlomo —
Interesting question. Any IP address on the internet can - theoretically - be reached by anyone else on the internet, so there’s no new risk introduced by publishing a list of your addresses in DNS. Your machine should be locked down to accept only legitimate traffic anyway. And, if these internal servers that are advertised in DNS are registered using an IP address that is not internet-routable then it is, for all practical purposes, unreachable without access to a compromised machine.
Bottom line: I don’t think any new security risk is introduced by advertising the existence of internal servers.
Dan —
Martijn —
Still, thanks for pointing it out. Definitely useful for some other tasks.