DEV Community

Cover image for Traffic mirroring by NGINX mirror module
Alexandr K
Alexandr K

Posted on

Traffic mirroring by NGINX mirror module

Hi everyone.

A few months ago I've decided to work on migrating one of our company services from Php to Go services because of no engineers to continue to develop or support our endpoints, the new endpoints were coming by developers using Go instead. As for me, I have good enough experience in Ruby/Go/JS but I don't think I wanted to increase in pretty old endpoints written for years ago.

Let's say if you have high load usage of your services what is the best way to make the migration. The idea came to me while I was reading the docs from Nginx website about the mirror module ( https://nginx.org/en/docs/http/ngx_http_mirror_module.html ).

Probably it would be a reasonable way to use it in my case. To make the prototype I used docker-compose where I put all of my services in one place and attached log during the making the basic requests.

The simplified version below:

version: "3"

services:
  entry:
    image: nginx
    volumes:
     - ./v2/nginx.template:/etc/nginx/conf.d/default.conf
    links:
      - v1
      - v2_backend
    ports:
     - "80:80"
    environment:
     - NGINX_PORT=80
    command: /bin/bash -c "exec nginx -g 'daemon off;'"

  v1:
    image: nginx
    volumes:
     - ./v1/nginx.template:/etc/nginx/conf.d/default.conf
    links:
      - v1_backend
    expose:
     - "80"
    environment:
     - NGINX_PORT=80
    command: /bin/bash -c "exec nginx -g 'daemon off;'"

  v1_backend:
    image: example-image1:latest
    expose:
      - "9000"

  v2_backend:
    image: example-image2:latest
    expose:
      - "5000"

v1/nginx.template is Php Nginx configuration file that's using FastCGI.

upstream backend {
  server v1_backend:9000;
}

server {
    listen       80;
    server_name  _;
    root /var/www/html;

    location / {
        try_files $uri $uri/ @rewrite;
    }

    location @rewrite {
      rewrite ^/(.*)$ /index.php?q=$1 last;
    }

    error_page  404              /404.html;
    error_page   500 502 503 504  /50x.html;

    location ~ .php$ {
        try_files  $uri =404;

        fastcgi_pass   backend;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME /var/www/html$fastcgi_script_name;
        include        fastcgi_params;

        fastcgi_max_temp_file_size 0;

        fastcgi_buffers 32 256k;
    }
}

v2/nginx.template is Go Nginx template to serve our new v2 API and attaching mirror to feed our V2 API

upstream v1.backend {
    server v1;
}

upstream v2.backend {
    server v2_backend:5000;
}

server {
    listen 80;
    server_name _;

    location / {
      mirror /mirror;
      mirror_request_body on;

      proxy_pass http://v1.backend;
    }

    location = /mirror {
      internal;

      proxy_pass http://v2.backend$request_uri;
      proxy_set_header X-SERVER-PORT $server_port;
      proxy_set_header X-SERVER-ADDR $server_addr;
      proxy_set_header X-Original-URI $request_uri;
      proxy_set_header HOST $http_host;
      proxy_set_header X-REAL-IP $remote_addr;
    }
}

I think it's the basic example of how to achieve the goal by having the primary APP like in my case it was Php application and Go app as V2 API.

mirror /mirror; - is an instruction to use location /mirror as the next step to process request than upstream it to v2 backend and we are ready to verify the traffic from the console application.

Once V2 application is working fine we can fully remove the V1 and replace Nginx template by the new version without mirror.

Thank you for reading!

Top comments (3)

Collapse
 
cafecraft profile image
cafecraft

Hi Alexandr,

I'm trying to apply the nginx mirror module without success, I did exactly what you did, on the mirror server (v2.backend) I find these errors in the error.log:
failed (2: No such file or directory), which leads me to believe that v2 is unable to mirror the folder "/ usr / share / nginx / html" when it receives requests from "server_name_;"

open () "/usr/share/nginx/html/static/imagem.png" failed (2: No such file or directory)

Collapse
 
oivoodoo profile image
Alexandr K • Edited

Hi @cafecraft .

Are you sure that you have /usr/share/nginx/html/static/imagem.png file in v2.backend?

It's not mirroring the folder, it mirrors the traffic that's coming to v1 and run it for v2 as well. It means if you serve a static file in your case you should have these files for v1 and v2. in my example v1 and v2 are running as separate containers without sharing resources via volumes. if you want to share folders, you can do it by using volume:.

Example:

v1:
...
  volume:
    - ./html:/usr/share/nginx/html
...

v2:
  volume:
    - ./html:/usr/share/nginx/html
Collapse
 
cafecraft profile image
cafecraft • Edited

Thanks for the answer, I think I understand ... I thought Nginx automatically mirrored static content as there was traffic, so I have to mirror the content of my v1 (backend) to v2 (mirror), the problem that static content v1 is in Zope / Plone which stores in ZODB database.

EDIT: I am downloading the content with HTTrack