kube-dnsとlibc、スタブリゾルバ、またはいつdnsmasqやgo-dnsmasqが必要になるのか

[thockinさんの「Alpineが(`/etc/resolve.conf`の`search`レコードに対応したよ!」](https://twitter.com/thockin/status/704909611279781888
)というツイートを見て、「Alpineで`go-dnsmasq`いらなくなったのかな?」と思ったら、まだ必要なケースはありそうだったのでメモ。

## TL;DR;

* Kubernetesがアプリケーションコンテナ内`/etc/resolv.conf`の1つの`nameserver`を消費するので、それで`nameserver`の上限を超えるようなら`dnsmasq`をDockerイメージにインストールしておきましょう。
  * Alpine Linuxなら3.4から`apk add dnsmasq`できます。`musl libc`を使うAlpine Linuxは上限が低いですが、`glibc`を使う場合でも上限(=3)はあります。
* Kubernetesが3つの`search`レコードを消費するので、それで上限を超えるようなら`go-dnsmasq`をDockerイメージにインストールしておきましょう。
  * `musl libc`を使うAlpine Linuxに関しては
     * 3.3までは`search`に対応してなかったので、上限0です。
     * 3.4からは`search`に対応しています（上限がいくつかは不明)
     * `docker run -it --rm --dns-search google.com alpine:3.3 nslookup www`などでテストできます。alpine:3.3だとエラー、alpine:3.4だと成功します。
  * Alpineに関しては、`go-dnsmasq`を使って複数`search`に対応したDNSサーバをコンテナ内に立てるか(上限なしになる)、もしくは`musl libc`ではなく`glibc`を使ってスタブリゾルバ側で対応(上限6になる)しましょう。
  * `glibc`を既に使っているなら、`go-dnsmasq`を使ってスタブリゾルバではなくDNSサーバ側で対応しましょう(上限6から、上限なしに改善)。
* `nameserver`と`search`の両方が上限に引っかかる場合は`go-dnsmasq`を利用しましょう

## なぜ?

* kube-dnsは各アプリケーションコンテナの`/etc/resolv.conf`における1 `nameserver`レコードと3 `search`レコードを消費する
* ところが、glibcのスタブリゾルバは最大3つの`nameserver`レコードと6つの`search`レコードにまで対応
* なので、システム要件的にkube-dns以外に3つ以上の`nameserver`または4つ以上の`search`レコードが必要な場合、`glibc`のスタブリゾルバで対応できなくなる
* また、Alpine Linuxに使われていることで有名なmusl libcのスタブリゾルバは最大１つ(要出典)の`nameserver`レコードに対応、`search`には非対応。`nameserver`に関してはkube-dns以外を使うともう上限を超えてしまうし、`search`は対応してないのでdigやnslookupがエラーになっていた
* Alpine Linux 3.4から`dnsmasq`がインストールできるようになった
* `dnsmasq`は複数`nameserver`に対応しているので、スタブリゾルバ→`dnsmasq`経由で複数`nameserver`がある場合の名前解決が可能になった
* でも`dnsmasq`は`search`に対応していない(複数の`search`ドメインそれぞれのDNSクエリを投げるのはリゾルバの責務 by `dnsmasq`メンテナ)
* したがって、Alpine 3.4からも、複数の`search`には対応できない
* 対応したいときは、`glibc`をAlpineに入れるか、`dnsmasq`ではなく`go-dnsmasq`を使うとよい

## リゾルバ

* DNSサーバに名前解決を依頼する
* スタブリゾルバ=DNS Client。ライブラリに同梱されている
  * libcのgetaddrinfo(3), getnameinfo(3)
     * libcの実装の一つであるglibcには`search`レコードに対応したスタブリゾルバが同梱されている
     * 同様にlibcの実装の一つであるmusl libcには`search`レコードに対応していないスタブリゾルバが同梱されている
  * golangはlibcのgetaddrinfo等を使う実装と、`/etc/resolv.conf`に書かれたネームサーバに問い合わせるだけの実装を選べる([On Unix systems, the resolver has two options for resolving names ...](https://golang.org/pkg/net/))
     * `net.LookupIP`

参考: [DNS System Components](http://www.zytrax.com/books/dns/ch2/#components)

### スタブリゾルバとは・名前解決の流れ

> 1. An application invokes the stub resolver as function call, from a
   library.
2. The stub resolver, possibly after consulting /etc/hosts, ldap, nis+,
   etc., sends a recursive DNS query to a DNS server via the network.
   If necessary, the stub resolver will retransmit the query, query
   another DNS server, etc., until either it gets an answer or gives up.
3. The DNS server, acting as a full resolver (a caching name server),
   consults its cache and then, if necessary, performs recursion (asks
   other name servers, traversing the DNS name hierarchy) in order to
   find the answer.
4. The caching name server (full resolver) sends an answer back to the
   stub resolver in the form of a DNS message.
5. The stub resolver function returns a data structure to the
   application.
> 
> 引用元: [confused wiht the full resolver and stub resolver](https://lists.isc.org/pipermail/bind-users/2009-November/078203.html)

### glibcとは何なのか

> The GNU Library is not just a single library, but is in fact a collection of libraries that include the C library, math library, threading libraries, DNS stub resolver library, name service libraries. All of these libraries together constitute "the implementation."
> [FAQ - glibc wiki](https://sourceware.org/glibc/wiki/FAQ#How_do_I_install_all_of_the_GNU_C_Library_project_libraries_that_I_just_built.3F)

## dnsmasq

* DNSサーバ/フォワーダ、DHCPサーバ
* dnsmasq自体も`/etc/resolve.conf`を使ってレゾルバ経由で名前解決をする (参考: [HowTo/dnsmasq - Debian Wiki](https://wiki.debian.org/HowTo/dnsmasq))
* [/etc/resolv.conf について - Qiita](http://qiita.com/kasei-san/items/137b7fc86a0eacd60765)
* `/etc/dnsmasq.conf`で`/etc/resolve.conf`以外のリゾルバ設定ファイルを指定できる(参考: [linux - How to overcome libc resolver limitation of maximum 3 nameservers? - Unix & Linux Stack Exchange](http://unix.stackexchange.com/a/28608))
* 複数の`nameserver`に対応できる
* `/etc/resolv.conf`の`search`には対応できない(事例: [dnsmasq with multiple search domains (Page 1) — General Discussion — OpenWrt](https://forum.openwrt.org/viewtopic.php?id=55223))
  * というか、`dnsmasq`のメンテナによると、複数の`search`に対応すべきはレゾルバであってdnsmasqではない

> the search-domain function is implemented by the libc resolver code,
which should generate separate DNS queries for host2.domain1.local,
host2.domain2.local, etc.
> [Comment #4 : Bug #781557 : Bugs : dnsmasq package : Ubuntu](https://bugs.launchpad.net/ubuntu/+source/dnsmasq/+bug/781557/comments/4) in [Bug #781557 “multiple .local search domains not honoured” : Bugs : dnsmasq package : Ubuntu](https://bugs.launchpad.net/ubuntu/+source/dnsmasq/+bug/781557)

## go-dnsmasq

GitHub: https://github.com/janeczku/go-dnsmasq

* 軽量なキャシュありのDNSサーバ/フォワーダ
* dnsmasqの代替として機能
* `dnsmasq`同様、`nameserver`を複数設定できる
* `dnsmasq`と違って`search`を複数設定できる