问题
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 withproxy_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.
回答1:
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;
...
}
}
来源:https://stackoverflow.com/questions/62968482/expose-multiple-api-uri-on-the-same-nginx-server-block