使用 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 文檔

下一步