ExternalDNS, Istio y AWS Route 53
Tags: kubernetes, istio, devops
Published: 2019-06-12 07:53:50

En el [anterior artículo](https://dev.to/samcre/istio-sds-y-cert-manager-4a9e) dejaste configurado Istio y cert-manager para que trabajaran juntos sirviendo pods del service mesh sobre HTTPS. Menuda gozada, ¿verdad? Pero hasta que no configurasteis el registro DNS en vuestro proveedor, evidentemente, no se podía acceder. Hacer cosas a mano, ¿qué año es este? ¿2017?

En este artículo vamos a dejar instalado y configurado en el clúster la utilidad [ExternalDNS](https://github.com/kubernetes-incubator/external-dns), que se encargará de actualizar automáticamente nuestro proveedor de DNS basándose en los hosts configurados en los Gateways de Istio.

ExternalDNS es compatible con más de 15 proveedores de DNS, aunque solo dos de manera estable (GCloud y AWS Route 53), y es relativamente fácil de instalar y configurar en nuestro clúster. En esta parte, seguiremos con el ejemplo del artículo anterior, y vamos a integrarlo con AWS Route 53, para que cree automáticamente en Route 53 los hosts que activemos en los Gateways de Istio.

## Configuración previa

Al igual que con cert-manager, y con todo en AWS, necesitamos crear una _policy_ de IAM que otorgue al clúster o los pods que ejecuten ExternalDNS los permisos necesarios para trabajar con Route 53. En la propia documentación de ExternalDNS podemos encontrar el [JSON](https://github.com/kubernetes-incubator/external-dns/blob/master/docs/tutorials/aws.md#iam-permissions) que la define, pero básicamente es esta:

```json
{
 "Version": "2012-10-17",
 "Statement": [
   {
     "Effect": "Allow",
     "Action": ["route53:ChangeResourceRecordSets"],
     "Resource": ["arn:aws:route53:::hostedzone/*"]
   },
   {
     "Effect": "Allow",
     "Action": [
       "route53:ListHostedZones",
       "route53:ListResourceRecordSets"
     ],
     "Resource": ["*"]
   }
 ]
}
```

Creo que es importante señalar que la línea `"Resource": ["arn:aws:route53:::hostedzone/*"]` otorga permisos demasiado abiertos, y deberíamos modificarla indicando única y exclusivamente el `ZONE_ID` de las zonas que queremos que ExternalDNS gestione por nosotros.

Evidentemente, ya deberemos tener creada una zona DNS en AWS para que sea gestionada por ExternalDNS, porque esta utilidad no las crea.

## Instalando y configurando ExternalDNS

Para instalar ExternalDNS tenemos disponible un [_chart_ de Helm](https://github.com/helm/charts/tree/master/stable/external-dns), pero su fichero [YAML de despliegue](https://github.com/kubernetes-incubator/external-dns/blob/master/docs/tutorials/aws.md#manifest-for-clusters-with-rbac-enabled) es tan sencillo, que nos vamos a limitar a copiarlo, y modificar los parámetros que integrarán ExternalDNS con Route 53. Si lo instaláis con el _chart_, podéis compartir la configuración a seguir en los comentarios.

Guardamos en un fichero local el YAML anterior, y pasamos a editar la parte del Deployment. En concreto, los valores para `args`:

```yaml
[...]
        args:
        - --source=service
        - --source=ingress
        - --source=istio-gateway
        - --istio-ingress-gateway=custom-istio-namespace/custom-istio-ingressgateway # load balancer service to be used; can be specified multiple times. Omit to use the default (istio-system/istio-ingressgateway)
        - --domain-filter=external-dns-test.my-org.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
        - --provider=aws
        - --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization
        - --aws-zone-type=public # only look at public hosted zones (valid values are public, private or no value for both)
        - --registry=txt
        - --txt-owner-id=my-identifier
```
* **`--source`**: Indicamos a ExternalDNS qué tipo de recursos dentro del clúster debe analizar para actualizar el DNS. Si solo vamos a usar `istio-gateway`, podemos dejar únicamente este.
* **`--istio-ingress-gateway`**: Este _flag_ solo es obligatorio si hemos creado o usamos un _ingressgateway_ distintio al que Istio crea por defecto (_istio-ingressgateway_). De ser así, eliminamos o comentamos esta línea.
* **`--domain-filter`**: Restringimos la actualización de DNS de ExternalDNS a uno o varios dominios. Si eliminamos este _flag_, actualizará cualquier dominio que encuentre. Esto va muy relacionado con crear un política de IAM con los permisos necesarios.
* **`--provider`**: En este caso, `aws`, ya que lo queremos integrar con Route 53.
* **`--policy`**: Por defecto usa el valor `sync`, que elimina registros DNS cuando no los encuentra en ninguno de los `source` configurados. El valor `upsert-only` simplemente añade nuevos registros al DNS.
* **`--aws-zone-type`**: Indicamos que la(s) zona(s) que ha de gestionar tienen carácter privado o público en Route 53.

Una vez que hayáis modificado estas opciones a vuestras necesidades, aplicáis al clúster. Podéis instalarlo en su propio namespace:

```bash
kubectl apply -n externaldns -f externaldns.yaml
```

Cuando el pod esté en estado `Running`, si tenemos algún Gateway de Istio configurado, ExternalDNS empezará a crear registros DNS en Route 53. En caso de no tener aún nada, aplicamos nuestro `Gateway` y `VirtualService`, y en unos pocos minutos veremos en los logs del pod cómo empieza a añadir los registros:

```txt
time="2019-06-10T15:51:33Z" level=info msg="Desired change: CREATE nginx.example.com A"
time="2019-06-10T15:51:33Z" level=info msg="Desired change: CREATE nginx.example.com TXT"
time="2019-06-10T15:51:36Z" level=info msg="2 record(s) in zone example.com. were successfully updated"
```

Con estas opciones conseguiremos despreocuparnos de la configuración DNS, que ExternalDNS se encargará de gestionar por nosotros. Esta configuración puede sernos tremendamente útil para presentar en endpoints temporales, mediante Integración Continua, una nueva feature de nuestra web y, combinándolo con cert-manager, en un perfecto HTTPS.