nginx 反向代理 - 如何为多个应用程序提供服务 [英] nginx reverse proxy - how to serve multiple apps
问题描述
我正在尝试使用 nginx 构建反向代理,以使我的项目中的所有 Is 都可以从单个地址访问.对于单个服务,以下配置可以正常工作
I am trying to build a reverse proxy with nginx to make all Is in my project reachable from single address. For a single service the configuration below works without problem
/etc/nginx/sites-enabled/reverse-proxy.conf
server {
listen 80;
listen [::]:80;
location / {
resolver 127.0.0.1;
allow "x.x.x.x";
deny all;
proxy_pass http://consul:8500;
}
}
因此,当我在浏览器中调用服务器的 ip x.x.x.x
时,我会看到 Consul UI 和显示 x.x.x.x/ui/dc1
的 URL.除此之外,我看到 UI 成功地请求了资产文件.
So when I call server's ip x.x.x.x
in my browser I see the Consul UI and the URL showing x.x.x.x/ui/dc1
. Besides that, I see that the UI did requests for asset files successfully.
我的问题;是否有可能在同一台服务器上托管不同的服务,并仅在不同的位置引用它们?例如,如果我想包含 Vault UI,那么我会考虑做这样的事情:
My question; is it possible two host different services on the same server and just reference to them with different location? For example, if I want to include Vault UI then I would think of doing something like this:
server {
listen 80;
listen [::]:80;
location /consul {
resolver 127.0.0.1;
allow "x.x.x.x";
deny all;
proxy_pass http://consul:8500;
}
location /vault {
resolver 127.0.0.1;
allow "x.x.x.x";
deny all;
proxy_pass http://vault:8200;
}
}
但是我不确定这是否可以通过这种方式完成.我得到的最简单的方法是打开 Consul UI,但未找到所有其他子请求(即加载资产).
However I am not sure if this could be done this way. The farest I got, is to open the Consul UI with all other sub requests not found (i.e. loading assets).
更新
我认为我的问题是我错误地使用了 location
和 proxy_pass
I think my problem is that I am wrongly using location
and proxy_pass
观察第一个配置(正在运行)
observing the first configuration (which is working)
server {
listen 80;
listen [::]:80;
location / {
resolver 127.0.0.1;
allow "x.x.x.x";
deny all;
proxy_pass http://consul:8500;
}
}
如果我看 curl 命令 curl localhost -L -vvvv
If I look at the curl command curl localhost -L -vvvv
* Trying 127.0.0.1:80...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: localhost
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 301 Moved Permanently
< Server: nginx/1.18.0 (Ubuntu)
< Date: Fri, 10 Jul 2020 16:24:38 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 39
< Connection: keep-alive
< Location: /ui/
<
* Ignoring the response-body
* Connection #0 to host localhost left intact
* Issue another request to this URL: 'http://localhost/ui/'
* Found bundle for host localhost: 0x557b754549e0 [serially]
* Can not multiplex, even if we wanted to!
* Re-using existing connection! (#0) with host localhost
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET /ui/ HTTP/1.1
> Host: localhost
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: nginx/1.18.0 (Ubuntu)
< Date: Fri, 10 Jul 2020 16:24:38 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 7806
< Connection: keep-alive
< Accept-Ranges: bytes
< Last-Modified: Fri, 10 Jul 2020 07:37:44 GMT
<
<!DOCTYPE html>
<html lang="en" class="ember-loading">
...
我已经可以看到html了.但是,如果我将 conf 文件更改为:
and I can see the html already. However, if I changed the conf file to this:
server {
listen 80;
listen [::]:80;
location /consul/ {
resolver 127.0.0.1;
allow "x.x.x.x";
deny all;
proxy_pass http://consul:8500;
}
}
然后尝试像 curl localhost/consul -L -vvvv
一样调用它,我得到以下信息:
and then try to call it like curl localhost/consul -L -vvvv
, I get the following:
* Trying 127.0.0.1:80...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET /consul HTTP/1.1
> Host: localhost
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 301 Moved Permanently
< Server: nginx/1.18.0 (Ubuntu)
< Date: Fri, 10 Jul 2020 16:32:35 GMT
< Content-Type: text/html
< Content-Length: 178
< Location: http://localhost/consul/
< Connection: keep-alive
<
* Ignoring the response-body
* Connection #0 to host localhost left intact
* Issue another request to this URL: 'http://localhost/consul/'
* Found bundle for host localhost: 0x55ba7959f9e0 [serially]
* Can not multiplex, even if we wanted to!
* Re-using existing connection! (#0) with host localhost
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET /consul/ HTTP/1.1
> Host: localhost
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 404 Not Found
< Server: nginx/1.18.0 (Ubuntu)
< Date: Fri, 10 Jul 2020 16:32:35 GMT
< Content-Length: 0
< Connection: keep-alive
我很感激关于这个问题的任何想法
I would appreciate any ideas on this issue
推荐答案
你说得对,你用错了location
和proxy_pass
.当您使用
You are right, you are using location
and proxy_pass
a wrong way. When you use the
location /vault {
proxy_pass http://vault:8200;
}
构造,您将 URI 按原样传递给上游,而您很可能希望从中去除 /vault
前缀.要做到这一点,你应该使用这个:
construction, you are passing your URI to the upstream as-is, while most likely you want to strip the /vault
prefix from it. To do it, you should use this one:
location /vault/ {
proxy_pass http://vault:8200/;
}
您可以阅读有关第一个和第二个区别的更多信息 这里.但是,这仍然会阻止资产正确加载.
You can read more about the difference of the first and the second one here. However this still can prevent the assets from loading correctly.
这个问题 - 如何在某个 URI 前缀下代理某个 web 应用 - 在 stackoverflow 上被反复询问.唯一正确的方法是让您的代理应用程序仅通过相对 URL 请求其资产(考虑 assets/script.js
而不是 /assets/script.js
)或使用正确的前缀 (/vault/assets/script.js
).一些编写良好的应用程序能够检测它们是否在这样的 URI 前缀下使用并在生成资产链接时使用它,一些应用程序允许通过某些设置指定它,但有些应用程序根本不适合这种使用.如果不满足这些要求,webapp 将无法工作的原因很明显 - 任何不以 /vault
开头的 URL 都不会匹配您的 location/vault/{ ... }
块,并将通过主 location
块代替.因此,最好的方法是修复您的 web 应用程序,但是如果您确实不能,可以使用多种解决方法.
This question - how to proxy some webapp under some URI prefix - is being asked again and again on stackoverflow. The only right way to do it is to made your proxied app request its assets via relative URLs only (consider assets/script.js
instead of /assets/script.js
) or using the right prefix (/vault/assets/script.js
). Some well-written apps are able to detect if they are used under such an URI prefix and use it when an asset link is being generated, some apps allows to specify it via some settings, but some are not suited for the such use at all. The reason why the webapp won't work without fulfilling these requirements is quite obvious - any URL not started with /vault
won't match your location /vault/ { ... }
block and would be served via main location
block instead. So the best way to do it is to fix your webapp, however several workarounds can be used if you really cannot.
一些 Web 框架已经使用相对 URL 构建了它们的 Web 应用程序,但是在
index.html 的 head 部分使用了
<base href="/">
代码>.例如,React 或 Angular 使用这种方法.如果您的 web 应用程序根目录中有这样一行index.html
,只需将其更改为<base href="/vault/">
.
使用基于 HTTP Referer
标头值的条件路由.这种方法对于加载资产的单页面应用程序非常有效,但是如果 web 应用程序包含多个页面,这种方法将不起作用,正确的上游检测的逻辑会在第一次从一个页面跳转到另一个页面后中断.下面是一个例子:
Using conditional routing based on HTTP Referer
header value. This approach works quite well for a single page applications for loading assets, but if a webapp contains several pages this approach won't work, it's logic for the right upstream detection would break after the first jump from one page to another. Here is an example:
map $http_referer $prefix {
~https?://[^/]+/vault/ vault;
# other webapps prefixes could be defined here
# ...
default base;
}
server {
# listen port, server name and other global definitions here
# ...
location / {
# "unconditional" jump-to-location idea taken from this answer:
# https://serverfault.com/questions/908086/nginx-directly-send-from-location-to-another-named-location/965779#965779
try_files /dev/null @$prefix;
}
location /vault/ {
# proxy request to the vault upstream, remove "/vault" part from the URI
proxy_pass http://vault:8200/;
}
location @vault {
# proxy request to the vault upstream, do not change the URI
proxy_pass http://vault:8200;
}
location @base {
# default "root" location
proxy_pass http://consul:8500;
}
}
使用来自 ngx_http_sub_module
.这是最丑陋的一个,但仍然可以用作最后一个可用选项.这种方法具有明显的性能影响.重写模式应从您的上游响应正文中确定.通常这种类型的配置看起来像
Rewriting the links inside the response body using sub_filter
directive from ngx_http_sub_module
. This is the ugliest one, but still can be used as the last available option. This approach has an obvious perfomance impact. Rewrite patterns should be determined from your upstream response body. Usually that type of configuration looked like
location /vault/ {
proxy_pass http://vault:8200/;
sub_filter_types text/css application/javascript;
sub_filter_once off;
sub_filter 'href="/' 'href="/vault/';
sub_filter "href='/" "href='/vault/";
sub_filter 'src="/' 'src="/vault/';
sub_filter "src='/" "src='/vault/";
sub_filter 'url("/' 'url("/vault/';
sub_filter "url('/" "url('/vault/";
sub_filter "url(/" "url(/vault/";
}
这篇关于nginx 反向代理 - 如何为多个应用程序提供服务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!