LoginSignup
0
0

controller-managerはクラスタ内DNSを使わない

Last updated at Posted at 2023-10-17

はじめに

訳あって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のコードから確認します。

controller-manager内のresolv.conf
nameserver 172.18.0.1
nameserver 2001:4860:4860::8888
nameserver 2001:4860:4860::8844
options edns0 trust-ad ndots:0

参考:通常のPodのresolv.conf

通常の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-proxyresolv.confcontroller-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が使用されていてリクエストが失敗しているログもありましたが、そのあとは成功しているようでした。

ちなみに、対処法としてはを他に以下のような方法が考えられました。

  1. configMapでresolve.confを与えてcontroller-managerからマウントする
  2. static podのyamlファイルを編集してdnsPolicy, nameserversで設定する
  3. Goのhttpライブラリで明示的にリゾルバとして10.96.0.10を使うようにする
  4. kindのクラスタ作成時にkubeadmのconfigでPatchを当てるようにする

1, 2の方法は前述のstatic podの制約より、Node上から直接編集する必要がありそうでクライアント(e.g. kubectl, k9s)からの編集ができなさそうなため、やめておきました。
3は、コンテナ全体のhttp, grpcクライアントに対してリゾルバを設定するのが難しそうでした。
4はkind, kubeadmとの親和性は高いですが少し工数が高そうな気がしたので今回は見送りました。

おわりに

static podはクラスタ内のDNSを使わないとのこと、k8s詳しい人でも知らない人が多かったんじゃないかと思います。

これからk8sをビルドする時には気をつけようと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0