15.1. Install External DNS#

Updating DNS is easy but it’s boring. Wouldn’t it be nice if when you create a new LoadBalancer service or Ingress with a name annotation a DNS record was created automatically? That’s the job of the External DNS project. External DNS is an official Kubernetes project.

Before You Begin

There are prerequisites that must be complete for you to do this lab:

  1. You must have your own custom domain hosted using CloudDNS

  2. You must not have a conflicting address record for your application (delete it before you begin)

Introduction#

External DNS runs as Pods in your cluster. One pod looks for new Ingress and LoadBalancers and sees if they have been annotated with a domain name. If so it reaches out of the cluster to update your DNS server. External DNS requires that you have a supported DNS server and the permission to modify records. In this lab we’ll:

  1. Create a GCP service account with the permission to update DNS records.

  2. Install External DNS

  3. Check for new records

Create a Service Account#

Create a GCP service account (GSA) for ExternalDNS and save its email address.

$ project=$(gcloud config get-value core/project)
$ sa_name="Kubernetes external-dns"
$ gcloud iam service-accounts create sa-edns --display-name="$sa_name"
$ sa_email=$(gcloud iam service-accounts list --format='value(email)' \
    --filter="displayName:$sa_name")

Bind the ExternalDNS GSA to the DNS admin role.

$ gcloud projects add-iam-policy-binding $project \
    --member="serviceAccount:$sa_email" --role=roles/dns.admin

Link the ExternalDNS GSA to the Kubernetes service account (KSA) that external-dns will run under, i.e., the external-dns KSA in the external-dns namespaces.

$ gcloud iam service-accounts add-iam-policy-binding "$sa_email" \
    --member="serviceAccount:${project}.svc.id.goog[default/external-dns]" \
    --role=roles/iam.workloadIdentityUser

Deploy External DNS#

Just like any Helm chart External DNS needs configuration.

Warning

Update the file to contain your project and DNS name

Put the following into a file called values-edns.yaml:

provider: "google"

policy: "sync"

serviceAccount:
  create: true
  name: external-dns
  annotations:
    iam.gke.io/gcp-service-account: sa-edns@MY-PROJECT-ID-HERE.iam.gserviceaccount.com

sources:
  - service
  - ingress

domainFilters:
  - MYDOMAIN.HERE.

txtOwnerId: "kubernetes-app"

resources:
  requests:
    memory: "512Mi"
    cpu: "250m"
    ephemeral-storage: "10Mi"
  limits:
    memory: "512Mi"
    cpu: "250m"
    ephemeral-storage: "10Mi"

Deploy external DNS:

$ helm repo add external-dns https://kubernetes-sigs.github.io/external-dns/
$ helm repo update
$ helm install external-dns external-dns/external-dns --values values-edns.yaml

Now watch the pod get created:

$ kubectl get all 
NAME                               READY   STATUS    RESTARTS   AGE
pod/external-dns-bbfcd6d55-cx5lc   1/1     Running   0          8m46s
pod/mysite                         1/1     Running   0          52m

NAME                   TYPE           CLUSTER-IP    EXTERNAL-IP       PORT(S)        AGE
service/external-dns   ClusterIP      10.46.1.197   <none>            7979/TCP       8m46s
service/kubernetes     ClusterIP      10.46.0.1     <none>            443/TCP        40d
service/mysite         LoadBalancer   10.46.0.162   104.154.173.213   80:31633/TCP   3m21s

NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/external-dns   1/1     1            1           8m47s

NAME                                     DESIRED   CURRENT   READY   AGE
replicaset.apps/external-dns-bbfcd6d55   1         1         1       8m47s

Once running check the Pod logs. This will help you debug any problems:

$ kubectl logs -f pod/external-dns-bbfcd6d55-cx5lc

Annotate a Service#

Do this if you have not setup an Ingress controller. Set your service:

apiVersion: v1
kind: Service
metadata:
  name: mysite
  annotations:
    external-dns.alpha.kubernetes.io/hostname: myhost.mydomain.com.
spec:
  type: LoadBalancer
...

With the service annotation applied the host myhost.mydomain.com will be automatically registered in your Cloud DNS zone.

Annotate an Ingress#

If you followed the Ingress lab you should already have an annotation specifying your hostname:

...
spec:
  rules:
  - host: "www.mydomain.com"
    http:
...

This is sufficient to update the records.

Teardown#

If it works you should leave External DNS running so that it constantly updates your records. However, if you want to save money you can disable it until you make changes.

$ helm uninstall external-dns