エラーのシナリオ
マイクロサービスが正常に通信していたかと思えば、次の瞬間にはログが接続エラーで埋め尽くされることがあります。これは通常、Podが別のサービスにアクセスしようとして壁に突き当たったときに発生します。ハンドシェイクに成功する代わりに、次のようなエラーが表示されます。
dial tcp: lookup my-api-service on 10.96.0.10:53: no such host
このメッセージは2つのことを示しています。第一に、PodがKubernetes DNSサービス(通常は 10.96.0.10 にマッピングされています)を正しく使用しようとしていること。第二に、DNSサーバーが応答しなかったか、my-api-service のアドレスを見つけられなかったことです。
KubernetesでDNSが失敗する理由
kubeadm で構築されたクラスターでは、10.96.0.10 は kube-dns サービスの標準的なClusterIPです。ルックアップが失敗する場合、根本的な原因は通常、以下の4つの領域のいずれかにあります。
- CoreDNSの不安定さ: DNS Podがクラッシュしている、
CrashLoopBackOff状態に陥っている、または過負荷になっている。 - 名前空間のスコープ: フルネームを使用せずに、別の名前空間にあるサービスにアクセスしようとしている。
- ネットワークポリシーによるブロック: ポリシーによって、Podが
kube-system名前空間のポート53へトラフィックを送信することが禁止されている。 - 設定の乖離(ドリフト): コンテナ内の
resolv.confファイルが、正しいDNSサービスのIPを指していない。
クイックフィックス:CoreDNSの再起動
まずは基本から始めましょう。CoreDNSで一時的な不具合やメモリリークが発生している場合、ロールアウト再起動(rollout restart)を行うことで解決することがよくあります。まず、DNS Podの状態を確認してください。
# CoreDNS Podが実行中か確認する(可用性のために少なくとも2つ表示されるはずです)
kubectl get pods -n kube-system -l k8s-app=kube-dns
# 新規ロールアウトをトリガーする
kubectl rollout restart deployment coredns -n kube-system
トラブルシューティングの深掘り
1. 完全修飾ドメイン名(FQDN)を使用する
KubernetesのDNS解決は、<service>.<namespace>.svc.cluster.local という特定の階層に依存しています。アプリケーションが namespace-a にあり、namespace-b の my-api と通信したい場合、http://my-api への単純なリクエストは失敗します。ローカルの検索パスは namespace-a 内しか検索しないためです。
解決策: 名前空間をまたぐ通信には、常に完全な内部アドレスを使用してください。
# 変更前:
my-api-service
# 変更後:
my-api-service.target-namespace.svc.cluster.local
2. 専用のネットワークツールでデバッグする
問題がコードにあるのかクラスターにあるのか不明な場合は、デバッグ用のPodを起動します。特に busybox:1.28 イメージを使用してください。BusyBoxの新しいバージョンには、テスト中に誤検知を引き起こす可能性のあるDNS検索パスの既知のバグが含まれています。
kubectl run dns-test --rm -it --image=busybox:1.28 -- /bin/sh
# クラスター内部の解決をテストする
nslookup kubernetes.default
# 特定のサービスをテストする
nslookup my-api-service.my-namespace.svc.cluster.local
もし nslookup kubernetes.default が失敗する場合、DNSサブシステム全体がダウンしている可能性があります。それが成功しても特定のサービスが失敗する場合は、サービスのラベル(label)とセレクター(selector)を確認してください。
3. Podの resolv.conf を検査する
各Podには、DNSクエリの送信先を規定する /etc/resolv.conf ファイルがあります。このファイルの設定が誤っていると、PodはDNSサーバーを見つけることができません。次のコマンドを実行して、Podからどのように見えているかを確認してください。
kubectl exec <pod-name> -- cat /etc/resolv.conf
正常な設定は以下のようになります。
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
もし nameserver のIPが kube-dns サービスのIPと一致しない場合(kubectl get svc -n kube-system で確認してください)、Kubeletの設定が同期されていない可能性があります。
4. 制限の厳しいネットワークポリシーを確認する
セキュリティが強化されたクラスターでは、トラフィックを制限するために NetworkPolicies がよく使用されます。CalicoやCiliumを使用している場合は、アプリケーションPodが kube-system 名前空間のポート53(UDPおよびTCP)への外部送信(egress)トラフィックを許可されていることを確認してください。これがないと、DNSクエリがPodから送信されることはありません。
# DNSアクセスのためのegressルールの例
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
予防:CIDRの衝突を避ける
クラスターのセットアップ時によくある罠は、IP範囲の重複です。Service CIDR(例:10.96.0.0/12)が企業のVPNやローカルデータセンターのネットワークと重複している場合、DNSパケットがCoreDNSではなくクラスター外にルーティングされてしまう可能性があります。設計段階で IP Subnet Calculator を使用し、10.96.0.0 の範囲が物理インフラから完全に隔離されていることを確認することをお勧めします。
最終確認
修正を適用したら、アプリケーションコンテナからサービスを直接照会して確認します。レスポンスに有効なClusterIPが返されることを確認してください。
kubectl exec <pod-name> -- nslookup my-api-service
# 成功した場合は以下のようになります:
Server: 10.96.0.10
Address: 10.96.0.10:53
Name: my-api-service.default.svc.cluster.local
Address: 10.105.22.45
Address の行が表示されれば、dial tcp エラーは解消され、サービスの通信が再開されます。

