En este tutorial se muestra cómo solucionar problemas de un servicio de Knative Serving que no funciona correctamente mediante las herramientas de Stackdriver para la detección y un flujo de trabajo de desarrollo local para la investigación.
Esta guía detallada, que complementa la guía para solucionar problemas, utiliza un proyecto de ejemplo que genera errores de tiempo de ejecución al implementarse. Tu tarea será solucionar estos errores para encontrar y corregir el problema.
Objetivos
- Escribir, compilar y desplegar un servicio en el servicio de Knative
- Usar Cloud Logging para identificar un error
- Recuperar la imagen de contenedor de Container Registry para analizar la causa raíz
- Corregir el servicio "production" y, a continuación, mejorarlo para mitigar problemas futuros
Costes
En este documento, se utilizan los siguientes componentes facturables de Google Cloud:
Para generar una estimación de costes basada en el uso previsto,
utiliza la calculadora de precios.
Antes de empezar
- En este tutorial se presupone que tienes Knative Serving instalado y configurado en tu clúster.
- Asegúrate de que el entorno de línea de comandos esté configurado y que las herramientas estén actualizadas:
- Instala curl para probar el servicio.
- Instala Docker de forma local.
Ensamblar el código
Crea un nuevo servicio de saludo de Knative paso a paso. Te recordamos que este servicio crea un error de tiempo de ejecución a propósito para el ejercicio de solución de problemas.
Para crear un proyecto:
Node.js
Crea un proyecto de Node.js definiendo el paquete de servicio, las dependencias iniciales y algunas operaciones comunes.Crea un directorio
hello-service
:mkdir hello-service cd hello-service
Generar un archivo
package.json
:npm init --yes npm install express@4
Abre el nuevo archivo
package.json
en tu editor y configura una secuencia de comandosstart
para ejecutarnode index.js
. Cuando haya terminado, el archivo tendrá este aspecto:{ "name": "hello-service", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "node index.js", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "express": "^4.17.1" } }
Si sigues desarrollando este servicio más allá del tutorial inmediato, te recomendamos que rellenes la descripción y el autor, y que evalúes la licencia. Para obtener más información, consulta la documentación de package.json.
Python
Para crear un directorio de
hello-service
, sigue estos pasos:mkdir hello-service cd hello-service
Crea un archivo requirements.txt y copia tus dependencias en él:
Go
Crea un directorio
hello-service
:mkdir hello-service cd hello-service
Crea un proyecto de Go inicializando un nuevo módulo de Go:
go mod init <var>my-domain</var>.com/hello-service
Puedes actualizar el nombre específico como quieras. Debes actualizar el nombre si el código se publica en un repositorio de código accesible a través de la Web.
Java
Crea un proyecto de Maven:
mvn archetype:generate \ -DgroupId=com.example \ -DartifactId=hello-service \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DinteractiveMode=false
Copia las dependencias en la lista de dependencias
pom.xml
(entre los elementos<dependencies>
):Copia el ajuste de compilación en tu
pom.xml
(en los elementos<dependencies>
):
Crea un servicio HTTP para gestionar las solicitudes entrantes:
Node.js
Python
Go
Java
Crea un
Dockerfile
para definir la imagen de contenedor que se usará para desplegar el servicio:Node.js
Python
Go
Java
En este ejemplo se usa Jib para crear imágenes de Docker con herramientas comunes de Java. Jib optimiza las compilaciones de contenedores sin necesidad de usar un Dockerfile ni de tener Docker instalado. Más información sobre cómo crear contenedores Java con Jib
Envío del código
El envío de código consta de tres pasos: crear una imagen de contenedor con Cloud Build, subir la imagen de contenedor a Container Registry y desplegar la imagen de contenedor en Knative Serving.
Para enviar tu código, sigue estos pasos:
Crea el contenedor y publícalo en Container Registry:
Node.js
gcloud builds submit --tag gcr.io/PROJECT_ID/hello-service
Donde PROJECT_ID es el ID de tu proyecto. Google Cloud Puedes consultar el ID de tu proyecto actual con
gcloud config get-value project
.Si la operación se realiza correctamente, debería aparecer un mensaje SUCCESS que contenga el ID, la hora de creación y el nombre de la imagen. La imagen se almacena en Container Registry y se puede volver a usar si quieres.
Python
gcloud builds submit --tag gcr.io/PROJECT_ID/hello-service
Donde PROJECT_ID es el ID de tu proyecto. Google Cloud Puedes consultar el ID de tu proyecto actual con
gcloud config get-value project
.Si la operación se realiza correctamente, debería aparecer un mensaje SUCCESS que contenga el ID, la hora de creación y el nombre de la imagen. La imagen se almacena en Container Registry y se puede volver a usar si quieres.
Go
gcloud builds submit --tag gcr.io/PROJECT_ID/hello-service
Donde PROJECT_ID es el ID de tu proyecto. Google Cloud Puedes consultar el ID de tu proyecto actual con
gcloud config get-value project
.Si la operación se realiza correctamente, debería aparecer un mensaje SUCCESS que contenga el ID, la hora de creación y el nombre de la imagen. La imagen se almacena en Container Registry y se puede volver a usar si quieres.
Java
mvn compile jib:build -Dimage=gcr.io/PROJECT_ID/hello-service
Donde PROJECT_ID es el ID de tu proyecto. Google Cloud Puedes consultar el ID de tu proyecto actual con
gcloud config get-value project
.Si todo va bien, debería aparecer el mensaje BUILD SUCCESS. La imagen se almacena en Container Registry y se puede volver a usar si quieres.
Ejecuta el siguiente comando para implementar tu aplicación:
gcloud run deploy hello-service --image gcr.io/PROJECT_ID/hello-service
Sustituye PROJECT_ID por el ID de tu proyecto. Google Cloud
hello-service
es tanto el nombre de la imagen del contenedor como el nombre del servicio de Knative Serving. Verás que la imagen del contenedor se ha desplegado en el servicio y el clúster que configuraste anteriormente en Configurar gcloud.Espera a que se complete el despliegue, que puede tardar aproximadamente medio minuto. Si la acción se realiza correctamente, la línea de comandos mostrará la URL del servicio.
Probar
Prueba el servicio para confirmar que lo has implementado correctamente. Las solicitudes deben fallar con un error HTTP 500 o 503 (miembros de la clase Errores del servidor 5xx). En el tutorial se explica cómo solucionar este error.
Si tu clúster está configurado con un dominio predeterminado enrutable, omite los pasos anteriores y copia la URL en tu navegador web.
Si no usas certificados TLS automáticos ni asignación de dominio, no se te proporcionará una URL navegable para tu servicio.
En su lugar, usa la URL proporcionada y la dirección IP de la puerta de enlace de entrada del servicio para crear un comando curl
que pueda enviar solicitudes a tu servicio:
-
Para obtener la IP externa del balanceador de carga, ejecuta el siguiente comando:
kubectl get svc istio-ingressgateway -n ASM-INGRESS-NAMESPACE
Sustituye ASM-INGRESS-NAMESPACE por el espacio de nombres en el que se encuentra tu entrada de Cloud Service Mesh. Especifica
istio-system
si has instalado Cloud Service Mesh con su configuración predeterminada.La salida resultante tiene un aspecto similar al siguiente:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) istio-ingressgateway LoadBalancer XX.XX.XXX.XX pending 80:32380/TCP,443:32390/TCP,32400:32400/TCP
donde el valor EXTERNAL-IP es la dirección IP externa del balanceador de carga.
Ejecuta un comando
curl
con esta direcciónGATEWAY_IP
en la URL.curl -G -H "Host: SERVICE-DOMAIN" https://EXTERNAL-IP/
Sustituye SERVICE-DOMAIN por el dominio asignado de forma predeterminada a tu servicio. Para obtenerla, toma la URL predeterminada y elimina el protocolo
http://
.Aparece el mensaje de error HTTP 500 o HTTP 503.
Investigar el problema
Imagina que el error HTTP 5xx que se ha producido en la sección Probar de arriba se ha producido como un error de tiempo de ejecución en producción. En este tutorial se explica un proceso formal para gestionarlo. Aunque los procesos de resolución de errores de producción varían mucho, en este tutorial se presenta una secuencia de pasos concreta para mostrar la aplicación de herramientas y técnicas útiles.
Para investigar este problema, tendrás que completar las siguientes fases:
- Recoge más detalles sobre el error notificado para poder investigar más a fondo y definir una estrategia de mitigación.
- Reduce el impacto en los usuarios decidiendo si quieres seguir adelante con una corrección o volver a una versión que funcione correctamente.
- Reproduce el error para confirmar que se han recogido los detalles correctos y que no se trata de un fallo puntual.
- Realiza un análisis de la causa raíz del error para encontrar el código, la configuración o el proceso que lo ha provocado.
Al principio de la investigación, tienes una URL, una marca de tiempo y el mensaje "Error interno del servidor".
Gathering further details
Recaba más información sobre el problema para saber qué ha ocurrido y determinar los pasos siguientes.
Usa las herramientas disponibles para obtener más detalles:
Consulta los registros para obtener más información.
Usa Cloud Logging para revisar la secuencia de operaciones que han provocado el problema, incluidos los mensajes de error.
Restaurar una versión correcta
Si tienes una revisión que sabes que funcionaba, puedes restaurar tu servicio para usar esa revisión. Por ejemplo, no podrás revertir el servicio hello-service
que has implementado en este tutorial porque solo contiene una revisión.
Para localizar una revisión y restaurar tu servicio, sigue estos pasos:
Reproducir el error
Con los detalles que has obtenido anteriormente, confirma que el problema se produce de forma constante en condiciones de prueba.
Envía la misma solicitud HTTP probándola de nuevo y comprueba si se informa del mismo error y los mismos detalles. Los detalles del error pueden tardar un poco en aparecer.
Como el servicio de ejemplo de este tutorial es de solo lectura y no activa ningún efecto secundario que complique las cosas, es seguro reproducir errores en producción. Sin embargo, en muchos servicios reales no será así: es posible que tengas que reproducir errores en un entorno de prueba o limitar este paso a una investigación local.
Reproducir el error establece el contexto para seguir trabajando. Por ejemplo, si los desarrolladores no pueden reproducir el error, es posible que se necesite una instrumentación adicional del servicio para investigar más a fondo.
Llevar a cabo un análisis de causas
El análisis de la causa raíz es un paso importante para solucionar problemas de forma eficaz y asegurarte de que resuelves el problema en lugar de un síntoma.
En una sección anterior de este tutorial, has reproducido el problema en el servicio de Knative, lo que confirma que el problema está activo cuando el servicio se aloja en el servicio de Knative. Ahora, reproduce el problema de forma local para determinar si se limita al código o si solo se produce en el alojamiento de producción.
Si no has usado Docker CLI de forma local con Container Registry, autentícalo con gcloud:
gcloud auth configure-docker
Para ver otras opciones, consulta Métodos de autenticación de Container Registry.
Si el nombre de la imagen de contenedor usada más recientemente no está disponible, la descripción del servicio tiene la información de la imagen de contenedor implementada más recientemente:
gcloud run services describe hello-service
Busca el nombre de la imagen del contenedor en el objeto
spec
. Un comando más específico puede obtenerlo directamente:gcloud run services describe hello-service \ --format="value(spec.template.spec.containers.image)"
Este comando muestra el nombre de una imagen de contenedor, como
gcr.io/PROJECT_ID/hello-service
.Extrae la imagen de contenedor de Container Registry a tu entorno. Este paso puede tardar varios minutos, ya que se descarga la imagen de contenedor:
docker pull gcr.io/PROJECT_ID/hello-service
Las actualizaciones posteriores de la imagen de contenedor que reutilicen este nombre se podrán recuperar con el mismo comando. Si te saltas este paso, el comando
docker run
de abajo extraerá una imagen de contenedor si no hay ninguna en la máquina local.Ejecuta el comando de forma local para confirmar que el problema no es exclusivo de Knative Serving:
PORT=8080 && docker run --rm -e PORT=$PORT -p 9000:$PORT \ gcr.io/PROJECT_ID/hello-service
Desglosando los elementos del comando anterior,
- El servicio usa la variable de entorno
PORT
para determinar el puerto en el que debe escuchar dentro del contenedor. - El comando
run
inicia el contenedor. De forma predeterminada, se usa el comando de punto de entrada definido en el Dockerfile o en una imagen de contenedor principal. - La marca
--rm
elimina la instancia del contenedor al salir. - La marca
-e
asigna un valor a una variable de entorno.-e PORT=$PORT
propaga la variablePORT
del sistema local al contenedor con el mismo nombre de variable. - La marca
-p
publica el contenedor como un servicio disponible en localhost en el puerto 9000. Las solicitudes a localhost:9000 se enrutarán al contenedor en el puerto 8080. Esto significa que la salida del servicio sobre el número de puerto en uso no coincidirá con la forma en que se accede al servicio. - El último argumento,
gcr.io/PROJECT_ID/hello-service
, es una ruta de repositorio que apunta a la versión más reciente de la imagen del contenedor. Si no está disponible localmente, Docker intenta obtener la imagen de un registro remoto.
En el navegador, abre http://localhost:9000. Comprueba la salida de la terminal para ver si hay mensajes de error que coincidan con los de Google Cloud Observability.
Si el problema no se puede reproducir de forma local, puede que sea exclusivo del entorno de servicio de Knative. Consulta la guía para solucionar problemas de Knative Serving para saber qué áreas concretas debes investigar.
En este caso, el error se reproduce de forma local.
- El servicio usa la variable de entorno
Ahora que se ha confirmado que el error persiste y que lo provoca el código de servicio en lugar de la plataforma de alojamiento, es el momento de investigar el código más a fondo.
Para los fines de este tutorial, es seguro asumir que el código del contenedor y el código del sistema local son idénticos.
Node.js
Busca el origen del mensaje de error en el archivoindex.js
alrededor del número de línea que se indica en el seguimiento de pila que se muestra en los registros:
Python
Busca el origen del mensaje de error en el archivomain.py
alrededor del número de línea que se indica en el seguimiento de pila que se muestra en los registros:
Go
Busca el origen del mensaje de error en el archivo main.go
alrededor del número de línea que se indica en el seguimiento de pila que se muestra en los registros:
Java
Busca el origen del mensaje de error en el archivo App.java
alrededor del número de línea indicado en el seguimiento de pila que se muestra en los registros:
Al examinar este código, se llevan a cabo las siguientes acciones cuando no se define la variable de entorno NAME
:
- Se registra un error en Google Cloud Observability
- Se envía una respuesta de error HTTP
El problema se debe a que falta una variable, pero la causa principal es más específica: el cambio de código que añade la dependencia fija de una variable de entorno no incluye los cambios relacionados en los scripts de implementación ni en la documentación de los requisitos de tiempo de ejecución.
Corregir la causa principal
Ahora que hemos recogido el código y hemos identificado la posible causa principal, podemos tomar medidas para solucionarlo.
Comprueba si el servicio funciona localmente con el entorno
NAME
disponible:Ejecuta el contenedor de forma local con la variable de entorno añadida:
PORT=8080 && docker run --rm -e PORT=$PORT -p 9000:$PORT \ -e NAME="Local World!" \ gcr.io/PROJECT_ID/hello-service
En el navegador, ve a http://localhost:9000.
Verás el mensaje "Hello Local World!" en la página.
Modifica el entorno del servicio de Knative en ejecución para incluir esta variable:
Ejecuta el comando de actualización de servicios con el parámetro
--update-env-vars
para añadir una variable de entorno:gcloud run services update hello-service \ --update-env-vars NAME=Override
Espera unos segundos mientras Knative Serving crea una nueva revisión basada en la revisión anterior con la nueva variable de entorno añadida.
Confirma que el servicio ya funciona:
- En el navegador, ve a la URL del servicio de Knative.
- Verás el mensaje "Hello Override!" en la página.
- Comprueba que no aparezcan mensajes ni errores inesperados en Cloud Logging.
Mejorar la velocidad de solución de problemas en el futuro
En este ejemplo de problema de producción, el error estaba relacionado con la configuración operativa. Se han realizado cambios en el código para minimizar el impacto de este problema en el futuro.
- Mejorar el registro de errores para que incluya detalles más específicos.
- En lugar de devolver un error, haz que el servicio vuelva a un valor predeterminado seguro. Si el uso de un valor predeterminado representa un cambio en la funcionalidad normal, utiliza un mensaje de advertencia para monitorizarlo.
Vamos a eliminar la variable de entorno NAME
como dependencia obligatoria.
Quita el código de gestión de
NAME
:Node.js
Python
Go
Java
Añade un código nuevo que defina un valor alternativo:
Node.js
Python
Go
Java
Prueba de forma local volviendo a compilar y ejecutar el contenedor en los casos de configuración afectados:
Node.js
docker build --tag gcr.io/PROJECT_ID/hello-service .
Python
docker build --tag gcr.io/PROJECT_ID/hello-service .
Go
docker build --tag gcr.io/PROJECT_ID/hello-service .
Java
mvn compile jib:build
Comprueba que la variable de entorno
NAME
sigue funcionando:PORT=8080 && docker run --rm -e $PORT -p 9000:$PORT \ -e NAME="Robust World" \ gcr.io/PROJECT_ID/hello-service
Confirma que el servicio funciona sin la variable
NAME
:PORT=8080 && docker run --rm -e $PORT -p 9000:$PORT \ gcr.io/PROJECT_ID/hello-service
Si el servicio no devuelve ningún resultado, confirma que al eliminar el código en el primer paso no se han eliminado líneas adicionales, como las que se usan para escribir la respuesta.
Para ello, vuelve a la sección Implementar el código.
Cada despliegue en un servicio crea una revisión nueva y empieza a servir tráfico automáticamente cuando está listo.
Para borrar las variables de entorno definidas anteriormente, sigue estos pasos:
gcloud run services update hello-service --clear-env-vars
Añade la nueva función del valor predeterminado a la cobertura de pruebas automatizadas del servicio.
Buscar otros problemas en los registros
Es posible que veas otros problemas en el visualizador de registros de este servicio. Por ejemplo, una llamada al sistema no compatible aparecerá en los registros como "Container Sandbox Limitation".
Por ejemplo, los servicios de Node.js a veces generan este mensaje de registro:
Container Sandbox Limitation: Unsupported syscall statx(0xffffff9c,0x3e1ba8e86d88,0x0,0xfff,0x3e1ba8e86970,0x3e1ba8e86a90). Please, refer to https://gvisor.dev/c/linux/amd64/statx for more information.
En este caso, la falta de compatibilidad no afecta al servicio de ejemplo hello-service.
Limpieza
Puedes eliminar los recursos creados para este tutorial para evitar que se te cobren.
Eliminar recursos del tutorial
Elimina el servicio de Knative que has desplegado en este tutorial:
gcloud run services delete SERVICE-NAME
Donde SERVICE-NAME es el nombre del servicio que has elegido.
También puedes eliminar servicios de Knative Serving desde la consola deGoogle Cloud :
Elimina las configuraciones predeterminadas de gcloud que has añadido durante la configuración del tutorial:
gcloud config unset run/platform gcloud config unset run/cluster gcloud config unset run/cluster_location
Elimina la configuración del proyecto:
gcloud config unset project
Elimina otros recursos de Google Cloud que hayas creado en este tutorial:
- Elimina la imagen de contenedor llamada
gcr.io/<var>PROJECT_ID</var>/hello-service
de Container Registry. - Si has creado un clúster para este tutorial, elimínalo.
- Elimina la imagen de contenedor llamada
Siguientes pasos
- Consulta más información sobre cómo usar Cloud Logging para obtener información valiosa sobre el comportamiento de producción.
- Consulta más información sobre la solución de problemas de Knative serving.
- Consulta arquitecturas de referencia, diagramas y prácticas recomendadas sobre Google Cloud. Consulta nuestro Centro de arquitectura de Cloud.