【问题标题】:Why does a variable not work in NGINX `proxy_pass`?为什么变量在 NGINX `proxy_pass` 中不起作用?
【发布时间】:2022-02-25 16:28:00
【问题描述】:

为什么变量在proxy_pass 中不起作用?

这很好用:

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

这根本不起作用:

location /foo/ {
  set $FOO http://127.0.0.1/;
  proxy_pass $FOO;
  add_header x-debug $FOO;
}

我看到了x-header: http://127.0.0.1/,但结果是 404,所以我不知道它在哪里代理,但它与第一​​个示例不同。

Source 其中解释了在proxy_pass中使用变量将防止上游不可用时NGINX启动错误。

更新:问题是上游路径重写。我希望它将/foo/blah 重写到上游/blah 删除/foo 前缀。它适用于静态主机/uri 条目,但不适用于变量。

【问题讨论】:

  • 您是否在您的 nginx 配置中添加了 resolver 指令?
  • 我很困惑,解析器是对 DNS 服务器的引用。这如何适用于解析变量?事实上,我使用 127.0.0.1(而不是 localhost)所以我不需要 DNS...
  • 在沙盒中用 OpenResty 1.17.8.2(基于 nginx 1.17 核心)测试了它,它对我来说就像一个魅力,没有任何额外的 resolver。现在无法使用 vanilla nginx 发短信...
  • @IvanShatsky 问题是路径剥离。我们希望入站 /foo/bar/ 路由到上游 /bar/。
  • 确实,set $FOO http://127.0.0.1; proxy_pass $FOO/; 也没有删除 /foo URI 前缀。但是,您的最终解决方案有点过于复杂,更简单的rewrite ^/foo(/.*) $1 break; proxy_pass http://$FOO; 会更有效地执行相同的操作。

标签: nginx


【解决方案1】:

nginx.conf 中变量的想法是延迟评估。解析 nginx.conf 配置有两个不同的原因:在启动时,以及稍后发出请求时。在第二次解析中,填充了请求相关的变量。

这个解析器不是很聪明,这个行为也没有官方指定。您链接中的技巧是适当的破解。

我在生产中使用的解决方法是proxy-pass http://$FOO。文本字符串http 和变量$FOO 的连接确实发生在发出请求时,这表明变量doproxy-pass 中起作用。为什么它不适用于普通替换,我不知道。而且由于它是一个未记录的黑客,这可能会因版本而异。如果 nginx 本身更聪明一点就好了。

[编辑]

在某些情况下,无法确定要替换的请求 URI 部分: ... 在 proxy_pass 中使用变量时: location /name/ { proxy_pass http://127.0.0.1$request_uri;} 在这种情况下,如果指令中指定了 URI,则按原样传递给服务器,替换原始请求 URI。

手册中详细说明了存在变量时的这种不同行为。您可以尝试使用 rewrite 指令,它会修改要发送的 URI。

【讨论】:

  • 仍然不适用于该解决方法 - 它的代理方式不同,并且不会删除路径前缀 /foo。没有等价物。尝试了 set $FOO 127.0.0.1/; proxy_pass http://$FOO;set $FOO 127.0.0.1; proxy_pass http://$FOO/; 带/不带斜线。
  • 我已经更新了我的答案 - 问题不在于它不起作用,而是它与在代理之前剥离 /foo 路径之前的工作方式不同。
  • @Marc:见手册;这是一个记录在案的怪癖。
  • 是的,这很古怪。我无法将主机名解析为变量,静态输入时它解析得很好。 some.host could not be resolved (3: Host not found) 如果我只输入 http://some.host 就可以了。
  • 添加解析器似乎可以解决问题,因为 NGINX 在看到变量时不会执行正常的 DNS。 resolver 10.43.0.10 valid=10s;(用于 Kubernetes)
【解决方案2】:

在@MSalters 的大力帮助下,最终答案比我想象的要复杂。原因是 NGINX 对变量的工作方式与对静态输入的主机名的工作方式不同——它甚至不使用相同的 DNS 机制。

主要问题是路径处理和前缀剥离与变量的工作方式不同。您必须自己去除路径前缀。在我原来的例子中:

location /foo/ {
  set $FOO 127.0.0.1;
  rewrite /foo/(.*) /$1 break;
  proxy_pass http://$FOO/$1$is_args$args;
}

在我的示例中,我使用 IP 地址,因此不需要解析器。但是,如果您使用主机名,则需要 resolver,因此请在此处添加您的 DNS IP。耸耸肩。

为了全面披露,我们在 Kubernetes 中使用 NGINX,因此它变得更加复杂。特别的兴趣点是:

  1. 添加一个resolver 指令和集群DNS 服务的IP(在我的例子中是10.43.0.10)。这是kube-system 命名空间中kube-dns 服务的ClusterIP。
  2. 即使您的 NGINX 位于同一个命名空间中,也要使用 FQDN,因为 DNS 显然只能解析 FQDN。
location /foo/ {
  set $MYSERVICE myservice.mynamespace.svc.cluster.local;
  rewrite /foo/(.*) /$1 break;
  proxy_pass http://$MYSERVICE/$1$is_args$args;
  resolver 10.43.0.10 valid=10s;
}

注意:由于 NGINX 中的一个 BUG(不幸的是,NGINX 维护者没有承认),如果路径包含空格,在 URL 中使用 $1 将中断。所以/foo%20bar/ 将作为/foo bar/ 向上游传递,然后就中断了。

【讨论】:

    猜你喜欢
    • 2018-09-28
    • 1970-01-01
    • 2015-02-10
    • 2015-07-23
    • 1970-01-01
    • 2020-02-22
    • 1970-01-01
    • 2020-09-04
    相关资源
    最近更新 更多