RKE2 the Hard Way: Part 9 – Installing CoreDNS
Part 9 – Installing CoreDNS
In this part of the “RKE2 the Hard Way” training series, we will install and configure CoreDNS for Kubernetes cluster DNS resolution. CoreDNS is a flexible, extensible DNS server that serves as the cluster DNS provider in Kubernetes, allowing service discovery by DNS name.
DNS resolution is critical in a Kubernetes cluster because:
- It enables services to be discovered by their names rather than IP addresses
- It allows pods to find and communicate with other pods and services
- It provides a stable naming scheme even when IPs change due to pod rescheduling
✅ Note: We already configured kubelet in Part 7 to use 10.43.0.10 as the DNS server, which is the IP we’ll use for the CoreDNS service.
1. Prepare the CoreDNS Manifest
First, let’s create a YAML manifest for CoreDNS:
cat > coredns.yaml << EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: coredns
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: system:coredns
rules:
- apiGroups: [""]
resources:
- endpoints
- services
- pods
- namespaces
verbs:
- list
- watch
- apiGroups: ["discovery.k8s.io"]
resources:
- endpointslices
verbs:
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: system:coredns
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:coredns
subjects:
- kind: ServiceAccount
name: coredns
namespace: kube-system
---
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: coredns
namespace: kube-system
labels:
k8s-app: kube-dns
spec:
replicas: 2
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
selector:
matchLabels:
k8s-app: kube-dns
template:
metadata:
labels:
k8s-app: kube-dns
spec:
priorityClassName: system-cluster-critical
serviceAccountName: coredns
tolerations:
- key: CriticalAddonsOnly
operator: Exists
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
containers:
- name: coredns
image: registry.k8s.io/coredns/coredns:v1.10.1
imagePullPolicy: IfNotPresent
resources:
limits:
memory: 170Mi
requests:
cpu: 100m
memory: 70Mi
args: [ "-conf", "/etc/coredns/Corefile" ]
volumeMounts:
- name: config-volume
mountPath: /etc/coredns
readOnly: true
ports:
- containerPort: 53
name: dns
protocol: UDP
- containerPort: 53
name: dns-tcp
protocol: TCP
- containerPort: 9153
name: metrics
protocol: TCP
securityContext:
allowPrivilegeEscalation: false
capabilities:
add:
- NET_BIND_SERVICE
drop:
- all
readOnlyRootFilesystem: true
livenessProbe:
httpGet:
path: /health
port: 8080
scheme: HTTP
initialDelaySeconds: 60
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 5
readinessProbe:
httpGet:
path: /ready
port: 8181
scheme: HTTP
volumes:
- name: config-volume
configMap:
name: coredns
items:
- key: Corefile
path: Corefile
dnsPolicy: Default
---
apiVersion: v1
kind: Service
metadata:
name: kube-dns
namespace: kube-system
annotations:
prometheus.io/port: "9153"
prometheus.io/scrape: "true"
labels:
k8s-app: kube-dns
kubernetes.io/cluster-service: "true"
kubernetes.io/name: "CoreDNS"
spec:
selector:
k8s-app: kube-dns
clusterIP: 10.43.0.10
ports:
- name: dns
port: 53
protocol: UDP
- name: dns-tcp
port: 53
protocol: TCP
- name: metrics
port: 9153
protocol: TCP
EOF
This manifest:
- Creates a ServiceAccount for CoreDNS
- Sets up necessary RBAC permissions
- Creates a ConfigMap with the CoreDNS configuration
- Deploys CoreDNS as a Deployment with 2 replicas for high availability
- Creates a Service named kube-dns with the IP address 10.43.0.10
2. Apply the CoreDNS Manifest
Now, apply the CoreDNS manifest to your cluster:
kubectl apply -f coredns.yaml
3. Verify CoreDNS Deployment
Check that the CoreDNS pods are running successfully:
kubectl get pods -n kube-system -l k8s-app=kube-dns
You should see output similar to:
NAME READY STATUS RESTARTS AGE coredns-xxxxxxxx-xxxxx 1/1 Running 0 1m coredns-xxxxxxxx-yyyyy 1/1 Running 0 1m
Also, verify that the CoreDNS service is created with the correct cluster IP:
kubectl get svc -n kube-system kube-dns
You should see output similar to:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kube-dns ClusterIP 10.43.0.1053/UDP,53/TCP,9153/TCP 1m
4. Test DNS Resolution
Let’s test if DNS resolution is working properly by creating a test pod and trying to resolve some domains:
# Create a test pod
kubectl run dns-test --image=busybox:1.28 -- sleep 3600
# Wait for the pod to be ready
kubectl wait --for=condition=Ready pod/dns-test
# Test DNS resolution for kubernetes.default service
kubectl exec -it dns-test -- nslookup kubernetes.default
# Test DNS resolution for an external domain
kubectl exec -it dns-test -- nslookup google.com
If DNS is working correctly, you should get successful responses from both nslookup commands.
For the kubernetes.default service, you should see something similar to:
Server: 10.43.0.10 Address 1: 10.43.0.10 kube-dns.kube-system.svc.cluster.local Name: kubernetes.default Address 1: 10.43.0.1 kubernetes.default.svc.cluster.local
For google.com, you should get its actual IP addresses.
5. Clean Up Test Resources
After successful testing, clean up the test pod:
kubectl delete pod dns-test
Next Steps
Now that we have CoreDNS running for cluster DNS resolution, we’ll proceed to Part 10 where we’ll set up NGINX Ingress Controller to enable external access to our cluster’s services.
👉 Continue to Part 10: Installing Ingress Nginx