הדרכה: פתרון בעיות מקומי בשירות Cloud Run

במדריך הזה נסביר איך מפתח שירות יכול לפתור בעיות בשירות Cloud Run באמצעות כלי Google Cloud Observability לגילוי, ובעזרת תהליך עבודה מקומי לפיתוח לצורך בדיקה.

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

מטרות

  • כתיבה, פיתוח ופריסה של שירות ב-Cloud Run
  • שימוש ב-Error Reporting וב-Cloud Logging כדי לזהות שגיאה
  • אחזור קובץ אימג' של קונטיינר מ-Container Registry לצורך ניתוח שורש הבעיה
  • לתקן את שירות ה'ייצור', ואז לשפר את השירות כדי לצמצם את הסיכון לבעיות עתידיות

עלויות

במסמך הזה משתמשים ברכיבים הבאים של Google Cloud, והשימוש בהם כרוך בתשלום:

כדי להעריך את ההוצאות בהתאם לתחזית השימוש שלכם, אתם יכולים להיעזר במחשבון העלויות.

משתמשים חדשים של Google Cloud ? יכול להיות שאתם זכאים לתקופת ניסיון בחינם.

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

  1. נכנסים לחשבון Google Cloud . אם אתם משתמשים חדשים ב- Google Cloud, צרו חשבון כדי שתוכלו להעריך את הביצועים של המוצרים שלנו בתרחישים מהעולם האמיתי. לקוחות חדשים מקבלים בחינם גם קרדיט בשווי 300$ להרצה, לבדיקה ולפריסה של עומסי העבודה.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  3. Verify that billing is enabled for your Google Cloud project.

  4. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  5. Verify that billing is enabled for your Google Cloud project.

  6. הפעלת Cloud Run Admin API
  7. מתקינים ומפעילים את ה-CLI של gcloud.
  8. עדכון רכיבים:
    gcloud components update
  9. פועלים לפי ההוראות כדי להתקין את Docker באופן מקומי.

התפקידים הנדרשים

כדי לקבל את ההרשאות שדרושות להשלמת המדריך, צריך לבקש מהאדמין להקצות לכם את תפקידי ה-IAM הבאים בפרויקט:

להסבר על מתן תפקידים, ראו איך מנהלים את הגישה ברמת הפרויקט, התיקייה והארגון.

יכול להיות שאפשר לקבל את ההרשאות הנדרשות גם באמצעות תפקידים בהתאמה אישית או תפקידים מוגדרים מראש.

הגדרת ברירות מחדל ב-gcloud

כדי להגדיר את gcloud עם ערכי ברירת מחדל לשירות Cloud Run:

  1. מגדירים את פרויקט ברירת המחדל:

    gcloud config set project PROJECT_ID

    מחליפים את PROJECT_ID בשם הפרויקט שיצרתם לצורך המדריך הזה.

  2. מגדירים את gcloud לאזור שבחרתם:

    gcloud config set run/region REGION

    מחליפים את REGION באזור נתמך ב-Cloud Run לבחירתכם.

מיקומים של Cloud Run

‫Cloud Run הוא שירות אזורי, כלומר התשתית שמריצה את שירותי Cloud Run ממוקמת באזור ספציפי ומנוהלת על ידי Google כך שתהיה זמינה באופן יתירתי בכל התחומים באותו אזור.

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

‫Cloud Run זמין באזורים הבאים:

בכפוף לתמחור ברמה 1

בכפוף לתמחור ברמה 2

  • africa-south1 (יוהנסבורג)
  • asia-east2 (הונג קונג)
  • asia-northeast3 (סיאול, קוריאה הדרומית)
  • asia-southeast1 (סינגפור)
  • asia-southeast2 (ג'אקארטה)
  • asia-south2 (דלהי, הודו)
  • australia-southeast1 (סידני)
  • australia-southeast2 (מלבורן)
  • europe-central2 (ורשה, פולין)
  • europe-west10 (Berlin)
  • europe-west12 (טורינו)
  • europe-west2 (לונדון, בריטניה) סמל של עלה רמה נמוכה של CO2
  • europe-west3 (פרנקפורט, גרמניה)
  • europe-west6 (ציריך, שווייץ) סמל של עלה רמה נמוכה של CO2
  • me-central1 (דוחה)
  • me-central2 (דמאם)
  • northamerica-northeast1 (מונטריאול) סמל של עלה רמה נמוכה של CO2
  • northamerica-northeast2 (טורונטו) סמל של עלה רמה נמוכה של CO2
  • southamerica-east1 (סאו פאולו, ברזיל) סמל של עלה רמה נמוכה של CO2
  • southamerica-west1 (סנטיאגו, צ'ילה) סמל של עלה רמה נמוכה של CO2
  • us-west2 (לוס אנג'לס)
  • us-west3 (סולט לייק סיטי)
  • us-west4 (לאס וגאס)

אם כבר יצרתם שירות Cloud Run, תוכלו לראות את האזור בלוח הבקרה של Cloud Run בGoogle Cloud מסוף.

הרכבת הקוד

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

  1. כדי ליצור פרויקט חדש:

    Node.js

    יוצרים פרויקט Node.js על ידי הגדרת חבילת השירות, התלויות הראשוניות וכמה פעולות נפוצות.

    1. יצירת ספרייה חדשה ב-hello-service:

      mkdir hello-service
      cd hello-service
      
    2. יוצרים פרויקט חדש ב-Node.js על ידי יצירת קובץ package.json:

      npm init --yes
      npm install express@4
      
    3. פותחים את הקובץ החדש package.json בכלי העריכה ומגדירים סקריפט start להרצת node index.js. בסיום, הקובץ ייראה כך:

      {
        "name": "hello-broken",
        "description": "Broken Cloud Run service for troubleshooting practice",
        "version": "1.0.0",
        "private": true,
        "main": "index.js",
        "scripts": {
          "start": "node index.js",
          "test": "echo \"Error: no test specified\" && exit 0",
          "system-test": "NAME=Cloud c8 mocha -p -j 2 test/system.test.js --timeout=360000 --exit"
        },
        "engines": {
          "node": ">=16.0.0"
        },
        "author": "Google LLC",
        "license": "Apache-2.0",
        "dependencies": {
          "express": "^4.17.1"
        },
        "devDependencies": {
          "c8": "^10.0.0",
          "google-auth-library": "^9.0.0",
          "got": "^11.5.0",
          "mocha": "^10.0.0"
        }
      }
      

    אם אתם מתכננים להמשיך לפתח את השירות הזה מעבר להדרכה המיידית, כדאי למלא את התיאור, את שם המחבר ולבדוק את הרישיון. פרטים נוספים זמינים במסמכי התיעוד בנושא package.json.

    Python

    1. יצירת ספרייה חדשה ב-hello-service:

      mkdir hello-service
      cd hello-service
      
    2. יוצרים קובץ requirements.txt ומעתיקים אליו את התלות:

      pytest==9.0.3; python_version >= "3.10"
      Flask==3.1.3; python_version >= '3.9'
      gunicorn==23.0.0
      Werkzeug==3.1.8; python_version >= '3.9'
      

    המשך

    1. יצירת ספרייה חדשה ב-hello-service:

      mkdir hello-service
      cd hello-service
      
    2. יוצרים פרויקט Go על ידי הפעלה של מודול Go חדש:

      go mod init example.com/hello-service
      

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

    Java

    1. יוצרים פרויקט Maven חדש:

      mvn archetype:generate \
        -DgroupId=com.example.cloudrun \
        -DartifactId=hello-service \
        -DarchetypeArtifactId=maven-archetype-quickstart \
        -DinteractiveMode=false
      
    2. מעתיקים את יחסי התלות לרשימת יחסי התלות pom.xml (בין רכיבי <dependencies>):

      <dependency>
        <groupId>com.sparkjava</groupId>
        <artifactId>spark-core</artifactId>
        <version>2.9.4</version>
      </dependency>
      <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>2.0.12</version>
      </dependency>
      <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>2.0.12</version>
      </dependency>
      
    3. מעתיקים את הגדרת ה-build אל pom.xml (מתחת לרכיבי <dependencies>):

      <build>
        <plugins>
          <plugin>
            <groupId>com.google.cloud.tools</groupId>
            <artifactId>jib-maven-plugin</artifactId>
            <version>3.4.0</version>
            <configuration>
              <to>
                <image>gcr.io/PROJECT_ID/hello-service</image>
              </to>
            </configuration>
          </plugin>
        </plugins>
      </build>
      

  2. יוצרים שירות HTTP לטיפול בבקשות נכנסות:

    Node.js

    const express = require('express');
    const app = express();
    
    app.get('/', (req, res) => {
      console.log('hello: received request.');
    
      const {NAME} = process.env;
      if (!NAME) {
        // Plain error logs do not appear in Stackdriver Error Reporting.
        console.error('Environment validation failed.');
        console.error(new Error('Missing required server parameter'));
        return res.status(500).send('Internal Server Error');
      }
      res.send(`Hello ${NAME}!`);
    });
    const port = parseInt(process.env.PORT) || 8080;
    app.listen(port, () => {
      console.log(`hello: listening on port ${port}`);
    });

    Python

    import json
    import os
    
    from flask import Flask
    
    
    app = Flask(__name__)
    
    
    @app.route("/", methods=["GET"])
    def index():
        """Example route for testing local troubleshooting.
    
        This route may raise an HTTP 5XX error due to missing environment variable.
        """
        print("hello: received request.")
    
        NAME = os.getenv("NAME")
    
        if not NAME:
            print("Environment validation failed.")
            raise Exception("Missing required service parameter.")
    
        return f"Hello {NAME}"
    
    
    if __name__ == "__main__":
        PORT = int(os.getenv("PORT")) if os.getenv("PORT") else 8080
    
        # This is used when running locally. Gunicorn is used to run the
        # application on Cloud Run. See entrypoint in Dockerfile.
        app.run(host="127.0.0.1", port=PORT, debug=True)

    המשך

    
    // Sample hello demonstrates a difficult to troubleshoot service.
    package main
    
    import (
    	"fmt"
    	"log"
    	"net/http"
    	"os"
    )
    
    func main() {
    	log.Print("hello: service started")
    
    	http.HandleFunc("/", helloHandler)
    
    
    	port := os.Getenv("PORT")
    	if port == "" {
    		port = "8080"
    		log.Printf("Defaulting to port %s", port)
    	}
    
    	log.Printf("Listening on port %s", port)
    	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
    }
    
    func helloHandler(w http.ResponseWriter, r *http.Request) {
    	log.Print("hello: received request")
    
    	name := os.Getenv("NAME")
    	if name == "" {
    		log.Printf("Missing required server parameter")
    		// The panic stack trace appears in Cloud Error Reporting.
    		panic("Missing required server parameter")
    	}
    
    	fmt.Fprintf(w, "Hello %s!\n", name)
    }
    

    Java

    import static spark.Spark.get;
    import static spark.Spark.port;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class App {
    
      private static final Logger logger = LoggerFactory.getLogger(App.class);
    
      public static void main(String[] args) {
        int port = Integer.parseInt(System.getenv().getOrDefault("PORT", "8080"));
        port(port);
    
        get(
            "/",
            (req, res) -> {
              logger.info("Hello: received request.");
              String name = System.getenv("NAME");
              if (name == null) {
                // Standard error logs do not appear in Stackdriver Error Reporting.
                System.err.println("Environment validation failed.");
                String msg = "Missing required server parameter";
                logger.error(msg, new Exception(msg));
                res.status(500);
                return "Internal Server Error";
              }
              res.status(200);
              return String.format("Hello %s!", name);
            });
      }
    }

  3. יוצרים Dockerfile כדי להגדיר את קובץ האימג' של הקונטיינר שמשמש לפריסת השירות:

    Node.js

    
    # Use the official lightweight Node.js image.
    # https://hub.docker.com/_/node
    FROM node:20-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 copying both package.json AND package-lock.json (when available).
    # Copying this first prevents re-running npm install on every code change.
    COPY package*.json ./
    
    # Install dependencies.
    # if you need a deterministic and repeatable build create a
    # package-lock.json file and use npm ci:
    # RUN npm ci --omit=dev
    # if you need to include development dependencies during development
    # of your application, use:
    # RUN npm install --dev
    
    RUN npm install --omit=dev
    
    # Copy local code to the container image.
    COPY . ./
    
    # Run the web service on container startup.
    CMD [ "npm", "start" ]
    

    Python

    
    # Use the official Python image.
    # https://hub.docker.com/_/python
    FROM python:3.14
    
    # Allow statements and log messages to immediately appear in the Cloud Run logs
    ENV PYTHONUNBUFFERED True
    
    # Copy application dependency manifests to the container image.
    # Copying this separately prevents re-running pip install on every code change.
    COPY requirements.txt ./
    
    # Install production dependencies.
    RUN pip install -r requirements.txt
    
    # Copy local code to the container image.
    ENV APP_HOME /app
    WORKDIR $APP_HOME
    COPY . ./
    
    # Run the web service on container startup.
    # Use 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.
    # Timeout is set to 0 to disable the timeouts of the workers to allow Cloud Run to handle instance scaling.
    CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app
    

    המשך

    
    # Use the official Go image to create a binary.
    # This is based on Debian and sets the GOPATH to /go.
    # https://hub.docker.com/_/golang
    FROM golang:1.25-bookworm as builder
    
    # Create and change to the app directory.
    WORKDIR /app
    
    # Retrieve application dependencies.
    # This allows the container build to reuse cached dependencies.
    # Expecting to copy go.mod and if present go.sum.
    COPY go.* ./
    RUN go mod download
    
    # Copy local code to the container image.
    COPY . ./
    
    # Build the binary.
    RUN go build -v -o server
    
    # Use the official Debian slim image for a lean production container.
    # https://hub.docker.com/_/debian
    # https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
    FROM debian:bookworm-slim
    RUN set -x && apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
        ca-certificates && \
        rm -rf /var/lib/apt/lists/*
    
    # 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"]
    

    Java

    בדוגמה הזו נעשה שימוש ב-Jib כדי ליצור תמונות Docker באמצעות כלים נפוצים של Java. ‫Jib מבצע אופטימיזציה של בניית קונטיינרים בלי צורך בקובץ Dockerfile או בהתקנה של Docker. מידע נוסף על יצירת מאגרי Java באמצעות Jib

    <plugin>
      <groupId>com.google.cloud.tools</groupId>
      <artifactId>jib-maven-plugin</artifactId>
      <version>3.4.0</version>
      <configuration>
        <to>
          <image>gcr.io/PROJECT_ID/hello-service</image>
        </to>
      </configuration>
    </plugin>
    

שליחת הקוד

תהליך שליחת הקוד כולל שלושה שלבים: יצירת קובץ אימג' של קונטיינר באמצעות Cloud Build, העלאת קובץ האימג' של הקונטיינר ל-Container Registry ופריסת קובץ האימג' של הקונטיינר ב-Cloud Run.

כדי לשלוח את הקוד:

  1. יוצרים את הקונטיינר ומפרסמים אותו ב-Container Registry:

    Node.js

    gcloud builds submit --tag gcr.io/PROJECT_ID/hello-service

    כאשר PROJECT_ID הוא מזהה הפרויקט שלכם ב-Google Cloud. אפשר לבדוק את מזהה הפרויקט הנוכחי באמצעות gcloud config get-value project.

    אם הפעולה תצליח, תופיע הודעת SUCCESS עם המזהה, זמן היצירה ושם התמונה. התמונה מאוחסנת ב-Container Registry ואפשר לעשות בה שימוש חוזר אם רוצים.

    Python

    gcloud builds submit --tag gcr.io/PROJECT_ID/hello-service

    כאשר PROJECT_ID הוא מזהה הפרויקט שלכם ב-Google Cloud. אפשר לבדוק את מזהה הפרויקט הנוכחי באמצעות gcloud config get-value project.

    אם הפעולה תצליח, תופיע הודעת SUCCESS עם המזהה, זמן היצירה ושם התמונה. התמונה מאוחסנת ב-Container Registry ואפשר לעשות בה שימוש חוזר אם רוצים.

    המשך

    gcloud builds submit --tag gcr.io/PROJECT_ID/hello-service

    כאשר PROJECT_ID הוא מזהה הפרויקט שלכם ב-Google Cloud. אפשר לבדוק את מזהה הפרויקט הנוכחי באמצעות gcloud config get-value project.

    אם הפעולה תצליח, תופיע הודעת SUCCESS עם המזהה, זמן היצירה ושם התמונה. התמונה מאוחסנת ב-Container Registry ואפשר לעשות בה שימוש חוזר אם רוצים.

    Java

    1. משתמשים בכלי העזר לפרטי כניסה של gcloud כדי לתת ל-Docker הרשאה להעביר בדחיפה אל Container Registry.
      gcloud auth configure-docker
    2. משתמשים בתוסף Jib Maven כדי ליצור את הקונטיינר ולהעביר אותו בדחיפה ל-Container Registry.
      mvn compile jib:build -Dimage=gcr.io/PROJECT_ID/hello-service

    כאשר PROJECT_ID הוא מזהה הפרויקט שלכם ב-Google Cloud. אפשר לבדוק את מזהה הפרויקט הנוכחי באמצעות הפקודה gcloud config get-value project.

    אם הפעולה תצליח, תוצג ההודעה BUILD SUCCESS. התמונה מאוחסנת ב-Container Registry ואפשר להשתמש בה שוב אם רוצים.

  2. מריצים את הפקודה הבאה כדי לפרוס את האפליקציה:

    gcloud run deploy hello-service --image gcr.io/PROJECT_ID/hello-service

    מחליפים את PROJECT_ID במזהה הפרויקט ב-Google Cloud. ‫hello-service הוא גם השם של קובץ אימג' של קונטיינר וגם השם של שירות Cloud Run. שימו לב שקובץ אימג' של קונטיינר נפרס בשירות ובאזור שהגדרתם קודם בקטע הגדרת gcloud.

    עונים y, ‏Yes (כן), להנחיה allow unauthenticated (מתן גישה ללא אימות). פרטים נוספים על אימות מבוסס-IAM זמינים במאמר ניהול גישה.

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

אני רוצה לנסות

כדאי לנסות את השירות כדי לוודא שהפריסה בוצעה בהצלחה. הבקשות צריכות להיכשל עם שגיאת HTTP 500 או 503 (שגיאות ששייכות לסיווג 5xx שגיאות שרת). במדריך מוסבר איך לפתור את הבעיה שמופיעה בתגובה לשגיאה.

לשירות מוקצית באופן אוטומטי כתובת URL שניתן לנווט אליה.

  1. עוברים לכתובת ה-URL הזו בדפדפן האינטרנט:

    1. פותחים דפדפן אינטרנט

    2. מחפשים את כתובת ה-URL של השירות שמופיעה בפלט של פקודת הפריסה הקודמת.

      אם פקודת הפריסה לא סיפקה כתובת URL, סימן שמשהו השתבש. קוראים את הודעת השגיאה ופועלים בהתאם. אם לא מופיעות הוראות לביצוע פעולה, כדאי לעיין במדריך לפתרון בעיות ואולי לנסות שוב את פקודת הפריסה.

    3. כדי לעבור לכתובת ה-URL הזו, מעתיקים אותה לסרגל הכתובות בדפדפן ולוחצים על ENTER.

  2. צריך להציג את השגיאה HTTP 500 או HTTP 503.

    אם מופיעה שגיאת HTTP 403, יכול להיות שדחיתם את allow unauthenticated invocations בהנחיה לפריסה. כדי לפתור את הבעיה, צריך להעניק גישה לכולם לשירות:

    gcloud run services add-iam-policy-binding hello-service \
      --member="allUsers" \
      --role="roles/run.invoker"
    

מידע נוסף זמין במאמר בנושא מתן גישה ציבורית (ללא אימות).

בדיקת הבעיה

תארו לעצמכם ששגיאת HTTP 5xx שנתקלתם בה למעלה בקטע Trying it out התרחשה כשגיאת זמן ריצה בייצור. במדריך הזה מוסבר תהליך רשמי לטיפול בבעיה. תהליכי פתרון שגיאות בהפקה משתנים מאוד, אבל במדריך הזה מוצגת רצף מסוים של שלבים כדי להראות את השימוש בכלים ובטכניקות שימושיים.

כדי לחקור את הבעיה הזו, צריך לעבור את השלבים הבאים:

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

בתחילת הבדיקה יש לכם כתובת URL, חותמת זמן וההודעה Internal Server Error (שגיאת שרת פנימית).

איסוף פרטים נוספים

כדאי לאסוף מידע נוסף על הבעיה כדי להבין מה קרה ולקבוע מה השלבים הבאים.

כדי לאסוף פרטים נוספים, אפשר להשתמש בכלים הזמינים של Google Cloud Observability:

  1. אפשר להשתמש במסוף Error Reporting, שכולל לוח בקרה עם פרטים ומעקב אחר חזרות של שגיאות עם דוח קריסות מוכרות.

    כניסה למסוף Error Reporting

    צילום מסך של רשימת השגיאות, כולל העמודות &#39;סטטוס פתרון&#39;, &#39;מקרים&#39;, &#39;שגיאה&#39; ו&#39;נצפה ב&#39;.
    רשימת השגיאות שנרשמו. השגיאות מקובצות לפי הודעה בגרסאות, בשירותים ובפלטפורמות.
  2. לוחצים על השגיאה כדי לראות את פרטי מעקב המחסנית, ושימו לב לקריאות הפונקציה שבוצעו ממש לפני השגיאה.

    צילום מסך של דוח קריסות יחיד שנותח, שמדגים פרופיל נפוץ של השגיאה הזו.
    הקטע 'דוגמה של דוח קריסות' בדף פרטי השגיאה מציג מופע יחיד של השגיאה. אפשר לבדוק כל מופע בנפרד.
  3. משתמשים ב-Cloud Logging כדי לבדוק את רצף הפעולות שהובילו לבעיה, כולל הודעות שגיאה שלא נכללות במסוף Error Reporting בגלל חוסר בדוח קריסות מוכר:

    כניסה למסוף Cloud Logging

    בתפריט הנפתח הראשון, בוחרים באפשרות Cloud Run Revision > hello-service. הפעולה הזו תסנן את הרשומות ביומן כך שיוצגו רק אלה שנוצרו על ידי השירות שלכם.

מידע נוסף על הצגת יומנים ב-Cloud Run

חזרה לגרסה תקינה

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

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

שחזור השגיאה

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

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

מכיוון שהשירות לדוגמה במדריך הזה הוא לקריאה בלבד ולא מפעיל תופעות לוואי מסובכות, אפשר לשחזר שגיאות בייצור בצורה בטוחה. עם זאת, במקרים רבים של שירותים אמיתיים, זה לא יקרה: יכול להיות שתצטרכו לשחזר שגיאות בסביבת בדיקה או להגביל את השלב הזה לבדיקה מקומית.

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

ביצוע ניתוח שורש הבעיה

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

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

  1. אם לא השתמשתם ב-Docker CLI באופן מקומי עם Container Registry, צריך לאמת אותו באמצעות gcloud:

    gcloud auth configure-docker

    אפשר למצוא גישות חלופיות במאמר שיטות אימות ב-Container Registry.

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

    gcloud run services describe hello-service

    מחפשים את השם של קובץ אימג' של קונטיינר בתוך האובייקט spec. אפשר להשתמש בפקודה ממוקדת יותר כדי לאחזר אותו ישירות:

    gcloud run services describe hello-service \
       --format="value(spec.template.spec.containers.image)"

    הפקודה הזו חושפת את השם של קובץ אימג' של קונטיינר, כמו gcr.io/PROJECT_ID/hello-service.

  3. מושכים את קובץ אימג' של קונטיינר מ-Container Registry לסביבה שלכם. השלב הזה עשוי להימשך כמה דקות כי קובץ אימג' של קונטיינר יורד:

    docker pull gcr.io/PROJECT_ID/hello-service

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

  4. מריצים באופן מקומי כדי לוודא שהבעיה לא ייחודית ל-Cloud Run:

    PORT=8080 && docker run --rm -e PORT=$PORT -p 9000:$PORT \
       gcr.io/PROJECT_ID/hello-service

    פירוט הרכיבים של הפקודה שלמעלה:

    • משתנה הסביבה PORT משמש את השירות כדי לקבוע את היציאה להאזנה בתוך הקונטיינר.
    • הפקודה run מפעילה את הקונטיינר, ומוגדרת כברירת מחדל לפקודת נקודת הכניסה שמוגדרת בקובץ Dockerfile או בקובץ אימג' של קונטיינר של אב.
    • הדגל --rm מוחק את מופע הקונטיינר ביציאה.
    • הדגל -e מקצה ערך למשתנה סביבה. ‫-e PORT=$PORT מעביר את המשתנה PORT מהמערכת המקומית למאגר התגים עם אותו שם משתנה.
    • הדגל -p מפרסם את מאגר התגים כשירות שזמין ב-localhost ביציאה 9000. בקשות אל localhost:9000 ינותבו אל הקונטיינר ביציאה 8080. המשמעות היא שהפלט מהשירות לגבי מספר היציאה שבשימוש לא יהיה זהה לדרך שבה ניגשים לשירות.
    • הארגומנט האחרון gcr.io/PROJECT_ID/hello-service הוא תמונת קונטיינר tag, תווית שניתן לקרוא, של מזהה הגיבוב sha256 של תמונת קונטיינר. אם התמונה לא זמינה באופן מקומי, Docker מנסה לאחזר אותה ממאגר מרוחק.

    פותחים את הכתובת http://localhost:9000 בדפדפן. בודקים את הפלט של הטרמינל כדי לראות אם יש הודעות שגיאה שתואמות לאלה ב-{ops_name}}.

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

    במקרה כזה, השגיאה משוכפלת באופן מקומי.

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

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

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

Node.js

מחפשים את המקור של הודעת השגיאה בקובץ index.js סביב מספר השורה שמופיע בדוח קריסות שמוצגים ביומנים:
const {NAME} = process.env;
if (!NAME) {
  // Plain error logs do not appear in Stackdriver Error Reporting.
  console.error('Environment validation failed.');
  console.error(new Error('Missing required server parameter'));
  return res.status(500).send('Internal Server Error');
}

Python

מחפשים את המקור של הודעת השגיאה בקובץ main.py סביב מספר השורה שמופיע בדוח קריסות שמוצגים ביומנים:
NAME = os.getenv("NAME")

if not NAME:
    print("Environment validation failed.")
    raise Exception("Missing required service parameter.")

המשך

מחפשים את המקור של הודעת השגיאה בקובץ main.go סביב מספר השורה שמופיע בדוח קריסות שמוצגים ביומנים:

name := os.Getenv("NAME")
if name == "" {
	log.Printf("Missing required server parameter")
	// The panic stack trace appears in Cloud Error Reporting.
	panic("Missing required server parameter")
}

Java

מחפשים את המקור של הודעת השגיאה בקובץ App.java סביב מספר השורה שמופיע בדוח קריסות שמוצג ביומנים:

String name = System.getenv("NAME");
if (name == null) {
  // Standard error logs do not appear in Stackdriver Error Reporting.
  System.err.println("Environment validation failed.");
  String msg = "Missing required server parameter";
  logger.error(msg, new Exception(msg));
  res.status(500);
  return "Internal Server Error";
}

בבדיקה של הקוד הזה, הפעולות הבאות מתבצעות כשמשתנה הסביבה NAME לא מוגדר:

  • שגיאה נרשמת ביומן ב-Google Cloud Observability
  • נשלחת תגובת שגיאה של HTTP

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

תיקון שורש הבעיה

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

  • בודקים אם השירות פועל באופן מקומי עם סביבת NAME שזמינה במקום:

    1. מריצים את הקונטיינר באופן מקומי עם משתנה הסביבה שנוסף:

      PORT=8080 && docker run --rm -e PORT=$PORT -p 9000:$PORT \
       -e NAME="Local World!" \
       gcr.io/PROJECT_ID/hello-service
    2. בדפדפן, עוברים לכתובת http://localhost:9000

    3. הטקסט 'Hello Local World!‎' מופיע בדף

  • משנים את סביבת השירות הפועל של Cloud Run כך שתכלול את המשתנה הזה:

    1. מריצים את פקודת עדכון השירותים כדי להוסיף משתנה סביבה:

      gcloud run services update hello-service \
        --set-env-vars NAME=Override
      
    2. ממתינים כמה שניות בזמן ש-Cloud Run יוצר גרסה חדשה על סמך הגרסה הקודמת עם משתנה הסביבה החדש שנוסף.

  • מוודאים שהשירות תוקן:

    1. בדפדפן, עוברים לכתובת ה-URL של שירות Cloud Run.
    2. הטקסט 'Hello Override!‎' יופיע בדף.
    3. מוודאים שלא מופיעות הודעות או שגיאות לא צפויות ב-Cloud Logging או ב-Error Reporting.

שיפור מהירות פתרון הבעיות בעתיד

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

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

נראה איך מסירים את משתנה הסביבה NAME כתלות מחייבת.

  1. מסירים את הקוד הקיים לטיפול ב-NAME:

    Node.js

    const {NAME} = process.env;
    if (!NAME) {
      // Plain error logs do not appear in Stackdriver Error Reporting.
      console.error('Environment validation failed.');
      console.error(new Error('Missing required server parameter'));
      return res.status(500).send('Internal Server Error');
    }

    Python

    NAME = os.getenv("NAME")
    
    if not NAME:
        print("Environment validation failed.")
        raise Exception("Missing required service parameter.")

    המשך

    name := os.Getenv("NAME")
    if name == "" {
    	log.Printf("Missing required server parameter")
    	// The panic stack trace appears in Cloud Error Reporting.
    	panic("Missing required server parameter")
    }

    Java

    String name = System.getenv("NAME");
    if (name == null) {
      // Standard error logs do not appear in Stackdriver Error Reporting.
      System.err.println("Environment validation failed.");
      String msg = "Missing required server parameter";
      logger.error(msg, new Exception(msg));
      res.status(500);
      return "Internal Server Error";
    }

  2. מוסיפים קוד חדש שמגדיר ערך ברירת מחדל:

    Node.js

    const NAME = process.env.NAME || 'World';
    if (!process.env.NAME) {
      console.log(
        JSON.stringify({
          severity: 'WARNING',
          message: `NAME not set, default to '${NAME}'`,
        })
      );
    }

    Python

    NAME = os.getenv("NAME")
    
    if not NAME:
        NAME = "World"
        error_message = {
            "severity": "WARNING",
            "message": f"NAME not set, default to {NAME}",
        }
        print(json.dumps(error_message))

    המשך

    name := os.Getenv("NAME")
    if name == "" {
    	name = "World"
    	log.Printf("warning: NAME not set, default to %s", name)
    }

    Java

    String name = System.getenv().getOrDefault("NAME", "World");
    if (System.getenv("NAME") == null) {
      logger.warn(String.format("NAME not set, default to %s", name));
    }

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

    Node.js

    docker build --tag gcr.io/PROJECT_ID/hello-service .

    Python

    docker build --tag gcr.io/PROJECT_ID/hello-service .

    Go

    docker build --tag gcr.io/PROJECT_ID/hello-service .

    Java

    mvn compile jib:build

    מוודאים שמשתנה הסביבה NAME עדיין פועל:

    PORT=8080 && docker run --rm -e PORT=$PORT -p 9000:$PORT \
     -e NAME="Robust World" \
     gcr.io/PROJECT_ID/hello-service

    מוודאים שהשירות פועל ללא המשתנה NAME:

    PORT=8080 && docker run --rm -e PORT=$PORT -p 9000:$PORT \
     gcr.io/PROJECT_ID/hello-service

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

  4. כדי להטמיע את הקוד, חוזרים לקטע הטמעת הקוד.

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

    כדי למחוק את משתני הסביבה שהוגדרו קודם:

    gcloud run services update hello-service --clear-env-vars

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

חיפוש בעיות אחרות ביומנים

יכול להיות שיופיעו בעיות אחרות בכלי Log Viewer בשירות הזה. לדוגמה, קריאת מערכת לא נתמכת תופיע ביומנים כ-Container Sandbox Limitation (הגבלה של ארגז חול של קונטיינר).

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

Container Sandbox Limitation: Unsupported syscall statx(0xffffff9c,0x3e1ba8e86d88,0x0,0xfff,0x3e1ba8e86970,0x3e1ba8e86a90). Please, refer to https://gvisor.dev/c/linux/amd64/statx for more information.

במקרה הזה, חוסר התמיכה לא משפיע על שירות הדוגמה hello-service.

פתרון בעיות ב-Terraform

לפתרון בעיות או לשאלות שקשורות ל-Terraform, אפשר לעיין במאמר פתרון בעיות שקשורות לאימות מדיניות ב-Terraform או לפנות אל התמיכה של Terraform.

הסרת המשאבים

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

מחיקת הפרויקט

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

הדרך הקלה ביותר לבטל את החיוב היא למחוק את הפרויקט שיצרתם בשביל המדריך.

כדי למחוק את הפרויקט:

  1. במסוף Google Cloud , נכנסים לדף Manage resources.

    כניסה לדף Manage resources

  2. ברשימת הפרויקטים, בוחרים את הפרויקט שרוצים למחוק ולוחצים על Delete.
  3. כדי למחוק את הפרויקט, כותבים את מזהה הפרויקט בתיבת הדו-שיח ולוחצים על Shut down.

מחיקת משאבי הדרכה

  1. מוחקים את שירות Cloud Run שפרסתם במדריך הזה. שירותי Cloud Run לא צוברים עלויות עד שהם מקבלים בקשות.

    כדי למחוק את שירות Cloud Run, מריצים את הפקודה הבאה:

    gcloud run services delete SERVICE-NAME

    מחליפים את SERVICE-NAME בשם השירות.

    אפשר גם למחוק שירותים של Cloud Run מGoogle Cloud המסוף.

  2. מסירים את הגדרת ברירת המחדל של האזור gcloud שהוספתם במהלך ההגדרה של המדריך:

     gcloud config unset run/region
    
  3. מסירים את הגדרות הפרויקט:

     gcloud config unset project
    
  4. מחיקת משאבים אחרים Google Cloud שנוצרו במדריך הזה:

המאמרים הבאים