GKE で kube-dns のトラブルシューティングを行う


このページでは、Google Kubernetes Engine(GKE)の kube-dns に関する問題を解決する方法について説明します。

kube-dns の DNS の問題の原因を特定する

dial tcp: i/o timeoutno such hostCould not resolve host などのエラーは、kube-dns がクエリを解決する機能に問題があることを示します。

上記のエラーのいずれかが表示されたが、原因がわからない場合は、次のセクションで原因を特定してください。以降のセクションは、最も役立つと思われる手順から順に並べられています。各セクションを順に試してください。

kube-dns Pod が実行されていることを確認する

kube-dns Pod は、クラスタ内の名前解決に不可欠です。実行されていない場合は、DNS の解決に問題が発生する可能性があります。

kube-dns Pod が最近再起動されていないことを確認するには、これらの Pod のステータスを確認します。

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

出力は次のようになります。

NAME                   READY          STATUS          RESTARTS       AGE
kube-dns-POD_ID_1      5/5            Running         0              16d
kube-dns-POD_ID_2      0/5            Terminating     0              16d

この出力で、POD_ID_1POD_ID_2 は、kube-dns Pod に自動的に追加される一意の識別子を表します。

出力に、kube-dns Pod のステータスが Running ではないものがある場合は、次の手順を実施します。

  1. 管理アクティビティ監査ログを使用して、クラスタまたはノードプールのバージョンのアップグレード、kube-dns ConfigMap の変更など、最近変更が加えられたかどうかを調査します。監査ログの詳細については、GKE 監査ロギングの情報をご覧ください。変更が見つかった場合は、元に戻して Pod のステータスを再度確認します。

  2. 関連する最近の変更が見つからない場合は、kube-dns Pod が実行されているノードで OOM エラーが発生しているかどうかを調査します。Cloud Logging のログ メッセージに次のようなエラーが表示される場合は、これらの Pod で OOM エラーが発生しています。

    Warning: OOMKilling Memory cgroup out of memory
    

    このエラーを解決するには、エラー メッセージ: 「Warning: OOMKilling Memory cgroup out of memory」をご覧ください。

  3. OOM エラー メッセージが表示されない場合は、kube-dns Deployment を再起動します。

    kubectl rollout restart deployment/kube-dns --namespace=kube-system
    

    Deployment を再起動したら、kube-dns Pod が実行されていることを確認します。

上記の手順を試しても問題が解決しない、またはすべての kube-dns Pod のステータスが Running なのに DNS の問題が解決しない場合は、/etc/resolv.conf ファイルが正しく構成されていることを確認します。

/etc/resolv.conf が正しく構成されていることを確認する

DNS の問題が発生している Pod の /etc/resolv.conf ファイルを確認して、含まれているエントリが正しいことを確認します。

  1. Pod の /etc/resolv.conf ファイルを表示します。

    kubectl exec -it POD_NAME -- cat /etc/resolv.conf
    

    POD_NAME は、DNS の問題が発生している Pod の名前に置き換えます。問題が発生している Pod が複数ある場合は、Pod ごとにこのセクションの手順を繰り返します。

    Pod バイナリが kubectl exec コマンドをサポートしていない場合、このコマンドは失敗する可能性があります。この場合は、テスト環境として使用するシンプルな Pod を作成します。この手順では、問題のある Pod と同じ Namespace でテスト Pod を実行します。

  2. /etc/resolv.conf ファイル内のネームサーバー IP アドレスが正しいことを確認します。

    • ホスト ネットワークを使用している Pod は、ノードの /etc/resolv.conf ファイルの値を使用する必要があります。ネームサーバー IP アドレスは 169.254.169.254 にする必要があります。
    • ホスト ネットワークを使用していない Pod の場合、kube-dns Service の IP アドレスはネームサーバー IP アドレスと同じにする必要があります。IP アドレスを比較する手順は次のとおりです。

      1. kube-dns Service の IP アドレスを取得します。

        kubectl get svc kube-dns -n kube-system
        

        出力は次のようになります。

        NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE
        kube-dns   ClusterIP   192.0.2.10   <none>        53/UDP,53/TCP   64d
        
      2. [クラスタの IP] 列の値をメモします。この例では 192.0.2.10 です。

      3. kube-dns Service の IP アドレスと /etc/resolv.conf ファイルの IP アドレスを比較します。

        # cat /etc/resolv.conf
        
        search default.svc.cluster.local svc.cluster.local cluster.local c.PROJECT_NAME google.internal
        nameserver 192.0.2.10
        options ndots:5
        

        この例では、2 つの値が一致しているため、名前サーバー IP アドレスの誤りが問題の原因ではありません。

        ただし、IP アドレスが一致しない場合は、アプリケーション Pod のマニフェストに dnsConfig フィールドが構成されていることを意味します。

        dnsConfig.nameservers フィールドの値が正しい場合は、DNS サーバーを調査して、正しく機能していることを確認します。

        カスタム ネームサーバーを使用しない場合は、フィールドを削除して Pod のローリング再起動を行います。

        kubectl rollout restart deployment POD_NAME
        

        POD_NAME は、Pod の名前に置き換えます。

  3. /etc/resolv.confsearch エントリと ndots エントリを確認します。スペルミスや古い構成がないこと、失敗したリクエストが正しい Namespace 内の既存のサービスを指していることを確認します。

DNS ルックアップを実行する

/etc/resolv.conf が正しく構成され、DNS レコードが正しいことを確認したら、dig コマンドライン ツールを使用して、DNS エラーを報告している Pod から DNS ルックアップを実行します。

  1. Pod 内でシェルを開いて、Pod を直接クエリします。

    kubectl exec -it POD_NAME -n NAMESPACE_NAME -- SHELL_NAME
    

    次のように置き換えます。

    • POD_NAME: DNS エラーを報告している Pod の名前。
    • NAMESPACE_NAME: Pod が属する Namespace。
    • SHELL_NAME: 開くシェルの名前。たとえば、sh/bin/bash です。

    Pod で kubectl exec コマンドが許可されていない場合や、Pod に dig バイナリがない場合、このコマンドは失敗する可能性があります。この場合、dig がインストールされているイメージを使用してテスト Pod を作成します。

    kubectl run "test-$RANDOM" ti --restart=Never --image=thockin/dnsutils - bash
    
  2. Pod がクラスタの内部 DNS Service を正しく解決できるかどうかを確認します。

    dig kubernetes
    

    /etc/resolv.conf ファイルは kube-dns Service の IP アドレスを参照しているため、このコマンドを実行すると、DNS サーバーは kube-dns Service になります。

    Kubernetes API Service の IP アドレス(通常は 10.96.0.1 など)を含む成功した DNS レスポンスが表示されます。SERVFAIL が表示されるか、レスポンスがない場合、通常は kube-dns Pod が内部サービス名を解決できないことを示します。

  3. kube-dns Service が外部ドメイン名を解決できるかどうかを確認します。

    dig example.com
    
  4. 特定の kube-dns Pod が DNS クエリに応答しない場合は、その Pod が外部ドメイン名を解決できるかどうかを確認します。

     dig example.com @KUBE_DNS_POD_IP
    

    KUBE_DNS_POD_IP は、kube-dns Pod の IP アドレスに置き換えます。この IP アドレスの値がわからない場合は、次のコマンドを実行します。

     kubectl get pods -n kube-system -l k8s-app=kube-dns -o wide
    

    IP アドレスは IP 列に表示されます。

    コマンドの解決が成功すると、次の例に示すように、status: NOERROR と A レコードの詳細が表示されます。

     ; <<>> DiG 9.16.27 <<>> example.com
     ;; global options: +cmd
     ;; Got answer:
     ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 31256
     ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
    
     ;; OPT PSEUDOSECTION:
     ; EDNS: version: 0, flags:; udp: 512
     ;; QUESTION SECTION:
     ;example.com.                   IN      A
    
     ;; ANSWER SECTION:
     example.com.            30      IN      A       93.184.215.14
    
     ;; Query time: 6 msec
     ;; SERVER: 10.76.0.10#53(10.76.0.10)
     ;; WHEN: Tue Oct 15 16:45:26 UTC 2024
     ;; MSG SIZE  rcvd: 56
    
  5. シェルを終了します。

    exit
    

これらのコマンドのいずれかが失敗した場合は、kube-dns Deployment のローリング再起動を行います。

kubectl rollout restart deployment/kube-dns --namespace=kube-system

再起動が完了したら、dig コマンドをもう一度試して、コマンドが成功するかどうかを確認します。それでも失敗する場合は、パケット キャプチャに進みます。

パケット キャプチャを取得する

パケット キャプチャを取得して、DNS クエリが kube-dns Pod によって適切に受信され、応答されているかどうかを確認します。

  1. SSH を使用して、kube-dns Pod を実行しているノードに接続します。次に例を示します。

    1. Google Cloud コンソールで、[VM インスタンス] ページに移動します。

      [VM インスタンス] に移動

    2. 接続するノードを見つけます。kube-dns Pod のノードの名がわからない場合は、次のコマンドを実行します。

      kubectl get pods -n kube-system -l k8s-app=kube-dns -o wide
      

      ノードの名前は [ノード] 列に表示されます。

    3. [接続] 列で、[SSH] をクリックします。

  2. ターミナルで、プリインストールされているデバッグツールの toolbox を起動します。

    toolbox
    
  3. root プロンプトで、tcpdump パッケージをインストールします。

    apt update -y && apt install -y tcpdump
    
  4. tcpdump を使用して、DNS トラフィックのパケット キャプチャを取得します。

    tcpdump -i eth0 port 53" -w FILE_LOCATION
    

    FILE_LOCATION は、キャプチャを保存するパスに置き換えます。

  5. パケット キャプチャを確認します。宛先 IP アドレスが kube-dns Service の IP アドレスと一致するパケットがあるかどうかを確認します。これにより、DNS リクエストが解決のために正しい宛先に到達します。DNS トラフィックが正しい Pod に到達しない場合は、リクエストをブロックしているネットワーク ポリシーが存在している可能性があります。

ネットワーク ポリシーを確認する

制限的なネットワーク ポリシーにより、DNS トラフィックが中断されることがあります。kube-system Namespace にネットワーク ポリシーが存在するかどうかを確認するには、次のコマンドを実行します。

kubectl get networkpolicy -n kube-system

ネットワーク ポリシーが見つかった場合は、ポリシーを確認し、必要な DNS 通信が許可されていることを確認します。たとえば、すべての下り(外向き)トラフィックをブロックするネットワーク ポリシーがある場合、そのポリシーは DNS リクエストもブロックします。

出力が No resources found in kube-system namespace の場合、ネットワーク ポリシーがないため、問題の原因として除外できます。ログを調査すると、障害の原因をさらに特定できます。

一時的な DNS クエリのロギングを有効にする

誤った DNS レスポンスなどの問題を特定するには、一時的に DNS クエリのデバッグ ロギングを有効にします。

これはリソースを大量に消費するプロシージャであるため、適切なログのサンプルを収集した後に、このロギングを無効にすることをおすすめします。

kube-dns Pod を調査する

Cloud Logging を使用して、kube-dns Pod が DNS クエリを受信して解決する方法を確認します。

kube-dns Pod に関連するログエントリを表示するには、次の操作を行います。

  1. Google Cloud コンソールで、[ログ エクスプローラ] ページに移動します。

    [ログ エクスプローラ] に移動

  2. クエリペインで次のフィルタを入力して、kube-dns コンテナに関連するイベントを表示します。

    resource.type="k8s_container"
    resource.labels.namespace_name="kube-system"
    resource.labels.Pod_name:"kube-dns"
    resource.labels.cluster_name="CLUSTER_NAME"
    resource.labels.location="CLUSTER_LOCATION"
    

    次のように置き換えます。

    • CLUSTER_NAME: kube-dns Pod が属するクラスタの名前。
    • CLUSTER_LOCATION: クラスタのロケーション。
  3. [クエリを実行] をクリックします。

  4. 出力を確認します。次の出力例は、表示される可能性があるエラーの 1 つを示しています。

    {
       "timestamp": "2024-10-10T15:32:16.789Z",
       "severity": "ERROR",
       "resource": {
          "type": "k8s_container",
          "labels": {
          "namespace_name": "kube-system",
          "Pod_name": "kube-dns",
          "cluster_name": "CLUSTER_NAME",
          "location": "CLUSTER_LOCATION"
          }
       },
       "message": "Failed to resolve 'example.com': Timeout."
    },
    

    この例では、kube-dns は example.com を妥当な時間内に解決できませんでした。このタイプのエラーは、複数の問題が原因で発生する可能性があります。たとえば、上流サーバーが kube-dns ConfigMap で正しく構成されていないか、ネットワーク トラフィックが大量に発生している可能性があります。

Cloud Logging を有効にしていない場合は、代わりに Kubernetes ログを表示します。

Pod=$(kubectl get Pods -n kube-system -l k8s-app=kube-dns -o name | head -n1)
kubectl logs -n kube-system $Pod -c dnsmasq
kubectl logs -n kube-system $Pod -c kubedns
kubectl logs -n kube-system $Pod -c sidecar

kube-dns ConfigMap の最近の変更を調査する

クラスタで DNS 解決エラーが突然発生した場合、その原因の 1 つは、kube-dns ConfigMap に対する不適切な構成変更です。特に、スタブドメインとアップストリーム サーバーの定義の構成変更により問題が発生する可能性があります。

スタブドメイン設定の更新を確認する手順は次のとおりです。

  1. Google Cloud コンソールで、[ログ エクスプローラ] ページに移動します。

    [ログ エクスプローラ] に移動

  2. クエリペインに次のクエリを入力します。

    resource.labels.cluster_name="clouddns"
    resource.type="k8s_container"
    resource.labels.namespace_name="kube-system"
    labels.k8s-pod/k8s-app="kube-dns" jsonPayload.message=~"Updated stubDomains to"
    
  3. [クエリを実行] をクリックします。

  4. 出力を確認します。更新がある場合、出力は次のようになります。

    Updated stubDomains to map[example.com: [8.8.8.8 8.8.4.4 1.1.3.3 1.0.8.111]]
    

    更新がある場合は、結果を開いて変更の詳細を確認します。スタブドメインとそれに対応するアップストリーム DNS サーバーが正しく定義されていることを確認します。ここでの入力が正しくないと、それらのドメインの解決に失敗する可能性があります。

アップストリーム サーバーの変更を確認する手順は次のとおりです。

  1. Google Cloud コンソールで、[ログ エクスプローラ] ページに移動します。

    [ログ エクスプローラ] に移動

  2. クエリペインに次のクエリを入力します。

    resource.labels.cluster_name="clouddns"
    resource.type="k8s_container" resource.labels.namespace_name="kube-system"
    labels.k8s-pod/k8s-app="kube-dns" jsonPayload.message=~"Updated upstreamNameservers to"
    
  3. [クエリを実行] をクリックします。

  4. 出力を確認します。変更がある場合、出力は次のようになります。

    Updated upstreamNameservers to [8.8.8.8]
    

    結果を開くと、変更の詳細を確認できます。アップストリーム DNS サーバーのリストが正確であり、これらのサーバーにクラスタからアクセスできることを確認します。これらのサーバーが使用できない場合や、構成が正しくない場合、一般的な DNS 解決が失敗する可能性があります。

スタブドメインとアップストリーム サーバーの変更を確認しても結果が見つからない場合は、次のフィルタを使用してすべての変更を確認します。

resource.type="k8s_cluster"
protoPayload.resourceName:"namespaces/kube-system/configmaps/kube-dns"
protoPayload.methodName=~"io.k8s.core.v1.configmaps."

表示された変更を確認し、エラーの原因となっているかどうかを確認します。

Cloud カスタマーケアへのお問い合わせ

上記のセクションの手順をすべて試しても問題の原因を特定できない場合は、Cloud カスタマーケアにお問い合わせください

よくある問題を解決する

特定のエラーや問題が発生した場合は、次のセクションのアドバイスを使用してください。

問題: 断続的な DNS タイムアウト

DNS トラフィックの増加時や営業時間の開始時に、DNS 解決のタイムアウトが断続的に発生する場合は、次のソリューションを試して DNS のパフォーマンスを最適化してください。

  • クラスタで実行されている kube-dns Pod の数を確認し、GKE ノードの合計数と比較します。十分なリソースがない場合は、kube-dns Pod のスケールアップを検討してください。

  • 平均 DNS ルックアップ時間を短縮するには、NodeLocal DNS Cache を有効にします。

  • 外部名への DNS 解決により、kube-dns Pod が過負荷になる可能性があります。クエリの数を減らすには、/etc/resolv.conf ファイルの ndots 設定を調整します。ndots は、最初の絶対クエリの前にクエリを解決するためにドメイン名に表示する必要があるドット数を表します。

    次の例は、アプリケーション Pod の /etc/resolv.conf ファイルです。

    search default.svc.cluster.local svc.cluster.local cluster.local c.PROJECT_ID.internal google.internal
    nameserver 10.52.16.10
    options ndots:5
    

    この例では、kube-dns はクエリされたドメインで 5 つのドットを検索します。Pod が example.com の DNS 解決呼び出しを行う場合、ログは次の例のようになります。

    "A IN example.com.default.svc.cluster.local." NXDOMAIN
    "A IN example.com.svc.cluster.local." NXDOMAIN
    "A IN example.com.cluster.local." NXDOMAIN
    "A IN example.com.google.internal." NXDOMAIN
    "A IN example.com.c.PROJECT_ID.internal." NXDOMAIN
    "A IN example.com." NOERROR
    

    この問題を解決するには、ndots の値を 1 に変更して 1 つのドットのみ検索するか、クエリまたは使用するドメインの末尾にドット(.)を追加します。次に例を示します。

    dig example.com.
    

問題: 一部のノードから DNS クエリが断続的に失敗する

一部のノードから DNS クエリが断続的に失敗する場合は、次の症状が発生することがあります。

  • kube-dns Service の IP アドレスまたは Pod の IP アドレスに対して dig コマンドを実行すると、DNS クエリが断続的にタイムアウトします。
  • kube-dns Pod と同じノード上の Pod から dig コマンドを実行すると失敗します。

この問題を解決するには、次の操作を行います。

  1. 接続テストを実施します。問題の Pod またはノードを送信元として、宛先を kube-dns Pod の IP アドレスとして設定します。これにより、このトラフィックを許可するために必要なファイアウォール ルールが設定されているかどうかを確認できます。
  2. テストが成功せず、トラフィックがファイアウォール ルールによってブロックされている場合は、Cloud Logging を使用して、ファイアウォール ルールに加えた手動変更を一覧表示します。特定の種類のトラフィックをブロックする変更を探します。

    1. Google Cloud コンソールで、[ログ エクスプローラ] ページに移動します。

      [ログ エクスプローラ] に移動

    2. クエリペインに次のクエリを入力します。

      logName="projects/project-name/logs/cloudaudit.googleapis.com/activity"
      resource.type="gce_firewall_rule"
      
    3. [クエリを実行] をクリックします。クエリの出力を使用して、変更が加えられているかどうかを確認します。エラーが見つかった場合は、修正してファイアウォール ルールを再度適用します。

      自動化されたファイアウォール ルールに変更を加えないようにしてください。

  3. ファイアウォール ルールに変更が加えられていない場合は、ノードプールのバージョンを確認し、コントロール プレーンと他の正常なノードプールとの互換性を確認します。クラスタのノードプールのいずれかがコントロール プレーンの 2 つ前のマイナー バージョンよりも古い場合、問題が発生する可能性があります。この非互換性の詳細については、コントロール プレーンのバージョンとノードのバージョンに互換性がないをご覧ください。

  4. リクエストが正しい kube-dns サービス IP に送信されているかどうかを確認するには、問題のあるノードでネットワーク トラフィックをキャプチャし、ポート 53(DNS トラフィック)をフィルタします。kube-dns Pod 自体でトラフィックをキャプチャして、リクエストが目的の Pod に到達しているかどうか、リクエストが正常に解決されているかどうかを確認します。

次のステップ

  • Kubernetes DNS の問題の診断に関する一般的な情報については、DNS 解決のデバッグをご覧ください。