ExternalDNS is the unsung hero that bridges your Kubernetes cluster’s internal service discovery with the outside world’s DNS resolution, automatically updating AWS Route 53 records as your services change.
Let’s see it in action. Imagine you have a Kubernetes Service of type LoadBalancer for your web application.
apiVersion: v1
kind: Service
metadata:
name: my-webapp
namespace: default
spec:
selector:
app: webapp
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer
When Kubernetes provisions a load balancer (in AWS, this means an ELB or NLB), it gets an external DNS name like a1b2c3d4e5f67890-abcdef.us-east-1.elb.amazonaws.com. If ExternalDNS is configured to watch this Service and is pointed at your Route 53 hosted zone for example.com, it will automatically create a DNS record, say webapp.example.com, pointing to that ELB’s DNS name.
Here’s a simplified view of the ExternalDNS deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
namespace: kube-system
spec:
strategy:
type: Recreate
template:
metadata:
labels:
app: external-dns
spec:
containers:
- name: external-dns
image: k8s.gcr.io/external-dns/external-dns:v0.10.0 # Use a specific version
args:
- --source=service
- --source=ingress
- --domain-filter=example.com
- --provider=aws
- --aws-zone-type=public # Or 'private' if applicable
- --policy=upsert-only # Or 'sync'
- --aws-prefer-cname=false # Or true if you want CNAMEs for Services
# Add your AWS credentials configuration here (e.g., IAM role for Service Accounts)
The source arguments tell ExternalDNS what Kubernetes objects to monitor. service means it will look at Service objects, and ingress means it will watch Ingress resources. The domain-filter restricts which DNS zones it will manage. provider=aws specifies the cloud provider, and aws-zone-type is crucial for selecting between public and private hosted zones in Route 53. The policy determines how it reconciles DNS records: upsert-only adds or updates, while sync also removes records that are no longer managed by ExternalDNS.
The core problem ExternalDNS solves is the disconnect between ephemeral, dynamically provisioned cloud resources (like load balancers) and the stable, globally resolvable DNS names required to access them. Without it, you’d be manually creating and updating Route 53 records every time a service is deployed, scaled, or its underlying load balancer changes. This is error-prone and doesn’t scale.
Internally, ExternalDNS works by:
- Watching Kubernetes API: It subscribes to events for
ServiceandIngressobjects. - Extracting DNS Information: For Services of type
LoadBalancer, it looks for anexternal-dns.alpha.kubernetes.io/hostnameannotation (or uses the service name and domain filter). For Ingresses, it uses thespec.rules[*].hostfield. - Querying Route 53: It checks your Route 53 hosted zone for existing records matching the desired DNS names.
- Comparing and Reconciling: It compares the desired state (based on Kubernetes objects) with the actual state in Route 53 and makes API calls to
ChangeResourceRecordSetsto add, update, or delete records as needed.
The aws-prefer-cname flag is a nuanced but important detail. When false (the default for provider=aws), ExternalDNS will create A records for Services of type LoadBalancer that point directly to the IP address of the load balancer. If true, it will attempt to create CNAME records that point to the load balancer’s DNS name. For Ingresses, it typically uses A records (or ALIAS records when targeting AWS resources like ELBs/ALBs). Using ALIAS records is generally preferred for AWS resources as they behave like A records but can point to other AWS resources without needing to know their IP addresses, and they don’t incur additional DNS query costs.
When you annotate a Service with external-dns.alpha.kubernetes.io/hostname: app.example.com, ExternalDNS will prioritize this specific hostname over generating one from the service name and domain filter. This gives you fine-grained control over how your services are exposed.
The next concept you’ll likely grapple with is managing DNS for services exposed via Ingress controllers, which often involves different annotation strategies and considerations for TLS certificates.