Esta es la tercera publicación de blog de nuestra serie sobre la implementación de NGINX Open Source y NGINX Plus como puerta de enlace de API.
Nota: Salvo que se indique lo contrario, toda la información de esta publicación se aplica tanto a NGINX Plus como a NGINX Open Source. Para facilitar la lectura, el resto del blog se refiere simplemente a “NGINX” cuando la discusión se aplica a ambas versiones.
Los conceptos y beneficios de las arquitecturas de aplicação de microservicios han sido bien documentados en los últimos años, y en ningún otro lugar más que en el blog de NGINX . En el corazón de las aplicações de microservicios se encuentra la API HTTP, y las dos primeras publicaciones de blog de esta serie utilizan una API REST hipotética para ilustrar cómo NGINX aborda este estilo de aplicação.
A pesar de la popularidad de las API REST con formatos de mensajes JSON para aplicações modernas, no es un enfoque ideal para todos los escenarios ni para todas las organizaciones. Los desafíos más comunes son:
En los últimos años, gRPC ha surgido como un enfoque alternativo para crear aplicações distribuidas, y aplicações de microservicios en particular. gRPC fue desarrollado originalmente en Google, se convirtió en código abierto en 2015 y ahora es un proyecto de la Cloud Native Computing Foundation. Es importante destacar que gRPC utiliza HTTP/2 como mecanismo de transporte, aprovechando su formato de datos binarios y sus capacidades de transmisión multiplexada.
Los principales beneficios de gRPC son:
Las dos primeras publicaciones de esta serie describieron cómo se pueden entregar múltiples API a través de un único punto de entrada (por ejemplo, https://api.example.com ). El comportamiento y las características predeterminadas del tráfico gRPC nos llevan a adoptar el mismo enfoque cuando NGINX se implementa como puerta de enlace gRPC. Si bien es posible compartir el tráfico HTTP y gRPC en el mismo nombre de host y puerto, existen varias razones por las que es preferible separarlos:
Para lograr esta separación, colocamos la configuración de nuestro gateway gRPC en su propio bloque server{}
en el archivo de configuración principal de gRPC, grpc_gateway.conf , ubicado en el directorio /etc/nginx/conf.d .
Comenzamos definiendo el formato de las entradas en el registro de acceso para el tráfico gRPC (líneas 1 a 4). En este ejemplo, utilizamos un formato JSON para capturar los datos más relevantes de cada solicitud. Tenga en cuenta, por ejemplo, que el método HTTP no está incluido, ya que todas las solicitudes gRPC utilizan POST
. También registramos el código de estado gRPC junto con el código de estado HTTP. Sin embargo, el código de estado gRPC se puede generar de diferentes maneras. En condiciones normales, grpc-status
se devuelve como un tráiler HTTP/2 desde el backend, pero en algunas condiciones de error puede devolverse como un encabezado HTTP/2, ya sea por el backend o por NGINX mismo. Para simplificar el registro de acceso, utilizamos un bloque de mapa
(líneas 6 a 9) para evaluar una nueva variable $grpc_status
y obtener el estado de gRPC desde donde sea que se origine.
Esta configuración contiene dos directivas de escucha
(líneas 12 y 13) para que podamos probar tanto el tráfico de texto simple (puerto 50051) como el tráfico protegido por TLS (puerto 443). El parámetro http2
configura NGINX para aceptar conexiones HTTP/2; tenga en cuenta que esto es independiente del parámetro ssl
. Tenga en cuenta también que el puerto 50051 es el puerto de texto simple convencional para gRPC, pero no es adecuado para su uso en producción.
La configuración de TLS es convencional, con la excepción de la directiva ssl_protocols
(línea 23), que especifica TLS 1.2 como el protocolo aceptable más débil. La especificación HTTP/2 exige el uso de TLS 1.2 (o superior), lo que garantiza que todos los clientes admitan la extensión de Indicación de nombre de servidor (SNI) para TLS. Esto significa que la puerta de enlace gRPC puede compartir el puerto 443 con servidores virtuales definidos en otros bloques server{}
.
Para explorar las capacidades gRPC de NGINX, utilizamos un entorno de prueba simple que representa los componentes clave de una puerta de enlace gRPC, con múltiples servicios gRPC implementados. Utilizamos dos aplicações de muestra de las guías oficiales de gRPC : helloworld (escrita en Go) y RouteGuide (escrita en Python). La aplicação RouteGuide es especialmente útil porque incluye cada uno de los cuatro métodos de servicio gRPC:
Ambos servicios gRPC están instalados como contenedores Docker en nuestro host NGINX. Para obtener instrucciones completas sobre cómo crear el entorno de prueba, consulte el Apéndice .
Configuramos NGINX para que conozca los servicios RouteGuide y helloworld, junto con las direcciones de los contenedores disponibles.
Agregamos un bloque ascendente
para cada uno de los servicios gRPC (líneas 40 a 45 y 47 a 51) y los completamos con las direcciones de los contenedores individuales que ejecutan el código del servidor gRPC.
Con NGINX escuchando en el puerto de texto simple convencional para gRPC (50051), agregamos información de enrutamiento a la configuración, para que las solicitudes del cliente lleguen al servicio backend correcto. Pero primero debemos entender cómo se representan las llamadas al método gRPC como solicitudes HTTP/2. El siguiente diagrama muestra una versión abreviada del archivo route_guide.proto para el servicio RouteGuide, que ilustra cómo el paquete, el servicio y el método RPC forman la URI, como lo ve NGINX.
Por lo tanto, la información transportada en la solicitud HTTP/2 se puede usar para fines de enrutamiento simplemente haciendo coincidir el nombre del paquete (aquí, routeguide
o helloworld
).
El primer bloque de ubicación
(línea 26), sin ningún modificador, define una coincidencia de prefijo tal que /routeguide.
coincida con todos los servicios y métodos RPC definidos en el archivo .proto correspondiente para ese paquete. Por lo tanto, la directiva grpc_pass
(línea 27) pasa todas las solicitudes del cliente RouteGuide al grupo ascendente routeguide_service . Esta configuración (y la paralela para el servicio helloworld en las líneas 29 y 30) proporciona un mapeo simple entre un paquete gRPC y sus servicios de backend.
Tenga en cuenta que el argumento para las directivas grpc_pass
comienza con el esquema grpc://
, que envía solicitudes mediante una conexión gRPC de texto simple. Si el backend está configurado para TLS, podemos usar el esquema grpcs://
para asegurar la conexión gRPC con cifrado de extremo a extremo.
Después de ejecutar el cliente RouteGuide, podemos confirmar el comportamiento de enrutamiento revisando las entradas del archivo de registro. Aquí vemos que el método RPC RouteChat se enrutó al contenedor que se ejecuta en el puerto 10002.
$ python route_guide_client.py ... $ tail -1 /var/log/nginx/grpc_log.json | jq { "marca de tiempo": "2021-01-20T12:17:56+01:00", "cliente": "127.0.0.1", "uri": "/routeguide.RouteGuide/RouteChat", "estado http": 200, "estado de grpc": 0, "aguas arriba": "127.0.0.1:10002", "bytes de recepción": 161, "bytes de transmisión": 212 }
Como se muestra arriba, el enrutamiento de múltiples servicios gRPC a diferentes backends es simple, eficiente y requiere muy pocas líneas de configuración. Sin embargo, los requisitos de enrutamiento en un entorno de producción pueden ser más complejos y requerir un enrutamiento basado en otros elementos en la URI (el servicio gRPC o incluso métodos RPC individuales).
El siguiente fragmento de configuración extiende el ejemplo anterior de modo que el método RPC de transmisión bidireccional RouteChat
se enruta a un backend y todos los demás métodos RouteGuide
a un backend diferente.
La segunda directiva de ubicación
(línea 7) utiliza el modificador =
(signo igual) para indicar que se trata de una coincidencia exacta en la URI del método RPC de RouteChat
. Las coincidencias exactas se procesan antes que las coincidencias de prefijo, lo que significa que no se consideran otros bloques de ubicación
para el URI de RouteChat
.
Los errores de gRPC son algo diferentes de los del tráfico HTTP convencional. Los clientes esperan que las condiciones de error se expresen como respuestas gRPC, lo que hace que el conjunto predeterminado de páginas de error de NGINX (en formato HTML) no sea adecuado cuando NGINX está configurado como una puerta de enlace gRPC. Abordamos esto especificando un conjunto de respuestas de error personalizadas para los clientes gRPC.
El conjunto completo de respuestas de error de gRPC es una configuración relativamente larga y en gran medida estática, por lo que las guardamos en un archivo separado, errors.grpc_conf , y usamos la directiva include
(línea 34) para hacer referencia a ellas. A diferencia de los clientes HTTP/REST, no se espera que las aplicações cliente gRPC manejen una amplia gama de códigos de estado HTTP. La documentación de gRPC especifica cómo un proxy intermedio como NGINX debe convertir los códigos de error HTTP en códigos de estado de gRPC para que los clientes siempre reciban una respuesta adecuada. Utilizamos la directiva error_page
para realizar este mapeo.
Cada uno de los códigos de estado HTTP estándar se pasa a una ubicación determinada utilizando el prefijo @
para que se pueda generar una respuesta compatible con gRPC. Por ejemplo, el protocolo HTTP404
La respuesta se redirige internamente a la ubicación @grpc_unimplemented
, que se define más adelante en el archivo:
La ubicación con nombre @grpc_unimplemented
solo está disponible para el procesamiento interno de NGINX: los clientes no pueden solicitarla directamente, ya que no existe ningún URI enrutable. Dentro de esta ubicación, construimos una respuesta gRPC rellenando los encabezados gRPC obligatorios y enviándolos, sin un cuerpo de respuesta, mediante el código de estado HTTP.204
( Sin
contenido
).
Podemos usar el comando curl(1)
para imitar un cliente gRPC con mal comportamiento que solicita un método gRPC inexistente. Sin embargo, tenga en cuenta que curl
generalmente no es adecuado como cliente de prueba de gRPC porque los buffers de protocolo utilizan un formato de datos binarios. Para probar gRPC en la línea de comando, considere usar grpc_cli
.
$ curl -i --http2 -H "Tipo-de-contenido: aplicação/grpc" -H "TE: tráilers" -X POST https://grpc.example.com/does.Not/Exist HTTP/2 204 servidor: nginx/1.19.5 fecha: Mié, 20 Ene 2021 15:03:41 GMT grpc-status: 12 grpc-message: no implementado
El archivo grpc_errors.conf mencionado anteriormente también contiene asignaciones de códigos de estado HTTP a gRPC para otras respuestas de error que NGINX podría generar, como tiempos de espera y errores de certificado de cliente.
Los metadatos de gRPC permiten a los clientes enviar información adicional junto con las llamadas al método RPC, sin necesidad de que esos datos sean parte de la especificación de los buffers de protocolo (archivo .proto ). Los metadatos son una lista simple de pares clave-valor, y cada par se transmite como un encabezado HTTP/2 independiente. Por lo tanto, NGINX puede acceder fácilmente a los metadatos.
De los muchos casos de uso de metadatos, la autenticación del cliente es el más común para una puerta de enlace de API de gRPC. El siguiente fragmento de configuración muestra cómo NGINX Plus puede usar metadatos gRPC para realizar la autenticación JWT (la autenticación JWT es exclusiva de NGINX Plus). En este ejemplo, el JWT se envía en los metadatos del token de autenticación
.
Cada encabezado de solicitud HTTP está disponible para NGINX Plus como una variable llamada $http_ header
. Los guiones ( -
) en el nombre del encabezado se convierten en guiones bajos ( _
) en el nombre de la variable, por lo que el JWT está disponible como $http_auth_token
(línea 2).
Si se utilizan claves API para la autenticación, quizás con las API HTTP/REST existentes, estas también pueden incluirse en los metadatos de gRPC y ser validadas por NGINX. En la Parte 1 de esta serie de blogs se proporciona una configuración para la autenticación con clave API<.htmla>.
Al equilibrar la carga del tráfico hacia múltiples backends, es importante evitar enviar solicitudes a backends que están inactivos o no disponibles. Con NGINX Plus, podemos usar controles de estado activos para enviar de forma proactiva solicitudes fuera de banda a los backends y eliminarlos de la rotación de equilibrio de carga cuando no responden a los controles de estado como se esperaba. De esta forma garantizamos que las solicitudes de los clientes nunca lleguen a backends que estén fuera de servicio.
El siguiente fragmento de configuración habilita comprobaciones de estado activas para los servicios gRPC RouteGuide y helloworld; para resaltar la configuración relevante, omite algunas directivas que están incluidas en el archivo grpc_gateway.conf utilizado en secciones anteriores.
Para cada ruta ahora también especificamos la directiva health_check
(líneas 17 y 21). Como se especifica en el argumento type=grpc
, NGINX Plus utiliza el protocolo de verificación de estado gRPC para enviar una verificación de estado a cada servidor del grupo ascendente. Sin embargo, nuestros servicios gRPC simples no implementan el protocolo de verificación de estado de gRPC y, por lo tanto, esperamos que respondan con el código de estado que significa “no implementado” ( grpc_status=12
). Cuando lo hacen, es suficiente para indicar que nos estamos comunicando con un servicio gRPC activo.
Con esta configuración implementada, podemos desactivar cualquiera de los contenedores backend sin que los clientes gRPC experimenten demoras o tiempos de espera. Los controles de salud activos son exclusivos de NGINX Plus; lea más sobre los controles de salud de gRPC en nuestro blog.
La configuración de muestra en grpc_gateway.conf es adecuada para uso en producción, con algunas modificaciones menores para TLS. La capacidad de enrutar solicitudes gRPC según el paquete, servicio o método RPC significa que la funcionalidad NGINX existente se puede aplicar al tráfico gRPC exactamente de la misma manera que para las API HTTP/REST, o incluso como para el tráfico web normal. En cada caso, el bloque de ubicación
relevante se puede ampliar con una configuración adicional, como limitación de velocidad o control de ancho de banda.
En esta tercera y última publicación de blog de nuestra serie sobre la implementación de NGINX Open Source y NGINX Plus como puerta de enlace de API, nos centramos en gRPC como una tecnología nativa de la nube para crear aplicações de microservicios. Demostramos cómo NGINX puede entregar aplicações gRPC con la misma eficacia con la que lo hace con las API HTTP/REST, y cómo ambos estilos de API pueden publicarse a través de NGINX como una puerta de enlace API multipropósito.
Las instrucciones para configurar el entorno de prueba utilizado en esta publicación de blog se encuentran en el apéndice a continuación, y puede descargar todos los archivos desde nuestro repositorio GitHub Gist .
Consulte las otras publicaciones de blog de esta serie:
Para probar NGINX Plus como puerta de enlace API, comience hoy su prueba gratuita de 30 días o contáctenos para analizar sus casos de uso . Durante su prueba, utilice el conjunto completo de archivos de configuración de nuestro repositorio GitHub Gist .
Las siguientes instrucciones instalan el entorno de prueba en una máquina virtual para que esté aislado y sea repetible. Sin embargo, no hay ninguna razón por la cual no pueda instalarse en un servidor físico.
Para simplificar el entorno de prueba, utilizamos contenedores Docker para ejecutar los servicios gRPC. Esto significa que no necesitamos múltiples hosts para el entorno de prueba, pero aún podemos hacer que NGINX realice conexiones mediante proxy con una llamada de red, como en un entorno de producción.
El uso de Docker también nos permite ejecutar múltiples instancias de cada servicio gRPC en un puerto diferente sin necesidad de realizar cambios en el código. Cada servicio gRPC escucha en el puerto 50051 dentro del contenedor, que está asignado a un puerto localhost único en la máquina virtual. Esto a su vez libera el puerto 50051 para que NGINX pueda usarlo como su puerto de escucha. Por lo tanto, cuando los clientes de prueba se conectan utilizando su puerto preconfigurado de 50051, llegan a NGINX.
Instale NGINX Open Source o NGINX Plus según las instrucciones de la Guía de administración de NGINX Plus.
Copie los siguientes archivos del repositorio Gist de GitHub a /etc/nginx/conf.d :
Nota: Si no utiliza TLS, comente las directivas ssl_*
en grpc_gateway.conf .
Inicie NGINX Open Source o NGINX Plus.
$ sudo nginx
Para Debian y Ubuntu, ejecute:
$ sudo apt-get install docker.io
Para CentOS, RHEL y Oracle Linux, ejecute:
$ sudo yum install docker
Construya la imagen de Docker para los contenedores de RouteGuide a partir del siguiente Dockerfile.
Puede copiar el Dockerfile a un subdirectorio local antes de la compilación o especificar la URL del Gist para el Dockerfile como argumento para el comando docker
build
:
$ sudo docker build -t routeguide https://gist.githubusercontent.com/nginx-gists/87ed942d4ee9f7e7ebb2ccf757ed90be/raw/ce090f92f3bbcb5a94bbf8ded4d597cd47b43cbe/routeguide.Dockerfile
La descarga y creación de la imagen puede tardar unos minutos. La aparición del mensaje “ Completado
exitosamente”
y una cadena hexadecimal (el ID de la imagen) señalan la finalización de la compilación.
Confirme que la imagen se creó ejecutando docker
images
.
$ sudo docker images REPOSITORIO ETIQUETA ID DE IMAGEN CREADO TAMAÑO routeguide latest 63058a1cf8ca hace 1 minuto 1.31 GB python latest 825141134528 hace 9 días 923 MB
Inicie los contenedores de RouteGuide.
$ sudo docker run --name rg1 -p 10001:50051 -d routeguide $ sudo docker run --name rg2 -p 10002:50051 -d routeguide $ sudo docker run --name rg3 -p 10003:50051 -d routeguide
A medida que cada comando tiene éxito, aparece una cadena hexadecimal larga que representa el contenedor en ejecución.
Compruebe que los tres contenedores estén activos ejecutando docker
ps
. (La salida de muestra se divide en varias líneas para facilitar la lectura).
$ sudo docker ps ID DEL CONTENEDOR IMAGEN COMANDO ESTADO ... d0cdaaeddf0f routeguide "python route_g..." Arriba 2 segundos... c04996ca3469 routeguide "python route_g..." Arriba 9 segundos...
2170ddb62898 guía de ruta "python route_g..." Sube 1 minuto...... NOMBRES DE PUERTOS... 0.0.0.0:10003->50051/tcp rg3 ... 0.0.0.0:10002->50051/tcp rg2 ... 0.0.0.0:10001->50051/tcp rg1
La columna PUERTOS
en la salida muestra cómo cada uno de los contenedores ha asignado un puerto local diferente al puerto 50051 dentro del contenedor.
Construya la imagen de Docker para los contenedores helloworld a partir del siguiente Dockerfile.
Puede copiar el Dockerfile a un subdirectorio local antes de la compilación o especificar la URL del Gist para el Dockerfile como argumento para el comando docker
build
:
$ sudo docker build -t holamundo https://gist.githubusercontent.com/nginx-gists/87ed942d4ee9f7e7ebb2ccf757ed90be/raw/ce090f92f3bbcb5a94bbf8ded4d597cd47b43cbe/holamundo.Dockerfile
La descarga y creación de la imagen puede tardar unos minutos. La aparición del mensaje “ Completado
exitosamente”
y una cadena hexadecimal (el ID de la imagen) señalan la finalización de la compilación.
Confirme que la imagen se creó ejecutando docker
images
.
$ sudo docker images REPOSITORIO ETIQUETA ID DE IMAGEN CREADO TAMAÑO helloworld latest e5832dc0884a hace 10 segundos 926 MB routeguide latest 170761fa3f03 hace 4 minutos 1.31 GB python latest 825141134528 hace 9 días 923 MB golang latest d0e7a411e3da hace 3 semanas 794 MB
Inicie los contenedores de helloworld.
$ sudo docker run --name hw1 -p 20001:50051 -d holamundo $ sudo docker run --name hw2 -p 20002:50051 -d holamundo
A medida que cada comando tiene éxito, aparece una cadena hexadecimal larga que representa el contenedor en ejecución.
Compruebe que los dos contenedores helloworld estén activos ejecutando docker
ps
.
$ sudo docker ps ID DEL CONTENEDOR IMAGEN COMANDO ESTADO ... e0d204ae860a helloworld "ve y ejecuta greeter..." Arriba 5 segundos...
66f21d89be78 holamundo "ve a correr, saludador..." Arriba 9 segundos... d0cdaaeddf0f routeguide "python route_g..." Arriba 4 minutos... c04996ca3469 routeguide "python route_g..." Arriba 4 minutos...
2170ddb62898 guía de ruta "python route_g..." Arriba 5 minutos...... NOMBRES DE PUERTOS... 0.0.0.0:20002->50051/tcp hw2... 0.0.0.0:20001->50051/tcp hw1... 0.0.0.0:10003->50051/tcp rg3 ... 0.0.0.0:10002->50051/tcp rg2 ... 0.0.0.0:10001->50051/tcp rg1
Instale los requisitos previos del lenguaje de programación, algunos de los cuales pueden ya estar instalados en el entorno de prueba.
Para Ubuntu y Debian, ejecute:
$ sudo apt-get install golang-go python3 python-pip git
Para CentOS, RHEL y Oracle Linux, ejecute:
$ sudo yum instalar golang python python-pip git
Tenga en cuenta que python-pip
requiere que el repositorio EPEL esté habilitado (ejecute sudo
yum
install
epel-release
primero según sea necesario).
Descargue la aplicação helloworld:
$ ve a google.golang.org/grpc
Descargue la aplicação RouteGuide:
$ git clone -b v1.14.1 https://github.com/grpc/grpc $ pip install grpcio-tools
Ejecute el cliente helloworld:
$ go run go/src/google.golang.org/grpc/examples/helloworld/greeter_client/main.go
Ejecute el cliente RouteGuide:
$ cd grpc/ejemplos/python/guia_de_ruta $ python guia_de_ruta_cliente.py
Verifique los registros de NGINX para confirmar que el entorno de prueba esté operativo:
$ tail /var/log/nginx/grpc_log.json
"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.