הדרכה: פתרון בעיות מקומי בשירות 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 ביחד בכמה מיקומים יכול להשפיע על זמן האחזור של השירות וגם על העלות.

אפשר להשתמש ב-Cloud Run באזורים הבאים:

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

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

  • africa-south1 (יוהנסבורג)
  • asia-east2 (הונג קונג)
  • asia-northeast3 (סיאול, דרום קוריאה)
  • asia-southeast1 (סינגפור)
  • asia-southeast2 (ג'קרטה)
  • asia-south2 (דלהי, הודו)
  • australia-southeast1 (סידני)
  • australia-southeast2 (מלבורן)
  • europe-central2 (ורשה, פולין) סמל של עלה רמה נמוכה של CO2
  • europe-west10 (ברלין)
  • ‫europe-west12 (טורינו) סמל של עלה רמה נמוכה של CO2
  • ‫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": ">=20.0.0"
        },
        "author": "Google LLC",
        "license": "Apache-2.0",
        "dependencies": {
          "express": "^5.0.0"
        },
        "devDependencies": {
          "c8": "^12.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.9; python_version >= '3.10'
      

    המשך

    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)

    Go

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

    Go

    
    # 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 מבצע אופטימיזציה של גרסאות build של קונטיינרים בלי צורך בקובץ Docker או בהתקנה של 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. אפשר לבדוק את מזהה הפרויקט הנוכחי באמצעות gcloud config get-value project.

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

    Python

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

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

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

    המשך

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

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

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

    Java

    1. משתמשים ב-gcloud credential helper כדי לאשר ל-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. כדי לבדוק את מזהה הפרויקט הנוכחי, אפשר להשתמש בפקודה 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, "כן", להנחיה allow unauthenticated. למידע נוסף על אימות מבוסס-IAM, ראו ניהול הגישה.

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

רוצים לנסות?

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

לשירות מוקצית באופן אוטומטי כתובת 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 שנתקלנו בה למעלה בקטע התנסות הייתה שגיאת זמן ריצה בסביבת הייצור. במדריך הזה נסביר על תהליך רשמי לטיפול בבעיה. למרות שיש הרבה תהליכים לפתרון שגיאות בסביבת הייצור, במדריך הזה מוצג רצף מסוים של שלבים כדי להראות איך משתמשים בכלים ובשיטות שימושיים.

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

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

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

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

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

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

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

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

    צילום מסך של רשימת השגיאות, כולל העמודות &quot;סטטוס פתרון&quot;, &quot;מופעים&quot;, &quot;שגיאה&quot; ו&quot;נצפה ב&quot;.
    רשימת השגיאות שתועדו. השגיאות מקובצות לפי הודעות בגרסאות, בשירותים ובפלטפורמות השונות.
  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 מפעילה את הקונטיינר, כברירת מחדל לפקודת נקודת הכניסה שמוגדרת בקובץ Docker או בקובץ אימג' של קונטיינר הורה.
    • הדגל --rm מוחק את המכונה של הקונטיינר ביציאה.
    • הדגל -e מקצה ערך למשתנה סביבה. -e PORT=$PORT מפיץ את המשתנה PORT מהמערכת המקומית למאגר עם אותו שם משתנה.
    • הדגל -p מפרסם את מאגר התגים כשירות שזמין ב-localhost ביציאה 9000. בקשות אל localhost:9000 ינותבו למאגר ביציאה 8080. המשמעות היא שהפלט מהשירות לגבי מספר היציאה שבשימוש לא יתאים לאופן הגישה לשירות.
    • הארגומנט האחרון gcr.io/PROJECT_ID/hello-service הוא קובץ אימג' של קונטיינר tag, תווית קריאה לאנשים של מזהה הגיבוב (hash) מסוג 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.")

    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

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

    Go

    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

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

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

יכול להיות שיופיעו בעיות אחרות ב-מציג היומנים של השירות הזה. לדוגמה, קריאת מערכת שלא נתמכת תופיע ביומנים בתור "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 שנוצרו במדריך הזה:

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