TT Lab
はじめる
学ぶ 学習パス コース

Kubernetesネットワーク — 本物のクラスタで

クラスタDNSを実際に掘ってみる

TT Labで続きを見る

このラボは本物のKubernetes上で動きます

VMの中でk3sが1台、実際に起動しています。CoreDNSが本当に動き、クエリをログに残し、Podは実際のコンテナです。そのため、「名前を1つ探すのに、クエリが何回出るか」のようなことを、数えて確認できます。

最初に起動するまでに、2分ほどかかります。VMが起動して、k3sをインストールするからです。

目標

Podが名前を探す経路を最初から最後までたどり、ndots:5が作り出す隠れたコストを自分で数えて確認したうえで、dnsConfigで直します。

なぜ重要なのか

クラスターでの「遅い」という報告の相当数が、DNSです。ところが、アプリケーションのメトリクスにはなかなか現れません。リクエスト1つが、名前1つを探すのにクエリを5回投げて、そのうち4回がNXDOMAINでも、アプリケーションの立場では、ただの「少し遅いリクエスト」です。CoreDNSの負荷は静かに上がり、ある日Podの数が増えたときに、そこで破綻します。

原因は、resolv.confの1行です。ndots:5は、ドットが5個未満の名前は、先に検索ドメインを付けて試すという意味です。example.comはドットが1つなので、example.com.default.svc.cluster.localから問い合わせることになります。クラスター内の名前を短く書けるようにするための便利さが、外へ出ていく名前には、そのままコストになります。

ステップ

  1. CoreDNSがどこにあって何をしているかを確認してください(保存先: /root/k8sdns/coredns.txt)。kube-dnsServiceのClusterIPと、Corefileの全文が入っている必要があります。
  2. Pod内の/etc/resolv.confをそのまま保存してください(保存先: /root/k8sdns/resolv.txt)。nameserver・search・optionsの3行がすべてある必要があります。
  3. webというDeployment(レプリカ2、nginx:1.27-alpine、ポート名http)と、同じ名前のServiceを作成し、長い名前と短い名前の両方をルックアップして保存してください(保存先: /root/k8sdns/svc-a.txt)。同じClusterIPが出る必要があります。
  4. CoreDNSのCorefileでlogプラグインを有効にして、example.comを2種類のツールでそれぞれルックアップし、CoreDNSのログのクエリ数を数えて保存してください(保存先: /root/k8sdns/ndots.txt)。queries_nslookup=とqueries_getaddrinfo=の2行で始めて、その下にログを貼り付けます。
    • busyboxのnslookupを1回
    • curlを1回(curlimages/curl:8.10.1) 2つの数字が大きく違います。なぜ違うのかが、このステップの核心です。
  5. web-hというヘッドレスService(clusterIP: None)を作成して、ルックアップ結果を保存してください(保存先: /root/k8sdns/headless.txt)。PodのIPがそのまま出る必要があります。
  6. webのSRVレコードをルックアップして保存してください(保存先: /root/k8sdns/srv.txt)。
  7. lowdotsというPodを、dnsConfigでndots: 1を指定して起動し、ステップ4のcurlと同じツールでクエリ数をもう一度数えて保存してください(保存先: /root/k8sdns/fixed.txt、queries=の行を含めます)。3回以下である必要があります。ツールを変えると、比較が成立しません。
  8. dns_ip=、queries_before=、queries_after=の3行と一緒に、ndotsがなぜ問題だったのか、そして、nslookupで測るとなぜ問題が見えないのかを、/root/k8sdns/report.mdに書いてください。

参考

CoreDNSはどこにあるのか

CoreDNSがどこにあって何をしているかを確認してください(保存先: /root/k8sdns/coredns.txt)。kube-dnsServiceのClusterIPと、Corefileの全文が入っている必要があります。

kube-dnsという名前のServiceをkube-systemで探し、corednsConfigMapのCorefileを一緒に入れます。名前がCoreDNSなのにService名がkube-dnsなのは、以前の実装の名前をそのまま引き継いだからです。

Podは何を見て名前を探すのか

Pod内の/etc/resolv.confをそのまま保存してください(保存先: /root/k8sdns/resolv.txt)。nameserver・search・optionsの3行がすべてある必要があります。

Pod1つを起動して、その中の/etc/resolv.confをそのまま読みます。VM自身のresolv.confではなく、Podの中のものである必要があります。2つはまったく違います。

Service名がアドレスになる瞬間

webというDeployment(レプリカ2、nginx:1.27-alpine、ポート名http)と、同じ名前のServiceを作成し、長い名前と短い名前の両方をルックアップして保存してください(保存先: /root/k8sdns/svc-a.txt)。同じClusterIPが出る必要があります。

DeploymentとServiceを作成したあと、同じネームスペースのPodから、webとweb.default.svc.cluster.localをそれぞれルックアップします。短い名前が使えるのは、searchドメインのおかげです。

名前1つにクエリが何回か

CoreDNSのCorefileでlogプラグインを有効にして、example.comを2種類のツールでそれぞれルックアップし、CoreDNSのログのクエリ数を数えて保存してください(保存先: /root/k8sdns/ndots.txt)。queries_nslookup=とqueries_getaddrinfo=の2行で始めて、その下にログを貼り付けます。

Corefileにlogを1行入れて、CoreDNSを再起動したあと、Podからexample.comを1回だけルックアップして、ログを数えます。末尾にドットを付けると(example.com.)、検索ドメインをスキップするので、付けないでください。

ヘッドレスは何を返すのか

web-hというヘッドレスService(clusterIP: None)を作成して、ルックアップ結果を保存してください(保存先: /root/k8sdns/headless.txt)。PodのIPがそのまま出る必要があります。

clusterIP: NoneのServiceを作成して、ルックアップします。普通のServiceと違い、アドレスが複数出ます。

ポート番号を名前で探す

webのSRVレコードをルックアップして保存してください(保存先: /root/k8sdns/srv.txt)。

SRVの名前は_<포트이름>._tcp.<서비스>.<네임스페이스>.svc.cluster.localです(プレースホルダーはポート名、Service、ネームスペースです)。ステップ3で、ポートにhttpという名前を付けておきました。

ndotsを下げて直す

lowdotsというPodを、dnsConfigでndots: 1を指定して起動し、ステップ4のcurlと同じツールでクエリ数をもう一度数えて保存してください(保存先: /root/k8sdns/fixed.txt、queries=の行を含めます)。3回以下である必要があります。ツールを変えると、比較が成立しません。

PodのspecのdnsConfig.optionsに{name: ndots, value: "1"}を入れます。そうすると、ドットが1つだけでも、検索ドメインを通りません。

何を学んだか

dns_ip=、queries_before=、queries_after=の3行と一緒に、ndotsがなぜ問題だったのか、そして、nslookupで測るとなぜ問題が見えないのかを、/root/k8sdns/report.mdに書いてください。

dns_ip=、queries_before=、queries_after=の3行と一緒に、ndotsがなぜ問題だったのかを、本文に書きます。