# K8s 容器内 nginx 带变量的域名解析问题 如果 nginx 的 `proxy_pass` 指令带有变量名的话: ```nginx server { server_name ~^(\w+)\.example\.com$; location / { proxy_pass http://svc-$1; } } ``` 不配置 [`resolver`](http://nginx.org/en/docs/http/ngx_http_core_module.html#resolver) 是不能使用的(虽然可以成功加载配置): ```shell-session $ curl --resolve 'a.example.com:80:127.0.0.1' a.example.com 502 Bad Gateway

502 Bad Gateway


nginx/1.14.2
``` nginx 的错误日志: ```- [error] 615#615: *1 no resolver defined to resolve svc-a ``` 查看当前的 resolve 配置: ```- $ cat /etc/resolv.conf search default.svc.cluster.local svc.cluster.local cluster.local nameserver 10.152.183.10 options ndots:5 ``` 尝试把它添加到 nginx 的配置里面: ```nginx server { server_name ~^(\w+)\.example\.com$; location / { resolver 10.152.183.10; proxy_pass http://svc-$1; } } ``` 结果是仍然不能可用: ```- [error] 3817#3817: *10 svc-a could not be resolved (3: Host not found) ``` nginx 要求配置 resolver 指令,那么可以猜测,它没有使用系统的 resolve.conf 文件,也就是:不走系统那一套来解析域名。 所以现在的差异就在于下面 search 和 ndots 这两行了: ```resolve.conf search default.svc.cluster.local svc.cluster.local cluster.local options ndots:5 ``` [`search`](https://www.man7.org/linux/man-pages/man5/resolv.conf.5.html) 指令的用途:当名字无法解析时,加上这些后缀再尝试解析。 根据 K8s 的 [Service 的 DNS](https://kubernetes.io/zh/docs/concepts/services-networking/dns-pod-service/) 文章所言可以推测: * `cluster.local` 是 K8s 的 cluster_domain * `default.svc.cluster.local` 是名字空间的域名( default 是我的名字空间) 而服务的域名则是:`<服务>.<名字空间域名>`。 所以当我 `curl svc-a` 的时候,实际上返回的是 `svc-a.default.svc.cluster.local` 的结果: ```- $ host svc-a svc-a.default.svc.cluster.local has address 10.152.183.160 $ host svc-a.default svc-a.default.svc.cluster.local has address 10.152.183.160 $ host svc-a.default.svc svc-a.default.svc.cluster.local has address 10.152.183.160 $ host svc-a.default.svc.cluster.local svc-a.default.svc.cluster.local has address 10.152.183.160 ``` 看出来了吗?以上命令省略的恰好是 `search` 指令列出来的部分。 但是,nginx 的 `resolver` 不支持 `search`,所以应该写完整的域名。 ```nginx server { server_name ~^(\w+)\.example\.com$; location / { resolver 10.152.183.10; proxy_pass http://svc-$1.default.svc.cluster.local; } } ``` 以上这样配置就没有问题了。 但是,hardcode 了一个 resolver,不好,得去掉,用服务名的方式找到 K8s DNS 的域名。 我的 K8s 的域名是 kube-dns 服务提供的,名字空间是 `kube-system`,所以完整的 resolver 是: `kube-dns.kube-system.svc.cluster.local`。 ```- $ host kube-dns.kube-system.svc.cluster.local kube-dns.kube-system.svc.cluster.local has address 10.152.183.10 ``` 最终的 nginx 配置: ```nginx server { server_name ~^(\w+)\.example\.com$; location / { resolver kube-dns.kube-system.svc.cluster.local; proxy_pass http://svc-$1.default.svc.cluster.local; } } ``` 其中的变量,应该按你的场景来修改: * `kube-dns` 是我的集群使用的 DNS 服务 * `kube-system` 是我的 DNS 服务所在的名字空间 * `default` 是我的名字空间 * `cluster.local` 是集群域名(cluster_domain) 有至少两种方式可以拿到这个集群域名: * `/etc/resolv.conf` 文件的 `search` 指令。 * 这是 kubelet 的一个启动参数,可以找到你的集群启动参数来获取到。 我这里用的是 microk8s 搭建的测试集群,其中就有一个集群域名参数的值。 ```sh $ ps aux|grep kubelet root 728595 16.0 11.1 3549776 872556 ? Ssl 02:25 9:44 /snap/microk8s/2870/kubelite --scheduler-args-file=/var/snap/microk8s/2870/args/kube-scheduler --controller-manager-args-file=/var/snap/microk8s/2870/args/kube-controller-manager --proxy-args-file=/var/snap/microk8s/2870/args/kube-proxy --kubelet-args-file=/var/snap/microk8s/2870/args/kubelet --apiserver-args-file=/var/snap/microk8s/2870/args/kube-apiserver --kubeconfig-file=/var/snap/microk8s/2870/credentials/client.config --start-control-plane=true $ cat /var/snap/microk8s/2870/args/kubelet ... --cluster-domain=cluster.local ... ``` 由于集群域名不是变化的量,所以每次从文件里面读取并无必要(第一种方式),所以推荐第二种。 关于 `ndots` 的用法详见:[CoreDNS系列1:Kubernetes内部域名解析原理、弊端及优化方式 | 国南之境](https://hansedong.github.io/2018/11/20/9/)。