nginx 反向代理 - 如何为多个应用程序提供服务 [英] nginx reverse proxy - how to serve multiple apps

查看:68
本文介绍了nginx 反向代理 - 如何为多个应用程序提供服务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 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).

更新

我认为我的问题是我错误地使用了 locationproxy_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

推荐答案

你说得对,你用错了locationproxy_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屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆