Hola Habr! Les traigo a su atención la traducción de la publicación: Migración de Nginx a Envoy Proxy .
Envoy es un servidor proxy distribuido de alto rendimiento (escrito en C ++) diseñado para servicios y aplicaciones individuales, también es un bus de comunicación y un "plano de datos universal" diseñado para grandes arquitecturas de "servicio de malla" de microservicios. Cuando se creó, se tuvieron en cuenta las soluciones a los problemas que surgieron durante el desarrollo de servidores como NGINX, HAProxy, equilibradores de carga de hardware y equilibradores de carga en la nube. Envoy funciona con todas las aplicaciones y abstrae la red, proporcionando funciones comunes independientemente de la plataforma. Cuando todo el tráfico de la oficina en la infraestructura pasa a través de la cuadrícula Envoy, se vuelve fácil visualizar las áreas problemáticas con una observación constante, sintonizando el rendimiento general y agregando funciones básicas en un lugar específico.
Las posibilidades
- Arquitectura fuera de proceso: enviado es un servidor independiente de alto rendimiento que consume una pequeña cantidad de RAM. Funciona en conjunción con cualquier lenguaje o marco de aplicación.
- Soporte para http / 2 y grpc: envoy tiene soporte de primera clase para http / 2 y grpc para conexiones entrantes y salientes. Este es un proxy transparente de http / 1.1 a http / 2.
- Equilibrio de carga mejorado: enviado admite funciones avanzadas de equilibrio de carga, incluidos los reintentos automáticos, circuito abierto, límite de velocidad global, solicitudes de sombreado, equilibrio de carga de zona local, etc.
- API de gestión de configuración: enviado proporciona una API robusta para gestionar dinámicamente su configuración.
- Observabilidad: observabilidad profunda del tráfico L7, soporte integrado para rastreo distribuido y observabilidad de mongodb, dynamodb y muchas otras aplicaciones.
Paso 1 - Ejemplo de configuración NGINX
Este script utiliza un archivo nginx.conf especialmente creado, basado en un ejemplo completo de NGINX Wiki . Puede ver la configuración en el editor abriendo nginx.conf
Fuente nginx config
user www www; pid /var/run/nginx.pid; worker_processes 2; events { worker_connections 2000; } http { gzip on; gzip_min_length 1100; gzip_buffers 4 8k; gzip_types text/plain; log_format main '$remote_addr - $remote_user [$time_local] ' '"$request" $status $bytes_sent ' '"$http_referer" "$http_user_agent" ' '"$gzip_ratio"'; log_format download '$remote_addr - $remote_user [$time_local] ' '"$request" $status $bytes_sent ' '"$http_referer" "$http_user_agent" ' '"$http_range" "$sent_http_content_range"'; upstream targetCluster { 172.18.0.3:80; 172.18.0.4:80; } server { listen 8080; server_name one.example.com www.one.example.com; access_log /var/log/nginx.access_log main; error_log /var/log/nginx.error_log info; location / { proxy_pass http://targetCluster/; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } }
Las configuraciones de NGINX generalmente tienen tres elementos clave:
- Configuración del servidor NGINX, la estructura de registro y la funcionalidad de Gzip. Esto se determina globalmente en todos los casos.
- Configurar NGINX para aceptar solicitudes de host one.example.com en el puerto 8080.
- Establecer su ubicación de destino, cómo manejar el tráfico para diferentes partes de la URL.
No toda la configuración se aplicará a Envoy Proxy, y no necesita configurar algunos ajustes. Envoy Proxy tiene cuatro tipos clave que admiten la infraestructura subyacente que ofrece NGINX. El núcleo es:
- Oyentes: determinan cómo Envoy Proxy acepta las solicitudes entrantes. Envoy Proxy actualmente solo es compatible con escuchas basadas en TCP. Una vez que se establece la conexión, se transfiere a un conjunto de filtros para su procesamiento.
- Filtros: forman parte de una arquitectura canalizada que puede procesar datos entrantes y salientes. Esta funcionalidad incluye filtros, como Gzip, que comprime los datos antes de enviarlos al cliente.
- Enrutadores: Redirigen el tráfico al destino deseado, definido como un clúster.
- Clústeres: definen el punto final para el tráfico y la configuración.
Utilizaremos estos cuatro componentes para crear la configuración de Envoy Proxy para que coincida con la configuración específica de NGINX. El objetivo de Envoy es trabajar con la API y la configuración dinámica. En este caso, la configuración básica utilizará parámetros estáticos codificados de NGINX.
Paso 2 - Configurar NGINX
La primera parte de nginx.conf define algunos de los componentes internos de NGINX que deben configurarse.
Conexiones de trabajadores
La siguiente configuración determina la cantidad de procesos de trabajo y conexiones. Esto indica cómo NGINX escalará para satisfacer la demanda.
worker_processes 2; events { worker_connections 2000; }
Envoy Proxy gestiona los flujos de trabajo y las conexiones de manera diferente.
Envoy crea un flujo de trabajo para cada hilo de hardware en el sistema. Cada subproceso de trabajo ejecuta un bucle de evento sin bloqueo que es responsable de
- Escuchando a cada oyente
- Aceptar nuevas conexiones
- Crear un conjunto de filtros para una conexión
- Manejo de todas las operaciones de E / S durante la vida útil de una conexión.
Todo el procesamiento de conexión adicional se procesa completamente en el flujo de trabajo, incluido cualquier comportamiento de reenvío.
Para cada flujo de trabajo en Envoy, hay una conexión en el grupo. Por lo tanto, los grupos de conexiones HTTP / 2 establecen solo una conexión para cada host externo a la vez; si hay cuatro subprocesos de trabajo, habrá cuatro conexiones HTTP / 2 para cada host externo en un estado estable. Al almacenar todo en un flujo de trabajo, casi todo el código se puede escribir sin bloqueo, como si fuera un subproceso único. Si se asignan flujos de trabajo más de lo necesario, esto puede conducir a un uso no racional de la memoria, la creación de una gran cantidad de conexiones inactivas y una disminución en la cantidad de conexiones devueltas al grupo.
Para obtener más información, visite el blog Envoy Proxy .
Configuración HTTP
El siguiente bloque de configuración de NGINX define la configuración de HTTP, como:
- Qué tipos de mime son compatibles
- Tiempos de espera predeterminados
- Configuración de Gzip
Puede configurar estos aspectos utilizando filtros en Envoy Proxy, que discutiremos más adelante.
Paso 3 - Configuración del servidor
En el bloque de configuración HTTP, la configuración NGINX le indica que escuche en el puerto 8080 y responda a las solicitudes entrantes para los dominios one.example.com y www.one.example.com .
server { listen 8080; server_name one.example.com www.one.example.com;
Dentro del Enviado, los oyentes lo controlan.
Enviados oyentes
El aspecto más importante para comenzar con Envoy Proxy es identificar a los oyentes. Debe crear un archivo de configuración que describa cómo desea ejecutar una instancia de Envoy.
El fragmento a continuación creará un nuevo oyente y lo asociará con el puerto 8080. La configuración le dice a Envoy Proxy a qué puertos debe estar vinculado para las solicitudes entrantes.
Envoy Proxy utiliza la notación YAML para su configuración. Para familiarizarse con esta notación, vea el enlace aquí.
Copy to Editorstatic_resources: listeners: - name: listener_0 address: socket_address: { address: 0.0.0.0, port_value: 8080 }
No hay necesidad de definir nombre_servidor , ya que los filtros de Envoy Proxy pueden manejar esto.
Paso 4 - Configuración de ubicación
Cuando llega una solicitud a NGINX, el bloque de ubicación determina cómo procesar y dónde dirigir el tráfico. En el siguiente fragmento, todo el tráfico hacia el sitio se transmite a un clúster en sentido ascendente (nota del traductor: en sentido ascendente suele ser un servidor de aplicaciones) denominado targetCluster . El clúster ascendente define los nodos que deben procesar la solicitud. Discutiremos esto en el siguiente paso.
location / { proxy_pass http://targetCluster/; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }
En Envoy, Filters hace esto.
Filtros de enviado
Para una configuración estática, los filtros determinan cómo manejar las solicitudes entrantes. En este caso, establecemos filtros que coinciden con server_names en el paso anterior. Cuando llegan solicitudes entrantes que corresponden a dominios y rutas específicos, el tráfico se enruta al clúster. Este es el equivalente de la configuración ascendente NGINX.
Copy to Editor filter_chains: - filters: - name: envoy.http_connection_manager config: codec_type: auto stat_prefix: ingress_http route_config: name: local_route virtual_hosts: - name: backend domains: - "one.example.com" - "www.one.example.com" routes: - match: prefix: "/" route: cluster: targetCluster http_filters: - name: envoy.router
El nombre envoy.http_connection_manager es un filtro incorporado en Envoy Proxy. Otros filtros incluyen Redis , Mongo , TCP . Puede encontrar la lista completa en la documentación .
Para obtener más información sobre otras políticas de equilibrio de carga, visite la Documentación de Envoy .
Paso 5 - Proxy y configuración aguas arriba
En NGINX, la configuración ascendente define el conjunto de servidores de destino que manejarán el tráfico. En este caso, se asignaron dos grupos.
upstream targetCluster { 172.18.0.3:80; 172.18.0.4:80; }
En Envoy, se gestiona en clúster.
Grupos de enviados
El equivalente de aguas arriba se define como grupos. En este caso, se identificaron los hosts que servirán al tráfico. Un método para acceder a los hosts, como un tiempo de espera, se define como una configuración de clúster. Esto le permite controlar con mayor precisión la granularidad de aspectos como la latencia y el equilibrio de carga.
Copy to Editor clusters: - name: targetCluster connect_timeout: 0.25s type: STRICT_DNS dns_lookup_family: V4_ONLY lb_policy: ROUND_ROBIN hosts: [ { socket_address: { address: 172.18.0.3, port_value: 80 }}, { socket_address: { address: 172.18.0.4, port_value: 80 }} ]
Cuando se utiliza el descubrimiento de servicio STRICT_DNS, Envoy resolverá de forma continua y asíncrona los objetivos DNS especificados. Cada dirección IP devuelta como resultado de DNS se considerará un host explícito en el clúster ascendente. Esto significa que si la solicitud devuelve dos direcciones IP, Envoy asumirá que hay dos hosts en el clúster, y ambos deben tener una carga equilibrada. Si el host se elimina del resultado, Envoy supone que ya no existe y seleccionará el tráfico de cualquier grupo de conexiones existente.
Para obtener más información, consulte la documentación de proxy de Envoy .
Paso 6 - Acceso al registro y errores
La configuración final es el registro. En lugar de transferir registros de errores al disco, Envoy Proxy utiliza un enfoque basado en la nube. Todos los registros de aplicaciones se muestran en stdout y stderr .
Cuando los usuarios realizan una solicitud, los registros de acceso son opcionales y están deshabilitados de manera predeterminada. Para habilitar los registros de acceso para las solicitudes HTTP, habilite la configuración de access_log para el Administrador de conexión HTTP. La ruta puede ser un dispositivo, como stdout , o un archivo en el disco, según sus requisitos.
La siguiente configuración redirigirá todos los registros de acceso a stdout (nota del traductor: stdout es necesario para usar enviado dentro de Docker. Si lo usa sin docker, reemplace / dev / stdout con la ruta al archivo de registro normal). Copie el fragmento a la sección de configuración para el administrador de conexión:
Copy to Clipboardaccess_log: - name: envoy.file_access_log config: path: "/dev/stdout"
Los resultados deberían verse así:
- name: envoy.http_connection_manager config: codec_type: auto stat_prefix: ingress_http access_log: - name: envoy.file_access_log config: path: "/dev/stdout" route_config:
Por defecto, Envoy tiene una cadena de formato que incluye los detalles de la solicitud HTTP:
[%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%" "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%"\n
El resultado de esta cadena de formato:
[2018-11-23T04:51:00.281Z] "GET / HTTP/1.1" 200 - 0 58 4 1 "-" "curl/7.47.0" "f21ebd42-6770-4aa5-88d4-e56118165a7d" "one.example.com" "172.18.0.4:80"
El contenido de la salida se puede personalizar configurando el campo de formato. Por ejemplo:
access_log: - name: envoy.file_access_log config: path: "/dev/stdout" format: "[%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%" %RESPONSE_CODE% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%"\n"
La cadena de registro también se puede generar en formato JSON configurando el campo json_format . Por ejemplo:
access_log: - name: envoy.file_access_log config: path: "/dev/stdout" json_format: {"protocol": "%PROTOCOL%", "duration": "%DURATION%", "request_method": "%REQ(:METHOD)%"}
Para obtener más información sobre las técnicas de registro de enviados, visite
https://www.envoyproxy.io/docs/envoy/latest/configuration/access_log#config-access-log-format-dictionaries
El registro no es la única forma de hacerse una idea de trabajar con Envoy Proxy. Tiene funciones avanzadas incorporadas para rastreo y métricas. Puede obtener más información en la documentación de seguimiento o mediante el Script de seguimiento interactivo .
Paso 7 - Lanzamiento
Ahora ha transferido la configuración de NGINX a Envoy Proxy. El último paso es ejecutar una instancia de Envoy Proxy para probarlo.
Ejecutar desde usuario
En la parte superior de la configuración de NGINX, el usuario de línea www www; indica que NGINX se ha lanzado como un usuario con pocos privilegios para mejorar la seguridad.
Envoy Proxy adopta un enfoque basado en la nube para administrar quién es el propietario del proceso. Cuando ejecutamos Envoy Proxy a través del contenedor, podemos especificar un usuario con un nivel de privilegio bajo.
Lanzar Envoy Proxy
El siguiente comando iniciará Envoy Proxy a través del contenedor Docker en el host. Este comando proporciona a Envoy la capacidad de escuchar las solicitudes entrantes a través del puerto 80. Sin embargo, como se indica en la configuración de escucha, Envoy Proxy escucha el tráfico entrante a través del puerto 8080. Esto permite que el proceso se ejecute como un usuario con pocos privilegios.
docker run --name proxy1 -p 80:8080 --user 1000:1000 -v /root/envoy.yaml:/etc/envoy/envoy.yaml envoyproxy/envoy
Prueba
Con los servidores proxy en ejecución, las pruebas ahora se pueden hacer y procesar. El siguiente comando cURL emite una solicitud con el encabezado del host definido en la configuración del proxy.
curl -H "Host: one.example.com" localhost -i
Una solicitud HTTP generará el error 503 . Esto se debe al hecho de que las conexiones ascendentes no funcionan y no están disponibles. Por lo tanto, Envoy Proxy no tiene destinos de destino disponibles para la solicitud. El siguiente comando lanzará una serie de servicios HTTP que coinciden con la configuración definida para Envoy.
docker run -d katacoda/docker-http-server; docker run -d katacoda/docker-http-server;
Con los servicios disponibles, Envoy puede enviar con éxito el tráfico a su destino.
curl -H "Host: one.example.com" localhost -i
Debería ver una respuesta que indica qué contenedor de Docker ha procesado la solicitud. En los registros de Envoy Proxy, también debería ver la cadena de acceso que se muestra.
Encabezados de respuesta HTTP adicionales
Verá encabezados HTTP adicionales en los encabezados de respuesta de la solicitud real. El encabezado muestra el tiempo que el host ascendente pasó procesando la solicitud. Se expresa en milisegundos. Esto es útil si el cliente desea determinar el tiempo de servicio en comparación con la latencia de la red.
x-envoy-upstream-service-time: 0 server: envoy
Configuración final
static_resources: listeners: - name: listener_0 address: socket_address: { address: 0.0.0.0, port_value: 8080 } filter_chains: - filters: - name: envoy.http_connection_manager config: codec_type: auto stat_prefix: ingress_http route_config: name: local_route virtual_hosts: - name: backend domains: - "one.example.com" - "www.one.example.com" routes: - match: prefix: "/" route: cluster: targetCluster http_filters: - name: envoy.router clusters: - name: targetCluster connect_timeout: 0.25s type: STRICT_DNS dns_lookup_family: V4_ONLY lb_policy: ROUND_ROBIN hosts: [ { socket_address: { address: 172.18.0.3, port_value: 80 }}, { socket_address: { address: 172.18.0.4, port_value: 80 }} ] admin: access_log_path: /tmp/admin_access.log address: socket_address: { address: 0.0.0.0, port_value: 9090 }
Las instrucciones de instalación de Envoy Proxy se pueden encontrar en https://www.getenvoy.io/
Por defecto en rpm no hay configuración de servicio systemd.
Agregue la configuración del servicio systemd /etc/systemd/system/envoy.service:
[Unit] Description=Envoy Proxy Documentation=https://www.envoyproxy.io/ After=network-online.target Requires=envoy-auth-server.service Wants=nginx.service [Service] User=root Restart=on-failure ExecStart=/usr/bin/envoy --config-path /etc/envoy/config.yaml [Install] WantedBy=multi-user.target
Debe crear el directorio / etc / envoy / y colocar la configuración config.yaml allí.
Por proxy enviado hay un chat de telegramas: https://t.me/envoyproxy_ru
Envoy Proxy no admite la distribución de contenido estático. Entonces, ¿quién puede votar por la función: https://github.com/envoyproxy/envoy/issues/378