Categories
Cloud Developer Tips

Using AWS Route 53 to Keep Track of EC2 Instances

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:

  1. Sign up to Amazon Route 53.
  2. Download and install cli53 from https://github.com/barnybug/cli53 (follow the instructions to download the latest Boto and dnspython)
  3. Set up a domain/subdomain you want to use for the mapping (e.g., ec2farm.mycompany.com):
    1. Set it up on Route53 using cli53:
      ./cli53.py create ec2farm.mycompany.com
    2. Use your domain provider’s interface to set Amazon’s DNS servers (reported in the response to the create command)
    3. 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.

18 replies on “Using AWS Route 53 to Keep Track of EC2 Instances”

The only problem with the script as found is that it only will register the instances in the default region. If you have instances in multiple aws regions, the following additions to the script will allow you to register instances in many regions to region-specific dns entries.

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”‘

Shlomo, you said: “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.”

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.

@Gene Vayngrib,

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.

@Shlomo Swidler sorry for mixup. Thx @Guy Rosen.
Yes, I know that a DNS query within EC2 must return private IP, beats me why it does not work for me this way. I just did a test again on two instances, one with elastic IP and another without, On both instances ping command returned public IP as soon as I removed a line in /etc/hosts. What gives? Could it be ping-specific or am I missing something obvious.

@Gene Vayngrib,

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?

@Shlomo Swidler
here is the log of experiment I did yesterday on a machine with elastic IP.

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 5004ms

thanks for your help

@Gene Vayngrib,

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.

@Shlomo Swidler,
aha, that explains it – CNAME to Amazon-owned A record allows EC2 DNS resolver re-map to internal IP address. So, I wonder if my vulcanbase.lablz.com A record was in Route 53, would that remove the need for CNAME?

@Gene Vayngrib,

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.

We had need of registering multiple ShortNames to any given instance, so I thought I would contribute back our solution (admittedly I don’t know perl so it may not be the best way to do what we did). This allows you to have the ‘ShortName’ tag and have a comma-separated list of values that are all registered to the given instance.

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 $0`
region=`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.com

not a dns guru, was wondering, would you be runing a security risk by advertising all your internal servers in your dns? is it posible that anyone can request the complete list of records you have in your dns?

@jim soho,

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, while that seems to be a useful tool it doesn’t solve the problem for me. I prefer to use public DNS because, once the instance registered, it doesn’t require any configuration or installation of software on any other hosts. Every OS has basic DNS resolution built-in.

Still, thanks for pointing it out. Definitely useful for some other tasks.

Leave a Reply

Your email address will not be published.