Kubernetes: Automated DNS with External-DNS on GKE


Updated: 2020-06-17

I’ll be using a helm chart to install external-dns on my Kubernetes GKE cluster.

Create zone

Create GCP service account

Create a GCP service account to enable an account to edit Cloud DNS.

export PROJECT_NAME=[YOUR_PROJECT_NAME]

# create service account
gcloud iam service-accounts create k8s-external-dns \
  --display-name="Service Account to support ACME DNS-01 challenge." \
  --project=$PROJECT_NAME

# create service account key
# this will also download the json key
gcloud iam service-accounts keys create ./credentials.json \
  --iam-account=k8s-external-dns@$PROJECT_NAME.iam.gserviceaccount.com \
  --project=$PROJECT_NAME

# give dns admin permissions
gcloud projects add-iam-policy-binding $PROJECT_NAME \
  --member=serviceAccount:k8s-external-dns@$PROJECT_NAME.iam.gserviceaccount.com \
  --role=roles/dns.admin

Create secret from GCP service account

kubectl create secret generic external-dns \
--from-file=./credentials.json \
--namespace=itsmetommy

Add repo

helm repo add bitnami https://charts.bitnami.com/bitnami

Update repo

helm repo update

Create namespace

kubectl create ns external-dns

Install

  • domainFilters – used for limiting the domains that ExternalDNS can manage
  • registry=txt – TXT Registry Identifier
  • txtOwnerId=k8s – Prefix to create a TXT record with a name following the pattern prefix.<CNAME record>
  • sources – K8s resources type to be observed for new DNS entries by ExternalDNS (I added istio-gateway)

IMPORTANT: Make sure that the txtOwnerId is unique when managing the same zone, otherwise DNS records will be deleted and re-added constantly. Basically external-dns will be syncing from multiple locations.

helm install external-dns \
  --set provider=google \
  --set google.serviceAccountSecret=external-dns \
  --set policy=sync \
  --set registry=txt \
  --set txtOwnerId=k8s-itsmetommy \
  --set rbac.create=true \
  --set domainFilters={your-doamin.com} \
  --set sources="{ingress,istio-gateway,service}" \
  -n external-dns \
  bitnami/external-dns

Ingress

Single domain.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    external-dns.alpha.kubernetes.io/hostname: your-domain.com

Multiple domains.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    external-dns.alpha.kubernetes.io/hostname: foo.your-domain.com,bar.your-domain.com

Service

kubectl annotate service nginx "external-dns.alpha.kubernetes.io/hostname=tommy.itsmetommy.io."

OR

apiVersion: v1
kind: Service
metadata:
  name: nginx
  annotations:
    external-dns.alpha.kubernetes.io/hostname: your-domain.com.

Logs

kubectl logs -f `kubectl get pods -n external-dns -o jsonpath="{.items[0].metadata.name}"` -n external-dns

Uninstall

helm delete external-dns -n external-dns

Errors

Error 1

kubectl logs external-dns-d46d86697-4nxlw
time="2019-06-12T04:51:45Z" level=fatal msg="google: error getting credentials using GOOGLE_APPLICATION_CREDENTIALS environment variable: open /etc/secrets/service-account/credentials.json: no such file or directory"

Fix.

Make sure your key name is credentials.json.

kubectl create secret generic external-dns \
  --from-file=./credentials.json \
  --namespace=external-dns

Or edit the existing secret directly.

kubectl edit secret generic external-dns -n external-dns

Error 2

helm install external-dns \
--set provider=google \
--set google.serviceAccountSecret=external-dns \
--set policy=sync \
--set registry=txt \
--set txtOwnerId=k8s \
--set rbac.create=true \
--set domainFilters=your-domain.com \
--set sources="{ingress,istio-gateway,service}" \
-n external-dns \
bitnami/external-dns
Error: render error in "external-dns/templates/deployment.yaml": template: external-dns/templates/deployment.yaml:35:27: executing "external-dns/templates/deployment.yaml" at <.Values.domainFilters>: range can't iterate over your-domain.com

Fix.

Use brackets { }.

  --set domainFilters={your-domain.com} \