大多数L7用的是Nginx,而一般Nginx upstream 配置是静态的不会变动,这其中有两种常用情况
- 如AWS之类的云服务厂商提供ELB域名,域名解析的IP随时会变动
- 对于服务运行在容器里的,例如Docker、LXC、LXD等,一个常见的现象就是容器ip的变更,例如扩容缩容,迁移等等。
Nginx使用upstream时,
- 里面如果填写的是域名,那么Nginx会在请求DNS后把对应的IP信息缓存起来,后续的请求就一直用缓存的IP。直到下次reload的时候才会再次查询domain,不想经常reload,需要通过set设置一个变量变量实现
- upstream直接配置的容器ip,扩容缩容,迁移ip变更,Nginx upstream不能随之变更,官方没有Dynamic Upstream,一般有多种解决方案:
- 编写lua模块动态处理
- upstream在文件中定义,该文件随时更新,而每次更新触发Nginx reload,且需要通过set设置一个变量变量实现。至于文件如果变动可通过多种语言,多种方法实现
所以不管upstream是域名还是动态IP,通过set设置一个变量是经常的行为,但恰恰是因为引入了这个变量,导致出现了一个proxy_pass转发规则与平时不一样的深坑。
问题描述
正常情况(静态upstream)下
想象一下,我们有一个包含以下内容的Nginx配置:
location /foo/ {
proxy_pass http://127.0.0.1:8080;
}
当你向站点请求 /foo/bar/baz时,Nginx会将请求转发到
http://127.0.0.1:8080/foo/bar/baz
如果配置看起来像这样:
location /foo/ {
# 注意尾部的斜线 ↓
proxy_pass http://127.0.0.1:8080/;
}
然后,Nginx将剥离在location指令中指定的URI部分,并将其余部分传递给上游服务器。因此,对 /foo/bar/baz 的请求将转发到:
引入变量的情况
当我们使用变量作为proxy_pass的参数时,上面显示的带有尾部的斜杠行为会发生改变。假设我们有以下配置:
set $upstream_endpoint http://service-xxxxxxxx.elb.amazonaws.com/;
location /foo/ {
proxy_pass $upstream_endpoint;
}
当请求 /foo/bar/baz时,转发的给后端请求将变为 / 而不是预期的 /bar/baz。于是变为
坑就在此
解决办法
解决方法是upstream删除尾部斜杠 / ,然后像这样手动 rewrite:
set $upstream_endpoint http://service-xxxxxxxx.elb.amazonaws.com/;
location /foo/ {
rewrite ^/foo/(.*) /$1 break;
proxy_pass $upstream_endpoint;
}
然后,当你向/foo/bar/baz发出请求时,upstream将像我们想要的那样获得对/bar/baz的请求。
如果不知道这个坑的话,是无论如何也想不到的我真实的经历过,后端研发要求上线新接口,转发至新的后端,我按照一般的思路配置好,后端研发说无论怎样请求后端都会转发到根 / 下
反反复复来去几次,甚至最后额外另搞了一个域名,另开了一个Nginx作为后端测试,把截图甩了过去。。。说你后端写的就是有问题呀
做测试时想的是把一切都简化,控制变量法抛离一切的不可能,最后剩下的那个就是有问题的。没想到控制变量法,出问题的就是变量(双关)。。。
真的!非常感谢,解决了我的问题~