Can I use external-dns and OpenStack Designate for automatic DNS?

Yes, to reduce manual effort and automate the configuration of DNS zones, you may want to use external-dns. In summary, external-dns allows you to control DNS records dynamically with Kubernetes resources in a DNS provider-agnostic way. external-dns is not a DNS server by itself, but merely configures other DNS providers (for example, OpenStack Designate, Amazon Route53, Google Cloud DNS, and so on.)

Prerequisites

To successfully complete the following steps, you need the following:

Configure your domain to use Designate

Delegate your domains from your DNS provider to the following DNS name servers so that Designate can control the DNS resources of your domain.

ns1.openstack.de-west-01.wiit-cloud.io
ns2.openstack.de-west-01.wiit-cloud.io

Create a new DNS Zone

Before you use ExternalDNS, you need to add your DNS zones to your DNS provider, in this case, Designate DNS.

In our example we use the test domain name example.foo. It is important to create the zones before starting to control the DNS resources with Kubernetes.

Note: You must include the final . at the end of the zone/domain to be created.

$ openstack zone create --email webmaster@example.foo example.foo.

Next, make sure that the zone was created successfully and the status is active.

$ openstack zone list
$ openstack zone show example.foo.

Create application credentials for external-dns

Attention: It is important to log in with your service user into your OpenStack project, because Application Credentials are user specific.

Visit the web UI and go to Identity -> Application Credentials, then click the + CREATE APPLICATION CREDENTIAL button, or create credentials via the CLI:

$ openstack application credential create <name_of_app_credentials>

Install external-dns into your cluster

Note: Don’t forget to change the OpenStack application credentials and the command line arguments in the external-dns deployment --domain-filter=example.foo and the --txt-owner-id=<owner-id>. The domain filter is the DNS zone. With the TXT owner ID, external-dns can identify the entries it manages. If you want to use it in production, change the image to a newer version if available and keep it updated.

  • Namespace:
apiVersion: v1
kind: Namespace
metadata:
  name: external-dns
  • RBAC:
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: external-dns
  namespace: external-dns
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: external-dns
rules:
  - apiGroups: [""]
    resources: ["services", "endpoints", "pods"]
    verbs: ["get", "watch", "list"]
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "watch", "list"]
  - apiGroups: ["extensions", "networking.k8s.io"]
    resources: ["ingresses"]
    verbs: ["get", "watch", "list"]
  - apiGroups: [""]
    resources: ["nodes"]
    verbs: ["watch", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: external-dns-viewer
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: external-dns
subjects:
  - kind: ServiceAccount
    name: external-dns
    namespace: external-dns
  • Secret:
apiVersion: v1
kind: Secret
type: Opaque
metadata:
  name: designate-openstack-credentials
  namespace: external-dns
stringData:
  OS_AUTH_URL: <auth-url>
  OS_REGION_NAME: <region>
  OS_AUTH_TYPE: v3applicationcredential
  OS_APPLICATION_CREDENTIAL_ID: <appcred_id>
  OS_APPLICATION_CREDENTIAL_SECRET: <appcred_secret>
  • Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: external-dns
  namespace: external-dns
spec:
  selector:
    matchLabels:
      app: external-dns
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: external-dns
    spec:
      serviceAccountName: external-dns
      containers:
        - args:
            - --source=service
            - --source=ingress
            - --registry=txt
            - --provider=designate
            - --domain-filter=example.foo
            - --txt-owner-id=<owner-id>
            - --log-level=debug
          envFrom:
            - secretRef:
                name: designate-openstack-credentials
          image: registry.k8s.io/external-dns/external-dns:v0.14.2
          imagePullPolicy: IfNotPresent
          name: external-dns

Annotate the service or ingress

To add the DNS entry to Designate, annotate the service or ingress with external-dns.alpha.kubernetes.io/hostname: my-app.example.foo. For example:

apiVersion: v1
kind: Namespace
metadata:
  name: my-app
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: my-app
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
        ports:
        - containerPort: 80
apiVersion: v1
kind: Service
metadata:
  name: nginx
  namespace: my-app
  annotations:
    external-dns.alpha.kubernetes.io/hostname: my-app.example.foo
spec:
  selector:
    app: nginx
  type: LoadBalancer
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

DNS Lookup

Make sure the DNS record can be resolved correctly:

$ openstack recordset list example.foo.
$ dig my-app.example.foo @ns1.openstack.de-west-01.wiit-cloud.io.
$ dig my-app.example.foo @ns2.openstack.de-west-01.wiit-cloud.io.
$ dig my-app.example.foo

Further information

For further information, please have a look at: