Kubernetes DIY Dynamic DNS Operator

I’ve rolled my own Dynamic DNS Operator with Kubernetes CronJob, kubectl, and External DNS. I’m going to try capture the solution in a Google Design Document.

Context

A solution to dynamically maintain a DNS record containing my routers public IP.

Goals

Non-Goal

Design

The design is broken down into the following sections:

Discovering Router IP

With simple internet egress via router NAT - we can use several internet based services to return the IP:

$ curl --silent ifconfig.me
120.148.147.73

Changing DNS Records

External DNS Ingress annotations can configure a DNS record:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    external-dns.alpha.kubernetes.io/hostname: ip.home.jamesmoriarty.xyz
    external-dns.alpha.kubernetes.io/target: 110.144.168.172
...

Generating Dynamic Configuration

CronJob to periodically generate and apply the configuration. The configuration variables are interpolated via heredoc template:

cat << EOF > /tmp/ingress.yml && kubectl apply -f /tmp/ingress.yml
  ...
EOF

Interaction Diagram

Interaction diagram

Network Diagram

Network diagram

Example Manifest

An example of what the full Kubernetes manifest might look like:

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: dynamic-dns-operator
spec:
  schedule: "*/5 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: dynamic-dns-operator
            image: bitnami/kubectl
            command:
            - /bin/sh
            - -c
            - |
              export IP=$(curl --silent ifconfig.me)

              cat << EOF > /tmp/ingress.yml && kubectl apply -f /tmp/ingress.yml
              apiVersion: networking.k8s.io/v1
              kind: Ingress
              metadata:
                name: dynamic-dns-operator
                annotations:
                  kubernetes.io/ingress.class: nginx
                  external-dns.alpha.kubernetes.io/hostname: '$HOSTNAME'
                  external-dns.alpha.kubernetes.io/target: '$IP'
              spec:
                rules:
                - host: '$HOSTNAME'
              EOF
            env:
            - name: HOSTNAME
              valueFrom:
                configMapKeyRef:
                  name: dynamic-dns-operator
                  key: hostname

Conclusion

If you’re already using External DNS - another 30-40 lines CronJob can support Dynamic DNS.