הצגת תוכן דינמי ואירוח מיקרו-שירותים באמצעות Cloud Run

אפשר לשלב בין Cloud Run לבין Firebase Hosting כדי ליצור ולהציג את התוכן הדינמי שלכם או כדי ליצור ממשקי API ל-REST בתור מיקרו-שירותים.

באמצעות Cloud Run, אפשר לפרוס אפליקציה שארוזה בקובץ אימג' של קונטיינר. לאחר מכן, באמצעות Firebase Hosting, אפשר להפנות בקשות HTTPS להפעלת האפליקציה שמוכלת בקונטיינר.

תרחישי שימוש לדוגמה ודוגמאות ל-Cloud Run שמשולב עם Firebase Hosting זמינים בסקירה הכללית בנושא שרתים וירטואליים.


במדריך הזה נסביר איך:

  1. כתיבת אפליקציית Hello World פשוטה
  2. העברת אפליקציה לקונטיינר והעלאה שלה אל Artifact Registry
  3. פריסת קובץ האימג' של הקונטיינר אל Cloud Run
  4. הפניית בקשות ישירות לאפליקציה שמופעלת בתוך קונטיינרHosting

שימו לב: כדי לשפר את הביצועים של הצגת תוכן דינמי, אפשר לשנות את הגדרות המטמון.

לפני שמתחילים

לפני שמשתמשים ב-Cloud Run, צריך להשלים כמה משימות ראשוניות, כולל הגדרת חשבון Cloud Billing, הפעלת Cloud Run API והתקנת כלי שורת הפקודה gcloud.

הגדרת חיוב לפרויקט

ב-Cloud Run יש מכסת שימוש ללא תשלום, אבל עדיין צריך חשבון Cloud Billing שמשויך לפרויקט Firebase כדי להשתמש ב-Cloud Run או לנסות אותו.

הפעלת ה-API והתקנת ה-SDK

  1. מפעילים את Cloud Run API ב-Google APIs Console:

    1. פותחים את הדף Cloud Run API ב-Google APIs Console.

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

    הקוד הזה יוצר שרת אינטרנט בסיסי שמקשיב ליציאה שמוגדרת על ידי משתנה הסביבה PORT.

האפליקציה מוכנה להעברה לקונטיינר ולהעלאה אל Artifact 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.21.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);
    });
    

    הקוד הזה יוצר שרת אינטרנט בסיסי שמקשיב ליציאה שמוגדרת על ידי משתנה הסביבה PORT.

האפליקציה מוכנה להעברה לקונטיינר ולהעלאה אל Artifact 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.

האפליקציה מוכנה להעברה לקונטיינר ולהעלאה אל Artifact Registry.

Java

  1. מתקינים Java SE 8 או גרסה מתקדמת יותר של JDK ו-CURL.

    שימו לב: אנחנו צריכים לעשות את זה רק כדי ליצור את פרויקט האינטרנט החדש בשלב הבא. קובץ ה-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. מעדכנים את המחלקה SpringBootApplication בקובץ src/main/java/com/example/helloworld/HelloworldApplication.java על ידי הוספת @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);
      }
    }
    

    הקוד הזה יוצר שרת אינטרנט בסיסי שמקשיב ליציאה שמוגדרת על ידי משתנה הסביבה PORT.

האפליקציה מוכנה להעברה לקונטיינר ולהעלאה אל Artifact Registry.

שלב 2: יצירת קונטיינר לאפליקציה והעלאה שלו אל Artifact 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
    
    # Copy local code to the container image.
    COPY . ./
    
    # Install dependencies and tidy up the go.mod and go.sum files.
    RUN go mod tidy
    
    # 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, מריצים את הפקודה הבאה מהספרייה שמכילה את קובץ ה-Dockerfile:

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

    אם הפעולה תצליח, תוצג הודעה על הצלחה עם שם התמונה
    (gcr.io/PROJECT_ID/helloworld).

תמונת הקונטיינר מאוחסנת עכשיו ב-Artifact Registry ואפשר לעשות בה שימוש חוזר אם רוצים.

שימו לב: במקום Cloud Build, אתם יכולים להשתמש בגרסה של Docker שהתקנתם באופן מקומי כדי ליצור את הקונטיינר באופן מקומי.

שלב 3: פריסת תמונת המאגר ב-Cloud Run

  1. מבצעים פריסה באמצעות הפקודה הבאה:

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

  2. כשמוצגת בקשה:

    • בוחרים אזור (לדוגמה, us-central1)
    • מאשרים את שם השירות (לדוגמה, helloworld)
    • עונים Y על allow unauthenticated invocations (אישור הפעלות לא מאומתות)
  3. מחכים כמה רגעים עד שהפריסה תושלם. אם הפעולה בוצעה ללא שגיאות, כתובת ה-URL של השירות תוצג בשורת הפקודה. לדוגמה: https://helloworld-RANDOM_HASH-us-central1.a.run.app

  4. פותחים את כתובת ה-URL של השירות בדפדפן אינטרנט כדי להיכנס למאגר שהופעל.

בשלב הבא מוסבר איך לגשת לאפליקציה הזו שמבוססת על קונטיינר מכתובת URL מסוג Firebase Hosting כדי שהיא תוכל ליצור תוכן דינמי לאתר שלכם שמארח ב-Firebase.

שלב 4: הפניית בקשות אירוח לאפליקציה בקונטיינר

בעזרת כללי שכתוב, אפשר להפנות בקשות שתואמות לדפוסים ספציפיים ליעד יחיד.

בדוגמה הבאה אנחנו מראים איך להפנות את כל הבקשות מהדף /helloworld באתר Hosting כדי להפעיל את מופע מאגר התגים helloworld.

  1. חשוב לוודא ש:

    הוראות מפורטות להתקנת ה-CLI ולאתחול Hosting זמינות במאמר תחילת העבודה עם Hosting.

  2. פותחים את קובץ firebase.json.

  3. מוסיפים את ההגדרה rewrite בקטע hosting:

    "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 הבאות:

מידע נוסף על כללי שכתוב זמין בדף ההגדרה של Hosting. אפשר גם לקרוא על סדר העדיפויות של התגובות להגדרות שונות של Hosting.

בדיקה מקומית

במהלך הפיתוח, אתם יכולים להריץ ולבדוק את קובץ האימג' בקונטיינר באופן מקומי. הוראות מפורטות זמינות במאמרי העזרה בנושא Cloud Run.

השלבים הבאים