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
Verify that your credentials are correctly configured:
aws sts get-caller-identity
Next, create a Hosted Zone in AWS Route53:
aws route53 create-hosted-zone --name example-tutorial.com --caller-reference external-dns-tutorial
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
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
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.]"
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
Apply the manifest:
kubectl apply -f test.example-tutorial.com.yaml
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
You should see:
test.example-tutorial.com. 180 IN A 127.0.0.1
3. Checking TXT Records
Verify the TXT record:
dig @ns-1694.awsdns-19.co.uk +noall +answer -t TXT test.example-tutorial.com
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"
This TXT record contains management information:
-
heritage=external-dns
- Indicates the record is managed by external-dns
-
external-dns/owner=EXAMPLE-ZONE-ID123
- OwnerID identifying specific external-dns instance
- Prevents conflicts between multiple instances
-
external-dns/resource=crd/default/test.example-tutorial.com
- References the source Kubernetes resource
- In this case, the CRD
test.example-tutorial.com
in thedefault
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
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.
Verify that the DNS records are deleted:
dig @ns-1694.awsdns-19.co.uk +noall +answer -t A test.example-tutorial.com
Removing external-dns
Delete the Helm release:
helm uninstall external-dns
3. Removing the Hosted Zone
First, list your hosted zones:
aws route53 list-hosted-zones
Then delete the zone using its ID:
aws route53 delete-hosted-zone --id Z08033563HFN15GSXJ766
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!
Top comments (0)