【问题标题】:Expose multiple api uri on the same nginx server block在同一个 nginx 服务器块上公开多个 api uri
【发布时间】:2020-11-08 02:47:46
【问题描述】:

目标

我的目标是在同一个 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://account-service:9000/profile

到目前为止我尝试了什么

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

会议

这是我的 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:
     ...

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

【问题讨论】:

    标签: docker nginx nginx-location nginx-config


    【解决方案1】:

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

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

    但是这个会按预期工作:

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

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

    这是最简单的解决方案:

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

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

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

    请注意,我们需要一个新的resolver 指令,因为我们使用变量来指定后端名称。您可以阅读更多详细信息here,以及从this 答案中获取的 docker 解析器地址。

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

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

    【讨论】:

    • 非常感谢,我现在可以调用我的后端容器了。另外,您评论的最后一部分令人大开眼界(不知道您可以像这样使用地图)
    猜你喜欢
    • 1970-01-01
    • 2020-02-04
    • 2015-03-08
    • 1970-01-01
    • 2019-10-29
    • 1970-01-01
    • 1970-01-01
    • 2022-11-23
    • 2010-12-05
    相关资源
    最近更新 更多