Reputation: 57
My goal is to setup multiple backend api container exposed on the same nginx server :
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 :
That my backend account container serve this uri :
rewrite
(doesn't help with fastcgi params)upstream
server with proxy_pass
fastcgi_param REQUEST_URI
(without any success)alias
(forbidden access)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.
Upvotes: 0
Views: 2634
Reputation: 15662
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.
Update @ 2022.05.18
Actually there is a way to made the above config works without an additional resolver
directive. To do it we need to define every service as an upstream. This way nginx will need to resolve the used service names to docker containers IP addresses only once at startup, eliminating all the internal DNS traffic and making the whole config somewhat more performant:
upstream account {
server account-service:9000;
}
upstream cart {
server cart-service:9000;
}
upstream order {
server order-service:9000;
}
upstream product {
server product-service:9000;
}
After doing that the last line from the config given above can be changed to
fastcgi_pass $api;
and the resolver
directive can be safely removed from the nginx configuration.
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;
...
}
}
Upvotes: 1