45
votes

We're using Route 53 DNS to point to an EC2 instance. Is there any way to get Route 53 to point to the instance directly, instead of to an Elastic IP or CNAME?

I have multiple reasons for this:

  1. I don't want to burn an IP.

  2. CNAMEs are unreliable, because if an instance goes down and comes back up, the full name, ec2-X-X-X-X.compute-1.amazonaws.com, will change.

  3. In the future, I need to spin up instances programmatically and address them with a subdomain, and I see no easy way to do this with either elastic IPs or CNAMEs.

What's the best approach?

6
This probably won't get answered, but I'm interested in the answer too. If I own "example.com" and have my own DNS servers, can I direct "foo.example.com" to an EC2 instance?Edward Falk
Well, to answer my own meta-question, I assigned an elastic IP to my instance and then created an A record for it. You can also create a CNAME record for the instance's public DNS name, but that's not stable since the public DNS name changes when you stop and restart the instance.Edward Falk
Actually, once you assign an elastic IP, you can predict the public DNS name that will be created: ec2-{{Elastic IP}}.{{AWS AZ}}.compute.amazonaws.com. If you assign a cname to that DNS in route 53, it should always point correctly to the instance you assign the EIP to. This has the advantage of resolving to the internal EC2 ip when you are inside the EC2 network and the correct public Elastic IP when outside.jslatts

6 Answers

17
votes

I wrote my own solution to this problem since I was unhappy with other approaches that were presented here. Using Amazon CLI tools is nice, but they IMHO tend to be slower than direct API calls using other Amazon API libraries (Ruby for example).

Here's a link to my AWS Route53 DNS instance update Gist. It contains an IAM policy and a Ruby script. You should create a new user in IAM panel, update it with the attached policy (with your zone id in it) and set the credentials and parameters in the Ruby script. First parameter is the hostname alias for your instance in your hosted zone. Instance's private hostname is aliased to <hostname>.<domain> and instance's public hostname is aliased to <hostname>-public.<domain>

UPDATE: Here's a link to AWS Route53 DNS instance update init.d script registering hostnames when instance boots. Here's another one if want to use AWS Route53 DNS load-balancing in similar fashion.

5
votes

If you stick to using route53, you can make a script that updates the CNAME record for that instance everytime it reboots.

see this -> http://cantina.co/automated-dns-for-aws-instances-using-route-53/ (disclosure, i did not create this, though i used it as a jumping point for a similar situation)

better yet, because you mentioned being able to spin up instances programmatically, this approach should guide you to that end.

see also -> http://docs.pythonboto.org/en/latest/index.html

5
votes

Using a combination of Cloudwatch, Route53 and Lambda is also an option if you host at a least part of your dns in Route53. The advantage of this is that you don't need any applications running on the instance itself.

To use this this approach you configure a Cloudwatch rule to trigger a Lambda function whenever the status of an EC2 instance changes to running. The Lambda function can then retrieve the public ip address of the instance and update the dns record in Route53.

The Lambda could look something like this (using Node.js runtime):

var AWS = require('aws-sdk');

var ZONE_ID = 'Z1L432432423';
var RECORD_NAME = 'testaws.domain.tld';
var INSTANCE_ID = 'i-423423ccqq';

exports.handler = (event, context, callback) => {
    var retrieveIpAddressOfEc2Instance = function(instanceId, ipAddressCallback) {
        var ec2 = new AWS.EC2();

        var params = {
                InstanceIds: [instanceId]
        };

        ec2.describeInstances(params, function(err, data) {
            if (err) {
                callback(err);                
            } else {
                ipAddressCallback(data.Reservations[0].Instances[0].PublicIpAddress);
            }            
        });
    }

    var updateARecord = function(zoneId, name, ip, updateARecordCallback) {
        var route53 = new AWS.Route53();

        var dnsParams = {
                ChangeBatch: {
                    Changes: [
                        {
                            Action: "UPSERT", 
                            ResourceRecordSet: {
                                Name: name, 
                                ResourceRecords: [
                                    {
                                        Value: ip
                                    }
                                    ], 
                                    TTL: 60, 
                                    Type: "A"
                            }
                        }
                        ], 
                        Comment: "updated by lambda"
                },
                HostedZoneId: zoneId
        };

        route53.changeResourceRecordSets(dnsParams, function(err, data) {
            if (err) {
                callback(err, data);
            } else {
                updateARecordCallback();
            }            
        });
    }

    retrieveIpAddressOfEc2Instance(INSTANCE_ID, function(ip) {
        updateARecord(ZONE_ID, RECORD_NAME, ip, function() {
            callback(null, 'record updated with: ' + ip);
        });
    });
}

You will need to execute the Lambda with a role that has permissions to describe EC2 instances and update records in Route53.

1
votes

With Route 53 you can create alias records that map to an Elastic Load Balancer (ELB):

http://docs.amazonwebservices.com/Route53/latest/DeveloperGuide/HowToAliasRRS.html

0
votes

I've not tried on aws EC2 instance but it should work too. I've written a small Java program that detect the public IP of the machine and update a certain record on aws route 53.

The only requirement is that you need Java installed on your EC2 instance.

The project is hosted on https://github.com/renatodelgaudio/awsroute53 and you are also free to modify it in case you need it

You could configure it to run at boot time or as a crontab job so that your record get updated with the new public IP following instructions similar to these Linux manual installation steps

0
votes

I used this cli53 tool to let an EC2 instance create an A record for itself during startup.

https://github.com/barnybug/cli53

I added file following lines to my rc.local (please check your linux calls this script during startup):

IP=$(curl http://169.254.169.254/latest/meta-data/public-ipv4)
/usr/local/bin/cli53 rrcreate example.com "play 30 A $IP" --wait --replace

It creates an A record play.example.com pointing to the current public IP of the EC2 instance.

You need to assign a IAM role to EC2 instance, which allows the instance to manipulate Route 53. In the simplest case just create a IAM role using a predefined policy AmazonRoute53FullAccess. Then assign this role to the EC2 instance.