modify the contents of /etc/resolv.conf when the pod is running
Kubernetes provides a method to modify the configuration of the /etc/resolv.conf
file for pods using the spec.dnsConfig
and spec.dnsPolicy
fields. You can find specific information on this in the Customizing DNS Service documentation. However, this approach leads to the recreation of pods.
In our specific business scenario, we need pods to use local DNS instead of the centralized CoreDNS, even for pods created before the change in cluster DNS configuration. We need to update the nameserver for these existing pods to point to the local DNS server. However, we cannot actively delete pods or restart containers. This practice is not considered ideal in container usage, but it aligns with our company culture, as the business application doesn’t support graceful termination.
1 How to Modify the Contents of /etc/resolv.conf Without Recreating Pods?
1.1 Directly Modify the /etc/resolv.conf Inside Containers
The first method involves directly modifying the /etc/resolv.conf
file inside containers. Since containers run using Docker, all containers within a pod share the same mounted /etc/resolv.conf
file. Therefore, modifying the /etc/resolv.conf
in one container achieves the goal of changing the nameserver for the entire pod.
All containers within the pod use the same resolv.conf file.
# docker ps
a15fc9af0da0 75c6ae8cb401 "/app/push-netstat -…" 5 seconds ago Up 4 seconds k8s_main-container_push-netstat-2pwbn_ops_b90f9867-6c13-4242-9904-26354f2b65df_0
9007620aa2cb k8s-google-containers/pause-amd64:3.2 "/pause" 6 seconds ago Up 5 seconds k8s_POD_push-netstat-2pwbn_ops_b90f9867-6c13-4242-9904-26354f2b65df_0
# docker inspect 9007620aa2cb
[
{
"Id": "9007620aa2cb23f2d91a3fb194771c39be45d75e73ef36a3f78b99a4574a1cb0",
"Created": "2022-12-01T04:51:14.862746022Z",
"Path": "/pause",
"Image": "sha256:80d28bedfe5dec59da9ebf8e6260224ac9008ab5c11dbbe16ee3ba3e4439ac2c",
"ResolvConfPath": "/data/kubernetes/docker/containers/9007620aa2cb23f2d91a3fb194771c39be45d75e73ef36a3f78b99a4574a1cb0/resolv.conf",
# docker inspect a15fc9af0da0
[
{
"Id": "a15fc9af0da02a93a9c84f3528cc3970a44157c3b63d1d2efa17e8694cded9b5",
"Created": "2022-12-01T04:51:15.425130718Z",
"ResolvConfPath": "/data/kubernetes/docker/containers/9007620aa2cb23f2d91a3fb194771c39be45d75e73ef36a3f78b99a4574a1cb0/resolv.conf",
#cat /proc/1/mountinfo
952 703 253:1 /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/UCT ro,noatime - ext4 /dev/vda1 rw,stripe=32411,data=ordered
953 705 253:17 /kubernetes/kubelet/pods/b90f9867-6c13-4242-9904-26354f2b65df/containers/main-container/7adc41b0 /dev/termination-log rw,relatime - ext4 /dev/vdb1 rw,data=ordered
954 703 253:17 /kubernetes/docker/containers/9007620aa2cb23f2d91a3fb194771c39be45d75e73ef36a3f78b99a4574a1cb0/resolv.conf /etc/resolv.conf rw,relatime - ext4 /dev/vdb1 rw,data=ordered
955 703 253:17 /kubernetes/docker/containers/9007620aa2cb23f2d91a3fb194771c39be45d75e73ef36a3f78b99a4574a1cb0/hostname /etc/hostname rw,relatime - ext4 /dev/vdb1 rw,data=ordered
956 703 253:17 /kubernetes/kubelet/pods/b90f9867-6c13-4242-9904-26354f2b65df/etc-hosts /etc/hosts rw,relatime - ext4 /dev/vdb1 rw,data=ordered
957 705 0:122 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw,size=65536k
958 703 0:120 / /run/secrets/kubernetes.io/serviceaccount ro,relatime - tmpfs tmpfs rw,size=1048576k
Drawback: Requires a program inside the container that can modify files, like echo
(you cannot use vim
or sed
directly because they create a new file and overwrite the source file). Also, it requires the container to run as the root user (or it will result in “Permission denied” errors).
Advantage: Simple and straightforward.
Using sed
inside a container run as root results in an error.
# sed -i '$a \sdasd' /etc/resolv.conf
sed: cannot rename /etc/sedz6ITFW: Device or resource busy
Modifying the file inside a non-root user container results in a “Permission denied” error.
$ echo a >> /etc/resolv.conf
bash: /etc/resolv.conf: Permission denied
1.2 Modify the ResolvConfPath
of the host mounted source file corresponding to /etc/resolv.conf in the Docker container
The second method involves modifying the source file mounted on the host corresponding to /etc/resolv.conf
inside Docker containers. Similar to the first method, you cannot use vim
or sed
directly for this, but you can use the echo
command to make changes.
The original idea for this method comes from dockershim, which modifies /etc/resolv.conf
inside the sandbox.
// Rewrite resolv.conf file generated by docker.
// NOTE: cluster dns settings aren't passed anymore to docker api in all cases,
// not only for pods with host network: the resolver conf will be overwritten
// after sandbox creation to override docker's behaviour. This resolv.conf
// file is shared by all containers of the same pod, and needs to be modified
// only once per pod.
if dnsConfig := config.GetDnsConfig(); dnsConfig != nil {
containerInfo, err := ds.client.InspectContainer(createResp.ID)
if err != nil {
return nil, fmt.Errorf("failed to inspect sandbox container for pod %q: %v", config.Metadata.Name, err)
}
if err := rewriteResolvFile(containerInfo.ResolvConfPath, dnsConfig.Servers, dnsConfig.Searches, dnsConfig.Options); err != nil {
return nil, fmt.Errorf("rewrite resolv.conf failed for pod %q: %v", config.Metadata.Name, err)
}
}
Drawback: Requires root permissions on the host machine and is only applicable when Docker is the container runtime.
Advantage: It’s a simple method.
Modifying the file using vim
on the host doesn’t take effect inside the container.
#options ndots:5 --> options ndots:4
# vim /data/kubernetes/docker/containers/9007620aa2cb23f2d91a3fb194771c39be45d75e73ef36a3f78b99a4574a1cb0/resolv.conf
nameserver 169.254.20.10
search xxx-ops.svc.cluster.local svc.cluster.local cluster.local internal.xxx.com
options ndots:4
# doesn't take effect inside the container
# kubectl exec -it -n ops push-netstat-2pwbn cat /etc/resolv.conf
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
nameserver 169.254.20.10
search xxx-ops.svc.cluster.local svc.cluster.local cluster.local internal.xxx.com
options ndots:5
# cat /data/kubernetes/docker/containers/9007620aa2cb23f2d91a3fb194771c39be45d75e73ef36a3f78b99a4574a1cb0/resolv.conf
nameserver 169.254.20.10
search xxx-ops.svc.cluster.local svc.cluster.local cluster.local internal.xxx.com
options ndots:4
# run a container with same network namespace, but /etc/resolv.conf is changed
#docker run -it --name test-resolv-conf --network container:9007620aa2cb ubuntu bash
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
e96e057aae67: Pull complete
Digest: sha256:4b1d0c4a2d2aaf63b37111f34eb9fa89fa1bf53dd6e4ca954d47caebca4005c2
Status: Downloaded newer image for ubuntu:latest
root@push-netstat-5pxlr:/# cat /etc/resolv.conf
nameserver 169.254.20.10
search xxx-ops.svc.cluster.local svc.cluster.local cluster.local internal.xxx.com
options ndots:4
Using the echo
command to modify the mounted source file on the host allows the changes to be reflected inside the container. This method enables the container to detect and use the modified /etc/resolv.conf
file on the host.
# echo 11 >> /data/kubernetes/docker/containers/9007620aa2cb23f2d91a3fb194771c39be45d75e73ef36a3f78b99a4574a1cb0/resolv.conf
# ll -i /data/kubernetes/docker/containers/9007620aa2cb23f2d91a3fb194771c39be45d75e73ef36a3f78b99a4574a1cb0/resolv.conf
3276908 -rw-r--r-- 1 root root 132 Dec 1 13:01 /data/kubernetes/docker/containers/9007620aa2cb23f2d91a3fb194771c39be45d75e73ef36a3f78b99a4574a1cb0/resolv.conf
# kubectl exec -it -n ops push-netstat-2pwbn /bin/cat /etc/resolv.conf
nameserver 169.254.20.10
search xxx-ops.svc.cluster.local svc.cluster.local cluster.local internal.xxx.com
options ndots:5
11
# kubectl exec -it -n ops push-netstat-2pwbn /bin/ls /etc/resolv.conf -i
3276908 /etc/resolv.conf
2 Does the Application Utilize the New Contents of /etc/resolv.conf?
Even though the content of the /etc/resolv.conf
file has been updated, does the application re-read the /etc/resolv.conf
file?
Different applications exhibit different behaviors:
Java JVM: By default, it only reads the file at startup and does not re-read /etc/resolv.conf
once it’s running.
As a program (any process, JVM included) has its very first DNS request it reads and caches forever the entire contents of
/etc/resolv.conf
by default. It never refreshes that info later, even when it encounters a total DNS failure. The program would need to have some specific system calls programmed to behave in more user-friendly manner. This SO question explains the details.
https://serverfault.com/a/901790/405334
networkaddress.cache.ttl
Specified in java.security to indicate the caching policy for successful name lookups from the name service.. The value is specified as integer to indicate the number of seconds to cache the successful lookup.
A value of -1 indicates “cache forever”. The default behavior is to cache forever when a security manager is installed, and to cache for an implementation specific period of time, when a security manager is not installed.
https://docs.oracle.com/javase/8/docs/technotes/guides/net/properties.html
Golang: When using the net/http library in Go 1.17 for testing, Go will re-read /etc/resolv.conf
.
The behavior of other applications may vary. Some applications periodically check the DNS configuration file for updates, while others read it once at startup and keep it unchanged. To understand the behavior of a specific application, you may need to consult the documentation or configuration options for that application or perform testing to determine how it handles changes to the /etc/resolv.conf
file.
3 Summary
There are two methods to modify the /etc/resolv.conf
file of pods: using Kubernetes’ native support with spec.dnsConfig
and spec.dnsPolicy
, directly modifying the /etc/resolv.conf
inside containers, and altering the resolv.conf
based on Docker’s characteristics.
The first two methods are generally applicable, while the third method is specific to Docker (or containerd) as the container runtime. When modifying the /etc/resolv.conf
file of a running container, it does not necessarily trigger applications to reread its contents. Different applications behave differently, with some reading the /etc/resolv.conf
file only once at startup and not rereading it, while others may periodically reread it for updated DNS configurations.
how to graceful update the node /etc/resolv.conf
on kubernetes cluster see this articles