¿Cómo duplicar el tráfico TCP a uno o varios servidores remotos con fines de evaluación comparativa?


30

Infraestructura: Servidores en Datacenter, OS - Debian Squeeze, Webserver - Apache 2.2.16


Situación:

Nuestros clientes usan el servidor en vivo todos los días, lo que hace que sea imposible probar los ajustes y las mejoras. Por lo tanto, nos gustaría duplicar el tráfico HTTP entrante en el servidor en vivo a uno o varios servidores remotos en tiempo real. El tráfico debe pasar al servidor web local (en este caso, Apache) Y al servidor o servidores remotos. De este modo, podemos ajustar las configuraciones y usar un código diferente / actualizado en los servidores remotos para la evaluación comparativa y la comparación con el servidor actual. Actualmente el servidor web está escuchando aprox. 60 puertos adicionales además de 80 y 443, debido a la estructura del cliente.


Pregunta: ¿Cómo se puede implementar esta duplicación en uno o varios servidores remotos?

Ya hemos intentado:

  • duplicador agnoster: esto requeriría una sesión abierta por puerto que no es aplicable. ( https://github.com/agnoster/duplicator )
  • Proxy kklis: solo reenvía el tráfico al servidor remoto, pero no lo pasa al servidor web lcoal. ( https://github.com/kklis/proxy )
  • iptables: DNAT solo reenvía el tráfico, pero no lo pasa al servidor web local
  • iptables - TEE solo duplica a los servidores en la red local -> los servidores no están ubicados en la misma red debido a la estructura del centro de datos
  • las alternativas sugeridas para la pregunta "duplicar el tráfico de TCP con un proxy" en stackoverflow ( https://stackoverflow.com/questions/7247668/duplicate-tcp-traffic-with-a-proxy ) no tuvieron éxito. Como se mencionó, TEE no funciona con servidores remotos fuera de la red local. teeproxy ya no está disponible ( https://github.com/chrislusf/tee-proxy ) y no pudimos encontrarlo en otro lugar.
  • Hemos agregado una segunda dirección IP (que está en la misma red) y la asignamos a eth0: 0 (la dirección IP principal se asigna a eth0). Sin éxito al combinar esta nueva interfaz IP o virtual eth0: 0 con la función o rutas TEE de iptables.
  • Las alternativas sugeridas para la pregunta "duplicar el tráfico tcp entrante en Debian Squeeze" ( Duplicar el tráfico TCP entrante en Debian Squeeze ) no tuvieron éxito. Las sesiones cat | nc (cat / tmp / prodpipe | nc 127.0.0.1 12345 y cat / tmp / testpipe | nc 127.0.0.1 23456) se interrumpen después de cada solicitud / conexión por parte de un cliente sin previo aviso o registro. Keepalive no cambió esta situación. Los paquetes TCP no se transportaron al sistema remoto.
  • Pruebas adicionales con diferentes opciones de socat (HowTo: http://www.cyberciti.biz/faq/linux-unix-tcp-port-forwarding/ , https://stackoverflow.com/questions/9024227/duplicate-input- unix-stream-to-multiple-tcp-clients-using-socat ) y herramientas similares no tuvieron éxito, porque la función TEE proporcionada solo escribirá en FS.
  • Por supuesto, buscar en Google y buscar este "problema" o configuración tampoco tuvo éxito.

Nos estamos quedando sin opciones aquí.

¿Existe algún método para deshabilitar la aplicación del "servidor en la red local" de la función TEE cuando se usan IPTABLES?

¿Se puede lograr nuestro objetivo mediante el uso diferente de IPTABLES o Rutas?

¿Conoces una herramienta diferente para este propósito que haya sido probada y funcione para estas circunstancias específicas?

¿Existe una fuente diferente para tee-proxy (que se ajuste perfectamente a nuestros requisitos, AFAIK)?


Gracias de antemano por tus respuestas.

----------

editar: 05.02.2014

Aquí está el script de Python, que funcionaría de la manera que lo necesitamos:

import socket  
import SimpleHTTPServer  
import SocketServer  
import sys, thread, time  

def main(config, errorlog):
    sys.stderr = file(errorlog, 'a')

    for settings in parse(config):
        thread.start_new_thread(server, settings)

    while True:
        time.sleep(60)

def parse(configline):
    settings = list()
    for line in file(configline):
        parts = line.split()
        settings.append((int(parts[0]), int(parts[1]), parts[2], int(parts[3])))
    return settings

def server(*settings):
    try:
        dock_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        dock_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        dock_socket.bind(('', settings[0]))

        dock_socket.listen(5)

        while True:
            client_socket = dock_socket.accept()[0]

            client_data = client_socket.recv(1024)
            sys.stderr.write("[OK] Data received:\n %s \n" % client_data)

            print "Forward data to local port: %s" % (settings[1])
            local_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            local_socket.connect(('', settings[1]))
            local_socket.sendall(client_data)

            print "Get response from local socket"
            client_response = local_socket.recv(1024)
            local_socket.close()

            print "Send response to client"
            client_socket.sendall(client_response)
            print "Close client socket"
            client_socket.close()

            print "Forward data to remote server: %s:%s" % (settings[2],settings[3])
            remote_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            remote_socket.connect((settings[2], settings[3]))
            remote_socket.sendall(client_data)       

            print "Close remote sockets"
            remote_socket.close()
    except:
        print "[ERROR]: ",
        print sys.exc_info()
        raise

if __name__ == '__main__':
    main('multiforwarder.config', 'error.log')

Los comentarios para usar esta secuencia de comandos:
Esta secuencia de comandos reenvía una serie de puertos locales configurados a otro servidor de socket local y remoto.

Configuración:
agregue al archivo de configuración port-forward.config líneas con contenido de la siguiente manera:

Los mensajes de error se almacenan en el archivo 'error.log'.

El script divide los parámetros del archivo de configuración:
Divida cada línea de configuración con espacios
0: puerto local para escuchar
1: puerto local para reenviar a
2: dirección IP remota del servidor de destino
3: puerto remoto del servidor de destino
y devuelva la configuración


¿Todo el tráfico es HTTP?
cuello largo

Sí, todo el tráfico es HTTP.
Sise

1
por cierto. teeproxy está disponible aquí: github.com/chrislusf/teeproxy
Tombart

1
Otra posibilidad: github.com/ebowman/splitter Scala / Netty-based.
Rich K.

Respuestas:


11

Es imposible. TCP es un protocolo con estado. La computadora del usuario final participa en cada paso de la conexión y nunca responderá a dos servidores separados que intentan comunicarse con ella. Todo lo que puede hacer es recopilar todas las solicitudes http en el servidor web o algún proxy y reproducirlas. Pero eso no dará una concurrencia exacta o condiciones de tráfico de un servidor en vivo.


Duplicar el TCP es imposible. Estoy de acuerdo con eso. Duplicar el tráfico de la capa 7 no lo es. Puede capturar las solicitudes del cliente y reproducirlas en los otros servidores. La simple solicitud de 1 por reproducción de sesión TCP debería ser bastante fácil. Las conexiones persistentes requerirán algo de reflexión en cuanto a la forma en que cronometra las solicitudes adicionales del cliente.
Evan Anderson

@Kazimieras Aliulis: no es necesario comunicarse con dos servidores separados. el cliente se comunica con el servidor primario = el servidor en vivo. el servidor en vivo está procesando las solicitudes del cliente y está respondiendo al cliente. Además de procesar y responder al cliente, el servidor primario está duplicando las solicitudes al segundo servidor = servidor de prueba. las respuestas del segundo servidor al servidor primario se descartarán / ignorarán en el servidor primario y no se reenviarán al cliente.
Sise

@Evan Anderson: la duplicación en el nivel HTTP también fue nuestra primera idea, pero, por ejemplo, el proxy apache o herramientas o módulos similares no permiten procesar simultáneamente las solicitudes localmente y duplicarlas en un host remoto. si tienes alguna otra idea, por favor consejo! :) preferimos la duplicación a la grabación y reproducción para obtener resultados de comparación instantáneos.
Sise

1
@Sise: puede intentar escribir su propio proxy HTTP, que pasa el tráfico a dos servidores. Debería ser bastante fácil de hacer con Python Twisted Framework twistedmatrix.com .
Kazimieras Aliulis

@Kazimieras Aliulis: ¡definitivamente es una alternativa! Nunca escuché sobre eso. pero comprobarlo muestra que encajaría perfectamente en nuestro propósito. No consideramos Python antes, pero actualmente estamos viendo el marco Twisted y las posibilidades con Python general también. ¡Informaré si tenemos éxito!
Sise


7

Teeproxy podría usarse para replicar el tráfico. El uso es realmente simple:

./teeproxy -l :80 -a localhost:9000 -b localhost:9001
  • a servidor de producción
  • b servidor de prueba

Cuando coloca un HAproxy (con roundrobin) antes de su servidor web, puede redirigir fácilmente el 50% de su tráfico al sitio de prueba:

         /------------------> production
HAproxy /                 ^
        \                /
         \---- teeproxy -.....> test (responses ignored)

4

TCP, al ser un protocolo con estado, no es capaz de enviar copias de los paquetes a otro host, como señala @KazimierasAliulis.

Recoger los paquetes en la capa de terminación TCP y retransmitirlos como una nueva secuencia TCP es razonable. La herramienta duplicadora a la que se vinculó parece su mejor apuesta. Funciona como un proxy TCP, lo que permite que la máquina de estado TCP funcione correctamente. Las respuestas de sus máquinas de prueba simplemente se descartarán. Parece que se ajusta a la factura de lo que quieres exactamente.

No me queda claro por qué descartó la herramienta duplicadora como inaceptable. Tendrá que ejecutar varias instancias de la herramienta, ya que solo escucha en un solo puerto pero, presumiblemente, desea retransmitir cada uno de esos diferentes puertos de escucha a diferentes puertos en el sistema de fondo. De lo contrario, podría usar iptables DNAT para dirigir todos los puertos de escucha a una sola copia de escucha de la herramienta duplicadora.

A menos que las aplicaciones que está probando sean muy simples, espero que tenga problemas con esta metodología de prueba relacionada con el tiempo y el estado interno de la aplicación. Lo que quieres hacer suena engañosamente simple: espero que encuentres muchos casos extremos.


sí, tiene toda la razón, la herramienta duplicadora agnoster se ajusta a nuestros requisitos, excepto en la situación de puertos múltiples. También se completa el descarte de las respuestas de la máquina de prueba. Para lograr nuestro objetivo de simular la situación real / en vivo con la mayor precisión posible, no podemos agrupar todos los puertos del servidor en vivo en un solo puerto en la máquina de prueba. Se utilizan diferentes puertos para dividir los dispositivos del cliente en diferentes clientes. Por lo tanto, tenemos que abrir 60-70 sesiones de esta herramienta duplicadora. Esto no es muy práctico como te puedes imaginar.
Sise

@Sise: las computadoras son buenas para hacer cosas tediosas. Creo que podría escribir un script para analizar sus configuraciones de Apache y escupir las líneas de comando necesarias para ejecutar 60 - 70 instancias de la herramienta duplicadora. No puedo imaginar que la herramienta duplicadora requiera muchos recursos, pero, incluso si lo fuera, podría ejecutar esas 60 - 70 instancias en otra máquina y hacer algunos trucos de red para obtener el tráfico allí. Para mí, al menos, eso parece completamente práctico y una forma bastante directa de manejar esto.
Evan Anderson

1

Estoy tratando de hacer algo similar, sin embargo, si simplemente está tratando de simular la carga en un servidor, miraría algo como un marco de prueba de carga. He usado locust.io en el pasado y funcionó muy bien para simular una carga en un servidor. Eso debería permitirle simular una gran cantidad de clientes y permitirle jugar con la configuración del servidor sin tener que pasar por el doloroso proceso de reenviar el tráfico a otro servidor.


0

En cuanto a "nos gustaría duplicar el tráfico HTTP entrante en el servidor en vivo a uno o varios servidores remotos en tiempo real", hay una forma no mencionada anteriormente, que es configurar un puerto espejo en el conmutador al que está conectado.

En el caso de los switches Cisco Catalyst, esto se llama SPAN (más información aquí ). En un entorno Cisco, incluso puede tener el puerto duplicado en un conmutador diferente.

Pero el propósito de esto es para el análisis de tráfico, por lo que será unidireccional: palabra clave en el texto citado en el primer párrafo anterior: entrante . No creo que ese puerto permita ningún tráfico de retorno, y si lo hiciera, ¿cómo lidiaría con el tráfico de retorno duplicado? Eso probablemente causará estragos en su red.

Entonces ... solo quería agregar una posibilidad a su lista, pero con la advertencia de que realmente será para el tráfico unidireccional. Tal vez pueda poner un concentrador en ese puerto espejo y tener respuestas duplicadas del servidor entregadas por algún simulador de cliente local que recogería las sesiones iniciadas y respondería, pero luego estaría duplicando el tráfico entrante a su servidor duplicado ... probablemente no lo que usted querer.


Hemos pensado en eso, he leído sobre la alternativa de usar SPAN. Pero, debido a que los servidores están ubicados en un centro de datos de un proveedor externo, tenemos posibilidades limitadas cuando se trata de cambios de hardware. Ya he solicitado conectar 2 servidores en un segundo nic directamente. Esta acción combinada con una red local solo para estos 2 servidores me permitiría usar IPTABLES con TEE. Pero para optar por esta alternativa, tendríamos que cambiar las IP externas de los servidores, lo cual es un NoGo porque los dispositivos del cliente están configurados para conectarse a la IP establecida.
Sise

0

También he escrito un proxy inverso / equilibrador de carga para un propósito similar con Node.js (es solo por diversión, no está listo para la producción en este momento).

https://github.com/losnir/ampel

Es muy obstinado y actualmente admite:

  • GET Uso de la selección round-robin (1: 1)
  • POSTUso de división de solicitudes. No existe un concepto de "maestro" y "sombra": el primer backend que responde es el que atenderá la solicitud del cliente, y luego se descartarán todas las demás respuestas.

Si alguien lo encuentra útil, entonces puedo mejorarlo para que sea más flexible.


Node.js es una elección de idioma muy extraña para una aplicación como esta que requerirá un rendimiento muy alto. No estoy seguro de que esto esté listo para la producción.
Michael Hampton

Tienes toda la razón. Esto no estaba destinado a ser altamente eficiente, solo fácil de escribir (para mí). Creo que depende de la carga requerida. Sin embargo, pude alcanzar un poco más de 1,000 rps en una máquina de gama baja (2 núcleos).
losnir

0

mi empresa tenía un requisito similar: clonar un paquete y enviarlo a otro host (ejecutamos simuladores de datos de mercado y necesitábamos una solución temporal que escuchara una alimentación TCP de datos de mercado, ingiriera cada paquete pero también enviara un clon de cada paquete a otro simulador servidor)

este binario funciona muy bien, es una versión de TCP Duplicator pero está escrito en golang en lugar de jscript, por lo que es más rápido y funciona como se anuncia,

https://github.com/mkevac/goduplicator


Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.