This topic explains how to deploy ExternalDNS into a Tanzu Kubernetes (workload) or shared services cluster in Tanzu Kubernetes Grid. The procedures below apply to vSphere, Amazon EC2, and Azure deployments.

ExternalDNS

The ExternalDNS service publishes DNS records for applications to DNS servers, using a declarative, Kubernetes-native interface. It is packaged as a user-managed package in Tanzu Kubernetes Grid.

In environments where Harbor is deployed in a shared services cluster with load balancing (AWS, Azure, and vSphere with NSX Advanced Load Balancer), ExternalDNS may be used to publish a DNS hostname for the Harbor service. This provides access to Harbor from other clusters. For more information, see Harbor Registry and ExternalDNS.

Prerequisites

  • You have installed the Tanzu CLI, kubectl, and the Carvel tools. For instructions, see Install the Tanzu CLI and Other Tools.
  • You have deployed a management cluster on vSphere, Amazon EC2, or Azure, in either an Internet-connected or Internet-restricted environment. If you are using Tanzu Kubernetes Grid in an Internet-restricted environment, you performed the procedure in Deploying Tanzu Kubernetes Grid in an Internet-Restricted Environment before you deployed the management cluster.
  • You have determined the Fully Qualified Domain Names (FQDNs) for the services you want to expose with ExternalDNS to your DNS server.

Prepare the Cluster for ExternalDNS Deployment

The ExternalDNS service must be deployed into the same cluster as the services for which it will export DNS records.

  • If you are deploying ExternalDNS into a shared services cluster with Harbor, ensure that you have completed all of the steps in Create a Shared Services Cluster. To install Harbor, follow the prerequisites and procedure in Deploy Harbor Registry as a Shared Service.
  • ExternalDNS supports creating records for both Kubernetes Services and Contour HTTPProxy resources. If you want to create records for Contour HTTPProxy resources in a workload cluster, you must install the Contour package in the cluster. For instructions, see Implementing Ingress Control with Contour. The Contour package is also required by Harbor.

Prepare the Configuration File for the ExternalDNS Package

The ExternalDNS package has been validated with AWS (Route 53), Azure DNS, and RFC2136 (BIND). The configuration provided below exports records for both Contour HTTPProxy resources and Kubernetes Services of type LoadBalancer.

AWS (Route 53)

  1. Create a hosted zone within Route 53 with the domain that your services will be using.
  2. Record the Hosted zone ID. You will use this ID later, when configuring ExternalDNS.
  3. Create an IAM policy for ExternalDNS that allows ExternalDNS to update Route 53. In the AWS Console, go to the IAM dashboard and under Access Management, navigate to the Policies screen. Click Create Policy and switch to the JSON tab. Paste in the following policy. If needed, you may fine-tune the policy to allow updates to the hosted zone that you just created. Complete the wizard.

    {
     "Version": "2012-10-17",
     "Statement": [
       {
         "Effect": "Allow",
         "Action": [
           "route53:ChangeResourceRecordSets"
         ],
         "Resource": [
           "arn:aws:route53:::hostedzone/*"
         ]
       },
       {
         "Effect": "Allow",
         "Action": [
           "route53:ListHostedZones",
           "route53:ListResourceRecordSets"
         ],
         "Resource": [
           "*"
         ]
       }
     ]
    }
    
  4. Create an IAM user for ExternalDNS with the policy that you created above. In the AWS Console, go to the Users screen and click Add users. Provide a name for the IAM user and ensure Programmatic access is enabled. On the Permissions screen of the wizard, click Attach existing policies directly and select the policy that you created in the previous step.

  5. Continue to the final page of the wizard. Record the Access key ID and Secret access key. To make these Route 53 credentials available to ExternalDNS, you create a Kubernetes secret in the namespace where ExternalDNS will be running.

    1. Set the context of kubectl to the cluster where you are deploying ExternalDNS. For example:

      kubectl config use-context tkg-services-admin@tkg-services
      
    2. Create the tanzu-system-service-discovery namespace if it does not already exist:

      kubectl create namespace tanzu-system-service-discovery
      
    3. Create the Kubernetes secret:

      kubectl -n tanzu-system-service-discovery create secret generic route53-credentials \
      --from-literal=aws_access_key_id=YOUR-ACCESS-KEY-ID \
      --from-literal=aws_secret_access_key=YOUR-SECRET-ACCESS-KEY
      

      Where YOUR-ACCESS-KEY-ID and YOUR-SECRET-ACCESS-KEY are the credentials that you recorded above.

  6. Create a configuration file, for example, external-dns-data-values.yaml, with the following contents. This file configures the ExternalDNS package.

    ---
    
    # Namespace in which to deploy ExternalDNS.
    namespace: tanzu-system-service-discovery
    
    # Deployment-related configuration.
    deployment:
     args:
       - --source=service
       - --source=ingress
       - --source=contour-httpproxy # Provide this to enable Contour HTTPProxy support. Must have Contour installed or ExternalDNS will fail.
       - --domain-filter=DOMAIN # Makes ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones.
       - --policy=upsert-only # Prevents ExternalDNS from deleting any records, omit to enable full synchronization.
       - --registry=txt
       - --txt-owner-id=HOSTED-ZONE-ID
       - --txt-prefix=txt # Disambiguates TXT records from CNAME records.
       - --provider=aws
       - --aws-zone-type=public # Looks only at public hosted zones. Valid values are public, private, or no value for both.
       - --aws-prefer-cname
     env:
       - name: AWS_ACCESS_KEY_ID
         valueFrom:
           secretKeyRef:
             name: route53-credentials
             key: aws_access_key_id
       - name: AWS_SECRET_ACCESS_KEY
         valueFrom:
           secretKeyRef:
             name: route53-credentials
             key: aws_secret_access_key
     securityContext: {}
     volumeMounts: []
     volumes: []
    
  7. Replace the placeholders in the external-dns-data-values.yaml file with your values. For more configuration options, see the ExternalDNS documentation.

    Before setting any additional configuration options in the external-dns-data-values.yaml file, review the values schema of the ExternalDNS package. To retrieve the values schema, run:

    tanzu package available get external-dns.tanzu.vmware.com/AVAILABLE-VERSION --values-schema
    

    Where AVAILABLE-VERSION is the version of the ExternalDNS package. The --values-schema flag retrieves the valuesSchema section from the Package API resource for the ExternalDNS package. You can set the output format, --output, for the values schema to yaml, json, or table. For more information, see Packages in CLI Reference for User-Managed Packages.

RFC2136 (BIND) Server

The RFC2136 provider allows you to use any RFC2136-compatible DNS server as a provider for ExternalDNS, such as BIND.

  1. Request or create a TSIG key for your server. This key must be authorized to update and transfer the zone you want to update. The key looks similar to the following:

    key "externaldns-key" {
     algorithm hmac-sha256;
     secret "/2avn5M4ndEztbDqy66lfQ+PjRZta9UXLtToW6NV5nM=";
    };
    

    If you are managing your own DNS server, then you can create a TSIG key using the tsig-keygen -a hmac-sha256 externaldns command. Copy the output to your DNS servers configuration. For example, for BIND, you add the key to the named.conf file and configure the zone with the allow-transfer and update-policy fields. For example:

    key "externaldns-key" {
     algorithm hmac-sha256;
     secret "/2avn5M4ndEztbDqy66lfQ+PjRZta9UXLtToW6NV5nM=";
    };
    zone "k8s.example.org" {
     type master;
     file "/etc/bind/zones/k8s.zone";
         allow-transfer {
           key "externaldns-key";
         };
         update-policy {
           grant externaldns-key zonesub ANY;
         };
    };
    

    The above assumes that you also have a zone file that might look similar to the following:

    $TTL 60 ; 1 minute
    @         IN SOA  k8s.example.org.  root.k8s.example.org. (
                     16  ; serial
                     60  ; refresh (1 minute)
                     60  ; retry (1 minute)
                     60  ; expire (1 minute)
                     60  ; minimum (1 minute)
                     )
               NS   ns.k8s.example.org.
    ns           A    1.2.3.4
    
  2. Create a configuration file, for example, external-dns-data-values.yaml, with the following contents. This file configures the ExternalDNS package.

    ---
    
    # Namespace in which to deploy ExternalDNS.
    namespace: tanzu-system-service-discovery
    
    # Deployment-related configuration.
    deployment:
     args:
       - --source=service
       - --source=ingress
       - --source=contour-httpproxy # Provide this to enable Contour HTTPProxy support. Must have Contour installed or ExternalDNS will fail.
       - --domain-filter=DOMAIN # For example, k8s.example.org. Makes ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones.
       - --policy=upsert-only # Prevents ExternalDNS from deleting any records, omit to enable full synchronization.
       - --registry=txt
       - --txt-owner-id=k8s
       - --txt-prefix=external-dns- # Disambiguates TXT records from CNAME records.
       - --provider=rfc2136
       - --rfc2136-host=IP-ADDRESS-OF-RFC2136-DNS-SERVER
       - --rfc2136-port=53
       - --rfc2136-zone=DNS-ZONE # For example, k8s.example.org.
       - --rfc2136-tsig-secret=TSIG-SECRET-FROM-STEP-1
       - --rfc2136-tsig-secret-alg=hmac-sha256
       - --rfc2136-tsig-keyname=TSIG-KEY-NAME # For example, externaldns-key.
       - --rfc2136-tsig-axfr
     env: []
     securityContext: {}
     volumeMounts: []
     volumes: []
    
  3. Replace the placeholders in the external-dns-data-values.yaml file with your values. For more configuration options, see the ExternalDNS documentation.

    Before setting any additional configuration options in the external-dns-data-values.yaml file, review the values schema of the ExternalDNS package. To retrieve the values schema, run:

    tanzu package available get external-dns.tanzu.vmware.com/AVAILABLE-VERSION --values-schema
    

    Where AVAILABLE-VERSION is the version of the ExternalDNS package. The --values-schema flag retrieves the valuesSchema section from the Package API resource for the ExternalDNS package. You can set the output format, --output, for the values schema to yaml, json, or table. For more information, see Packages in CLI Reference for User-Managed Packages.

Microsoft Azure

  1. Log in to the az CLI:

    az login
    
  2. Set your subscription:

    az account set -s SUBSCRIPTION-ID-GUID
    
  3. Create a service principal:

    az ad sp create-for-rbac -n SERVICE-PRINCIPAL-NAME
    

    The JSON output of the command looks similar to the following:

    {
      "appId": "a72a7cfd-7cb0-4b02-b130-03ee87e6ca89",
      "displayName": "foo",
      "name": "http://foo",
      "password": "515c55da-f909-4e17-9f52-236ffe1d3033",
      "tenant": "b35138ca-3ced-4b4a-14d6-cd83d9ea62f0"
    }
    
  4. Assign permissions to the service principal:

    1. Retrieve the ID of the resource group:

      az group show --name RESOURCE-GROUP --query id
      
    2. Assign the reader role to the service principal for the resource group scope. You will need the appId from the output of the az ad sp create-for-rbac command above.

      az role assignment create --role "Reader" --assignee APP-ID-GUID --scope RESOURCE-GROUP-RESOURCE-ID
      
    3. Retrieve the ID of the DNS zone:

      az network dns zone show --name DNS-ZONE-NAME -g RESOURCE-GROUP-NAME --query id
      
    4. Assign the contributor role to the service principal for the DNS zone scope:

      az role assignment create --role "Contributor" --assignee APP-ID-GUID --scope DNS-ZONE-RESOURCE-ID
      
  5. To connect the ExternalDNS service to the Azure DNS service, you create a configuration file named azure.json on your local machine with contents that look like the following:

    {
     "tenantId": "01234abc-de56-ff78-abc1-234567890def",
     "subscriptionId": "01234abc-de56-ff78-abc1-234567890def",
     "resourceGroup": "MyDnsResourceGroup",
     "aadClientId": "01234abc-de56-ff78-abc1-234567890def",
     "aadClientSecret": "uKiuXeiwui4jo9quae9o"
    }
    

    Replace the values in the example above with you own values as follows:

    • To retrieve tenantId, you can run the az account show --query "tenantId" command.
    • To retrieve subscriptionId, you can run the az account show --query "id" command.
    • resourceGroup is the name of the resource group that your DNS zone is within.
    • aadClientId is the appId from the output of the service principal.
    • aadClientSecret is the password from the output of the service principal.
  6. To make your Azure credentials available to ExternalDNS, create a Kubernetes secret in the namespace where ExternalDNS will be running:

    1. Set the context of kubectl to the cluster where you are deploying ExternalDNS. For example:

      kubectl config use-context tkg-services-admin@tkg-services
      
    2. Create the tanzu-system-service-discovery namespace if it does not already exist:

      kubectl create namespace tanzu-system-service-discovery
      
    3. Create the secret, using the azure.json configuration file from the previous step:

      kubectl -n tanzu-system-service-discovery create secret generic azure-config-file --from-file=azure.json
      
  7. Create a configuration file, for example, external-dns-data-values.yaml, with the following contents. This file configures the ExternalDNS package.

    ---
    
    # Namespace in which to deploy ExternalDNS.
    namespace: tanzu-system-service-discovery
    
    # Deployment-related configuration.
    deployment:
     args:
       - --source=service
       - --source=ingress
       - --source=contour-httpproxy # Provide this to enable Contour HTTPProxy support. Must have Contour installed or ExternalDNS will fail.
       - --domain-filter=DOMAIN # For example, k8s.example.org. Makes ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones.
       - --policy=upsert-only # Prevents ExternalDNS from deleting any records, omit to enable full synchronization.
       - --registry=txt
       - --txt-prefix=externaldns- # Disambiguates TXT records from CNAME records.
       - --provider=azure
       - --azure-resource-group=RESOURCE-GROUP # Azure resource group.
     env: []
     securityContext: {}
     volumeMounts:
       - name: azure-config-file
         mountPath: /etc/kubernetes
         readOnly: true
     volumes:
       - name: azure-config-file
         secret:
           secretName: azure-config-file
    
  8. Replace the placeholders in the external-dns-data-values.yaml file with your values. For more configuration options, see the ExternalDNS documentation.

    Before setting any additional configuration options in the external-dns-data-values.yaml file, review the values schema of the ExternalDNS package. To retrieve the values schema, run:

    tanzu package available get external-dns.tanzu.vmware.com/AVAILABLE-VERSION --values-schema
    

    Where AVAILABLE-VERSION is the version of the ExternalDNS package. The --values-schema flag retrieves the valuesSchema section from the Package API resource for the ExternalDNS package. You can set the output format, --output, for the values schema to yaml, json, or table. For more information, see Packages in CLI Reference for User-Managed Packages.

Install the ExternalDNS Package

  1. Set the context of kubectl to the cluster where you are deploying ExternalDNS. For example:

    kubectl config use-context tkg-services-admin@tkg-services
    
  2. Retrieve the name of the ExternalDNS package:

    tanzu package available list -A
    
  3. Retrieve the version of the ExternalDNS package:

    tanzu package available list external-dns.tanzu.vmware.com -A
    
  4. Install the package:

    • If the target namespace exists in the cluster, run:

      tanzu package install external-dns \
      --package-name external-dns.tanzu.vmware.com \
      --version AVAILABLE-PACKAGE-VERSION \
      --values-file external-dns-data-values.yaml
      --namespace TARGET-NAMESPACE
      

      Where:

      • TARGET-NAMESPACE is the namespace in which you want to install the ExternalDNS package and deploy the ExternalDNS package app, which is managed by kapp-controller. For example, my-packages. If this flag is not specified, the Tanzu CLI uses the default namespace. The ExternalDNS pods and any other resources associated with ExternalDNS are created in the tanzu-system-service-discovery namespace; do not install the ExternalDNS package into this namespace.
      • AVAILABLE-PACKAGE-VERSION is the version that you retrieved above.

      For example:

      tanzu package install external-dns \
      --package-name external-dns.tanzu.vmware.com \
      --version 0.8.0+vmware.1-tkg.1 \
      --values-file external-dns-data-values.yaml
      --namespace my-packages
      
    • If the target namespace does not exist in the cluster, run:

      tanzu package install external-dns \
      --package-name external-dns.tanzu.vmware.com \
      --version AVAILABLE-PACKAGE-VERSION \
      --values-file external-dns-data-values.yaml
      --namespace TARGET-NAMESPACE \
      --create-namespace
      

      Where:

      • TARGET-NAMESPACE is the namespace in which you want to install the ExternalDNS package and deploy the ExternalDNS package app, which is managed by kapp-controller. For example, my-packages. If this flag is not specified, the Tanzu CLI uses the default namespace. The ExternalDNS pods and any other resources associated with ExternalDNS are created in the tanzu-system-service-discovery namespace; do not install the ExternalDNS package into this namespace.
      • AVAILABLE-PACKAGE-VERSION is the version that you retrieved above.

      For example:

      tanzu package install external-dns \
      --package-name external-dns.tanzu.vmware.com \
      --version 0.8.0+vmware.1-tkg.1 \
      --values-file external-dns-data-values.yaml
      --namespace my-packages
      --create-namespace
      
  5. Confirm that the external-dns package has been installed:

    tanzu package installed list -A
    

    To see more details about the package, you can also run:

    tanzu package installed get external-dns --namespace PACKAGE-NAMESPACE
    

    Where PACKAGE-NAMESPACE is the namespace in which the external-dns package is installed.

  6. Confirm that the external-dns app has been successfully reconciled in your PACKAGE-NAMESPACE:

    kubectl get apps -A
    

    If the status is not Reconcile Succeeded, view the full status details of the external-dns app. Viewing the full status can help you troubleshoot the problem.

    kubectl get app external-dns --namespace PACKAGE-NAMESPACE -o yaml
    

    Where PACKAGE-NAMESPACE is the namespace in which you installed the package. If troubleshooting does not help you solve the problem, you must uninstall the package before installing it again:

    tanzu package installed delete external-dns
    
  7. Confirm that ExternalDNS is running in the tanzu-system-service-discovery namespace:

    kubectl get pods -A
    

Validating ExternalDNS

If configured with Contour, ExternalDNS will automatically watch the specified namespace for HTTPProxy resources and create DNS records for services with hostnames that match the configured domain filter.

ExternalDNS will also automatically watch for Kubernetes Services with the annotation external-dns.alpha.kubernetes.io/hostname and create DNS records for services whose annotations match the configured domain filter.

For example, for a service with the annotation external-dns.alpha.kubernetes.io/hostname: foo.k8s.example.org, ExternalDNS will create a DNS record for foo.k8s.example.org. You can validate that the record exists by examining the zone that you created.

Update a Running ExternalDNS Deployment

If you need to make changes to the configuration of the ExternalDNS package after deployment, follow these steps to update your deployed ExternalDNS package.

  1. Update the ExternalDNS configuration in external-dns-data-values.yaml.

  2. Update the configuration of the installed package:

    tanzu package installed update external-dns \
    --version INSTALLED-PACKAGE-VERSION \
    --values-file external-dns-data-values.yaml \
    --namespace INSTALLED-PACKAGE-NAMESPACE
    

    Where:

    • INSTALLED-PACKAGE-VERSION is the version of the installed ExternalDNS package.
    • INSTALLED-PACKAGE-NAMESPACE is the namespace in which the ExternalDNS package is installed.

    For example:

    tanzu package installed update external-dns \
    --version 0.8.0+vmware.1-tkg.1 \
    --values-file external-dns-data-values.yaml \
    --namespace my-packages
    

The ExternalDNS package will be reconciled using the new value or values that you added. It can take up to five minutes for kapp-controller to apply the changes.

For more information about the tanzu package installed update command, see Update a Package in CLI Reference for User-Managed Packages. You can use this command to update the version or the configuration of an installed package.

check-circle-line exclamation-circle-line close-line
Scroll to top icon