概要
KubernetesのPodから、一部のサイトへアクセスができませんでした。
その問題の原因と解決方法について説明します。
何が起きたか
KubernetesでWebアプリケーションを動かしていました。このWebアプリケーションでは外部のサーバーへアクセスを行います。
その際に、一部のサイトへのアクセスで次のようなエラーが発生しました。
wget: bad address registry.npmjs.org
ping: bad address registry.npmjs.org
原因として色々調査してみたところ、DNSのルックアップに失敗しているようでした。
DNSサーバーに原因があるのかと思い、さらに調査をしたのですが、Kubernetes以外からnslookupコマンドやdigコマンドで名前解決を行いました。しかし、サーバーに問題があるわけではないようで、正常に動作しました。
また、すべてのKubernetesのPodで名前解決に失敗するわけではありませんでした。一部のPodのみ今回の問題が発生していました。
原因
問題の原因として分かったのは、利用しているDNSクライアントのライブラリと、DNSサーバーの組み合わせに問題があるということです。
問題の発生したDNSサーバーは、内部のネットワークに設置していたdnsmasqを利用していました。また、クライアントではalpine linuxを利用したイメージを利用していました。
alpine linuxでは、DNSの名前解決にmusl libcというものが使われています。このmusl libcで名前解決が行われた際に、dnsmasqの応答に問題があり、正常に動作していないということが分かりました。
具体的には、一部の条件でdnsmasqが名前解決に失敗しているのに、正常なレスポンスを返すようです。さらに、musl libcがその間違ったレスポンスをそのまま解釈してしまうので、正常に名前解決が行われないようです。alpine以外のLinuxでよく使われているglibcではこのような問題が起きないような実装になっているようです。そのため、alpineのようなmusl libcを使っている場合に今回の問題が発生しなsy、
詳しくは、次の記事で解説されています。
解決方法
このことから、解決方法としていつくかの方法が考えられます。
- DNSクライアントから、正常に名前解決ができるリクエストを出す
- alpine以外のglibc等を使う、イメージを利用する
- dnsmasq以外のDNSサーバーへ問い合わせを行う
今回私がとった解決方法は、応急処置として正常に名前解決ができるリクエストを出せるように、Podの設定を変更しました。もしも、可能ならば利用するイメージを、DebianやUbuntuベースのものなどに変えるというのも手軽です。
次のようなYAMLファイルを作成して、DNSリクエストのカスタマイズができます。
apiVersion: v1
kind: Pod
metadata:
namespace: default
name: dns-example
spec:
containers:
- name: test
image: nginx
dnsConfig:
options:
- name: ndots
value: "1"
重要な部分は、dnsConfig
です。dnsConfig
のnodts
の値を変更することで、dnsmasqへのリクエストであっても正常にレスポンスが返ってきます。ndots
はFQDNで問い合わせを行う判断に使うもので、1にした場合はドットが1つあった場合にFQDNとして問い合わせを行うという意味になります。
DNSサーバーを変えるという大規模な修正をしなくても、これで問題が解決します。
さいごに
alpineベースのコンテナでのみ問題が発生したので、解決に手間取りました。alpineはイメージサイズが小さいというメリットがあり、よく使っていたのですが、この問題が発生してからは、alpineをなるべく使わないようにしています。alpineやその元になっているbusyboxは、結構クセの強いものだとは知っていたのですが、今回の問題で実感しました。
また、当初は応急処置としてPodコンテナの設定を変更しましたが、現在はdnsmasqではなくBINDを利用してDNSサーバーを構築しています。これであれば、今後Podが増えた際でも毎回設定を変更せずに済みます。