使用 Cloud Run 提供動態內容並託管微服務

將 Cloud Run 與 Firebase Hosting 配對,以生成和提供動態內容或將 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。

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.18.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。

爪哇

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

    爪哇

    # 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

    成功後,您將看到包含圖像名稱的成功消息
    gcr.io/ PROJECT_ID /helloworld )。

容器映像現在存儲在 Container Registry 中,並且可以根據需要重新使用。

請注意,您可以使用本地安裝的 Docker 版本來本地構建容器,而不是 Cloud Build。

步驟 3 :將容器映像部署到 Cloud Run

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

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

  2. 提示時:

為了獲得最佳性能,請使用以下區域將您的 Cloud Run 服務與託管並置:

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

以下區域支持從託管重寫到 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. 稍等片刻,讓部署完成。成功後,命令行將顯示服務 URL。例如: https://helloworld- RANDOM_HASH -us-central1.a.run.app

  2. 通過在 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)
          "pinTag": true              // optional (see note below)
        }
      } ]
    }
    
  4. 通過從項目目錄的根目錄運行以下命令,將託管配置部署到您的站點:

    firebase deploy --only hosting

現在可以通過以下 URL 訪問您的容器:

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

  • 任何連接的自定義域
    CUSTOM_DOMAIN /

有關重寫規則的更多詳細信息,請訪問託管配置頁面。您還可以了解各種託管配置的響應優先級順序

本地測試

在開發過程中,您可以在本地運行和測試容器鏡像。有關詳細說明,請訪問Cloud Run 文檔

下一步