Pemecahan masalah lokal


Tutorial ini menunjukkan cara memecahkan masalah layanan penayangan Knative yang rusak menggunakan alat Stackdriver untuk penemuan dan alur kerja pengembangan lokal untuk penyelidikan.

Pendamping "studi kasus" langkah demi langkah untuk panduan pemecahan masalah ini menggunakan project contoh yang menghasilkan error runtime saat di-deploy, yang Anda pecahkan masalahnya untuk ditemukan dan diperbaiki masalahnya.

Tujuan

  • Menulis, mem-build, dan men-deploy layanan ke penayangan Knative
  • Menggunakan Cloud Logging untuk mengidentifikasi error
  • Mengambil image container dari Container Registry untuk analisis akar masalah
  • Perbaiki layanan "produksi", lalu tingkatkan kualitas layanan untuk mengurangi masalah di masa mendatang

Biaya

Dalam dokumen ini, Anda menggunakan komponen Google Cloud yang dapat ditagih berikut:

Untuk membuat perkiraan biaya berdasarkan proyeksi penggunaan Anda, gunakan kalkulator harga. Pengguna baru Google Cloud mungkin memenuhi syarat untuk mendapatkan uji coba gratis.

Sebelum memulai

Susun kode

Bangun layanan penyambut baru yang ditayangkan Knative langkah demi langkah. Sebagai sebuah pengingat, layanan ini sengaja membuat error runtime untuk latihan pemecahan masalah.

  1. Membuat sebuah project baru.

    Node.js

    Membuat sebuah project Node.js dengan menentukan paket layanan, dependensi awal, dan beberapa operasi umum.

    1. Buat direktori hello-service:

      mkdir hello-service
      cd hello-service
      
    2. Buat file package.json:

      npm init --yes
      npm install --save express@4
      
    3. Buka file package.json baru di editor Anda dan konfigurasikan skrip start untuk menjalankan node index.js Setelah selesai, filenya akan terlihat seperti ini:

      {
        "name": "hello-service",
        "version": "1.0.0",
        "description": "",
        "main": "index.js",
        "scripts": {
            "start": "node index.js",
            "test": "echo \"Error: no test specified\" && exit 1"
        },
        "keywords": [],
        "author": "",
        "license": "ISC",
        "dependencies": {
            "express": "^4.17.1"
        }
      }

    Jika Anda terus mengembangkan layanan ini hingga lebih dari sekadar tutorial langsung, pertimbangkan untuk mengisi deskripsi, penulis, dan mengevaluasi lisensinya. Untuk mengetahui detail lengkapnya, baca dokumentasi package.json.

    Python

    1. Membuat direktori hello-service baru:

      mkdir hello-service
      cd hello-service
      
    2. Membuat file requirements.txt dan salin dependensi Anda ke dalamnya:

      Flask==3.0.3
      pytest==8.2.0; python_version > "3.0"
      # pin pytest to 4.6.11 for Python2.
      pytest==4.6.11; python_version < "3.0"
      gunicorn==22.0.0
      Werkzeug==3.0.3
      

    Go

    1. Buat direktori hello-service:

      mkdir hello-service
      cd hello-service
      
    2. Membuat project Go dengan melakukan inisialisasi modul go baru:

      go mod init <var>my-domain</var>.com/hello-service
      

    Anda dapat memperbarui nama spesifiknya sesuai keinginan: Anda harus memperbarui nama tersebut jika kode dipublikasikan ke repositori kode yang dapat diakses melalui web.

    Java

    1. Membuat project maven:

      mvn archetype:generate \
        -DgroupId=com.example \
        -DartifactId=hello-service \
        -DarchetypeArtifactId=maven-archetype-quickstart \
        -DinteractiveMode=false
      
    2. Salin dependensi ke daftar dependensi pom.xml Anda (di antara elemen <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. Salin setelan build ke dalam pom.xml Anda (di bawah elemen <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. Membuat layanan HTTP untuk menangani permintaan masuk:

    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. Membuat Dockerfile untuk menentukan image container yang digunakan untuk men-deploy layanan:

    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.11
    
    # 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 offical golang image to create a binary.
    # This is based on Debian and sets the GOPATH to /go.
    # https://hub.docker.com/_/golang
    FROM golang:1.21-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

    Contoh ini menggunakan Jib untuk mem-build image Docker menggunakan alat Java umum. Jib mengoptimalkan build container tanpa memerlukan Dockerfile atau menginstal Docker. Pelajari lebih lanjut cara mem-build container Java dengan 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>
    

Mengirimkan kode

Kode pengiriman terdiri dari tiga langkah: mem-build image container dengan Cloud Build, mengupload image container ke Container Registry, dan men-deploy image container ke penayangan Knative.

Untuk kode pengiriman Anda:

  1. Build container Anda dan publikasikan di Container Registry:

    Node.js

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

    Dengan PROJECT_ID sebagai project ID Google Cloud Anda. Anda dapat memeriksa project ID Anda saat ini dengan gcloud config get-value project.

    Setelah berhasil, Anda akan melihat pesan SUCCESS yang berisi ID, waktu pembuatan, dan nama image. Image tersebut disimpan di Container Registry dan dapat digunakan kembali bila diinginkan.

    Python

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

    Dengan PROJECT_ID sebagai project ID Google Cloud Anda. Anda dapat memeriksa project ID Anda saat ini dengan gcloud config get-value project.

    Setelah berhasil, Anda akan melihat pesan SUCCESS yang berisi ID, waktu pembuatan, dan nama image. Image tersebut disimpan di Container Registry dan dapat digunakan kembali bila diinginkan.

    Go

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

    Dengan PROJECT_ID sebagai project ID Google Cloud Anda. Anda dapat memeriksa project ID Anda saat ini dengan gcloud config get-value project.

    Setelah berhasil, Anda akan melihat pesan SUCCESS yang berisi ID, waktu pembuatan, dan nama image. Image tersebut disimpan di Container Registry dan dapat digunakan kembali bila diinginkan.

    Java

    mvn compile jib:build -Dimage=gcr.io/PROJECT_ID/hello-service

    Dengan PROJECT_ID sebagai project ID Google Cloud Anda. Anda dapat memeriksa project ID Anda saat ini dengan gcloud config get-value project.

    Setelah berhasil, Anda akan melihat pesan BUILD SUCCESS. Image tersebut disimpan di Container Registry dan dapat digunakan kembali bila diinginkan.

  2. Jalankan perintah berikut ini untuk menggunakan aplikasi anda:

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

    Ganti PROJECT_ID dengan ID project Google Cloud Anda. hello-service adalah nama image container dan nama layanan penayangan Knative. Perhatikan bahwa image container di-deploy ke layanan dan cluster yang Anda konfigurasikan sebelumnya di bagian Menyiapkan gcloud

    Tunggu hingga deployment selesai: proses ini memerlukan waktu sekitar setengah menit. Apabila berhasil, command line akan menampilkan URL layanan:

Melakukan Percobaan

Cobalah layanan untuk mengonfirmasi bahwa anda telah berhasil men-deploy-nya. Berbagai permintaan akan gagal apabila menggunakan HTTP 500 atau 503 error (anggota-anggota dari kelas 5xx Server errors). Tutorial ini berisi penjelasan mengenai pemecahan masalah pada tanggapan yang error.

Jika cluster Anda dikonfigurasi dengan domain default yang dapat dirutekan, lewati langkah-langkah di atas dan salin URL ke browser web Anda.

Jika tidak menggunakan sertifikat TLS otomatis dan pemetaan domain, Anda tidak akan diberikan URL yang dapat dijelajahi untuk layanan Anda.

Sebagai gantinya, gunakan URL yang disediakan dan alamat IP gateway masuk layanan untuk membuat perintah curl yang dapat membuat permintaan ke layanan Anda:

  1. Untuk mendapatkan IP eksternal untuk Load Balancer, jalankan perintah berikut:

    kubectl get svc istio-ingressgateway -n ASM-INGRESS-NAMESPACE

    Ganti ASM-INGRESS-NAMESPACE dengan namespace tempat ingress Cloud Service Mesh Anda berada. Tentukan istio-system jika Anda menginstal Cloud Service Mesh menggunakan konfigurasi defaultnya.

    Output yang dihasilkan akan terlihat mirip dengan berikut ini:

    NAME                   TYPE           CLUSTER-IP     EXTERNAL-IP  PORT(S)
    istio-ingressgateway   LoadBalancer   XX.XX.XXX.XX   pending      80:32380/TCP,443:32390/TCP,32400:32400/TCP

    dengan nilai EXTERNAL-IP adalah alamat IP eksternal Load Balancer Anda.

  2. Jalankan perintah curl menggunakan alamat GATEWAY_IP ini di URL.

     curl -G -H "Host: SERVICE-DOMAIN" https://EXTERNAL-IP/

    Ganti SERVICE-DOMAIN dengan domain default yang ditetapkan untuk layanan Anda. Anda bisa mendapatkannya dengan mengambil URL default dan menghapus protokol http://.

  3. Lihat pesan error HTTP 500 atau HTTP 503.

Menyelidiki masalah

Visualisasikan bahwa HTTP 5xx error yang ditemukan di atas dalam Trying it out ditemukan sebagai sebuah error runtime production. Tutorial ini berisi penjelasan mengenai proses formal untuk menangani itu. Meskipun proses resolusi error produksi sangat bervariasi, tutorial ini menyajikan langkah-langkah tertentu untuk menunjukkan pengaplikasian dari fitur-fitur dan teknik-teknik yang berguna.

Untuk menyelidiki masalah ini kamu akan menggunakan fase ini:

  • Kumpulkan detail selengkapnya tentang error yang dilaporkan untuk mendukung penyelidikan lebih lanjut dan menetapkan strategi mitigasi.
  • Kurangi dampak pengguna dengan memutuskan untuk meneruskan perbaikan atau rollback ke versi yang sehat.
  • Reproduksi error untuk konfirmasi bahwa detail yang benar telah dikumpulkan dan error bukanlah glitch satu kali
  • Lakukan analisis akar masalah pada bug untuk menemukan kode konfigurasi, atau proses yang membuat error ini

Pada saat memulai investigasi anda memiliki URL, stempel waktu, dan pesan. "Error Server Internal"

Mengumpulkan detail selengkapnya

Kumpulkan informasi selengkapnya tentang masalah untuk memahami yang terjadi dan menentukan langkah selanjutnya.

Gunakan alat yang tersedia untuk mengumpulkan detail selengkapnya:

  1. Lihat log untuk mengetahui detail selengkapnya.

  2. Gunakan Cloud Logging untuk meninjau urutan operasi yang menyebabkan masalah, termasuk pesan error.

Rollback ke versi stabil

Jika memiliki revisi yang Anda ketahui berfungsi, Anda dapat melakukan rollback layanan untuk menggunakan revisi tersebut. Misalnya, Anda tidak akan dapat melakukan rollback pada layanan hello-service baru yang Anda deploy dalam tutorial ini karena hanya berisi satu revisi.

Untuk menemukan revisi dan melakukan rollback layanan:

  1. Mencantumkan semua revisi layanan Anda.

  2. Migrasikan semua traffic ke revisi yang responsif.

Mengulangi error

Gunakan detail anda dapat sebelumnya, konfirmasi masalah yang terjadi secara konsisten pada kondisi tes.

kirim permintaan HTTP yang sama dengan melakukan percobaan lagi, dan lihat jika detail dan error yang sama di laporkan. Itu mungkin membutuhkan beberapa waktu untuk detail error muncul.

Karena layanan sampel pada tutorial ini adalah hanya baca dan tidak dapat memicu berbagai efek samping yang rumit, reproduksi error pada produksi adalah aman. Namun, untuk layanan nyata kebanyakan, ini tidak akan menjadi kasus: anda mungkin butuh untuk mereproduksi error dalam lingkungan pengujian atau batas langkah ini pada penyelidikan lokal.

mereproduksi error akan membangun konteks untuk pekerjaan lebih lanjut. misalnya, jika developer tidak dapat mereproduksi error, penyelidikan selanjutnya mungkin diperlukan instrumentasi tambahan dari layanan.

Melakukan analisis akar masalah

Analisis akar masalah adalah langkah penting dalam pemecahan masalah yang efektif untuk memastikan bahwa anda menyelesaikan masalah, bukan menemukan gejala.

Sebelumnya dalam tutorial ini, Anda mereproduksi masalah pada penayangan Knative yang mengonfirmasi bahwa masalah aktif saat layanan dihosting pada penayangan Knative. Sekarang, reproduksi masalah secara lokal untuk menentukan apakah masalah tersebut terisolasi pada kode atau itu hanya muncul di hosting produksi.

  1. Jika Anda belum menggunakan Docker CLI secara lokal dengan mengautentikasi Container Registry, itu dengan gcloud:

    gcloud auth configure-docker

    Untuk pendekatan alternatif lihat metode autentifikasi Container Registry.

  2. Jika nama image container yang paling baru digunakan tidak tersedia, deskripsi layanan tersebut memiliki informasi mengenai image container yang di-deploy paling baru:

    gcloud run services describe hello-service

    Temukan nama image container di dalam objek spec. Perintah yang lebih ditargetkan dapat secara langsung mengambilnya:

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

    Perintah ini menampilkan nama image container seperti gcr.io/PROJECT_ID/hello-service.

  3. Tarik image container dari Container Registry ke lingkungan anda. langkah ini mungkin memerlukan waktu beberapa menit saat mendownload image container:

    docker pull gcr.io/PROJECT_ID/hello-service

    pembaruan selanjutnya pada image container yang menggunakan kembali nama ini dapat diambil dengan perintah yang sama. Jika anda melewati langkah ini, perintah docker run di bawah akan menarik image container jika tidak ada di komputer lokal.

  4. Jalankan secara lokal untuk mengonfirmasi bahwa masalahnya tidak hanya terjadi pada penayangan Knative:

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

    lihat rincian elemen dari perintah di atas,

    • Variabel lingkungan PORT digunakan oleh layanan untuk menentukan port yang akan memproses di dalam container.
    • Perintah run memulai container, secara default ke perintah entrypoint yang ditentukan di Dockerfile atau image container induk.
    • Flag --rm menghapus instance penampung saat keluar.
    • Flag -e menetapkan nilai ke variabel lingkungan. -e PORT=$PORT menerapkan variabel PORT dari sistem lokal ke dalam container dengan nama variabel yang sama.
    • Flag -p memublikasikan container sebagai layanan yang tersedia di host lokal pada port 9000. Permintaan ke host lokal:9000 akan diarahkan kepada penampung pada port 8080. Artinya, output dari layanan tentang nomor port yang digunakan tidak akan cocok dengan cara layanan yang diakses.
    • Argumen terakhir gcr.io/PROJECT_ID/hello-service adalah jalur repositori yang mengarah ke versi terbaru image penampung. Jika tidak tersedia secara lokal, Docker akan mencoba mengambil image dari registry jarak jauh.

    Pada browser anda, buka http://localhost:9000. Periksa output terminal untuk menemukan pesan error yang cocok dengan pesan di Google Cloud Observability.

    Jika masalah tidak dapat direproduksi secara lokal, masalah mungkin hanya terjadi di lingkungan penayangan Knative. Tinjau panduan pemecahan masalah penayangan Knative untuk area tertentu yang perlu diselidiki.

    Dalam hal ini, error akan direproduksi secara lokal.

Setelah error dikonfirmasi dua kali sebagai persisten dan disebabkan oleh kode layanan bukan platform hosting, saatnya untuk menyelidiki kode tersebut lebih mendalam.

Untuk tujuan tutorial ini, anda dapat mengasumsikan bahwa kode di dalam penampung dan kode di sistem lokal adalah identik.

Node.js

Temukan sumber pesan error di dalam file index.js di sekitar nomor baris yang dipanggil dalam pelacakan tumpukan yang ditampilkan dalam log:
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

Temukan sumber pesan error di dalam file main.py di sekitar nomor baris yang dipanggil dalam pelacakan tumpukan yang ditampilkan dalam log:
NAME = os.getenv("NAME")

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

Go

Temukan sumber pesan error di dalam file main.go di sekitar nomor baris yang dipanggil dalam pelacakan tumpukan yang ditampilkan dalam log:

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

Temukan sumber dari pesan error dalam file App.java di sekitar nomor baris yang dipanggil dalam pelacakan tumpukan yang ditampilkan dalam log:

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";
}

Memeriksa kode ini, tindakan berikut akan dilakukan saat variabel lingkungan NAME tidak ditetapkan:

  • Error dicatat ke dalam log Google Cloud Observability
  • Respon error HTTP dikirim

Masalah ini disebabkan oleh tidak adanya variabel, tetapi akar masalahnya lebih spesifik: perubahan kode yang menambahkan dependensi keras pada variabel lingkungan tidak menyertakan perubahan terkait pada skrip deployment dan dokumentasi persyaratan runtime.

Memperbaiki akar masalah

Setelah mengumpulkan kode dan mengidentifikasi potensi penyebab utamanya, kita dapat mengambil langkah-langkah untuk memperbaikinya.

  • Periksa apakah layanan berfungsi secara lokal dengan lingkungan NAME yang tersedia di tempat:

    1. Jalankan container secara lokal dengan variabel lingkungan yang telah ditambahkan:

      PORT=8080 && docker run --rm -e PORT=$PORT -p 9000:$PORT \
       -e NAME="Local World!" \
       gcr.io/PROJECT_ID/hello-service
    2. Buka http://localhost:9000 pada browser anda

    3. Lihat "Hello Local World!" muncul pada halaman tersebut

  • Ubah lingkungan layanan penayangan Knative yang sedang berjalan untuk menyertakan variabel ini:

    1. Jalankan perintah update layanan dengan parameter --update-env-vars untuk menambahkan variabel lingkungan:

      gcloud run services update hello-service \
        --update-env-vars NAME=Override
      
    2. Tunggu beberapa detik saat penayangan Knative membuat revisi baru berdasarkan revisi sebelumnya dengan menambahkan variabel lingkungan baru.

  • Pastikan layanan kini telah diperbaiki:

    1. Arahkan browser Anda ke URL layanan penayangan Knative.
    2. Lihat "Halo Penggantian!" akan muncul pada halaman tersebut.
    3. Pastikan tidak ada pesan atau error yang tidak terduga yang muncul di Cloud Logging.

Meningkatkan kecepatan pemecahan masalah di masa mendatang

Dalam contoh masalah produksi ini, error terkait dengan konfigurasi operasional. Ada perubahan kode yang akan meminimalkan dampak masalah ini di masa mendatang.

  • Memperbaiki log error untuk menyertakan detail yang lebih spesifik.
  • Daripada menampilkan error, buat layanan kembali ke default yang aman. Jika penggunaan default mewakili perubahan pada fungsi normal, gunakan pesan peringatan untuk tujuan pemantauan.

Mari kita lanjutkan dengan menghapus variabel lingkungan NAME sebagai dependensi kuat.

  1. Hapus kode penanganan NAME yang sudah ada:

    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. Tambahkan kode baru yang menetapkan nilai penggantian:

    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. Uji secara lokal dengan mem-build ulang dan menjalankan container melalui kasus konfigurasi yang terpengaruh:

    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

    Pastikan variabel lingkungan NAME masih berfungsi:

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

    Pastikan layanan berfungsi tanpa variabel NAME:

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

    Jika layanan tidak menampilkan hasil, pastikan penghapusan kode pada langkah pertama tidak menghapus baris tambahan, seperti yang digunakan untuk menulis respons.

  4. Deploy ini dengan membuka kembali bagian Men-deploy kode Anda.

    Setiap deployment ke layanan membuat revisi baru dan otomatis mulai menyalurkan traffic jika sudah siap.

    Untuk menghapus variabel lingkungan yang telah disetel sebelumnya:

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

Tambahkan fungsi baru untuk nilai default ke cakupan pengujian otomatis untuk layanan.

Menemukan masalah lain di log

Anda mungkin melihat masalah lain di Log Viewer untuk layanan ini. Misalnya, panggilan sistem yang tidak didukung akan muncul di log sebagai "Container Sandbox Limitation".

Misalnya, layanan Node.js terkadang menghasilkan pesan log ini:

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.

Dalam hal ini, kurangnya dukungan tidak mempengaruhi layanan sampel hello-service.

Pembersihan

Anda dapat menghapus resource yang dibuat untuk tutorial ini agar tidak menimbulkan biaya.

Menghapus resource tutorial

  1. Hapus layanan penayangan Knative yang Anda deploy dalam tutorial ini:

    gcloud run services delete SERVICE-NAME

    Dengan SERVICE-NAME adalah nama layanan pilihan Anda.

    Anda juga dapat menghapus layanan penayangan Knative dari konsol Google Cloud:

    Buka Inferensi Knative

  2. Hapus konfigurasi default gcloud yang Anda tambahkan selama penyiapan tutorial:

     gcloud config unset run/platform
     gcloud config unset run/cluster
     gcloud config unset run/cluster_location
    
  3. Hapus konfigurasi project:

     gcloud config unset project
    
  4. Hapus resource Google Cloud lain yang dibuat dalam tutorial ini:

Langkah selanjutnya