ExternalDNS is a Kubernetes add-on for automatically managing DNS records for Kubernetes ingresses and services by using different DNS providers. This webhook provider allows you to automate DNS records from your Kubernetes clusters to your DNScaster service provider.
- ExternalDNS >=
v0.15.0
Note
v0.15.0 of ExternalDNS added support for providerSpecific annotations in Ingress/Service objects for webhook providers.
While older versions of ExternalDNS may work, support for this feature will not be present.
-
Targeting multiple Nameserver Set is not supported. A single NameserverSet ID must be given through an ENV var at runtime.
-
IP Monitors are only supported for A/AAAA records.
ExternalDNS sends the webhook batches of records to create, update and delete. They are applied in this order: deletions, then updates, then creations.
A DNScaster host holds a single target, so a record with several targets, such as a Service with more than one load balancer IP, is kept as one host per target, all sharing the record's name. Each host gets its own monitor, so DNScaster hides the targets that go offline and keeps answering with the others. The webhook reports these hosts to external-dns as a single record.
- A deleted record removes its hosts, then the monitors they used.
- An updated record changes its hosts in place with
PUT: data, TTL and properties are sent, and their monitors are updated the same way, so hosts and monitors keep their IDs. A host whose target is kept is only updated when its TTL or properties change. A host whose target was dropped takes over one of the new targets, or is deleted when none is left. New targets left over get new hosts. A host gaining a monitor annotation gets a new monitor. A host losing it is deleted and created again, as the update cannot detach the monitor from the host, and a monitor in use cannot be deleted. - A created record gets a monitor first, then a host, for each target.
Properties are replaced as a whole on every update, so labels removed from the annotations are removed from the hosts and their monitors too.
| Environment Variable | Description | Default Value |
|---|---|---|
DNSCASTER_OWNER_ID |
ID used when filtering resources to save on API requests | N/A |
DNSCASTER_API_KEY |
API key needed to communicate with DNScaster API | N/A |
DNSCASTER_NAMESERVER_SET_ID |
DNScaster Nameserver Set tied to this provider | N/A |
DNSCASTER_SKIP_TLS_VERIFY |
Whether to skip TLS verification (true or false). |
false |
DNSCASTER_DRY_RUN |
Log DNScaster writes instead of sending them. | false |
Note
external-dns does not forward its --dry-run flag to webhook providers, so set
DNSCASTER_DRY_RUN on the webhook container instead. Reads still reach the
DNScaster API. Since nothing is applied, external-dns sends the same changes
on every sync, and each one also lists zones and hosts again, which counts
against the API rate limit.
| Environment Variable | Description | Default Value |
|---|---|---|
DNSCASTER_DEFAULT_TTL |
Default TTL value to be set for DNS records with no specified TTL. | 300 |
DNSCASTER_DEFAULT_PAGE_SIZE |
Records requested per page when listing (max_results). |
100 |
| Environment Variable | Description | Default Value |
|---|---|---|
SERVER_HOST |
The host address where the server listens. | localhost |
SERVER_PORT |
The port where the server listens. | 8888 |
SERVER_READ_TIMEOUT |
Duration the server waits before timing out on read operations. | 0s |
SERVER_WRITE_TIMEOUT |
Time to answer a webhook request before returning a 5xx. | 0s |
DOMAIN_FILTER |
List of domains to include in the filter. | Empty |
EXCLUDE_DOMAIN_FILTER |
List of domains to exclude from filtering. | Empty |
REGEXP_DOMAIN_FILTER |
Regular expression for filtering domains. | Empty |
REGEXP_DOMAIN_FILTER_EXCLUSION |
Regular expression for excluding domains from the filter. | Empty |
Note
A timeout of 0s means no timeout. external-dns gives up on a webhook request
after --webhook-provider-read-timeout plus --webhook-provider-write-timeout
(15s by default) and exits. Set SERVER_WRITE_TIMEOUT a few seconds below that
sum (e.g. 12s) so the webhook answers with a 5xx first, which external-dns
retries on its next sync.
| Environment Variable | Description | Default Value |
|---|---|---|
LOG_LEVEL |
Change the verbosity of logs (debug, info, warn or error) |
info |
LOG_FORMAT |
The format in which logs will be printed. (text or json) |
text |
All provider annotations are prefixed with: external-dns.kubernetes.io/webhook-dnscaster-
Provider annotations are implemented using the properties field on DNScaster's API.
As such, the provider annotations have the same limitations. More info can be found
in DNScaster's docs.
Monitors are created from provider specific annotations. This provider currently supports the following annotations:
| Annotation | Description | Default |
|---|---|---|
| ip-monitor-uri | URI for IP monitor | N/A |
| ip-monitor-hostname | Hostname for IP monitor | Record's FQDN |
| ip-monitor-treat_redirects | How Monitor will treat redirects | online |
Example:
IP monitors can be used to provide monitoring for your endpoint. Since LoadBalancer IPs are usually not known ahead of time, defining a uri without a host is perfectly valid and the provider will grab the target from the record.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-route # Let's assume this route is backed by a LB IP at 1.1.1.1
annotations:
external-dns.kubernetes.io/webhook-dnscaster-ip-monitor-uri: "ping"This would create an IP Monitor with the following URI: ping://1.1.1.1
If your IP Monitor URI is based on HTTP/HTTPS, the hostname will also get populated from the underlying resource. Note that again, the IP does not need to be known ahead of time.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-route # Let's assume this route is backed by a LB IP at 1.1.1.1
annotations:
external-dns.kubernetes.io/webhook-dnscaster-ip-monitor-uri: "https:/health" # URI parsing is using Go's net/url package
# Note that this URI would result in the same output
external-dns.kubernetes.io/webhook-dnscaster-ip-monitor-uri: "https:///health"This would create an IP Monitor with the following URI: https://1.1.1.1/health
A port can be given without a host, and an IPv6 target is bracketed as DNScaster expects:
ip-monitor-uri |
Target | Monitor URI |
|---|---|---|
tcp://:9876 |
1.1.1.1 |
tcp://1.1.1.1:9876 |
ping |
2001:db8::1 |
ping://[2001:db8::1] |
https:///health |
2001:db8::1 |
https://[2001:db8::1]/health |
Of course, it is also possible to directly give a full URI if the IP you wish to monitor is known ahead of time. This can be useful when the monitor should target a different IP from the managed endpoint.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-route # Let's assume this route is backed by a LB IP at 1.1.1.1
annotations:
external-dns.kubernetes.io/webhook-dnscaster-ip-monitor-uri: "ping://1.2.3.4"This would create an IP Monitor with the following URI: ping://1.2.3.4.
A record with several targets still gets one monitor per host, so each of
them would watch 1.2.3.4.
It is also possible to use a different URI for HTTP/HTTPS monitors. Do not forget
to set the hostname through an annotation, otherwise the endpoint's FQDN will
be used instead:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-route # Let's assume this route is backed by a LB IP at 1.1.1.1
annotations:
external-dns.kubernetes.io/webhook-dnscaster-ip-monitor-uri: "https://1.2.3.4/health"
external-dns.kubernetes.io/webhook-dnscaster-ip-monitor-hostname: "api.other.com"
spec:
hostnames:
- "api.example.com"It is possible to set arbitrary labels on Hosts/Monitors using a provider specific
annotation. The labels are set with an annotation prefixed with: external-dns.kubernetes.io/webhook-dnscaster-label-
For example, if we want to set these labels:
| Label | Value |
|---|---|
| network | ethereum |
| service | token-api |
The annotation syntax will be:
external-dns.kubernetes.io/webhook-dnscaster-label-network: ethereum
external-dns.kubernetes.io/webhook-dnscaster-label-service: token-apiNote that since labels are arbitrary and can be anything, the label prefix will be trimmed on the resulting property. This allows you to not worry about the length of the label prefix in your implementation.
i.e.: The resulting properties for these labels on a host would be:
{
"properties": {
"network": "ethereum",
"service": "token-api"
}
}Labels with the format prefix/label: value require an escape sequence
for the / character. The escape sequence for / is ~1. As such, an
annotation for the label registry/name: mainnet would be written as:
external-dns.kubernetes.io/webhook-dnscaster-label-registry~1name: mainnetSimilarly, the ~ character also needs an escape sequence. The special
replacement character ~0 is used to replace ~ in label name.
make dev creates a kind cluster with a local image registry and starts Tilt
against it. Tilt installs cilium, kube-prometheus-stack, external-dns with the
webhook built from this checkout, the dnscaster-exporter and the manifests in
example/. Changes under cmd/, pkg/ and internal/ are rebuilt and synced
into the running webhook.
It needs docker and the tools from the nix dev shell (kind, tilt, helm, kubectl,
sops). The DNScaster API key is decrypted with sops, so you also need the age
key listed in .sops.yaml.
make dev # create the cluster if needed, then start Tilt
make dev-down # remove the example records from DNScaster, then delete the clusterWarning
The test env uses the real DNScaster API and creates records under
dnstest.pinax.network. Its calls count against the same rate limit as every
other cluster. Exiting Tilt leaves external-dns running, so run make dev-down
when you are done. Deleting the cluster directly leaves the example records in
DNScaster.