Skip to content

Commit

Permalink
support hostnames as endpoint targets (CNAME support) (#122)
Browse files Browse the repository at this point in the history
* feat(aws): support hostnames as endpoint targets

* docs: describe how to run ExternalDNS on AWS

* docs: update changelog with CNAME feature

* docs: update changelog to include AWS documentation

* fix(aws): test that updating records removes the old value

* feat(google): add CNAME support to Google provider

* fix(source): sanitize source and target hostnames

* docs: update changelog to include latest changes

* docs(aws): mention that ExternalDNS takes full ownership of a hosted zone

* fix(aws): switch route53 tests to use endpoint pointers

* docs: add TODO to remove record filtering once ownership is in place
  • Loading branch information
linki committed Apr 5, 2017
1 parent 5c794e1 commit b0f437a
Show file tree
Hide file tree
Showing 14 changed files with 546 additions and 47 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
Features:

- Support creation of CNAME records when endpoint target is a hostname.
- Allow omitting the trailing dot in Service annotations.

Bug fixes:

- AWS Route 53: Do not submit request when there are no changes.

Documentation:

- Add documentation on how to setup ExternalDNS for Services on AWS.

## v0.1.0 - 2017-03-30 (KubeCon)

Features:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ $ kubectl run nginx --image=nginx --replicas=1 --port=80
$ kubectl expose deployment nginx --port=80 --target-port=80 --type=LoadBalancer
```

Annotate the Service with your desired external DNS name. Make sure to change `example.org` to your domain (and that it includes the trailing dot):
Annotate the Service with your desired external DNS name. Make sure to change `example.org` to your domain.

```console
$ kubectl annotate service nginx "external-dns.alpha.kubernetes.io/hostname=nginx.example.org."
Expand Down
152 changes: 152 additions & 0 deletions docs/tutorials/aws.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Setting up ExternalDNS for Services on AWS

This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster on AWS.

Create a DNS zone which will contain the managed DNS records.

```console
$ aws route53 create-hosted-zone --name "external-dns-test.teapot.zalan.do." --caller-reference "external-dns-test-$(date +%s)"
```

Make a note of the ID of the hosted zone you just created.

```console
$ aws route53 list-hosted-zones-by-name --dns-name "external-dns-test.teapot.zalan.do." | jq -r '.HostedZones[0].Id'
/hostedzone/Z16P7IEWFWZ4RB
```

Make a note of the nameservers that were assigned to your new zone.

```console
$ aws route53 list-resource-record-sets --hosted-zone-id "/hostedzone/Z16P7IEWFWZ4RB" \
--query "ResourceRecordSets[?Type == 'NS']" | jq -r '.[0].ResourceRecords[].Value'
ns-1455.awsdns-53.org.
ns-1694.awsdns-19.co.uk.
ns-764.awsdns-31.net.
ns-62.awsdns-07.com.
```

In this case it's the ones shown above but your's will differ.

If you decide not to create a new zone but reuse an existing one, make sure it's currently **unused** and **empty**. This version of ExternalDNS will remove all records it doesn't recognize from the zone.

Connect your `kubectl` client to the cluster you want to test ExternalDNS with.
Then apply the following manifest file to deploy ExternalDNS.

```yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: external-dns
spec:
strategy:
type: Recreate
template:
metadata:
labels:
app: external-dns
spec:
containers:
- name: external-dns
image: registry.opensource.zalan.do/teapot/external-dns:v0.2.0-beta.0
args:
- --in-cluster
- --zone=external-dns-test.teapot.zalan.do.
- --source=service
- --provider=aws
- --dry-run=false
```

Create the following sample application to test that ExternalDNS works.

```yaml
apiVersion: v1
kind: Service
metadata:
name: nginx
annotations:
external-dns.alpha.kubernetes.io/hostname: nginx.external-dns-test.teapot.zalan.do.
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 80
selector:
app: nginx

---

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx
spec:
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
ports:
- containerPort: 80
```

After roughly two minutes check that a corresponding DNS record for your service was created.

```console
$ aws route53 list-resource-record-sets --hosted-zone-id "/hostedzone/Z16P7IEWFWZ4RB" \
--query "ResourceRecordSets[?Name == 'nginx.external-dns-test.teapot.zalan.do.']|[?Type == 'CNAME']"
[
{
"ResourceRecords": [
{
"Value": "ae11c2360188411e7951602725593fd1-1224345803.eu-central-1.elb.amazonaws.com"
}
],
"Type": "CNAME",
"Name": "nginx.external-dns-test.teapot.zalan.do.",
"TTL": 300
}
]
```

Let's check that we can resolve this DNS name. We'll ask the nameservers assigned to your zone first.

```console
$ dig +short @ns-1455.awsdns-53.org. nginx.external-dns-test.teapot.zalan.do.
ae11c2360188411e7951602725593fd1-1224345803.eu-central-1.elb.amazonaws.com.
```

If you hooked up your DNS zone with its parent zone correctly you can use `curl` to access your site.

```console
$ curl nginx.external-dns-test.teapot.zalan.do.
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
</head>
<body>
...
</body>
</html>
```

Ingress objects on AWS require a separately deployed Ingress controller which we'll describe in another tutorial.

## Clean up

Make sure to delete all Service objects before terminating the cluster so all load balancers get cleaned up correctly.

```console
$ kubectl delete service nginx
```

Give ExternalDNS some time to clean up the DNS records for you. Then delete the hosted zone.

```console
$ aws route53 delete-hosted-zone --id /hostedzone/Z16P7IEWFWZ4RB
```
10 changes: 5 additions & 5 deletions docs/tutorials/gke.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Setting up ExternalDNS on Google Container Engine

This tutorial describes how to setup `external-dns` for usage within a GKE cluster.
This tutorial describes how to setup ExternalDNS for usage within a GKE cluster.

Setup your environment to work with Google Cloud Platform. Fill in your values as needed, e.g. target project.

Expand Down Expand Up @@ -48,15 +48,15 @@ $ gcloud dns record-sets transaction add ns-cloud-e{1..4}.googledomains.com. \
$ gcloud dns record-sets transaction execute --zone "gcp-zalan-do"
```

If you decide not to create a new zone but reuse an existing one, make sure it's currently **unused** and **empty**. This version of `external-dns` will remove all records it doesn't recognize from the zone.
If you decide not to create a new zone but reuse an existing one, make sure it's currently **unused** and **empty**. This version of ExternalDNS will remove all records it doesn't recognize from the zone.

Connect your `kubectl` client to the cluster you just created.

```console
gcloud container clusters get-credentials "external-dns"
```

Apply the following manifest file to deploy `external-dns`.
Apply the following manifest file to deploy ExternalDNS.

```yaml
apiVersion: extensions/v1beta1
Expand Down Expand Up @@ -86,7 +86,7 @@ spec:

Use `dry-run=true` if you want to be extra careful on the first run. Note, that you will not see any records created when you are running in dry-run mode. You can, however, inspect the logs and watch what would have been done.

Create the following sample application to test that `external-dns` works.
Create the following sample application to test that ExternalDNS works.

```yaml
apiVersion: v1
Expand Down Expand Up @@ -216,7 +216,7 @@ $ kubectl delete service nginx
$ kubectl delete ingress nginx
```

Give `external-dns` some time to clean up the DNS records for you. Then delete the managed zone and cluster.
Give ExternalDNS some time to clean up the DNS records for you. Then delete the managed zone and cluster.

```console
$ gcloud dns managed-zones delete "external-dns-test-gcp-zalan-do"
Expand Down
9 changes: 7 additions & 2 deletions provider/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,12 @@ func (p *AWSProvider) Records(zone string) ([]*endpoint.Endpoint, error) {

f := func(resp *route53.ListResourceRecordSetsOutput, lastPage bool) (shouldContinue bool) {
for _, r := range resp.ResourceRecordSets {
if aws.StringValue(r.Type) != route53.RRTypeA {
// TODO(linki, ownership): Remove once ownership system is in place.
// See: https://github.com/kubernetes-incubator/external-dns/pull/122/files/74e2c3d3e237411e619aefc5aab694742001cdec#r109863370
switch aws.StringValue(r.Type) {
case route53.RRTypeA, route53.RRTypeCname:
break
default:
continue
}

Expand Down Expand Up @@ -255,7 +260,7 @@ func newChange(action string, endpoint *endpoint.Endpoint) *route53.Change {
},
},
TTL: aws.Int64(300),
Type: aws.String(route53.RRTypeA),
Type: aws.String(suitableType(endpoint.Target)),
},
}

Expand Down
Loading

0 comments on commit b0f437a

Please sign in to comment.