获取我们在 Firebase 峰会上发布的所有信息,了解 Firebase 可如何帮助您加快应用开发速度并满怀信心地运行应用。了解详情

使用 Cloud Run 提供动态内容和托管微服务

将 Cloud Run 与 Firebase 托管配对,以生成和提供动态内容或将 REST API 构建为微服务。

使用Cloud Run ,您可以部署打包在容器映像中的应用程序。然后,使用 Firebase 托管,您可以引导 HTTPS 请求来触发您的容器化应用。

  • Cloud Run 支持多种语言(包括 Go、Node.js、Python 和 Java),让您可以灵活地使用您选择的编程语言和框架。
  • Cloud Run 会自动横向扩展您的容器映像以处理收到的请求,然后在需求减少时进行缩减。
  • 只需为请求处理期间消耗的 CPU、内存和网络付费。

有关与 Firebase 托管集成的 Cloud Run 用例和示例,请访问我们的无服务器概述


本指南向您展示如何:

  1. 编写一个简单的 Hello World 应用程序
  2. 将应用容器化并将其上传到 Container Registry
  3. 将容器映像部署到 Cloud Run
  4. 将托管请求直接发送到您的容器化应用程序

请注意,为了提高提供动态内容的性能,您可以选择调整缓存设置

在你开始之前

在使用 Cloud Run 之前,您需要完成一些初始任务,包括设置 Cloud Billing 帐户、启用 Cloud Run API 以及安装gcloud命令行工具。

为您的项目设置帐单

Cloud Run 提供免费使用配额,但您仍然必须拥有与您的 Firebase 项目关联的Cloud Billing 帐号才能使用或试用 Cloud Run。

启用 API 并安装 SDK

  1. 在 Google API 控制台中启用 Cloud Run API:

    1. 在 Google API 控制台中打开Cloud Run API 页面

    2. 出现提示时,选择您的 Firebase 项目。

    3. 单击 Cloud Run API 页面上的启用

  2. 安装并初始化Cloud SDK。

  3. 检查是否为正确的项目配置了gcloud工具:

    gcloud config list

第 1 步:编写示例应用程序

请注意,除了以下示例中显示的语言之外,Cloud Run 还支持许多其他语言

  1. 创建一个名为helloworld-go的新目录,然后将目录更改为该目录:

    mkdir helloworld-go
    cd helloworld-go
  2. 创建一个名为helloworld.go的新文件,然后添加以下代码:

    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))
    }
    

    此代码创建一个基本的 Web 服务器,它侦听PORT环境变量定义的端口。

您的应用已完成并准备好进行容器化并上传到 Container Registry。

节点.js

  1. 创建一个名为helloworld-nodejs的新目录,然后将目录更改为该目录:

    mkdir helloworld-nodejs
    cd helloworld-nodejs
  2. 创建一个包含以下内容的package.json文件:

    {
      "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.16.4"
      }
    }
    
  3. 创建一个名为index.js的新文件,然后添加以下代码:

    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);
    });
    

    此代码创建一个基本的 Web 服务器,它侦听PORT环境变量定义的端口。

您的应用已完成并准备好进行容器化并上传到 Container Registry。

Python

  1. 创建一个名为helloworld-python的新目录,然后将目录更改为该目录:

    mkdir helloworld-python
    cd helloworld-python
  2. 创建一个名为app.py的新文件,然后添加以下代码:

    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)))
    

    此代码创建一个基本的 Web 服务器,它侦听PORT环境变量定义的端口。

您的应用已完成并准备好进行容器化并上传到 Container Registry。

爪哇

  1. 安装Java SE 8 或更高版本的 JDKCURL

    请注意,我们只需要在下一步创建新的 Web 项目时执行此操作。后面会介绍的 Dockerfile 会将所有依赖项加载到容器中。

  2. 在控制台中,使用 cURL 创建一个新的空 Web 项目,然后解压缩命令:

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

    这将创建一个 SpringBoot 项目。

  3. 更新src/main/java/com/example/helloworld/HelloworldApplication.java中的SpringBootApplication类,添加一个@RestController来处理/映射,并添加一个@Value字段来提供TARGET环境变量:

    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);
      }
    }
    

    此代码创建一个基本的 Web 服务器,它侦听PORT环境变量定义的端口。

您的应用已完成并准备好进行容器化并上传到 Container Registry。

第 2 步:将应用容器化并将其上传到 Container Registry

  1. 通过在与源文件相同的目录中创建一个名为Dockerfile的新文件来容器化示例应用程序。将以下内容复制到您的文件中。

    # Use the official Golang image to create a build artifact.
    # This is based on Debian and sets the GOPATH to /go.
    FROM golang:1.13 as builder
    
    # 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=linux 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"]
    

    节点.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
    

    爪哇

    # 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. 通过从包含 Dockerfile 的目录运行以下命令,使用 Cloud Build 构建容器映像:

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

    成功后,您将看到包含图像名称的 SUCCESS 消息
    ( gcr.io/ PROJECT_ID /helloworld )。

容器映像现在存储在 Container Registry 中,可以根据需要重复使用。

请注意,除了 Cloud Build,您可以使用本地安装的 Docker 版本在本地构建容器

第 3 步:将容器映像部署到 Cloud Run

  1. 使用以下命令进行部署:

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

  2. 提示时:

  3. 稍等片刻以完成部署。成功后,命令行会显示服务 URL。例如: https://helloworld- RANDOM_HASH -us-central1.a.run.app

  4. 通过在 Web 浏览器中打开服务 URL 来访问您部署的容器。

下一步将引导您了解如何从 Firebase 托管 URL访问此容器化应用,以便它可以为您的 Firebase 托管站点生成动态内容。

第 4 步:将托管请求定向到您的容器化应用程序

使用重写规则,您可以将匹配特定模式的请求定向到单个目标。

以下示例显示了如何引导来自托管站点上/helloworld页面的所有请求以触发helloworld容器实例的启动和运行。

  1. 确保:

    有关安装 CLI 和初始化 Hosting 的详细说明,请参阅 Hosting入门指南

  2. 打开你的firebase.json文件

  3. hosting部分下添加以下rewrite配置:

    "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)
        }
      } ]
    }
    
  4. 通过从项目目录的根目录运行以下命令,将托管配置部署到您的站点:

    firebase deploy

您的容器现在可以通过以下 URL 访问:

  • 您的 Firebase 子域:
    PROJECT_ID .web.app/PROJECT_ID .firebaseapp.com/

  • 任何连接的自定义域
    CUSTOM_DOMAIN /

有关重写规则的更多详细信息,请访问托管配置页面。您还可以了解各种托管配置的响应优先顺序

本地测试

在开发过程中,您可以在本地运行和测试您的容器镜像。如需详细说明,请访问Cloud Run 文档

下一步