Traffic mirroring by NGINX mirror module

#nginx#mirroring#api

Traffic Mirroring with NGINX Mirror Module

A few months ago, I decided to migrate one of our company services from PHP to Go. This decision came about because we lacked engineers to continue developing or supporting our existing PHP endpoints, while new endpoints were being developed in Go. Although I have substantial experience with Ruby, Go, and JavaScript, I wasn't eager to invest time in enhancing legacy endpoints written years ago.

The Migration Challenge

When dealing with high-traffic services, choosing the right migration strategy is crucial. While researching options, I discovered the NGINX mirror module in the official documentation: https://nginx.org/en/docs/http/ngx_http_mirror_module.html.

This module seemed like an ideal solution for my use case. To create a prototype, I used Docker Compose to organize all services in one place and implemented logging for basic requests.

Implementation Example

Here's a simplified version of my setup:

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"

PHP Configuration (v1/nginx.template)

This is the NGINX configuration file for the PHP service 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;
    }
}

Go Configuration with Mirroring (v2/nginx.template)

This NGINX template serves our new Go API while mirroring traffic to it:

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

How It Works

This example demonstrates a basic approach to achieve a smooth migration. The PHP application serves as the primary application, while the Go app functions as the V2 API.

The key configuration is mirror /mirror;, which instructs NGINX to process each request through the /mirror location and then forward it to the v2 backend. This allows us to verify traffic behavior through console logs.

Once the V2 application is functioning correctly, we can completely remove V1 and update the NGINX template to a new version without the mirroring configuration.

Conclusion

Using NGINX's mirror module provides an elegant solution for gradually migrating between services while ensuring continuity. This approach allows you to validate your new implementation under real traffic conditions before fully committing to the switch.

Thanks for reading!

← Back to all posts

© Copyright 2023 Bitscorp