Observability באפליקציות gRPC בלי שרת Proxy

כלים לניטור מיקרו-שירותים מאפשרים לכם להטמיע את האפליקציות שלכם כדי לאסוף ולהציג נתוני טלמטריה ב-Cloud Monitoring, ב-Cloud Logging וב-Cloud Trace מעומסי עבודה של gRPC שנפרסו ב- Google Cloud, כולל עומסי עבודה של gRPC ב-Cloud Service Mesh.

לקוחות ושרתים של gRPC משולבים עם OpenCensus כדי לייצא מדדים ועקבות למגוון של מערכות עורפיות, כולל Trace ו-Monitoring. אפשר לעשות את זה בשפות הבאות של gRPC:

  • C++‎
  • Go
  • Java

מומלץ לקרוא את סקירת הניראות של מיקרו-שירותים, ואז לפעול לפי ההוראות שבמאמר הגדרה של הניראות של מיקרו-שירותים כדי להגדיר את עומסי העבודה של gRPC ל:

ההוראות במסמך הזה מתייחסות למשימות הבאות:

צפייה בפרטי ההעברה בכלי Trace

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

תאימות של Trace עם שרת ה-proxy של Envoy

ייצוא נתוני מעקב אל Trace באמצעות Cloud Service Mesh ו-Envoy proxy, כפי שמתואר במאמר Observability with Envoy, מתבצע באמצעות הגדרת כלי המעקב OpenCensus של Envoy, שמאפשר תאימות מלאה בין נתוני מעקב שמיוצאים מאפליקציות gRPC בלי שרת Proxy ומ-Envoy proxy בתוך Service mesh. כדי להבטיח תאימות ל-gRPC בלי שרת Proxy, צריך להגדיר את הקשר של המעקב ב-bootstrap של Envoy כך שיכלול את GRPC_TRACE_BIN בפורמט המעקב ב-OpenCensusConfig שלו. לדוגמה:

tracing:
  http:
      name: envoy.tracers.opencensus
      typed_config:
        "@type": type.googleapis.com/envoy.config.trace.v2.OpenCensusConfig
        stackdriver_exporter_enabled: "true"
        stackdriver_project_id: "PROJECT_ID"
        incoming_trace_context: ["CLOUD_TRACE_CONTEXT", "GRPC_TRACE_BIN"]
        outgoing_trace_context: ["CLOUD_TRACE_CONTEXT", "GRPC_TRACE_BIN"]

חשיפת ממשק האדמין

לפעמים, מדדים ונתוני מעקב לא מספיקים כדי לפתור בעיה. יכול להיות שתצטרכו לבדוק את ההגדרה או את מצב זמן הריצה של ספריית gRPC באפליקציה. המידע הזה כולל פרטים על מפענח ה-DNS, מצב הקישוריות לעמיתים, נתונים סטטיסטיים של RPC בערוץ וההגדרה שהתקבלה מ-Cloud Service Mesh.

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

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

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

C++‎

ב-C++‎, משתמשים בקוד הזה כדי ליצור ממשק אדמין:

#include <grpcpp/ext/admin_services.h>

grpc::ServerBuilder builder;
grpc::AddAdminServices(&builder);
builder.AddListeningPort(":50051", grpc::ServerCredentials(...));
std::unique_ptr<grpc::Server> server(builder.BuildAndStart());

Go

ב-Go, משתמשים בקוד הזה כדי ליצור ממשק אדמין:

import "google.golang.org/grpc/admin"

lis, err := net.Listen("tcp", ":50051")
if err != nil {
        log.Fatalf("failed to listen: %v", err)
}
defer lis.Close()
grpcServer := grpc.NewServer(...opts)
cleanup, err := admin.Register(grpcServer)
if err != nil {
        log.Fatalf("failed to register admin services: %v", err)
}
defer cleanup()

if err := grpcServer.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
}

Java

ב-Java, משתמשים בקוד הזה כדי ליצור ממשק אדמין:

import io.grpc.services.AdminInterface;

server = ServerBuilder.forPort(50051)
        .useTransportSecurity(certChainFile, privateKeyFile)
        .addServices(AdminInterface.getStandardServices())
        .build()
        .start();
server.awaitTermination();

Python

ב-Python, משתמשים בקוד הזה כדי ליצור ממשק אדמין:

import grpc_admin

server = grpc.server(futures.ThreadPoolExecutor())
grpc_admin.add_admin_servicers(server)
server.add_insecure_port('[::]:50051')
server.start()
server.wait_for_termination()

שימוש ב-SSH כדי להתחבר למכונה וירטואלית

בדוגמה של gRPC Wallet ממשק האדמין כבר מופעל. אפשר לשנות את יציאת ממשק האדמין באמצעות הדגל הבא:

 --admin-port=PORT

יציאת האדמין שמוגדרת כברירת מחדל היא localhost:28881.

כדי לנפות באגים באפליקציית gRPC, אפשר להשתמש ב-SSH כדי להתחבר לאחת מהמכונות הווירטואליות שמריצות את wallet-service. כך מקבלים גישה אל localhost.

# List the Wallet VMs
$ gcloud compute instances list --filter="zone:(us-central1-a)" --filter="name~'grpcwallet-wallet-v2'"
NAME                                       ZONE            MACHINE_TYPE   PREEMPTIBLE  INTERNAL_IP    EXTERNAL_IP     STATUS
grpcwallet-wallet-v2-mig-us-central1-ccl1  us-central1-a   n1-standard-1               10.240.0.38    35.223.42.98    RUNNING
grpcwallet-wallet-v2-mig-us-central1-k623  us-central1-a   n1-standard-1               10.240.0.112   35.188.133.75   RUNNING
# Pick one of the Wallet VMs to debug
$ gcloud compute ssh grpcwallet-wallet-v2-mig-us-central1-ccl1 --zone=us-central1-a

התקנת הכלי grpcdebug

כדי לגשת לממשק האדמין, צריך לקוח gRPC שיכול לתקשר עם שירותי האדמין באפליקציית gRPC. בדוגמאות הבאות משתמשים בכלי שנקרא grpcdebug שאפשר להוריד ולהתקין במכונה וירטואלית או ב-Pod שבהם פועלת אפליקציית gRPC. המאגר של grpcdebug נמצא בכתובת grpc-ecosystem/grpcdebug.

הגרסה המינימלית של Golang שנתמכת היא 1.12. מדריך ההתקנה הרשמי של Golang זמין באתר Golang. אם אתם פועלים לפי המדריך ליצירת מכונת VM של Linux עבור wallet-service, אתם יכולים להתקין את Golang 1.16 באמצעות הפקודות הבאות:

sudo apt update && sudo apt install -y wget
wget https://golang.org/dl/go1.16.3.linux-amd64.tar.gz
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.16.3.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin
sudo ln -sf /usr/local/go/bin/go /usr/bin/go
go version
# go version go1.16.3 linux/amd64

כדי להתקין את הכלי grpcdebug, מריצים את הפקודות הבאות:

go install -v github.com/grpc-ecosystem/grpcdebug@latest
export PATH=$PATH:$(go env GOPATH)/bin

עכשיו יש לכם גישה לממשק שורת הפקודה grpcdebug. פלט העזרה מכיל מידע על פקודות נתמכות:

$ grpcdebug -h
grpcdebug is a gRPC service admin command-line interface

Usage:
  grpcdebug <target address> [flags]  <command>

Available Commands:
  channelz    Display gRPC states in human readable way.
  health      Check health status of the target service (default "").
  help        Help about any command
  xds         Fetch xDS related information.

Flags:
      --credential_file string        Sets the path of the credential file; used in [tls] mode
  -h, --help                          Help for grpcdebug
      --security string               Defines the type of credentials to use [tls, google-default, insecure] (default "insecure")
      --server_name_override string   Overrides the peer server name if non empty; used in [tls] mode
  -t, --timestamp                     Print timestamp as RFC 3339 instead of human readable strings
  -v, --verbose                       Print verbose information for debugging

כדי לקבל מידע נוסף על פקודה מסוימת, משתמשים בפקודה הבאה:

 grpcdebug <target address> [command] --help

שימוש בכלי grpcdebug לניפוי באגים באפליקציות

אפשר להשתמש בכלי grpcdebug כדי לנפות באגים באפליקציות. הכלי grpcdebug מספק הגדרה שדומה ל-ssh_config ותומך בכינויים, בשכתוב של שם המארח ובהגדרת אבטחת החיבור (לא מאובטח/TLS). מידע נוסף על התכונה המתקדמת הזו זמין במאמר grpcdebug/Connect&Security.

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

שימוש ב-Channelz

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

 --admin-port=PORT

אחרי ששולחים כמה בקשות RPC מלקוח בדיקה, כמו שמוסבר בקטע אימות ההגדרה, משתמשים בפקודות הבאות כדי לגשת לנתוני Channelz של שירותי gRPC:

  1. משתמשים ב-SSH כדי להתחבר למכונה וירטואלית שמופעל בה wallet-service.
  2. מגדירים את grpcdebug כדי להתחבר לאפליקציית gRPC שפועלת.

פלט ברירת המחדל של grpcdebug הוא בפורמט של טבלה שמתאים למסוף. אם מציינים את הדגל --json, הפלט מקודד כ-JSON.

הפקודה grpcdebug channelz משמשת לאחזור ולהצגה של מידע על ניפוי באגים משירות Channelz. הפקודה פועלת גם בלקוחות gRPC וגם בשרתי gRPC.

במקרה של לקוחות gRPC, הפקודה grpcdebug channelz channels מספקת רשימה של ערוצים קיימים ומידע בסיסי:

$ grpcdebug localhost:28881 channelz channels
Channel ID   Target                               State     Calls(Started/Succeeded/Failed)   Created Time
1            xds:///account.grpcwallet.io:10080   READY     0/0/0                             59 seconds ago
2            trafficdirector.googleapis.com:443   READY     2/0/0                             59 seconds ago
4            xds:///stats.grpcwallet.io:10080     READY     0/0/0                             59 seconds ago

אם אתם צריכים מידע נוסף על ערוץ מסוים, אתם יכולים להשתמש בgrpcdebug channelz channel [CHANNEL_ID] כדי לבדוק מידע מפורט על הערוץ הזה. מזהה הערוץ יכול להיות מזהה הערוץ או כתובת היעד, אם יש רק כתובת יעד אחת. ערוץ gRPC יכול להכיל כמה ערוצי משנה, שהם הפשטה של gRPC מעל חיבור TCP.

$ grpcdebug localhost:28881 channelz channel 2
Channel ID:        2
Target:            trafficdirector.googleapis.com:443
State:             READY
Calls Started:     2
Calls Succeeded:   0
Calls Failed:      0
Created Time:      10 minutes ago
---
Subchannel ID   Target                               State     Calls(Started/Succeeded/Failed)   CreatedTime
3               trafficdirector.googleapis.com:443   READY     2/0/0                             10 minutes ago
---
Severity   Time             Child Ref                      Description
CT_INFO    10 minutes ago                                  Channel Created
CT_INFO    10 minutes ago                                  parsed scheme: ""
CT_INFO    10 minutes ago                                  scheme "" not registered, fallback to default scheme
CT_INFO    10 minutes ago                                  ccResolverWrapper: sending update to cc: {[{trafficdirector.googleapis.com:443  <nil> 0 <nil>}] <nil> <nil>}
CT_INFO    10 minutes ago                                  Resolver state updated: {Addresses:[{Addr:trafficdirector.googleapis.com:443 ServerName: Attributes:<nil> Type:0 Metadata:<nil>}] ServiceConfig:<nil> Attributes:<nil>} (resolver returned new addresses)
CT_INFO    10 minutes ago                                  ClientConn switching balancer to "pick_first"
CT_INFO    10 minutes ago                                  Channel switches to new LB policy "pick_first"
CT_INFO    10 minutes ago   subchannel(subchannel_id:3 )   Subchannel(id:3) created
CT_INFO    10 minutes ago                                  Channel Connectivity change to CONNECTING
CT_INFO    10 minutes ago                                  Channel Connectivity change to READY

אפשר גם לבדוק מידע מפורט על ערוץ משני:

$ grpcdebug localhost:28881 channelz subchannel 3
Subchannel ID:     3
Target:            trafficdirector.googleapis.com:443
State:             READY
Calls Started:     2
Calls Succeeded:   0
Calls Failed:      0
Created Time:      12 minutes ago
---
Socket ID   Local->Remote                           Streams(Started/Succeeded/Failed)   Messages(Sent/Received)
9           10.240.0.38:60338->142.250.125.95:443   2/0/0                               214/132

אפשר לאחזר מידע על שקעי TCP:

$ grpcdebug localhost:28881 channelz socket 9
Socket ID:                       9
Address:                         10.240.0.38:60338->142.250.125.95:443
Streams Started:                 2
Streams Succeeded:               0
Streams Failed:                  0
Messages Sent:                   226
Messages Received:               141
Keep Alives Sent:                0
Last Local Stream Created:       12 minutes ago
Last Remote Stream Created:      a long while ago
Last Message Sent Created:       8 seconds ago
Last Message Received Created:   8 seconds ago
Local Flow Control Window:       65535
Remote Flow Control Window:      966515
---
Socket Options Name   Value
SO_LINGER             [type.googleapis.com/grpc.channelz.v1.SocketOptionLinger]:{duration:{}}
SO_RCVTIMEO           [type.googleapis.com/grpc.channelz.v1.SocketOptionTimeout]:{duration:{}}
SO_SNDTIMEO           [type.googleapis.com/grpc.channelz.v1.SocketOptionTimeout]:{duration:{}}
TCP_INFO              [type.googleapis.com/grpc.channelz.v1.SocketOptionTcpInfo]:{tcpi_state:1  tcpi_options:7  tcpi_rto:204000  tcpi_ato:40000  tcpi_snd_mss:1408  tcpi_rcv_mss:1408  tcpi_last_data_sent:8212  tcpi_last_data_recv:8212  tcpi_last_ack_recv:8212  tcpi_pmtu:1460  tcpi_rcv_ssthresh:88288  tcpi_rtt:2400  tcpi_rttvar:3012  tcpi_snd_ssthresh:2147483647  tcpi_snd_cwnd:10  tcpi_advmss:1408  tcpi_reordering:3}
---
Security Model:   TLS
Standard Name:    TLS_AES_128_GCM_SHA256

בצד השרת, אפשר להשתמש ב-Channelz כדי לבדוק את הסטטוס של אפליקציית השרת. לדוגמה, אפשר לקבל את רשימת השרתים באמצעות הפקודה grpcdebug channelz servers:

$ grpcdebug localhost:28881 channelz servers
Server ID   Listen Addresses    Calls(Started/Succeeded/Failed)   Last Call Started
5           [127.0.0.1:28881]   9/8/0                             now
6           [[::]:50051]        159/159/0                         4 seconds ago

כדי לקבל מידע נוסף על שרת ספציפי, משתמשים בפקודה grpcdebug channelz server. אפשר לבדוק שקעי שרת באותו אופן שבו בודקים שקעי לקוח.

$ grpcdebug localhost:28881 channelz server 6
Server Id:           6
Listen Addresses:    [[::]:50051]
Calls Started:       174
Calls Succeeded:     174
Calls Failed:        0
Last Call Started:   now
---
Socket ID   Local->Remote                            Streams(Started/Succeeded/Failed)   Messages(Sent/Received)
25          10.240.0.38:50051->130.211.1.39:44904    68/68/0                             68/68
26          10.240.0.38:50051->130.211.0.167:32768   54/54/0                             54/54
27          10.240.0.38:50051->130.211.0.22:32768    52/52/0                             52/52

שימוש בשירות לגילוי סטטוס הלקוח

‫API של Client Status Discovery Service ‏(CSDS) הוא חלק מ-xDS APIs. באפליקציית gRPC, שירות ה-CSDS מספק גישה להגדרה (שנקראת גם הגדרת xDS) שהוא מקבל מ-Cloud Service Mesh. כך תוכלו לזהות ולפתור בעיות שקשורות להגדרות ברשת ה-Mesh.

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

כדי להשתמש ב-CSDS לבדיקת ההגדרה:

  1. משתמשים ב-SSH כדי להתחבר למכונה וירטואלית שמופעל בה wallet-service. פועלים לפי ההוראות במאמר שימוש ב-SSH כדי להתחבר למכונה וירטואלית.
  2. מריצים את לקוח grpcdebug.

כדי לקבל סקירה כללית של סטטוס ההגדרה, מריצים את הפקודה הבאה:

grpcdebug localhost:28881 xds status

התוצאות אמורות להיראות כך:

Name                                                                    Status    Version               Type                                                                 LastUpdated
account.grpcwallet.io:10080                                             ACKED     1618529574783547920   type.googleapis.com/envoy.config.listener.v3.Listener                3 seconds ago
stats.grpcwallet.io:10080                                               ACKED     1618529574783547920   type.googleapis.com/envoy.config.listener.v3.Listener                3 seconds ago
URL_MAP/830293263384_grpcwallet-url-map_0_account.grpcwallet.io:10080   ACKED     1618529574783547920   type.googleapis.com/envoy.config.route.v3.RouteConfiguration         3 seconds ago
URL_MAP/830293263384_grpcwallet-url-map_1_stats.grpcwallet.io:10080     ACKED     1618529574783547920   type.googleapis.com/envoy.config.route.v3.RouteConfiguration         3 seconds ago
cloud-internal-istio:cloud_mp_830293263384_3566964729007423588          ACKED     1618529574783547920   type.googleapis.com/envoy.config.cluster.v3.Cluster                  3 seconds ago
cloud-internal-istio:cloud_mp_830293263384_7383783194368524341          ACKED     1618529574783547920   type.googleapis.com/envoy.config.cluster.v3.Cluster                  3 seconds ago
cloud-internal-istio:cloud_mp_830293263384_3363366193797120473          ACKED     1618529574783547920   type.googleapis.com/envoy.config.cluster.v3.Cluster                  3 seconds ago
cloud-internal-istio:cloud_mp_830293263384_3566964729007423588          ACKED     86                    type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment   2 seconds ago
cloud-internal-istio:cloud_mp_830293263384_3363366193797120473          ACKED     86                    type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment   2 seconds ago
cloud-internal-istio:cloud_mp_830293263384_7383783194368524341          ACKED     86                    type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment   2 seconds ago

ההגדרה של סטטוס ההגדרה מופיעה במסמכי התיעוד של Envoy proxy. בקצרה, הסטטוס של משאב xDS הוא אחד מהערכים REQUESTED, DOES_NOT_EXIST, ACKED או NACKED.

כדי לקבל dump של הגדרות xDS גולמיות, מריצים את הפקודה הבאה:

grpcdebug localhost:28881 xds config

מוצגת רשימה של JSON אובייקטים של PerXdsConfig:

{
  "config":  [
    {
      "node":  {
        "id":  "projects/830293263384/networks/default/nodes/6e98b038-6d75-4a4c-8d35-b0c7a8c9cdde",
        "cluster":  "cluster",
        "metadata":  {
          "INSTANCE_IP":  "10.240.0.38",
          "TRAFFICDIRECTOR_GCP_PROJECT_NUMBER":  "830293263384",
          "TRAFFICDIRECTOR_NETWORK_NAME":  "default"
        },
        "locality":  {
          "zone":  "us-central1-a"
        },
        "userAgentName":  "gRPC Go",
        "userAgentVersion":  "1.37.0",
        "clientFeatures":  [
          "envoy.lb.does_not_support_overprovisioning"
        ]
      },
      "xdsConfig":  [
        {
          "listenerConfig":  {
            "versionInfo":  "1618529930989701137",
            "dynamicListeners":  [
              {
...

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

$ grpcdebug localhost:28881 xds config --type=cds
{
  "versionInfo":  "1618530076226619310",
  "dynamicActiveClusters":  [
    {
      "versionInfo":  "1618530076226619310",
      "cluster":  {
        "@type":  "type.googleapis.com/envoy.config.cluster.v3.Cluster",
        "name":  "cloud-internal-istio:cloud_mp_830293263384_7383783194368524341",
        "altStatName":  "/projects/830293263384/global/backendServices/grpcwallet-stats-service",
        "type":  "EDS",
        "edsClusterConfig":  {
          "edsConfig":  {
            "ads":  {},
            "initialFetchTimeout":  "15s",
...

אפשר גם להציג את ההגדרות של כמה סוגי xDS בו-זמנית:

$ grpcdebug localhost:28881 xds config --type=lds,eds
{
  "versionInfo":  "1618530076226619310",
  "dynamicListeners":  [...]
}
{
  "dynamicEndpointConfigs":  [...]
}

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