Automated DNS Record Management for Kubernetes Resources using external-dns and AWS Route53

suin - Nov 6 - - Dev Community

When operating applications on Kubernetes, managing DNS records associated with Services and Ingress resources becomes necessary. Automating the creation, updating, and deletion of these DNS records can significantly reduce operational overhead.

In this article, I'll explain how to use "external-dns" to automatically create and manage AWS Route53 DNS records from Kubernetes resources.

While Crossplane is another option for automating record management, it requires creating composition mapping rules for each cloud vendor. external-dns, on the other hand, provides a unified approach for various nameservers beyond AWS Route53, making it particularly useful when you need to support multiple DNS providers.

Prerequisites

  • A configured Kubernetes cluster
  • Required command-line tools:
    • kubectl
    • helm
    • aws
    • dig

Understanding external-dns and AWS Route53

What is external-dns?

external-dns is a tool that monitors Kubernetes resources and automatically updates DNS records. It watches resources like Services, Ingress, and Gateway, creating, updating, and deleting DNS records based on configuration.

What is AWS Route53?

AWS Route53 is Amazon's managed DNS service. It offers high availability and scalability, providing features like multiple routing policies and health checks.

Setup Guide

1. Creating an AWS Route53 Hosted Zone

First, set your AWS credentials as environment variables:

export AWS_ACCESS_KEY_ID=AKIAXXXXXXXXXXXXXXXX
export AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Enter fullscreen mode Exit fullscreen mode

Verify that your credentials are correctly configured:

aws sts get-caller-identity
Enter fullscreen mode Exit fullscreen mode

Next, create a Hosted Zone in AWS Route53:

aws route53 create-hosted-zone --name example-tutorial.com --caller-reference external-dns-tutorial
Enter fullscreen mode Exit fullscreen mode

2. Installing external-dns

While kubernetes-sigs provides a Helm chart for external-dns, we'll use the one from Bitnami as it offers more options and simplifies AWS Route53 configuration.

helm install external-dns \
  --set provider=aws \
  --set aws.zoneType=public \
  --set aws.credentials.accessKey="$AWS_ACCESS_KEY_ID" \
  --set aws.credentials.secretKey="$AWS_SECRET_ACCESS_KEY" \
  --set txtOwnerId=EXAMPLE-ZONE-ID123 \
  --set "domainFilters[0]=example-tutorial.com" \
  --set policy=sync \
  --set "sources[0]=crd" \
  --set crd.create=true \
  --set crd.apiversion=externaldns.k8s.io/v1alpha1 \
  --set crd.kind=DNSEndpoint \
  oci://registry-1.docker.io/bitnamicharts/external-dns
Enter fullscreen mode Exit fullscreen mode

Installation Options Explained

Option Description
provider=aws Specifies AWS Route53 as the provider
aws.zoneType=public Specifies the use of public zones
aws.credentials.accessKey AWS access key
aws.credentials.secretKey AWS secret key
txtOwnerId TXT record owner ID
domainFilters[0] Domain to monitor
policy=sync DNS record synchronization policy
sources[0]=crd Specifies CRD usage
crd.create=true Enables CRD creation
crd.apiversion CRD API version
crd.kind CRD type

After installation, verify that external-dns is running properly:

kubectl logs -l app.kubernetes.io/name=external-dns -f
Enter fullscreen mode Exit fullscreen mode

You should see logs similar to this:

time="2024-11-06T05:12:29Z" level=info msg="Instantiating new Kubernetes client"
time="2024-11-06T05:12:29Z" level=info msg="Using inCluster-config based on serviceaccount-token"
time="2024-11-06T05:12:29Z" level=info msg="Created Kubernetes client https://10.43.0.1:443"
time="2024-11-06T05:12:31Z" level=info msg="Applying provider record filter for domains: [example-tutorial.com. .example-tutorial.com.]"
Enter fullscreen mode Exit fullscreen mode

Creating Records with external-dns

Let's verify that external-dns creates DNS records from Kubernetes resources.

While external-dns monitors various resources like Services, Ingress, and Gateway, we'll use the DNSEndpoint CRD for this demo as it doesn't require any underlying resources.

1. Creating a Test Record

Create a manifest for a DNS record:

apiVersion: externaldns.k8s.io/v1alpha1
kind: DNSEndpoint
metadata:
  name: test.example-tutorial.com
spec:
  endpoints:
  - dnsName: test.example-tutorial.com
    recordTTL: 180
    recordType: A
    targets:
    - 127.0.0.1
Enter fullscreen mode Exit fullscreen mode

Apply the manifest:

kubectl apply -f test.example-tutorial.com.yaml
Enter fullscreen mode Exit fullscreen mode

2. Verifying DNS Records

Check the created DNS record. Replace @ns-1694.awsdns-19.co.uk with your Route53 nameserver hostname from your Hosted Zone.

First, check the A record:

dig @ns-1694.awsdns-19.co.uk +noall +answer -t A test.example-tutorial.com
Enter fullscreen mode Exit fullscreen mode

You should see:

test.example-tutorial.com. 180  IN      A       127.0.0.1
Enter fullscreen mode Exit fullscreen mode

3. Checking TXT Records

Verify the TXT record:

dig @ns-1694.awsdns-19.co.uk +noall +answer -t TXT test.example-tutorial.com
Enter fullscreen mode Exit fullscreen mode

Result:

test.example-tutorial.com. 300  IN      TXT     "heritage=external-dns,external-dns/owner=EXAMPLE-ZONE-ID123,external-dns/resource=crd/default/test.example-tutorial.com"
Enter fullscreen mode Exit fullscreen mode

This TXT record contains management information:

  1. heritage=external-dns
    • Indicates the record is managed by external-dns
  2. external-dns/owner=EXAMPLE-ZONE-ID123
    • OwnerID identifying specific external-dns instance
    • Prevents conflicts between multiple instances
  3. external-dns/resource=crd/default/test.example-tutorial.com
    • References the source Kubernetes resource
    • In this case, the CRD test.example-tutorial.com in the default namespace

While this management information is publicly visible in TXT records, encryption options are available. I'll explore these features in a future article.

Cleanup

Deleting Records

To remove DNS records, delete the CRD resource:

kubectl delete -f test.example-tutorial.com.yaml
Enter fullscreen mode Exit fullscreen mode

external-dns will remove the DNS records shortly. Check the logs:

time="2024-11-06T05:48:24Z" level=info msg="Desired change:  DELETE  a-test.example-tutorial.com TXT" profile=default zoneID=/hostedzone/Z08033563HFN15GSXJ766 zoneName=example-tutorial.com.
time="2024-11-06T05:48:24Z" level=info msg="Desired change:  DELETE  test.example-tutorial.com A" profile=default zoneID=/hostedzone/Z08033563HFN15GSXJ766 zoneName=example-tutorial.com.
time="2024-11-06T05:48:24Z" level=info msg="Desired change:  DELETE  test.example-tutorial.com TXT" profile=default zoneID=/hostedzone/Z08033563HFN15GSXJ766 zoneName=example-tutorial.com.
time="2024-11-06T05:48:24Z" level=info msg="3 record(s) were successfully updated" profile=default zoneID=/hostedzone/Z08033563HFN15GSXJ766 zoneName=example-tutorial.com.
Enter fullscreen mode Exit fullscreen mode

Verify that the DNS records are deleted:

dig @ns-1694.awsdns-19.co.uk +noall +answer -t A test.example-tutorial.com
Enter fullscreen mode Exit fullscreen mode

Removing external-dns

Delete the Helm release:

helm uninstall external-dns
Enter fullscreen mode Exit fullscreen mode

3. Removing the Hosted Zone

First, list your hosted zones:

aws route53 list-hosted-zones
Enter fullscreen mode Exit fullscreen mode

Then delete the zone using its ID:

aws route53 delete-hosted-zone --id Z08033563HFN15GSXJ766
Enter fullscreen mode Exit fullscreen mode

Conclusion

That's it! In this article, we covered how to automatically manage DNS records from Kubernetes resources using external-dns and AWS Route53.

While there are additional considerations for production use, I hope this tutorial serves as a helpful starting point for working with external-dns!

. . . . . . . . . . . . . . .
Terabox Video Player