转到控制台

使用 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 Run API 以及安装 gcloud 命令行工具。

为您的项目设置结算功能

Cloud Run 确实提供免费使用配额,但您仍必须拥有与您的 Firebase 项目关联的结算帐号才能使用或试用 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。

安装 gcloud beta 组件

  1. 运行以下命令以安装 gcloud beta 组件:

    gcloud components install beta
  2. 更新组件:

    gcloud components update
  3. 检查是否将 gcloud 工具配置为关联到正确的项目:

    gcloud config list

第 1 步:编写示例应用

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

Go

  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("Hello world received a request.")
    	target := os.Getenv("TARGET")
    	if target == "" {
    		target = "World"
    	}
    	fmt.Fprintf(w, "Hello %s!\n", target)
    }
    
    func main() {
    	log.Print("Hello world sample started.")
    
    	http.HandleFunc("/", handler)
    
    	port := os.Getenv("PORT")
    	if port == "" {
    		port = "8080"
    	}
    
    	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
    }
    

    此代码会创建一个基本网络服务器以侦听由 PORT 环境变量定义的端口。

您的应用已编写完毕,可以进行容器化并上传到 Container Registry。

Node.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}!`);
    });
    
    const port = process.env.PORT || 8080;
    app.listen(port, () => {
      console.log('Hello world listening on port', port);
    });
    

    此代码会创建一个基本网络服务器以侦听由 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)))
    

    此代码会创建一个基本网络服务器以侦听由 PORT 环境变量定义的端口。

您的应用已编写完毕,可以进行容器化并上传到 Container Registry。

Java

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

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

  2. 在控制台中,使用 cURL 然后使用 unzip 命令新建一个空网页项目:

    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 message;
    
    	@RestController
    	class HelloworldController {
    		@GetMapping("/")
    		String hello() {
    			return "Hello " + message + "!";
    		}
    	}
    
    	public static void main(String[] args) {
    		SpringApplication.run(HelloworldApplication.class, args);
    	}
    }
    

    此代码会创建一个基本网络服务器以侦听由 PORT 环境变量定义的端口。

您的应用已编写完毕,可以进行容器化并上传到 Container Registry。

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

  1. 在源文件所在的目录中新建一个名为 Dockerfile 的文件,对示例应用进行容器化。将以下内容复制到您的文件中。

    Go

    # Use the offical Golang image to create a build artifact.
    # This is based on Debian and sets the GOPATH to /go.
    # https://hub.docker.com/_/golang
    FROM golang as builder
    
    # Copy local code to the container image.
    WORKDIR /go/src/github.com/knative/docs/helloworld
    COPY . .
    
    # Build the outyet command inside the container.
    # (You may fetch or manage dependencies here,
    # either manually or with a tool like "godep".)
    RUN CGO_ENABLED=0 GOOS=linux go build -v -o helloworld
    
    # Use a Docker multi-stage build to create a lean production image.
    # https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
    FROM alpine
    
    # Copy the binary to the production image from the builder stage.
    COPY --from=builder /go/src/github.com/knative/docs/helloworld/helloworld /helloworld
    
    # Service must listen to $PORT environment variable.
    # This default value facilitates local development.
    ENV PORT 8080
    
    # Run the web service on container startup.
    CMD ["/helloworld"]
    

    Node.js

    # Use the official Node.js 10 image.
    # https://hub.docker.com/_/node
    FROM node:10
    
    # 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 . .
    
    # Service must listen to $PORT environment variable.
    # This default value facilitates local development.
    ENV PORT 8080
    
    # Run the web service on container startup.
    CMD [ "npm", "start" ]
    

    Python

    # Use the official Python image.
    # https://hub.docker.com/_/python
    FROM python
    
    # Copy local code to the container image.
    ENV APP_HOME /app
    WORKDIR $APP_HOME
    COPY . .
    
    # Install production dependencies.
    RUN pip install Flask gunicorn
    
    # Service must listen to $PORT environment variable.
    # This default value facilitates local development.
    ENV PORT 8080
    
    # 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 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
    
    # Service must listen to $PORT environment variable.
    # This default value facilitates local development.
    ENV PORT 8080
    
    # Run the web service on container startup.
    CMD ["java","-Djava.security.egd=file:/dev/./urandom","-Dserver.port=${PORT}","-jar","/helloworld.jar"]
    

  2. 从包含 Dockerfile 的目录下运行以下命令,使用 Cloud Build 构建容器映像:

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

    成功完成后,您将看到一条包含映像名称
    (gcr.io/projectID/helloworld) 的 SUCCESS 消息。

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

请注意,您可以使用本地安装的 Docker 版本在本地构建容器,而不是使用 Cloud Build。

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

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

    gcloud beta run deploy --image gcr.io/projectID/helloworld

  2. 当系统提示时:

  3. 请稍等片刻,直到部署完成为止。成功完成时,命令行会显示服务网址。例如:

    https://helloworld-random_hash-us-central1.a.run.app
  4. 通过在网络浏览器中打开该服务网址来访问已部署的容器。

下一步会向您介绍如何通过 Firebase 托管网址访问此容器化应用,以便它可以为您的 Firebase 托管网站生成动态内容。

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

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

以下示例说明如何定向您的托管网站上 /helloworld 页面收到的所有请求,以触发 helloworld 容器实例的启动和运行。

  1. 请确保:

    • 您安装了最新版本的 Firebase CLI(运行 npm install -g firebase-tools)。

    • 您已初始化 Firebase 托管。

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

  2. 打开 firebase.json 文件

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

    "hosting": {
      // ...
    
      // Add the "rewrites" attribute within "hosting"
      "rewrites": [ {
        "source": "**",
        "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

现在,您可以通过以下网址访问容器:

  • 您的 Firebase 子网域:projectID.web.app/helloworldprojectID.firebaseapp.com/helloworld

  • 任何已关联的自定义网域custom-domain/helloworld

如需详细了解重写规则,请访问托管配置页面。您还可以了解针对各种托管配置的响应的优先级顺序

在本地测试

在开发期间,您可以在本地运行和测试容器映像。有关详细说明,请访问 Cloud Run 文档

后续步骤