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

查看:56
本文介绍了在同一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 (在每个apache容器上托管的symfony),并且它们没有任何称为 api/$ {NAME_SERVICE} (我不想在后端创建一些无用的父路由).

所以,我的要求很简单,例如,我想打电话给我时

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

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

  • http://帐户服务:9000/profile

到目前为止我尝试过的事情

  • 重写(对fastcgi参数无济于事)
  • 使用 proxy_pass
  • 设置上游服务器
  • 调整 fastcgi_param REQUEST_URI (没有成功)
  • 别名(禁止访问)

Conf

这是我的nginx.conf:

 <代码> ...服务器 {server_name〜.*;client_max_body_size 50m;地点/{try_files $ uri/index.php$is_args$args;}#如果我想在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;#包括fastcgi_params;#}#我最后一次使用别名的尝试位置〜* ^/api/account {别名/;索引index.php;位置〜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开启;包括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_file:-config/account.env构建:应用程序/帐户服务重新启动:失败暴露:-"9000";数量:-"./apps/account-service:/usr/src/app"取决于:-帐户数据库购物车服务:... 

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

解决方案

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

  fastcgi_pass服务:9000;fastcgi_param REQUEST_URI/some/path;包括fastcgi_params;#REQUEST_URI作为真实请求URI传递 

但是,这将按预期工作:

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

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

这是最有效的解决方案:

 服务器{...位置〜^/api(/(?: account | cart | order | product)/.*){#删除"/api"部分从URI中搜索新的位置块最后重写^ $ 1;}位置/帐户{#删除"/帐户"部分从URI继续在当前位置块内进行处理重写^/account(.*)$ 1休息;#首先包含默认的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 account-service:9000;}位置/购物车{重写^/cart(.*)$ 1休息;...fastcgi_pass cart-service:9000;}位置/顺序{重写^/order(.*)$ 1休息;...fastcgi_pass order-service:9000;}位置/产品{重写^/product(.*)$ 1休息;...fastcgi_pass产品服务:9000;}} 

使用高级nginx技术可以大大优化此解决方案:

 服务器{...#这是非常重要的一个!#由于我们使用变量作为后端名称,因此我们需要一个解析器来在运行时解析它#Docker默认内部解析器为127.0.0.11解析器127.0.0.11;位置〜^/api/(?api帐户|购物车|订单|产品)(?< 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 $ script {帐户/usr/src/app/public/index.php;购物车/some/other/path;...}服务器 {...位置〜^/api/(?api帐户|购物车|订单|产品)(?< 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天全站免登陆