Nginx 的动态 upstreams

本文探讨了如何配置Nginx与AWS Elastic Load Balancer (ELB)协同工作的问题,尤其是在DNS记录变更时保持服务高可用性的挑战及解决方案。

原文地址: http://www.oschina.net/translate/nginx-with-dynamic-upstreams

我最近在工作中做一个设置,我有一个面向用户的 Nginx 服务,它将访问转发到运行在 AWS Elastic Load Balancer(如你所知. ELB)上的一个服务。这本身似乎不是一个困难的任务,你只需要找到 ELB 的主机名,将 ngin x指向它,这样不就搞定了,对吧? 

location / {
    proxy_pass http://service-1234567890.us-east-1.elb.amazonaws.com;
}

测试没有问题,再正确设置一下防火墙/安全组配置,它就应该可以很好的工作了。几个小时之后,你可能会发现,服务不再工作了,尽管没有做任何改变。直接访问 ELB 端点是可以工作的,但访问 Nginx 却总是超时提醒。

ELB端启蒙

为了弄清楚为什么服务突然中止,需要先了解一下 ELB 是如何工作的:

当你创建一个弹性负载均衡(Elastic Load Balancer),你将会得到 DNS 的返回记录,AWS 会告诉你所有在使用的访问服务。DNS 记录是一个轮询 DNS(round robin DNS)记录,它指向两个或更多的 IP 地址——这取决于你有多少可用的区域。DNS 记录被设置成 60 秒的存活时间(time to live),这意味几乎不会有记录缓存。

短 TTL 可以让 AWS 快速改变机器的运行负载,在不中断服务的情况下,不会有任何复杂的虚拟 IP 问题。这也是他们特别告诉你不要查找主机名和发送流量到其中某个 IP 地址的原因,那样的话,你的服务可能会在未来某个未定义的时间,IP 地址可能会停止为负载均衡工作。

回到 Nginx

问题在于,对于 Nginx 来说,当它读取到一个配置时,它就会立刻向 DNS 请求主机名,然后使用其结果,直到下次重新加载配置。在这段时间到来之前,ELB 可能改变 IP 地址,让你的 Nginx 把请求转发到一些不为你服务的地址。

Nginx Plus

解决这个问题的方式是为 Nginx Plus 付费,它添加 resolve 标记对在 upstream 分组上的服务器进行指示。那就是让 Nginx 骄傲的 DNS 对 TTL 的记录,偶尔按顺序重新处理记录,并取得服务器使用的更新列表。

为这个功能花费每年每服务器 $1.500,看起来花费很多。当然这是你希望得到 Nginx Plus 带来的其他功能,如果你不需要它们,这将会是一个昂贵的升级。

 

免费的选择

一个更加实惠的选择是写这样一个配置:

1
2
3
4
5
resolver  172.16 . 0.23 ;
set $upstream_endpoint http: //service-1234567890.us-east-1.elb.amazonaws.com;
location / {
     proxy_pass $upstream_endpoint;
}

它将会生效并且 Nginx 会遵循记录 DNS 记录的 TTL,万一一个请求进来,会重新解释它而且缓存的记录会过期。为什么会这样?

答案可以在 proxy_pass 指令文档结尾找到,它声明了:

服务器名,端口以及传递的URI也可以使用变量被指定:

1
proxy_pass http: // $host$uri;

甚至像这样:

1
proxy_pass $request;

在这个案例中,服务器名会在所描述的server groups 中被查找,如果没找到,会使用 resolver 来决定.

当我们给 proxy_pass 提供一个变量的时候,我们基本上是利用其改变行为,但这样确实需要我们在配置中指定一个 DNS resolver。例子里边用到的 DNS resolver应该能够在 AWS 上面跑在默认 VPC或者 EC2 中的所有服务器工作(适用)。你也可以随时查看 /etc/resolv.conf 找出哪些 AWS 为你的服务器提供并使用了哪些 DNS 服务器。

关于转发 URI 的 Caveat(警告)

如果你在 Nginx 中设置的 Location 不只是 /,那么你需要注意到当给定一个变量作为参数时,proxy_pass 细微的改变行为。

先说重要的,快速概括 proxy_pass 如何在正常在操作中工作:

正常的表现行为

设想我们有一个 Nginx 配置包括这些:

location /foo/ {
    proxy_pass http://127.0.0.1:8080;
}

当我们发送一个 /foo/bar/baz 的请求到这个站点,Nginx 会转发请求到 http://127.0.0.1:8000/foo/bar/baz。

location /foo/ {
    # Note the trailing slash       ↓
    proxy_pass http://127.0.0.1:8080/;
}

Nginx 会在 Location 记录里边去掉部分指定的 URI,然后把剩下的部分传给 upstream 服务器。所以请求 /foo/bar/baz 会被转发到 http://127.0.0.1:8080/bar/baz。

改变行为

当我们使用一个变量作为 proxy_pass 的参数的时候,上面带有尾部斜杠的行为会改变。例如我们有这样的配置。

1
2
3
4
5
resolver 172.16.0.23;
set  $upstream_endpoint http: //service-1234567890 .us-east-1.elb.amazonaws.com/;
location  /foo/  {
     proxy_pass $upstream_endpoint;
}

当我们向那个配置发送请求 /foo/bar/baz,转发请求将不会去到/并且不是预想中的 /bar/baz。

为此解决方案就是从 upstream 的 endpoint 去掉尾部斜杠,然后像这样手动重写:

1
2
3
4
5
6
resolver 172.16.0.23;
set  $upstream_endpoint http: //service-1234567890 .us-east-1.elb.amazonaws.com;
location  /foo/  {
     rewrite ^ /foo/ (.*) /$1  break ;
     proxy_pass $upstream_endpoint;
}

然后当你发送请求 /foo/bar/baz,upstream 会接受到我们想要的请求 /bar/baz。

结束语

要知道这不单单只适用于设置用 elb 做 upstream 服务器,它适用于配置所有在 nginx 做 upstream 服务器的修改 DNS 配置的情况。

希望这对你有用,如果你有任何建议或者只是想单纯联系我,用 twitter 联系吧 Tenzer

### 关于Nginx出现'no live upstreams while connecting'错误的解决方案 当遇到`no live upstreams while connecting to upstream`错误时,通常意味着Nginx无法连接到配置中的上游服务器。这可能是由多种原因引起的,包括但不限于网络问题、服务未启动或健康检查失败。 #### 1. 检查上游服务器状态 确认所有上游服务器是否正常运行并能够响应请求。可以通过命令行工具如`curl`或浏览器直接测试上游URL来验证其可用性[^1]。 ```bash curl http://upstream-server-ip:port/path ``` 如果发现某些节点不可达,则需进一步排查这些节点的服务状况及其所在主机的状态。 #### 2. 配置文件审查 仔细检查Nginx配置文件中定义的upstream部分是否有误。确保地址正确无拼写错误,并且端口号匹配实际监听情况。另外还需注意负载均衡算法设置是否合理以及最大并发数等参数调整得当[^3]。 对于Kubernetes环境下的API Server访问问题特别需要注意的是VIP(虚拟IP)配置与Keepalived的工作机制之间的协调一致。因为这里提到的情况涉及到通过保持活动(VRRP)协议管理浮动IP用于高可用部署场景下对多个后端实例提供冗余支持的功能实现方式之一——即利用Keepalived创建了一个指向apiserver集群成员机器上的6443端口(默认HTTPS通信端口) 的 VIP (10.4.7.10),而该 IP 地址被映射到了 Nginx 反向代理所使用的自定义端口之上 (例如案例中的7443)。 因此,在这种特定情况下还需要额外关注两点: - **VRRP Master选举过程**:保证只有当前处于Master角色的节点才会接收来自客户端的数据包;其他Slave节点不应该处理任何流量直到它们成为新的领导者为止。 - **防火墙规则和安全组策略**:允许必要的TCP/IP层通讯路径畅通无阻地穿越整个基础设施栈直至目标应用进程层面。 #### 3. 增加超时时间和重试次数 适当增加proxy_read_timeout、proxy_connect_timeout 和 proxy_send_timeout 参数值可以给下游更多时间完成操作而不至于过早中断会话。同时也可以考虑提高尝试建立连接的最大次数(proxy_next_upstream_tries), 这样即使第一次握手失败也还有机会切换至备用资源继续尝试[^2]。 ```nginx location / { proxy_pass http://backend; # 设置更长的时间限制以适应较慢的应用程序响应速度 proxy_read_timeout 300s; proxy_connect_timeout 60s; proxy_send_timeout 300s; # 当前端收到HTTP 5XX错误码时自动转向下一个有效的UpStream成员重新发起请求最多三次 proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; proxy_next_upstream_tries 3; } ``` #### 4. 启用健康监测功能 启用主动式的Health Check可以帮助及时识别出不健康的后端并将之暂时移除出轮询池之外等待恢复后再加入进来参与分发任务。这对于维持整体系统的稳定性和可靠性非常有帮助。 ```nginx upstream backend { server 192.168.1.1 max_fails=3 fail_timeout=30s; server 192.168.1.2 max_fails=3 fail_timeout=30s; health_check uri=/healthz match=named_matcher interval=5s falls=3 passes=2; } server { ... } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值