在同一个 nginx 服务器块上公开多个 api uri [英] Expose multiple api uri on the same nginx server block

查看:35
本文介绍了在同一个 nginx 服务器块上公开多个 api uri的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目标

我的目标是在同一个 nginx 服务器上设置多个后端 api 容器:

  • http://localhost:80/api/account ->拨打 http://account-service:9000/
  • http://localhost:80/api/cart ->拨打 http://cart-service:9000/
  • http://localhost:80/api/order ->拨打 http://order-service:9000/
  • http://localhost:80/api/product ->调用 http://product-service:9000/
  • ...

我的后端容器基于 php:7.2-fpm(symfony 托管在每个 apache 容器上)并且它们没有任何名为 api/${NAME_SERVICE}(我不想在我的后端创建一些无用的父路由).

所以,我的要求很简单,例如,当我打电话时,我希望这样做:

  • http://localhost:80/api/account/profile

我的后端帐户容器为这个 uri 提供服务:

  • http://account-service:9000/profile

到目前为止我尝试了什么

  • rewrite(对 fastcgi 参数没有帮助)
  • 使用proxy_pass
  • 设置上游服务器
  • 调整fastcgi_param REQUEST_URI(没有任何成功)
  • alias(禁止访问)

会议

这是我的 nginx.conf :

...服务器 {server_name ~.*;client_max_body_size 50m;地点/{try_files $uri/index.php$is_args$args;}# 如果我想在 http://localhost:80/上提供帐户服务,请工作# 位置 ~ ^/index.php(/|$) {# fastcgi_pass account-service:9000;# fastcgi_buffers 16 16k;# fastcgi_buffer_size 32k;# fastcgi_param SCRIPT_FILENAME/usr/src/app/public/index.php;# 包括 fastcgi_params;# }# 我最后一次尝试使用别名位置 ~* ^/api/account {别名/;索引 index.php;位置 ~ index.php(/|$) {fastcgi_pass 帐户服务:9000;fastcgi_buffers 16 16k;fastcgi_buffer_size 32k;fastcgi_param SCRIPT_FILENAME/usr/src/app/public/index.php;fastcgi_intercept_errors 开启;包括 fastcgi_params;}}}...

docker-compose.yml :

 nginx:图片:nginx:1.15.3-alpine重启:失败卷:- ./build/nginx/default.conf:/etc/nginx/nginx.conf:ro"- ./logs/nginx:/var/log/nginx"端口:- 80:80"取决于:- 帐户服务- 帐户数据库- 购物车服务- 购物车数据库- 订购服务- 订单数据库- 产品服务- 产品数据库帐户服务:环境文件:- 配置/帐户.env构建:应用程序/帐户服务重启:失败暴露:- 9000"卷:- ./apps/account-service:/usr/src/app";取决于:- 帐户数据库购物车服务:...

PS:我知道您可以将 nginx conf 拆分为多个服务器块,这些块侦听不同的端口/主机名,但这不是我在这里想要实现的目标.

解决方案

调整 fastcgi_param REQUEST_URI 是什么意思?如果您尝试将一些自定义值设置为 REQUEST_URI before 您包含 fastcgi_params 文件,fastcgi_params 设置的值将覆盖您的任何调整:

fastcgi_pass service:9000;fastcgi_param REQUEST_URI/some/path;包括 fastcgi_params;# REQUEST_URI 作为真正的请求 URI 传递

但是这个会按预期工作:

fastcgi_pass service:9000;包括 fastcgi_params;fastcgi_param REQUEST_URI/some/path;# REQUEST_URI 作为/some/path"传递;

尝试使用 rewrite 更改此设置将不起作用,因为 REQUEST_URI fastcgi 参数设置为 $request_uri 内部 nginx 变量值fastcgi_params 文件,并且该变量不会被 rewrite 指令规则改变,它是一个 $uri 改变的.

这是应该起作用的最简单的解决方案:

server {...位置 ~ ^/api(/(?:account|cart|order|product)/.*) {# 带/api";部分从 URI 并搜索新的位置块最后重写 ^ $1;}位置/帐户{# 剥离/帐户"从 URI 中提取部分并在当前位置块内继续处理重写 ^/account(.*) $1 break;# 首先包含默认的 fastcgi 参数包括 fastcgi_params;# 我们所有的调整都在它之后fastcgi_buffers 16 16k;fastcgi_buffer_size 32k;# 使用重写的 $uri 变量代替默认的 $request_uri# $uri 变量不包含查询参数,因此如果存在,请手动添加它们fastcgi_param REQUEST_URI $uri$is_args$args;fastcgi_param SCRIPT_FILENAME/usr/src/app/public/index.php;fastcgi_intercept_errors 开启;fastcgi_pass 帐户服务:9000;}位置/购物车{重写 ^/cart(.*) $1 break;...fastcgi_pass 购物车服务:9000;}位置/订单{重写 ^/order(.*) $1 break;...fastcgi_pass 订单服务:9000;}位置/产品{重写 ^/product(.*) $1 break;...fastcgi_pass 产品服务:9000;}}

使用先进的 nginx 技术可以极大地优化此解决方案:

server {...#这是很重要的一点!# 由于我们使用变量作为后端名称,我们需要一个解析器来在运行时解析它# Docker 默认的内部解析器是 127.0.0.11解析器 127.0.0.11;位置 ~ ^/api/(?<api>account|cart|order|product)(?<path>/.*) {包括 fastcgi_params;fastcgi_buffers 16 16k;fastcgi_buffer_size 32k;# 注意我们在这里使用的是 $path 变量而不是 $uri 变量fastcgi_param REQUEST_URI $path$is_args$args;# 假设此路径在所有后端服务中都相同fastcgi_param SCRIPT_FILENAME/usr/src/app/public/index.php;fastcgi_intercept_errors 开启;# 使用 $api 变量作为后端容器名称的一部分fastcgi_pass $api-service:9000;}}

请注意,我们需要一个新的 resolver 指令,因为我们使用一个变量来指定后端名称.您可以在此处阅读更多详细信息,以及解析器来自这个答案的docker地址.>

如果您的脚本路径因不同的 API 后端容器而异,您可以使用额外的 map 块从 $api 变量值中获取脚本路径:

map $api $script {帐号/usr/src/app/public/index.php;购物车/一些/其他/路径;...}服务器 {...位置 ~ ^/api/(?<api>account|cart|order|product)(?<path>/.*) {...fastcgi_param SCRIPT_FILENAME $script;...}}

Goal

My goal is to setup multiple backend api container exposed on the same nginx server :

  • http://localhost:80/api/account -> call http://account-service:9000/
  • http://localhost:80/api/cart -> call http://cart-service:9000/
  • http://localhost:80/api/order -> call http://order-service:9000/
  • http://localhost:80/api/product -> call http://product-service:9000/
  • ...

My backend container are based on php:7.2-fpm (symfony hosted on every apache container) and they don't have any route called api/${NAME_SERVICE} (i don't want to create some useless parent route in my backends).

So, my request is simple, i want that when i call for example :

  • http://localhost:80/api/account/profile

That my backend account container serve this uri :

  • http://account-service:9000/profile

What i tried so far

  • rewrite (doesn't help with fastcgi params)
  • setting up an upstream server with proxy_pass
  • tweaking fastcgi_param REQUEST_URI (without any success)
  • alias (forbidden access)

Conf

Here is my nginx.conf :

...
server {
        server_name ~.*;
        client_max_body_size 50m;

        location / {
            try_files $uri /index.php$is_args$args;
        }
        # work if i want to serve account-service on http://localhost:80/
        # location ~ ^/index.php(/|$) {
        #     fastcgi_pass account-service:9000;
        #     fastcgi_buffers 16 16k;
        #     fastcgi_buffer_size 32k;
        #     fastcgi_param SCRIPT_FILENAME /usr/src/app/public/index.php;
        #     include fastcgi_params;
        # }

        # my last attempt with alias
        location ~* ^/api/account {
            alias /;
            index index.php;
            
            location ~ index.php(/|$) {
                fastcgi_pass account-service:9000;
                fastcgi_buffers 16 16k;
                fastcgi_buffer_size 32k;
                fastcgi_param SCRIPT_FILENAME /usr/src/app/public/index.php;
                fastcgi_intercept_errors on;
                include fastcgi_params;
            }
        }
}
...

docker-compose.yml :

  nginx:
    image: nginx:1.15.3-alpine
    restart: on-failure
    volumes:
      - "./build/nginx/default.conf:/etc/nginx/nginx.conf:ro"
      - "./logs/nginx:/var/log/nginx"
    ports:
      - "80:80"
    depends_on:
      - account-service
      - account-db
      - cart-service
      - cart-db
      - order-service
      - order-db
      - product-service
      - product-db

  account-service:
    env_file:
      - config/account.env
    build: apps/account-service
    restart: on-failure
    expose:
      - "9000"
    volumes:
      - "./apps/account-service:/usr/src/app"
    depends_on:
      - account-db

  cart-service:
     ...

P.S: I known that you can split nginx conf into multiple server blocks that listen on different port/hostname, but that's not what i want to achieve here.

解决方案

What do you mean by tweaking fastcgi_param REQUEST_URI? If you try to set some custom value to REQUEST_URI before you include the fastcgi_params file, value set by fastcgi_params would overwrite any of your tweakings:

fastcgi_pass service:9000;
fastcgi_param REQUEST_URI /some/path;
include fastcgi_params;
# REQUEST_URI passed as the real request URI

However this one would work as expected:

fastcgi_pass service:9000;
include fastcgi_params;
fastcgi_param REQUEST_URI /some/path;
# REQUEST_URI passed as "/some/path"

Trying to change this with rewrite won't work because the REQUEST_URI fastcgi parameter is set to $request_uri internal nginx variable value inside the fastcgi_params file, and that variable doesn't changed by rewrite directive rules, it is an $uri one that does.

Here is the most simple solution that should work:

server {
    ...
    location ~ ^/api(/(?:account|cart|order|product)/.*) {
        # strip "/api" part from the URI and search for the new location block
        rewrite ^ $1 last;
    }

    location /account {
        # strip "/account" part from the URI and continue processing within the current location block
        rewrite ^/account(.*) $1 break;
        # include default fastcgi parameters first
        include fastcgi_params;
        # all our tweakings goes after it
        fastcgi_buffers 16 16k;
        fastcgi_buffer_size 32k;
        # use the rewrited $uri variable instead of the default $request_uri
        # $uri variable does not include query arguments, so add them manually if they exists
        fastcgi_param REQUEST_URI $uri$is_args$args;
        fastcgi_param SCRIPT_FILENAME /usr/src/app/public/index.php;
        fastcgi_intercept_errors on;
        fastcgi_pass account-service:9000;
    }
    location /cart {
        rewrite ^/cart(.*) $1 break;
        ...
        fastcgi_pass cart-service:9000;
    }
    location /order {
        rewrite ^/order(.*) $1 break;
        ...
        fastcgi_pass order-service:9000;
    }
    location /product {
        rewrite ^/product(.*) $1 break;
        ...
        fastcgi_pass product-service:9000;
    }
}

This solution could be greatly optimized using advanced nginx techniques:

server {
    ...
    # This is a very important one!
    # Since we are using variables for backend name, we need a resolver to resolve it at the runtime
    # Docker default internal resolver is 127.0.0.11
    resolver 127.0.0.11;

    location ~ ^/api/(?<api>account|cart|order|product)(?<path>/.*) {
        include fastcgi_params;
        fastcgi_buffers 16 16k;
        fastcgi_buffer_size 32k;
        # note we are using the $path variable here instead of the $uri one
        fastcgi_param REQUEST_URI $path$is_args$args;
        # assuming this path is the same within all the backend services
        fastcgi_param SCRIPT_FILENAME /usr/src/app/public/index.php;
        fastcgi_intercept_errors on;
        # using $api variable as part of backend container name
        fastcgi_pass $api-service:9000;
    }
}

Note that we need a new resolver directive since we are using a variable to specify the backend name. You can read additional details here, and the resolver address for docker taken from this answer.

If your script path vary upon the different API backend containers, you can use an additional map block to get the script path from the $api variable value:

map $api $script {
    account    /usr/src/app/public/index.php;
    cart       /some/other/path;
    ...
}

server {
    ...
    location ~ ^/api/(?<api>account|cart|order|product)(?<path>/.*) {
        ...
        fastcgi_param SCRIPT_FILENAME $script;
        ...
    }
}

这篇关于在同一个 nginx 服务器块上公开多个 api uri的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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