GKE용 kube-dns 정보

Standard 클러스터에서 애플리케이션을 실행하는 경우 kube-dns는 서비스 검색 및 통신을 사용 설정하는 데 도움이 되는 기본 DNS 제공업체입니다. 이 문서에서는 GKE 환경 내에서 DNS 확인을 최적화하기 위한 아키텍처, 구성, 권장사항을 비롯하여 kube-dns로 DNS를 관리하는 방법을 설명합니다.

이 문서는 GKE에서 DNS를 관리하는 개발자, 관리자, 설계자를 대상으로 합니다. Google Cloud의 일반적인 역할 및 작업에 관한 컨텍스트는 일반 GKE Enterprise 사용자 역할 및 작업을 참고하세요.

시작하기 전에 Kubernetes 서비스 및 일반적인 DNS 개념을 숙지해야 합니다.

kube-dns 아키텍처 이해

kube-dns는 GKE 클러스터 내에서 작동하여 포드와 서비스 간에 DNS 변환을 지원합니다.

다음 다이어그램은 포드가 kube-dns 서비스와 상호작용하는 방식을 보여줍니다.

그림 1: `kube-dns` 포드로 지원되는 `kube-dns` 서비스에 포드가 DNS 쿼리를 전송하는 방법을 보여주는 다이어그램 `kube-dns` 포드는 내부 DNS 확인을 처리하고 외부 쿼리를 업스트림 DNS 서버로 전달합니다.

주요 구성요소

kube-dns에는 다음 주요 구성요소가 포함됩니다.

  • kube-dns 포드: 이 포드는 kube-dns 서버 소프트웨어를 실행합니다. 이러한 포드의 여러 복제본이 kube-system 네임스페이스에서 실행되며 고가용성 및 중복성을 제공합니다.
  • kube-dns 서비스: ClusterIP 유형의 이 Kubernetes 서비스kube-dns 포드를 그룹화하고 단일의 안정적인 엔드포인트로 노출합니다. ClusterIP는 클러스터의 DNS 서버 역할을 하며, 포드는 이를 사용하여 DNS 쿼리를 전송합니다. kube-dns헤드리스 서비스당 최대 1,000개의 엔드포인트를 지원합니다.
  • kube-dns-autoscaler: 이 포드는 노드 수와 CPU 코어 수를 포함한 클러스터 크기에 따라 kube-dns 복제본 수를 조정합니다. 이 접근 방식은 kube-dns가 다양한 DNS 쿼리 부하를 처리할 수 있도록 지원합니다.

내부 DNS 변환

포드가 클러스터 도메인(예: myservice.my-namespace.svc.cluster.local) 내에서 DNS 이름을 확인해야 하는 경우 다음 프로세스가 발생합니다.

  1. 포드 DNS 구성: 각 노드의 kubelet는 포드의 /etc/resolv.conf 파일을 구성합니다. 이 파일은 kube-dns 서비스의 ClusterIP을 네임 서버로 사용합니다.
  2. DNS 쿼리: 포드가 kube-dns 서비스에 DNS 쿼리를 보냅니다.
  3. 이름 확인: kube-dns가 쿼리를 수신합니다. 내부 DNS 레코드에서 해당 IP 주소를 조회하고 포드에 응답합니다.
  4. 통신: 포드는 확인된 IP 주소를 사용하여 타겟 서비스와 통신합니다.

외부 DNS 변환

포드가 외부 DNS 이름 또는 클러스터 도메인 외부의 이름을 확인해야 하는 경우 kube-dns이 재귀 확인자로 작동합니다. ConfigMap 파일에 구성된 업스트림 DNS 서버로 쿼리를 전달합니다. 스텁 도메인이라고도 하는 특정 도메인에 대해 커스텀 리졸버를 구성할 수도 있습니다. 이 구성은 kube-dns가 해당 도메인에 대한 요청을 특정 업스트림 DNS 서버로 전달하도록 지시합니다.

포드 DNS 구성

GKE에서 각 노드의 kubelet 에이전트는 해당 노드에서 실행되는 포드의 DNS 설정을 구성합니다.

/etc/resolv.conf 파일 구성

GKE가 포드를 만들면 kubelet 에이전트가 포드의 /etc/resolv.conf 파일을 수정합니다. 이 파일은 이름 확인을 위한 DNS 서버를 구성하고 검색 도메인을 지정합니다. 기본적으로 kubelet는 클러스터의 내부 DNS 서비스인 kube-dns을 네임서버로 사용하도록 포드를 구성합니다. 또한 파일에 검색 도메인을 채웁니다. 이러한 검색 도메인을 사용하면 DNS 쿼리에서 정규화되지 않은 이름을 사용할 수 있습니다. 예를 들어 포드가 myservice를 쿼리하면 Kubernetes는 먼저 myservice.default.svc.cluster.local를 확인하고, myservice.svc.cluster.local를 확인한 다음 search 목록의 다른 도메인을 확인하려고 시도합니다.

다음 예시에서는 기본 /etc/resolv.conf 구성을 보여줍니다.

nameserver 10.0.0.10
search default.svc.cluster.local svc.cluster.local cluster.local c.my-project-id.internal google.internal
options ndots:5

이 파일에는 다음 항목이 있습니다.

  • nameserver: kube-dns 서비스의 ClusterIP을 정의합니다.
  • search: DNS 조회 중에 정규화되지 않은 이름에 추가되는 검색 도메인을 정의합니다.
  • options ndots:5: GKE가 이름을 정규화된 것으로 간주하는 시점의 기준을 설정합니다. 점이 5개 이상인 이름은 정규화된 이름으로 간주됩니다.

hostNetwork: true 설정으로 구성된 포드는 호스트에서 DNS 구성을 상속받고 kube-dns를 직접 쿼리하지 않습니다.

kube-dns 맞춤설정

kube-dns는 강력한 기본 DNS 변환을 제공합니다. 해상도 효율성을 개선하거나 선호하는 DNS 리졸버를 사용하는 등 특정 요구사항에 맞게 동작을 맞춤설정할 수 있습니다. 스텁 도메인과 업스트림 네임서버는 모두 kube-system 네임스페이스에서 kube-dns ConfigMap을 수정하여 구성됩니다.

kube-dns ConfigMap 수정

kube-dns ConfigMap을 수정하려면 다음 단계를 따르세요.

  1. 수정할 ConfigMap을 엽니다.

    kubectl edit configmap kube-dns -n kube-system
    
  2. data 섹션에서 stubDomainsupstreamNameservers 필드를 다음 위치에 추가합니다.

    apiVersion: v1
    kind: ConfigMap
    metadata:
      labels:
        addonmanager.kubernetes.io/mode: EnsureExists
      name: kube-dns
      namespace: kube-system
    data:
      stubDomains: |
        {
          "example.com": [
            "8.8.8.8",
            "8.8.4.4"
          ],
          "internal": [ # Required if your upstream nameservers can't resolve GKE internal domains
            "169.254.169.254" # IP of the metadata server
          ]
        }
      upstreamNameservers: |
        [
          "8.8.8.8", # Google Public DNS
          "1.1.1.1" # Cloudflare DNS
        ]
    
  3. ConfigMap을 저장합니다. kube-dns가 구성을 자동으로 새로고침합니다.

스텁 도메인

스텁 도메인을 사용하면 특정 도메인에 대한 맞춤 DNS 리졸버를 정의할 수 있습니다. Pod가 해당 스텁 도메인 내에서 이름을 쿼리하면 kube-dns는 기본 확인 메커니즘을 사용하는 대신 지정된 리졸버로 쿼리를 전달합니다.

kube-dns ConfigMapstubDomains 섹션을 포함합니다.

이 섹션에서는 도메인과 해당하는 업스트림 네임서버를 지정합니다. 그런 다음 kube-dns는 해당 도메인 내 이름에 대한 쿼리를 지정된 서버로 전달합니다. 예를 들어 internal.mycompany.com의 모든 DNS 쿼리를 192.168.0.10로 라우팅하고 "internal.mycompany.com": ["192.168.0.10"]stubDomains에 추가할 수 있습니다.

example.com과 같은 스텁 도메인에 커스텀 리졸버를 설정하면 kube-dns*.example.com과 같은 하위 도메인을 포함하여 해당 도메인의 모든 이름 확인 요청을 지정된 서버로 전달합니다.

업스트림 네임서버

맞춤 업스트림 네임서버를 사용하여 외부 도메인 이름을 확인하도록 kube-dns를 구성할 수 있습니다. 이 구성은 클러스터의 내부 도메인(*.cluster.local)에 대한 요청을 제외한 모든 DNS 요청을 지정된 업스트림 서버로 전달하도록 kube-dns에 지시합니다. metadata.internal*.google.internal과 같은 내부 도메인은 맞춤 업스트림 서버에서 확인할 수 없을 수 있습니다. GKE용 워크로드 아이덴티티 제휴를 사용 설정하거나 이러한 도메인에 종속된 워크로드가 있는 경우 ConfigMapinternal의 스텁 도메인을 추가합니다. 메타데이터 서버의 IP 주소인 169.254.169.254를 이 스텁 도메인의 리졸버로 사용합니다.

맞춤 kube-dns 배포 관리

표준 GKE에서 kube-dns는 배포로 실행됩니다. 맞춤 kube-dns 배포는 클러스터 관리자가 기본 GKE 제공 배포를 사용하는 대신 배포를 제어하고 필요에 따라 맞춤설정할 수 있음을 의미합니다.

맞춤 배포가 필요한 이유

다음과 같은 이유로 맞춤 kube-dns 배포를 고려하세요.

  • 리소스 할당: kube-dns 포드의 CPU 및 메모리 리소스를 미세 조정하여 DNS 트래픽이 많은 클러스터의 성능을 최적화합니다.
  • 이미지 버전: kube-dns 이미지의 특정 버전을 사용하거나 CoreDNS와 같은 대체 DNS 제공업체로 전환합니다.
  • 고급 구성: 로깅 수준, 보안 정책, DNS 캐싱 동작을 맞춤설정합니다.

커스텀 배포 자동 확장

기본 제공 kube-dns-autoscaler는 기본 kube-dns 배포와 함께 작동합니다. 맞춤 kube-dns 배포를 만드는 경우 내장 자동 확장 처리기가 이를 관리하지 않습니다. 따라서 커스텀 배포의 복제본 수를 모니터링하고 조정하도록 특별히 구성된 별도의 자동 확장 처리를 설정해야 합니다. 이 방법은 클러스터에서 자체 자동 확장 처리 구성 만들고 배포하는 것입니다.

맞춤 배포를 관리하는 경우 자동 스케일러 이미지를 최신 상태로 유지하는 등 모든 구성요소에 대한 책임이 있습니다. 오래된 구성요소를 사용하면 성능 저하 또는 DNS 오류가 발생할 수 있습니다.

자체 kube-dns 배포를 구성하고 관리하는 방법에 관한 자세한 내용은 커스텀 kube-dns 배포 설정을 참고하세요.

문제 해결

kube-dns 문제 해결에 대한 자세한 내용은 다음 페이지를 참고하세요.

DNS 변환 최적화

이 섹션에서는 GKE에서 DNS를 관리할 때 발생하는 일반적인 문제와 권장사항을 설명합니다.

포드의 dnsConfig 검색 도메인 한도

Kubernetes는 DNS 검색 도메인 수를 32개로 제한합니다. 포드의 dnsConfig에 검색 도메인을 32개 이상 정의하려고 하면 kube-apiserver에서 포드를 만들지 않으며 다음과 유사한 오류가 발생합니다.

The Pod "dns-example" is invalid: spec.dnsConfig.searches: Invalid value: []string{"ns1.svc.cluster-domain.example", "my.dns.search.suffix1", "ns2.svc.cluster-domain.example", "my.dns.search.suffix2", "ns3.svc.cluster-domain.example", "my.dns.search.suffix3", "ns4.svc.cluster-domain.example", "my.dns.search.suffix4", "ns5.svc.cluster-domain.example", "my.dns.search.suffix5", "ns6.svc.cluster-domain.example", "my.dns.search.suffix6", "ns7.svc.cluster-domain.example", "my.dns.search.suffix7", "ns8.svc.cluster-domain.example", "my.dns.search.suffix8", "ns9.svc.cluster-domain.example", "my.dns.search.suffix9", "ns10.svc.cluster-domain.example", "my.dns.search.suffix10", "ns11.svc.cluster-domain.example", "my.dns.search.suffix11", "ns12.svc.cluster-domain.example", "my.dns.search.suffix12", "ns13.svc.cluster-domain.example", "my.dns.search.suffix13", "ns14.svc.cluster-domain.example", "my.dns.search.suffix14", "ns15.svc.cluster-domain.example", "my.dns.search.suffix15", "ns16.svc.cluster-domain.example", "my.dns.search.suffix16", "my.dns.search.suffix17"}: must not have more than 32 search paths.

kube-apiserver는 Pod 생성 시도에 대한 응답으로 이 오류 메시지를 반환합니다. 이 문제를 해결하려면 구성에서 추가 검색 경로를 삭제하세요.

kube-dns의 업스트림 nameservers 한도

kube-dnsupstreamNameservers 값을 3개로 제한합니다. 3개를 초과하여 정의하면 Cloud Logging에 다음과 유사한 오류가 표시됩니다.

Invalid configuration: upstreamNameserver cannot have more than three entries (value was &TypeMeta{Kind:,APIVersion:,}), ignoring update

이 시나리오에서 kube-dnsupstreamNameservers 구성을 무시하고 이전의 유효한 구성을 계속 사용합니다. 이 문제를 해결하려면 kube-dns ConfigMap에서 추가 upstreamNameservers를 삭제하세요.

kube-dns 확장

Standard 클러스터에서는 클러스터 노드가 확장될 때 더 많은 kube-dns 포드가 생성되도록 nodesPerReplica에 더 낮은 값을 사용할 수 있습니다. Kubernetes API를 보는 다수의 kube-dns 포드로 인해 GKE 컨트롤 플레인 가상 머신 (VM)이 과부하되지 않도록 max 필드의 명시적 값을 설정하는 것이 좋습니다.

max 필드의 값을 클러스터의 노드 수로 설정할 수 있습니다. 클러스터에 노드가 500개 이상 있으면 max 필드 값을 500로 설정합니다.

kube-dns-autoscaler ConfigMap를 수정하여 kube-dns 복제본 수를 수정할 수 있습니다.

kubectl edit configmap kube-dns-autoscaler --namespace=kube-system

출력은 다음과 비슷합니다.

linear: '{"coresPerReplica":256, "nodesPerReplica":16,"preventSinglePointFailure":true}'

kube-dns 복제본 수는 다음 공식을 사용하여 계산됩니다.

replicas = max( ceil( cores * 1/coresPerReplica ) , ceil( nodes * 1/nodesPerReplica ) )

확장하려면 nodesPerReplica 필드의 값을 더 작은 값으로 변경하고 max 필드의 값을 포함합니다.

linear: '{"coresPerReplica":256, "nodesPerReplica":8,"max": 15,"preventSinglePointFailure":true}'

이 구성은 클러스터의 노드 8개마다 kube-dns 포드 하나를 만듭니다. 24노드 클러스터에는 복제본이 3개, 40노드 클러스터에는 5개가 있습니다. 클러스터에서 노드가 120개를 넘어도 kube-dns 복제본 수는 max 필드 값인 15를 초과하여 증가하지 않습니다.

클러스터에서 DNS 가용성의 기준 수준을 보장하려면 kube-dns 필드에 대한 최소 복제본 수를 설정합니다.

min 필드가 구성된 kube-dns-autoscaler ConfigMap의 출력은 다음과 유사합니다.

linear: '{"coresPerReplica":256, "nodesPerReplica":8,"max": 15,"min": 5,"preventSinglePointFailure":true}'

DNS 조회 시간 개선

기본 kube-dns 제공업체의 DNS 조회 또는 DNS 변환 실패 시 지연 시간이 길어지는 데에는 여러 요인이 있을 수 있습니다. 애플리케이션에서 이러한 문제가 getaddrinfo EAI_AGAIN 오류로 표시될 수 있습니다. 이는 이름 확인의 일시적인 실패를 나타냅니다. 원인은 다음과 같습니다.

  • 워크로드 내에서 빈번한 DNS 조회
  • 노드당 포드 밀도가 높습니다.
  • 스팟 VM 또는 선점형 VM에서 kube-dns 실행. 이럴 경우 예기치 않은 노드 삭제가 발생할 수 있습니다.
  • kube-dns 포드 내에서 dnsmasq 인스턴스의 용량을 초과하는 높은 쿼리 볼륨 단일 kube-dns 인스턴스는 GKE 버전 1.31 이상에서 동시 TCP 연결이 200개로 제한되고 GKE 버전 1.30 이하에서는 동시 TCP 연결이 20개로 제한됩니다.

DNS 조회 시간을 개선하려면 다음 단계를 따르세요.

  • kube-dns와 같은 중요한 시스템 구성요소를 스팟 VM 또는 선점형 VM에서 실행하지 마세요. 표준 VM이 있고 스팟 VM이나 선점형 VM이 없는 노드 풀을 하나 이상 만듭니다. taint 및 톨러레이션(toleration)을 사용하여 중요한 워크로드가 이러한 안정적인 노드에 예약되도록 합니다.
  • NodeLocal DNSCache 사용 설정 NodeLocal DNSCache는 각 노드에 직접 DNS 응답을 캐시하므로 지연 시간과 kube-dns 서비스의 부하가 줄어듭니다. NodeLocal DNSCache를 사용 설정하고 기본 거부 규칙과 함께 네트워크 정책을 사용하는 경우 워크로드가 node-local-dns 포드에 DNS 쿼리를 전송하도록 허용하는 정책을 추가합니다.
  • kube-dns를 스케일 업합니다.
  • dns.lookup은 동기식이므로 애플리케이션이 dns.lookup 기반 함수가 아닌 dns.resolve* 기반 함수를 사용하는지 확인하세요.
  • https://google.com/ 대신 https://google.com./와 같은 정규화된 도메인 이름 (FQDN)을 사용합니다.

GKE 클러스터를 업그레이드하는 동안 kube-dns를 포함한 컨트롤 플레인 구성요소의 동시 업그레이드로 인해 DNS 변환 오류가 발생할 수 있습니다. 이러한 장애는 일반적으로 소수의 노드에 영향을 미칩니다. 프로덕션 클러스터에 적용하기 전에 비프로덕션 환경에서 클러스터 업그레이드를 철저히 테스트합니다.

서비스 검색 가능 여부 확인

kube-dns는 엔드포인트가 있는 서비스의 DNS 레코드만 만듭니다. 서비스에 엔드포인트가 없으면 kube-dns에서 해당 서비스의 DNS 레코드를 만들지 않습니다.

DNS TTL 불일치 관리

kube-dns가 업스트림 DNS 리졸버로부터 대규모 TTL 또는 무한 TTL로 DNS 응답을 받는 경우 이 TTL 값을 유지합니다. 이 동작으로 인해 캐시된 항목과 실제 IP 주소 간에 불일치가 발생할 수 있습니다.

GKE는 1.21.14-gke.9100 이상 또는 1.22.15-gke.2100 이상과 같은 특정 컨트롤 플레인 버전에서 이 문제를 해결합니다. 이러한 버전은 TTL이 더 높은 DNS 응답에 대해 최대 TTL 값을 30초로 설정합니다. 이 동작은 NodeLocal DNSCache와 비슷합니다.

kube-dns 측정항목 보기

kube-dns 포드에서 직접 클러스터의 DNS 쿼리에 관한 측정항목을 가져올 수 있습니다.

  1. kube-system 네임스페이스에서 kube-dns 포드를 찾습니다.

    kubectl get pods -n kube-system --selector=k8s-app=kube-dns
    

    출력은 다음과 비슷합니다.

    NAME                        READY     STATUS    RESTARTS   AGE
    kube-dns-548976df6c-98fkd   4/4       Running   0          48m
    kube-dns-548976df6c-x4xsh   4/4       Running   0          47m
    
  2. 포드 중 하나를 선택하고 해당 포드의 측정항목에 액세스할 수 있도록 포트 전달을 설정합니다.

    • 포트 10055kube-dns 측정항목을 노출합니다.
    • 포트 10054dnsmasq 측정항목을 노출합니다.

    POD_NAME을 선택한 포드의 이름으로 바꿉니다.

    POD_NAME="kube-dns-548976df6c-98fkd" # Replace with your pod name
    kubectl port-forward pod/${POD_NAME} -n kube-system 10055:10055 10054:10054
    

    출력은 다음과 비슷합니다.

    Forwarding from 127.0.0.1:10054 -> 10054
    Forwarding from 127.0.0.1:10055 -> 10055
    
  3. 새 터미널 세션에서 curl 명령어를 사용하여 측정항목 엔드포인트에 액세스합니다.

    # Get kube-dns metrics
    curl http://127.0.0.1:10055/metrics
    
    # Get dnsmasq metrics
    curl http://127.0.0.1:10054/metrics
    

    출력은 다음과 비슷하게 표시됩니다.

    kubedns_dnsmasq_errors 0
    kubedns_dnsmasq_evictions 0
    kubedns_dnsmasq_hits 3.67351e+06
    kubedns_dnsmasq_insertions 254114
    kubedns_dnsmasq_max_size 1000
    kubedns_dnsmasq_misses 3.278166e+06
    

다음 단계