BLOG | NGINX

Realización de pruebas A/B con NGINX y NGINX Plus

NGINX - Parte de F5 - horizontal, negro, tipo RGB
Miniatura de Kevin Jones
Kevin Jones
Publicado el 25 de julio de 2016

Cuando se prueban cambios en una aplicação, hay algunos factores que solo se pueden medir en un entorno de producción en lugar de en un banco de pruebas de desarrollo. Los ejemplos incluyen el efecto de los cambios de la interfaz de usuario en el comportamiento del usuario y el impacto en el rendimiento general. Un método de prueba común es la prueba A/B , también conocida como prueba dividida , en la que una proporción (generalmente pequeña) de usuarios se dirige a la nueva versión de una aplicação mientras la mayoría de los usuarios continúan usando la versión actual.

En esta publicación de blog, exploraremos por qué es importante realizar pruebas A/B al implementar nuevas versiones de su aplicação web y cómo usar NGINX y NGINX Plus para controlar qué versión de una aplicação ven los usuarios. Los ejemplos de configuración ilustran cómo se pueden utilizar las directivas, parámetros y variables de NGINX y NGINX Plus para lograr pruebas A/B precisas y mensurables.

¿Por qué realizar pruebas A/B?

Como mencionamos, las pruebas A/B le permiten medir la diferencia en el rendimiento o la efectividad de la aplicação entre dos versiones. Tal vez su equipo de desarrollo quiera cambiar la disposición visual de los botones en la interfaz de usuario o revisar todo el proceso del carrito de compras, pero quiere comparar la tasa de cierre de transacciones para asegurarse de que el cambio tenga el impacto comercial deseado. Mediante pruebas A/B puedes enviar un porcentaje definido del tráfico a la nueva versión y el resto a la versión anterior y medir la efectividad de ambas versiones de la aplicação.

O quizás su preocupación no es tanto el efecto sobre el comportamiento del usuario sino el impacto en el rendimiento. Digamos que planea implementar un gran conjunto de cambios en su aplicação web y no cree que las pruebas dentro de su entorno de control de calidad realmente capturen el posible efecto en el rendimiento en producción. En este caso, una implementación A/B le permite exponer la nueva versión a un porcentaje pequeño y definido de visitantes para medir el impacto en el rendimiento de los cambios y aumentar gradualmente el porcentaje hasta que finalmente implemente la aplicação modificada para todos los usuarios.

Uso de NGINX y NGINX Plus para pruebas A/B

NGINX y NGINX Plus proporcionan un par de métodos para controlar dónde se envía el tráfico de aplicação web. El primer método está disponible en ambos productos, mientras que el segundo solo está disponible en NGINX Plus.

Ambos métodos eligen el destino de una solicitud en función de los valores de una o más variables NGINX que capturan características del cliente (como su dirección IP) o de la URI de la solicitud (como un argumento con nombre), pero las diferencias entre ellos los hacen adecuados para diferentes casos de uso de pruebas A/B:

  • El método split_clients elige el destino de una solicitud basándose en un hash de los valores de las variables extraídos de la solicitud. El conjunto de todos los valores hash posibles se divide entre las versiones de la aplicação y se puede asignar una proporción diferente del conjunto a cada aplicação. La elección del destino acaba siendo aleatoria.
  • El método de ruta fija le proporciona un control mucho mayor sobre el destino de cada solicitud. La elección de la aplicação se basa en los valores de las variables en sí (no en un hash), por lo que puede establecer explícitamente qué aplicação recibe solicitudes que tienen determinados valores de variable. También puede utilizar expresiones regulares para basar la decisión sólo en partes del valor de una variable y elegir preferentemente una variable sobre otra como base para la decisión.

Usando el método split_clients

En este método, el bloque de configuración split_clients establece una variable para cada solicitud que determina a qué grupo ascendente la directiva proxy_pass envía la solicitud. En la configuración de ejemplo a continuación, el valor de la variable $appversion determina dónde la directiva proxy_pass envía una solicitud. El bloque split_clients utiliza una función hash para establecer dinámicamente el valor de la variable en uno de los dos nombres de grupo ascendentes, version_1a o version_1b .

http { # ...
# versión de la aplicação 1a
versión upstream_1a {
servidor 10.0.0.100:3001;
servidor 10.0.0.101:3001;
}

# versión de la aplicação 1b
versión upstream_1b {
servidor 10.0.0.104:6002;
servidor 10.0.0.105:6002;
}

clientes_divididos "${arg_token}" $appversion {
95% versión_1a;
* versión_1b;
}

servidor {
# ...
escuchar 80;
ubicación / {
encabezado_conjunto_proxy Host $host;
proxy_pass <a href="http://$appversion;">http://$versión de la aplicación;</a>
}
}
}

El primer parámetro de la directiva split_clients es la cadena ( "${arg_token}" en nuestro ejemplo) que se codifica mediante una función MurmurHash2 durante cada solicitud. Los argumentos URI están disponibles para NGINX como variables llamadas $ arg_name : en nuestro ejemplo, la variable $arg_token captura el argumento URI llamado token . Puede utilizar cualquier variable NGINX o cadena de variables como la cadena a codificar. Por ejemplo, puede codificar la dirección IP del cliente (la variable $remote_addr ), el puerto ( $remote_port ) o la combinación de ambos. Se desea utilizar una variable generada antes de que NGINX procese la solicitud. Las variables que contienen información sobre la solicitud inicial del cliente son ideales; por ejemplo, la dirección IP/puerto del cliente, como ya se mencionó, la URI de la solicitud o incluso los encabezados de la solicitud HTTP.

El segundo parámetro de la directiva split_clients ( $appversion en nuestro ejemplo) es la variable que se establece dinámicamente según el hash del primer parámetro. Las declaraciones dentro de las llaves dividen la tabla hash en “grupos”, cada uno de los cuales contiene un porcentaje de los hashes posibles. Puedes crear cualquier cantidad de depósitos y no es necesario que todos tengan el mismo tamaño. Tenga en cuenta que el porcentaje del último segmento siempre está representado por un asterisco (*) en lugar de un número específico, porque la cantidad de hashes podría no ser divisible de manera uniforme en los porcentajes especificados.

En nuestro ejemplo, colocamos el 95 % de los hashes en un depósito asociado con el grupo ascendente version_1a y el resto en un segundo depósito asociado con version_1b . El rango de valores hash posibles es de 0 a 4.294.967.295, por lo que el primer grupo contiene valores de 0 a aproximadamente 4.080.218.930 (el 95 % del total). La variable $appversion se establece en el upstream asociado con el bucket que contiene el hash de la variable $arg_token . Como ejemplo específico, el valor hash 100 000 000 cae en el primer grupo, por lo que $appversion se establece dinámicamente en version_1a .

Prueba de la configuración de split_clients

Para verificar que el bloque de configuración split_clients funciona como se espera, creamos una configuración de prueba que divide las solicitudes entre dos grupos ascendentes en la misma proporción que la anterior (95 % y el resto). Configuramos los servidores virtuales en los grupos para que devuelvan una cadena que indique qué grupo ( versión_1a o versión_1b ) manejó la solicitud (puede ver la configuración de prueba aquí ). Luego usamos curl para generar 20 solicitudes, con el valor del argumento URI token establecido aleatoriamente al ejecutar el comando cat en el archivo urandom . Esto es puramente para fines de demostración y aleatorización. Como pretendíamos, 1 de cada 20 solicitudes (95 %) se atendió desde la versión_1b (para abreviar, mostramos solo 10 de las solicitudes).

# para x en {1..20}; hacer curl 127.0.0.1?token=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1); hecho Token: p3Fsa86HfJDFwZ9ZnYz4QbaZLzcb70Ka Servido desde el sitio versión_1a. 
Token: a7z7afUz7zQijXwsLGfj6mWyPU8sCRIZ Servido desde el sitio versión_1a. 
Simbólico: CjIr6W57NtPzChf4qbYMzD1Estif7jOH Servido desde el sitio versión_1a. ... salida para 10 solicitudes omitidas ...
Token: gXq8cbG3jhl1LuYmICPTfDQT855gaO5y Servido desde el sitio versión_1a. 
Simbólico: VVqGuNN3cRU2slsl5AWOR93aQX8nXOpk Servido desde el sitio versión_1a. 
Token: z7KnewxTX5Sp6wscT0fgmTDohTsQuCmy ¡Servido desde el sitio version_1b!
Token: fWOgH9WRrR0kLJZcIaYchpLhceaQgPD1 Servido desde el sitio versión_1a. 
Token: mTADMXrVnwnr1cd5JE6QCSkgTwfWUnDk Servido desde el sitio versión_1a.
Token: w7AzSNmNJtxWZaH6cXe2PWIFqst2o3oP Servido desde el sitio versión_1a. 
Simbólico: QR7ay0dA39MmVlXtzgOVsj6SBTPi8ECC Servido desde el sitio versión_1a.

Usando el método de ruta pegajosa

En algunos casos, es posible que desee definir una ruta estática tomando decisiones de enrutamiento del cliente basadas en todo o parte del valor de una variable NGINX. Puedes hacer esto con la directiva de ruta fija , que solo está disponible en NGINX Plus. La directiva toma una lista de uno o más parámetros y establece la ruta al valor del primer parámetro no vacío en la lista. Podemos utilizar esta función para clasificar de manera preferencial qué variable de la solicitud controla la elección del destino y así acomodar más de un método de división de tráfico en una sola configuración.

Hay dos enfoques diferentes para utilizar este método.

  • Al utilizar el enfoque del lado del cliente, puede elegir la ruta en función de las variables NGINX que contienen valores que se envían inicialmente de manera directa desde el cliente, como la dirección IP del cliente o los encabezados de solicitud HTTP específicos del navegador, como el User-Agent del cliente.
  • Con el enfoque del lado del servidor o del lado de la aplicação , su aplicação decide a qué grupo de prueba se asigna a un usuario que lo utiliza por primera vez y le envía una cookie o un URI de redireccionamiento que incluye un indicador de ruta que representa al grupo elegido. La próxima vez que el cliente envía una solicitud, presenta la cookie o utiliza la URI de redirección; la directiva de ruta fija extrae el indicador de ruta y reenvía la solicitud al servidor apropiado.

En nuestro ejemplo, utilizamos el enfoque del lado de la aplicação: la directiva de ruta fija en el grupo ascendente establece de manera preferencial la ruta al valor especificado en la cookie proporcionada por un servidor (capturada en $route_from_cookie ). Si el cliente no tiene una cookie, la ruta se establece en un valor de un argumento de la URI de solicitud ( $route_from_uri ). El valor de la ruta determina entonces qué servidor en el grupo ascendente recibe la solicitud: el primer servidor si la ruta es a , el segundo servidor si la ruta es b (los dos servidores corresponden a las dos versiones de nuestra aplicação).

backend ascendente { zona backend 64k;
servidor 10.0.0.200:8098 ruta=a;
servidor 10.0.0.201:8099 ruta=b;

ruta fija $route_from_cookie $route_from_uri;
}

Pero la a o la b están incrustadas en una cadena de caracteres mucho más larga en la cookie o URI. Para extraer solo la letra, configuramos un bloque de configuración de mapa para cada cookie y URI:

mapa $cookie_route $route_from_cookie { ~.(?P<route>w+)$ $route;
}

mapa $arg_route $route_from_uri {
~.(?P<route>w+)$ $route;
}

En el primer bloque del mapa , la variable $cookie_route representa el valor de una cookie llamada ROUTE . La expresión regular en la segunda línea, que utiliza la sintaxis de expresión regular compatible con Perl (PCRE), extrae parte del valor (en este caso, la cadena de caracteres ( w+ ) después del punto) en la ruta del grupo de captura nombrado y la asigna a la variable interna con ese nombre. El valor también se asigna a la variable $route_from_cookie en la primera línea, lo que la hace disponible para pasar a la directiva de ruta fija .

A modo de ejemplo, el primer bloque de mapa extrae el valor “ a ” de esta cookie y lo asigna a $route_from_cookie :

RUTA=iDmDe26BdBDS28FuVJlWc1FH4b13x4fn .a

En el segundo bloque de mapa , la variable $arg_route representa un argumento llamado "ruta" en la URI de la solicitud. Al igual que con la cookie, la expresión regular de la segunda línea extrae parte de la URI; en este caso, es la cadena de caracteres ( w+ ) después del punto en el argumento "ruta" . El valor se lee en el grupo de captura nombrado, se asigna a una variable interna y también se asigna a la variable $route_from_uri .

A modo de ejemplo, el segundo bloque de mapa extrae el valor b de esta URI y lo asigna a $route_from_uri :

www.ejemplo.com/compras/mi-carrito?route=iLbLr35AeAET39GvWK2Xd2GI5c24y5go.b

Aquí está la configuración de muestra completa.

http { # ...
map $cookie_route $route_from_cookie {
~.(?P<route>w+)$ $route;
}

map $arg_route $route_from_uri {
~.(?P<route>w+)$ $route;
}

backend ascendente {
zona backend 64k;
servidor 10.0.0.200:8098 ruta=a;
servidor 10.0.0.201:8099 ruta=b;

ruta fija $route_from_cookie $route_from_uri;
}

servidor {
escuchar 80;

ubicación / {
# ...
contraseña_proxy http://backend;
}
}
}

Probando la configuración de la ruta fija

En cuanto al método split_clients , creamos una configuración de prueba, a la que puedes acceder aquí . Usamos curl para enviar una cookie llamada ROUTE o para incluir un argumento de ruta en la URI. El valor de la cookie o argumento es una cadena aleatoria generada al ejecutar el comando cat en el archivo urandom , con la extensión .a o .b añadida.

Primero, probamos con una cookie que termina en .a :

# curl --cookie "RUTA=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1).a" 127.0.0.1 Valor de la cookie: R0TdyJOJvxBkLC3f75Coa29I1pPySOeQ.a URI de solicitud: / Resultados: Sitio A - Ejecutándose en el puerto 8089

Luego probamos con una cookie que termina en .b .

# curl --cookie "RUTA=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1).b" 127.0.0.1 Valor de la cookie: JhdJZrScTnPBLhqmzK3podNRcJAIc8ST.b URI de solicitud: / Resultados: Sitio B - Ejecutándose en el puerto 8099

Finalmente probamos sin una cookie y en su lugar con un argumento de ruta en la URI de solicitud que termina en .a . La salida confirma que cuando no hay cookies (el campo Valor de cookie está vacío) NGINX Plus utiliza el valor de ruta derivado de la URI.

# curl 127.0.0.1?route=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1).a Valor de la cookie:
URI de la solicitud: /?route=yNp8pHskvukXK6XqbWefhVUcOBjbJv4v.a Resultados: Sitio A - Ejecutándose en el puerto 8089

Registro y análisis de sus resultados

El tipo de prueba que describimos aquí es suficiente para verificar que una configuración distribuye las solicitudes según lo previsto, pero interpretar los resultados de las pruebas A/B reales requiere un registro y un análisis mucho más detallados de cómo se procesan las solicitudes. La forma correcta de realizar el registro y el análisis depende de muchos factores y está más allá del alcance de esta publicación, pero NGINX y NGINX Plus brindan un registro y una monitorización integrados y sofisticados del procesamiento de solicitudes.

Puede utilizar la directiva log_format para definir un formato de registro personalizado que incluya cualquier variable NGINX. Los valores variables registrados en los registros NGINX estarán luego disponibles para su análisis en otro momento. Para obtener detalles sobre el registro personalizado y la supervisión del tiempo de ejecución, consulte la Guía de administración de NGINX Plus .

Algunas últimas cosas a considerar

Al diseñar un experimento o un plan de prueba A/B, asegúrese de que la forma en que distribuye las solicitudes entre las versiones de su aplicação no predetermine los resultados. Si desea un experimento completamente aleatorio, utilizar el método split_clients y aplicar hash a una combinación de múltiples variables proporcionará los mejores resultados. Por ejemplo, generar un token experimental único basado en la combinación de la cookie y el ID del usuario a partir de una solicitud proporciona un patrón de prueba más aleatorio que el hash solo del tipo y la versión del navegador del cliente, porque hay una buena posibilidad de que muchos usuarios tengan el mismo tipo de navegador y versión y, por lo tanto, todos serán dirigidos a la misma versión de la aplicação.

También hay que tener en cuenta que muchos usuarios pertenecen a lo que se denomina el grupo mixto . Acceden a aplicações web desde múltiples dispositivos: quizás tanto sus computadoras del trabajo como las de casa y también un dispositivo móvil como una tableta o un teléfono inteligente. Estos usuarios tienen múltiples direcciones IP de cliente, por lo que si utiliza la dirección IP del cliente como base para elegir la versión de la aplicação , podrían ver ambas versiones de su aplicação, lo que dañaría los resultados experimentales.

Probablemente la solución más sencilla es exigir a los usuarios que inicien sesión para poder rastrear su cookie de sesión, como en nuestro ejemplo del método de ruta fija . De esta forma podrás hacerles seguimiento y enviarles siempre a la misma versión que vieron la primera vez que accedieron a la prueba. Si no puede hacer esto, a veces tiene sentido colocar a los usuarios en grupos que probablemente no cambien durante el curso de la prueba, por ejemplo, usar la geolocalización para mostrar una versión a los usuarios de Los Ángeles y otra a los usuarios de San Francisco.

CONCLUSIÓN

Las pruebas A/B son una forma eficaz de analizar y rastrear cambios en su aplicação y monitorear el rendimiento de la aplicação dividiendo diferentes cantidades de tráfico entre servidores alternativos. Tanto NGINX como NGINX Plus proporcionan directivas, parámetros y variables que se pueden utilizar para construir un marco sólido para las pruebas A/B. También le permiten registrar detalles valiosos sobre cada solicitud. ¡Disfruta probando!

Pruebe NGINX Plus y el método de ruta fija usted mismo: comience hoy su prueba gratuita de 30 días o contáctenos para analizar sus casos de uso .


"Esta publicación de blog puede hacer referencia a productos que ya no están disponibles o que ya no reciben soporte. Para obtener la información más actualizada sobre los productos y soluciones F5 NGINX disponibles, explore nuestra familia de productos NGINX . NGINX ahora es parte de F5. Todos los enlaces anteriores de NGINX.com redirigirán a contenido similar de NGINX en F5.com.