When you deploy containerized applications to a cluster, you often need to resolve external domain names in addition to in-cluster service names. Container Service for Kubernetes (ACK) lets you customize DNS behavior for managed CoreDNS by creating a CustomDNSConfig custom resource (CR). Use this to route specific domains to upstream DNS servers, map hostnames to static IP addresses, or override the default resolver for all queries.
Overview
ACK's managed CoreDNS exposes a cluster-scoped CustomDNSConfig CustomResourceDefinition (CRD) in the networking.alibabacloud.com/v1beta1 API group. When you create or update the default CR, a controller reconciles it into the CoreDNS ConfigMap (Corefile) and triggers a live reload — no pod restarts required.
The CustomDNSConfig CRD is cluster-scoped with a single instance named default. This design ensures a single authoritative DNS configuration per cluster and prevents conflicts between multiple configurations. To update the configuration, apply changes to the existing CR rather than creating a new one.
How it works:
CustomDNSConfig CR (default)
↓ controller reconciles
CoreDNS ConfigMap (Corefile)
↓ CoreDNS reloads (~20 seconds)
Live DNS resolution updated
CR template
The following example shows the full structure of a CustomDNSConfig CR with all supported fields and placeholder values:
apiVersion: networking.alibabacloud.com/v1beta1
kind: CustomDNSConfig
metadata:
name: default
namespace: default
spec:
zones:
- name: example.com
forward:
protocolStrategy: ""
transportConfig: {}
upstreams:
- xxx.xxx.xxx.xxx # IP address
- xxx.xxx.xxx.xxx:53 # IP:port
hosts:
- hostName: "a.example.com"
ipAddress: xxx.xxx.xxx.xxx
CR field reference
The following table describes the fields in the CustomDNSConfig CR:
-
spec.zones[].name: string, required, default".". DNS zone name. Must be a fully qualified domain name (FQDN). The special zone"."matches all queries and represents the default resolver. -
spec.zones[].forward.upstreams: []string, optional. Upstream DNS server addresses. Format:IPorIP:PORT. IPv4 only. Maximum 15 addresses per zone. If not set, Alibaba Cloud DNS PrivateZone is used. -
spec.zones[].forward.protocolStrategy: string, optional, default"". Transport protocol.""uses UDP;tcpforces TCP. -
spec.zones[].forward.transportConfig: object, optional, default{}. TLS transport configuration. Cannot be changed. -
spec.zones[].hosts[].hostName: string, required. Hostname to map statically. Must comply with DNS naming rules. -
spec.zones[].hosts[].ipAddress: string, required. IPv4 address for the hostname.
Zone concept: A zone defines a DNS namespace. Specifying example.com matches example.com and all subdomains (for example, api.example.com). The special zone "." matches all queries and represents the default resolver.
The forward plugin routes queries for a zone to the specified upstream DNS servers. Use this for private domains hosted on on-premises DNS or another cloud DNS service.
The hosts plugin serves static hostname-to-IP mappings without querying an upstream server. Use this when you need a small number of fixed hostname overrides.
Prerequisites
-
The managed CoreDNS plugin is installed at version 1.9.3.20 or later. For more information, see Service discovery DNS.
-
A kubectl client is connected to the cluster. For more information, see Obtain the kubeconfig file of a cluster and use kubectl to connect to the cluster.
Constraints
-
You can only create one
CustomDNSConfigCR, and it must be nameddefault. If you create a CR with any other name, its phase is set toNotSupportedand the configuration is ignored. -
The
upstreamsfield supports IPv4 addresses only (format:IPorIP:PORT). Maximum 15 addresses per zone. -
The
ipAddressfield inhostsentries supports IPv4 addresses only. -
The
transportConfigfield cannot be modified.
Scenario 1: Configure a custom zone
Use this scenario to route queries for a specific domain to an upstream DNS server, or to map hostnames in a domain to static IP addresses.
The following example configures two custom zones:
-
example.com— routes all queries to upstream DNS servers100.100.2.136and100.100.2.138(the default internal DNS resolution service addresses; see Endpoints for details). -
foo.com— mapsa.foo.comandb.foo.comto static IP addresses using thehostsplugin.
Step 1: Create the CR manifest.
Create a file named default.yaml with the following content:
apiVersion: networking.alibabacloud.com/v1beta1
kind: CustomDNSConfig
metadata:
name: default # Must be named "default"
spec:
zones:
- name: example.com
forward:
upstreams:
- 100.100.2.136 # Set the upstream DNS server addresses for example.com to 100.100.2.136 and 100.100.2.138.
- 100.100.2.138
- name: foo.com
hosts:
- hostName: "a.foo.com" # Configure custom static IP addresses for a.foo.com and b.foo.com in the foo.com zone.
ipAddress: 192.168.0.251
- hostName: "b.foo.com"
ipAddress: 192.168.0.252
100.100.2.136 and 100.100.2.138 are the default internal DNS resolution service addresses allocated by the system. For more information, see Endpoints.
Step 2: Apply the manifest.
kubectl apply -f default.yaml
Step 3: Verify that the Corefile was generated.
kubectl get customdnsconfig default -o yaml | grep corefile -A 35 -B 1
Expected output:
status:
corefile: |
example.com:53 {
prometheus :9153
forward . 100.100.2.136 100.100.2.138 {
policy random
prefer_udp
}
...
}
foo.com:53 {
prometheus :9153
hosts {
192.168.0.251 a.foo.com
192.168.0.252 b.foo.com
fallthrough
}
forward . /etc/resolv.conf {
policy random
prefer_udp
}
...
}
...
--
corefileHash: 41f7be21cf3022c305091665ed33b1e5
lastTransitionTime: "2024-09-13T09:07:37Z"
phase: GenerateSuccess
The phase: GenerateSuccess status confirms that the CR was accepted and the Corefile was generated.
Step 4: Verify DNS resolution.
After the configuration reloads (approximately 20 seconds), run a test pod to confirm that DNS queries resolve correctly:
# Test that example.com resolves via the upstream DNS servers
kubectl run -it --rm dns-test --image=busybox:1.28 --restart=Never -- nslookup example.com
# Test that the static host mapping works for foo.com
kubectl run -it --rm dns-test --image=busybox:1.28 --restart=Never -- nslookup a.foo.com
The nslookup output should show the addresses you configured (192.168.0.251 for a.foo.com, 192.168.0.252 for b.foo.com).
You can map a single hostname to multiple IP addresses. For example:
hosts:
- hostName: "a.example.com"
ipAddress: 10.0.0.123
- hostName: "a.example.com"
ipAddress: 10.0.0.124
Scenario 2: Modify the default zone configuration
Use this scenario to change the upstream DNS servers for all cluster queries — that is, to override the built-in default resolver zone (".").
spec.zones configures custom zones. Use spec.zones with name: "." to override the upstream servers for the default zone that handles all cluster queries.
Step 1: Create the CR manifest.
Create a file named default.yaml with the following content:
apiVersion: networking.alibabacloud.com/v1beta1
kind: CustomDNSConfig
metadata:
name: default # Must be named "default"
spec:
zones:
- name: .
forward:
upstreams:
- 100.100.2.136 # The upstream DNS server addresses for the custom default zone named "." are 100.100.2.136 and 100.100.2.138.
- 100.100.2.138
Step 2: Apply the manifest.
kubectl apply -f default.yaml
Step 3: Verify that the Corefile was generated.
kubectl get customdnsconfig default -o yaml | grep corefile -A 35 -B 1
Expected output:
status:
corefile: |
.:53 {
errors
health {
lameduck 20s
}
ready
kubeapi {
kubeconfig /etc/kubernetes/config/managed-coredns.conf
}
k8s_event {
level error warning
}
...
prometheus :9153
forward . 100.100.2.136 100.100.2.138 {
policy random
prefer_udp
}
...
}
corefileHash: 847bf69cc4c97cee965945f45d17c661
lastTransitionTime: "2024-09-13T09:54:22Z"
phase: GenerateSuccess
The phase: GenerateSuccess status confirms that the default zone configuration was updated.
Step 4: Verify DNS resolution.
After the configuration reloads (approximately 20 seconds), run a test pod to confirm that external queries route through the configured upstream servers:
kubectl run -it --rm dns-test --image=busybox:1.28 --restart=Never -- nslookup kubernetes.default.svc.cluster.local
After you create or modify the CustomDNSConfig CR, CoreDNS performs a configuration reload, which takes approximately 20 seconds. You can adjust the reload duration by modifying the lameduck value in the Corefile. The default value is 20 seconds.
Troubleshooting
CR named something other than default shows NotSupported
You can only create one CustomDNSConfig CR per cluster, and it must be named default. If you create a CR with a different name, the controller sets its phase to NotSupported and ignores it.
The following example demonstrates this behavior.
Step 1: Create a file named test.yaml with a non-default CR name.
apiVersion: networking.alibabacloud.com/v1beta1
kind: CustomDNSConfig
metadata:
name: test ## You can create only a CustomDNSConfig CR named default.
spec:
zones:
- name: example.com
forward:
upstreams:
- 100.100.2.138
- name: foo.com
hosts:
- hostName: "ah.foo.com"
ipAddress: 1.1.xx.251
- hostName: "aha.foo.com"
ipAddress: 1.1.xx.252
Step 2: Apply the manifest.
kubectl apply -f test.yaml
Step 3: Check the status of all CustomDNSConfig CRs.
kubectl get customdnsconfig
Expected output:
NAME PHASE VERSION AGE
default GenerateSuccess 847bf69cc4c97cee96xxxxxxxxxxx 89m
test NotSupported 9s
The test CR shows NotSupported, confirming that only the CR named default is accepted. To fix this, delete the incorrectly named CR and apply your configuration to the default CR instead:
kubectl delete customdnsconfig test
kubectl apply -f default.yaml
Event information
The custom CoreDNS controller synchronizes event information to the default namespace. Use these events to confirm successful configuration sync or to diagnose failures.
Run the following command to view events:
kubectl get events
Expected output:
LAST SEEN TYPE REASON OBJECT MESSAGE
45m Normal CustomDNSConfigSyncOk customdnsconfig/default custom dns config sync to coredns configmap success
The following table describes the event types:
-
Normal/CustomDNSConfigSyncOk: The CR was successfully reconciled into the CoreDNS ConfigMap. No action required. -
NotSupported: The CR name is notdefault. The configuration was rejected. Delete the CR and reapply withname: default.
To monitor the status of the CR in real time while changes propagate:
kubectl get customdnsconfig default -w