1. บทนำ

อัปเดตล่าสุด: 15-07-2022
ความสามารถในการสังเกตแอปพลิเคชัน
ความสามารถในการสังเกตและ OpenTelemetry
ความสามารถในการสังเกตเป็นคำที่ใช้อธิบายแอตทริบิวต์ของระบบ ระบบที่มีความสามารถในการสังเกตได้ช่วยให้ทีมแก้ไขข้อบกพร่องของระบบได้อย่างมีประสิทธิภาพ ในบริบทนั้น เสาหลัก 3 ประการของความสามารถในการสังเกตการณ์ ได้แก่ บันทึก เมตริก และการติดตาม เป็นเครื่องมือพื้นฐานสำหรับการวัดคุมระบบเพื่อให้ได้ความสามารถในการสังเกตการณ์
OpenTelemetry คือชุดข้อกำหนด ไลบรารี และเอเจนต์ที่ช่วยเร่งการวัดคุมและการส่งออกข้อมูลการวัดและส่งข้อมูล (บันทึก เมตริก และการติดตาม) ที่ความสามารถในการสังเกตต้องการ OpenTelemetry เป็นมาตรฐานแบบเปิดและโปรเจ็กต์ที่ขับเคลื่อนโดยชุมชนภายใต้ CNCF การใช้ประโยชน์จากไลบรารีที่โปรเจ็กต์และระบบนิเวศของโปรเจ็กต์มีให้ช่วยให้นักพัฒนาแอปสามารถวัดประสิทธิภาพแอปพลิเคชันในลักษณะที่เป็นกลางต่อผู้ให้บริการและสถาปัตยกรรมหลายแบบ
นอกจากเสาหลัก 3 ประการของความสามารถในการสังเกตแล้ว การสร้างโปรไฟล์อย่างต่อเนื่องยังเป็นอีกองค์ประกอบสำคัญสำหรับความสามารถในการสังเกต และขยายฐานผู้ใช้ในอุตสาหกรรม Cloud Profiler เป็นหนึ่งในผู้ริเริ่มและมีอินเทอร์เฟซที่ใช้งานง่ายเพื่อเจาะลึกเมตริกประสิทธิภาพในสแต็กการเรียกแอปพลิเคชัน
Codelab นี้เป็นส่วนที่ 1 ของชุดข้อมูล และครอบคลุมถึงการติดตั้งเครื่องมือการติดตามแบบกระจายใน Microservice ด้วย OpenTelemetry และ Cloud Trace ส่วนที่ 2 จะครอบคลุมการทำโปรไฟล์อย่างต่อเนื่องด้วย Cloud Profiler
การติดตามแบบกระจาย
ในบรรดาบันทึก เมตริก และการติดตาม การติดตามคือการวัดและส่งข้อมูลทางไกลที่บอกเวลาในการตอบสนองของกระบวนการส่วนใดส่วนหนึ่งในระบบ โดยเฉพาะในยุคของไมโครเซอร์วิส การติดตามแบบกระจายคือปัจจัยสำคัญในการค้นหาคอขวดของเวลาในการตอบสนองในระบบแบบกระจายโดยรวม
เมื่อวิเคราะห์การติดตามแบบกระจาย การแสดงภาพข้อมูลการติดตามคือกุญแจสำคัญในการทำความเข้าใจเวลาในการตอบสนองโดยรวมของระบบได้อย่างรวดเร็ว ใน Distributed Trace เราจะจัดการชุดการเรียกเพื่อประมวลผลคำขอเดียวไปยังจุดแรกเข้าของระบบในรูปแบบของ Trace ที่มี Span หลายรายการ
Span คือหน่วยงานแต่ละหน่วยที่ทำในระบบแบบกระจาย โดยจะบันทึกเวลาเริ่มต้นและเวลาสิ้นสุด โดยทั่วไปแล้ว ช่วงเวลาจะมีลำดับชั้นซึ่งกันและกัน ในรูปภาพด้านล่าง ช่วงเวลาขนาดเล็กทั้งหมดเป็นช่วงเวลาลูกของช่วงเวลา /messages ขนาดใหญ่ และจะรวมกันเป็น Trace เดียวที่แสดงเส้นทางการทำงานผ่านระบบ

Google Cloud Trace เป็นหนึ่งในตัวเลือกสำหรับแบ็กเอนด์ของ Distributed Trace และผสานรวมกับผลิตภัณฑ์อื่นๆ ใน Google Cloud ได้เป็นอย่างดี
สิ่งที่คุณจะสร้าง
ใน Codelab นี้ คุณจะใช้เครื่องมือในการติดตามข้อมูลในบริการที่ชื่อว่า "แอปพลิเคชันเชกสเปียร์" (หรือที่เรียกว่า Shakesapp) ซึ่งทำงานบนคลัสเตอร์ Google Kubernetes Engine สถาปัตยกรรมของ Shakesapp เป็นไปตามที่อธิบายไว้ด้านล่าง

- Loadgen ส่งสตริงการค้นหาไปยังไคลเอ็นต์ใน HTTP
- ไคลเอ็นต์ส่งคำค้นหาจาก Loadgen ไปยังเซิร์ฟเวอร์ใน gRPC
- เซิร์ฟเวอร์ยอมรับคำค้นหาจากไคลเอ็นต์ ดึงข้อมูลผลงานทั้งหมดของเชกสเปียร์ในรูปแบบข้อความจาก Google Cloud Storage ค้นหาบรรทัดที่มีคำค้นหา และแสดงผลหมายเลขบรรทัดที่ตรงกันให้ไคลเอ็นต์
คุณจะใช้ข้อมูลการติดตามในคำขอ หลังจากนั้น คุณจะฝังเอเจนต์โปรไฟล์ในเซิร์ฟเวอร์และตรวจสอบจุดคอขวด
สิ่งที่คุณจะได้เรียนรู้
- วิธีเริ่มต้นใช้งานไลบรารีการติดตาม OpenTelemetry ในโปรเจ็กต์ Go
- วิธีสร้างช่วงโดยใช้ไลบรารี
- วิธีเผยแพร่บริบทของ Span ผ่านสายระหว่างคอมโพเนนต์ของแอป
- วิธีส่งข้อมูลการติดตามไปยัง Cloud Trace
- วิธีวิเคราะห์การติดตามใน Cloud Trace
Codelab นี้อธิบายวิธีติดตั้งเครื่องมือในไมโครเซอร์วิส ตัวอย่างนี้มีเพียง 3 องค์ประกอบ (เครื่องสร้างภาระงาน ไคลเอ็นต์ และเซิร์ฟเวอร์) เพื่อให้เข้าใจได้ง่าย แต่คุณสามารถใช้กระบวนการเดียวกันที่อธิบายไว้ใน Codelab นี้กับระบบที่ซับซ้อนและมีขนาดใหญ่กว่าได้
สิ่งที่คุณต้องมี
- มีความรู้พื้นฐานเกี่ยวกับ Go
- มีความรู้พื้นฐานเกี่ยวกับ Kubernetes
2. การตั้งค่าและข้อกำหนด
การตั้งค่าสภาพแวดล้อมแบบเรียนรู้ด้วยตนเอง
หากยังไม่มีบัญชี Google (Gmail หรือ Google Apps) คุณต้องสร้างบัญชี ลงชื่อเข้าใช้คอนโซล Google Cloud Platform ( console.cloud.google.com) แล้วสร้างโปรเจ็กต์ใหม่
หากมีโปรเจ็กต์อยู่แล้ว ให้คลิกเมนูแบบเลื่อนลงเพื่อเลือกโปรเจ็กต์ที่ด้านซ้ายบนของคอนโซล

แล้วคลิกปุ่ม "โปรเจ็กต์ใหม่" ในกล่องโต้ตอบที่ปรากฏขึ้นเพื่อสร้างโปรเจ็กต์ใหม่

หากยังไม่มีโปรเจ็กต์ คุณจะเห็นกล่องโต้ตอบแบบนี้เพื่อสร้างโปรเจ็กต์แรก

กล่องโต้ตอบการสร้างโปรเจ็กต์ในภายหลังจะช่วยให้คุณป้อนรายละเอียดของโปรเจ็กต์ใหม่ได้

โปรดจดจำรหัสโปรเจ็กต์ ซึ่งเป็นชื่อที่ไม่ซ้ำกันในโปรเจ็กต์ Google Cloud ทั้งหมด (ชื่อด้านบนมีผู้ใช้แล้วและจะใช้ไม่ได้ ขออภัย) ซึ่งจะเรียกว่า PROJECT_ID ในภายหลังใน Codelab นี้
จากนั้นหากยังไม่ได้ดำเนินการ คุณจะต้องเปิดใช้การเรียกเก็บเงินใน Developers Console เพื่อใช้ทรัพยากร Google Cloud และเปิดใช้ Cloud Trace API

การทำตาม Codelab นี้ไม่ควรมีค่าใช้จ่ายเกิน 2-3 ดอลลาร์ แต่ก็อาจมีค่าใช้จ่ายมากกว่านี้หากคุณตัดสินใจใช้ทรัพยากรเพิ่มเติมหรือปล่อยให้ทรัพยากรทำงานต่อไป (ดูส่วน "การล้างข้อมูล" ที่ท้ายเอกสารนี้) ราคาของ Google Cloud Trace, Google Kubernetes Engine และ Google Artifact Registry ระบุไว้ในเอกสารประกอบอย่างเป็นทางการ
- ราคาของชุดเครื่องมือการดำเนินการของ Google Cloud | Operations Suite
- ราคา | เอกสารประกอบของ Kubernetes Engine
- ราคา Artifact Registry | เอกสารประกอบของ Artifact Registry
ผู้ใช้ใหม่ของ Google Cloud Platform มีสิทธิ์รับช่วงทดลองใช้ฟรีมูลค่า$300 ซึ่งจะทำให้ Codelab นี้ไม่มีค่าใช้จ่ายใดๆ
การตั้งค่า Google Cloud Shell
แม้ว่าคุณจะใช้งาน Google Cloud และ Google Cloud Trace จากระยะไกลในแล็ปท็อปได้ แต่ใน Codelab นี้เราจะใช้ Google Cloud Shell ซึ่งเป็นสภาพแวดล้อมบรรทัดคำสั่งที่ทำงานในระบบคลาวด์
เครื่องเสมือนที่ใช้ Debian นี้มาพร้อมเครื่องมือพัฒนาทั้งหมดที่คุณต้องการ โดยมีไดเรกทอรีหลักแบบถาวรขนาด 5 GB และทำงานใน Google Cloud ซึ่งช่วยเพิ่มประสิทธิภาพเครือข่ายและการตรวจสอบสิทธิ์ได้อย่างมาก ซึ่งหมายความว่าคุณจะต้องมีเพียงเบราว์เซอร์เท่านั้นสำหรับโค้ดแล็บนี้ (ใช่แล้ว ใช้ได้ใน Chromebook)
หากต้องการเปิดใช้งาน Cloud Shell จาก Cloud Console เพียงคลิกเปิดใช้งาน 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 Console

นอกจากนี้ 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 นี้ คุณจะได้เรียกใช้คลัสเตอร์ของ Microservice ใน Google Kubernetes Engine (GKE) กระบวนการของ Codelab นี้มีดังนี้
- ดาวน์โหลดโปรเจ็กต์พื้นฐานลงใน Cloud Shell
- สร้าง Microservice ลงในคอนเทนเนอร์
- อัปโหลดคอนเทนเนอร์ไปยัง Google Artifact Registry (GAR)
- ทำให้คอนเทนเนอร์ใช้งานได้ใน GKE
- แก้ไขซอร์สโค้ดของบริการเพื่อการวัดคุมการติดตาม
- ไปที่ขั้นตอนที่ 2
เปิดใช้ Kubernetes Engine
ก่อนอื่นเราจะตั้งค่าคลัสเตอร์ Kubernetes ที่ Shakesapp ทำงานบน GKE ดังนั้นเราจึงต้องเปิดใช้ GKE ไปที่เมนู "Kubernetes Engine" แล้วกดปุ่มเปิดใช้

ตอนนี้คุณก็พร้อมสร้างคลัสเตอร์ Kubernetes แล้ว
สร้างคลัสเตอร์ Kubernetes
ใน Cloud Shell ให้เรียกใช้คำสั่งต่อไปนี้เพื่อสร้างคลัสเตอร์ Kubernetes โปรดยืนยันว่าค่าโซนอยู่ภายใต้ภูมิภาคที่คุณจะใช้ในการสร้างที่เก็บ 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 ที่พร้อมสำหรับการติดตั้งใช้งานแล้ว จากนั้นเราจะเตรียม Container Registry สำหรับการพุชและติดตั้งใช้งานคอนเทนเนอร์ สำหรับขั้นตอนเหล่านี้ เราต้องตั้งค่า Artifact Registry (GAR) และ Skaffold เพื่อใช้งาน
การตั้งค่า Artifact Registry
ไปที่เมนูของ "Artifact Registry" แล้วกดปุ่มเปิดใช้

หลังจากนั้นสักครู่ คุณจะเห็นเบราว์เซอร์ที่เก็บของ GAR คลิกปุ่ม "สร้างที่เก็บ" แล้วป้อนชื่อที่เก็บ

ใน Codelab นี้ ฉันตั้งชื่อที่เก็บข้อมูลใหม่ว่า trace-codelab รูปแบบของอาร์ติแฟกต์คือ "Docker" และประเภทตำแหน่งคือ "Region" เลือกภูมิภาคที่ใกล้กับภูมิภาคที่คุณตั้งค่าสำหรับโซนเริ่มต้นของ Google Compute Engine ตัวอย่างเช่น ตัวอย่างนี้เลือก "us-central1-f" ด้านบน ดังนั้นเราจึงเลือก "us-central1 (ไอโอวา)" ที่นี่ จากนั้นคลิกปุ่ม "สร้าง"

ตอนนี้คุณจะเห็น "trace-codelab" ในเบราว์เซอร์ที่เก็บ

เราจะกลับมาที่นี่ในภายหลังเพื่อตรวจสอบเส้นทางรีจิสทรี
การตั้งค่า Skaffold
Skaffold เป็นเครื่องมือที่มีประโยชน์เมื่อคุณสร้าง Microservice ที่ทำงานใน Kubernetes โดยจะจัดการเวิร์กโฟลว์ของการสร้าง พุช และการติดตั้งใช้งานคอนเทนเนอร์ของแอปพลิเคชันด้วยชุดคำสั่งเล็กๆ โดยค่าเริ่มต้น Skaffold จะใช้ Docker Registry เป็นรีจิสทรีคอนเทนเนอร์ ดังนั้นคุณจึงต้องกำหนดค่า Skaffold ให้รู้จัก GAR เมื่อพุชคอนเทนเนอร์ไปยัง 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 สำหรับ Container Registry
- ตั้งค่า Skaffold เพื่อใช้ Container Registry
- สร้างคลัสเตอร์ Kubernetes ที่ไมโครเซอร์วิสของ Codelab ทำงาน
ถัดไป
ในขั้นตอนถัดไป คุณจะสร้าง พุช และติดตั้งใช้งานไมโครเซอร์วิสในคลัสเตอร์
3. สร้าง พุช และติดตั้งใช้งานไมโครเซอร์วิส
ดาวน์โหลดเนื้อหา Codelab
ในขั้นตอนก่อนหน้า เราได้ตั้งค่าข้อกำหนดเบื้องต้นทั้งหมดสำหรับ Codelab นี้แล้ว ตอนนี้คุณพร้อมที่จะเรียกใช้ไมโครเซอร์วิสทั้งหมดบนแพลตฟอร์มดังกล่าวแล้ว เนื้อหา Codelab โฮสต์อยู่ใน GitHub ดังนั้นให้ดาวน์โหลดลงในสภาพแวดล้อมของ Shell ใน 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
- manifests: ไฟล์ Manifest ของ Kubernetes
- proto: คำจำกัดความ proto สำหรับการสื่อสารระหว่างไคลเอ็นต์และเซิร์ฟเวอร์
- src: ไดเรกทอรีสำหรับซอร์สโค้ดของแต่ละบริการ
- skaffold.yaml: ไฟล์การกำหนดค่าสำหรับ Skaffold
ใน Codelab นี้ คุณจะอัปเดตซอร์สโค้ดที่อยู่ในโฟลเดอร์ step0 นอกจากนี้ คุณยังดูซอร์สโค้ดในโฟลเดอร์ step[1-6] เพื่อหาคำตอบในขั้นตอนต่อไปนี้ได้ด้วย (ส่วนที่ 1 ครอบคลุมขั้นตอนที่ 0 ถึง 4 และส่วนที่ 2 ครอบคลุมขั้นตอนที่ 5 และ 6)
เรียกใช้คำสั่ง skaffold
สุดท้ายนี้ คุณก็พร้อมที่จะสร้าง พุช และติดตั้งใช้งานเนื้อหาทั้งหมดในคลัสเตอร์ Kubernetes ที่เพิ่งสร้าง ฟังดูเหมือนจะมีหลายขั้นตอน แต่จริงๆ แล้ว Skaffold จะจัดการทุกอย่างให้คุณ ลองใช้คำสั่งต่อไปนี้
cd step0 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 ทำงานตามที่คาดไว้
ถัดไป
ในขั้นตอนถัดไป คุณจะแก้ไขซอร์สโค้ดของบริการ Loadgen เพื่อวัดข้อมูลการติดตาม
4. การวัดคุมสำหรับ HTTP
แนวคิดของการวัดคุมและการเผยแพร่การติดตาม
ก่อนที่จะแก้ไขซอร์สโค้ด ผมขออธิบายสั้นๆ เกี่ยวกับวิธีการทำงานของ Distributed Tracing ในแผนภาพง่ายๆ

ในตัวอย่างนี้ เราจะใช้เครื่องมือกับโค้ดเพื่อส่งออกข้อมูลการติดตามและช่วงไปยัง Cloud Trace และเผยแพร่บริบทการติดตามในคำขอจากบริการ Loadgen ไปยังบริการเซิร์ฟเวอร์
แอปพลิเคชันต้องส่งข้อมูลเมตาของการติดตาม เช่น รหัสการติดตามและรหัส Span เพื่อให้ Cloud Trace รวบรวม Span ทั้งหมดที่มีรหัสการติดตามเดียวกันไว้ในการติดตามเดียว นอกจากนี้ แอปพลิเคชันยังต้องเผยแพร่บริบทการติดตาม (การรวมกันของรหัสการติดตามและรหัสช่วงของช่วงระดับบนสุด) เมื่อขอใช้บริการดาวน์สตรีม เพื่อให้ทราบว่ากำลังจัดการบริบทการติดตามใด
OpenTelemetry ช่วยให้คุณทำสิ่งต่อไปนี้ได้
- เพื่อสร้าง Trace ID และ Span ID ที่ไม่ซ้ำกัน
- เพื่อส่งออก Trace ID และ Span ID ไปยังแบ็กเอนด์
- เพื่อเผยแพร่บริบทการติดตามไปยังบริการอื่นๆ
- เพื่อฝังข้อมูลเมตาเพิ่มเติมที่จะช่วยวิเคราะห์การติดตาม
คอมโพเนนต์ในการติดตาม OpenTelemetry

กระบวนการในการวัดการติดตามแอปพลิเคชันด้วย OpenTelemetry มีดังนี้
- สร้างผู้ส่งออก
- สร้างการเชื่อมโยง TracerProvider ที่เชื่อมโยงเครื่องมือส่งออกใน 1 และตั้งค่าเป็นส่วนกลาง
- ตั้งค่า TextMapPropagaror เพื่อตั้งค่าวิธีการส่งต่อ
- รับ Tracer จาก TracerProvider
- สร้าง Span จาก Tracer
ในตอนนี้ คุณไม่จำเป็นต้องเข้าใจพร็อพเพอร์ตี้โดยละเอียดในแต่ละคอมโพเนนต์ แต่สิ่งที่สำคัญที่สุดที่ควรจดจำคือ
- ผู้ส่งออกที่นี่สามารถเสียบเข้ากับ TracerProvider ได้
- TracerProvider มีการกำหนดค่าทั้งหมดเกี่ยวกับการสุ่มตัวอย่างและการส่งออกการติดตาม
- รวมร่องรอยทั้งหมดไว้ในออบเจ็กต์ Tracer
เมื่อเข้าใจเรื่องนี้แล้ว เรามาดูงานเขียนโค้ดจริงกัน
ช่วงแรกของเครื่องมือ
บริการเครื่องมือสร้างภาระงาน
เปิด Cloud Shell Editor โดยกดปุ่ม
ที่ด้านขวาบนของ Cloud Shell เปิด step0/src/loadgen/main.go จาก Explorer ในแผงด้านซ้าย แล้วค้นหาฟังก์ชันหลัก
step0/src/loadgen/main.go
func main() {
...
for range t.C {
log.Printf("simulating client requests, round %d", i)
if err := run(numWorkers, numConcurrency); err != nil {
log.Printf("aborted round with error: %v", err)
}
log.Printf("simulated %d requests", numWorkers)
if numRounds != 0 && i > numRounds {
break
}
i++
}
}
ในฟังก์ชันหลัก คุณจะเห็นลูปที่เรียกฟังก์ชัน run ในนั้น ในการใช้งานปัจจุบัน ส่วนนี้มีบรรทัดบันทึก 2 บรรทัดที่บันทึกจุดเริ่มต้นและจุดสิ้นสุดของการเรียกใช้ฟังก์ชัน ตอนนี้มาใช้ข้อมูล Span เพื่อติดตามเวลาในการตอบสนองของการเรียกใช้ฟังก์ชันกัน
ก่อนอื่น ให้ตั้งค่าการกำหนดค่าทั้งหมดสำหรับ OpenTelemetry ตามที่ระบุไว้ในส่วนก่อนหน้า เพิ่มแพ็กเกจ OpenTelemetry ดังนี้
step0/src/loadgen/main.go
import (
"context" // step1. add packages
"encoding/json"
"fmt"
"io"
"log"
"math/rand"
"net/http"
"net/url"
"time"
// step1. add packages
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/propagation"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
"go.opentelemetry.io/otel/trace"
// step1. end add packages
)
เราสร้างฟังก์ชันการตั้งค่าที่ชื่อ initTracer และเรียกใช้ในฟังก์ชัน main เพื่อให้โค้ดอ่านง่าย
step0/src/loadgen/main.go
// step1. add OpenTelemetry initialization function
func initTracer() (*sdktrace.TracerProvider, error) {
// create a stdout exporter to show collected spans out to stdout.
exporter, err := stdout.New(stdout.WithPrettyPrint())
if err != nil {
return nil, err
}
// for the demonstration, we use AlwaysSmaple sampler to take all spans.
// do not use this option in production.
tp := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithBatcher(exporter),
)
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.TraceContext{})
return tp, nil
}
คุณอาจทราบว่าขั้นตอนการตั้งค่า OpenTelemetry เป็นไปตามที่อธิบายไว้ในส่วนก่อนหน้า ในการติดตั้งใช้งานนี้ เราใช้stdoutเครื่องมือส่งออกที่ส่งออกข้อมูลการติดตามทั้งหมดไปยัง stdout ในรูปแบบที่มีโครงสร้าง
จากนั้นคุณจะเรียกใช้จากฟังก์ชันหลักได้ เรียกใช้ initTracer() และอย่าลืมเรียกใช้ TracerProvider.Shutdown() เมื่อปิดแอปพลิเคชัน
step0/src/loadgen/main.go
func main() {
// step1. 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)
}
}()
// step1. end setup
log.Printf("starting worder with %d workers in %d concurrency", numWorkers, numConcurrency)
log.Printf("number of rounds: %d (0 is inifinite)", numRounds)
...
เมื่อตั้งค่าเสร็จแล้ว คุณต้องสร้าง Span ที่มีรหัสการติดตามและรหัส Span ที่ไม่ซ้ำกัน OpenTelemetry มีไลบรารีที่สะดวกสำหรับเรื่องนี้ เพิ่มแพ็กเกจใหม่เพิ่มเติมลงในไคลเอ็นต์ HTTP ของเครื่องมือ
step0/src/loadgen/main.go
import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"math/rand"
"net/http"
"net/http/httptrace" // step1. add packages
"net/url"
"time"
// step1. add packages
"go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
// step1. end add packages
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/propagation"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
"go.opentelemetry.io/otel/trace"
)
เนื่องจากเครื่องมือสร้างภาระงานเรียกใช้บริการไคลเอ็นต์ใน HTTP ด้วย net/http ในฟังก์ชัน runQuery เราจึงใช้แพ็กเกจ contrib สำหรับ net/http และเปิดใช้การวัดคุมด้วยส่วนขยายของแพ็กเกจ httptrace และ otelhttp
ก่อนอื่นให้เพิ่มตัวแปรส่วนกลางของแพ็กเกจ httpClient เพื่อเรียกคำขอ HTTP ผ่านไคลเอ็นต์ที่ติดตั้งเครื่องมือ
step0/src/loadgen/main.go
var httpClient = http.Client{
Transport: otelhttp.NewTransport(http.DefaultTransport)
}
จากนั้นเพิ่มการวัดคุมในฟังก์ชัน runQuery เพื่อสร้าง Span ที่กำหนดเองโดยใช้ OpenTelemetry และ Span ที่สร้างขึ้นโดยอัตโนมัติจากไคลเอ็นต์ HTTP ที่กำหนดเอง สิ่งที่คุณต้องทำมีดังนี้
- รับ Tracer จาก
TracerProviderทั่วโลกด้วยotel.Tracer() - สร้างสแปนรูทด้วยเมธอด
Tracer.Start() - สิ้นสุดช่วงรูทในเวลาที่กำหนด (ในกรณีนี้คือสิ้นสุดฟังก์ชัน
runQuery)
step0/src/loadgen/main.go
reqURL.RawQuery = v.Encode()
// step1. replace http.Get() with custom client call
// resp, err := http.Get(reqURL.String())
// step1. instrument trace
ctx := context.Background()
tr := otel.Tracer("loadgen")
ctx, span := tr.Start(ctx, "query.request", trace.WithAttributes(
semconv.TelemetrySDKLanguageGo,
semconv.ServiceNameKey.String("loadgen.runQuery"),
attribute.Key("query").String(s),
))
defer span.End()
ctx = httptrace.WithClientTrace(ctx, otelhttptrace.NewClientTrace(ctx))
req, err := http.NewRequestWithContext(ctx, "GET", reqURL.String(), nil)
if err != nil {
return -1, fmt.Errorf("error creating HTTP request object: %v", err)
}
resp, err := httpClient.Do(req)
// step1. end instrumentation
if err != nil {
return -1, fmt.Errorf("error sending request to %v: %v", reqURL.String(), err)
}
ตอนนี้คุณได้ทำการวัดคุมใน Loadgen (แอปพลิเคชันไคลเอ็นต์ HTTP) เสร็จแล้ว โปรดอย่าลืมอัปเดต go.mod และ go.sum ด้วยคำสั่ง go mod
go mod tidy
บริการลูกค้าของเครื่องมือ
ในส่วนก่อนหน้า เราได้ติดตั้งเครื่องมือในส่วนที่อยู่ในสี่เหลี่ยมผืนผ้าสีแดงในภาพวาดด้านล่าง เราได้ติดตั้งข้อมูล Span ในบริการเครื่องมือสร้างภาระงาน เช่นเดียวกับบริการเครื่องมือสร้างภาระงาน ตอนนี้เราต้องติดตั้งเครื่องมือบริการไคลเอ็นต์ ความแตกต่างจากบริการเครื่องมือสร้างภาระงานคือ บริการไคลเอ็นต์ต้องดึงข้อมูลรหัสการติดตามที่ส่งต่อจากบริการเครื่องมือสร้างภาระงานในส่วนหัว HTTP และใช้รหัสเพื่อสร้างช่วง

เปิด Cloud Shell Editor แล้วเพิ่มแพ็กเกจที่จำเป็นเหมือนที่เราทำสำหรับบริการเครื่องมือสร้างภาระงาน
step0/src/client/main.go
import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/url"
"os"
"time"
"opentelemetry-trace-codelab-go/client/shakesapp"
// step1. add new import
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/propagation"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
// step1. end new import
)
อีกครั้งที่เราต้องตั้งค่า OpenTelemetry เพียงคัดลอกและวางฟังก์ชัน initTracer จาก loadgen แล้วเรียกใช้ในฟังก์ชัน main ของบริการไคลเอ็นต์ด้วย
step0/src/client/main.go
// step1. add OpenTelemetry initialization function
func initTracer() (*sdktrace.TracerProvider, error) {
// create a stdout exporter to show collected spans out to stdout.
exporter, err := stdout.New(stdout.WithPrettyPrint())
if err != nil {
return nil, err
}
// for the demonstration, we use AlwaysSmaple sampler to take all spans.
// do not use this option in production.
tp := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithBatcher(exporter),
)
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.TraceContext{})
return tp, nil
}
ตอนนี้ได้เวลาสร้างเครื่องมือวัดช่วงแล้ว เนื่องจากบริการไคลเอ็นต์ต้องยอมรับคำขอ HTTP จากบริการ Loadgen จึงต้องใช้เครื่องมือจัดการ เซิร์ฟเวอร์ HTTP ในบริการไคลเอ็นต์ได้รับการติดตั้งใช้งานด้วย net/http และคุณสามารถใช้แพ็กเกจ otelhttp ได้เหมือนกับที่เราทำใน loadgen
ก่อนอื่น เราจะแทนที่การลงทะเบียนแฮนเดิลด้วย otelhttp Handler ในฟังก์ชัน main ให้ค้นหาบรรทัดที่ลงทะเบียนตัวแฮนเดิล HTTP ด้วย http.HandleFunc()
step0/src/client/main.go
// step1. change handler to intercept OpenTelemetry related headers
// http.HandleFunc("/", svc.handler)
otelHandler := otelhttp.NewHandler(http.HandlerFunc(svc.handler), "client.handler")
http.Handle("/", otelHandler)
// step1. end intercepter setting
http.HandleFunc("/_genki", svc.health)
จากนั้นเราจะสร้างเครื่องมือสำหรับ Span จริงภายในตัวแฮนเดิล ค้นหาฟังก์ชัน (*clientService) handler() แล้วเพิ่มการวัดคุม Span ด้วย trace.SpanFromContext()
step0/src/client/main.go
func (cs *clientService) handler(w http.ResponseWriter, r *http.Request) {
...
ctx := r.Context()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
// step1. instrument trace
span := trace.SpanFromContext(ctx)
defer span.End()
// step1. end instrument
...
การวัดคุมนี้จะทำให้คุณได้รับช่วงตั้งแต่ต้นจนจบของเมธอด handler หากต้องการให้วิเคราะห์ช่วงได้ง่าย ให้เพิ่มแอตทริบิวต์พิเศษที่จัดเก็บจำนวนที่ตรงกันลงในคำค้นหา เพิ่มโค้ดต่อไปนี้ก่อนบรรทัดบันทึก
func (cs *clientService) handler(w http.ResponseWriter, r *http.Request) {
...
// step1. add span specific attribute
span.SetAttributes(attribute.Key("matched").Int64(resp.MatchCount))
// step1. end adding attribute
log.Println(string(ret))
...
เมื่อใช้การวัดผลทั้งหมดข้างต้น คุณจะทำการวัดผลการติดตามระหว่าง Loadgen กับไคลเอ็นต์เสร็จสมบูรณ์ มาดูวิธีการทำงานกัน เรียกใช้โค้ดด้วย Skaffold อีกครั้ง
skaffold dev
หลังจากรันบริการในคลัสเตอร์ GKE สักระยะ คุณจะเห็นข้อความบันท จำนวนมากดังนี้
เอาต์พุตจากคำสั่ง
[loadgen] {
[loadgen] "Name": "query.request",
[loadgen] "SpanContext": {
[loadgen] "TraceID": "cfa22247a542beeb55a3434392d46b89",
[loadgen] "SpanID": "18b06404b10c418b",
[loadgen] "TraceFlags": "01",
[loadgen] "TraceState": "",
[loadgen] "Remote": false
[loadgen] },
[loadgen] "Parent": {
[loadgen] "TraceID": "00000000000000000000000000000000",
[loadgen] "SpanID": "0000000000000000",
[loadgen] "TraceFlags": "00",
[loadgen] "TraceState": "",
[loadgen] "Remote": false
[loadgen] },
[loadgen] "SpanKind": 1,
[loadgen] "StartTime": "2022-07-14T13:13:36.686751087Z",
[loadgen] "EndTime": "2022-07-14T13:14:31.849601964Z",
[loadgen] "Attributes": [
[loadgen] {
[loadgen] "Key": "telemetry.sdk.language",
[loadgen] "Value": {
[loadgen] "Type": "STRING",
[loadgen] "Value": "go"
[loadgen] }
[loadgen] },
[loadgen] {
[loadgen] "Key": "service.name",
[loadgen] "Value": {
[loadgen] "Type": "STRING",
[loadgen] "Value": "loadgen.runQuery"
[loadgen] }
[loadgen] },
[loadgen] {
[loadgen] "Key": "query",
[loadgen] "Value": {
[loadgen] "Type": "STRING",
[loadgen] "Value": "faith"
[loadgen] }
[loadgen] }
[loadgen] ],
[loadgen] "Events": null,
[loadgen] "Links": null,
[loadgen] "Status": {
[loadgen] "Code": "Unset",
[loadgen] "Description": ""
[loadgen] },
[loadgen] "DroppedAttributes": 0,
[loadgen] "DroppedEvents": 0,
[loadgen] "DroppedLinks": 0,
[loadgen] "ChildSpanCount": 5,
[loadgen] "Resource": [
[loadgen] {
[loadgen] "Key": "service.name",
[loadgen] "Value": {
[loadgen] "Type": "STRING",
[loadgen] "Value": "unknown_service:loadgen"
...
stdout ตัวส่งออกจะส่งข้อความเหล่านี้ คุณจะเห็นว่าช่วงทั้งหมดที่สร้างโดย loadgen มี TraceID: 00000000000000000000000000000000 เนื่องจากนี่คือช่วงรูท ซึ่งเป็นช่วงแรกในเทรซ นอกจากนี้ คุณยังพบว่าแอตทริบิวต์ฝัง "query" มีสตริงการค้นหาที่ส่งไปยังบริการไคลเอ็นต์
สรุป
ในขั้นตอนนี้ คุณได้ติดตั้งบริการเครื่องมือสร้างภาระงานและบริการไคลเอ็นต์ที่สื่อสารใน HTTP และยืนยันว่าคุณสามารถเผยแพร่บริบทการติดตามในบริการต่างๆ และส่งออกข้อมูล Span จากทั้ง 2 บริการไปยัง stdout ได้สำเร็จ
ถัดไป
ในขั้นตอนถัดไป คุณจะติดตั้งเครื่องมือในบริการไคลเอ็นต์และบริการเซิร์ฟเวอร์เพื่อยืนยันวิธีเผยแพร่บริบทการติดตามผ่าน gRPC
5. การวัดคุมสำหรับ gRPC
ในขั้นตอนก่อนหน้า เราได้ติดตั้งเครื่องมือในครึ่งแรกของคำขอในไมโครเซอร์วิสนี้ ในขั้นตอนนี้ เราจะพยายามวัดการสื่อสาร gRPC ระหว่างบริการไคลเอ็นต์และบริการเซิร์ฟเวอร์ (สี่เหลี่ยมผืนผ้าสีเขียวและสีม่วงในรูปภาพด้านล่าง)

การสร้างเครื่องมือล่วงหน้าสำหรับไคลเอ็นต์ gRPC
ระบบนิเวศของ OpenTelemetry มีไลบรารีที่มีประโยชน์มากมายซึ่งช่วยให้นักพัฒนาซอฟต์แวร์สามารถวัดประสิทธิภาพแอปพลิเคชันได้ ในขั้นตอนก่อนหน้า เราใช้เครื่องมือวัดผลที่สร้างไว้ล่วงหน้าสำหรับแพ็กเกจ net/http ในขั้นตอนนี้ เนื่องจากเราพยายามเผยแพร่บริบทการติดตามผ่าน gRPC เราจึงใช้ไลบรารีสำหรับบริบทการติดตาม
ก่อนอื่น ให้นำเข้าแพ็กเกจ gRPC ที่สร้างไว้ล่วงหน้าซึ่งชื่อ otelgrpc
step0/src/client/main.go
import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/url"
"os"
"time"
"opentelemetry-trace-codelab-go/client/shakesapp"
// step2. add prebuilt gRPC package (otelgrpc)
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/propagation"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
ในครั้งนี้ บริการไคลเอ็นต์เป็นไคลเอ็นต์ gRPC ที่เทียบกับบริการเซิร์ฟเวอร์ ดังนั้นคุณจึงต้องวัดประสิทธิภาพไคลเอ็นต์ gRPC ค้นหาฟังก์ชัน mustConnGRPC และเพิ่มตัวสกัดกั้น gRPC ที่สร้างช่วงใหม่ทุกครั้งที่ไคลเอ็นต์ส่งคำขอไปยังเซิร์ฟเวอร์
step0/src/client/main.go
// Helper function for gRPC connections: Dial and create client once, reuse.
func mustConnGRPC(ctx context.Context, conn **grpc.ClientConn, addr string) {
var err error
// step2. add gRPC interceptor
interceptorOpt := otelgrpc.WithTracerProvider(otel.GetTracerProvider())
*conn, err = grpc.DialContext(ctx, addr,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor(interceptorOpt)),
grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor(interceptorOpt)),
grpc.WithTimeout(time.Second*3),
)
// step2: end adding interceptor
if err != nil {
panic(fmt.Sprintf("Error %s grpc: failed to connect %s", err, addr))
}
}
เนื่องจากคุณตั้งค่า OpenTelemetry ไว้แล้วในส่วนก่อนหน้า จึงไม่จำเป็นต้องตั้งค่าอีก
การวัดคุมที่สร้างไว้ล่วงหน้าสำหรับเซิร์ฟเวอร์ gRPC
เช่นเดียวกับที่เราทำกับไคลเอ็นต์ gRPC เราจะเรียกใช้การตรวจสอบที่สร้างไว้ล่วงหน้าสำหรับเซิร์ฟเวอร์ gRPC เพิ่มแพ็กเกจใหม่ในส่วนการนำเข้า เช่น
step0/src/server/main.go
import (
"context"
"fmt"
"io/ioutil"
"log"
"net"
"os"
"regexp"
"strings"
"opentelemetry-trace-codelab-go/server/shakesapp"
"cloud.google.com/go/storage"
// step2. add OpenTelemetry packages including otelgrpc
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"go.opentelemetry.io/otel"
stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/propagation"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"google.golang.org/api/iterator"
"google.golang.org/api/option"
"google.golang.org/grpc"
healthpb "google.golang.org/grpc/health/grpc_health_v1"
)
เนื่องจากเป็นการวัดคุมเซิร์ฟเวอร์เป็นครั้งแรก คุณจึงต้องตั้งค่า OpenTelemetry ก่อน เช่นเดียวกับที่เราทำสำหรับบริการ Loadgen และไคลเอ็นต์
step0/src/server/main.go
// step2. add OpenTelemetry initialization function
func initTracer() (*sdktrace.TracerProvider, error) {
// create a stdout exporter to show collected spans out to stdout.
exporter, err := stdout.New(stdout.WithPrettyPrint())
if err != nil {
return nil, err
}
// for the demonstration, we use AlwaysSmaple sampler to take all spans.
// do not use this option in production.
tp := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithBatcher(exporter),
)
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.TraceContext{})
return tp, nil
}
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
...
และต่อไป คุณต้องเพิ่มตัวสกัดกั้นเซิร์ฟเวอร์ ในฟังก์ชัน main ให้ค้นหาตำแหน่งที่เรียกใช้ grpc.NewServer() แล้วเพิ่มอินเทอร์เซ็ปเตอร์ลงในฟังก์ชัน
step0/src/server/main.go
func main() {
...
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)
...
เรียกใช้ไมโครเซอร์วิสและยืนยันการติดตาม
จากนั้นเรียกใช้โค้ดที่แก้ไขด้วยคำสั่ง skaffold
skaffold dev
ตอนนี้คุณจะเห็นข้อมูล Span จำนวนมากใน stdout อีกครั้ง
เอาต์พุตจากคำสั่ง
...
[server] {
[server] "Name": "shakesapp.ShakespeareService/GetMatchCount",
[server] "SpanContext": {
[server] "TraceID": "89b472f213a400cf975e0a0041649667",
[server] "SpanID": "96030dbad0061b3f",
[server] "TraceFlags": "01",
[server] "TraceState": "",
[server] "Remote": false
[server] },
[server] "Parent": {
[server] "TraceID": "89b472f213a400cf975e0a0041649667",
[server] "SpanID": "cd90cc3859b73890",
[server] "TraceFlags": "01",
[server] "TraceState": "",
[server] "Remote": true
[server] },
[server] "SpanKind": 2,
[server] "StartTime": "2022-07-14T14:05:55.74822525Z",
[server] "EndTime": "2022-07-14T14:06:03.449258891Z",
[server] "Attributes": [
...
[server] ],
[server] "Events": [
[server] {
[server] "Name": "message",
[server] "Attributes": [
...
[server] ],
[server] "DroppedAttributeCount": 0,
[server] "Time": "2022-07-14T14:05:55.748235489Z"
[server] },
[server] {
[server] "Name": "message",
[server] "Attributes": [
...
[server] ],
[server] "DroppedAttributeCount": 0,
[server] "Time": "2022-07-14T14:06:03.449255889Z"
[server] }
[server] ],
[server] "Links": null,
[server] "Status": {
[server] "Code": "Unset",
[server] "Description": ""
[server] },
[server] "DroppedAttributes": 0,
[server] "DroppedEvents": 0,
[server] "DroppedLinks": 0,
[server] "ChildSpanCount": 0,
[server] "Resource": [
[server] {
...
[server] ],
[server] "InstrumentationLibrary": {
[server] "Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
[server] "Version": "semver:0.33.0",
[server] "SchemaURL": ""
[server] }
[server] }
...
คุณสังเกตเห็นว่าคุณไม่ได้ฝังชื่อ Span ใดๆ และสร้าง Span ด้วยตนเองด้วย trace.Start() หรือ span.SpanFromContext() แต่คุณยังคงเห็นช่วงจำนวนมากเนื่องจากตัวสกัดกั้น gRPC สร้างช่วงเหล่านั้น
สรุป
ในขั้นตอนนี้ คุณได้วัดคุมการสื่อสารที่อิงตาม gRPC ด้วยการรองรับจากไลบรารีระบบนิเวศ OpenTelemetry
ถัดไป
ในขั้นตอนถัดไป คุณจะได้เห็นภาพร่องรอยด้วย Cloud Trace และเรียนรู้วิธีวิเคราะห์ช่วงที่รวบรวม
6. แสดงภาพร่องรอยด้วย Cloud Trace
คุณได้ติดตั้งใช้งานการติดตามในทั้งระบบด้วย OpenTelemetry คุณได้เรียนรู้วิธีวัดประสิทธิภาพบริการ HTTP และ gRPC ไปแล้ว แม้ว่าคุณจะรู้วิธีติดตั้งเครื่องมือแล้ว แต่ก็ยังไม่รู้วิธีวิเคราะห์ ในส่วนนี้ คุณจะแทนที่เครื่องมือส่งออก stdout ด้วยเครื่องมือส่งออก Cloud Trace และเรียนรู้วิธีวิเคราะห์การติดตาม
ใช้เครื่องมือส่งออก Cloud Trace
ลักษณะเด่นอย่างหนึ่งของ OpenTelemetry คือความสามารถในการเสียบปลั๊ก หากต้องการแสดงภาพ Span ทั้งหมดที่รวบรวมโดยการวัดคุมของคุณ สิ่งที่คุณต้องทำคือแทนที่เครื่องมือส่งออก stdout ด้วยเครื่องมือส่งออก Cloud Trace
เปิดmain.goไฟล์ของแต่ละบริการ แล้วค้นหาฟังก์ชัน initTracer() ลบบรรทัดเพื่อสร้างโปรแกรมส่งออก stdout และสร้างโปรแกรมส่งออก Cloud Trace แทน
step0/src/loadgen/main.go
import (
...
// step3. add OpenTelemetry for Cloud Trace package
cloudtrace "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace"
)
// step1. add OpenTelemetry initialization function
func initTracer() (*sdktrace.TracerProvider, error) {
// step3. replace stdout exporter with Cloud Trace exporter
// cloudtrace.New() finds the credentials to Cloud Trace automatically following the
// rules defined by golang.org/x/oauth2/google.findDefaultCredentailsWithParams.
// https://pkg.go.dev/golang.org/x/oauth2/google#FindDefaultCredentialsWithParams
exporter, err := cloudtrace.New()
// step3. end replacing exporter
if err != nil {
return nil, err
}
// for the demonstration, we use AlwaysSmaple sampler to take all spans.
// do not use this option in production.
tp := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithBatcher(exporter),
)
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.TraceContext{})
return tp, nil
}
คุณต้องแก้ไขฟังก์ชันเดียวกันในบริการไคลเอ็นต์และเซิร์ฟเวอร์ด้วย
เรียกใช้ไมโครเซอร์วิสและยืนยันการติดตาม
หลังจากแก้ไขแล้ว ให้เรียกใช้คลัสเตอร์ตามปกติด้วยคำสั่ง skaffold
skaffold dev
จากนั้นคุณจะไม่เห็นข้อมูล Span มากนักในรูปแบบบันทึกที่มีโครงสร้างใน stdout เนื่องจากคุณแทนที่เครื่องมือส่งออกด้วย Cloud Trace
เอาต์พุตจากคำสั่ง
[loadgen] 2022/07/14 15:01:07 simulated 20 requests
[loadgen] 2022/07/14 15:01:07 simulating client requests, round 37
[loadgen] 2022/07/14 15:01:14 query 'sweet': matched 958
[client] 2022/07/14 15:01:14 {"match_count":958}
[client] 2022/07/14 15:01:14 {"match_count":3040}
[loadgen] 2022/07/14 15:01:14 query 'love': matched 3040
[client] 2022/07/14 15:01:15 {"match_count":349}
[loadgen] 2022/07/14 15:01:15 query 'hello': matched 349
[client] 2022/07/14 15:01:15 {"match_count":484}
[loadgen] 2022/07/14 15:01:15 query 'faith': matched 484
[loadgen] 2022/07/14 15:01:15 query 'insolence': matched 14
[client] 2022/07/14 15:01:15 {"match_count":14}
[client] 2022/07/14 15:01:21 {"match_count":484}
[loadgen] 2022/07/14 15:01:21 query 'faith': matched 484
[client] 2022/07/14 15:01:21 {"match_count":728}
[loadgen] 2022/07/14 15:01:21 query 'world': matched 728
[client] 2022/07/14 15:01:22 {"match_count":484}
[loadgen] 2022/07/14 15:01:22 query 'faith': matched 484
[loadgen] 2022/07/14 15:01:22 query 'hello': matched 349
[client] 2022/07/14 15:01:22 {"match_count":349}
[client] 2022/07/14 15:01:23 {"match_count":1036}
[loadgen] 2022/07/14 15:01:23 query 'friend': matched 1036
[loadgen] 2022/07/14 15:01:28 query 'tear': matched 463
...
ตอนนี้เรามาตรวจสอบกันว่าระบบได้ส่งช่วงทั้งหมดไปยัง Cloud Trace อย่างถูกต้องหรือไม่ เข้าถึง Cloud Console แล้วไปที่ "รายการการติดตาม" เข้าถึงได้ง่ายจากช่องค้นหา หรือจะคลิกเมนูในแผงด้านซ้ายก็ได้ 
จากนั้นคุณจะเห็นจุดสีน้ำเงินจำนวนมากกระจายอยู่ทั่วกราฟเวลาในการตอบสนอง แต่ละจุดแสดงร่องรอยเดียว

คลิกรายการใดรายการหนึ่ง แล้วคุณจะเห็นรายละเอียดภายในร่องรอย 
แม้จะดูอย่างรวดเร็วและง่ายๆ เช่นนี้ คุณก็ทราบข้อมูลเชิงลึกมากมายแล้ว ตัวอย่างเช่น จากกราฟน้ำตก คุณจะเห็นว่าสาเหตุของเวลาในการตอบสนองส่วนใหญ่เกิดจากช่วงที่มีชื่อว่า shakesapp.ShakespeareService/GetMatchCount (ดู 1 ในรูปภาพด้านบน) คุณสามารถยืนยันได้จากตารางสรุป (คอลัมน์ขวาสุดแสดงระยะเวลาของแต่ละช่วง) นอกจากนี้ ข้อมูลการติดตามนี้ยังใช้สำหรับการค้นหา "เพื่อน" ด้วย (ดู 2 ในรูปภาพด้านบน)
จากการวิเคราะห์แบบย่อเหล่านี้ คุณอาจตระหนักว่าคุณต้องทราบช่วงเวลาที่ละเอียดยิ่งขึ้นภายในวิธี GetMatchCount การแสดงภาพมีประสิทธิภาพมากกว่าข้อมูล stdout ดูข้อมูลเพิ่มเติมเกี่ยวกับรายละเอียด Cloud Trace ได้ที่เอกสารประกอบอย่างเป็นทางการ
สรุป
ในขั้นตอนนี้ คุณได้แทนที่เครื่องมือส่งออก stdout ด้วยเครื่องมือส่งออก Cloud Trace และแสดงภาพร่องรอยใน Cloud Trace นอกจากนี้ คุณยังได้เรียนรู้วิธีเริ่มวิเคราะห์การติดตามด้วย
ถัดไป
ในขั้นตอนถัดไป คุณจะแก้ไขซอร์สโค้ดของบริการเซิร์ฟเวอร์เพื่อเพิ่มช่วงย่อยใน GetMatchCount
7. เพิ่มช่วงย่อยเพื่อการวิเคราะห์ที่ดีขึ้น
ในขั้นตอนก่อนหน้า คุณพบว่าสาเหตุของเวลาในการรับส่งข้อมูลแบบไปกลับที่สังเกตได้จาก loadgen ส่วนใหญ่คือกระบวนการภายในเมธอด GetMatchCount ซึ่งเป็นตัวแฮนเดิล gRPC ในบริการเซิร์ฟเวอร์ อย่างไรก็ตาม เนื่องจากเราไม่ได้วัดผลสิ่งอื่นนอกเหนือจากตัวแฮนเดิล เราจึงไม่สามารถดูข้อมูลเชิงลึกเพิ่มเติมจากกราฟน้ำตกได้ นี่เป็นกรณีที่พบบ่อยเมื่อเราเริ่มใช้เครื่องมือในไมโครเซอร์วิส

ในส่วนนี้ เราจะสร้างเครื่องมือสำหรับช่วงย่อยที่เซิร์ฟเวอร์เรียกใช้ Google Cloud Storage เนื่องจากเป็นเรื่องปกติที่ I/O ของเครือข่ายภายนอกบางรายการอาจใช้เวลานานในกระบวนการ และการระบุว่าการเรียกใช้เป็นสาเหตุหรือไม่นั้นเป็นสิ่งสำคัญ
สร้างเครื่องมือช่วงย่อยในเซิร์ฟเวอร์
เปิด main.go ในเซิร์ฟเวอร์และค้นหาฟังก์ชัน readFiles ฟังก์ชันนี้เรียกคำขอไปยัง Google Cloud Storage เพื่อดึงไฟล์ข้อความทั้งหมดของผลงานของเชกสเปียร์ ในฟังก์ชันนี้ คุณสามารถสร้างช่วงย่อยได้ เช่นเดียวกับที่ทำกับการวัดคุมเซิร์ฟเวอร์ HTTP ในบริการไคลเอ็นต์
step0/src/server/main.go
func readFiles(ctx context.Context, bucketName, prefix string) ([]string, error) {
type resp struct {
s string
err error
}
// step4: add an extra span
span := trace.SpanFromContext(ctx)
span.SetName("server.readFiles")
span.SetAttributes(attribute.Key("bucketname").String(bucketName))
defer span.End()
// step4: end add span
...
เพียงเท่านี้ก็เพิ่มช่วงใหม่ได้แล้ว มาดูกันว่าจะเป็นอย่างไรเมื่อเรียกใช้แอป
เรียกใช้ไมโครเซอร์วิสและยืนยันการติดตาม
หลังจากแก้ไขแล้ว ให้เรียกใช้คลัสเตอร์ตามปกติด้วยคำสั่ง skaffold
skaffold dev
และเลือกการติดตาม 1 รายการชื่อ query.request จากรายการการติดตาม คุณจะเห็นกราฟ Waterfall ของการติดตามที่คล้ายกัน ยกเว้น Span ใหม่ภายใต้ shakesapp.ShakespeareService/GetMatchCount ช่วงที่อยู่ในสี่เหลี่ยมผืนผ้าสีแดงด้านล่าง

จากกราฟนี้ คุณจะเห็นว่าการเรียกใช้ภายนอกไปยัง Google Cloud Storage ทำให้เกิดเวลาในการตอบสนองจำนวนมาก แต่ก็ยังมีสิ่งอื่นๆ ที่ทำให้เกิดเวลาในการตอบสนองส่วนใหญ่
คุณได้รับข้อมูลเชิงลึกมากมายจากการดู 2-3 ครั้งจากกราฟน้ำตกของการติดตาม คุณจะดูรายละเอียดประสิทธิภาพเพิ่มเติมในแอปพลิเคชันได้อย่างไร ซึ่งเป็นจุดที่เครื่องมือสร้างโปรไฟล์เข้ามามีบทบาท แต่ตอนนี้เราจะปิด Codelab นี้และมอบหมายให้บทที่ 2 เป็นบทแนะนำเกี่ยวกับเครื่องมือสร้างโปรไฟล์ทั้งหมด
สรุป
ในขั้นตอนนี้ คุณได้สร้างเครื่องมือสำหรับช่วงอีกช่วงหนึ่งในบริการเซิร์ฟเวอร์และได้รับข้อมูลเชิงลึกเพิ่มเติมเกี่ยวกับเวลาในการตอบสนองของระบบ
8. ขอแสดงความยินดี
คุณสร้างการติดตามแบบกระจายด้วย OpenTelemetry และยืนยันเวลาในการตอบสนองของคำขอใน Microservice บน Google Cloud Trace เรียบร้อยแล้ว
หากต้องการฝึกเพิ่มเติม คุณสามารถลองทำตามหัวข้อต่อไปนี้ด้วยตนเอง
- การติดตั้งใช้งานปัจจุบันจะส่งช่วงทั้งหมดที่สร้างขึ้นโดยการตรวจสอบประสิทธิภาพการทำงาน (
grpc.health.v1.Health/Check) คุณกรองช่วงเหล่านั้นออกจาก Cloud Trace ได้อย่างไร ดูเคล็ดลับได้ที่นี่ - เชื่อมโยงบันทึกเหตุการณ์กับช่วง และดูวิธีการทำงานใน 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 และผู้ดูแลระบบ" > "การตั้งค่า" จากแผงเมนู แล้วคลิกปุ่ม "ปิด"

จากนั้นป้อนรหัสโปรเจ็กต์ (ไม่ใช่ชื่อโปรเจ็กต์) ในแบบฟอร์มในกล่องโต้ตอบและยืนยันการปิด