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

Cloud RunFirebase Hosting 配对,以生成并提供您的 动态内容或构建 REST API 作为微服务。

借助Cloud Run,您可以: 部署封装在容器映像中的应用然后,使用 Firebase Hosting,您可以定向 HTTPS 请求以触发您的 容器化应用

  • Cloud Run 支持 多种语言 (包括 Go、Node.js、Python 和 Java),让您可以灵活地使用 编程语言和框架
  • Cloud Run 自动和水平扩缩 用于处理收到的请求,然后在需要处理时 需求也会随之降低
  • 您只需为在处理请求期间消耗的 CPU、内存和网络资源付费

例如,与以下产品集成的 Cloud Run 的使用场景和示例: Firebase Hosting,请访问我们的 无服务器概览


本指南将介绍如何执行以下操作:

  1. 编写一个简单的 Hello World 应用
  2. 将应用容器化并将其上传到 Container Registry
  3. 将容器映像部署到 Cloud Run
  4. Hosting 请求定向到您的容器化应用

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

准备工作

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

为您的项目设置结算功能

Cloud Run 提供免费用量配额, 但仍需设置 Cloud Billing 个账号 与您的 Firebase 项目关联,以便使用或试用 Cloud Run

启用 API 并安装 SDK

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

    1. 打开 Cloud Run API 页面

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

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

  2. 安装并初始化 Cloud SDK。

  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("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

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.19.2"
      }
    }
    
  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

Java

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

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

  2. 在控制台中,依次使用 cURL 和 unzip 命令,新建一个空 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 的文件,对示例应用进行容器化。将以下内容复制到您的文件中。

    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. 运行以下命令,使用 Cloud Build 构建容器映像 命令:

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

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

容器映像现在存储在 Container Registry 中,如果存在以下情况,可以重复使用: 。

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

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

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

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

  2. 当系统提示时:

为获得最佳性能,请使用以下区域将您的 Cloud Run 服务与 Hosting 共置:

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

以下版本支持从 Hosting 重写 Cloud Run 以下区域:

  • 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. 请等待部署完成。成功完成时,命令行会显示服务网址。例如:https://helloworld-RANDOM_HASH-us-central1.a.run.app

  2. 在网络浏览器中打开该服务网址,访问部署的容器。

下一步将向您介绍如何Firebase Hosting网址,以便为您的广告资源生成动态内容。 Firebase 托管的网站。

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

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

以下示例展示了如何定向来自网页的所有请求 /helloworld 触发Hosting网站的启动和运行 您的 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)
          "pinTag": true              // optional (see note below)
        }
      } ]
    }
    
  4. 在项目的根目录中运行以下命令,将 Hosting 的配置部署到您的网站:

    firebase deploy --only hosting

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

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

  • 任何关联的自定义网域
    CUSTOM_DOMAIN/

访问 Hosting 配置页面 有关重写规则的更多详情。您可以 还可以了解 响应的优先顺序 Hosting

在本地测试

开发期间,您可以在本地运行和测试容器映像。对于 请访问 Cloud Run 文档

后续步骤