GKE 1.22 with Node Local DNS Cache で Alpine 3.13+ の名前解決に成功する

先月の記事 の解決編です。
GKE 1.22 における Node Local DNS Cache の挙動が修正されたようなので確認しました。

GKE 1.22 は現時点の最新リリースである 2022-R6 で 1.22.7-gke.1500 が登場していますので、今回はこちらで試してみます。

https://cloud.google.com/kubernetes-engine/docs/release-notes#2022-r6_version_updates

以下のように GKE クラスタを作成しました。

$ gcloud container clusters describe temp-cluster --zone us-central1-c
addonsConfig:
  dnsCacheConfig:
    enabled: true
  gcePersistentDiskCsiDriverConfig:
    enabled: true
  horizontalPodAutoscaling: {}
  httpLoadBalancing: {}
  kubernetesDashboard:
    disabled: true
  networkPolicyConfig:
    disabled: true
authenticatorGroupsConfig: {}
autoscaling:
  autoscalingProfile: BALANCED
clusterIpv4Cidr: 10.16.0.0/14
createTime: '2022-03-31T12:01:05+00:00'
currentMasterVersion: 1.22.7-gke.1500
currentNodeCount: 1
currentNodeVersion: 1.22.7-gke.1500
databaseEncryption:
  state: DECRYPTED
defaultMaxPodsConstraint:
  maxPodsPerNode: '110'
endpoint: ********************************
id: ********************************
initialClusterVersion: 1.22.7-gke.1500
instanceGroupUrls:
- https://www.googleapis.com/compute/v1/projects/*******************/zones/us-central1-c/instanceGroupManagers/gke-temp-cluster-default-pool-17d86d4d-grp
ipAllocationPolicy:
  clusterIpv4Cidr: 10.16.0.0/14
  clusterIpv4CidrBlock: 10.16.0.0/14
  clusterSecondaryRangeName: gke-temp-cluster-pods-fbb68f9f
  servicesIpv4Cidr: 10.20.0.0/20
  servicesIpv4CidrBlock: 10.20.0.0/20
  servicesSecondaryRangeName: gke-temp-cluster-services-fbb68f9f
  useIpAliases: true
labelFingerprint: a9dc16a7
legacyAbac: {}
location: us-central1-c
locations:
- us-central1-c
loggingConfig:
  componentConfig:
    enableComponents:
    - SYSTEM_COMPONENTS
    - WORKLOADS
loggingService: logging.googleapis.com/kubernetes
maintenancePolicy:
  resourceVersion: e3b0c442
masterAuth:
  clusterCaCertificate: ********************************
masterAuthorizedNetworksConfig: {}
monitoringConfig:
  componentConfig:
    enableComponents:
    - SYSTEM_COMPONENTS
monitoringService: monitoring.googleapis.com/kubernetes
name: temp-cluster
network: default
networkConfig:
  datapathProvider: LEGACY_DATAPATH
  defaultSnatStatus: {}
  network: projects/*******************/global/networks/default
  serviceExternalIpsConfig: {}
  subnetwork: projects/*******************/regions/us-central1/subnetworks/default
nodeConfig:
  diskSizeGb: 100
  diskType: pd-standard
  imageType: COS_CONTAINERD
  machineType: e2-medium
  metadata:
    disable-legacy-endpoints: 'true'
  oauthScopes:
  - https://www.googleapis.com/auth/devstorage.read_only
  - https://www.googleapis.com/auth/logging.write
  - https://www.googleapis.com/auth/monitoring
  - https://www.googleapis.com/auth/servicecontrol
  - https://www.googleapis.com/auth/service.management.readonly
  - https://www.googleapis.com/auth/trace.append
  preemptible: true
  serviceAccount: default
  shieldedInstanceConfig:
    enableIntegrityMonitoring: true
nodePoolAutoConfig: {}
nodePoolDefaults:
  nodeConfigDefaults: {}
nodePools:
- autoscaling: {}
  config:
    diskSizeGb: 100
    diskType: pd-standard
    imageType: COS_CONTAINERD
    machineType: e2-medium
    metadata:
      disable-legacy-endpoints: 'true'
    oauthScopes:
    - https://www.googleapis.com/auth/devstorage.read_only
    - https://www.googleapis.com/auth/logging.write
    - https://www.googleapis.com/auth/monitoring
    - https://www.googleapis.com/auth/servicecontrol
    - https://www.googleapis.com/auth/service.management.readonly
    - https://www.googleapis.com/auth/trace.append
    preemptible: true
    serviceAccount: default
    shieldedInstanceConfig:
      enableIntegrityMonitoring: true
  initialNodeCount: 1
  instanceGroupUrls:
  - https://www.googleapis.com/compute/v1/projects/*******************/zones/us-central1-c/instanceGroupManagers/gke-temp-cluster-default-pool-17d86d4d-grp
  locations:
  - us-central1-c
  management:
    autoRepair: true
    autoUpgrade: true
  maxPodsConstraint:
    maxPodsPerNode: '110'
  name: default-pool
  networkConfig:
    podIpv4CidrBlock: 10.16.0.0/14
    podRange: gke-temp-cluster-pods-fbb68f9f
  podIpv4CidrSize: 24
  selfLink: https://container.googleapis.com/v1/projects/*******************/zones/us-central1-c/clusters/temp-cluster/nodePools/default-pool
  status: RUNNING
  upgradeSettings:
    maxSurge: 1
  version: 1.22.7-gke.1500
notificationConfig:
  pubsub: {}
releaseChannel: {}
selfLink: https://container.googleapis.com/v1/projects/*******************/zones/us-central1-c/clusters/temp-cluster
servicesIpv4Cidr: 10.20.0.0/20
shieldedNodes:
  enabled: true
status: RUNNING
subnetwork: default
zone: us-central1-c

Alpine 3.13 をクラスタにデプロイし、コンテナから名前解決を試みます。

$ kubectl run alpine313 --image=alpine:3.13 --tty -i sh
If you don't see a command prompt, try pressing enter.

/ # nslookup www.google.com
Server:         10.20.0.10
Address:        10.20.0.10:53

*** Can't find www.google.com: No answer

Non-authoritative answer:
Name:   www.google.com
Address: 142.251.6.147
Name:   www.google.com
Address: 142.251.6.99
Name:   www.google.com
Address: 142.251.6.105
Name:   www.google.com
Address: 142.251.6.104
Name:   www.google.com
Address: 142.251.6.106
Name:   www.google.com
Address: 142.251.6.103

/ # wget www.google.com
Connecting to www.google.com (142.251.6.147:80)
saving to 'index.html'
index.html           100% |********************************************************************************************************************************************************************| 14053  0:00:00 ETA
'index.html' saved

/ #

無事に通りましたね。

Node Local DNS Cache の設定はどう変わったのでしょうか。
ConfigMap を確認してみます。

$ kubectl get configmap node-local-dns -n kube-system -o yaml
(以下、Corefile 内を抜粋)

  cluster.local:53 {
    errors
    
    template ANY AAAA {
      rcode NOERROR
    }
    
    cache {
            success 9984 30
            denial 9984 5
    }
    reload
    loop
    
    bind 169.254.20.10 10.20.0.10
    health 169.254.20.10:8080
    
    forward . __PILLAR__CLUSTER__DNS__ {
            force_tcp
            expire 1s
    }
    prometheus :9253
    }
in-addr.arpa:53 {
    errors
    cache 30
    reload
    loop
    
    bind 169.254.20.10 10.20.0.10
    
    forward . __PILLAR__CLUSTER__DNS__ {
            force_tcp
            expire 1s
    }
    prometheus :9253
    }
ip6.arpa:53 {
    errors
    cache 30
    reload
    loop
    
    bind 169.254.20.10 10.20.0.10
    
    forward . __PILLAR__CLUSTER__DNS__ {
            force_tcp
            expire 1s
    }
    prometheus :9253
    }
.:53 {
    errors
    
    template ANY AAAA {
      rcode NOERROR
    }
    
    cache 30
    reload
    loop
    
    bind 169.254.20.10 10.20.0.10
    
    forward . __PILLAR__UPSTREAM__SERVERS__ {
            force_tcp
    }
    prometheus :9253
    }

AAAA クエリに対するレスポンスが NOERROR に変更されました。
これで Pod が A + AAAA ペアクエリを投げても A クエリのレスポンスから名前解決に成功できますね。

ちなみに、以下のとおり Node Local DNS Cache のコンテナイメージは変更されていませんでした(k8s-dns-node-cache:1.21.4-gke.0)。

$ kubectl get daemonset node-local-dns -n kube-system -o yaml
(以下、一部抜粋)

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-local-dns
  namespace: kube-system
spec:
  template:
    spec:
      containers:
      - image: gke.gcr.io/k8s-dns-node-cache:1.21.4-gke.0
        name: node-cache
      volumes:
      - configMap:
          defaultMode: 420
          items:
          - key: Corefile
            path: Corefile.base
          name: node-local-dns
        name: config-volume

まとめ

GKE 1.22 における Node Local DNS Cache 利用時の名前解決の問題は解消しました。
利用している環境のライブラリやプラグインIPv6 対応が進むことで、似たような事象にまた出会うことがあるかもしれません。
特に DNS は多数の RFC を合わせて全体のセマンティクスが出来上がっていることがわかり、設計・実装に関わる場合は注意したいと思いました。