NodeLocal DNSCache を設定する

このドキュメントでは、NodeLocal DNSCache を使用して DNS ルックアップのレイテンシを短縮し、Google Kubernetes Engine(GKE)クラスタでアプリケーションのパフォーマンスを改善する方法について説明します。

NodeLocal DNSCache は、各クラスタノードで DNS キャッシュを DaemonSet として直接実行することで、DNS のパフォーマンスを向上させる GKE アドオンです。Pod が DNS リクエストを行うと、そのリクエストはまず同じノードのローカル キャッシュに送信されます。リクエストをローカルで処理すると、DNS ルックアップの平均時間が大幅に短縮され、kube-dns や Cloud DNS for GKE などのクラスタの中央 DNS プロバイダの負荷が軽減されます。DNS アーキテクチャと NodeLocal DNSCache のメリットの詳細については、サービス ディスカバリについてをご覧ください。

GKE Autopilot クラスタでは、NodeLocal DNSCache はデフォルトで有効になっており、無効にすることはできません。バージョン 1.33.1 以降を実行している GKE Standard クラスタでは、NodeLocal DNSCache はデフォルトで有効になっていますが、無効にすることもできます。

このドキュメントは、デベロッパー、管理者、アーキテクトなどの GKE ユーザーを対象としています。 Google Cloudの一般的なロールとタスクの例の詳細については、一般的な GKE Enterprise ユーザーのロールとタスクをご覧ください。

このドキュメントは、次の内容を理解していることを前提としています。

アーキテクチャ

NodeLocal DNSCache は、kube-dns に加えて実行できる GKE アドオンです。

GKE では、NodeLocal DNSCache はクラスタ内の各ノードで DNS キャッシュを実行する DaemonSet として実装されます。

Pod が DNS リクエストを行うと、そのリクエストは、Pod と同じノード上で実行されている DNS キャッシュに移動します。キャッシュで DNS リクエストを解決できない場合、キャッシュはクエリの宛先に基づいて次のいずれかの場所にリクエストを転送します。

  • kube-dns: クラスタ DNS ドメイン(cluster.local)に対するクエリはすべて kube-dns に転送されます。node-local-dns Pod は、kube-dns-upstream Service を使用して kube-dns Pod にアクセスします。
  • カスタムスタブ ドメインまたはアップストリーム ネームサーバー: クエリは NodeLocal DNSCache Pod から直接転送されます。
  • Cloud DNS: 他のすべてのクエリは、クエリの送信元と同じノードで実行されるローカル メタデータ サーバーに転送されます。ローカル メタデータ サーバーは Cloud DNS にアクセスします。

前の段落で説明されている DNS リクエストのパス。

既存のクラスタで NodeLocal DNSCache を有効にすると、GKE は ノードのアップグレード プロセスに沿って、GKE バージョン 1.15 以降を実行しているすべてのクラスタノードを再作成します。

ノードが再作成されると、GKE が自動的に addon.gke.io/node-local-dns-ds-ready=true ラベルをノードに追加します。このラベルは、クラスタノードに手動で追加しないでください。

NodeLocal DNSCache のメリット

NodeLocal DNSCache には次のメリットがあります。

  • 平均 DNS ルックアップ時間が短縮される
  • Pod からローカル キャッシュへの接続によって、conntrack テーブル エントリは作成されません。これにより、conntrack テーブルの消耗や競合状態による接続のドロップや拒否が回避されます。
  • NodeLocal DNSCache は GKE 向け Cloud DNS で使用できます。
  • 外部 URL(クラスタ リソースを参照しない URL)に対する DNS クエリは、ローカル メタデータ サーバーに直接転送され、kube-dns をバイパスします。
  • ローカル DNS キャッシュは、スタブドメインのカスタム リゾルバの追加セクションで指定されているスタブドメインとアップストリーム ネームサーバーを自動的に選択します。

要件と制限事項

  • NodeLocal DNSCache は、クラスタの各ノードでコンピューティング リソースを消費します。
  • NodeLocal DNSCache は Windows Server ノードプールではサポートされていません。
  • NodeLocal DNSCache には GKE バージョン 1.15 以降が必要です。
  • NodeLocal DNSCache は、TCP を使用して kube-dns Pod にアクセスします。
  • NodeLocal DNSCache は、GKE バージョン 1.18 以降で TCP と UDP を使用して upstreamServersstubDomains にアクセスします。DNS サーバーは、TCP と UDP を使用して到達可能である必要があります。
  • DNS レコードは、次の期間キャッシュに保存されます。
    • レコードの有効期間(TTL)。または、TTL が 30 秒を超える場合は 30 秒。
    • DNS レスポンスが NXDOMAIN の場合は 5 秒。
  • NodeLocal DNSCache Pod は、ノードのポート 53、9253、9353、8080 でリッスンします。 他の hostNetwork Pod を実行するか、これらのポートで hostPorts を構成した場合、NodeLocal DNSCache が失敗し、DNS エラーが発生します。GKE Dataplane V2 を使用し、GKE 向け Cloud DNS を使用しない場合、NodeLocal DNSCache Pod は hostNetwork モードを使用しません。
  • ローカル DNS キャッシュは、GKE バージョン 1.15 以降を実行しているノードプールでのみ実行されます。これよりも前のバージョンを実行しているノードを持つクラスタで NodeLocal DNSCache を有効にすると、それらのノード上の Pod は kube-dns を使用します。

始める前に

作業を始める前に、次のタスクが完了していることを確認してください。

  • Google Kubernetes Engine API を有効にする。
  • Google Kubernetes Engine API の有効化
  • このタスクに Google Cloud CLI を使用する場合は、gcloud CLI をインストールして初期化する。gcloud CLI をインストール済みの場合は、gcloud components update コマンドを実行して最新のバージョンを取得します。以前のバージョンの gcloud CLI では、このドキュメントのコマンドを実行できない場合があります。
  • 既存の Autopilot クラスタまたは Standard クラスタがあることを確認します。必要な場合は、Autopilot クラスタを作成します。Autopilot クラスタの場合、NodeLocal DNSCache はデフォルトで有効になっており、オーバーライドできません。

NodeLocal DNSCache を有効にする

Standard クラスタの場合、Google Cloud CLI または Google Cloud コンソールを使用して NodeLocal DNSCache を有効にできます。

gcloud

既存のクラスタで NodeLocal DNSCache を有効にするには、引数 NodeLocalDNS=ENABLED を指定して --update-addons フラグを使用します。

gcloud container clusters update CLUSTER_NAME \
    --location=COMPUTE_LOCATION \
    --update-addons=NodeLocalDNS=ENABLED

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

コンソール

新しいクラスタで NodeLocal DNSCache を有効にするには、次の手順を行います。

  1. Google Cloud コンソールで [Google Kubernetes Engine] ページに移動します。

    [Kubernetes クラスタ] に移動

  2. 変更するクラスタの名前をクリックします。

  3. [ネットワーキング] の [DNS プロバイダ] フィールドで、 [DNS プロバイダを編集] をクリックします。

  4. [NodeLocal DNSCache を有効にする] チェックボックスをオンにします。

  5. [変更を保存] をクリックします。

この変更を行うにはノードの再作成が必要になり、実行中のワークロードが停止する可能性があります。この変更について詳しくは、ノード アップグレード戦略に従ってノードを再作成し、メンテナンス ポリシーを遵守する手動変更の表で対応する行をご覧ください。ノードの更新の詳細については、ノードの更新による中断の計画をご覧ください。

NodeLocal DNSCache が有効になっていることを確認する

node-local-dns Pod を一覧表示して、NodeLocal DNSCache が実行されていることを確認できます。

kubectl get pods -n kube-system -o wide | grep node-local-dns

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

node-local-dns-869mt    1/1   Running   0   6m24s   10.128.0.35   gke-test-pool-69efb6b8-5d7m   <none>   <none>
node-local-dns-htx4w    1/1   Running   0   6m24s   10.128.0.36   gke-test-pool-69efb6b8-wssk   <none>   <none>
node-local-dns-v5njk    1/1   Running   0   6m24s   10.128.0.33   gke-test-pool-69efb6b8-bhz3   <none>   <none>

GKE バージョン 1.15 以降を実行している各ノードの node-local-dns Pod が出力に表示されます。

NodeLocal DNSCache を無効にする

NodeLocal DNSCache を無効にするには、次のコマンドを使用します。

gcloud container clusters update CLUSTER_NAME \
    --location=COMPUTE_LOCATION \
    --update-addons=NodeLocalDNS=DISABLED

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

この変更を行うにはノードの再作成が必要になり、実行中のワークロードが中断する可能性があります。この変更について詳しくは、ノード アップグレード戦略に従ってノードを再作成し、メンテナンス ポリシーを遵守する手動変更の表で対応する行をご覧ください。ノードの更新の詳細については、ノードの更新による中断の計画をご覧ください。

NodeLocal DNSCache のトラブルシューティング

Kubernetes DNS の問題の診断に関する一般的な情報については、Debugging DNS Resolution をご覧ください。

NodeLocal DNSCache がすぐに有効にならない

既存のクラスタで NodeLocal DNSCache を有効にしても、クラスタにメンテナンスの時間枠または除外が構成されている場合は、GKE でノードがすぐに更新されないことがあります。詳細については、ノードの再作成とメンテナンスの時間枠に関する注意点をご覧ください。

待機しないようにするには、gcloud container clusters upgrade コマンドを呼び出し、すでにノードプールが動作している GKE バージョンを指定した --cluster-version フラグを渡すことで、ノードに変更を手動で適用できます。この対処法には、Google Cloud CLI を使用する必要があります。

Cloud DNS での NodeLocal DNSCache

Cloud DNS で NodeLocal DNSCache を使用する場合、次の図のように、クラスタはネームサーバー IP アドレス 169.254.20.10 を使用します。

NodeLocal DNSCache と Cloud DNS を使用するアーキテクチャ。

その結果、kube-dns Service の IP アドレスが、Pod が使用するネームサーバー IP アドレスと異なる場合があります。この IP アドレスの違いは想定内です。Cloud DNS が正しく機能するには、169.254.20.10 ネームサーバー IP アドレスが必要です。

IP アドレスを確認するには、次のコマンドを実行します。

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

    kubectl get svc -n kube-system kube-dns -o jsonpath="{.spec.clusterIP}"
    

    出力は kube-dns の IP アドレスです(例: 10.0.0.10)。

  2. Pod でシェル セッションを開きます。

    kubectl exec -it POD_NAME -- /bin/bash
    
  3. Pod シェル セッションで、/etc/resolv.conf ファイルの内容を読み取ります。

    cat /etc/resolv.conf
    

    出力は 169.254.20.10 です。

NodeLocal DNSCache を使用したネットワーク ポリシー

NodeLocal DNSCache でネットワーク ポリシーを使用し、Cloud DNS または GKE Dataplane V2 を使用していない場合、ワークロードと node-local-dns Pod が DNS クエリを送信できるようにルールを構成する必要があります。

マニフェストの ipBlock ルールを使用して、Pod と kube-dns 間の通信を許可します。

次のマニフェストは、ipBlock ルールを使用するネットワーク ポリシーを記述しています。

spec:
  egress:
  - ports:
    - port: 53
      protocol: TCP
    - port: 53
      protocol: UDP
    to:
    - ipBlock:
        cidr: KUBE_DNS_SVC_CLUSTER_IP/32
  podSelector: {}
  policyTypes:
    - Egress

KUBE_DNS_SVC_CLUSTER_IP は、kube-dns Service の IP アドレスに置き換えます。kube-dns Service の IP アドレスを取得するには、次のコマンドを使用します。

kubectl get svc -n kube-system kube-dns -o jsonpath="{.spec.clusterIP}"

既知の問題

このセクションでは、NodeLocal DNSCache の既知の問題について説明します。

NodeLocal DNSCache と GKE Dataplane V2 を使用する場合の ClusterFirstWithHostNet dnsPolicy での DNS タイムアウト

GKE Dataplane V2 と NodeLocal DNSCache を使用するクラスタでは、hostNetwork フィールドが true に、dnsPolicy フィールドが ClusterFirstWithHostNet に設定されている Pod はクラスタの DNS バックエンドに到達できません。DNS ログには、次のようなエントリが含まれている場合があります。

dnslookup: write to 'a.b.c.d': Operation not permitted

;; connection timed out; no servers could be reached

出力には、DNS リクエストがバックエンド サーバーに到達できないことが示されています。

この問題を回避するには、hostNetwork Pod に dnsPolicy フィールドと dnsConfig フィールドを設定します。

spec:
 dnsPolicy: "None"
 dnsConfig:
   nameservers:
     - KUBE_DNS_UPSTREAM
   searches:
     - NAMESPACE.svc.cluster.local
     - svc.cluster.local
     - cluster.local
     - c.PROJECT_ID.internal
     - google.internal
   options:
     - name: ndots
       value: "5"

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

  • NAMESPACE: hostNetwork Pod の名前空間。
  • PROJECT_ID: 実際の Google Cloud プロジェクト ID。
  • KUBE_DNS_UPSTREAM: アップストリーム kube-dns サービスの ClusterIP。この値は、次のコマンドで取得できます。

    kubectl get svc -n kube-system kube-dns-upstream -o jsonpath="{.spec.clusterIP}"
    

これで、Pod からの DNS リクエストが kube-dns に到達し、NodeLocal DNSCache をバイパスできるようになります。

NodeLocal DNSCache タイムアウト エラー

NodeLocal DNSCache が有効になっているクラスタでは、ログに次のようなエントリが含まれていることがあります。

[ERROR] plugin/errors: 2 <hostname> A: read tcp <node IP: port>-><kubedns IP>:53: i/o timeout

出力には、kube-dns-upstream クラスタ IP サービスの IP アドレスが含まれます。この例では、DNS リクエストに対するレスポンスを kube-dns から 2 秒以内に受信していません。この問題の原因としては次のいずれかが考えられます。

  • 使用しているネットワーク接続の問題。
  • ワークロードからの DNS クエリ、またはノードプールのアップスケーリングにより、DNS クエリが大幅に増加した。

その結果、既存の kube-dns Pod では、すべてのリクエストを時間内に処理できなくなります。回避策は、kube-dns をスケールアップして kube-dns レプリカの数を増やすことです。

kube-dns のスケールアップ

nodesPerReplica フィールドに小さい値を使用すると、クラスタノードのスケールアップ時に kube-dns Pod がさらに作成されます。Kubernetes API を監視する kube-dns Pod が多いことが原因で GKE コントロール プレーン仮想マシン(VM)が過負荷状態にならないように、明示的な max 値を設定することを強くおすすめします。

max フィールドにクラスタ内のノード数を設定できます。クラスタのノード数が 500 を超える場合は、max フィールドを 500 に設定します。

Standard クラスタの場合、kube-dns-autoscaler ConfigMap を編集して、kube-dns レプリカの数を変更できます。この構成は、Autopilot クラスタでサポートされていません。

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))

ConfigMap で min フィールドと max フィールドを定義すると、レプリカはこれらの値によって制限されます。スケールアップするには、nodesPerReplica フィールドの値を小さくし、max フィールドの値を設定します。

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

この構成では、クラスタ内の 8 ノードごとに 1 つの kube-dns Pod を作成します。24 ノードクラスタには 3 つのレプリカがあり、40 ノードクラスタには 5 つのレプリカがあります。クラスタが 120 ノードを超えても、kube-dns レプリカの数は 15(max 値)を超えることはありません。

クラスタで DNS の可用性のベースライン レベルを確保するには、kube-dns の最小レプリカ数を設定します。

min フィールドが定義されている kube-dns-autoscaler ConfigMap の出力は次のようになります。

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

次のステップ