There are different use-cases for this. Mine was that I have a monolith server which I want to split up for a rewrite, but I did not want to rewrite it all by once. Instead I want to do it piece-by-piece so that I can have both servers running at once while doing the transition.
Other reasons might include
- A change of architecture (to microservices or something else)
- Split a monolith up to make it scale better
- You want to catch a hard-to-catch bug that only appears on your actual backend and not on your locally running server
- Any other reason, sky's the limit here
One might wonder how this can be done. When you think about it, it would be obvious that you need to run both servers at once and have both respond to a single client during the transition period. You can do this with a reverse proxy that you can run locally, to which your client will connect to and which will then route the traffic to the correct server by the configuration. Here's how I configured mine.
Nginx as a reverse proxy
Nginx is a nice piece of software that can be pretty easily configured to do this task. Essentially you define the paths that your locally running dev server should respond to, and the rest are catched and sent to the other server.
First you need to define both of your servers as upstream in the nginx config
upstream backend {
server <backend-address>:<backend-port>;
}
upstream new-backend {
server host.docker.internal:5000;
}
The address host.docker.internal
is a special docker feature that will allow you to access your host machine from inside a docker container. You can also use something else here, if you for example are running the new server in another docker container. I'm not, so I'm using this address.
Then you define the URL pattern that is sent to the new backend and the catch-the-rest pattern that will be sent to the old backend like this
location ~* ^/master/api/v1/(races.*) {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host <backend-address>;
proxy_pass http://new-backend/$1$is_args$args;
}
location ~* ^/(.*) {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host <backend-address>;
proxy_pass http://backend/$1$is_args$args;
}
In this example I am catching /master/api/v1/races*
to be sent to the new backend running on my local machine, and then catching the reset to be forwarded to the old backend. There is also additional config to catch query string and add that to the request the reverse proxy does.
Here's my full nginx configuration file for reference
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 16;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$proxy_host" "$upstream_addr"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
upstream backend {
server <backend-address>:80;
}
upstream localbox {
server host.docker.internal:5000;
}
server {
listen 80;
server_name <backend-address>;
location ~* ^/master/api/v1/(races.*) {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host <backend-address>;
proxy_pass http://localbox/$1$is_args$args;
}
location ~* ^/(.*) {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host <backend-address>;
proxy_pass http://backend/$1$is_args$args;
}
}
}
Run it in a docker container
As I said I'm running the reverse proxy in a docker container to make it easier to manage, and easier for others to adopt. Here's my docker-compose.yml
configuration
services:
reverse-proxy:
image: nginx:1.21.1
ports:
- "8000:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./logs:/var/log/nginx # To save logs
networks:
- inter # to access host PC via host.docker.internal
- ext # to access container via localhost from the host PC
command: [nginx-debug, '-g', 'daemon off;']
networks:
inter:
internal: true
ext:
Then I can run this with
docker-compose up -d
And that's it! Now you should be able to configure your client to target this reverse proxy which will then route the traffic to the correct server.