En esta guía, se proporcionan las prácticas recomendadas para diseñar, implementar y probar un servicio de Cloud Run. Para obtener más sugerencias, consulta Migra un servicio existente.
Escribe servicios eficaces
En esta sección, se describen las prácticas recomendadas generales para diseñar y, también, implementar un servicio de Cloud Run.
Actividad en segundo plano
La actividad en segundo plano es todo lo que sucede después de que se entrega la respuesta HTTP. Para determinar si hay actividad en segundo plano no evidente en el servicio, revisa los registros para ver si hay algún registro después de la entrada de la solicitud HTTP.
Configura la CPU para que siempre se asigne a fin de usar actividades en segundo plano
Si deseas admitir actividades en segundo plano en tu servicio de Cloud Run, configura tu CPU de servicio de Cloud Run para que siempre se asigne a fin de que puedas ejecutar actividades en segundo plano fuera de las solicitudes. tienen acceso a la CPU
Evita las actividades en segundo plano si la CPU se asigna solo durante el procesamiento de la solicitud
Si necesitas configurar el servicio para asignar CPU solo durante el procesamiento de solicitudes, cuando el servicio de Cloud Run termine de controlar una solicitud, se inhabilitará el acceso de la instancia a la CPU. o muy limitada. No debes iniciar subprocesos o rutinas en segundo plano que se ejecuten fuera del alcance de los controladores de solicitudes si usas este tipo de asignación de CPU.
Revisa el código para asegurarte de que todas las operaciones asíncronas finalicen antes de entregar la respuesta.
La ejecución de subprocesos en segundo plano con este tipo de asignación de CPU puede provocar un comportamiento inesperado porque las solicitudes posteriores a la misma instancia de contenedor reanudan cualquier actividad en segundo plano suspendida.
Borra archivos temporales
En el entorno de Cloud Run, el almacenamiento en disco es un sistema de archivos en la memoria. Los archivos escritos en el disco consumen memoria disponible para el servicio y pueden persistir entre invocaciones. Si no se borran, es posible que se produzca un error de memoria insuficiente y un inicio en frío posterior.
Informa errores
Controla todas las excepciones y no permitas que el servicio falle en caso de errores. Una falla genera un inicio en frío mientras el tráfico se pone en cola para una instancia de reemplazo.
Consulta la guía de Error Reporting para obtener información sobre cómo informar errores de forma correcta.
Optimiza el rendimiento
Esta sección describe las recomendaciones para optimizar el rendimiento.
Inicia los contenedores con rapidez
Debido a que las instancias se escalan según sea necesario, su tiempo de inicio tiene un impacto en la latencia de tu servicio. Si bien Cloud Run separa el inicio de instancia del procesamiento de solicitudes, puede ocurrir que una solicitud deba esperar a que se inicie una nueva instancia. En particular, esto sucede cuando se escala desde cero. Esto se conoce como “inicio en frío”.
La rutina de inicio consta de los siguientes pasos:
- Descargar la imagen del contenedor (mediante la tecnología de transmisión de imágenes de contenedor de Cloud Run)
- Inicia el contenedor mediante la ejecución del comando entrypoint.
- Esperar a que el contenedor comience a escuchar en el puerto configurado.
Optimizar la velocidad de inicio del contenedor minimiza la latencia de procesamiento de solicitudes.
Usa el aumento de la CPU de inicio para reducir la latencia del inicio
Puedes habilitar el aumento de CPU de inicio para aumentar de forma temporal la asignación de CPU durante el inicio de la instancia a fin de reducir la latencia de inicio.
Usa instancias mínimas para reducir los inicios en frío
Puedes configurar instancias mínimas y simultaneidad para minimizar los inicios en frío. Por ejemplo, si usas un mínimo de instancias de 1, el servicio está listo para recibir hasta la cantidad de solicitudes simultáneas configuradas para el servicio sin necesidad de iniciar una instancia nueva.
Ten en cuenta que una solicitud que espera a que se inicie una instancia se mantendrá pendiente en una cola de la siguiente manera:
- Si se inician instancias nuevas, como durante un escalamiento horizontal, las solicitudes admitirán al menos el tiempo de inicio promedio de las instancias de contenedor de este servicio. Esto incluye el momento en que la solicitud inicia un escalamiento horizontal, como cuando escalas desde cero.
- Si el tiempo de inicio es inferior a 10 segundos, las solicitudes permanecerán hasta 10 segundos.
- Si no hay instancias en el proceso de inicio y la solicitud no inicia un escalamiento horizontal, las solicitudes se almacenarán hasta por 10 segundos.
Usa las dependencias de manera inteligente
Si usas un lenguaje dinámico con bibliotecas dependientes, como la importación de módulos en Node.js, el tiempo de carga de esos módulos se suma a la latencia de inicio.
Reduce la latencia de inicio de las siguientes maneras:
- Minimiza la cantidad y el tamaño de las dependencias para compilar un servicio optimizado.
- Carga de forma diferida el código que se usa con poca frecuencia, si tu lenguaje lo admite.
- Usa optimizaciones de carga de código como la optimización del cargador automático de composer de PHP.
Usa variables globales
En Cloud Run, no puedes suponer que el estado del servicio se conserva entre las solicitudes. Sin embargo, Cloud Run vuelve a usar las instancias individuales para entregar tráfico continuo, por lo que puedes declarar una variable en el permiso global a fin de permitir que el valor se vuelva a usar en invocaciones posteriores. No se puede saber con anticipación si alguna solicitud individual recibe el beneficio de esta reutilización.
También puedes almacenar objetos en la memoria caché si son costosos de volver a crear en cada solicitud de servicio. Esta migración de la lógica de la solicitud al permiso global da como resultado un mejor rendimiento.
Node.js
Python
Go
Java
Realiza una inicialización diferida de variables globales
La inicialización de las variables globales siempre ocurre durante el inicio, lo que aumenta el tiempo de inicio en frío. Usa la inicialización diferida para los objetos usados con poca frecuencia a fin de diferir el costo del tiempo y disminuir los tiempos de inicio en frío.
Node.js
Python
Go
Java
Usa un entorno de ejecución diferente
Es posible que experimentes tiempos de inicio más rápidos mediante un entorno de ejecución diferente.
Optimiza la simultaneidad
Las instancias de Cloud Run pueden entregar varias solicitudes de manera simultánea hasta una simultaneidad máxima configurable.
Esto es diferente de Cloud Run Functions, que usa concurrency = 1
.
Cloud Run ajusta de forma automática la simultaneidad hasta el máximo configurado.
La simultaneidad máxima predeterminada de 80 es una buena opción para muchas imágenes de contenedor. Sin embargo, debes hacer lo siguiente:
- Redúcela si tu contenedor no puede procesar muchas solicitudes simultáneas.
- Auméntala si tu contenedor puede manejar un gran volumen de solicitudes.
Ajusta la simultaneidad para tu servicio
La pila tecnológica y el uso de recursos compartidos, como variables y conexiones de bases de datos, pueden limitar la cantidad de solicitudes simultáneas que puede entregar cada instancia.
Para optimizar el servicio a fin de obtener la máxima simultaneidad estable, sigue estos pasos:
- Optimiza el rendimiento del servicio.
- Establece el nivel de compatibilidad de simultaneidad esperado en cualquier configuración de simultaneidad a nivel de código. No todas las technology stacks requieren esa configuración.
- Implementa el servicio.
- Configura la simultaneidad de Cloud Run para el servicio en igual o inferior a cualquier configuración a nivel de código. Si no hay una configuración a nivel de código, usa la simultaneidad esperada.
- Usa herramientas de prueba de carga que admitan una simultaneidad configurable. Debes confirmar que el servicio se mantiene estable con la carga y la simultaneidad esperadas.
- Si el servicio funciona mal, ve al paso 1 para mejorar el servicio o al paso 2 para reducir la simultaneidad. Si el servicio funciona bien, vuelve al paso 2 y aumenta la simultaneidad
Continúa iterando hasta encontrar la simultaneidad estable máxima.
Coincidencia entre memoria y simultaneidad
Cada solicitud que el servicio administra requiere cierta cantidad de memoria adicional. Por lo tanto, cuando aumentes o disminuyas la simultaneidad, asegúrate de ajustar también el límite de memoria.
Evita el estado global mutable
Si deseas aprovechar el estado global mutable en un contexto de simultaneidad, realiza pasos adicionales en el código para asegurarte de que esto se haga de forma segura. Para minimizar la contención, limita las variables globales a una inicialización única y vuelve a usarlas como se describió antes en Rendimiento.
Si usas variables globales mutables en un servicio que entrega varias solicitudes al mismo tiempo, asegúrate de usar bloqueos o exclusiones mutuas para evitar condiciones de carrera.
Equilibrio entre la capacidad de procesamiento, la latencia y el costo
Ajustar la configuración de solicitudes simultáneas máximas puede ayudarte a equilibrar la compensación entre la capacidad de procesamiento, la latencia y el costo de tu servicio.
En general, una configuración más baja de solicitudes simultáneas máximas genera una latencia y una capacidad de procesamiento más bajas por instancia. Con una cantidad máxima de solicitudes simultáneas más baja, menos solicitudes compiten por los recursos dentro de cada instancia y cada solicitud logra un mejor rendimiento. Sin embargo, como cada instancia puede entregar menos solicitudes a la vez, la capacidad de procesamiento por instancia es menor y el servicio necesita más instancias para entregar el mismo tráfico.
En el sentido opuesto, una configuración más alta de solicitudes simultáneas máximas generalmente genera una latencia y una capacidad de procesamiento más altas por instancia. Es posible que las solicitudes deban esperar el acceso a recursos como el ancho de banda de la CPU, la GPU y la memoria dentro de la instancia, lo que aumenta la latencia. Sin embargo, cada instancia puede procesar más solicitudes a la vez, de modo que el servicio necesita menos instancias en general para procesar el mismo tráfico.
Consideraciones de costo
La facturación de Cloud Run se realiza por tiempo de instancia. Si la CPU siempre se asigna, el tiempo de la instancia es la vida útil total de cada instancia. Si la CPU no siempre se asigna, el tiempo de la instancia es el tiempo que cada instancia dedica a procesar al menos una solicitud.
El impacto de la cantidad máxima de solicitudes simultáneas en la facturación depende de tu patrón de tráfico. Reducir la cantidad máxima de solicitudes simultáneas puede generar una factura más baja si la configuración más baja genera lo siguiente:
- Disminución de la latencia
- Instancias que completan su trabajo más rápido
- Las instancias se cierran más rápido, incluso si se requieren más instancias en total
Sin embargo, también es posible lo contrario: reducir la cantidad máxima de solicitudes simultáneas puede aumentar la facturación si el aumento en la cantidad de instancias no supera la reducción en el tiempo que se ejecuta cada instancia, debido a la latencia mejorada.
La mejor manera de optimizar la facturación es mediante pruebas de carga con diferentes parámetros de configuración de solicitudes simultáneas máximas para identificar el parámetro que genera el tiempo de instancia facturable más bajo, como se ve en la métrica de supervisión container/billable_instance_time.
Seguridad de contenedores
Se aplican muchas prácticas de seguridad de software de uso general a las aplicaciones en contenedores. Hay algunas prácticas que son específicas de los contenedores o que se alinean con su filosofía y arquitectura.
Para mejorar la seguridad de los contenedores, haz lo siguiente:
Usa imágenes base seguras y mantenidas de forma activa, como las imágenes base de Google o las imágenes oficiales de Docker Hub.
Aplica actualizaciones de seguridad a los servicios Para hacerlo, vuelve a compilar imágenes de contenedor con regularidad y vuelve a implementar los servicios.
Incluye en el contenedor solo lo necesario para ejecutar el servicio. El código, los paquetes y las herramientas adicionales son vulnerabilidades de seguridad potenciales. Consulta más arriba el impacto en el rendimiento relacionado.
Implementa un proceso de compilación determinista que incluya versiones específicas de software y bibliotecas. Esto evita que se incluya código sin verificar en el contenedor.
Configura el contenedor para que se ejecute como un usuario que no sea
root
con la declaración deUSER
de Dockerfile. Es posible que algunas imágenes de contenedor ya tengan un usuario específico configurado.Evita el uso de las funciones de versión preliminar con las políticas de la organización personalizadas.
Automatiza el análisis de seguridad
Habilita el análisis de vulnerabilidades para el análisis de seguridad de las imágenes de contenedor almacenadas en Artifact Registry.
Compila imágenes de contenedor mínimas
Es probable que las imágenes de contenedor grandes aumenten las vulnerabilidades de seguridad porque contienen más de lo que necesita el código.
Debido a la tecnología de transmisión de imágenes de contenedor de Cloud Run, el tamaño de la imagen de contenedor no afecta el tiempo de procesamiento de solicitudes ni el inicio en frío. Además, el tamaño de la imagen de contenedor no se considera en la memoria disponible del contenedor.
Para compilar un contenedor mínimo, considera trabajar con una imagen base eficiente, como las siguientes:
Ubuntu es más grande, pero es una imagen base de uso común con un entorno de servidor integrado más completo.
Si el servicio tiene un proceso de compilación con muchas herramientas, considera usar compilaciones de varias etapas para mantener el contenedor simple en el tiempo de ejecución.
Estos recursos proporcionan más información sobre la creación de imágenes de contenedores eficientes:
- Prácticas recomendadas de Kubernetes: Cómo y por qué crear imágenes de contenedor pequeñas
- 7 prácticas recomendadas para compilar contenedores