Entrega contenido dinámico y aloja microservicios con Cloud Run

Usa Cloud Run con Firebase Hosting para generar y entregar tu contenido dinámico o crear APIs de REST como microservicios.

Cloud Run te permite implementar aplicaciones empaquetadas en imágenes de contenedor. Luego, puedes usar Firebase Hosting a fin de dirigir las solicitudes HTTPS para activar una app alojada en contenedores.

  • Cloud Run admite varios lenguajes (como Go, Node.js, Python y Java), por lo que puedes usar el framework y lenguaje de programación que prefieras.
  • Cloud Run escala la imagen de contenedor de manera automática y horizontal para controlar las solicitudes que se reciben 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.

Consulta la descripción general de la computación sin servidores para ver muestras y casos de uso de ejemplo de Cloud Run integrado en Firebase Hosting.


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

  1. Escribir una app de Hello World sencilla
  2. Alojar una app en un contenedor y subirla a Container Registry
  3. Implementar la imagen del contenedor en Cloud Run
  4. Dirigir las solicitudes de Hosting a tu app alojada en un contenedor

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, como configurar una cuenta de Facturación de Cloud, habilitar la API 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. No obstante, debes tener una cuenta de Facturación de Cloud 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 pida, 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 lenguajes además de los 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.

La app está 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.18.2"
      }
    }
    
  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.

La app está 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.

La app está 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.

La app está 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. Usa Cloud Build a fin de compilar la imagen de contenedor. Para ello, ejecuta el siguiente 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).

Ahora la imagen del contenedor se almacena en Container Registry y la puedes volver a usar si lo deseas.

Ten en cuenta que, en lugar de Cloud Build, puedes usar una versión de Docker instalada de manera local 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 de Cloud Run con Hosting por medio de las siguientes regiones:

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

Las reescrituras en Cloud Run desde Hosting 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 la app alojada en contenedores desde una URL de Firebase Hosting a fin de que esta pueda generar contenido dinámico para el sitio alojado en Firebase.

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

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

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

  1. Asegúrate de cumplir con estas condiciones:

    Consulta la guía de introducción de Hosting para obtener instrucciones detalladas sobre cómo instalar la CLI e inicializar 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 obtener más detalles sobre las reglas de reescritura. También puedes obtener información sobre el orden de prioridad de las respuestas de varias configuraciones de Hosting.

Realiza pruebas locales

Durante el desarrollo, puedes ejecutar y probar la imagen del contenedor de manera local. Consulta la documentación de Cloud Run para obtener instrucciones detalladas.

Próximos pasos