
Las tarifas de transferencia de datos son uno de los ítems más molestos en una factura de nube. Cuando dos servicios se comunican a través de internet público, pagas por cada byte. El soporte de múltiples contenedores de Google Cloud Run cambia esto para arquitecturas estilo sidecar: los contenedores dentro de la misma aplicación Cloud Run comparten un namespace de red y se comunican a través de localhost, por lo que ese tráfico no se contabiliza como transferencia externa de datos.
Entendiendo el Soporte de Múltiples Contenedores en Google Cloud Run
Cloud Run siempre ha sido directo para deployments de contenedor único. El soporte de múltiples contenedores agrega la capacidad de ejecutar contenedores sidecar junto al principal, todos compartiendo el mismo entorno. El detalle clave: al compartir un namespace de red, las solicitudes entre contenedores nunca salen del host. Esto elimina el costo de transferencia de datos y reduce la latencia en comparación con llamar a un servicio separado.
Beneficios de las Aplicaciones Multi-Contenedor en Cloud Run
- Reducción de costos: el tráfico entre servicios a través de
localhostno genera tarifas de transferencia de datos por internet. - Arquitectura simplificada: servicios auxiliares como agentes de logging o procesadores específicos de lenguaje se ejecutan junto al contenedor principal sin un deployment separado.
- Superficie de ataque reducida: menos llamadas de red externas.
- Menor latencia en la comunicación entre procesos.
Reduciendo los Costos de Transferencia de Datos
En una configuración estándar de microservicios, cada llamada entre servicios cruza la red, generando latencia y costos de transferencia. Los deployments multi-contenedor eliminan ambos para el tráfico entre contenedores dentro de la misma aplicación.
Cómo los Deployments Multi-Contenedor Reducen Costos
Los contenedores comparten la misma interfaz localhost. El tráfico entre ellos nunca atraviesa internet público, por lo que no aparece en tu factura de transferencia de datos. Los recursos compartidos también implican menor overhead de memoria y CPU en comparación con ejecutar servicios Cloud Run separados.
Construyendo una Aplicación Multi-Contenedor: Ejemplo con Node.js y Python
El escenario: un contenedor Node.js maneja solicitudes HTTP de los usuarios y llama a un contenedor Python para el procesamiento de datos. Al estar co-localizados, esa llamada interna no genera ningún costo de transferencia.
Visión General de la Aplicación
El contenedor Node.js sirve la interfaz web y gestiona las solicitudes de los usuarios. El contenedor Python realiza el procesamiento de datos, como inferencias de machine learning o análisis de datos.
Configurando el Contenedor Node.js
// server.js
const express = require('express');
const axios = require('axios');
const app = express();
const PORT = process.env.PORT || 8080;
app.get('/', async (req, res) => {
try {
// Call the Python service
const response = await axios.get('http://localhost:5000/process');
res.send(`Python Service Response: ${response.data}`);
} catch (error) {
res.status(500).send('Error communicating with Python service');
}
});
app.listen(PORT, () => {
console.log(`Node.js server listening on port ${PORT}`);
});
Dockerfile para el Contenedor Node.js:
# Node.js Dockerfile
FROM node:14
# Create app directory
WORKDIR /usr/src/app
# Install app dependencies
COPY package*.json ./
RUN npm install
# Bundle app source
COPY . .
# Expose the port
EXPOSE 8080
# Run the application
CMD [ "node", "server.js" ]
package.json:
{
"name": "nodejs-container",
"version": "1.0.0",
"description": "Node.js container for multi-container Cloud Run application",
"main": "server.js",
"dependencies": {
"axios": "^0.21.1",
"express": "^4.17.1"
}
}
Configurando el Contenedor Python
# app.py
from flask import Flask
app = Flask(__name__)
@app.route('/process')
def process():
# Perform data processing
return 'Data processed successfully by Python service'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Dockerfile para el Contenedor Python:
# Python Dockerfile
FROM python:3.8-slim
# Install dependencies
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
# Copy the application
COPY . /app
WORKDIR /app
# Expose the port
EXPOSE 5000
# Run the application
CMD [ "python", "app.py" ]
requirements.txt:
flask
Configurando el Servicio Cloud Run
Define un archivo service.yaml que liste ambos contenedores:
# service.yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: multi-container-app
spec:
template:
spec:
containers:
- image: gcr.io/your-project-id/nodejs-container
ports:
- containerPort: 8080
- image: gcr.io/your-project-id/python-container
ports:
- containerPort: 5000
Reemplaza your-project-id con el ID real de tu proyecto de Google Cloud. Ambos contenedores comparten el mismo namespace de red, por lo que el servidor Node.js puede alcanzar el servicio Python en http://localhost:5000.
Desplegando en Google Cloud Run
1. Build y Push de los Contenedores
Build y push del contenedor Node.js:
docker build -t gcr.io/your-project-id/nodejs-container .
docker push gcr.io/your-project-id/nodejs-container
Build y push del contenedor Python:
docker build -t gcr.io/your-project-id/python-container .
docker push gcr.io/your-project-id/python-container
2. Despliegue del Servicio
gcloud run services replace service.yaml
Este comando crea o reemplaza el servicio Cloud Run definido en service.yaml.
Verificando la Aplicación
Tras el despliegue, abre la URL del servicio Cloud Run. La aplicación Node.js llama al contenedor Python internamente y devuelve:
Python Service Response: Data processed successfully by Python service
Ventajas de Este Enfoque
- Menor latencia entre contenedores en comparación con llamar a un servicio Cloud Run separado a través de internet.
- Sin cargos de transferencia de datos externa para la comunicación entre contenedores.
- Una sola unidad de deployment para gestionar en lugar de dos servicios separados.
Casos de Uso para Aplicaciones Multi-Contenedor
Los deployments multi-contenedor en Cloud Run encajan bien para patrones sidecar (agentes de logging, proxies de autenticación, recolectores de monitoreo), procesamiento en lenguajes mixtos (un modelo de ML en Python junto a una API Node.js) y la descomposición gradual de un monolito donde se desea co-localización sin el overhead total de microservicios.
Buenas Prácticas
- Establece límites de recursos para cada contenedor para evitar que uno agote los del otro.
- Implementa health checks en ambos contenedores para que Cloud Run pueda detectar fallos con precisión.
- Usa un formato de logging unificado para que los logs de ambos contenedores aparezcan de forma coherente en Cloud Logging.
Posibles Desafíos
Las configuraciones multi-contenedor añaden complejidad al deployment. El orden de inicio de los contenedores no está garantizado, por lo que el servidor Node.js debe manejar el caso en que el servicio Python aún no esté disponible. Las asignaciones de puertos no deben entrar en conflicto. Y si ambos contenedores necesitan la misma variable de entorno con valores distintos, deberás ser explícito en el service.yaml.
Conclusión
El Cloud Run multi-contenedor elimina el costo de transferencia para servicios que de otro modo se comunicarían a través de la red. El ejemplo con Node.js y Python descrito arriba es sencillo de desplegar y elimina de inmediato los cargos de transferencia de datos entre servicios. Para arquitecturas donde dos servicios se comunican con frecuencia, esto representa un ahorro real, y el modelo de red compartida escala hacia patrones sidecar más complejos sin cambiar la forma en que los contenedores se comunican entre sí.