Entrega contenido dinámico y aloja microservicios con Cloud Run

Vincula Cloud Run con Firebase Hosting para generar y publicar tu contenido dinámico o compilar APIs de REST como microservicios.

Con Cloud Run, puedes implementar una aplicación empaquetada en una imagen de contenedor. Luego, con Firebase Hosting, puedes dirigir las solicitudes HTTPS para activar tu app alojada en contenedores.

  • Cloud Run admite varios idiomas (incluidos Go, Node.js, Python y Java), lo que te da la flexibilidad de usar el lenguaje de programación y el framework que prefieras.
  • Cloud Run escala de forma automática y horizontal tu imagen de contenedor para manejar las solicitudes recibidas y, luego, reduce la escala verticalmente cuando disminuye la demanda.
  • Solo debes pagar por la CPU, la memoria y las herramientas de redes que se utilicen durante la administración de la solicitud.

Para ver casos de uso y muestras de ejemplo de Cloud Run integrado en Firebase Hosting, visita nuestra descripción general de la computación sin servidores.


En esta guía, se indica cómo realizar las siguientes acciones:

  1. Escribir una app de Hello World sencilla
  2. Organiza una app en contenedores y súbela a Container Registry
  3. Implementa la imagen de contenedor en Cloud Run
  4. Dirige las solicitudes de Hosting hacia tu app alojada en contenedores

Recuerda que si quieres mejorar el rendimiento de la entrega de contenido dinámico, puedes configurar la caché.

Antes de comenzar

Antes de usar Cloud Run, debes completar algunas tareas iniciales. incluida la configuración de una cuenta de Cloud Billing, la habilitación de Cloud Run e instalar la herramienta de línea de comandos de gcloud

Configura la facturación para el proyecto

Cloud Run ofrece una cuota de uso gratuito, pero igualmente debes tener una Cuenta de Cloud Billing asociada con tu proyecto de Firebase para usar o probar Cloud Run.

Habilita la API e instala el SDK

  1. Habilita la API de Cloud Run en la Consola de APIs de Google:

    1. Abre la página de la API de Cloud Run en la consola de APIs de Google.

    2. Cuando se te solicite, selecciona tu proyecto de Firebase.

    3. Haz clic en Habilitar en la página de la API de Cloud Run.

  2. Instala e inicializa el SDK de Cloud.

  3. Asegúrate de que la herramienta gcloud esté configurada para el proyecto adecuado:

    gcloud config list

Paso 1: Escribe la aplicación de muestra

Ten en cuenta que Cloud Run admite muchos otros idiomas además de los lenguajes que se muestran en el siguiente ejemplo.

Go

  1. Crea un directorio nuevo llamado helloworld-go y, luego, usa el comando de cambio de directorio en él, de la siguiente manera:

    mkdir helloworld-go
    cd helloworld-go
  2. Crea un archivo nuevo llamado helloworld.go y, luego, agrega el siguiente código:

    package main
    
    import (
    	"fmt"
    	"log"
    	"net/http"
    	"os"
    )
    
    func handler(w http.ResponseWriter, r *http.Request) {
    	log.Print("helloworld: received a request")
    	target := os.Getenv("TARGET")
    	if target == "" {
    		target = "World"
    	}
    	fmt.Fprintf(w, "Hello %s!\n", target)
    }
    
    func main() {
    	log.Print("helloworld: starting server...")
    
    	http.HandleFunc("/", handler)
    
    	port := os.Getenv("PORT")
    	if port == "" {
    		port = "8080"
    	}
    
    	log.Printf("helloworld: listening on port %s", port)
    	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
    }
    

    Con este código se crea un servidor web básico que recibe datos en el puerto definido por la variable de entorno PORT.

Tu app está terminada, lista para alojarla en contenedores y subirla a Container Registry

Node.js

  1. Crea un directorio nuevo llamado helloworld-nodejs y, luego, usa el comando de cambio de directorio en él, de la siguiente manera:

    mkdir helloworld-nodejs
    cd helloworld-nodejs
  2. Crea un archivo package.json con el siguiente contenido:

    {
      "name": "knative-serving-helloworld",
      "version": "1.0.0",
      "description": "Simple hello world sample in Node",
      "main": "index.js",
      "scripts": {
        "start": "node index.js"
      },
      "author": "",
      "license": "Apache-2.0",
      "dependencies": {
        "express": "^4.20.0"
      }
    }
    
  3. Crea un archivo nuevo llamado index.js y, luego, agrega el siguiente código:

    const express = require('express');
    const app = express();
    
    app.get('/', (req, res) => {
      console.log('Hello world received a request.');
    
      const target = process.env.TARGET || 'World';
      res.send(`Hello ${target}!\n`);
    });
    
    const port = process.env.PORT || 8080;
    app.listen(port, () => {
      console.log('Hello world listening on port', port);
    });
    

    Con este código se crea un servidor web básico que recibe datos en el puerto definido por la variable de entorno PORT.

Tu app está terminada, lista para alojarla en contenedores y subirla a Container Registry

Python

  1. Crea un directorio nuevo llamado helloworld-python y, luego, usa el comando de cambio de directorio en él, de la siguiente manera:

    mkdir helloworld-python
    cd helloworld-python
  2. Crea un archivo nuevo llamado app.py y, luego, agrega el siguiente código:

    import os
    
    from flask import Flask
    
    app = Flask(__name__)
    
    @app.route('/')
    def hello_world():
        target = os.environ.get('TARGET', 'World')
        return 'Hello {}!\n'.format(target)
    
    if __name__ == "__main__":
        app.run(debug=True,host='0.0.0.0',port=int(os.environ.get('PORT', 8080)))
    

    Con este código se crea un servidor web básico que recibe datos en el puerto definido por la variable de entorno PORT.

Tu app está terminada y lista para alojarla en contenedores y subirla a Container Registry

Java

  1. Instala Java SE 8 (o un JDK más reciente) y CURL.

    Ten en cuenta que solo debemos realizar lo anterior para crear el proyecto web nuevo en el próximo paso. El Dockerfile, que se describe más adelante, cargará todas las dependencias en el contenedor.

  2. En la consola, usa cURL para crear un proyecto web nuevo y vacío y, luego, descomprime los comandos:

    curl https://start.spring.io/starter.zip \
        -d dependencies=web \
        -d name=helloworld \
        -d artifactId=helloworld \
        -o helloworld.zip
    unzip helloworld.zip

    Se creará un proyecto de SpringBoot.

  3. Para actualizar la clase SpringBootApplication en src/main/java/com/example/helloworld/HelloworldApplication.java, agrega un @RestController para manejar la asignación de / y, también, agrega un campo @Value para proporcionar la variable de entorno TARGET, de la siguiente manera:

    package com.example.helloworld;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @SpringBootApplication
    public class HelloworldApplication {
    
      @Value("${TARGET:World}")
      String target;
    
      @RestController
      class HelloworldController {
        @GetMapping("/")
        String hello() {
          return "Hello " + target + "!";
        }
      }
    
      public static void main(String[] args) {
        SpringApplication.run(HelloworldApplication.class, args);
      }
    }
    

    Con este código se crea un servidor web básico que recibe datos en el puerto definido por la variable de entorno PORT.

Tu app está terminada y lista para alojarla en contenedores y subirla a Container Registry

Paso 2: Aloja una app en un contenedor y súbela a Container Registry

  1. Crea un archivo nuevo llamado Dockerfile en el mismo directorio que los archivos de origen para alojar la app de ejemplo en contenedores. Copia el siguiente contenido en el archivo.

    Go

    # Use the official Golang image to create a build artifact.
    # This is based on Debian and sets the GOPATH to /go.
    FROM golang:latest as builder
    
    ARG TARGETOS
    ARG TARGETARCH
    
    # Create and change to the app directory.
    WORKDIR /app
    
    # Retrieve application dependencies using go modules.
    # Allows container builds to reuse downloaded dependencies.
    COPY go.* ./
    RUN go mod download
    
    # Copy local code to the container image.
    COPY . ./
    
    # Build the binary.
    # -mod=readonly ensures immutable go.mod and go.sum in container builds.
    RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -mod=readonly -v -o server
    
    # Use the official Alpine image for a lean production container.
    # https://hub.docker.com/_/alpine
    # https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
    FROM alpine:3
    RUN apk add --no-cache ca-certificates
    
    # Copy the binary to the production image from the builder stage.
    COPY --from=builder /app/server /server
    
    # Run the web service on container startup.
    CMD ["/server"]
    

    Node.js

    # Use the official lightweight Node.js 12 image.
    # https://hub.docker.com/_/node
    FROM node:12-slim
    
    # Create and change to the app directory.
    WORKDIR /usr/src/app
    
    # Copy application dependency manifests to the container image.
    # A wildcard is used to ensure both package.json AND package-lock.json are copied.
    # Copying this separately prevents re-running npm install on every code change.
    COPY package*.json ./
    
    # Install production dependencies.
    RUN npm install --only=production
    
    # Copy local code to the container image.
    COPY . ./
    
    # Run the web service on container startup.
    CMD [ "npm", "start" ]
    

    Python

    # Use the official lightweight Python image.
    # https://hub.docker.com/_/python
    FROM python:3.7-slim
    
    # Allow statements and log messages to immediately appear in the Knative logs
    ENV PYTHONUNBUFFERED True
    
    # Copy local code to the container image.
    ENV APP_HOME /app
    WORKDIR $APP_HOME
    COPY . ./
    
    # Install production dependencies.
    RUN pip install Flask gunicorn
    
    # Run the web service on container startup. Here we use the gunicorn
    # webserver, with one worker process and 8 threads.
    # For environments with multiple CPU cores, increase the number of workers
    # to be equal to the cores available.
    CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 app:app
    

    Java

    # Use the official maven/Java 8 image to create a build artifact: https://hub.docker.com/_/maven
    FROM maven:3.5-jdk-8-alpine as builder
    
    # Copy local code to the container image.
    WORKDIR /app
    COPY pom.xml .
    COPY src ./src
    
    # Build a release artifact.
    RUN mvn package -DskipTests
    
    # Use the Official OpenJDK image for a lean production stage of our multi-stage build.
    # https://hub.docker.com/_/openjdk
    # https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
    FROM openjdk:8-jre-alpine
    
    # Copy the jar to the production image from the builder stage.
    COPY --from=builder /app/target/helloworld-*.jar /helloworld.jar
    
    # Run the web service on container startup.
    CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/helloworld.jar"]
    

  2. Compila tu imagen de contenedor con Cloud Build ejecutando el siguiente comando comando desde el directorio que contiene el Dockerfile:

    gcloud builds submit --tag gcr.io/PROJECT_ID/helloworld

    Tras la ejecución satisfactoria del comando, verás el mensaje SUCCESS con el nombre de la imagen
    (gcr.io/PROJECT_ID/helloworld).

La imagen del contenedor ahora se almacena en Container Registry y se puede volver a usar si deseado.

Ten en cuenta que, en lugar de Cloud Build, puedes usar una versión instalada de forma local. de Docker para compilar el contenedor de forma local.

Paso 3: Implementa la imagen de contenedor en Cloud Run

  1. Realiza la implementación con el siguiente comando:

    gcloud run deploy --image gcr.io/PROJECT_ID/helloworld

  2. Realiza las siguientes acciones cuando se te solicite:

Para obtener el mejor rendimiento, coloca tu servicio Cloud Run con Hosting mediante las siguientes regiones:

  • us-west1
  • us-central1
  • us-east1
  • europe-west1
  • asia-east1

Las reescrituras de Hosting a Cloud Run se admiten en las siguientes regiones:

  • asia-east1
  • asia-east2
  • asia-northeast1
  • asia-northeast2
  • asia-northeast3
  • asia-south1
  • asia-south2
  • asia-southeast1
  • asia-southeast2
  • australia-southeast1
  • australia-southeast2
  • europe-central2
  • europe-north1
  • europe-southwest1
  • europe-west1
  • europe-west12
  • europe-west2
  • europe-west3
  • europe-west4
  • europe-west6
  • europe-west8
  • europe-west9
  • me-central1
  • me-west1
  • northamerica-northeast1
  • northamerica-northeast2
  • southamerica-east1
  • southamerica-west1
  • us-central1
  • us-east1
  • us-east4
  • us-east5
  • us-south1
  • us-west1
  • us-west2
  • us-west3
  • us-west4
  • us-west1
  • us-central1
  • us-east1
  • europe-west1
  • asia-east1
  1. Espera unos instantes a que finalice la implementación. Si la operación es exitosa, la línea de comandos mostrará la URL de servicio. Por ejemplo: https://helloworld-RANDOM_HASH-us-central1.a.run.app

  2. Abre la URL de servicio en un navegador web para visitar el contenedor implementado.

En el siguiente paso, se explica cómo acceder a esta aplicación alojada en contenedores desde un Firebase Hosting para que pueda generar contenido dinámico para tu Sitio alojado en Firebase.

Paso 4: Dirige las solicitudes de Hosting hacia la app alojada en contenedores

Con las reglas de reescritura, puedes dirigir solicitudes que coincidan con patrones específicos a un mismo destino.

En el siguiente ejemplo, se muestra cómo dirigir todas las solicitudes desde la página /helloworld en tu sitio Hosting para activar la inicialización y ejecución de tu instancia de contenedor helloworld.

  1. Asegúrate de cumplir con estas condiciones:

    Para obtener instrucciones detalladas sobre cómo instalar la CLI y cómo inicializar el entorno Hosting, consulta la Guía de introducción de Hosting

  2. Abre el archivo firebase.json.

  3. Agrega la siguiente configuración de rewrite en la sección hosting:

    "hosting": {
      // ...
    
      // Add the "rewrites" attribute within "hosting"
      "rewrites": [ {
        "source": "/helloworld",
        "run": {
          "serviceId": "helloworld",  // "service name" (from when you deployed the container image)
          "region": "us-central1",    // optional (if omitted, default is us-central1)
          "pinTag": true              // optional (see note below)
        }
      } ]
    }
  4. Implementa la configuración de Hosting en tu sitio. Para ello, ejecuta el siguiente comando desde la raíz del directorio del proyecto:

    firebase deploy --only hosting

Ahora se puede acceder al contenedor con las siguientes URLs:

  • Tus subdominios de Firebase:
    PROJECT_ID.web.app/ y PROJECT_ID.firebaseapp.com/

  • Cualquier dominio personalizado conectado:
    CUSTOM_DOMAIN/

Visita la página de configuración de Hosting para ver Obtén más información sobre las reglas de reescritura. Puedes aprenderás sobre la orden de prioridad de las respuestas para varias configuraciones de Hosting.

Realiza pruebas locales

Durante el desarrollo, puedes ejecutar y probar la imagen del contenedor de manera local. Para instrucciones detalladas, visita la Documentación de Cloud Run

Próximos pasos