external-dns Headless Services We will go through a small example of deploying a simple Kafka with use of a headless service Use cases The main use cases that inspired this feature is the necessity for fixed addressable hostnames with services such as Kafka when trying to access them from outside the cluster In this scenario quite often only the Node IP addresses are actually routable and as in systems like Kafka more direct connections are preferable This tutorial describes how to setup ExternalDNS for usage in conjunction with a Headless service Setup

Answer:
# Headless Services

This tutorial describes how to setup ExternalDNS for usage in conjunction with a Headless service.

## Use cases
The main use cases that inspired this feature is the necessity for fixed addressable hostnames with services, such as Kafka when trying to access them from outside the cluster. In this scenario, quite often, only the Node IP addresses are actually routable and as in systems like Kafka more direct connections are preferable.

## Setup

We will go through a small example of deploying a simple Kafka with use of a headless service.

### External DNS

A simple deploy could look like this:
### Manifest (for clusters without RBAC enabled)
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: external-dns
spec:
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: external-dns
  template:
    metadata:
      labels:
        app: external-dns
    spec:
      containers:
      - name: external-dns
        image: registry.k8s.io/external-dns/external-dns:v0.15.0
        args:
        - --log-level=debug
        - --source=service
        - --source=ingress
        - --namespace=dev
        - --domain-filter=example.org.
        - --provider=aws
        - --registry=txt
        - --txt-owner-id=dev.example.org
```

### Manifest (for clusters with RBAC enabled)
```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: 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: ["extensions","networking.k8s.io"]
  resources: ["ingresses"]
  verbs: ["get","watch","list"]
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["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: default
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: external-dns
spec:
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: external-dns
  template:
    metadata:
      labels:
        app: external-dns
    spec:
      serviceAccountName: external-dns
      containers:
      - name: external-dns
        image: registry.k8s.io/external-dns/external-dns:v0.15.0
        args:
        - --log-level=debug
        - --source=service
        - --source=ingress
        - --namespace=dev
        - --domain-filter=example.org.
        - --provider=aws
        - --registry=txt
        - --txt-owner-id=dev.example.org
```

### Kafka Stateful Set

First lets deploy a Kafka Stateful set, a simple example(a lot of stuff is missing) with a headless service called `ksvc`

```yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: kafka
spec:
  serviceName: ksvc
  replicas: 3
  template:
    metadata:
      labels:
        component: kafka
    spec:
      containers:
      - name:  kafka
        image: confluent/kafka
        ports:
        - containerPort: 9092
          hostPort: 9092
          name: external
        command:
        - bash
        - -c
        - " export DOMAIN=$(hostname -d) && \
            export KAFKA_BROKER_ID=$(echo $HOSTNAME|rev|cut -d '-' -f 1|rev) && \
            export KAFKA_ZOOKEEPER_CONNECT=$ZK_CSVC_SERVICE_HOST:$ZK_CSVC_SERVICE_PORT && \
            export KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://$HOSTNAME.example.org:9092 && \
            /etc/confluent/docker/run"
        volumeMounts:
        - name: datadir
          mountPath: /var/lib/kafka
  volumeClaimTemplates:
  - metadata:
      name: datadir
      annotations:
          volume.beta.kubernetes.io/storage-class: st1
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage:  500Gi
```
Very important here, is to set the `hostPort`(only works if the PodSecurityPolicy allows it)! and in case your app requires an actual hostname inside the container, unlike Kafka, which can advertise on another address, you have to set the hostname yourself.

### Headless Service

Now we need to define a headless service to use to expose the Kafka pods. There are generally two approaches to use expose the nodeport of a Headless service:

1. Add `--fqdn-template=.example.org`
2. Use a full annotation

If you go with #1, you just need to define the headless service, here is an example of the case #2:

```yaml
apiVersion: v1
kind: Service
metadata:
  name: ksvc
  annotations:
    external-dns.alpha.kubernetes.io/hostname:  example.org
spec:
  ports:
  - port: 9092
    name: external
  clusterIP: None
  selector:
    component: kafka
```
This will create 3 dns records:
```
kafka-0.example.org
kafka-1.example.org
kafka-2.example.org
```

If you set `--fqdn-template=.example.org` you can omit the annotation.
Generally it is a better approach to use  `--fqdn-template=.example.org`, because then
you would get the service name inside the generated A records:

```
kafka-0.ksvc.example.org
kafka-1.ksvc.example.org
kafka-2.ksvc.example.org
```

#### Using pods' HostIPs as targets

Add the following annotation to your `Service`:

```yaml
external-dns.alpha.kubernetes.io/endpoints-type: HostIP
```

external-dns will now publish the value of the `.status.hostIP` field of the pods backing your `Service`.

#### Using node external IPs as targets

Add the following annotation to your `Service`:

```yaml
external-dns.alpha.kubernetes.io/endpoints-type: NodeExternalIP
```

external-dns will now publish the node external IP (`.status.addresses` entries of with `type: NodeExternalIP`) of the nodes on which the pods backing your `Service` are running.

#### Using pod annotations to specify target IPs

Add the following annotation to the **pods** backing your `Service`:

```yaml
external-dns.alpha.kubernetes.io/target: "1.2.3.4"
```

external-dns will publish the IP specified in the annotation of each pod instead of using the podIP advertised by Kubernetes.

This can be useful e.g. if you are NATing public IPs onto your pod IPs and want to publish these in DNS.

Headless Services  This tutorial describes how to setup ExternalDNS for usage in conjunction with a Headless service      Use cases The main use cases that inspired this feature is the necessity for fixed addressable hostnames with services  such as Kafka when trying to access them from outside the cluster  In this scenario  quite often  only the Node IP addresses are actually routable and as in systems like Kafka more direct connections are preferable      Setup  We will go through a small example of deploying a simple Kafka with use of a headless service       External DNS  A simple deploy could look like this      Manifest  for clusters without RBAC enabled     yaml apiVersion  apps v1 kind  Deployment metadata    name  external dns spec    strategy      type  Recreate   selector      matchLabels        app  external dns   template      metadata        labels          app  external dns     spec        containers          name  external dns         image  registry k8s io external dns external dns v0 15 0         args              log level debug             source service             source ingress             namespace dev             domain filter example org               provider aws             registry txt             txt owner id dev example org          Manifest  for clusters with RBAC enabled     yaml apiVersion  v1 kind  ServiceAccount metadata    name  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    extensions   networking k8s io     resources    ingresses      verbs    get   watch   list     apiGroups         resources    nodes     verbs    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  default     apiVersion  apps v1 kind  Deployment metadata    name  external dns spec    strategy      type  Recreate   selector      matchLabels        app  external dns   template      metadata        labels          app  external dns     spec        serviceAccountName  external dns       containers          name  external dns         image  registry k8s io external dns external dns v0 15 0         args              log level debug             source service             source ingress             namespace dev             domain filter example org               provider aws             registry txt             txt owner id dev example org           Kafka Stateful Set  First lets deploy a Kafka Stateful set  a simple example a lot of stuff is missing  with a headless service called  ksvc      yaml apiVersion  apps v1 kind  StatefulSet metadata    name  kafka spec    serviceName  ksvc   replicas  3   template      metadata        labels          component  kafka     spec        containers          name   kafka                 image  confluent kafka         ports            containerPort  9092           hostPort  9092           name  external         command            bash            c             export DOMAIN   hostname  d                   export KAFKA BROKER ID   echo  HOSTNAME rev cut  d      f 1 rev                   export KAFKA ZOOKEEPER CONNECT  ZK CSVC SERVICE HOST  ZK CSVC SERVICE PORT                  export KAFKA ADVERTISED LISTENERS PLAINTEXT    HOSTNAME example org 9092                   etc confluent docker run          volumeMounts            name  datadir           mountPath   var lib kafka   volumeClaimTemplates      metadata        name  datadir       annotations            volume beta kubernetes io storage class  st1     spec        accessModes     ReadWriteOnce          resources          requests            storage   500Gi     Very important here  is to set the  hostPort  only works if the PodSecurityPolicy allows it   and in case your app requires an actual hostname inside the container  unlike Kafka  which can advertise on another address  you have to set the hostname yourself       Headless Service  Now we need to define a headless service to use to expose the Kafka pods  There are generally two approaches to use expose the nodeport of a Headless service   1  Add    fqdn template  example org  2  Use a full annotation   If you go with  1  you just need to define the headless service  here is an example of the case  2      yaml apiVersion  v1 kind  Service metadata    name  ksvc   annotations      external dns alpha kubernetes io hostname   example org spec    ports      port  9092     name  external   clusterIP  None   selector      component  kafka     This will create 3 dns records      kafka 0 example org kafka 1 example org kafka 2 example org      If you set    fqdn template  example org  you can omit the annotation  Generally it is a better approach to use     fqdn template  example org   because then you would get the service name inside the generated A records       kafka 0 ksvc example org kafka 1 ksvc example org kafka 2 ksvc example org           Using pods  HostIPs as targets  Add the following annotation to your  Service       yaml external dns alpha kubernetes io endpoints type  HostIP      external dns will now publish the value of the   status hostIP  field of the pods backing your  Service         Using node external IPs as targets  Add the following annotation to your  Service       yaml external dns alpha kubernetes io endpoints type  NodeExternalIP      external dns will now publish the node external IP    status addresses  entries of with  type  NodeExternalIP   of the nodes on which the pods backing your  Service  are running        Using pod annotations to specify target IPs  Add the following annotation to the   pods   backing your  Service       yaml external dns alpha kubernetes io target   1 2 3 4       external dns will publish the IP specified in the annotation of each pod instead of using the podIP advertised by Kubernetes   This can be useful e g  if you are NATing public IPs onto your pod IPs and want to publish these in DNS


external-dns Exoscale provider support was added via thus you need to use external dns v0 5 5 Exoscale and are configured correctly It does not add remove or configure new zones in anyway The Exoscale provider expects that your Exoscale zones you wish to add records to already exists To do this please refer to the Prerequisites

Answer:
# Exoscale

## Prerequisites

Exoscale provider support was added via [this PR](https://github.com/kubernetes-sigs/external-dns/pull/625), thus you need to use external-dns v0.5.5.

The Exoscale provider expects that your Exoscale zones, you wish to add records to, already exists
and are configured correctly. It does not add, remove or configure new zones in anyway.

To do this please refer to the [Exoscale DNS documentation](https://community.exoscale.com/documentation/dns/).

Additionally you will have to provide the Exoscale...:

* API Key
* API Secret
* Elastic IP address, to access the workers

## Deployment

Deploying external DNS for Exoscale is actually nearly identical to deploying
it for other providers. This is what a sample `deployment.yaml` looks like:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: external-dns
spec:
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: external-dns
  template:
    metadata:
      labels:
        app: external-dns
    spec:
      # Only use if you're also using RBAC
      # serviceAccountName: external-dns
      containers:
      - name: external-dns
        image: registry.k8s.io/external-dns/external-dns:v0.15.0
        args:
        - --source=ingress # or service or both
        - --provider=exoscale
        - --domain-filter=
        - --policy=sync # if you want DNS entries to get deleted as well
        - --txt-owner-id=
        - --exoscale-apikey=
        - --exoscale-apisecret=
        # - --exoscale-apizone=
        # - --exoscale-apienv=
```

Optional arguments `--exoscale-apizone` and `--exoscale-apienv` define [Exoscale API Zone](https://community.exoscale.com/documentation/platform/exoscale-datacenter-zones/)
(default `ch-gva-2`) and Exoscale API environment (default `api`, can be used to target non-production API server) respectively.

## RBAC

If your cluster is RBAC enabled, you also need to setup the following, before you can run external-dns:

```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: external-dns
  namespace: default

---

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: external-dns
rules:
- apiGroups: [""]
  resources: ["services","endpoints","pods"]
  verbs: ["get","watch","list"]
- apiGroups: ["extensions","networking.k8s.io"]
  resources: ["ingresses"]
  verbs: ["get","watch","list"]
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["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: default
```

## Testing and Verification

**Important!**: Remember to change `example.com` with your own domain throughout the following text.

Spin up a simple nginx HTTP server with the following spec (`kubectl apply -f`):

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx
  annotations:
    external-dns.alpha.kubernetes.io/target:
spec:
  ingressClassName: nginx
  rules:
  - host: via-ingress.example.com
    http:
      paths:
      - backend:
          service:
            name: "nginx"
            port:
              number: 80
        path: /
        pathType: Prefix

---

apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: nginx

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
        ports:
        - containerPort: 80
```

**Important!**: Don't run dig, nslookup or similar immediately (until you've
confirmed the record exists). You'll get hit by [negative DNS caching](https://tools.ietf.org/html/rfc2308), which is hard to flush.

Wait about 30s-1m (interval for external-dns to kick in), then check Exoscales [portal](https://portal.exoscale.com/dns/example.com)... via-ingress.example.com should appear as a A and TXT record with your Elastic-IP-address.

Exoscale     Prerequisites  Exoscale provider support was added via  this PR  https   github com kubernetes sigs external dns pull 625   thus you need to use external dns v0 5 5   The Exoscale provider expects that your Exoscale zones  you wish to add records to  already exists and are configured correctly  It does not add  remove or configure new zones in anyway   To do this please refer to the  Exoscale DNS documentation  https   community exoscale com documentation dns     Additionally you will have to provide the Exoscale        API Key   API Secret   Elastic IP address  to access the workers     Deployment  Deploying external DNS for Exoscale is actually nearly identical to deploying it for other providers  This is what a sample  deployment yaml  looks like      yaml apiVersion  apps v1 kind  Deployment metadata    name  external dns spec    strategy      type  Recreate   selector      matchLabels        app  external dns   template      metadata        labels          app  external dns     spec          Only use if you re also using RBAC         serviceAccountName  external dns       containers          name  external dns         image  registry k8s io external dns external dns v0 15 0         args              source ingress   or service or both             provider exoscale             domain filter              policy sync   if you want DNS entries to get deleted as well             txt owner id              exoscale apikey              exoscale apisecret                exoscale apizone                exoscale apienv       Optional arguments    exoscale apizone  and    exoscale apienv  define  Exoscale API Zone  https   community exoscale com documentation platform exoscale datacenter zones    default  ch gva 2   and Exoscale API environment  default  api   can be used to target non production API server  respectively      RBAC  If your cluster is RBAC enabled  you also need to setup the following  before you can run external dns      yaml apiVersion  v1 kind  ServiceAccount metadata    name  external dns   namespace  default       apiVersion  rbac authorization k8s io v1 kind  ClusterRole metadata    name  external dns rules    apiGroups         resources    services   endpoints   pods     verbs    get   watch   list     apiGroups    extensions   networking k8s io     resources    ingresses     verbs    get   watch   list     apiGroups         resources    nodes     verbs    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  default         Testing and Verification    Important     Remember to change  example com  with your own domain throughout the following text   Spin up a simple nginx HTTP server with the following spec   kubectl apply  f        yaml apiVersion  networking k8s io v1 kind  Ingress metadata    name  nginx   annotations      external dns alpha kubernetes io target   spec    ingressClassName  nginx   rules      host  via ingress example com     http        paths          backend            service              name   nginx              port                number  80         path            pathType  Prefix       apiVersion  v1 kind  Service metadata    name  nginx spec    ports      port  80     targetPort  80   selector      app  nginx       apiVersion  apps v1 kind  Deployment metadata    name  nginx spec    selector      matchLabels        app  nginx   template      metadata        labels          app  nginx     spec        containers          image  nginx         name  nginx         ports            containerPort  80        Important     Don t run dig  nslookup or similar immediately  until you ve confirmed the record exists   You ll get hit by  negative DNS caching  https   tools ietf org html rfc2308   which is hard to flush   Wait about 30s 1m  interval for external dns to kick in   then check Exoscales  portal  https   portal exoscale com dns example com     via ingress example com should appear as a A and TXT record with your Elastic IP address


external-dns IBM Cloud commands and assumes that the Kubernetes cluster was created via IBM Cloud Kubernetes Service and commands are being run on an orchestration node This tutorial uses for all This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster using IBMCloud DNS Creating a IBMCloud DNS zone IBMCloud The IBMCloud provider for ExternalDNS will find suitable zones for domains it manages it will

Answer:
# IBMCloud

This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster using IBMCloud DNS.

This tutorial uses [IBMCloud CLI](https://cloud.ibm.com/docs/cli?topic=cli-getting-started) for all
IBM Cloud commands and assumes that the Kubernetes cluster was created via IBM Cloud Kubernetes Service and `kubectl` commands
are being run on an orchestration node.

## Creating a IBMCloud DNS zone
The IBMCloud provider for ExternalDNS will find suitable zones for domains it manages; it will
not automatically create zones.
For public zone, This tutorial assume that the [IBMCloud Internet Services](https://cloud.ibm.com/catalog/services/internet-services) was provisioned and the [cis cli plugin](https://cloud.ibm.com/docs/cis?topic=cis-cli-plugin-cis-cli) was installed with IBMCloud CLI
For private zone, This tutorial assume that the [IBMCloud DNS Services](https://cloud.ibm.com/catalog/services/dns-services) was provisioned and the [dns cli plugin](https://cloud.ibm.com/docs/dns-svcs?topic=dns-svcs-cli-plugin-dns-services-cli-commands) was installed with IBMCloud CLI

### Public Zone
For this tutorial, we create public zone named `example.com` on IBMCloud Internet Services instance `external-dns-public`
```
$ ibmcloud cis domain-add example.com -i external-dns-public
```
Follow [step](https://cloud.ibm.com/docs/cis?topic=cis-getting-started#configure-your-name-servers-with-the-registrar-or-existing-dns-provider) to active your zone

### Private Zone
For this tutorial, we create private zone named `example.com` on IBMCloud DNS Services instance `external-dns-private`
```
$ ibmcloud dns zone-create example.com -i external-dns-private
```

## Creating configuration file

The preferred way to inject the configuration file is by using a Kubernetes secret. The secret should contain an object named azure.json with content similar to this:

```
{
  "apiKey": "1234567890abcdefghijklmnopqrstuvwxyz",
  "instanceCrn": "crn:v1:bluemix:public:internet-svcs:global:a/bcf1865e99742d38d2d5fc3fb80a5496:b950da8a-5be6-4691-810e-36388c77b0a3::"
}
```

You can create or find the `apiKey` in your ibmcloud IAM --> [API Keys page](https://cloud.ibm.com/iam/apikeys)

You can find the `instanceCrn` in your service instance details

Now you can create a file named 'ibmcloud.json' with values gathered above and with the structure of the example above. Use this file to create a Kubernetes secret:
```
$ kubectl create secret generic ibmcloud-config-file --from-file=/local/path/to/ibmcloud.json
```
## Deploy ExternalDNS

Connect your `kubectl` client to the cluster you want to test ExternalDNS with.
Then apply one of the following manifests file to deploy ExternalDNS.

### Manifest (for clusters without RBAC enabled)

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: external-dns
spec:
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: external-dns
  template:
    metadata:
      labels:
        app: external-dns
    spec:
      containers:
      - name: external-dns
        image: registry.k8s.io/external-dns/external-dns:v0.15.0
        args:
        - --source=service # ingress is also possible
        - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
        - --provider=ibmcloud
        - --ibmcloud-proxied # (optional) enable the proxy feature of IBMCloud
        volumeMounts:
        - name: ibmcloud-config-file
          mountPath: /etc/kubernetes
          readOnly: true
      volumes:
      - name: ibmcloud-config-file
        secret:
          secretName: ibmcloud-config-file
          items:
          - key: externaldns-config.json
            path: ibmcloud.json
```

### Manifest (for clusters with RBAC enabled)

```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: 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: ["extensions","networking.k8s.io"]
  resources: ["ingresses"]
  verbs: ["get","watch","list"]
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["list", "watch"]
---
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: default
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: external-dns
spec:
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: external-dns
  template:
    metadata:
      labels:
        app: external-dns
    spec:
      serviceAccountName: external-dns
      containers:
      - name: external-dns
        image: registry.k8s.io/external-dns/external-dns:v0.15.0
        args:
        - --source=service # ingress is also possible
        - --domain-filter=example.com # (optional) limit to only example.com domains; change to match the zone created above.
        - --provider=ibmcloud
        - --ibmcloud-proxied # (optional) enable the proxy feature of IBMCloud public zone
        volumeMounts:
        - name: ibmcloud-config-file
          mountPath: /etc/kubernetes
          readOnly: true
      volumes:
      - name: ibmcloud-config-file
        secret:
          secretName: ibmcloud-config-file
          items:
          - key: externaldns-config.json
            path: ibmcloud.json
```

## Deploying an Nginx Service

Create a service file called `nginx.yaml` with the following contents:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
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
  annotations:
    external-dns.alpha.kubernetes.io/hostname: www.example.com
    external-dns.alpha.kubernetes.io/ttl: "120" #optional
spec:
  selector:
    app: nginx
  type: LoadBalancer
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
```

Note the annotation on the service; use the hostname as the IBMCloud DNS zone created above. The annotation may also be a subdomain
of the DNS zone (e.g. 'www.example.com').

By setting the TTL annotation on the service, you have to pass a valid TTL, which must be 120 or above.
This annotation is optional, if you won't set it, it will be 1 (automatic) which is 300.

ExternalDNS uses this annotation to determine what services should be registered with DNS.  Removing the annotation
will cause ExternalDNS to remove the corresponding DNS records.

Create the deployment and service:

```
$ kubectl create -f nginx.yaml
```

Depending where you run your service it can take a little while for your cloud provider to create an external IP for the service.

Once the service has an external IP assigned, ExternalDNS will notice the new service IP address and synchronize
the IBMCloud DNS records.

## Verifying IBMCloud DNS records
Run the following command to view the A records:

### Public Zone
```
# Get the domain ID with below command on IBMCloud Internet Services instance `external-dns-public`
$ ibmcloud cis domains -i external-dns-public
# Get the records with domain ID
$ ibmcloud cis dns-records DOMAIN_ID  -i external-dns-public
```

### Private Zone
```
# Get the domain ID with below command on IBMCloud DNS Services instance `external-dns-private`
$ ibmcloud dns zones -i external-dns-private
# Get the records with domain ID
$ ibmcloud dns resource-records ZONE_ID  -i external-dns-public
```
This should show the external IP address of the service as the A record for your domain.

## Cleanup

Now that we have verified that ExternalDNS will automatically manage IBMCloud DNS records, we can delete the tutorial's example:

```
$ kubectl delete -f nginx.yaml
$ kubectl delete -f externaldns.yaml
```

## Setting proxied records on public zone

Using the `external-dns.alpha.kubernetes.io/ibmcloud-proxied: "true"` annotation on your ingress or service, you can specify if the proxy feature of IBMCloud public DNS should be enabled for that record. This setting will override the global `--ibmcloud-proxied` setting.

## Active priviate zone with VPC allocated

By default, IBMCloud DNS Services don't active your private zone with new zone added, with externale DNS, you can use `external-dns.alpha.kubernetes.io/ibmcloud-vpc: "crn:v1:bluemix:public:is:us-south:a/bcf1865e99742d38d2d5fc3fb80a5496::vpc:r006-74353823-a60d-42e4-97c5-5e2551278435"` annotation on your ingress or service, it will active your private zone with in specific VPC for that record created in. this setting won't work if the private zone was active already.

Note: the annotaion value is the VPC CRN, every IBM Cloud service have a valid CRN.

IBMCloud  This tutorial describes how to setup ExternalDNS for usage within a Kubernetes cluster using IBMCloud DNS   This tutorial uses  IBMCloud CLI  https   cloud ibm com docs cli topic cli getting started  for all IBM Cloud commands and assumes that the Kubernetes cluster was created via IBM Cloud Kubernetes Service and  kubectl  commands are being run on an orchestration node      Creating a IBMCloud DNS zone The IBMCloud provider for ExternalDNS will find suitable zones for domains it manages  it will not automatically create zones  For public zone  This tutorial assume that the  IBMCloud Internet Services  https   cloud ibm com catalog services internet services  was provisioned and the  cis cli plugin  https   cloud ibm com docs cis topic cis cli plugin cis cli  was installed with IBMCloud CLI For private zone  This tutorial assume that the  IBMCloud DNS Services  https   cloud ibm com catalog services dns services  was provisioned and the  dns cli plugin  https   cloud ibm com docs dns svcs topic dns svcs cli plugin dns services cli commands  was installed with IBMCloud CLI      Public Zone For this tutorial  we create public zone named  example com  on IBMCloud Internet Services instance  external dns public        ibmcloud cis domain add example com  i external dns public     Follow  step  https   cloud ibm com docs cis topic cis getting started configure your name servers with the registrar or existing dns provider  to active your zone      Private Zone For this tutorial  we create private zone named  example com  on IBMCloud DNS Services instance  external dns private        ibmcloud dns zone create example com  i external dns private         Creating configuration file  The preferred way to inject the configuration file is by using a Kubernetes secret  The secret should contain an object named azure json with content similar to this            apiKey    1234567890abcdefghijklmnopqrstuvwxyz      instanceCrn    crn v1 bluemix public internet svcs global a bcf1865e99742d38d2d5fc3fb80a5496 b950da8a 5be6 4691 810e 36388c77b0a3           You can create or find the  apiKey  in your ibmcloud IAM      API Keys page  https   cloud ibm com iam apikeys   You can find the  instanceCrn  in your service instance details  Now you can create a file named  ibmcloud json  with values gathered above and with the structure of the example above  Use this file to create a Kubernetes secret        kubectl create secret generic ibmcloud config file   from file  local path to ibmcloud json        Deploy ExternalDNS  Connect your  kubectl  client to the cluster you want to test ExternalDNS with  Then apply one of the following manifests file to deploy ExternalDNS       Manifest  for clusters without RBAC enabled      yaml apiVersion  apps v1 kind  Deployment metadata    name  external dns spec    strategy      type  Recreate   selector      matchLabels        app  external dns   template      metadata        labels          app  external dns     spec        containers          name  external dns         image  registry k8s io external dns external dns v0 15 0         args              source service   ingress is also possible             domain filter example com    optional  limit to only example com domains  change to match the zone created above              provider ibmcloud             ibmcloud proxied    optional  enable the proxy feature of IBMCloud         volumeMounts            name  ibmcloud config file           mountPath   etc kubernetes           readOnly  true       volumes          name  ibmcloud config file         secret            secretName  ibmcloud config file           items              key  externaldns config json             path  ibmcloud json          Manifest  for clusters with RBAC enabled      yaml apiVersion  v1 kind  ServiceAccount metadata    name  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    extensions   networking k8s io     resources    ingresses      verbs    get   watch   list     apiGroups         resources    nodes     verbs    list    watch       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  default     apiVersion  apps v1 kind  Deployment metadata    name  external dns spec    strategy      type  Recreate   selector      matchLabels        app  external dns   template      metadata        labels          app  external dns     spec        serviceAccountName  external dns       containers          name  external dns         image  registry k8s io external dns external dns v0 15 0         args              source service   ingress is also possible             domain filter example com    optional  limit to only example com domains  change to match the zone created above              provider ibmcloud             ibmcloud proxied    optional  enable the proxy feature of IBMCloud public zone         volumeMounts            name  ibmcloud config file           mountPath   etc kubernetes           readOnly  true       volumes          name  ibmcloud config file         secret            secretName  ibmcloud config file           items              key  externaldns config json             path  ibmcloud json         Deploying an Nginx Service  Create a service file called  nginx yaml  with the following contents      yaml apiVersion  apps v1 kind  Deployment metadata    name  nginx 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   annotations      external dns alpha kubernetes io hostname  www example com     external dns alpha kubernetes io ttl   120   optional spec    selector      app  nginx   type  LoadBalancer   ports        protocol  TCP       port  80       targetPort  80      Note the annotation on the service  use the hostname as the IBMCloud DNS zone created above  The annotation may also be a subdomain of the DNS zone  e g   www example com     By setting the TTL annotation on the service  you have to pass a valid TTL  which must be 120 or above  This annotation is optional  if you won t set it  it will be 1  automatic  which is 300   ExternalDNS uses this annotation to determine what services should be registered with DNS   Removing the annotation will cause ExternalDNS to remove the corresponding DNS records   Create the deployment and service         kubectl create  f nginx yaml      Depending where you run your service it can take a little while for your cloud provider to create an external IP for the service   Once the service has an external IP assigned  ExternalDNS will notice the new service IP address and synchronize the IBMCloud DNS records      Verifying IBMCloud DNS records Run the following command to view the A records       Public Zone       Get the domain ID with below command on IBMCloud Internet Services instance  external dns public    ibmcloud cis domains  i external dns public   Get the records with domain ID   ibmcloud cis dns records DOMAIN ID   i external dns public          Private Zone       Get the domain ID with below command on IBMCloud DNS Services instance  external dns private    ibmcloud dns zones  i external dns private   Get the records with domain ID   ibmcloud dns resource records ZONE ID   i external dns public     This should show the external IP address of the service as the A record for your domain      Cleanup  Now that we have verified that ExternalDNS will automatically manage IBMCloud DNS records  we can delete the tutorial s example         kubectl delete  f nginx yaml   kubectl delete  f externaldns yaml         Setting proxied records on public zone  Using the  external dns alpha kubernetes io ibmcloud proxied   true   annotation on your ingress or service  you can specify if the proxy feature of IBMCloud public DNS should be enabled for that record  This setting will override the global    ibmcloud proxied  setting      Active priviate zone with VPC allocated  By default  IBMCloud DNS Services don t active your private zone with new zone added  with externale DNS  you can use  external dns alpha kubernetes io ibmcloud vpc   crn v1 bluemix public is us south a bcf1865e99742d38d2d5fc3fb80a5496  vpc r006 74353823 a60d 42e4 97c5 5e2551278435   annotation on your ingress or service  it will active your private zone with in specific VPC for that record created in  this setting won t work if the private zone was active already   Note  the annotaion value is the VPC CRN  every IBM Cloud service have a valid CRN


external-dns GKE with nginx ingress controller This tutorial describes how to setup ExternalDNS for usage within a GKE cluster that doesn t make use of Google s but rather uses for that task gcloud config set project zalando external dns test Set up your environment console Setup your environment to work with Google Cloud Platform Fill in your values as needed e g target project

Answer:
# GKE with nginx-ingress-controller

This tutorial describes how to setup ExternalDNS for usage within a GKE cluster that doesn't make use of Google's [default ingress controller](https://github.com/kubernetes/ingress-gce) but rather uses [nginx-ingress-controller](https://github.com/kubernetes/ingress-nginx) for that task.

## Set up your environment

Setup your environment to work with Google Cloud Platform. Fill in your values as needed, e.g. target project.

```console
$ gcloud config set project "zalando-external-dns-test"
$ gcloud config set compute/region "europe-west1"
$ gcloud config set compute/zone "europe-west1-d"
```

## GKE Node Scopes

The following instructions use instance scopes to provide ExternalDNS with the
permissions it needs to manage DNS records. Note that since these permissions
are associated with the instance, all pods in the cluster will also have these
permissions. As such, this approach is not suitable for anything but testing
environments.

Create a GKE cluster without using the default ingress controller.

```console
$ gcloud container clusters create "external-dns" \
    --num-nodes 1 \
    --scopes "https://www.googleapis.com/auth/ndev.clouddns.readwrite"
```

Create a DNS zone which will contain the managed DNS records.

```console
$ gcloud dns managed-zones create "external-dns-test-gcp-zalan-do" \
    --dns-name "external-dns-test.gcp.zalan.do." \
    --description "Automatically managed zone by ExternalDNS"
```

Make a note of the nameservers that were assigned to your new zone.

```console
$ gcloud dns record-sets list \
    --zone "external-dns-test-gcp-zalan-do" \
    --name "external-dns-test.gcp.zalan.do." \
    --type NS
NAME                             TYPE  TTL    DATA
external-dns-test.gcp.zalan.do.  NS    21600  ns-cloud-e1.googledomains.com.,ns-cloud-e2.googledomains.com.,ns-cloud-e3.googledomains.com.,ns-cloud-e4.googledomains.com.
```

In this case it's `ns-cloud-{e1-e4}.googledomains.com.` but your's could slightly differ, e.g. `{a1-a4}`, `{b1-b4}` etc.

Tell the parent zone where to find the DNS records for this zone by adding the corresponding NS records there. Assuming the parent zone is "gcp-zalan-do" and the domain is "gcp.zalan.do" and that it's also hosted at Google we would do the following.

```console
$ gcloud dns record-sets transaction start --zone "gcp-zalan-do"
$ gcloud dns record-sets transaction add ns-cloud-e{1..4}.googledomains.com. \
    --name "external-dns-test.gcp.zalan.do." --ttl 300 --type NS --zone "gcp-zalan-do"
$ gcloud dns record-sets transaction execute --zone "gcp-zalan-do"
```

Connect your `kubectl` client to the cluster you just created and bind your GCP
user to the cluster admin role in Kubernetes.

```console
$ gcloud container clusters get-credentials "external-dns"
$ kubectl create clusterrolebinding cluster-admin-me \
    --clusterrole=cluster-admin --user="$(gcloud config get-value account)"
```

### Deploy the nginx ingress controller

First, you need to deploy the nginx-based ingress controller. It can be deployed in at least two modes: Leveraging a Layer 4 load balancer in front of the nginx proxies or directly targeting pods with hostPorts on your worker nodes. ExternalDNS doesn't really care and supports both modes.

#### Default Backend

The nginx controller uses a default backend that it serves when no Ingress rule matches. This is a separate Service that can be picked by you. We'll use the default backend that's used by other ingress controllers for that matter. Apply the following manifests to your cluster to deploy the default backend.

```yaml
apiVersion: v1
kind: Service
metadata:
  name: default-http-backend
spec:
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: default-http-backend

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: default-http-backend
spec:
  selector:
    matchLabels:
      app: default-http-backend
  template:
    metadata:
      labels:
        app: default-http-backend
    spec:
      containers:
      - name: default-http-backend
        image: gcr.io/google_containers/defaultbackend:1.3
```

#### Without a separate TCP load balancer

By default, the controller will update your Ingress objects with the public IPs of the nodes running your nginx controller instances. You should run multiple instances in case of pod or node failure. The controller will do leader election and will put multiple IPs as targets in your Ingress objects in that case. It could also make sense to run it as a DaemonSet. However, we'll just run a single replica. You have to open the respective ports on all of your worker nodes to allow nginx to receive traffic.

```console
$ gcloud compute firewall-rules create "allow-http" --allow tcp:80 --source-ranges "0.0.0.0/0" --target-tags "gke-external-dns-9488ba14-node"
$ gcloud compute firewall-rules create "allow-https" --allow tcp:443 --source-ranges "0.0.0.0/0" --target-tags "gke-external-dns-9488ba14-node"
```

Change `--target-tags` to the corresponding tags of your nodes. You can find them by describing your instances or by looking at the default firewall rules created by GKE for your cluster.

Apply the following manifests to your cluster to deploy the nginx-based ingress controller. Note, how it receives a reference to the default backend's Service and that it listens on hostPorts. (You may have to use `hostNetwork: true` as well.)

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-ingress-controller
spec:
  selector:
    matchLabels:
      app: nginx-ingress-controller
  template:
    metadata:
      labels:
        app: nginx-ingress-controller
    spec:
      containers:
      - name: nginx-ingress-controller
        image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3
        args:
        - /nginx-ingress-controller
        - --default-backend-service=default/default-http-backend
        env:
          - name: POD_NAME
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: POD_NAMESPACE
            valueFrom:
              fieldRef:
                fieldPath: metadata.namespace
        ports:
        - containerPort: 80
          hostPort: 80
        - containerPort: 443
          hostPort: 443
```

#### With a separate TCP load balancer

However, you can also have the ingress controller proxied by a Kubernetes Service. This will instruct the controller to populate this Service's external IP as the external IP of the Ingress. This exposes the nginx proxies via a Layer 4 load balancer (`type=LoadBalancer`) which is more reliable than the other method. With that approach, you can run as many nginx proxy instances on your cluster as you like or have them autoscaled. This is the preferred way of running the nginx controller.

Apply the following manifests to your cluster. Note, how the controller is receiving an additional flag telling it which Service it should treat as its public endpoint and how it doesn't need hostPorts anymore.

Apply the following manifests to run the controller in this mode.

```yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-ingress-controller
spec:
  type: LoadBalancer
  ports:
  - name: http
    port: 80
    targetPort: 80
  - name: https
    port: 443
    targetPort: 443
  selector:
    app: nginx-ingress-controller

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-ingress-controller
spec:
  selector:
    matchLabels:
      app: nginx-ingress-controller
  template:
    metadata:
      labels:
        app: nginx-ingress-controller
    spec:
      containers:
      - name: nginx-ingress-controller
        image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.3
        args:
        - /nginx-ingress-controller
        - --default-backend-service=default/default-http-backend
        - --publish-service=default/nginx-ingress-controller
        env:
          - name: POD_NAME
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: POD_NAMESPACE
            valueFrom:
              fieldRef:
                fieldPath: metadata.namespace
        ports:
        - containerPort: 80
        - containerPort: 443
```

### Deploy ExternalDNS

Apply the following manifest file to deploy ExternalDNS.

```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: 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: ["extensions","networking.k8s.io"]
  resources: ["ingresses"]
  verbs: ["get","watch","list"]
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["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: default
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: external-dns
spec:
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: external-dns
  template:
    metadata:
      labels:
        app: external-dns
    spec:
      serviceAccountName: external-dns
      containers:
      - name: external-dns
        image: registry.k8s.io/external-dns/external-dns:v0.15.0
        args:
        - --source=ingress
        - --domain-filter=external-dns-test.gcp.zalan.do
        - --provider=google
        - --google-project=zalando-external-dns-test
        - --registry=txt
        - --txt-owner-id=my-identifier
```

Use `--dry-run` if you want to be extra careful on the first run. Note, that you will not see any records created when you are running in dry-run mode. You can, however, inspect the logs and watch what would have been done.

### Deploy a sample application

Create the following sample application to test that ExternalDNS works.

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx
spec:
  ingressClassName: nginx
  rules:
  - host: via-ingress.external-dns-test.gcp.zalan.do
    http:
      paths:
      - path: /
        backend:
          service:
            name: nginx
            port:
              number: 80
        pathType: Prefix

---

apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: nginx

---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
        ports:
        - containerPort: 80
```

After roughly two minutes check that a corresponding DNS record for your Ingress was created.

```console
$ gcloud dns record-sets list \
    --zone "external-dns-test-gcp-zalan-do" \
    --name "via-ingress.external-dns-test.gcp.zalan.do." \
    --type A
NAME                                         TYPE  TTL  DATA
via-ingress.external-dns-test.gcp.zalan.do.  A     300  35.187.1.246
```

Let's check that we can resolve this DNS name as well.

```console
dig +short @ns-cloud-e1.googledomains.com. via-ingress.external-dns-test.gcp.zalan.do.
35.187.1.246
```

Try with `curl` as well.

```console
$ curl via-ingress.external-dns-test.gcp.zalan.do
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
</head>
<body>
...
</body>
</html>
```

### Clean up

Make sure to delete all Service and Ingress objects before terminating the cluster so all load balancers and DNS entries get cleaned up correctly.

```console
$ kubectl delete service nginx-ingress-controller
$ kubectl delete ingress nginx
```

Give ExternalDNS some time to clean up the DNS records for you. Then delete the managed zone and cluster.

```console
$ gcloud dns managed-zones delete "external-dns-test-gcp-zalan-do"
$ gcloud container clusters delete "external-dns"
```

Also delete the NS records for your removed zone from the parent zone.

```console
$ gcloud dns record-sets transaction start --zone "gcp-zalan-do"
$ gcloud dns record-sets transaction remove ns-cloud-e{1..4}.googledomains.com. \
    --name "external-dns-test.gcp.zalan.do." --ttl 300 --type NS --zone "gcp-zalan-do"
$ gcloud dns record-sets transaction execute --zone "gcp-zalan-do"
```

## GKE with Workload Identity

The following instructions use [GKE workload
identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity)
to provide ExternalDNS with the permissions it needs to manage DNS records.
Workload identity is the Google-recommended way to provide GKE workloads access
to GCP APIs.

Create a GKE cluster with workload identity enabled and without the
HttpLoadBalancing add-on.

```console
$ gcloud container clusters create external-dns \
    --workload-metadata-from-node=GKE_METADATA_SERVER \
    --identity-namespace=zalando-external-dns-test.svc.id.goog \
    --addons=HorizontalPodAutoscaling
```

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

```console
$ 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.

```console
$ gcloud projects add-iam-policy-binding zalando-external-dns-test \
    --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.

```console
$ gcloud iam service-accounts add-iam-policy-binding "$sa_email" \
    --member="serviceAccount:zalando-external-dns-test.svc.id.goog[external-dns/external-dns]" \
    --role=roles/iam.workloadIdentityUser
```

Create a DNS zone which will contain the managed DNS records.

```console
$ gcloud dns managed-zones create external-dns-test-gcp-zalan-do \
    --dns-name=external-dns-test.gcp.zalan.do. \
    --description="Automatically managed zone by ExternalDNS"
```

Make a note of the nameservers that were assigned to your new zone.

```console
$ gcloud dns record-sets list \
    --zone=external-dns-test-gcp-zalan-do \
    --name=external-dns-test.gcp.zalan.do. \
    --type NS
NAME                             TYPE  TTL    DATA
external-dns-test.gcp.zalan.do.  NS    21600  ns-cloud-e1.googledomains.com.,ns-cloud-e2.googledomains.com.,ns-cloud-e3.googledomains.com.,ns-cloud-e4.googledomains.com.
```

In this case it's `ns-cloud-{e1-e4}.googledomains.com.` but your's could
slightly differ, e.g. `{a1-a4}`, `{b1-b4}` etc.

Tell the parent zone where to find the DNS records for this zone by adding the
corresponding NS records there. Assuming the parent zone is "gcp-zalan-do" and
the domain is "gcp.zalan.do" and that it's also hosted at Google we would do the
following.

```console
$ gcloud dns record-sets transaction start --zone=gcp-zalan-do
$ gcloud dns record-sets transaction add ns-cloud-e{1..4}.googledomains.com. \
    --name=external-dns-test.gcp.zalan.do. --ttl 300 --type NS --zone=gcp-zalan-do
$ gcloud dns record-sets transaction execute --zone=gcp-zalan-do
```

Connect your `kubectl` client to the cluster you just created and bind your GCP
user to the cluster admin role in Kubernetes.

```console
$ gcloud container clusters get-credentials external-dns
$ kubectl create clusterrolebinding cluster-admin-me \
    --clusterrole=cluster-admin --user="$(gcloud config get-value account)"
```

### Deploy ingress-nginx

Follow the [ingress-nginx GKE installation
instructions](https://kubernetes.github.io/ingress-nginx/deploy/#gce-gke) to
deploy it to the cluster.

```console
$ kubectl apply -f \
    https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.35.0/deploy/static/provider/cloud/deploy.yaml
```

### Deploy ExternalDNS

Apply the following manifest file to deploy external-dns.

```yaml
apiVersion: v1
kind: Namespace
metadata:
  name: external-dns
---
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: ["extensions", "networking.k8s.io"]
    resources: ["ingresses"]
    verbs: ["get", "watch", "list"]
  - apiGroups: [""]
    resources: ["nodes"]
    verbs: ["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
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: external-dns
  namespace: external-dns
spec:
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: external-dns
  template:
    metadata:
      labels:
        app: external-dns
    spec:
      containers:
        - args:
            - --source=ingress
            - --domain-filter=external-dns-test.gcp.zalan.do
            - --provider=google
            - --google-project=zalando-external-dns-test
            - --registry=txt
            - --txt-owner-id=my-identifier
          image: registry.k8s.io/external-dns/external-dns:v0.15.0
          name: external-dns
      securityContext:
        fsGroup: 65534
        runAsUser: 65534
      serviceAccountName: external-dns
```

Then add the proper workload identity annotation to the cert-manager service
account.

```bash
$ kubectl annotate serviceaccount --namespace=external-dns external-dns \
    "iam.gke.io/gcp-service-account=$sa_email"
```

### Deploy a sample application

Create the following sample application to test that ExternalDNS works.

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx
spec:
  ingressClassName: nginx
  rules:
  - host: via-ingress.external-dns-test.gcp.zalan.do
    http:
      paths:
      - path: /
        backend:
          service:
            name: nginx
            port:
              number: 80
        pathType: Prefix
---
apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
        ports:
        - containerPort: 80
```

After roughly two minutes check that a corresponding DNS record for your ingress
was created.

```console
$ gcloud dns record-sets list \
    --zone "external-dns-test-gcp-zalan-do" \
    --name "via-ingress.external-dns-test.gcp.zalan.do." \
    --type A
NAME                                         TYPE  TTL  DATA
via-ingress.external-dns-test.gcp.zalan.do.  A     300  35.187.1.246
```

Let's check that we can resolve this DNS name as well.

```console
$ dig +short @ns-cloud-e1.googledomains.com. via-ingress.external-dns-test.gcp.zalan.do.
35.187.1.246
```

Try with `curl` as well.

```console
$ curl via-ingress.external-dns-test.gcp.zalan.do
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
</head>
<body>
...
</body>
</html>
```

### Clean up

Make sure to delete all service and ingress objects before terminating the
cluster so all load balancers and DNS entries get cleaned up correctly.

```console
$ kubectl delete service --namespace=ingress-nginx ingress-nginx-controller
$ kubectl delete ingress nginx
```

Give ExternalDNS some time to clean up the DNS records for you. Then delete the
managed zone and cluster.

```console
$ gcloud dns managed-zones delete external-dns-test-gcp-zalan-do
$ gcloud container clusters delete external-dns
```

Also delete the NS records for your removed zone from the parent zone.

```console
$ gcloud dns record-sets transaction start --zone gcp-zalan-do
$ gcloud dns record-sets transaction remove ns-cloud-e{1..4}.googledomains.com. \
    --name=external-dns-test.gcp.zalan.do. --ttl 300 --type NS --zone=gcp-zalan-do
$ gcloud dns record-sets transaction execute --zone=gcp-zalan-do
```

## User Demo How-To Blogs and Examples

* Run external-dns on GKE with workload identity. See [Kubernetes, ingress-nginx, cert-manager & external-dns](https://blog.atomist.com/kubernetes-ingress-nginx-cert-manager-external-dns/)

GKE with nginx ingress controller  This tutorial describes how to setup ExternalDNS for usage within a GKE cluster that doesn t make use of Google s  default ingress controller  https   github com kubernetes ingress gce  but rather uses  nginx ingress controller  https   github com kubernetes ingress nginx  for that task      Set up your environment  Setup your environment to work with Google Cloud Platform  Fill in your values as needed  e g  target project      console   gcloud config set project  zalando external dns test    gcloud config set compute region  europe west1    gcloud config set compute zone  europe west1 d          GKE Node Scopes  The following instructions use instance scopes to provide ExternalDNS with the permissions it needs to manage DNS records  Note that since these permissions are associated with the instance  all pods in the cluster will also have these permissions  As such  this approach is not suitable for anything but testing environments   Create a GKE cluster without using the default ingress controller      console   gcloud container clusters create  external dns          num nodes 1         scopes  https   www googleapis com auth ndev clouddns readwrite       Create a DNS zone which will contain the managed DNS records      console   gcloud dns managed zones create  external dns test gcp zalan do          dns name  external dns test gcp zalan do           description  Automatically managed zone by ExternalDNS       Make a note of the nameservers that were assigned to your new zone      console   gcloud dns record sets list         zone  external dns test gcp zalan do          name  external dns test gcp zalan do           type NS NAME                             TYPE  TTL    DATA external dns test gcp zalan do   NS    21600  ns cloud e1 googledomains com  ns cloud e2 googledomains com  ns cloud e3 googledomains com  ns cloud e4 googledomains com       In this case it s  ns cloud  e1 e4  googledomains com   but your s could slightly differ  e g    a1 a4      b1 b4   etc   Tell the parent zone where to find the DNS records for this zone by adding the corresponding NS records there  Assuming the parent zone is  gcp zalan do  and the domain is  gcp zalan do  and that it s also hosted at Google we would do the following      console   gcloud dns record sets transaction start   zone  gcp zalan do    gcloud dns record sets transaction add ns cloud e 1  4  googledomains com          name  external dns test gcp zalan do     ttl 300   type NS   zone  gcp zalan do    gcloud dns record sets transaction execute   zone  gcp zalan do       Connect your  kubectl  client to the cluster you just created and bind your GCP user to the cluster admin role in Kubernetes      console   gcloud container clusters get credentials  external dns    kubectl create clusterrolebinding cluster admin me         clusterrole cluster admin   user    gcloud config get value account            Deploy the nginx ingress controller  First  you need to deploy the nginx based ingress controller  It can be deployed in at least two modes  Leveraging a Layer 4 load balancer in front of the nginx proxies or directly targeting pods with hostPorts on your worker nodes  ExternalDNS doesn t really care and supports both modes        Default Backend  The nginx controller uses a default backend that it serves when no Ingress rule matches  This is a separate Service that can be picked by you  We ll use the default backend that s used by other ingress controllers for that matter  Apply the following manifests to your cluster to deploy the default backend      yaml apiVersion  v1 kind  Service metadata    name  default http backend spec    ports      port  80     targetPort  8080   selector      app  default http backend       apiVersion  apps v1 kind  Deployment metadata    name  default http backend spec    selector      matchLabels        app  default http backend   template      metadata        labels          app  default http backend     spec        containers          name  default http backend         image  gcr io google containers defaultbackend 1 3           Without a separate TCP load balancer  By default  the controller will update your Ingress objects with the public IPs of the nodes running your nginx controller instances  You should run multiple instances in case of pod or node failure  The controller will do leader election and will put multiple IPs as targets in your Ingress objects in that case  It could also make sense to run it as a DaemonSet  However  we ll just run a single replica  You have to open the respective ports on all of your worker nodes to allow nginx to receive traffic      console   gcloud compute firewall rules create  allow http    allow tcp 80   source ranges  0 0 0 0 0    target tags  gke external dns 9488ba14 node    gcloud compute firewall rules create  allow https    allow tcp 443   source ranges  0 0 0 0 0    target tags  gke external dns 9488ba14 node       Change    target tags  to the corresponding tags of your nodes  You can find them by describing your instances or by looking at the default firewall rules created by GKE for your cluster   Apply the following manifests to your cluster to deploy the nginx based ingress controller  Note  how it receives a reference to the default backend s Service and that it listens on hostPorts   You may have to use  hostNetwork  true  as well       yaml apiVersion  apps v1 kind  Deployment metadata    name  nginx ingress controller spec    selector      matchLabels        app  nginx ingress controller   template      metadata        labels          app  nginx ingress controller     spec        containers          name  nginx ingress controller         image  gcr io google containers nginx ingress controller 0 9 0 beta 3         args             nginx ingress controller             default backend service default default http backend         env              name  POD NAME             valueFrom                fieldRef                  fieldPath  metadata name             name  POD NAMESPACE             valueFrom                fieldRef                  fieldPath  metadata namespace         ports            containerPort  80           hostPort  80           containerPort  443           hostPort  443           With a separate TCP load balancer  However  you can also have the ingress controller proxied by a Kubernetes Service  This will instruct the controller to populate this Service s external IP as the external IP of the Ingress  This exposes the nginx proxies via a Layer 4 load balancer   type LoadBalancer   which is more reliable than the other method  With that approach  you can run as many nginx proxy instances on your cluster as you like or have them autoscaled  This is the preferred way of running the nginx controller   Apply the following manifests to your cluster  Note  how the controller is receiving an additional flag telling it which Service it should treat as its public endpoint and how it doesn t need hostPorts anymore   Apply the following manifests to run the controller in this mode      yaml apiVersion  v1 kind  Service metadata    name  nginx ingress controller spec    type  LoadBalancer   ports      name  http     port  80     targetPort  80     name  https     port  443     targetPort  443   selector      app  nginx ingress controller       apiVersion  apps v1 kind  Deployment metadata    name  nginx ingress controller spec    selector      matchLabels        app  nginx ingress controller   template      metadata        labels          app  nginx ingress controller     spec        containers          name  nginx ingress controller         image  gcr io google containers nginx ingress controller 0 9 0 beta 3         args             nginx ingress controller             default backend service default default http backend             publish service default nginx ingress controller         env              name  POD NAME             valueFrom                fieldRef                  fieldPath  metadata name             name  POD NAMESPACE             valueFrom                fieldRef                  fieldPath  metadata namespace         ports            containerPort  80           containerPort  443          Deploy ExternalDNS  Apply the following manifest file to deploy ExternalDNS      yaml apiVersion  v1 kind  ServiceAccount metadata    name  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    extensions   networking k8s io     resources    ingresses      verbs    get   watch   list     apiGroups         resources    nodes     verbs    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  default     apiVersion  apps v1 kind  Deployment metadata    name  external dns spec    strategy      type  Recreate   selector      matchLabels        app  external dns   template      metadata        labels          app  external dns     spec        serviceAccountName  external dns       containers          name  external dns         image  registry k8s io external dns external dns v0 15 0         args              source ingress             domain filter external dns test gcp zalan do             provider google             google project zalando external dns test             registry txt             txt owner id my identifier      Use    dry run  if you want to be extra careful on the first run  Note  that you will not see any records created when you are running in dry run mode  You can  however  inspect the logs and watch what would have been done       Deploy a sample application  Create the following sample application to test that ExternalDNS works      yaml apiVersion  networking k8s io v1 kind  Ingress metadata    name  nginx spec    ingressClassName  nginx   rules      host  via ingress external dns test gcp zalan do     http        paths          path            backend            service              name  nginx             port                number  80         pathType  Prefix       apiVersion  v1 kind  Service metadata    name  nginx spec    ports      port  80     targetPort  80   selector      app  nginx       apiVersion  apps v1 kind  Deployment metadata    name  nginx spec    selector      matchLabels        app  nginx   template      metadata        labels          app  nginx     spec        containers          image  nginx         name  nginx         ports            containerPort  80      After roughly two minutes check that a corresponding DNS record for your Ingress was created      console   gcloud dns record sets list         zone  external dns test gcp zalan do          name  via ingress external dns test gcp zalan do           type A NAME                                         TYPE  TTL  DATA via ingress external dns test gcp zalan do   A     300  35 187 1 246      Let s check that we can resolve this DNS name as well      console dig  short  ns cloud e1 googledomains com  via ingress external dns test gcp zalan do  35 187 1 246      Try with  curl  as well      console   curl via ingress external dns test gcp zalan do   DOCTYPE html   html   head   title Welcome to nginx   title        head   body        body    html           Clean up  Make sure to delete all Service and Ingress objects before terminating the cluster so all load balancers and DNS entries get cleaned up correctly      console   kubectl delete service nginx ingress controller   kubectl delete ingress nginx      Give ExternalDNS some time to clean up the DNS records for you  Then delete the managed zone and cluster      console   gcloud dns managed zones delete  external dns test gcp zalan do    gcloud container clusters delete  external dns       Also delete the NS records for your removed zone from the parent zone      console   gcloud dns record sets transaction start   zone  gcp zalan do    gcloud dns record sets transaction remove ns cloud e 1  4  googledomains com          name  external dns test gcp zalan do     ttl 300   type NS   zone  gcp zalan do    gcloud dns record sets transaction execute   zone  gcp zalan do          GKE with Workload Identity  The following instructions use  GKE workload identity  https   cloud google com kubernetes engine docs how to workload identity  to provide ExternalDNS with the permissions it needs to manage DNS records  Workload identity is the Google recommended way to provide GKE workloads access to GCP APIs   Create a GKE cluster with workload identity enabled and without the HttpLoadBalancing add on      console   gcloud container clusters create external dns         workload metadata from node GKE METADATA SERVER         identity namespace zalando external dns test svc id goog         addons HorizontalPodAutoscaling      Create a GCP service account  GSA  for ExternalDNS and save its email address      console   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      console   gcloud projects add iam policy binding zalando external dns test         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      console   gcloud iam service accounts add iam policy binding   sa email          member  serviceAccount zalando external dns test svc id goog external dns external dns           role roles iam workloadIdentityUser      Create a DNS zone which will contain the managed DNS records      console   gcloud dns managed zones create external dns test gcp zalan do         dns name external dns test gcp zalan do          description  Automatically managed zone by ExternalDNS       Make a note of the nameservers that were assigned to your new zone      console   gcloud dns record sets list         zone external dns test gcp zalan do         name external dns test gcp zalan do          type NS NAME                             TYPE  TTL    DATA external dns test gcp zalan do   NS    21600  ns cloud e1 googledomains com  ns cloud e2 googledomains com  ns cloud e3 googledomains com  ns cloud e4 googledomains com       In this case it s  ns cloud  e1 e4  googledomains com   but your s could slightly differ  e g    a1 a4      b1 b4   etc   Tell the parent zone where to find the DNS records for this zone by adding the corresponding NS records there  Assuming the parent zone is  gcp zalan do  and the domain is  gcp zalan do  and that it s also hosted at Google we would do the following      console   gcloud dns record sets transaction start   zone gcp zalan do   gcloud dns record sets transaction add ns cloud e 1  4  googledomains com          name external dns test gcp zalan do    ttl 300   type NS   zone gcp zalan do   gcloud dns record sets transaction execute   zone gcp zalan do      Connect your  kubectl  client to the cluster you just created and bind your GCP user to the cluster admin role in Kubernetes      console   gcloud container clusters get credentials external dns   kubectl create clusterrolebinding cluster admin me         clusterrole cluster admin   user    gcloud config get value account            Deploy ingress nginx  Follow the  ingress nginx GKE installation instructions  https   kubernetes github io ingress nginx deploy  gce gke  to deploy it to the cluster      console   kubectl apply  f       https   raw githubusercontent com kubernetes ingress nginx controller v0 35 0 deploy static provider cloud deploy yaml          Deploy ExternalDNS  Apply the following manifest file to deploy external dns      yaml apiVersion  v1 kind  Namespace metadata    name  external dns     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    extensions    networking k8s io       resources    ingresses       verbs    get    watch    list       apiGroups           resources    nodes       verbs    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     apiVersion  apps v1 kind  Deployment metadata    name  external dns   namespace  external dns spec    strategy      type  Recreate   selector      matchLabels        app  external dns   template      metadata        labels          app  external dns     spec        containers            args                  source ingress                 domain filter external dns test gcp zalan do                 provider google                 google project zalando external dns test                 registry txt                 txt owner id my identifier           image  registry k8s io external dns external dns v0 15 0           name  external dns       securityContext          fsGroup  65534         runAsUser  65534       serviceAccountName  external dns      Then add the proper workload identity annotation to the cert manager service account      bash   kubectl annotate serviceaccount   namespace external dns external dns        iam gke io gcp service account  sa email           Deploy a sample application  Create the following sample application to test that ExternalDNS works      yaml apiVersion  networking k8s io v1 kind  Ingress metadata    name  nginx spec    ingressClassName  nginx   rules      host  via ingress external dns test gcp zalan do     http        paths          path            backend            service              name  nginx             port                number  80         pathType  Prefix     apiVersion  v1 kind  Service metadata    name  nginx spec    ports      port  80     targetPort  80   selector      app  nginx     apiVersion  apps v1 kind  Deployment metadata    name  nginx spec    selector      matchLabels        app  nginx   template      metadata        labels          app  nginx     spec        containers          image  nginx         name  nginx         ports            containerPort  80      After roughly two minutes check that a corresponding DNS record for your ingress was created      console   gcloud dns record sets list         zone  external dns test gcp zalan do          name  via ingress external dns test gcp zalan do           type A NAME                                         TYPE  TTL  DATA via ingress external dns test gcp zalan do   A     300  35 187 1 246      Let s check that we can resolve this DNS name as well      console   dig  short  ns cloud e1 googledomains com  via ingress external dns test gcp zalan do  35 187 1 246      Try with  curl  as well      console   curl via ingress external dns test gcp zalan do   DOCTYPE html   html   head   title Welcome to nginx   title        head   body        body    html           Clean up  Make sure to delete all service and ingress objects before terminating the cluster so all load balancers and DNS entries get cleaned up correctly      console   kubectl delete service   namespace ingress nginx ingress nginx controller   kubectl delete ingress nginx      Give ExternalDNS some time to clean up the DNS records for you  Then delete the managed zone and cluster      console   gcloud dns managed zones delete external dns test gcp zalan do   gcloud container clusters delete external dns      Also delete the NS records for your removed zone from the parent zone      console   gcloud dns record sets transaction start   zone gcp zalan do   gcloud dns record sets transaction remove ns cloud e 1  4  googledomains com          name external dns test gcp zalan do    ttl 300   type NS   zone gcp zalan do   gcloud dns record sets transaction execute   zone gcp zalan do         User Demo How To Blogs and Examples    Run external dns on GKE with workload identity  See  Kubernetes  ingress nginx  cert manager   external dns  https   blog atomist com kubernetes ingress nginx cert manager external dns