第一坑
我们有一个后台服务A,架构是这样:
1
管理员 ====> nginx ====> AWS的云服务器(服务A)
此时在nginx上做了一个proxy_pass
的转发,如下:
1
2
3
4
5
6
7
8 server {
listen 8081;
server_name xxx.com;
location / {
proxy_pass aws云服务器的IP地址:对应端口;
}
}
一直用的挺好,但是后来这个服务业务量上来了,就扩了一台机器,也部署了A服务,然后在前面加上了一个ELB做最小连接数分配,如图:
1
2
3 AWS的云服务器1(服务A)
管理员 ====> nginx ===> ELB ===>
AWS的云服务器2(服务A)
此时修改了一下proxy_pass
的配置:
1
2
3
4
5
6
7
8 server {
listen 8081;
server_name xxx.com;
location / {
proxy_pass http://aws的ELB域名地址:对应端口;
}
}
当时是好使的,但是第二天就不好使了,nginx调用结果是502 Bad Gateway,当时是分别curl路径然后又抓包,一路十三招查下来也没什么头绪。后来发现原来proxy_pass
后面如果接的是域名地址的话,那么Nginx会在每次启动和重载设置时,使用DNS将域名解析为IP地址缓存下来,并在之后一直使用这个IP!只有通过nginx -s reload
这样的重启才会强制刷新IP,所以当时在nginx上抓包发现跳转的IP其实并不存在。由于在AWS中ELB的内网域名对应的IP并不是一直不变的,这才导致了上面的问题。
新更改的配置如下:
1
2
3
4
5
6
7
8
9
10
11server {
listen 80;
server_name xxx.xxx.net;
resolver DNS服务器地址 valid=30s; #resolver 是 DNS 服务器地址, valid 设定 DNS 刷新频率
set $service_lb aws的ELB域名地址;
location / {
proxy_pass http://$service_lb; #若有路径的必要,就加上$request_uri
}
}
需要特别注意的一点是set语句不能写到 location 里面,否则不会生效。
第二坑
proxy_pass
这个配置对相对路径和绝对路径也有很有讲究,比如:
1
2
3location /proxy/ {
proxy_pass http://10.0.0.1:8080/; #这里以/结尾
}
当访问 http://127.0.0.1/proxy/a/b/c.txt 时,nginx匹配到/proxy/
路径,把请求转发给10.0.0.1:8080服务,实际请求代理服务器的路径为http://10.0.0.1:8080/a/b/c.txt
(不带location目录)。
如果是这么写的:
1
2
3location /proxy/ {
proxy_pass http://10.0.0.1:8080; #这里没有以/结尾
}
当访问 http://127.0.0.1/proxy/a/b/c.txt 时,nginx匹配到/proxy/
路径,把请求转发给10.0.0.1:8080服务,实际请求代理服务器的路径为http://10.0.0.1:8080/proxy/a/b/c.txt
(带location目录),此时nginx会把匹配的proxy也代理给代理服务器。
proxy_pass
后面接多一级路径的情况跟上面一致。
补充一个$host 400错误的故障
400这个状态码是Bad Request
,基本上就是请求header或者是cookie
有问题,而请求header的问题要么是头过大要么是头没有。在HTTP/1.0
里不支持Host请求头的,而在HTTP/1.1
中,Host请求头部必须存在,否则会返回400 Bad Request
。如果请求的URI不包含所请求服务的主机名,则必须为Host头字段指定一个空值。
nginx如果配置了$http_host
变量做反向代理时,后端真实的服务器是需要知道请求的host头,而$http_host
的值是’’,所以会触发400这个错误。这个情况就需要用$hosts
来替代$http_host
。
也就是说:proxy_set_header Host $host
这一行的作用是把原http请求的Header中的Host字段也放到转发的请求里。如果不加这一行的话,nginx转发的请求header里就不会有Host字段,而服务器是靠这个Host值来区分你请求的是哪个域名的资源的。
还有一种400的情况:如果是GET请求没问题,而POST请求返回400错误,那么可能是websocket配置成了全局单独配置websocket的地址即可。
参考资料
https://www.jibing57.com/2018/11/27/nginx-with-dynamic-upstreams-to-ELB/
http://www.syyong.com/net/Using-nginx-s-proxy_pass-function-to-do-domain-name-forwarding-causes-an-accident.html
https://blog.nswebfrog.com/2018/09/09/nginx-proxypass-dns/
https://www.jianshu.com/p/b113bd14f584
https://www.cnblogs.com/woshimrf/p/nginx-proxy-rewrite-url.html