Streamline Kubernetes DNS Management: Effortless Lifecycle Control with ExternalDNS
Are your kubernetes workloads DNS records management creating too much manual work and a lot of downtimes due to frequent endpoint changes? If yes, then external DNS is for you.
ExternalDNS is a kubernetes add-on which you can deploy in any kubernetes cluster and seamlessly manage the entire lifecycle of the DNS records. It allows you to control DNS records dynamically via Kubernetes resources in a DNS provider-agnostic way.
This article covers how to install ExternalDNS in GKE and use the add-on to manage the lifecycle of DNS records. The installation is based on Helm, but you can also use kustomize.
Prerequisites
- A GKE Cluster with workload identity enabled
- A public or private Cloud DNS zone. Refer to https://cloud.google.com/dns/docs/quickstart
- Helm3 and kubectl
Configure Workload Identity for ExternalDNS
ExternalDNS requires access to the Cloud DNS zone to manage DNS records. Run the below commands to set up the resources for workload identity. Replace the variables with actual values.
#Creare a GCP service account
gcloud iam service-accounts create external-dns --project=$GCP_PROJECT_ID
#Defaut DNS admin role is used for this demo but it is not recommended for production usecase, create a custom role with minimal permissions
gcloud projects add-iam-policy-binding $GCP_PROJECT_ID \
--member "serviceAccount:external-dns@$GCP_PROJECT_ID.iam.gserviceaccount.com" \
--role "roles/dns.admin"
#Bind the kubernetes service account to GCP service account
gcloud iam service-accounts add-iam-policy-binding external-dns@$GCP_PROJECT_ID.iam.gserviceaccount.com \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:$GCP_PROJECT_ID.svc.id.goog[external-dns/external-dns]"
Install ExternalDNS
Add the external-dns
repo to Helm.
helm repo add external-dns https://kubernetes-sigs.github.io/external-dns/
Custom values.yaml, Replace the variables with actual values and update domainFilters.
# Default values for external-dns.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
image:
repository: registry.k8s.io/external-dns/external-dns
# Overrides the image tag whose default is v{{ .Chart.AppVersion }}
tag: ""
pullPolicy: IfNotPresent
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
commonLabels: {}
serviceAccount:
# Specifies whether a service account should be created
create: true
# Annotations to add to the service account.The GCP IAM serviceaccount name will be added to implement workload identity
annotations:
iam.gke.io/gcp-service-account: external-dns@$GCP_PROJECT_ID.iam.gserviceaccount.com
# Labels to add to the service account
labels: {}
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name: ""
rbac:
# Specifies whether RBAC resources should be created
create: true
additionalPermissions: []
# Annotations to add to the Deployment
deploymentAnnotations: {}
podLabels: {}
# Annotations to add to the Pod
podAnnotations: {}
shareProcessNamespace: false
podSecurityContext:
fsGroup: 65534
securityContext:
runAsNonRoot: true
runAsUser: 65534
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
# Defaults to `ClusterFirst`.
# Valid values are: `ClusterFirstWithHostNet`, `ClusterFirst`, `Default` or `None`.
dnsPolicy:
priorityClassName: ""
terminationGracePeriodSeconds:
serviceMonitor:
enabled: false
# force namespace
# namespace: monitoring
# Fallback to the prometheus default unless specified
# interval: 10s
## scheme: HTTP scheme to use for scraping. Can be used with `tlsConfig` for example if using istio mTLS.
# scheme: ""
## tlsConfig: TLS configuration to use when scraping the endpoint. For example if using istio mTLS.
## Of type: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#tlsconfig
# tlsConfig: {}
# bearerTokenFile:
# Fallback to the prometheus default unless specified
# scrapeTimeout: 30s
## Used to pass Labels that are used by the Prometheus installed in your cluster to select Service Monitors to work with
## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#prometheusspec
additionalLabels: {}
## Used to pass annotations that are used by the Prometheus installed in your cluster to select Service Monitors to work with
## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#prometheusspec
annotations: {}
## Metric relabel configs to apply to samples before ingestion.
## [Metric Relabeling](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#metric_relabel_configs)
metricRelabelings: []
# - action: keep
# regex: 'kube_(daemonset|deployment|pod|namespace|node|statefulset).+'
# sourceLabels: [__name__]
## Relabel configs to apply to samples before ingestion.
## [Relabeling](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config)
relabelings: []
# - sourceLabels: [__meta_kubernetes_pod_node_name]
# separator: ;
# regex: ^(.*)$
# targetLabel: nodename
# replacement: $1
# action: replace
targetLabels: []
env: []
livenessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 2
successThreshold: 1
readinessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
successThreshold: 1
service:
port: 7979
annotations: {}
extraVolumes: []
extraVolumeMounts: []
resources: {}
nodeSelector: {}
tolerations: []
affinity: {}
topologySpreadConstraints: []
logLevel: info
logFormat: text
interval: 1m
triggerLoopOnEvent: false
namespaced: false
sources:
- service
- ingress
policy: sync
registry: txt
txtOwnerId: ""
txtPrefix: ""
txtSuffix: ""
## List of domains that can be managed. Should be managed by Google Cloud DNS
domainFilters: ["external.dns.decom."]
provider: google
extraArgs: ["--google-project=$GCP_PROJECT_ID"]
secretConfiguration:
enabled: false
mountPath: ""
subPath: ""
data: {}
deploymentStrategy:
type: Recreate
Run the below command to install the chart with the custom values file.
helm upgrade --install external-dns external-dns/external-dns --values values.yaml --namespace external-dns --create-namespace --wait --debug
Verify the installation and check the logs to ensure the application runs without errors.
Deploy Sample application
Run the below command to deploy a sample nginx application in the external-dns-test
namespace.
kubectl run nginx --image nginx --namespace external-dns-test
Expose the nginx pod via Kubernetes service type loadbalancer. To expose the service via ingress, create a service type NodePort and an ingress object.
kubectl expose pod nginx --namespace external-dns-test --target-port 80 --port 80 --type LoadBalancer
For kubernetes services, ExternalDNS will look for the annotation external-dns.alpha.kubernetes.io/hostname
on the service and use the loadbalancer IP, it also will look for the annotation external-dns.alpha.kubernetes.io/internal-hostname
on the service and use the service IP.
For ingress objects, ExternalDNS will create a DNS record based on the hosts specified for the ingress object, as well as the external-dns.alpha.kubernetes.io/hostname
annotation.
For ingresses, you can optionally force ExternalDNS to create records based on either the hosts specified or the external-dns.alpha.kubernetes.io/hostname
annotation. This behaviour is controlled by setting the external-dns.alpha.kubernetes.io/ingress-hostname-source
annotation on that ingress to either defined-hosts-only
or annotation-only
.
Add the annotation to the nginx service with the required DNS record name value.
kubectl annotate service -n external-dns-test nginx external-dns.alpha.kubernetes.io/hostname=chimbu.external.dns.decom.
In the above images, you can see DNS records are successfully deleted automatically by external-dns in the required Cloud DNS zone.
Remove the annotation to test the sync action.
kubectl annotate service -n external-dns-test nginx external-dns.alpha.kubernetes.io/hostname-
In the above images, you can see DNS records are successfully deleted automatically by external-dns in the required Cloud DNS zone.
Conclusion
ExternalDNS eliminates the manual DNS record management and automatically creates/updates the DNS records for you, which will minimize downtime if there is any change in IP address.
Refer to the official Github repository and FAQ for all the supported DNS providers and key concepts of ExternalDNS.