Hola a todos!
Hoy queremos hablar sobre cómo el equipo del servicio de reservas de hoteles
Ostrovok.ru resolvió el problema del crecimiento del microservicio, cuya tarea es intercambiar información con nuestros proveedores. Sobre su experiencia le dice que no
muere , Líder del equipo DevOps en Ostrovok.ru.
Al principio, el microservicio era pequeño y realizaba las siguientes funciones:
- aceptar una solicitud de un servicio local;
- hacer una solicitud a un socio;
- normalizar la respuesta;
- devolver el resultado al servicio de consulta.
Sin embargo, con el tiempo, el servicio creció junto con el número de socios y las solicitudes que les hicieron.
A medida que el servicio creció, comenzaron a surgir varios tipos de problemas. Diferentes proveedores presentan sus propias reglas: alguien limita el número máximo de conexiones, alguien restringe a los clientes a listas blancas.
Como resultado, tuvimos que resolver los siguientes problemas:
- es deseable tener varias direcciones IP externas fijas para que pueda proporcionarlas a los socios para agregarlas a las listas blancas,
- tener un único grupo de conexiones con todos los proveedores para que al escalar nuestro microservicio el número de conexiones sea mínimo,
- finalice SSL y manténgase
keepalive
en un solo lugar, reduciendo así la carga en los propios socios.
No pensaron durante mucho tiempo e inmediatamente se preguntaron qué elegir: Nginx o Haproxy.
Al principio, el péndulo giró hacia Nginx, ya que resolví la mayoría de los problemas asociados con HTTP / HTTPS con su ayuda y siempre estuve satisfecho con el resultado.
El esquema era simple: se realizó una solicitud a nuestro nuevo Servidor Proxy en Nginx con un dominio de la forma
<partner_tag>.domain.local
, en Nginx había un
map
, donde
<partner_tag>
correspondía a la dirección del socio. Se tomó una dirección del
map
y se realizó
proxy_pass
a esta dirección.
Aquí hay un
map
ejemplo con el que analizamos el dominio y seleccionamos el flujo ascendente de la lista:
Y así es como se ve "
snippet.d/upstreams_map
":
“one” “http://one.domain.net”; “two” “https://two.domain.org”;
Aquí tenemos el
server{}
:
server { listen 80; location / { proxy_http_version 1.1; proxy_pass $upstream_address$request_uri; proxy_set_header Host $upstream_host; proxy_set_header X-Forwarded-For ""; proxy_set_header X-Forwarded-Port ""; proxy_set_header X-Forwarded-Proto ""; } }
Todo es genial, todo funciona. Es posible finalizar este artículo, si no es por un matiz.
Cuando se usa proxy_pass, la solicitud va directamente a la dirección deseada, como regla, usando el protocolo HTTP / 1.0 sin
keepalive
y se cierra inmediatamente después de que se completa la respuesta. Incluso si
proxy_http_version 1.1
, nada cambiará sin upstream (
proxy_http_version ).
Que hacer El primer pensamiento es lograr que todos los proveedores ingresen en el flujo ascendente, donde el servidor será la dirección del proveedor que necesitamos, y en el
map
mantenga
"tag" "upstream_name"
.
Agregue otro
map
para analizar el esquema:
Y cree
upstreams
con nombres de etiquetas:
upstream one { keepalive 64; server one.domain.com; } upstream two { keepalive 64; server two.domain.net; }
El servidor en sí está ligeramente modificado para tener en cuenta el esquema y usar el nombre de la cadena en lugar de la dirección:
server { listen 80; location / { proxy_http_version 1.1; proxy_pass $upstream_scheme$upstream_prefix$request_uri; proxy_set_header Host $upstream_host; proxy_set_header X-Forwarded-For ""; proxy_set_header X-Forwarded-Port ""; proxy_set_header X-Forwarded-Proto ""; } }
Genial La solución funciona, agregue la directiva
keepalive
a cada
proxy_http_version 1.1
ascendente, establezca
proxy_http_version 1.1
, ahora tenemos un grupo de conexiones y todo funciona como debería.
Esta vez, definitivamente puedes terminar el artículo e ir a tomar té. O no?
Después de todo, mientras bebemos té, uno de los proveedores puede cambiar la dirección IP o el grupo de direcciones bajo el mismo dominio (hola, Amazon), por lo que uno de los proveedores puede caerse a la altura de nuestra fiesta de té.
Bueno, que hacer? Nginx tiene un matiz interesante: durante la recarga, puede sobriar los servidores dentro del
upstream
a nuevas direcciones y ponerles tráfico. En general, también una solución. Agregue
cron reload nginx
cada 5 minutos y continúe tomando té.
Pero aún así me pareció una decisión más o menos, así que comencé a mirar con recelo hacia Haproxy.
Haproxy tiene la capacidad de especificar
dns resolvers
y configurar
dns cache
. Por lo tanto, Haproxy actualizará el
dns cache
si las entradas han expirado, y reemplazará las direcciones de flujo ascendente si han cambiado.
Genial Ahora depende de la configuración.
Aquí hay un breve ejemplo de configuración para Haproxy:
frontend http bind *:80 http-request del-header X-Forwarded-For http-request del-header X-Forwarded-Port http-request del-header X-Forwarded-Proto capture request header Host len 32 capture request header Referer len 128 capture request header User-Agent len 128 acl host_present hdr(host) -m len gt 0 use_backend %[req.hdr(host),lower,field(1,'.')] if host_present default_backend default resolvers dns hold valid 1s timeout retry 100ms nameserver dns1 1.1.1.1:53 backend one http-request set-header Host one.domain.com server one--one.domain.com one.domain.com:80 resolvers dns check backend two http-request set-header Host two.domain.net server two--two.domain.net two.domain.net:443 resolvers dns check ssl verify none check-sni two.domain.net sni str(two.domain.net)
Todo parece funcionar como debería esta vez. Lo único que no me gusta de Haproxy es la complejidad de la descripción de la configuración. Necesita construir una gran cantidad de texto para agregar uno que funcione en sentido ascendente. Pero la pereza es el motor del progreso: si no quieres escribir lo mismo, escribe un generador.
Ya tenía un mapa de Nginx con el formato
"tag" "upstream"
, así que decidí tomarlo como base, analizar y generar un back-end de haproxy basado en estos valores.
Ahora todo lo que necesitamos es agregar un nuevo host en nginx_map, iniciar el generador y obtener la configuración de haproxy lista.
Eso es probablemente todo por hoy. Este artículo se refiere más al introductorio y se dedicó al problema de elegir una solución y su integración en el entorno actual.
En el próximo artículo, hablaré con más detalle acerca de los escollos que encontramos al usar Haproxy, qué métricas resultaron útiles para monitorear y qué se debe optimizar exactamente en el sistema para obtener el máximo rendimiento de los servidores.
Gracias a todos por su atención, nos vemos!