CRD Source¶
CRD source provides a generic mechanism to manage DNS records in your favorite DNS provider supported by external-dns.
Details¶
CRD source watches for a user specified CRD to extract Endpoints from its Spec.
So users need to create such a CRD and register it to the kubernetes cluster and then create new object(s) of the CRD specifying the Endpoints.
Registering CRD¶
Here is typical example of CRD API type which provides Endpoints to CRD source:
type TTL int64
type Targets []string
type ProviderSpecificProperty struct {
Name string `json:"name,omitempty"`
Value string `json:"value,omitempty"`
}
type ProviderSpecific []ProviderSpecificProperty
type Labels map[string]string
type Endpoint struct {
// The hostname of the DNS record
DNSName string `json:"dnsName,omitempty"`
// The targets the DNS record points to
Targets Targets `json:"targets,omitempty"`
// RecordType type of record, e.g. CNAME, A, SRV, TXT etc
RecordType string `json:"recordType,omitempty"`
// TTL for the record
RecordTTL TTL `json:"recordTTL,omitempty"`
// Labels stores labels defined for the Endpoint
// +optional
Labels Labels `json:"labels,omitempty"`
// ProviderSpecific stores provider specific config
// +optional
ProviderSpecific ProviderSpecific `json:"providerSpecific,omitempty"`
}
type DNSEndpointSpec struct {
Endpoints []*Endpoint `json:"endpoints,omitempty"`
}
type DNSEndpointStatus struct {
// The generation observed by the external-dns controller.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
}
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// DNSEndpoint is the CRD wrapper for Endpoint
// +k8s:openapi-gen=true
// +kubebuilder:resource:path=dnsendpoints
// +kubebuilder:subresource:status
type DNSEndpoint struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec DNSEndpointSpec `json:"spec,omitempty"`
Status DNSEndpointStatus `json:"status,omitempty"`
}
Refer to kubebuilder to create and register the CRD.
Usage¶
One can use CRD source by specifying --source flag with crd and specifying the ApiVersion and Kind of the CRD with --crd-source-apiversion and crd-source-kind respectively.
for e.g:
build/external-dns --source crd --crd-source-apiversion externaldns.k8s.io/v1alpha1 --crd-source-kind DNSEndpoint --provider inmemory --policy sync --once --dry-run
As with every source, --annotation-filter is optional. If you set it, ExternalDNS applies it to DNSEndpoint objects too, so a DNSEndpoint must match the filter itself or the CRD source will ignore it.
For example, with:
build/external-dns --source crd --crd-source-apiversion externaldns.k8s.io/v1alpha1 --crd-source-kind DNSEndpoint --provider inmemory --once --dry-run --annotation-filter=external-dns=public
the DNSEndpoint metadata must include a matching annotation:
apiVersion: externaldns.k8s.io/v1alpha1
kind: DNSEndpoint
metadata:
name: examplednsrecord
annotations:
external-dns: public
spec:
endpoints:
- dnsName: foo.bar.com
recordTTL: 180
recordType: A
targets:
- 192.168.99.216
Creating DNS Records¶
Create the objects of CRD type by filling in the fields of CRD and DNS record would be created accordingly.
Example¶
Here is an example CRD manifest generated by kubebuilder.
Apply this to register the CRD
$ kubectl apply --server-side=true -f "https://raw.githubusercontent.com/kubernetes-sigs/external-dns/master/config/crd/standard/dnsendpoints.externaldns.k8s.io.yaml"
customresourcedefinition.apiextensions.k8s.io "dnsendpoints.externaldns.k8s.io" created
Then you can create the dns-endpoint yaml similar to dnsendpoint-example
$ kubectl apply -f docs/sources/crd/dnsendpoint-example.yaml
dnsendpoint.externaldns.k8s.io "examplednsrecord" created
Run external-dns in dry-mode to see whether external-dns picks up the DNS record from CRD.
$ build/external-dns --source crd --crd-source-apiversion externaldns.k8s.io/v1alpha1 --crd-source-kind DNSEndpoint --provider inmemory --policy sync --once --dry-run
INFO[0000] running in dry-run mode. No changes to DNS records will be made.
INFO[0000] Connected to cluster at https://192.168.99.100:8443
INFO[0000] CREATE: foo.bar.com 180 IN A 192.168.99.216
INFO[0000] CREATE: foo.bar.com 0 IN TXT "heritage=external-dns,external-dns/owner=default"
Using CRD source to manage DNS records in different DNS providers¶
CRD source provides a generic mechanism and declarative way to manage DNS records in different DNS providers using external-dns.
Not all the record types are enabled by default so the required record types must be enabled by using --managed-record-types.
external-dns --source=crd \
--domain-filter=example.com \
--managed-record-types=A \
--managed-record-types=CNAME \
--managed-record-types=NS
- Example for record type
A
apiVersion: externaldns.k8s.io/v1alpha1
kind: DNSEndpoint
metadata:
name: examplearecord
spec:
endpoints:
- dnsName: example.com
recordTTL: 60
recordType: A
targets:
- 10.0.0.1
- Example for record type
CNAME
apiVersion: externaldns.k8s.io/v1alpha1
kind: DNSEndpoint
metadata:
name: examplecnamerecord
spec:
endpoints:
- dnsName: test-a.example.com
recordTTL: 300
recordType: CNAME
targets:
- example.com
Note: CNAME and DNAME targets accept both bare hostnames (
example.com) and absolute FQDNs with a trailing dot (example.com.), as defined by RFC 1035 ยง5.1. Other record types (A, AAAA, NS, etc.) do not accept a trailing dot.
- Example for record type
NS
apiVersion: externaldns.k8s.io/v1alpha1
kind: DNSEndpoint
metadata:
name: ns-record
spec:
endpoints:
- dnsName: zone.example.com
recordTTL: 300
recordType: NS
targets:
- ns1.example.com
- ns2.example.com
- Example for record type
DNAME
DNAME (RFC 6672) redirects an entire subtree of the DNS namespace to another domain. It has a single domain-name target and, like CNAME, accepts both bare hostnames and absolute FQDNs with a trailing dot.
The difference from CNAME is what gets aliased:
CNAME foo.example.com โ bar.example.netaliases onlyfoo.example.com. Names beneath it, such asx.foo.example.com, are unaffected.DNAME sub.example.com โ example.netaliases everything belowsub.example.com. A query forx.sub.example.comis rewritten by the resolver tox.example.net(the owner namesub.example.comitself is not redirected), so you don’t have to create aCNAMEfor every subdomain.
This makes DNAME useful for redirecting or renaming a whole subtree โ for example, moving *.old.example.com to new.example.net โ without maintaining a record per name.
apiVersion: externaldns.k8s.io/v1alpha1
kind: DNSEndpoint
metadata:
name: dname-record
spec:
endpoints:
- dnsName: sub.example.com
recordTTL: 300
recordType: DNAME
targets:
- example.net
Note:
DNAMEis only stored by providers whose DNS backend supports it (e.g. Gandi, NS1, OCI, PowerDNS, Scaleway, and RFC2136 backends such as BIND or Knot).
Providers whose backend has noDNAMEtype (e.g. AWS Route 53, Azure, Google Cloud DNS, Cloudflare) will reject the record on write.
BecauseDNAMEmasks the entire subtree beneath its owner name, do not manage other records below a name that carries aDNAME.
RBAC configuration¶
If you use RBAC, extend the external-dns ClusterRole with: