ExternalDNS

repository·master·Indexed 11 days ago

https://github.com/kubernetes-sigs/external-dns

A Kubernetes controller that synchronizes resources like Services and Ingresses with external DNS providers (e.g., AWS Route 53, Google Cloud DNS) to make cluster resources discoverable via public DNS. It supports various sources including Gateway API, Istio, and Contour, and can be deployed via Helm with options for cluster-wide or namespace-scoped RBAC.

Tokens
160.9K
Snippets
381
Records
544
Agent score
91%

What's inside ExternalDNS

  1. What is ExternalDNS?

    master

    ExternalDNS is a Kubernetes controller that synchronizes exposed Kubernetes resources (such as Services and Ingresses) with external DNS providers (e.g., AWS Route 53, Google Cloud DNS).

    Unlike KubeDNS, which is a cluster-internal DNS server, ExternalDNS acts as a configuration engine that makes your Kubernetes resources discoverable via public DNS servers by dynamically managing DNS records in a provider-agnostic way.

  2. What is the DynamoDB registry?

    master
    The DynamoDB registry is an alternative to the default TXT registry. Instead of storing DNS record metadata in TXT records within a hosted zone, it stores this metadata in an AWS DynamoDB table. This approach can be used to manage DNS record ownership and metadata more efficiently than using TXT records.
  3. What is FQDN Templating?

    master
    FQDN templating allows you to dynamically construct Fully Qualified Domain Names (FQDNs) using a Go templating engine. Instead of using static names or annotations, you can programmatically generate DNS records using metadata from Kubernetes objects like Service or Ingress (e.g., names, namespaces, or labels). This is useful for maintaining consistent naming conventions, reducing annotation boilerplate, and supporting multi-tenant or dynamic environments.
  4. What is a registry in ExternalDNS

    master

    A registry is a mechanism used to persist metadata pertaining to DNS records. This metadata is critical for ExternalDNS to track which deployment owns which records, preventing multiple deployments from conflicting over the same DNS zone.

    To ensure correct ownership, you must specify a unique identifier for your deployment using the --txt-owner-id flag. This value must be unique to the specific deployment and should not change for the lifetime of that deployment. If you have multiple deployments in different clusters that share the same DNS zone, each must use a different --txt-owner-id.

  5. Filter which Gateways are matched for Routes

    master

    When processing a Route, ExternalDNS looks at status.parents to find matching Gateways. A parent is ignored if:

    • Its parentRef.group is not gateway.networking.k8s.io or its kind is not Gateway.
    • The --gateway-name flag is set and the parentRef.name does not match.
    • The --gateway-namespace flag is set and the parentRef.namespace does not match.
    • The --gateway-label-filter flag is set and the Gateway does not match the labels.
    • The Gateway does not exist or has not accepted the route.
  6. Handle State Conflicts and Ownership

    master

    External-dns operates by computing a plan to reconcile the desired state (Kubernetes resources) with the current state (DNS provider). If a provider returns a conflict error (e.g., HTTP 409), it indicates a state mismatch rather than a software bug.

    Key behaviors:

    • No Auto-Correction: External-dns does not automatically resolve arbitrary conflicts to avoid accidentally deleting records that services depend on. It drops some well-known invalid records (like CNAME self-references) but otherwise requires manual intervention.
    • Crashloop Amplification: If a conflict causes a hard exit, the resulting crashloop causes Kubernetes informers to perform full LIST calls on every restart, which can lead to Kubernetes API server throttling.

    How to resolve conflicts:

    1. Ensure Single Ownership: Verify only one external-dns instance owns a specific zone or record set. If using the TXT or DynamoDB registry, use a unique --txt-owner-id per instance and ensure --domain-filter scopes do not overlap.
    2. Direct Provider Cleanup: Manually remove or update conflicting records directly in your DNS provider's console.
    3. Validate Annotations: Check for invalid definitions, such as mixing CNAME records with A/AAAA records for the same hostname.
    4. Check Other Controllers: Ensure no other automation or controllers are writing to the same DNS zone.
    5. Emergency Scaling: If the state is highly inconsistent during a migration, scale external-dns to zero, reconcile the DNS state manually, and then scale it back up.
  7. Concept: Multiple targets per hostname

    master

    ExternalDNS supports defining multiple targets (IPs or Hostnames) within a single Kubernetes resource (such as an Ingress or Service). Instead of creating separate DNS records for each target, ExternalDNS aims to treat a single resource as the owner of a DNS name and manage all its associated targets as a single unit.

    Key Behaviors

    • Resource Ownership: A DNS record is owned by a specific Kubernetes resource. This is managed via TXT records that store a back-reference to the resource (e.g., "heritage=external-dns,external-dns/resource=ingress/default/my-ingress-object-name").
    • Conflict Resolution: If two different resources (e.g., Ingress A and Ingress B) request the same hostname but provide different targets, ExternalDNS will not allow both to create records. It will choose one (based on a predefined strategy) to prevent one resource from "hijacking" another's record.
    • Updates: DNS records are updated only if:
      • The owning resource's target list changes.
      • The owning resource is deleted (allowing the record to be reclaimed by another resource).
      • Other properties like TTL change.

    Provider Support

    If a specific DNS provider does not fully support multiple targets, the provider implementation is responsible for handling the change list. A common fallback strategy is to use only the first target in the list.

  8. How ExternalDNS determines DNS names

    master

    ExternalDNS uses three prioritized sources of information to decide which DNS name to create for a Kubernetes object:

    1. Object Specifications & Annotations:
      • Ingress: Uses the hosts specified in the Ingress object or the external-dns.kubernetes.io/hostname annotation. You can force the source using the external-dns.kubernetes.io/ingress-hostname-source annotation with values defined-hosts-only or annotation-only.
      • Services: Uses the external-dns.kubernetes.io/hostname annotation for the LoadBalancer IP, or the external-dns.kubernetes.io/internal-hostname annotation for the Service IP.
    2. Compatibility Mode: If the --compatibility flag is set (e.g., --compatibility={mate,molecule}), ExternalDNS parses annotations used by Zalando/Mate or wearemolecule/route53-kubernetes.
    3. FQDN Template: If the --fqdn-template flag is provided (e.g., --fqdn-template={{.Name}}.my-org.com), ExternalDNS generates the name using the provided template applied to the service/ingress specifications.
  9. Use Batch APIs to Reduce Rate Limiting

    master

    For zones with large or frequent change sets, individual per-record API calls can exhaust provider rate limits. Where supported, external-dns uses a batch API to group updates.

    Comparison:

    • Individual approach: API calls grow linearly with the number of records changed.
    • Batch approach: API calls grow with the number of batches, significantly reducing total volume.

    Error Handling: If a batch submission fails (e.g., due to one misconfigured record), providers typically fall back to individual per-record calls for that sync cycle, ensuring one bad record doesn't block updates for the rest of the zone.

  10. Handle Canonical Hosted Zones in AWS

    master

    When creating ALIAS type records in Route53, ExternalDNS must be aware of the canonical hosted zone. ExternalDNS automatically identifies canonical zones for many hostnames based on known suffixes.

    If a hostname uses an unknown suffix, you have two options:

    1. Add the suffix to the aws.go source code.
    2. Manually define the canonical hosted zone ID using the target-hosted-zone annotation on your resource.