נתוני מעקב של הכלים באמצעות OpenTelemetry

1. מבוא

5af4a7e43b0feaab.png

עדכון אחרון: 2021-03-05

יכולת התבוננות באפליקציה

יכולת צפייה ו-OpenTelemetry

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

OpenTelemetry היא קבוצה של מפרטים וערכות SDK שמאיצה את האינסטרומנטציה ואת הייצוא של נתוני טלמטריה (יומנים, מדדים ועקבות) שנדרשים לצורך ניראות (observability). ‫OpenTelemetry הוא פרויקט קוד פתוח מבוסס-קהילה במסגרת CNCF. באמצעות ספריות שהפרויקט והמערכת האקולוגית שלו מספקים, מפתחים יכולים להטמיע את האפליקציות שלהם בדרך ניטרלית לספק ובהתאם לארכיטקטורות שונות.

Distributed Trace

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

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

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

adbd3ecd69d410cb.png

‫Google Cloud Trace היא אחת מהאפשרויות ל-backend של מעקב מבוזר, והיא משולבת היטב עם מוצרים אחרים ב-Google Cloud.

מה תפַתחו

ב-Codelab הזה נסביר איך להגדיר מעקב אחר מידע בשירותים שנקראים Shakesapp שפועלים באשכול Kubernetes ב-Google Kubernetes Engine. הארכיטקטורה של Shakesapp היא כפי שמתואר בהמשך:

68873c018a7be7de.png

  • הלקוחות שולחים מחרוזת שאילתה לשרת
  • השרת מקבל את השאילתה מהלקוח, מאחזר את כל היצירות של שייקספיר בפורמט טקסט מ-Google Cloud Storage, מחפש את השורות שמכילות את השאילתה ומחזיר ללקוח את מספר השורה שתואמת לשאילתה.

תטמיעו את פרטי המעקב בבקשה.

מה תלמדו

  • איך מתחילים להשתמש בספריות OpenTelemetry Trace בפרויקט Python
  • איך יוצרים תג span באמצעות הספרייה
  • איך מעבירים הקשרים של יחידות לוגיות למעקב בין רכיבי אפליקציה
  • איך שולחים נתוני מעקב אל Google Cloud Trace
  • איך מנתחים את ה-trace ב-Google Cloud Trace

ב-Codelab הזה מוסבר איך להטמיע את המיקרו-שירותים. כדי שיהיה קל להבין, הדוגמה הזו מכילה רק 3 רכיבים (מחולל עומסים, לקוח ושרת), אבל אפשר להשתמש באותו תהליך שמוסבר ב-codelab הזה גם במערכות מורכבות וגדולות יותר.

מה תצטרכו

  • ידע ב-Python 3

2. הגדרה ודרישות

הגדרת סביבה בקצב אישי

אם עדיין אין לכם חשבון Google (Gmail או Google Apps), אתם צריכים ליצור חשבון. נכנסים אל Google Cloud Platform Console‏ ( console.cloud.google.com) ויוצרים פרויקט חדש.

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

15b8b6ac4d917005.png

ולוחצים על הלחצן 'פרויקט חדש' בתיבת הדו-שיח שמופיעה כדי ליצור פרויקט חדש:

7136b3ee36ebaf89.png

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

90977ce514204b51.png

בתיבת הדו-שיח הבאה ליצירת פרויקט, אפשר להזין את הפרטים של הפרויקט החדש:

6d9573e346e930b4.png

חשוב לזכור את מזהה הפרויקט, שהוא שם ייחודי בכל הפרויקטים ב-Google Cloud (השם שלמעלה כבר תפוס ולא יתאים לכם, מצטערים!). בהמשך ה-codelab הזה, הוא יופיע כ-PROJECT_ID.

לאחר מכן, אם עדיין לא עשיתם זאת, תצטרכו להפעיל את החיוב במסוף למפתחים כדי להשתמש במשאבים של Google Cloud ולהפעיל את Cloud Trace API.

eb5325f65619ad6a.png

העלות של התרגול הזה לא אמורה להיות גבוהה מכמה דולרים, אבל היא יכולה להיות גבוהה יותר אם תחליטו להשתמש ביותר משאבים או אם תשאירו אותם פועלים (ראו את הקטע 'ניקוי' בסוף המסמך הזה). המחירים של Google Cloud Trace, ‏ Google Kubernetes Engine ו-Google Artifacat Registry מפורטים במסמכים הרשמיים.

משתמשים חדשים ב-Google Cloud Platform זכאים לתקופת ניסיון בחינם בשווי 300$, כך שסדנת ה-codelab הזו אמורה להיות בחינם לגמרי.

הגדרה של Google Cloud Shell

אפשר להפעיל את Google Cloud ואת Google Cloud Trace מרחוק מהמחשב הנייד, אבל ב-codelab הזה נשתמש ב-Google Cloud Shell, סביבת שורת פקודה שפועלת בענן.

המכונה הווירטואלית הזו מבוססת על Debian, וטעונים בה כל הכלים הדרושים למפתחים. יש בה ספריית בית בנפח מתמיד של 5GB והיא פועלת ב-Google Cloud, מה שמשפר מאוד את הביצועים והאימות של הרשת. כלומר, כל מה שצריך כדי לבצע את ההוראות במאמר הזה הוא דפדפן (כן, זה עובד ב-Chromebook).

כדי להפעיל את Cloud Shell ממסוף Cloud, פשוט לוחצים על 'הפעלת Cloud Shell' gcLMt5IuEcJJNnMId-Bcz3sxCd0rZn7IzT_r95C8UZeqML68Y1efBG_B0VRp7hc7qiZTLAF-TXD7SsOadxn8uadgHhaLeASnVS3ZHK39eOlKJOgj9SJua_oeGhMxRrbOg3qigddS2A (הקצאת המשאבים והחיבור לסביבה אמורים להימשך רק כמה רגעים).

ff81d016724c4f67.png

fbe156ee6edfbb2e.png

אחרי שמתחברים ל-Cloud Shell, אמור להופיע אימות שכבר בוצע, ושהפרויקט כבר הוגדר ל-PROJECT_ID.

gcloud auth list

פלט הפקודה

Credentialed accounts:
 - <myaccount>@<mydomain>.com (active)
gcloud config list project

פלט הפקודה

[core]
project = <PROJECT_ID>

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

gcloud config set project <PROJECT_ID>

מחפש את PROJECT_ID? כדאי לבדוק באיזה מזהה השתמשתם בשלבי ההגדרה, או לחפש אותו בלוח הבקרה של Cloud Console:

a3e716fc9e7454e9.png

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

echo $GOOGLE_CLOUD_PROJECT

פלט הפקודה

<PROJECT_ID>

לבסוף, מגדירים את אזור ברירת המחדל ואת הגדרת הפרויקט.

gcloud config set compute/zone us-central1-f

אפשר לבחור מתוך מגוון אזורים שונים. מידע נוסף זמין במאמר בנושא אזורים ותחומים.

הגדרה של Python

ב-codelab הזה אנחנו משתמשים ב-poetry כדי לנהל גרסאות של חבילות בצורה קפדנית. מריצים את הפקודה הבאה ב-Cloud Shell:

curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python3 -
source $HOME/.poetry/env

הגדרה של אשכול Google Kubernetes

ב-Codelab הזה נריץ אשכול של מיקרו-שירותים ב-Google Kubernetes Engine ‏ (GKE). תהליך ה-codelab הוא כדלקמן:

  1. הורדת פרויקט הבסיס ל-Cloud Shell
  2. איך יוצרים מיקרו-שירותים בקונטיינרים
  3. העלאת קונטיינרים ל-Google Artifact Registry ‏ (GAR)
  4. פריסת קונטיינרים ב-GKE
  5. שינוי קוד המקור של שירותים לצורך מעקב אחר מכשור
  6. מעבר לשלב 2

הפעלת Kubernetes Engine

קודם כל, אנחנו מגדירים אשכול Kubernetes שבו Shakesapp פועלת ב-GKE, ולכן אנחנו צריכים להפעיל את GKE. עוברים לתפריט Kubernetes Engine ולוחצים על הלחצן ENABLE (הפעלה).

56c680e93e169731.png

עכשיו אפשר ליצור אשכול Kubernetes.

יצירת אשכול Kubernetes

ב-Cloud Shell, מריצים את הפקודה הבאה כדי ליצור אשכול Kubernetes. עליך לוודא שערך האזור נמצא מתחת לאזור שבו השתמשת ליצירת מאגר Artifact Registry. שנה את הערך של התחום (zone) us-central1-f אם האזור של המאגר לא כולל את התחום (zone).

gcloud container clusters create otel-trace-codelab --zone us-central1-f \
--num-nodes 1 \
--machine-type e2-highcpu-4

פלט הפקודה

Creating cluster otel-trace-codelab in us-central1-f... Cluster is being health-checked (master is healthy)...done.
Created [https://container.googleapis.com/v1/projects/psychic-order-307806/zones/us-central1-f/clusters/otel-trace-codelab].
To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/us-central1-f/otel-trace-codelab?project=psychic-order-307806
kubeconfig entry generated for otel-trace-codelab.
NAME                LOCATION       MASTER_VERSION    MASTER_IP        MACHINE_TYPE  NODE_VERSION      NUM_NODES  STATUS
otel-trace-codelab  us-central1-f  1.18.12-gke.1210  104.154.162.176  e2-medium     1.18.12-gke.1210  3          RUNNING

הגדרת Artifact Registry ו-skaffold

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

הגדרת Artifact Registry

מנווטים לתפריט Artifact Registry ולוחצים על הלחצן ENABLE (הפעלה).

f7493243bae0cdf7.png

אחרי כמה רגעים, יופיע דפדפן המאגר של GAR. לוחצים על הלחצן CREATE REPOSITORY (יצירת מאגר) ומזינים את שם המאגר.

f97f337f5476651.png

ב-codelab הזה, קראתי למאגר החדש trace-codelab. הפורמט של הארטיפקט הוא Docker וסוג המיקום הוא Region. בוחרים את האזור שקרוב לאזור שהגדרתם כברירת מחדל ב-Google Compute Engine. לדוגמה, בדוגמה שלמעלה נבחרה האפשרות us-central1-f, ולכן כאן נבחרת האפשרות us-central1 (Iowa). אחר כך לוחצים על הלחצן 'יצירה'.

2f04143077ca56db.png

עכשיו מופיע trace-codelab בדפדפן המאגר.

7a3c1f47346bea15.png

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

הגדרה של Skaffold

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

פותחים שוב את Cloud Shell ומוודאים ש-skaffold מותקן. (כברירת מחדל, Cloud Shell מתקין את skaffold בסביבה). מריצים את הפקודה הבאה כדי לראות את הגרסה של skaffold.

skaffold version

פלט הפקודה

v1.20.0

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

55173fe922f40327.png

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

a9b0fa44c37e0178.png

כשלוחצים על לחצן ההעתקה, מופיעה תיבת הדו-שיח בתחתית הדפדפן עם הודעה כמו:

הטקסט us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab הועתק

חוזרים אל Cloud Shell. מריצים את הפקודה skaffold config set default-repo עם הערך שהעתקתם מלוח הבקרה.

skaffold config set default-repo us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab

פלט הפקודה

set value default-repo to us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab for context gke_stackdriver-sandbox-3438851889_us-central1-b_stackdriver-sandbox

בנוסף, צריך להגדיר את הרישום בהגדרות של Docker. מריצים את הפקודה הבאה:

gcloud auth configure-docker us-central1-docker.pkg.dev --quiet

פלט הפקודה

{
  "credHelpers": {
    "gcr.io": "gcloud",
    "us.gcr.io": "gcloud",
    "eu.gcr.io": "gcloud",
    "asia.gcr.io": "gcloud",
    "staging-k8s.gcr.io": "gcloud",
    "marketplace.gcr.io": "gcloud",
    "us-central1-docker.pkg.dev": "gcloud"
  }
}
Adding credentials for: us-central1-docker.pkg.dev

עכשיו אפשר לעבור לשלב הבא ולהגדיר מאגר Kubernetes ב-GKE.

סיכום

בשלב הזה מגדירים את סביבת ה-codelab:

  • הגדרת Cloud Shell
  • יצירת מאגר Artifact Registry עבור Container Registry
  • הגדרת skaffold לשימוש במאגר הקונטיינרים
  • נוצר אשכול Kubernetes שבו פועלים המיקרו-שירותים של ה-codelab

הבא בתור

בשלב הבא, תבנו, תדחפו ותפרוסו את המיקרו-שירותים שלכם באשכול

3. פיתוח, שליחה ופריסה של המיקרו-שירותים

הורדת חומרי ה-codelab

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

cd ~
git clone https://github.com/GoogleCloudPlatform/opentelemetry-trace-codelab-python.git

מבנה הספריות של הפרויקט הוא כדלקמן:

shakesapp-python
├── LICENSE
├── manifests
│   ├── client.yaml
│   ├── loadgen.yaml
│   └── server.yaml
├── proto
│   └── shakesapp.proto
├── skaffold.yaml
└── src
    ├── client
    ├── loadgen
    └── server
  • מניפסטים: קובצי מניפסט של Kubernetes
  • proto: הגדרת proto לתקשורת בין הלקוח לשרת
  • ‫src: ספריות של קוד המקור של כל שירות
  • skaffold.yaml: קובץ תצורה של skaffold

הרצת פקודת skaffold

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

cd shakesapp-python
skaffold run --tail

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

פלט הפקודה

...
---> Running in c39b3ea8692b
 ---> 90932a583ab6
Successfully built 90932a583ab6
Successfully tagged us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/serverservice:step1
The push refers to repository [us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/serverservice]
cc8f5a05df4a: Preparing
5bf719419ee2: Preparing
2901929ad341: Preparing
88d9943798ba: Preparing
b0fdf826a39a: Preparing
3c9c1e0b1647: Preparing
f3427ce9393d: Preparing
14a1ca976738: Preparing
f3427ce9393d: Waiting
14a1ca976738: Waiting
3c9c1e0b1647: Waiting
b0fdf826a39a: Layer already exists
88d9943798ba: Layer already exists
f3427ce9393d: Layer already exists
3c9c1e0b1647: Layer already exists
14a1ca976738: Layer already exists
2901929ad341: Pushed
5bf719419ee2: Pushed
cc8f5a05df4a: Pushed
step1: digest: sha256:8acdbe3a453001f120fb22c11c4f6d64c2451347732f4f271d746c2e4d193bbe size: 2001

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

פלט הפקודה

sha256:b71fce0a96cea08075dc20758ae561cf78c83ff656b04d211ffa00cedb77edf8 size: 1997
Tags used in deployment:
 - serverservice -> us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/serverservice:step4@sha256:8acdbe3a453001f120fb22c11c4f6d64c2451347732f4f271d746c2e4d193bbe
 - clientservice -> us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/clientservice:step4@sha256:b71fce0a96cea08075dc20758ae561cf78c83ff656b04d211ffa00cedb77edf8
 - loadgen -> us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/loadgen:step4@sha256:eea2e5bc8463ecf886f958a86906cab896e9e2e380a0eb143deaeaca40f7888a
Starting deploy...
 - deployment.apps/clientservice created
 - service/clientservice created
 - deployment.apps/loadgen created
 - deployment.apps/serverservice created
 - service/serverservice created

זהירות: אם מופיעה שגיאה כמו "אין גישת push למאגר התמונות שצוין", צריך לבדוק אם פקודת skaffold מנסה לבצע push של תמונות ל-Docker Hub ‏ (docker.io) בלי קשר להגדרה שלכם במאגר ברירת המחדל ב-skaffold. במקרה כזה, כדאי לנסות להוסיף את האפשרות ‎–default-repo לפקודה skaffold run, כמו בדוגמה שלמטה.

$ skaffold run –tail –default-repo=us-central1-docker.pkg.dev/[מזהה פרויקט]/[repository name]

אחרי הפריסה, יומני האפליקציה בפועל שמועברים אל stdout יופיעו בכל אחד מהקונטיינרים באופן הבא:

פלט הפקודה

[server] {"event": "starting server: 0.0.0.0:5050", "severity": "info", "timestamp": "2021-03-17T05:25:56.758575Z"}
[client] [2021-03-17 05:25:54 +0000] [1] [INFO] Starting gunicorn 20.0.4
[client] [2021-03-17 05:25:54 +0000] [1] [INFO] Listening at: http://0.0.0.0:8080 (1)
[client] [2021-03-17 05:25:54 +0000] [1] [INFO] Using worker: threads
[client] [2021-03-17 05:25:54 +0000] [7] [INFO] Booting worker with pid: 7
[client] {"event": "server address is serverservice:5050", "severity": "info", "timestamp": "2021-03-17T05:25:54.888627Z"}
[client] {"event": "request to server with query: world", "severity": "info", "timestamp": "2021-03-17T05:26:11.550923Z"}
[server] {"event": "query: world", "severity": "info", "timestamp": "2021-03-17T05:26:11.567048Z"}
[loadgen] {"event": "check connectivity: http://clientservice:8080/_healthz", "severity": "info", "timestamp": "2021-03-17T05:26:11.533605Z"}
[loadgen] {"event": "/_healthz response: ok", "severity": "info", "timestamp": "2021-03-17T05:26:11.544267Z"}
[loadgen] {"event": "confirmed connection ot clientservice", "severity": "info", "timestamp": "2021-03-17T05:26:11.544527Z"}

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

סיכום

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

הבא בתור

בשלב הבא, תשנו את קוד המקור של שירות loadgen כדי להגדיר את פרטי העקבות.

4. אינסטרומנטציה ל-HTTP

הקונספט של מעקב אחר מכשור והפצה

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

c8c659deaa9c9091.png

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

האפליקציה צריכה לשלוח מטא-נתונים של Trace, כמו מזהה Trace ומזהה Span, כדי ש-Cloud Trace ירכיב את כל ה-Spans עם אותו מזהה Trace ל-Trace אחד. בנוסף, האפליקציה צריכה להעביר הקשרים של מעקב (השילוב של מזהה המעקב ומזהה הטווח של טווח האב) כשמבקשים שירותים במורד הזרם, כדי שהם יוכלו לדעת איזה הקשר מעקב הם מטפלים בו.

בעזרת OpenTelemetry, אפשר:

  • כדי ליצור מזהה ייחודי של מעקב ומזהה ייחודי של יחידה לוגית למעקב
  • כדי לייצא את מזהה המעקב ואת מזהה הטווח אל ה-Backend
  • להעביר הקשרים של מעקב לשירותים אחרים

Instrument first span

שירות מחולל עומס מכשירים

לוחצים על הלחצן 776a11bfb2122549.png בפינה השמאלית העליונה של Cloud Shell כדי לפתוח את Cloud Shell Editor. פותחים את src/loadgen/loadgen.py בחלונית השמאלית ומוצאים את הפונקציה main.

src/loadgen/loadgen.py

def main():
    ...
    # start request loop to client service
    logger.info("start client request loop")
    addr = f"http://{target}"
    while True:
        logger.info("start request to client")
        call_client(addr)
        logger.info("end request to client")
        time.sleep(2.0)

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

קודם צריך ליצור Span עם Trace ID ו-Span ID ייחודיים. ‫OpenTelemetry מספקת ספרייה שימושית לכך. מוסיפים את השורות הבאות כדי לייבא ספריות של OpenTelemetry לקוד.

 import structlog
+from opentelemetry import propagate, trace
+from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter
+from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.instrumentation.requests import RequestsInstrumentor
+from opentelemetry.sdk.trace.export import SimpleSpanProcessor
+from opentelemetry.propagators.cloud_trace_propagator import CloudTraceFormatPropagator

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

 from opentelemetry.propagators.cloud_trace_propagator import CloudTraceFormatPropagator
+
+RequestsInstrumentor().instrument()

לאחר מכן מגדירים מופע של Tracer שמטפל בהגדרות של Trace Context ושל כלי הייצוא

     target = os.environ.get("CLIENT_ADDR", "0.0.0.0:8080")

+    exporter = CloudTraceSpanExporter()
+    trace.get_tracer_provider().add_span_processor(SimpleSpanProcessor(exporter))
+    tracer = trace.get_tracer(__name__)
+    propagate.set_global_textmap(CloudTraceFormatPropagator())
+    trace.set_tracer_provider(TracerProvider())
+
     # connectivity check to client service
     healthz = f"http://{target}/_healthz"
     logger.info(f"check connectivity: {healthz}")

שימו לב: מכיוון שזהו Codelab שמטרתו להסביר איך עובד מעקב אחר אינסטרומנטציה, אנחנו מגדירים את הכלי למעקב כדי שיתעד כל בקשה וישלח אותה אל ה-Backend. ‫(SimpleSpanProcessor()) לא מתאים לסביבות ייצור, לכן חשוב לשנות את החלק הזה כשמגדירים את האפליקציה לייצור.

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

     logger.info("start client request loop")
     addr = f"http://{target}"
     while True:
-        logger.info("start request to client")
-        call_client(addr)
-        logger.info("end request to client")
+        with tracer.start_as_current_span("loadgen") as root_span:
+            root_span.add_event(name="request_start")
+            logger.info("start request to client")
+            call_client(addr)
+            root_span.add_event(name="request_end")
+            logger.info("end request to client")
         time.sleep(2.0)

כדי ש-Docker build יאחזר את חבילות OpenTelemetry הנדרשות, מריצים את הפקודה הבאה:

poetry add "opentelemetry-exporter-gcp-trace=^1.0.0rc0"
poetry add "opentelemetry-propagator-gcp=^1.0.0rc0"
poetry add "opentelemetry-instrumentation-requests=^0.20b0"

אפשר לוודא שתיאור התלות המתאים כתוב ב-pyproject.toml.

שירות לקוחות של Instrument

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

ae074d4513c9931f.png

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

src/client/client.py

 import flask
 import grpc
 import structlog
+from opentelemetry import propagate, trace
+from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter
+from opentelemetry.instrumentation.flask import FlaskInstrumentor
+from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.sdk.trace.export import SimpleSpanProcessor
+from opentelemetry.propagators.cloud_trace_propagator import \
+    CloudTraceFormatPropagator

 import shakesapp_pb2
 import shakesapp_pb2_grpc

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

 app = flask.Flask(__name__)
+FlaskInstrumentor().instrument_app(app)

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

 logger.info(f"server address is {SERVER_ADDR}")

+exporter = CloudTraceSpanExporter()
+trace.get_tracer_provider().add_span_processor(SimpleSpanProcessor(exporter))
+propagate.set_global_textmap(CloudTraceFormatPropagator())
+trace.set_tracer_provider(TracerProvider())

 @app.route("/")
 def main_handler():
    ....

עכשיו אפשר להוסיף אינסטרומנטציה ב-handler. מאתרים את main_handler() ומשנים את החלק ששולח בקשת gRPC לשירות השרת.

@app.route("/")
def main_handler():
    q, count = random.choice(list(queries.items()))

    # get Tracer
    tracer = trace.get_tracer(__name__)

    with tracer.start_as_current_span("client") as cur_span:
        channel = grpc.insecure_channel(SERVER_ADDR)
        stub = shakesapp_pb2_grpc.ShakespeareServiceStub(channel)
        logger.info(f"request to server with query: {q}")
        cur_span.add_event("server_call_start")
        resp = stub.GetMatchCount(shakesapp_pb2.ShakespeareRequest(query=q))
        cur_span.add_event("server_call_end")
        if count != resp.match_count:
            raise UnexpectedResultError(
                f"The expected count for '{q}' was {count}, but result was {resp.match_count } obtained"
            )
        result = str(resp.match_count)
        logger.info(f"matched count for '{q}' is {result}")
    return result

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

poetry add "opentelemetry-exporter-gcp-trace=^1.0.0rc0"
poetry add "opentelemetry-propagator-gcp=^1.0.0rc0"
poetry add "opentelemetry-instrumentation-flask=^0.20b0"

אחר כך מנסים להפעיל את האפליקציה באמצעות הפקודה skaffold run ובודקים מה מוצג בלוח הבקרה של Cloud Trace:

skaffold run --tail

אחרי שתראו כמה הודעות לגבי build, push ופריסה, תראו יומני אפליקציות בפורמט JSON. עוברים אל Cloud Trace > רשימת מעקב כדי לבדוק אם מתקבלים פרטי המעקב. מכיוון ששירות יצירת העומס שולח בקשות לשירות הלקוח באופן תקופתי והפעלתם מעקב אחרי כל הבקשות, תתחילו לראות הרבה נקודות ברשימת המעקב.

f7440360551980e.png

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

67596a4a313738.png

יכול להיות שתשימו לב שלא רואים את טווחי הזמן משירות השרת. התשובה נכונה כי לא הטמענו Spans בשירות השרת בכלל.

סיכום

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

הבא בתור

בשלב הבא, תגדירו את שירות הלקוח ואת שירות השרת כדי לוודא איך להפיץ את הקשר של Trace באמצעות gRPC.

5. אינסטרומנטציה ל-gRPC

בשלב הקודם, הטמענו את המחצית הראשונה של הבקשה במיקרו-שירותים האלה. בשלב הזה, אנחנו מנסים להגדיר את תקשורת gRPC בין שירות הלקוח לבין שירות השרת. (מלבן ירוק וסגול בתמונה שלמטה)

c4dec3e741c3ab4f.png

הוספת מכשור אוטומטית ללקוח gRPC

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

src/client/client.py

 import flask
 import grpc
 import structlog
 from opentelemetry import propagate, trace
 from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter
 from opentelemetry.instrumentation.flask import FlaskInstrumentor
+from opentelemetry.instrumentation.grpc import GrpcInstrumentorClient
 from opentelemetry.sdk.trace import TracerProvider
 from opentelemetry.sdk.trace.export import SimpleSpanProcessor
 from opentelemetry.propagators.cloud_trace_propagator import \
     CloudTraceFormatPropagator
 import shakesapp_pb2
 import shakesapp_pb2_grpc


 app = flask.Flask(__name__)
 FlaskInstrumentor().instrument_app(app)
+GrpcInstrumentorClient().instrument()

במקרה של שירות לקוחות, מה שאנחנו צריכים לעשות עבור האינסטרומנטציה הוא די פשוט. אנחנו רוצים להפיץ את Trace Context, שהוא השילוב של Trace ID ו-Span ID של Span הנוכחי באמצעות gRPC. לכן אנחנו קוראים ל-GrpcInstrumentatorClient.instrument() כדי שלקוח gRPC בפונקציית ה-handler יוכל להטמיע את ההקשר של Trace בכותרת ה-HTTP שמתחת.

חשוב להוסיף תלות חדשה ל-pyproject.toml באמצעות הפקודה poetry add:

poetry add "opentelemetry-instrumentation-grpc=^0.20b0"

הוספת מכשור אוטומטית לשרת gRPC

בדומה למה שעשינו עבור לקוח gRPC, אנחנו קוראים לאינסטרומנטציה אוטומטית לשרת gRPC. מוסיפים ייבוא כמו followings ו-call GrpcInstrumentationServer().instrument() בחלק העליון של הקובץ.

חשוב: הקפידו להתקשר

GrpcInstrumentationServe() 

בשלב הזה, לא

GrpcInstrumentationClient()

.

src/server/server.py

 import grpc
 import structlog
 from google.cloud import storage
 from grpc_health.v1 import health_pb2, health_pb2_grpc
+from opentelemetry import propagate, trace
+from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter
+from opentelemetry.instrumentation.grpc import GrpcInstrumentorServer
+from opentelemetry.sdk.trace import TracerProvider
+from opentelemetry.sdk.trace.export import SimpleSpanProcessor
+from opentelemetry.propagators.cloud_trace_propagator import CloudTraceFormatPropagator

 import shakesapp_pb2
 import shakesapp_pb2_grpc


 BUCKET_NAME = "dataflow-samples"
 BUCKET_PREFIX = "shakespeare/"

+# enable auto gRPC server trace instrumentation
+GrpcInstrumentorServer().instrument()
+

בשלב הבא, מוסיפים את כלי הייצוא כדי לשלוח את פרטי העקבות אל העורף של Cloud Trace. מוסיפים את הקוד הבא לפונקציה serve().

def serve():
+    # start trace exporter
+    trace.set_tracer_provider(TracerProvider())
+    trace.get_tracer_provider().add_span_processor(
+        SimpleSpanProcessor(CloudTraceSpanExporter())
+    )
+    propagators.set_global_textmap(CloudTraceFormatPropagator())
+
+    # add gRPC services to server
     server = grpc.server(futures.ThreadPoolExecutor(max_workers=4))
     service = ShakesappService()
     shakesapp_pb2_grpc.add_ShakespeareServiceServicer_to_server(service, server)
     health_pb2_grpc.add_HealthServicer_to_server(service, server)

חשוב להוסיף את החבילות החדשות לשירות השרת.

poetry add "opentelemetry-exporter-gcp-trace=^1.0.0rc0"
poetry add "opentelemetry-instrumentation-grpc=^0.20b0"
poetry add "opentelemetry-propagator-gcp=^1.0.0rc0"
poetry add "opentelemetry-instrumentation=^0.20b0"

הפעלת המיקרו-שירות ואישור העקבות

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

skaffold run --tail

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

141cb620245b689d.png

סיכום

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

6. מזל טוב

יצרתם בהצלחה עקבות מבוזרים באמצעות OpenTelemery ואישרתם את השהיות של הבקשות במיקרו-שירות ב-Google Cloud Trace.

כדי להרחיב את התרגול, אפשר לנסות את הנושאים הבאים לבד.

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

שימו לב: המשאב נצרך באופן קבוע על ידי Google Kubernetes Engine ו-Google Artifact Registry.

פינוי נפח

בסיום ה-Codelab, חשוב להפסיק את אשכול Kubernetes ולמחוק את הפרויקט כדי שלא תחויבו באופן לא צפוי ב-Google Kubernetes Engine, ב-Google Cloud Trace וב-Google Artifact Registry.

קודם מוחקים את האשכול באמצעות הפקודה הבאה:

skaffold delete

פלט הפקודה

Cleaning up...
 - deployment.apps "clientservice" deleted
 - service "clientservice" deleted
 - deployment.apps "loadgen" deleted
 - deployment.apps "serverservice" deleted
 - service "serverservice" deleted

אחרי שמוחקים את האשכול, בחלונית התפריט בוחרים באפשרות 'IAM & Admin' (ניהול הרשאות וניהול) > 'Settings' (הגדרות), ואז לוחצים על הלחצן 'SHUT DOWN' (כיבוי).

578ca2b72a161e9d.png

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