Nginx 缓存 DNS 解析问题
网上看到一些文章,觉得很不错,这里记录一下。
一、缓存问题的解决方案
1 每次更改 DNS 解析都重载 Nginx
重载 Nginx 一定会刷新缓存。
2 使用 Nginx 的 resolver
我们在使用 Nginx 过程中,有时需要根据 Url 传值动态选择 host 进行代理转发,这种模式下,一开始是不会去进行 DNS 解析的,只有请求的时候才会进行 DNS 解析,并且要设置 resolver
指定 DNS 服务器 IP。
这个时候,我们就可以使用 resolver
语法来解决 DNS 缓存的问题,比如说,我在原来的 Nginx 配置里指定 DNS IP,并设置缓存 60 秒。
server {listen 80;server_name www.test.com;resolver 127.0.0.1 valid=60s;resolver_timeout 3s;set $proxy_url "proxy.test.com";location / {proxy_set_header Host proxy.test.com;proxy_pass http://$proxy_url;}
}
但这种方法无法作用于 upstream 里的域名。
3 使用模块nginx-upstream-dynamic-servers
模块地址: nginx-upstream-dynamic-servers
https://github.com/GUI/nginx-upstream-dynamic-servers
该模块在第一次启动的时候会进行一次解析,解析完后,在 DNS 服务器设定的 TTL 过期时间内不会再次更新,过期后会再次发起解析请求
使用方法
http {resolver 8.8.8.8;upstream example {server example.com resolve;}
}
使用这种方法的时候,DNS TTL 时间需要设置短一些。
注意:官方文档值验证了nginx 1.6, 1.7, 1.8, 1.9.,对于高版本是否支持还需验证。
并且官方文档也提及:
Alternatives
- proxy_pass + resolver: If you only need to proxy to 1 domain and don't need the additional capabilities of upstreams, nginx's
proxy_pass
can perform resolving at run-time. - ngx_upstream_jdomain: An nginx module that asyncronously resolves domain names. The primary differences between jdomain and this module is that this module keeps domain names up to date even if no server traffic is being generated (jdomain requires traffic to each upstream in order to keep it up to date). This module also allows nginx to startup if unresolvable domain names are given.
- tengine's dynamic_resolve: If you're using tengine (an nginx fork), there's a new feature (currently unreleased) to support resolving domain names in upstreams at run-time.
- NGINX Plus
4 使用模块 ngx_upstream_jdomain
文档地址: domain_resolve
https://github.com/nicholaschiasson/ngx_upstream_jdomain
ngx_upstream_jdomain
模块是一个依赖 DNS 解析实现的 upstream
负载均衡,该模式下,允许使用域名来写 upstream
后端。该模块默认情况下,会每秒做一次 DNS 解析,使用方法如下
http {resolver 8.8.8.8;resolver_timeout 10s;upstream backend {jdomain www.baidu.com port=80 interval=5; # 每 5 秒解析一次}server {listen 8080;location / {proxy_set_header Host $host;proxy_pass http://backend;}}
}
使用nginx的ngx_upstream_jdomain模块实现k8s容器的负载均衡
最近一直在准备k8s上线事宜,目前已经在测试环境中全面部署并通过压力测试环境检验。离正式上线基本只剩下时间问题。我们目前测试环境中的容器负载均衡大量使用到了nginx,就是借助了ngx_upstream_jdomain模块,从而放弃了k8s官方的ingress。
在这里简单说下k8s的ingress。k8s官方的ingress controller其实也是通过nginx来实现的,但是Ingress本身依赖于service,它通过查询service的映射,来找到service后端的pod的真实ip,并将其挂载到ingress controll的upstream中来实现负载均衡。这本身其实并没有什么问题。但由于在我们的k8s中,鉴于标准service基于kube-proxy的转发效率不高,我们放弃了标准service。转而直接采用了headless service的方式。这种方式的好处是,dns解析会直接解析到每个pod的ip,而不再解析到service ip,也不再需要kube-proxy来实现转发。缺点是负载均衡只能依赖dns轮循,没有灵活的调度策略,但毫无疑问,由于去除了kube-proxy这个中间层,转发效率得到了提高。另外,由于我们直接打通了Pod与物理服务器之间的网络,物理网络中的主机可以与Pod之间通信,如果在中间采用标准的service,service的ip反而无法与物理网络直接实现通信。
这样一来,我们外部的负载均衡就没办法再去依赖Ingress了,我们采用了在外部部署nginx来实现负载均衡的方法,由于upstream里的pod ip会动态变化,所以我们不能直接在upstream里写死pod的ip地址,而只能用service的域名来替代,并让nginx自己去解析这个域名,我们知道headless service的域名由于没有内部的service ip,所以是直接解析到pod ip上的,这样就等于动态拿到了pod ip。在这种情况下,nginx_upstream_jdomain模块就登场了。
使用nginx的ngx_upstream_jdomain模块实现k8s容器的负载均衡 - breezey - 博客园
另外,还需要说明的是,通过这种方式代理到后端的服务,后端服务接收到的$host的值即为www-stress-80,如果后端服务以主机头的方式来接受服务,这显然是不能接受的。所以在代理配置中,必须加上如下配置以将主机头传递给后端服务器:
proxy_set_header Host $host;
5 使用其他版本 Nginx
- Tengine Tengine 的
ngx_http_upstream_dynamic
模块可以提供动态的 DNS 解析 - NGINX Plus Plus版本提供了动态解析的语法
在这里我才用了第三种方案,使用模块nginx-upstream-dynamic-servers。
参考资料
- 运维遇坑记录(3)-Nginx缓存了DNS解析造成后端不通
- 使用 buildx 构建多平台 Docker 镜像
- nginx-upstream-dynamic-servers
- 利用 docker buildx 静态编译 nginx