简体   繁体   English

nginx 反向代理 - 如何为多个应用程序提供服务

[英]nginx reverse proxy - how to serve multiple apps

I am trying to build a reverse proxy with nginx to make all Is in my project reachable from single address.我正在尝试使用 nginx 构建反向代理,以使我的项目中的所有 Is 都可以从单个地址访问。 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;
    }

}

So when I call server's ip xxxx in my browser I see the Consul UI and the URL showing xxxx/ui/dc1 .因此,当我在浏览器中调用服务器的 ip xxxx时,我看到 Consul UI 和 URL 显示xxxx/ui/dc1 Besides that, I see that the UI did requests for asset files successfully.除此之外,我看到 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:例如,如果我想包含 Vault UI,那么我会考虑做这样的事情:

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;
    }

}

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 (ie loading assets).我得到的最多的是打开 Consul UI,但没有找到所有其他子请求(即加载资产)。


UPDATE更新

I think my problem is that I am wrongly using location and proxy_pass我认为我的问题是我错误地使用了locationproxy_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;
    }

}

If I look at the curl command curl localhost -L -vvvv如果我查看 curl 命令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">
...

and I can see the html already.我已经可以看到 html 了。 However, if I changed the conf file to this:但是,如果我将 conf 文件更改为:

server {
        listen 80;
        listen [::]:80;
        location  /consul/ {

        resolver 127.0.0.1;
        allow "x.x.x.x";
        deny   all;
        proxy_pass http://consul:8500;
    }

}

and then try to call it like curl localhost/consul -L -vvvv , I get the following:然后尝试将其称为curl localhost/consul -L -vvvv ,我得到以下信息:

*   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我将不胜感激有关此问题的任何想法

You are right, you are using location and proxy_pass a wrong way.你是对的,你使用了locationproxy_pass错误的方式。 When you use the当您使用

location /vault {
    proxy_pass http://vault:8200;
}

construction, you are passing your URI to the upstream as-is, while most likely you want to strip the /vault prefix from it.构造时,您将 URI 按原样传递给上游,而您很可能希望从中删除/vault前缀。 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.但是,这仍然会阻止资产正确加载。

This question - how to proxy some webapp under some URI prefix - is being asked again and again on stackoverflow.这个问题 - 如何在一些 URI 前缀下代理一些 webapp - 在 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 ).唯一正确的方法是让您的代理应用程序仅通过相对 URL 请求其资产(考虑assets/script.js而不是/assets/script.js )或使用正确的前缀( /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.一些编写良好的应用程序能够检测它们是否在这样的 URI 前缀下使用,并在生成资产链接时使用它,一些应用程序允许通过一些设置来指定它,但有些应用程序根本不适合这种使用. 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.如果不满足这些要求,webapp 将无法运行的原因非常明显 - 任何不以/vault开头的 URL 都不会匹配您的location /vault/ {... }块,而是通过主location块提供服务。 So the best way to do it is to fix your webapp, however several workarounds can be used if you really cannot.所以最好的方法是修复你的 webapp,但是如果你真的不能,可以使用几种解决方法。

  • Some web frameworks already builds their webapps with relative URLs, but uses a <base href="/"> in the head section of index.html .一些 web 框架已经使用相对 URL 构建了他们的 webapps,但是在index.html的头部部分使用了<base href="/"> For example, React or Angular use this approach.例如,React 或 Angular 使用这种方法。 If you have such a line within your webapp root index.html , just change it to <base href="/vault/"> .如果你的 webapp 根index.html中有这样一行,只需将其更改为<base href="/vault/">

  • Using conditional routing based on HTTP Referer header value.使用基于 HTTP Referer header 值的条件路由。 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.这种方法非常适用于加载资产的单页面应用程序,但如果 webapp 包含多个页面,这种方法将不起作用,正确的上游检测逻辑会在第一次从一个页面跳转到另一个页面后中断。 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; } }
  • Rewriting the links inside the response body using sub_filter directive from ngx_http_sub_module .使用来自ngx_http_sub_modulesub_filter指令重写响应正文中的链接。 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/"; }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM