DNS Automation
DNS Automation (external-dns) keeps DNS records in sync with your apps. It runs ExternalDNS: a hostname on an Ingress, a Service or a Gateway API route becomes a record in your Route 53, Cloud DNS or Azure DNS zone, and the record goes when the resource goes. It writes with the cluster's own cloud identity, or with a Secret you create; AstroPulse never receives or stores a DNS credential.
When to use it
- You put apps on your domain. Without it, every hostname needs a record you create by hand and fix when the load balancer's address changes. A model behind an access policy waits meanwhile with "Waiting for DNS:
<hostname>does not resolve yet. If nothing manages its record, create one that points at your gateway's load balancer." With it, the record follows the app. - Your certificates come from Let's Encrypt over HTTP. Without a record, the certificate waits with "Waiting for the certificate: the hostname does not resolve yet, so Let's Encrypt cannot check it over HTTP. Nothing to do: cert-manager retries by itself once the DNS record is live." With it, the record is published and Certificates issues the certificate once the name resolves.
- You expose models through the Gateway. Turn on Gateway routes, and their hostnames get records like any other app.
- Several clusters share one zone. Each marks its records with its own owner ID and touches only those. See Record cleanup and ownership.
You don't need it when you create and keep every record yourself, or when no app is served on a domain.
Before you start
- A DNS zone in Route 53, Cloud DNS or Azure DNS, and its name for Domain filter, such as
example.com. - Access to that zone for the add-on. On an EKS cluster AstroPulse created, there is nothing to do. Anywhere else, set it up first: Give it access to your cloud.
In the console
After Version, its steps are Settings, then Advanced and Resources. Identity joins them once Cloud access is Workload identity. Review runs the dry run, and Ask Nova at the top of the steps answers about the open step.
-
Open the cluster from the cluster list, select the Add-ons tab, then Install Add-on, and pick DNS Automation.
-
Pick the Provider; it starts on the cluster's own cloud. Enter the Domain filter, such as
example.com, comma-separated for several zones.Open zoom view -
Optional: TXT owner ID, Policy, Dry run, Gateway routes, and Cloud access: Workload identity with its Identity fields, or Secret with the Secret ref it signs in with.
Open zoom view -
On Review, the dry run runs by itself; select Continue to deploy once it passes, then Deploy external-dns.
You should see
The add-on reads Deployed. Then check it can write:
astroctl infra k8s addons logs <cluster> external-dns --level Error --follow=false --interactive=false
An empty error log means it can reach your zone. A permission problem shows here as an error from your cloud's DNS service, such as AccessDenied, 403 or AuthorizationFailed. To publish one record by hand:
kubectl annotate service <service> external-dns.alpha.kubernetes.io/hostname=test.example.com
dig +short test.example.com
Within a minute or two the name resolves to the Service's address, with a TXT record beside it that marks it as this cluster's.
Give it access to your cloud
DNS Automation runs as service account external-dns in namespace external-dns. Everything below gives DNS permission to that one account and nothing else.
| Cluster | What to do |
|---|---|
| EKS, created by AstroPulse | Nothing. A Route 53 role for this add-on is created with the cluster and used automatically, limited to your Domain filter, or to your organization's verified domains when you set none |
| GKE or AKS, created by AstroPulse | Set it up yourself, below. AstroPulse creates no DNS access on these clouds, and the install is refused until you name an identity or a secretRef |
| Registered, any cloud | Set it up yourself, below. On AWS the install is refused until you name an identity or a secretRef |
An identity or secretRef you write always wins over the automatic one. The rules every add-on shares are in Cloud access for add-ons. On EKS and GKE, the cluster needs workload identity first: Enable workload identity on your cluster.
EKS
Give external-dns an IAM role that can change records in your hosted zone, and name it in identity. Full recipe: Let DNS Automation change records in one Route 53 zone on EKS.
GKE
Name a Google service account that can write the zone (roles/dns.admin) in identity, under the annotation iam.gke.io/gcp-service-account, and let external-dns/external-dns act as it through Workload Identity. Full recipe: Let DNS Automation change records in Cloud DNS on GKE.
AKS
Name a managed identity with DNS Zone Contributor on the zone in identity: its client ID under the annotation azure.workload.identity/client-id, and the label azure.workload.identity/use: "true", with a federated credential for external-dns/external-dns. Or use a secretRef holding an azure.json for a service principal that can write the zone (below). Full recipe: Let DNS Automation change records in one Azure DNS zone on AKS.
A zone in another cloud
When the zone is in another cloud than the cluster, or the cluster has no workload identity, use secretRef. It keeps a long-lived credential on the cluster: use one that can change records in that one zone, and rotate it. A DNS host other than Route 53, Cloud DNS or Azure DNS is not supported by this add-on.
Letting external-dns read a credential from a Secret
identity binds a cloud workload identity by its annotations (and, on Azure, labels), and nothing secret is stored:
apiVersion: platform.astropulse.io/v1
kind: K8sClusterAddon
metadata:
name: external-dns
spec:
clusterName: eks-prod
configuration:
externalDns:
provider: aws
domainFilter: example.com
identity:
serviceAccountAnnotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/dns-writer
secretRef is the name of a Secret you create yourself in the external-dns namespace, before you apply the add-on. AstroPulse stores the name and never reads the Secret:
apiVersion: platform.astropulse.io/v1
kind: K8sClusterAddon
metadata:
name: external-dns
spec:
clusterName: eks-prod
configuration:
externalDns:
provider: gcp
domainFilter: example.com
secretRef: clouddns-credentials
provider | Key the Secret holds |
|---|---|
aws | credentials |
gcp | credentials.json |
azure | azure.json |
Set one of identity and secretRef, never both. An identity only binds on the cluster's own cloud.
Security: with identity, no DNS key exists on the cluster to leak or rotate, and your cloud revokes it in one place. With secretRef, AstroPulse stores only the Secret's name and never reads it. The role AstroPulse creates on EKS may change records only under your Domain filter, or your verified domains when you set none.
Record cleanup and ownership
With Policy Sync (what a new add-on runs), DNS Automation creates, updates and deletes records to match the cluster: when an app goes, its record goes. It touches only records it owns, which it marks with a TXT record carrying its TXT owner ID.
- Shared zone, no deletes.
UpsertOnlycreates and updates but never deletes, for a zone that also holds records you keep by hand. A stale record then keeps pointing at an address your cloud can give to someone else, so remove those yourself. - One owner per cluster. Two clusters with the same owner ID on one zone delete each other's records. Left empty, a first install takes the cluster's name followed by a suffix unique to the cluster, and keeps it.
- Keep the owner of a running install. Records it already owns would no longer be managed.
Put the hostname on an Ingress host, on a Service with the external-dns.alpha.kubernetes.io/hostname annotation, or on an HTTPRoute with Gateway routes on. It must fall under the Domain filter, or your verified domains when you set none.
Run it in production
- One copy, by design. It has no leader election, so it runs one pod. While it is down, records stop updating but are not deleted; it catches up when it returns.
- It checks every minute. A new or changed application's record appears within about a minute of its address.
- Who owns which record decides what it can delete: Record cleanup and ownership. Use
UpsertOnlyon a zone that other tools also write to.
Other ways in
- Nova: ask it, for example "install DNS Automation for example.com on eks-prod in dry-run mode".
- SDK and API: the same operations, in the API reference.
With astroctl
apiVersion: platform.astropulse.io/v1
kind: K8sClusterAddon
metadata:
name: external-dns
spec:
clusterName: eks-prod
configuration:
externalDns:
domainFilter: example.com
dryRun: true
astroctl infra k8s addons apply -f external-dns.yaml --dry-run
astroctl infra k8s addons apply -f external-dns.yaml
With dryRun: true it logs what it would publish and writes nothing. Confirm from the logs that it sees your zone, then apply again with dryRun off.
Settings
Its own settings, under externalDns, plus availability and resources.
externalDns.providerThe DNS service to publish through. Left out, it is the cluster's own cloud. Required on a registered cluster whose cloud AstroPulse does not know.
awsAWS Route 53 Publishes to Amazon Route 53, as an IAM role that may change records in the zone.gcpGoogle Cloud DNS Publishes to Cloud DNS, as a Google service account with the DNS Administrator role.azureAzure DNS Publishes to Azure DNS, as an identity with the DNS Zone Contributor role.
externalDns.domainFilterThe DNS zone it may write to, such as example.com, comma-separated for several. A suffix, not a pattern.
externalDns.txtOwnerIdMarks the records this cluster owns, so clusters sharing a zone don't delete each other's.
externalDns.policyWhat it may do to the records it owns. Left out, an update keeps the current one and a new add-on runs Sync.
SyncSync Creates, updates and deletes the records it owns.UpsertOnlyUpsert only Creates and updates records but never deletes them, so stale records stay for you to remove.
externalDns.dryRunLogs what it would publish without publishing it.
externalDns.gatewayRoutesAlso publishes the hostnames of Gateway API HTTPRoutes, beside the Ingress hostnames. Needs the Gateway API CRDs on the cluster.
externalDns.identityShown when Cloud access is Workload identity. A cloud workload identity that may change records in your zone: serviceAccountAnnotations on its service account, and serviceAccountLabels on its pods.
externalDns.secretRefShown when Cloud access is Secret. The name of a Secret in the external-dns namespace holding DNS credentials, where a workload identity can't be used. Not with identity.
If it's refused
configuration.externalDns.identity.serviceAccountAnnotations to the IAM role for service accounts it should run as ..."identity or secretRef.identity, or a Secret in secretRef, as the message says.identity or secretRef.identity, or a Secret in secretRef.identity for a zone in another cloud.secretRef for that zone.provider.aws, gcp or azure, or leave it out.provider.