はじめに
訳あってcontroller-manager
のコードをいじろうとしていた。
動作確認をするが、なぜか名前解決がうまくいかない...
環境
k8sのソースコード(v1.26.8)をローカルで編集して、kindで動作確認をしています。
$ kind version
kind v0.20.0 go1.20.4 linux/amd64
問題点
controller-manager
内からクラスタ内のService名でアドレスを解決しようとするが、DNS周りでエラーになります。
名前解決したいService
$ kubectl get service -n SVC-NS
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
SVC-NAME ClusterIP 10.96.114.192 <none> 8080/TCP 16h
controller-manager
Podのログ
dial tcp: lookup SVC-NAME.NS-NAME.svc.cluster.local on [2001:4860:4860::8844]:53: read udp [fc00:f853:ccd:e793::2]:43612->[2001:4860:4860::8844]:53: i/o timeout
IPv6で名前解決しようとしてるようだし、クラスタ内DNS(coredns)ではなくGoogleのDNSに問い合わせてるし、よく分からないです。
結論
controller-manager
は通常作成されるPodとは異なるresolv.conf
を持っており、クラスタ内DNS(10.96.0.10
)が利用されていません。
controller-managerコンテナのresolv.conf
を書き換えることでクラスタ内のServiceの名前解決に成功しました。
原因調査
原因を切り分けるために、別のPod内のシェルから名前解決を試してみます。
$ kubectl run tmp-curl --image curlimages/curl -it -- nslookup SVC-NAME.NS-NAME.cluster.local
Server: 10.96.0.10
Address: 10.96.0.10:53
Name: SVC-NAME.NS-NAME.svc.cluster.local
Address: 10.96.114.192 # 解決できている
こちらは普通に解決できています。
もしかしたらNetworkPolicy的なものが関連していて、namespaceによって異なるかもと思いkube-system
ns内でも同様のことをしたが、こちらでも名前解決できた。
ちなみに、通常Podのデバッグをするときはephemeral containerを追加してデバッグすることも多いですが、static podにはephemaral containerを設定できないらしく、以下のようにエラーになります。(公式の説明)
$ kubectl debug -n kube-system kube-controller-manager -it --image=curlimages/curl -- curl SVC-NAME.NS-NAME.svc.cluster.local
Defaulting debug container name to debugger-gkv6z.
The Pod "kube-controller-manager" is invalid: []: Forbidden: static pods do not support ephemeral containers
resolv.conf
を確認しようとしましたがcontroller-manager
コンテナにはシェルが入っていないため、Goのコードから確認します。
nameserver 172.18.0.1
nameserver 2001:4860:4860::8888
nameserver 2001:4860:4860::8844
options edns0 trust-ad ndots:0
参考:通常のPodのresolv.conf
# cat /etc/resolv.conf
search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5
ちなみに、同じstatic podであるkube-proxy
のresolv.conf
もcontroller-manager
と同様でした。
ここで重要なのがcoredns(kube-dns)のServiceである10.96.0.10
がnameserverとして含まれておらず、代わりに先ほどのエラーメッセージ中にあったGoogleのパブリックDNSが指定されていることです。
これで原因が判明しました。controller-manager
コンテナではそもそもクラスタ内DNSが設定されていないようでした。
そもそもstatic podはクラスタ内DNSよりも先に作成されるコンポーネントなので、クラスタ内DNSに依存した処理は想定してないのでしょう。
(ということは、自分が今やろうとしていることって筋が悪いのか?(・∀・;))
ちなみに、172.18.0.1
はDockerが作成するネットワークのデフォルトゲートウェイだそうです。
対処
原因は分かりましたが、なんとかしてクラスタ内DNSを使うようにさせたいです。
kindで作成されるデフォルトのcontroller-manager
はシェルも入っていないため、Goのプログラムからresolv.confを書き換える方法をやってみます。
以下のようなコードをコントローラ内に挿入。
resolvconf := `search default.svc.cluster.local svc.cluster.local cluster.local
nameserver 10.96.0.10
options ndots:5
`
fp, err := os.Create("/etc/resolv.conf")
_, err = fp.Write([]byte(resolvconf))
これでビルドして実行します。
成功しました。(特に出力はないです)
一部変更前のresolv.comf
が使用されていてリクエストが失敗しているログもありましたが、そのあとは成功しているようでした。
ちなみに、対処法としてはを他に以下のような方法が考えられました。
- configMapでresolve.confを与えて
controller-manager
からマウントする - static podのyamlファイルを編集して
dnsPolicy
,nameservers
で設定する - Goのhttpライブラリで明示的にリゾルバとして
10.96.0.10
を使うようにする - kindのクラスタ作成時にkubeadmのconfigでPatchを当てるようにする
1, 2の方法は前述のstatic podの制約より、Node上から直接編集する必要がありそうでクライアント(e.g. kubectl, k9s)からの編集ができなさそうなため、やめておきました。
3は、コンテナ全体のhttp, grpcクライアントに対してリゾルバを設定するのが難しそうでした。
4はkind, kubeadmとの親和性は高いですが少し工数が高そうな気がしたので今回は見送りました。
おわりに
static podはクラスタ内のDNSを使わないとのこと、k8s詳しい人でも知らない人が多かったんじゃないかと思います。
これからk8sをビルドする時には気をつけようと思います。