kubeadm 은 CoreDNS 를 어떻게 설치할까요? 파드는 서비스 도메인을 어떻게 처리할까요? 그리고 설정은 어떻게 할까요? (feat: NodeLocal DNSCache)

kubeadm 은 CoreDNS 를 어떻게 설치할까요? 파드는 서비스 도메인을 어떻게 처리할까요? 그리고 설정은 어떻게 할까요? (feat: NodeLocal DNSCache)
💡
CoreDNS 는 쿠버네티스 “서비스 디스커버리”를 위한 핵심 애드온입니다. kubeadm 에서는 CoreDNS , kube-proxy 두가지의 애드온을 설치합니다. 만약 바닐라 쿠버네티스 클러스터를 프로비저닝 하고 있다면 이러한 애드온은 수동으로 설치해야 합니다.

들어가며

Kubeadm 의 경우 CoreDNS 및 kube-proxy 를 addon 으로 부르며, 클러스터 프로비저닝 시점에서 자동으로 설치합니다. Kubeadm 을 wrapping 한 Kubespray 또한 마찬가지입니다.

만약 Kubeadm 과 같은 프로비저닝 툴을 사용하지 않고 클러스터를 프로비저닝 했다면 CNI 설치, CoreDNS , kube-proxy의 설치같은 작업은 관리자가 직접 해야합니다.

바닐라 쿠버네티스 프로비저닝 툴 작업을 진행하면서 Kubeadm 은 과연 어떻게 CoreDNS 를 설치하고, 또 어떻게 10.96.0.10 등의 ClusterIP 를 고정해서 사용하게 되는지 궁금하여 Kubeadm 의 코드를 확인해가면서 그 동작을 확인해보았습니다.

그 이후 과연 파드들은 어떻게 nameserver 설정을 디폴트로 갖고 오게 되는지 확인해보고, PodSpec 내의 dnsPolicy, dnsConfig 등의 내용을 확인해보았습니다.

마지막으로는 NodeLocal DNSCache 라는 각 노드별로 설치되는 DNS 캐싱 에이전트에 대해서 설명하고, 설치하는 방법에 대해서 소개합니다.

Kubespary로 클러스터를 프로비저닝 하게 되면 NodeLocal DNSCache 는 디폴트로 설치하게 됩니다. 하지만 Kubeadm 으로 클러스터를 프로비저닝하면 수동으로 설치해야합니다.

CoreDNS

CoreDNS 는 Go 로 작성된 DNS 서버입니다. 다양한 환경에서 사용 가능한 DNS 서버로 보통은 쿠버네티스 클러스터에 설치되어서 클러스터내에서 도메인 네임 resolving 을 위해 사용됩니다.(cluster.local)

Kubernetes Pod 의 쿠버네티스 서비스 디스커버리를 위한 nameserver 설정

 

쿠버네티스 파드를 띄우면 모든 파드에는 /etc/resolv.conf 파일이 있고. 이 파일에는 nameserver 정보가 들어있습니다. kubeadm 으로 프로비저닝 한 클러스터 내의 파드의 /etc/resolv.conf 를 한번 확인해보겠습니다.

$ kubectl run -i -t --rm nginx --image=nginx --restart=Never – sh

$ cat /etc/resolv.conf 를 수행한 결과
💡
search 는 어떤 Domain 에 대한 요청에 대해서 Domain Name Resolve 가 실패하는경우 해당 도메인 요청에 뒷부분에 default.svc.cluster.local , svc.cluster.local, cluster.local 등을 순서대로 추가하려고 하는 동작이다. 예를들어서 nginx-0 에 대한 요청이 들어오면 nginx-0.default.svc.cluster.local 에 대해서 resolve 되는지 확인하고, 실패하면 nginx-0.svc.cluster.local 로 시도하고 하는 식이다.

확인해보면 nameserver 10.96.0.10 이라는 값이 들어가있는 것을 볼 수 있습니다. 이 네임서버는 어디에 있는 것일까요?

클러스터 내에 설치된 coreDNS 의 서비스 오브젝트를 확인해보겠습니다.

$ kubectl get svc -n kube-system

kube-dns라는 이름의 서비스 오브젝트를 확인 할 수 있습니다.
coreDNS 를 설치했지만 서비스 오브젝트 이름이 kube-dns 인 이유는 kubernetes/cmd/kubeadm/app/phases/addons/dns/manifests.go at 5732a8bbb44113f7fba40721f0f2e03051999da0 · kubernetes/kubernetes manifests 자체에 coreDNS 이지만 서비스 오브젝트의 이름을 kube-dns 로 생성해서 그렇습니다. 처음에는 쿠버네티스 팀이 놓친것이라고 생각했지만 코드를 잘 보니, node-local-dns-cache 등이 뒷단이 coreDNS 든 kube-dns 든 상관없이 지원하기 위해서라고 쿠버네티스 DNS 서비스 이름은 그냥 kube-dns 로 두려고 한것이 아닐까라고 생각했는데, 실제로 하위호환성을 위해 그렇게 두었다고 합니다. (참고 : https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/?ref=kimsehwan96.com#introduction)

위 서비스 오브젝트의 ClusterIP 를 확인해보면 아까 nameserver 10.96.0.10 에서 본것과 동일하다는 것을 알 수 있습니다.

그럼 이 파드별로 들어가게 될 /etc/resolv.conf 는 어떻게 지정이 되었고, kubeadm 을 사용하면서 별도로 지정한적도 없는 coreDNS(혹은 kube-dns)의 ClusterIP 는 어떻게 10.96.0.10 으로 고정이 되었을까요?

Kubelet 의 clusterDNS 설정

각 파드별로 생성될 /etc/resolv.conf 의 정보는 kubelet configuration 에서의 clusterDNS 항목을 통해 설정이 됩니다. kubelet config 파일을 확인해보면

clusterDNS 에 10.96.0.10 주소가 들어있다.

clusterDNS 항목에 클러스터 Domain Name 을 처리하기 위한 nameserver 리스트를 넣을 수 있도록 되어있음을 확인 할 수 있습니다. (Customizing DNS Service )

기본적으로 node 의 kubelet 의 clusterDNS 항목을 상속하지만, 파드별로 별도로 수정해야 할 필요가 있다면 podSpec 내의 dnsPolicydnsConfig 를 이용해 변경 가능합니다.

DNS for Services and Pods

Pod 의 DNS Policy 는 파드별로 설정이 가능합니다. 여기에 설정 가능한 값으로는 Default, ClusterFirst , ClusterFirstWithHostNet , None 등이 설정 가능합니다. 기본적으로 명시하지 않고 파드를 생성하면 ClusterFirst 로 설정됩니다.

ClusterFirst 로 지정하면 cluster.local 과 같은 suffix 가 아닌 www.google.com 과 같은 DNS 쿼리는 DNS 서버의 업스트림 네임서버로 넘어가도록 하는, 기본적으로 CoreDNS(kube-dns) 를 사용하는 옵션입니다.

Default 로 지정하면 해당 파드가 떠있는 노드의 /etc/resolv.conf 설정을 그대로 사용합니다. 따라서 이렇게 지정하고 cluster.local suffix 를 가진 DNS 쿼리를 하게 되었을 때, 노드 에 적절한 네임서버가 설정되지 않은경우 처리되지 않을 수 있습니다.

ClusterFirstWithHostNet 은 파드의 hostNetwork: true 설정을 했을 때 사용해야하며, 만약 hostNetwork: true 설정을 했지만 , ClusterFirst 로 설정하거나, 명시적으로 설정하지 않으면 Default 설정으로 fallback 하게 됩니다.

None 의 경우는 해당 파드가 떠있는 쿠버네티스 환경에서 상속받는 모든 DNS 설정을 무시하고, dnsConfig 를 통해 DNS 설정을 커스텀 할 때 사용합니다.

만약 Pod 내의 DNS 설정을 직접 하고 싶다면, dnsConfig 를 통해 namserver , searches, options 등을 지정 할 수 있습니다.

apiVersion: v1
kind: Pod
metadata:
  name: dnsutils
  namespace: hayden
spec:
  containers:
  - name: dnsutils
    image: registry.k8s.io/e2e-test-images/jessie-dnsutils:1.3
    command:
      - sleep
      - "infinity"
    imagePullPolicy: IfNotPresent
  restartPolicy: Always
  dnsConfig:
    nameservers:
      - 10.96.0.10
      - 123.123.123.123
      - 8.8.8.8
    searches: 
      - consul.local

위와 같이 dnsConfig 를 설정하고 실제 생성된 Pod 내에서 /etc/resolv.conf 를 확인해보면

CoreDNS(kube-dns)의 IP 주소는 어떻게 결정되었을까요?

kubeadm 에서는 kubernetes/cmd/kubeadm/app/phases/addons/dns/manifests.go at 1d5589e4910ed859a69b3e57c25cbbd3439cd65f · kubernetes/kubernetes 여기서 .DNSIP 라는 변수를 통해서 받아서 CoreDNS 의 ClusterIP 주소를 지정하는데요.

kubernetes/cmd/kubeadm/app/constants/constants.go at 5732a8bbb44113f7fba40721f0f2e03051999da0 · kubernetes/kubernetes

위 코드부분을 보면 알겠지만, Service Object 의 CIDR 블록에서 10번째의 IP 주소를 사용하도록 하고있습니다. 그래서 kubeadm 으로 클러스터를 프로비저닝하면 DNS 를 위한 서비스 오브젝트의 ClusterIP 주소는 10.96.0.10 과 같이 10번째 IP 주소로 고정이 되는것입니다.

이것을 kubelet 의 clusterDNS 항목에도, CoreDNS 의 ClusterIP 항목에도 넣어서 일치시켜주는것입니다.

kubeadm 을 사용하지 않고 쿠버네티스 클러스터를 프로비저닝 한 경우(Vanilia) 위와 같은 형태로 사전에 CoreDNS(혹은 kube-dns) 서비스에 대한 ClusterIP 를 미리 계획하고, Kubelet 의 clusterDNS 항목에 설정해줘야 별다른 설정을 하지 않은 파드들이 올바르게 서비스 도메인에 대한 DNS 쿼리 Resolve 를 수행 할 수 있습니다.

Kubeadm 은 어떻게 CoreDNS 를 설치하나요?

$ kubeadm init 으로 컨트롤 플레인 노드를 최초로 생성하는 시점에서 https://github.com/kubernetes/kubernetes/blob/bce55b94cdc3a4592749aa919c591fa7df7453eb/cmd/kubeadm/app/cmd/init.go#L106-L192

혹은 $ kubeadm init phase addon 명령을 수행하는 시점에서 (https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-init-phase/#cmd-phase-addon)

kubeadm workflow runner 에 아래 코드와 같은 명령어를 등록하게 됩니다.

https://github.com/kubernetes/kubernetes/blob/1d5589e4910ed859a69b3e57c25cbbd3439cd65f/cmd/kubeadm/app/cmd/phases/init/addons.go#L49-L85

여기서 runCoreDNSAddon 은 아래 코드와 같은것을 수행하고

https://github.com/kubernetes/kubernetes/blob/bce55b94cdc3a4592749aa919c591fa7df7453eb/cmd/kubeadm/app/cmd/phases/init/addons.go#L108-L114

여기에서의 EnsureDNSAddon는 아래쪽 코드에 정의되어있고

https://github.com/kubernetes/kubernetes/blob/bce55b94cdc3a4592749aa919c591fa7df7453eb/cmd/kubeadm/app/phases/addons/dns/dns.go#L90-L170

전반적으로 사용하는 템플릿 manifests 는 아래에 정의되어있습니다.

https://github.com/kubernetes/kubernetes/blob/1d5589e4910ed859a69b3e57c25cbbd3439cd65f/cmd/kubeadm/app/phases/addons/dns/manifests.go

쉽게 추상화해보면, kubeadm init 을 수행하면 여러 phases 를 수행하는데, 그 중 addon phase 가 있고, 별도로 kubeadm init phase addon 으로 해당 phase 만 수행이 가능하기도 합니다.

addon phase 에서는 사전에 정의된 manifests 를 템플리팅하여서 kubeadm 설정에 맞게 manifests를 렌더링하고 그것을 쿠버네티스 클러스터에 반영하는 형태로 CoreDNS를 설치하고있습니다. (kube-proxy 도 마찬가지입니다.)

NodeLocal DNSCache

Using NodeLocal DNSCache in Kubernetes Clusters

kubeadm 으로 프로비저닝 한 클러스터는 CoreDNS 정도만 설치하는 반면, kubeadm 을 wrapping 한 kubespray 의 경우 기본적으로 NodeLocal DNSCache 를 설치하도록 되어있습니다.

NodeLocal DNSCache 는 각 노드별로 DNS 캐싱 에이전트를 DaemonSet 으로 실행하여 클러스터의 DNS 성능을 향상시키기 위해 사용합니다.

쿠버네티스에서 내부 파드간 통신은 대부분 서비스 오브젝트를 통해서 하게 되는데, 이 때 DNS Query 과정은 Pod 가 로컬의 /etc/resolv.conf 설정을 보고 네임서버에 DNS Query 를 하게 되고. 해당 설정에는 보통 CoreDNS 에 대한 서비스 오브젝트 ClusterIP 가 지정되어있습니다. 이 ClusterIP 에 대해서 요청하게되면 내부적으로는 kube-proxy 가 추가한 iptables rule 에 의해 실제 CoreDNS(kube-dns) 엔드포인트로 변환되는 과정을 거치게 됩니다.

만약 각 노드별로 쿠버네티스 클러스터 도메인에 대한 DNS 정보를 캐싱한 데몬셋이 있는 경우 이 iptables 의 DNAT 규칙, connection tracking 을 피하고 단순히 각 노드의 로컬에 있는 DNS 캐시 서버에 질의를 하는 방식으로 단순화 되고, 만약 각 로컬에 있는 DNS 캐싱 에이전트가 Cache Miss 되는 경우에만 CoreDNS(kube-dns)에 캐싱 에이전트가 대신 DNS 정보를 업데이트 하는 식으로 구조가 간단하게 바뀝니다.

보통 NodeLocal DNSCache 는 클러스터의 서비스 오브젝트 IP 대역과 충돌하지 않는 대역의 IP 를 기반으로 사용되고, 권장되는 대역은 169.254.0.0/16 입니다. 보통은 169.254.xx.10 를 주로 사용합니다.

NodeLocal DNSCache 설치 방법

Using NodeLocal DNSCache in Kubernetes Clusters

만약 기존 클러스터에 NodeLocal DNSCache 를 설치하고 싶다면 아래와 같은 방식으로 구성해야 합니다.

우선 kubernetes/cluster/addons/dns/nodelocaldns/nodelocaldns.yaml at master · kubernetes/kubernetes 주소의 yaml 파일을 내려받습니다.

# Copyright 2018 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

apiVersion: v1
kind: ServiceAccount
metadata:
  name: node-local-dns
  namespace: kube-system
  labels:
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: Reconcile
---
apiVersion: v1
kind: Service
metadata:
  name: kube-dns-upstream
  namespace: kube-system
  labels:
    k8s-app: kube-dns
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: Reconcile
    kubernetes.io/name: "KubeDNSUpstream"
spec:
  ports:
  - name: dns
    port: 53
    protocol: UDP
    targetPort: 53
  - name: dns-tcp
    port: 53
    protocol: TCP
    targetPort: 53
  selector:
    k8s-app: kube-dns
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: node-local-dns
  namespace: kube-system
  labels:
    addonmanager.kubernetes.io/mode: Reconcile
data:
  Corefile: |
    __PILLAR__DNS__DOMAIN__:53 {
        errors
        cache {
                success 9984 30
                denial 9984 5
        }
        reload
        loop
        bind __PILLAR__LOCAL__DNS__ __PILLAR__DNS__SERVER__
        forward . __PILLAR__CLUSTER__DNS__ {
                force_tcp
        }
        prometheus :9253
        health __PILLAR__LOCAL__DNS__:8080
        }
    in-addr.arpa:53 {
        errors
        cache 30
        reload
        loop
        bind __PILLAR__LOCAL__DNS__ __PILLAR__DNS__SERVER__
        forward . __PILLAR__CLUSTER__DNS__ {
                force_tcp
        }
        prometheus :9253
        }
    ip6.arpa:53 {
        errors
        cache 30
        reload
        loop
        bind __PILLAR__LOCAL__DNS__ __PILLAR__DNS__SERVER__
        forward . __PILLAR__CLUSTER__DNS__ {
                force_tcp
        }
        prometheus :9253
        }
    .:53 {
        errors
        cache 30
        reload
        loop
        bind __PILLAR__LOCAL__DNS__ __PILLAR__DNS__SERVER__
        forward . __PILLAR__UPSTREAM__SERVERS__
        prometheus :9253
        }
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-local-dns
  namespace: kube-system
  labels:
    k8s-app: node-local-dns
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: Reconcile
spec:
  updateStrategy:
    rollingUpdate:
      maxUnavailable: 10%
  selector:
    matchLabels:
      k8s-app: node-local-dns
  template:
    metadata:
      labels:
        k8s-app: node-local-dns
      annotations:
        prometheus.io/port: "9253"
        prometheus.io/scrape: "true"
    spec:
      priorityClassName: system-node-critical
      serviceAccountName: node-local-dns
      hostNetwork: true
      dnsPolicy: Default  # Don't use cluster DNS.
      tolerations:
      - key: "CriticalAddonsOnly"
        operator: "Exists"
      - effect: "NoExecute"
        operator: "Exists"
      - effect: "NoSchedule"
        operator: "Exists"
      containers:
      - name: node-cache
        image: registry.k8s.io/dns/k8s-dns-node-cache:1.23.0
        resources:
          requests:
            cpu: 25m
            memory: 5Mi
        args: [ "-localip", "__PILLAR__LOCAL__DNS__,__PILLAR__DNS__SERVER__", "-conf", "/etc/Corefile", "-upstreamsvc", "kube-dns-upstream" ]
        securityContext:
          capabilities:
            add:
            - NET_ADMIN
        ports:
        - containerPort: 53
          name: dns
          protocol: UDP
        - containerPort: 53
          name: dns-tcp
          protocol: TCP
        - containerPort: 9253
          name: metrics
          protocol: TCP
        livenessProbe:
          httpGet:
            host: __PILLAR__LOCAL__DNS__
            path: /health
            port: 8080
          initialDelaySeconds: 60
          timeoutSeconds: 5
        volumeMounts:
        - mountPath: /run/xtables.lock
          name: xtables-lock
          readOnly: false
        - name: config-volume
          mountPath: /etc/coredns
        - name: kube-dns-config
          mountPath: /etc/kube-dns
      volumes:
      - name: xtables-lock
        hostPath:
          path: /run/xtables.lock
          type: FileOrCreate
      - name: kube-dns-config
        configMap:
          name: kube-dns
          optional: true
      - name: config-volume
        configMap:
          name: node-local-dns
          items:
            - key: Corefile
              path: Corefile.base
---
# A headless service is a service with a service IP but instead of load-balancing it will return the IPs of our associated Pods.
# We use this to expose metrics to Prometheus.
apiVersion: v1
kind: Service
metadata:
  annotations:
    prometheus.io/port: "9253"
    prometheus.io/scrape: "true"
  labels:
    k8s-app: node-local-dns
  name: node-local-dns
  namespace: kube-system
spec:
  clusterIP: None
  ports:
    - name: metrics
      port: 9253
      targetPort: 9253
  selector:
    k8s-app: node-local-dns

파일 명은 nodelocaldns.yaml 로 합니다. (아래쪽의 sed 명령어의 타겟 파일명으로 사용합니다)

kubedns=`kubectl get svc kube-dns -n kube-system -o jsonpath={.spec.clusterIP}`
domain=<cluster-domain>
localdns=<node-local-address>

위와 같은 명령어를 수행해서 올바른 값을 넣습니다. 대부분의 경우

kubedns=10.96.0.10 (서비스 오브젝트의 CIDR 대역에서의 10번째 값)

domain=cluster.local

localdns=169.254.25.10

으로 설정하면 됩니다.

만약 kube-proxy 가 IPTABLES 모드로 실행되고있다면

sed -i "s/__PILLAR__LOCAL__DNS__/$localdns/g; s/__PILLAR__DNS__DOMAIN__/$domain/g; s/__PILLAR__DNS__SERVER__/$kubedns/g" nodelocaldns.yaml

IPVS 모드로 실행되고 있다면

sed -i "s/__PILLAR__LOCAL__DNS__/$localdns/g; s/__PILLAR__DNS__DOMAIN__/$domain/g; s/,__PILLAR__DNS__SERVER__//g; s/__PILLAR__CLUSTER__DNS__/$kubedns/g" nodelocaldns.yaml
⚠️
MacOS 를 사용중이라면 $ brew install gnu-sed 를 설치하고. sed 명령어대신 gsed 로 위 명령어를 수행합니다.

위 명령어들은 ConfigMap 을 수정하기 위한 명령어입니다.

apiVersion: v1
kind: ConfigMap
metadata:
  name: node-local-dns
  namespace: kube-system
  labels:
    addonmanager.kubernetes.io/mode: Reconcile
data:
  Corefile: |
    __PILLAR__DNS__DOMAIN__:53 {
        errors
        cache {
                success 9984 30
                denial 9984 5
        }
        reload
        loop
        bind __PILLAR__LOCAL__DNS__ __PILLAR__DNS__SERVER__
        forward . __PILLAR__CLUSTER__DNS__ {
                force_tcp
        }
        prometheus :9253
        health __PILLAR__LOCAL__DNS__:8080
        }
    in-addr.arpa:53 {
        errors
        cache 30
        reload
        loop
        bind __PILLAR__LOCAL__DNS__ __PILLAR__DNS__SERVER__
        forward . __PILLAR__CLUSTER__DNS__ {
                force_tcp
        }
        prometheus :9253
        }
    ip6.arpa:53 {
        errors
        cache 30
        reload
        loop
        bind __PILLAR__LOCAL__DNS__ __PILLAR__DNS__SERVER__
        forward . __PILLAR__CLUSTER__DNS__ {
                force_tcp
        }
        prometheus :9253
        }
    .:53 {
        errors
        cache 30
        reload
        loop
        bind __PILLAR__LOCAL__DNS__ __PILLAR__DNS__SERVER__
        forward . __PILLAR__UPSTREAM__SERVERS__
        prometheus :9253
        }

여기서 __PILLAR__LOCAL__DNS__ 는 각 노드에서 이 DNS Cache 에이전트가 어떤 IP 로 바인딩 될지를 결정하는 부분이고. 우리는 여기서 169.254.25.10 으로 대치하게 됩니다.

__PILLAR__DNS__SERVER__ 의 경우 CoreDNS(kube-dns) 의 ClusterIP 로 대치됩니다.

__PILLAR__CLUSTER__DNS____PILLAR__UPSTREAM__SERVERS__ 는 node-local-dns-cache 데몬셋 파드 내부에서 동적으로 대치하는 영역인데, __PILLAR__CLUSTER__DNS__ 는 이 yaml 파일을 사용해서 생성하는 kube-dns-upstream 서비스 오브젝트의 ClusterIP 입니다.

여기서 유의해야하는점은 kube-dns-upstream 서비스 오브젝트의 레이블 셀렉터로 k8s-app=kube-dns 로 되어있기 때문에 CoreDNS 를 수동으로 설치한 경우 레이블에 k8s-app=kube-dns 를 추가해줘야합니다.

(만약 CoreDNS 를 helm 으로 설치했다면 values 에 k8sAppLabelOverride: "kube-dns" 라는 값을 사용하면 됩니다)

__PILLAR__UPSTREAM__SERVERS__ 의 경우 __PILLAR__LOCAL__DNS__ 값과 CoreDNS(kube-dns) ClusterIP 값으로 평가되어서 들어가게 됩니다.

sed 명령어를 사용하지 않고 직접 수정할 경우 아래 값들을 직접 대치하면 됩니다.

__PILLAR__DNS__DOMAIN__ : cluster.local 등의 클러스터 도메인

__PILLAR__LOCAL__DNS__ : 169.254.25.10 등의 node local dns cache 데몬셋 파드가 Listen 할 IP 지정

__PILLAR__DNS__SERVER__ : CoreDNS(kube-dns) 의 ClusterIP

이렇게 해서 node-local-dns 라는 이름의 데몬셋이 성공적으로 배포되고 나면, 실제 파드들이 node-local-dns 파드로 DNS 쿼리를 하도록 변경해야 합니다.

KubeletConfigurationclusterDNS 정보를 업데이트하고, kubelet 재시작을 함으로서 새로 생성되는 파드들에 대해서 node-local-dns 에 클러스터 도메인에 대한 DNS 쿼리를 하도록 설정 할 수 있습니다.

 

Wrapping up

쿠버네티스의 Pod 가 어떻게 클러스터 도메인에 대한 DNS Query Resolve 를 하는지 알아보면서, CoreDNS 에 대해서 알아보고, kubeadm 은 그것을 어떻게 설치하는지, 그리고 각 파드별 DNS 설정은 어떻게 하는지, 더 나아가서 NodeLocal DNSCache 까지 다양하게 다루게 되었습니다.  

kubadm / kubespray 등의 프로비저닝 툴은 기본적으로 kube-proxy, CoreDNS 등의 애드온을 알아서 설치하고 처리해줍니다. 하지만 Vanilia Kubernetes 클러스터를 구축하는 경우에는 그런것들을 모두 직접 설정해줘야 합니다.

kubeadm 에서 CoreDNS 를 어떻게 설치하고, CoreDNS 의 ClusterIP 는 어떻게 설정된것인지 알아보면서 왜 10.96.0.10 과 같은 IP 주소가 설정되었는지 알 수 있었고. 더 나아가서 kubelet 과도 긴밀히 연계되어있음을 알 수 있었습니다.