短い名前はタダではない
一言でいうと
短い名前が便利な分の代償は、外へ出ていく名前を探すときに請求されます。ドットが5個未満の名前は、すべてsearchを先に回るからです。
Kubernetesでは、webとだけ書いても、同じネームスペースのServiceを見つけられます。便利です。ところが、その便利さには代償があり、その代償は、外へ出ていく名前を探すときに請求されます。
searchとndots
Podの/etc/resolv.confは、このような形です。
search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.43.0.10
options ndots:5
searchは、短い名前の後ろに順番に付けてみるサフィックスで、ndots:5は、ドットが5個未満の名前は、まだ完成した名前ではないかもしれないので、先にsearchを試すという意味です。
webはドットが0個なので、当然searchを使います。問題はexample.comです。ドットが1つだけなので、これも5未満で、そのため、この順序で問い合わせます。
example.com.default.svc.cluster.local → NXDOMAIN
example.com.svc.cluster.local → NXDOMAIN
example.com.cluster.local → NXDOMAIN
example.com. → 드디어 응답
1回のリクエストで往復が4回、そのうち3回は捨てられます。
なぜメトリクスに現れないのか
DNSのルックアップは、アプリケーションコードではなく、その下のライブラリで行われます。そのため、アプリケーションのトレースにスパンが現れないことが多く、ユーザーには「少し遅いリクエスト」としか見えません。CoreDNSの負荷だけが静かに上がっていき、Podの数が増えたある日、限界に達します。
直せるが、無料ではない
Podごとに、dnsConfigでndotsを下げられます。ただし、そのPodは、短い名前でクラスターのServiceを見つけられなくなります。そのため、外へ出ていくリクエストが多いPodにだけ設定するのが正解です。クラスター内の通信が多いPodに設定すると、かえって損です。
なぜ往復が4回なのか
Podの/etc/resolv.confは、このような形です。
search labhub-prod.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5
ndots:5は、ドットが5個未満の名前は、完成した名前ではないと見て、先にsearchを付けてみるという意味です。api.example.comはドットが2個なので、ここに引っかかります。
1. api.example.com.labhub-prod.svc.cluster.local → NXDOMAIN
2. api.example.com.svc.cluster.local → NXDOMAIN
3. api.example.com.cluster.local → NXDOMAIN
4. api.example.com → 응답 ← 네 번째에 성공
IPv6が有効なら、各クエリでAとAAAAを一緒に問い合わせるので、実際のクエリは8個です。外部のAPIを頻繁に呼ぶサービスで、CoreDNSの負荷の大半が、これです。
3つの対応と、それぞれの代償
| 方法 | 効果 | 代償 |
|---|---|---|
名前の末尾にドット(example.com.) |
searchを完全にスキップします | コードを直す必要があります |
Podにndots: 1 |
そのPodのすべての名前に適用されます | 短い名前が使えなくなります |
| NodeLocal DNSCache | ノードでキャッシュし、CoreDNSの負荷が急減します | インストールと運用の負担があります |
1行目が最も安く、安全です。ndotsをグローバルに下げない理由は、kubectlで確認できます。短い名前でServiceを呼ぶコードが、すべて壊れます。
# 파드 단위로만, 그 파드가 짧은 이름을 안 쓴다는 것을 확인하고
spec:
dnsConfig:
options:
- name: ndots
value: "1"
DNSの問題を診断するコマンド
# 파드 안에서 — 어느 단계에서 실패하는지 본다
nslookup api.example.com
nslookup api.example.com. # 끝점을 찍으면 되는가?
# search 가 실제로 어떻게 붙는지
dig +search +short api.example.com
# CoreDNS 가 무엇을 받고 있나
kubectl -n kube-system logs -l k8s-app=kube-dns --tail=50 | grep NXDOMAIN
CoreDNSのログが空なら、Corefileにlogプラグインがないということです。調査するときだけ有効にして、無効に戻します。有効にしておくと、ログの量が多くなります。
症状別に見ると、こうなります。断続的な失敗は、たいていconntrackやUDPパケットの損失で、すべて失敗は、CoreDNSかネットワークポリシーで、遅いだけのものは、上のsearchの往復です。
実務で本当に大切なこと
外部のドメインを頻繁に呼ぶ場所には、末尾にドットを打ちます。example.com.のようにピリオドを付けると、完成した名前として扱われ、searchを丸ごとスキップします。コード1文字で往復が4回から1回になり、ndotsには触れないので、短い名前もそのまま使えます。
シグナルは、CoreDNSのクエリ数で探します。無駄なクエリも正常な応答(NXDOMAIN)なので、エラー率は上がりません。クエリ数がサービス呼び出し数より何倍も多ければ、それがこのコストを払っているという意味です。
ndotsは、絶対にグローバルには下げません。短い名前でクラスター内のServiceを呼ぶコードが、すべて壊れます。変えるなら、Pod単位で、そのPodが短い名前を使っていないことを確認してから行います。
トレードオフを知ることが、このテーマのすべてです。次のラボでクエリ数を自分で数えてみれば、この話が数字で手に取れます。