1. מבוא
עדכון אחרון:14 ביולי 2022
ניראות (observability) של האפליקציה
ניראות (observability) ופרופיל רציף (continuous delivery)
ניראות (observability) היא המונח שמשמש לתיאור תכונה של מערכת. מערכת עם ניראות (observability) מאפשרת לצוותים לנפות באגים במערכת באופן פעיל. בהקשר הזה, שלושה עמודי תווך של ניראות (observability); יומנים, מדדים ועקבות הם הכלים הבסיסיים שבאמצעותם המערכת יכולה להשיג ניראות (observability).
בנוסף לשלושת עמודי התווך של הניראות (observability), הפרופיילינג הרציף הוא רכיב מרכזי נוסף בניראות, והוא מרחיב את בסיס המשתמשים בתחום. הכלי Cloud Profiler הוא אחד מהגורמים שיצרו, והוא מספק ממשק פשוט שבו אפשר להציג פירוט של מדדי הביצועים במקבצי הקריאות של האפליקציות.
ה-Codelab הזה הוא חלק שני בסדרה והוא עוסק באינסטרומנטציה של סוכן רציף של פרופיל. חלק 1 עוסק במעקב מבוזר באמצעות OpenTelemetry ו-Cloud Trace, וניתן לקבל מידע נוסף על זיהוי צוואר הבקבוק במיקרו-שירותים (microservices) בחלק 1.
מה תפַתחו
ב-Codelab הזה, תשתמשו בסוכן רציף של ביצועי הפרופיל בשירות השרת של אפליקציית Shakespeare (שנקראת גם Shakesapp) שרצה על אשכול של Google Kubernetes Engine. הארכיטקטורה של שייקסאפ מתוארת בהמשך:
- Loadgen שולח מחרוזת שאילתה ללקוח ב-HTTP
- הלקוחות מעבירים את השאילתה מיוצר העומסים לשרת ב-gRPC
- השרת מקבל את השאילתה מהלקוח, מאחזר את כל העבודות בפורמט טקסט מ-Google Cloud Storage, מחפש בשורות שמכילות את השאילתה ומחזיר את מספר השורה שהתאימה ללקוח
בחלק הראשון גיליתם שצוואר הבקבוק קיים במקום כלשהו בשירות השרת, אבל לא הצלחתם לזהות את הסיבה המדויקת.
מה תלמדו
- איך מטמיעים סוכן פרופיל
- איך חוקרים את צוואר הבקבוק ב-Cloud Profiler
ב-Codelab הזה מוסבר איך להשתמש בסוכן פרופיל רציף באפליקציה שלכם.
מה צריך להכין
- ידע בסיסי ב-Go
- ידע בסיסי ב-Kubernetes
2. הגדרה ודרישות
הגדרת סביבה בקצב עצמאי
אם אין לכם עדיין חשבון Google (Gmail או Google Apps), עליכם ליצור חשבון. נכנסים למסוף Google Cloud Platform ( console.cloud.google.com) ויוצרים פרויקט חדש.
אם כבר יש לכם פרויקט, לוחצים על התפריט הנפתח לבחירת פרויקט בפינה השמאלית העליונה של המסוף:
ולוחצים על 'New project' (פרויקט חדש). בתיבת הדו-שיח שמתקבלת כדי ליצור פרויקט חדש:
אם עדיין אין לכם פרויקט, אמורה להופיע תיבת דו-שיח כזו כדי ליצור את הפרויקט הראשון:
בתיבת הדו-שיח הבאה ליצירת פרויקט תוכלו להזין את פרטי הפרויקט החדש:
חשוב לזכור את מזהה הפרויקט, שהוא שם ייחודי בכל הפרויקטים ב-Google Cloud (השם שלמעלה כבר תפוס ולא מתאים לכם, סליחה). בהמשך ב-Codelab הזה, המערכת תתייחס אליה בתור PROJECT_ID.
בשלב הבא, אם עדיין לא עשית זאת, יהיה עליך להפעיל חיוב ב-Developers Console כדי להשתמש במשאבים של Google Cloud ולהפעיל את Cloud Trace API.
ההרצה של Codelab הזה לא אמורה לעלות לך יותר מכמה דולרים, אבל זה יכול להיות גבוה יותר אם תחליטו להשתמש ביותר משאבים או אם תשאירו אותם פועלים (עיינו בקטע 'ניקוי' בסוף המסמך). המחירים של Google Cloud Trace, Google Kubernetes Engine ו-Google Artifact Registry מפורטים במסמכי התיעוד הרשמיים.
- תמחור חבילת התפעול של Google Cloud | חבילת התפעול
- תמחור | מסמכי תיעוד של Kubernetes Engine
- תמחור של Artifact Registry | חומרי עזר של Artifact Registry
משתמשים חדשים ב-Google Cloud Platform זכאים לתקופת ניסיון בחינם בשווי 300$, שמאפשרת ל-Codelab הזה בחינם לגמרי.
הגדרת Google Cloud Shell
אפשר להפעיל את Google Cloud ואת Google Cloud Trace מרחוק מהמחשב הנייד, אבל ב-Codelab הזה נשתמש ב-Google Cloud Shell, סביבת שורת הפקודה שפועלת ב-Cloud.
המכונה הווירטואלית הזו שמבוססת על Debian נטענת עם כל הכלים למפתחים שדרושים לכם. יש בה ספריית בית בנפח מתמיד של 5GB והיא פועלת ב-Google Cloud, מה שמשפר משמעותית את ביצועי הרשת והאימות. כלומר, כל מה שדרוש ל-Codelab הזה הוא דפדפן (כן, הוא פועל ב-Chromebook).
כדי להפעיל את Cloud Shell ממסוף Cloud, לוחצים על Activate Cloud Shell (ההקצאה וההתחברות של הסביבה אמורות להימשך כמה דקות).
אחרי ההתחברות ל-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:
Cloud Shell גם מגדירה משתני סביבה כברירת מחדל, והוא יכול להיות שימושי כשמריצים פקודות עתידיות.
echo $GOOGLE_CLOUD_PROJECT
פלט הפקודה
<PROJECT_ID>
בשלב האחרון, מגדירים את ברירת המחדל של האזור והפרויקט.
gcloud config set compute/zone us-central1-f
אפשר לבחור מגוון אזורים שונים. מידע נוסף זמין במאמר אזורים ו אזורים.
מעבר להגדרת שפה
ב-Codelab הזה, אנחנו משתמשים ב-Go לכל קודי המקור. מריצים את הפקודה הבאה ב-Cloud Shell ומוודאים שהגרסה של Go היא 1.17 ואילך.
go version
פלט הפקודה
go version go1.18.3 linux/amd64
הגדרת אשכול של Google Kubernetes
ב-Codelab הזה, תריצו אשכול של מיקרו-שירותים (microservices) ב-Google Kubernetes Engine (GKE). התהליך של Codelab הזה הוא כך:
- הורדת פרויקט הבסיס אל Cloud Shell
- יצירת מיקרו-שירותים (microservices) בקונטיינרים
- העלאת קונטיינרים ל-Google Artifact Registry (GAR)
- פריסת קונטיינרים ב-GKE
- שינוי קוד המקור של השירותים עבור אינסטרומנטציה למעקב
- מעבר לשלב 2
הפעלת Kubernetes Engine
בשלב הראשון אנחנו מגדירים אשכול Kubernetes שבו Shakesapp פועל ב-GKE, ולכן עלינו להפעיל את GKE. עוברים לתפריט 'Kubernetes Engine'. ולוחצים על לחצן ההפעלה.
עכשיו אתם מוכנים ליצור אשכול Kubernetes.
יצירת אשכול Kubernetes
ב-Cloud Shell, מריצים את הפקודה הבאה כדי ליצור אשכול Kubernetes. צריך לוודא שערך התחום (zone) נמצא מתחת לאזור שבו תשתמשו ליצירת מאגר Artifact Registry. אם האזור של המאגר לא מכסה את התחום, משנים את ערך התחום us-central1-f
.
gcloud container clusters create otel-trace-codelab2 \ --zone us-central1-f \ --release-channel rapid \ --preemptible \ --enable-autoscaling \ --max-nodes 8 \ --no-enable-ip-alias \ --scopes cloud-platform
פלט הפקודה
Note: Your Pod address range (`--cluster-ipv4-cidr`) can accommodate at most 1008 node(s). Creating cluster otel-trace-codelab2 in us-central1-f... Cluster is being health-checked (master is healthy)...done. Created [https://container.googleapis.com/v1/projects/development-215403/zones/us-central1-f/clusters/otel-trace-codelab2]. To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/us-central1-f/otel-trace-codelab2?project=development-215403 kubeconfig entry generated for otel-trace-codelab2. NAME: otel-trace-codelab2 LOCATION: us-central1-f MASTER_VERSION: 1.23.6-gke.1501 MASTER_IP: 104.154.76.89 MACHINE_TYPE: e2-medium NODE_VERSION: 1.23.6-gke.1501 NUM_NODES: 3 STATUS: RUNNING
הגדרת Artifact Registry ו-skaffold
עכשיו יש לנו אשכול Kubernetes מוכן לפריסה. בשלב הבא אנחנו מכינים רישום קונטיינרים לצורך דחיפה ופריסה של קונטיינרים. כדי לבצע את השלבים האלה, צריך להגדיר Artifact Registry (GAR) ו-skaffold כדי להשתמש בו.
הגדרה של Artifact Registry
עוברים לתפריט של Artifact Registry ולוחצים על לחצן ההפעלה.
אחרי כמה רגעים תראו את דפדפן המאגר של GAR. לוחצים על "CREATE REPOSITORY". ותזין את שם המאגר.
ב-Codelab הזה, אקרא למאגר החדש trace-codelab
. הפורמט של פריט המידע שנוצר בתהליך הפיתוח (Artifact) הוא 'Docker' וסוג המיקום הוא Region (אזור). בוחרים את האזור שקרוב לאזור שהגדרתם לאזור ברירת המחדל של Google Compute Engine. לדוגמה, בדוגמה הזו בחרו 'us-central1-f' למעלה, אז כאן אנחנו בוחרים את 'us-central1 (איווה)'. לאחר מכן לוחצים על 'יצירה' לחצן.
עכשיו מופיעה האפשרות 'trace-codelab' בדפדפן של המאגר.
נחזור לכאן מאוחר יותר כדי לבדוק את נתיב הרישום.
הגדרת Skaffold
Skaffold הוא כלי שימושי לפיתוח מיקרו-שירותים (microservices) שרצים ב-Kubernetes. הוא מטפל בתהליך העבודה של פיתוח, דחיפה ופריסה של קונטיינרים של אפליקציות עם קבוצת פקודות קטנה. כברירת מחדל, Skaffold משתמש ב-Docker Registry כרישום קונטיינרים, לכן צריך להגדיר את skaffold כך שיזהה GAR בדחיפת קונטיינרים אליהם.
פותחים שוב את Cloud Shell ובודקים אם skaffold מותקן. (Cloud Shell מתקינה את skaffold בסביבה כברירת מחדל). מריצים את הפקודה הבאה ובודקים את הגרסה של skaffold.
skaffold version
פלט הפקודה
v1.38.0
עכשיו אפשר לרשום את מאגר ברירת המחדל לשימוש ב-skaffold. כדי למצוא את נתיב הרישום, עוברים למרכז הבקרה של Artifact Registry ולוחצים על שם המאגר שהגדרתם בשלב הקודם.
לאחר מכן יופיעו נתיבי ניווט בחלק העליון של הדף. לוחצים על סמל כדי להעתיק את נתיב הרישום ללוח.
כשלוחצים על לחצן ההעתקה, תוצג תיבת דו-שיח בחלק התחתון של הדפדפן עם הודעה כמו:
"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 למרשם הקונטיינרים
- הגדרת skaffold לשימוש במרשם הקונטיינרים
- יצרתם אשכול Kubernetes שבו רצים מיקרו-שירותים של Codelab
הנושא הבא
בשלב הבא תבצעו אינסטרומנטציה רציפה של סוכן פרופיל, בשירות השרת.
3. פיתוח, דחיפה ופריסה של מיקרו-שירותים (microservices)
הורדת החומר של ה-Codelab
בשלב הקודם, הגדרנו את כל הדרישות המוקדמות ל-Codelab הזה. עכשיו אתם מוכנים להריץ עליהם מיקרו-שירותים שלמים. חומרי ה-Codelab מתארחים ב-GitHub, לכן צריך להוריד אותם לסביבת Cloud Shell באמצעות פקודת ה-Git הבאה.
cd ~ git clone https://github.com/ymotongpoo/opentelemetry-trace-codelab-go.git cd opentelemetry-trace-codelab-go
מבנה הספריות של הפרויקט הוא:
. ├── README.md ├── step0 │ ├── manifests │ ├── proto │ ├── skaffold.yaml │ └── src ├── step1 │ ├── manifests │ ├── proto │ ├── skaffold.yaml │ └── src ├── step2 │ ├── manifests │ ├── proto │ ├── skaffold.yaml │ └── src ├── step3 │ ├── manifests │ ├── proto │ ├── skaffold.yaml │ └── src ├── step4 │ ├── manifests │ ├── proto │ ├── skaffold.yaml │ └── src ├── step5 │ ├── manifests │ ├── proto │ ├── skaffold.yaml │ └── src └── step6 ├── manifests ├── proto ├── skaffold.yaml └── src
- מניפסטים: קובצי מניפסט של Kubernetes
- proto: הגדרת אב לתקשורת בין לקוח לשרת
- src: ספריות לקוד המקור של כל שירות
- skaffold.yaml: קובץ התצורה של skaffold
ב-Codelab הזה, צריך לעדכן את קוד המקור שנמצא בתיקייה step4
. אפשר גם לעיין בקוד המקור ב-step[1-6]
תיקיות כדי לראות את השינויים מההתחלה. (חלק 1 מכסה את שלב 0 עד שלב 4, וחלק 2 מכסה את שלבים 5 ו-6)
הרצת פקודת skaffold
בסוף אתם מוכנים לפתח, לדחוף ולפרוס תוכן שלם באשכול Kubernetes שיצרתם. זה נשמע כאילו הסרטון כולל כמה שלבים, אבל המודל עצמו עושה את כל העבודה בשבילך. ננסה לפעול באמצעות הפקודה הבאה:
cd step4 skaffold dev
מיד לאחר הרצת הפקודה, פלט היומן של 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
לאחר הפריסה, יומני האפליקציות שהתקבלו ל-stdout יופיעו בכל קונטיינרים בצורה הבאה:
פלט הפקודה
[client] 2022/07/14 06:33:15 {"match_count":3040} [loadgen] 2022/07/14 06:33:15 query 'love': matched 3040 [client] 2022/07/14 06:33:15 {"match_count":3040} [loadgen] 2022/07/14 06:33:15 query 'love': matched 3040 [client] 2022/07/14 06:33:16 {"match_count":3040} [loadgen] 2022/07/14 06:33:16 query 'love': matched 3040 [client] 2022/07/14 06:33:19 {"match_count":463} [loadgen] 2022/07/14 06:33:19 query 'tear': matched 463 [loadgen] 2022/07/14 06:33:20 query 'world': matched 728 [client] 2022/07/14 06:33:20 {"match_count":728} [client] 2022/07/14 06:33:22 {"match_count":463} [loadgen] 2022/07/14 06:33:22 query 'tear': matched 463
שים לב שבשלב הזה רוצים לראות את כל ההודעות מהשרת. טוב, עכשיו הכול מוכן ואפשר להתחיל להוסיף לאפליקציה שלך אינסטרומנטציה באמצעות OpenTelemetry לצורך מעקב מבוזר של השירותים.
לפני התחלת האינסטרומנטציה בשירות, יש לכבות את האשכול באמצעות Ctrl-C.
פלט הפקודה
... [client] 2022/07/14 06:34:57 {"match_count":1} [loadgen] 2022/07/14 06:34:57 query 'what's past is prologue': matched 1 ^CCleaning up... - W0714 06:34:58.464305 28078 gcp.go:120] WARNING: the gcp auth plugin is deprecated in v1.22+, unavailable in v1.25+; use gcloud instead. - To learn more, consult https://cloud.google.com/blog/products/containers-kubernetes/kubectl-auth-changes-in-gke - deployment.apps "clientservice" deleted - service "clientservice" deleted - deployment.apps "loadgen" deleted - deployment.apps "serverservice" deleted - service "serverservice" deleted
סיכום
בשלב הזה הכנתם את חומר ה-Codelab בסביבה שלכם ואישרתם הפעלות של skaffold כמצופה.
הנושא הבא
בשלב הבא, תשנה את קוד המקור של שירות עומסים כדי להגדיר את פרטי המעקב.
4. אינסטרומנטציה של סוכן Cloud Profiler
מושג פרופיילינג רציף
לפני להסביר את המושג של פרופיילינג רציף, עלינו להבין את זה של פרופיילינג. יצירת פרופילים היא אחת הדרכים לניתוח דינמי של האפליקציה (ניתוח דינמי של התוכנית) והיא בדרך כלל מבוצעת במהלך פיתוח האפליקציה בתהליך של בדיקת העומסים וכן הלאה. זו פעילות מסוג 'צילום אחד' למדידה של מדדי המערכת, כמו שימוש במעבד (CPU) ובזיכרון, במהלך התקופה הספציפית. אחרי איסוף נתוני הפרופיל, המפתחים מנתחים אותם ללא צורך בקוד.
פרופיילינג רציף הוא הגישה המורחבת של פרופיילינג רגיל: הוא מפעיל פרופילים של חלונות קצרים מול האפליקציה הרצה מדי פעם ואוסף כמות גדולה של נתוני פרופיל. לאחר מכן היא יוצרת באופן אוטומטי את הניתוח הסטטיסטי על סמך מאפיין מסוים של האפליקציה, כמו מספר גרסה, אזור הפריסה, זמן המדידה וכן הלאה. פרטים נוספים על הקונספט הזה זמינים במסמכים שלנו.
מאחר שהיעד הוא אפליקציה פעילה, קיימת דרך לאסוף נתוני פרופיל באופן תקופתי ולשלוח אותם לקצה עורפי מסוים שמעבד את הנתונים הסטטיסטיים לאחר עיבוד. זה הסוכן Cloud Profiler ועליך להטמיע אותו בקרוב בשירות השרת.
הטמעת סוכן Cloud Profiler
לוחצים על הלחצן בפינה הימנית העליונה של Cloud Shell כדי לפתוח את Cloud Shell Editor. פותחים את
step4/src/server/main.go
דרך ה-Explorer בחלונית הימנית ומוצאים את הפונקציה הראשית.
step4/src/server/main.go
func main() { ... // step2. setup OpenTelemetry tp, err := initTracer() if err != nil { log.Fatalf("failed to initialize TracerProvider: %v", err) } defer func() { if err := tp.Shutdown(context.Background()); err != nil { log.Fatalf("error shutting down TracerProvider: %v", err) } }() // step2. end setup svc := NewServerService() // step2: add interceptor interceptorOpt := otelgrpc.WithTracerProvider(otel.GetTracerProvider()) srv := grpc.NewServer( grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor(interceptorOpt)), grpc.StreamInterceptor(otelgrpc.StreamServerInterceptor(interceptorOpt)), ) // step2: end adding interceptor shakesapp.RegisterShakespeareServiceServer(srv, svc) healthpb.RegisterHealthServer(srv, svc) if err := srv.Serve(lis); err != nil { log.Fatalf("error serving server: %v", err) } }
בפונקציה main
מופיע קוד הגדרה של OpenTelemetry ושל gRPC, שבוצע בחלק 1 של Codelab. עכשיו נוסיף כאן אינסטרומנטציה לסוכן Cloud Profiler. כמו שעשינו בשביל initTracer()
, אפשר לכתוב פונקציה בשם initProfiler()
כדי לשפר את הקריאוּת.
step4/src/server/main.go
import ( ... "cloud.google.com/go/profiler" // step5. add profiler package "cloud.google.com/go/storage" ... ) // step5: add Profiler initializer func initProfiler() { cfg := profiler.Config{ Service: "server", ServiceVersion: "1.0.0", NoHeapProfiling: true, NoAllocProfiling: true, NoGoroutineProfiling: true, NoCPUProfiling: false, } if err := profiler.Start(cfg); err != nil { log.Fatalf("failed to launch profiler agent: %v", err) } }
בואו נסתכל מקרוב על האפשרויות שצוינו באובייקט profiler.Config{}
.
- Service: שם השירות שאפשר לבחור ולהפעיל במרכז השליטה של Profiler
- ServiceVersion: השם של גרסת השירות. ניתן להשוות בין קבוצות של נתוני פרופילים על סמך הערך הזה.
- NoHeapProfiling: השבתת הפרופיילינג של צריכת הזיכרון
- NoAllocProfiling: השבתת הפרופיילינג של הקצאת הזיכרון
- NoGoroutineProfiling: השבתת פרופיילינג גורוטין
- NoCPUProfiling: השבתת הפרופיילינג של המעבד (CPU)
ב-Codelab הזה, אנחנו מפעילים רק את הפרופיילינג של המעבד (CPU).
עכשיו צריך פשוט לקרוא לפונקציה הזו בפונקציה main
. צריך לייבא את חבילת Cloud Profiler בבלוק הייבוא.
step4/src/server/main.go
func main() { ... defer func() { if err := tp.Shutdown(context.Background()); err != nil { log.Fatalf("error shutting down TracerProvider: %v", err) } }() // step2. end setup // step5. start profiler go initProfiler() // step5. end svc := NewServerService() // step2: add interceptor ... }
חשוב לדעת: מבצעים קריאה לפונקציה initProfiler()
עם מילת המפתח go
. מכיוון ש-profiler.Start()
חוסם אותו, צריך להריץ אותו בבורות אחר. עכשיו האפליקציה מוכנה לבנייה. חשוב להריץ את הפקודה go mod tidy
לפני הפריסה.
go mod tidy
עכשיו פורסים את האשכול עם שירות השרת החדש.
skaffold dev
בדרך כלל לוקח כמה דקות לראות את תרשים הלהבות ב-Cloud Profiler. מקלידים "פרופילr" בתיבת החיפוש שלמעלה ולוחצים על סמל Profiler.
לאחר מכן יופיע תרשים הלהבות הבא.
סיכום
בשלב הזה הטמעתם סוכן של Cloud Profiler בשירות השרת, ואישרתם שהוא יוצר תרשים להבות (flame Graph).
הנושא הבא
בשלב הבא, לחקור את הגורם לצוואר הבקבוק באפליקציה באמצעות תרשים הלהבות.
5. ניתוח תרשים הלהבה של Cloud Profiler
מהו Flame Graph?
אחת הדרכים להציג את נתוני הפרופיל באופן חזותי היא אחת מהדרכים להשתמש ב-Fflame Graph. להסבר מפורט, אפשר לעיין במסמך שלנו, אבל הסיכום הקצר הוא:
- כל עמודה מייצגת את הפעלת השיטה/הפונקציה באפליקציה
- הכיוון האנכי הוא ערימת הקריאות. מספר השיחות עולה מלמעלה למטה
- כיוון אופקי הוא השימוש במשאבים; ככל שארוך יותר, כך גרוע יותר.
בהתאם לכך, נסתכל על תרשים הלהבות שהתקבל.
ניתוח Flame Graph
בקטע הקודם למדתם שכל עמודה בתרשים הלהבות מבטאת את הקריאה לפונקציה/ל-method, ומשך הזמן הזה מציין את השימוש במשאבים בפונקציה/ב-method. תרשים הלהבות של Cloud Profiler ממיין את העמודה בסדר יורד או באורך משמאל לימין. אתם יכולים להתחיל להסתכל קודם בפינה השמאלית העליונה של התרשים.
במקרה שלנו, ברור ש-grpc.(*Server).serveStreams.func1.2
גוזל את רוב זמן המעבד (CPU), ועל ידי בדיקה של מקבץ הקריאות מלמעלה למטה, רוב הזמן נמצא ב-main.(*serverService).GetMatchCount
, שהוא ה-handler של שרת gRPC בשירות השרת.
בקטע GetMatchCount מוצגת סדרה של פונקציות regexp: regexp.MatchString
ו-regexp.Compile
. הם מגיעים מחבילה רגילה: כלומר, צריך לבדוק אותם היטב מנקודות מבט רבות, כולל ביצועים. עם זאת, התוצאה כאן מראה שהשימוש במשאבי זמן המעבד (CPU) גבוה ב-regexp.MatchString
וב-regexp.Compile
. על סמך העובדות האלה, ההנחה כאן היא שהשימוש ב-regexp.MatchString
קשור לבעיות בביצועים. עכשיו נקרא את קוד המקור שבו נעשה שימוש בפונקציה.
step4/src/server/main.go
func (s *serverService) GetMatchCount(ctx context.Context, req *shakesapp.ShakespeareRequest) (*shakesapp.ShakespeareResponse, error) { resp := &shakesapp.ShakespeareResponse{} texts, err := readFiles(ctx, bucketName, bucketPrefix) if err != nil { return resp, fmt.Errorf("fails to read files: %s", err) } for _, text := range texts { for _, line := range strings.Split(text, "\n") { line, query := strings.ToLower(line), strings.ToLower(req.Query) isMatch, err := regexp.MatchString(query, line) if err != nil { return resp, err } if isMatch { resp.MatchCount++ } } } return resp, nil }
זה המקום שבו regexp.MatchString
נקרא. אם תקראו את קוד המקור, ייתכן שתבחינו שהפונקציה מופעלת בתוך רכיב ה-for-loop שהוצב בו. לכן יכול להיות שהשימוש בפונקציה הזו שגוי. נבחן את GoDoc של regexp.
בהתאם למסמך, regexp.MatchString
מהדר את תבנית הביטוי הרגולרי בכל קריאה. כך שהסיבה לצריכת משאבים גדולה נשמעת כך.
סיכום
בשלב הזה, בדקתם את הסיבה לצריכת המשאבים על ידי ניתוח תרשים הלהבות.
הנושא הבא
בשלב הבא, עליך לעדכן את קוד המקור של שירות השרת ולאשר את השינוי מהגרסה 1.0.0.
6. עדכון קוד המקור והבדלים בין תרשימי הלהבות
עדכון של קוד המקור
בשלב הקודם, ההנחה שלך היא שהשימוש ב-regexp.MatchString
קשור לצריכת המשאבים הגדולה. בואו נפתור את זה. פותחים את הקוד ומשנים קצת את החלק הזה.
step4/src/server/main.go
func (s *serverService) GetMatchCount(ctx context.Context, req *shakesapp.ShakespeareRequest) (*shakesapp.ShakespeareResponse, error) { resp := &shakesapp.ShakespeareResponse{} texts, err := readFiles(ctx, bucketName, bucketPrefix) if err != nil { return resp, fmt.Errorf("fails to read files: %s", err) } // step6. considered the process carefully and naively tuned up by extracting // regexp pattern compile process out of for loop. query := strings.ToLower(req.Query) re := regexp.MustCompile(query) for _, text := range texts { for _, line := range strings.Split(text, "\n") { line = strings.ToLower(line) isMatch := re.MatchString(line) // step6. done replacing regexp with strings if isMatch { resp.MatchCount++ } } } return resp, nil }
כמו שאפשר לראות, תהליך ההידור של דפוס regexp נשלף מה-regexp.MatchString
ומוענק מחוץ ללולאת for.
לפני פריסת הקוד הזה, חשוב לעדכן את מחרוזת הגרסה בפונקציה initProfiler()
.
step4/src/server/main.go
func initProfiler() { cfg := profiler.Config{ Service: "server", ServiceVersion: "1.1.0", // step6. update version NoHeapProfiling: true, NoAllocProfiling: true, NoGoroutineProfiling: true, NoCPUProfiling: false, } if err := profiler.Start(cfg); err != nil { log.Fatalf("failed to launch profiler agent: %v", err) } }
עכשיו נראה איך זה עובד. פריסת האשכול באמצעות פקודת skaffold.
skaffold dev
אחר כך אפשר לטעון מחדש את מרכז הבקרה של Cloud Profiler ולראות איך הוא נראה.
חשוב להקפיד לשנות את הגרסה ל-"1.1.0"
כדי שיוצגו רק הפרופילים מגרסה 1.1.0. כמו שאפשר לראות, האורך של העמודה GetMatchCount הצטמצם ויחס השימוש בזמן המעבד (CPU) היה קצר יותר.
לא רק על ידי התבוננות בתרשים הלהבות של גרסה יחידה, תוכלו גם להשוות את ההבדלים בין שתי גרסאות.
שינוי הערך של 'השוואה אל' מהתפריט הנפתח 'גרסה' ומשנים את הערך של 'גרסה להשוואה'. ל-"1.0.0", הגרסה המקורית.
תראו תרשים להבות מהסוג הזה. צורת התרשים זהה ל-1.1.0, אבל הצבעים שונים. במצב השוואה, משמעות הצבע היא:
- כחול: הערך (צריכת המשאבים) מופחתת
- כתום: הערך (צריכת משאבים) שנצברו
- אפור: ניטרלי
בהתאם למקרא, נבחן מקרוב את הפונקציה. אם תלחצו על הסרגל שבו רוצים להגדיל את התצוגה, תוכלו לראות פרטים נוספים בתוך המקבץ. צריך ללחוץ על עמודת main.(*serverService).GetMatchCount
. כמו כן, אם תעבירו את העכבר מעל העמודה, יוצגו פרטי ההשוואה.
כתוב שזמן המעבד הכולל קוצר מ-5.26 שניות ל-2.88 שניות (סך הכול הוא 10 שניות = חלון דגימה). זה שיפור משמעותי!
עכשיו אפשר לשפר את ביצועי האפליקציה בעזרת ניתוח נתוני הפרופיל.
סיכום
בשלב הזה ביצעתם עריכה בשירות השרת ואישרתם את השיפור במצב ההשוואה של Cloud Profiler.
הנושא הבא
בשלב הבא, עליך לעדכן את קוד המקור של שירות השרת ולאשר את השינוי מהגרסה 1.0.0.
7. שלב נוסף: מאשרים את השיפור ב-Waterfall של Trace
ההבדלים בין מעקב מבוזר לבין פרופיילינג רציף
בחלק הראשון של שיעור ה-Codelab, אישרת שאפשר להבין מהו שירות צוואר הבקבוק בכל המיקרו-שירותים (microservices) בנתיב בקשה, ושלא הצלחת להבין מה בדיוק גרם לצוואר הבקבוק בשירות הספציפי. בחלק השני של הקוד, למדתם פרופיילינג רציף מאפשר לזהות את צוואר הבקבוק בתוך שירות יחיד מערימות של קריאות.
בשלב הזה נבחן את תרשים ה-Waterfall של המעקב המבוזר (Cloud Trace) ונראה את ההבדל בין הפרופיילינג הרציף.
תרשים ה-Waterfall הזה הוא אחד מהמעקבים עם השאילתה 'love'. נדרשות כ-6.7 שניות (6,700 אלפיות השנייה) בסה"כ.
וזהו אחרי השיפור של אותה שאילתה. כמו שאפשר לראות, זמן האחזור הכולל הוא עכשיו 1.5 שניות (1,500 אלפיות השנייה), וזה שיפור עצום מההטמעה הקודמת.
הנקודה החשובה כאן היא שבתרשים ה-Waterfall של מעקב מבוזר, המידע על ערימת הקריאות לא זמין, אלא אם הכלי מתפרס על פני כל מקום. גם מעקבים מבוזרים מתמקדים רק בזמן האחזור בין השירותים, ואילו פרופיילינג רציף מתמקד במשאבי המחשב (מעבד, זיכרון, שרשורי מערכת הפעלה) של שירות יחיד.
בהיבט אחר, המעקב המבוזר הוא בסיס האירועים, ופרופיל רציף הוא סטטיסטי. לכל מעקב יש תרשים זמן אחזור שונה, וצריך להשתמש בפורמט שונה, כמו הפצה, כדי להבין את המגמה של שינויים בזמן האחזור.
סיכום
בשלב הזה בדקתם את ההבדל בין מעקב מבוזר לבין פרופיילינג רציף.
8. מזל טוב
יצרת בהצלחה מעקבים מבוזרים באמצעות OpenTelemery ואישרת את זמני האחזור של הבקשות במיקרו-שירות (microservice) ב-Google Cloud Trace.
בתרגילים מורחבים, אתם יכולים לנסות את הנושאים הבאים בעצמכם.
- ההטמעה הנוכחית שולחת את כל החלקים שנוצרו על ידי בדיקת התקינות. (
grpc.health.v1.Health/Check
) איך מסננים את החלקים האלה מ-Cloud Traces? הרמז נמצא כאן. - קורלציה בין יומני אירועים ל-span – כדי לראות איך זה עובד ב-Google Cloud Trace וב-Google Cloud Logging. הרמז נמצא כאן.
- מחליפים חלק מהשירות בשירות בשפה אחרת ומנסים לבצע אינסטרומנטציית OpenTelemetry באותה שפה.
בנוסף, אם ברצונך לקבל מידע נוסף על כלי הפרופיל אחרי זה, עליך לעבור לחלק 2. במקרה כזה, אפשר לדלג על קטע הניקוי שלמטה.
פינוי מקום
אחרי שתעשו את ה-codelab הזה, צריך להפסיק את אשכול Kubernetes ולוודא למחוק את הפרויקט כדי שלא יתקבלו חיובים לא צפויים ב-Google Kubernetes Engine, ב-Google Cloud Trace ו-Google Artifact Registry.
קודם מוחקים את האשכול. אם אתם מפעילים את האשכול עם skaffold dev
, צריך רק להקיש על Ctrl-C. אם מריצים את האשכול עם skaffold run
, מריצים את הפקודה הבאה:
skaffold delete
פלט הפקודה
Cleaning up... - deployment.apps "clientservice" deleted - service "clientservice" deleted - deployment.apps "loadgen" deleted - deployment.apps "serverservice" deleted - service "serverservice" deleted
אחרי מחיקת האשכול, בחלונית התפריט, בוחרים באפשרות 'IAM & Admin" (אדמין) > 'הגדרות', ולאחר מכן ללחוץ על 'להורדה' לחצן.
אחר כך מזינים בטופס את מזהה הפרויקט (לא שם הפרויקט) בתיבת הדו-שיח ומאשרים את הכיבוי.