1. บทนำ
อัปเดตล่าสุด 15-07-2022
ความสามารถในการสังเกตของแอปพลิเคชัน
ความสามารถในการสังเกตและ OpenTelemetry
การสังเกตเป็นคำที่ใช้เพื่ออธิบายแอตทริบิวต์ของระบบ ระบบที่มีความสามารถในการสังเกตช่วยให้ทีมแก้ไขข้อบกพร่องของระบบของตนได้ในเชิงรุก ในบริบทดังกล่าว เสาหลัก 3 ประการของความสามารถในการสังเกต บันทึก เมตริก และการติดตามเป็นเครื่องมือพื้นฐานสำหรับระบบเพื่อให้ได้ความสามารถในการสังเกต
OpenTelemetry คือชุดข้อกำหนด ไลบรารี และตัวแทนที่เร่งการใช้เครื่องมือและการส่งออกข้อมูลการวัดและส่งข้อมูลทางไกล (บันทึก เมตริก และการติดตาม) ที่ความสามารถในการสังเกตต้องการ OpenTelemetry คือโครงการมาตรฐานแบบเปิดและโครงการที่ขับเคลื่อนด้วยชุมชนภายใต้ CNCF การใช้ประโยชน์จากไลบรารีที่โครงการและระบบนิเวศมีให้ ทำให้นักพัฒนาซอฟต์แวร์สามารถนำแอปพลิเคชันของตนไปใช้ในแนวทางที่เป็นกลางของผู้ให้บริการและป้องกันการใช้สถาปัตยกรรมหลายๆ แบบได้
นอกจากเสาหลัก 3 ประการสำหรับความสามารถในการสังเกตแล้ว การทำโปรไฟล์อย่างต่อเนื่องก็เป็นองค์ประกอบหลักอีกอย่างหนึ่งของความสามารถในการสังเกต ทั้งยังเป็นการขยายฐานผู้ใช้ในอุตสาหกรรมอีกด้วย Cloud Profiler คือหนึ่งในผู้ริเริ่มและให้อินเทอร์เฟซง่ายๆ ในการเจาะลึกเมตริกประสิทธิภาพในสแต็กการเรียกใช้แอปพลิเคชัน
Codelab นี้เป็นส่วนที่ 1 ของซีรีส์ และครอบคลุมการใช้การติดตามที่มีการกระจายใน Microservice ด้วย OpenTelemetry และ Cloud Trace ส่วนที่ 2 จะครอบคลุมการทำโปรไฟล์อย่างต่อเนื่องด้วย Cloud Profiler
การติดตามแบบกระจาย
การติดตามเป็นการวัดและส่งข้อมูลทางไกลที่บอกเวลาในการตอบสนองของส่วนที่เฉพาะเจาะจงของกระบวนการในระบบ โดยในบันทึก เมตริก และการติดตาม โดยเฉพาะอย่างยิ่งในยุคของ Microservice การติดตามแบบกระจายเป็นตัวขับเคลื่อนที่แข็งแกร่งในการค้นหาจุดคอขวดของเวลาในการตอบสนองในระบบแบบกระจายโดยรวม
เมื่อวิเคราะห์การติดตามแบบกระจาย การแสดงภาพข้อมูลการติดตามเป็นกุญแจสำคัญในการทำความเข้าใจเวลาในการตอบสนองของระบบโดยรวมอย่างรวดเร็ว ในการติดตามแบบกระจาย เราจะจัดการชุดการเรียกใช้เพื่อประมวลผลคำขอเดียวไปยังจุดแรกเข้าของระบบในรูปแบบ Trace ที่มี Spans หลายรายการ
ช่วงแสดงถึงหน่วยงานแต่ละหน่วยที่ทำในระบบที่กระจายตัว ซึ่งจะบันทึกเวลาเริ่มต้นและเวลาสิ้นสุด Span มักจะมีความสัมพันธ์แบบลำดับชั้นระหว่างกัน - ในรูปภาพใต้สแปนที่เล็กกว่าทั้งหมดคือสแปนลูกของช่วง /messages ขนาดใหญ่ และถูกประกอบกันเป็น Trace เดียวที่จะแสดงเส้นทางการทำงานผ่านระบบ
Google Cloud Trace เป็นหนึ่งในตัวเลือกสำหรับแบ็กเอนด์การติดตามแบบกระจายและผสานรวมกับผลิตภัณฑ์อื่นๆ ใน Google Cloud เป็นอย่างดี
สิ่งที่คุณจะสร้าง
ใน Codelab นี้ คุณจะป้อนข้อมูลการติดตามการใช้เครื่องมือในบริการที่มีชื่อว่า "แอปพลิเคชัน Shakespeare" (หรือที่รู้จักในชื่อ Shakesapp) ที่เรียกใช้บนคลัสเตอร์ Google Kubernetes Engine สถาปัตยกรรมของ Shakesapp อธิบายไว้ด้านล่าง
- Loadgen ส่งสตริงการค้นหาไปยังไคลเอ็นต์ใน HTTP
- ไคลเอ็นต์ส่งผ่านการค้นหาจากตัวจัดสรรภาระงานไปยังเซิร์ฟเวอร์ใน gRPC
- เซิร์ฟเวอร์ยอมรับคำค้นหาจากไคลเอ็นต์ ดึงข้อมูลงานของ Shakespare ทั้งหมดในรูปแบบข้อความจาก Google Cloud Storage, ค้นหาบรรทัดที่มีคำค้นหา และแสดงผลจำนวนบรรทัดที่ตรงกับไคลเอ็นต์
คุณจะใช้ข้อมูลการติดตามในคำขอ หลังจากนั้น คุณจะต้องฝัง Agent ของเครื่องมือสร้างโปรไฟล์ในเซิร์ฟเวอร์และตรวจสอบจุดคอขวด
สิ่งที่คุณจะได้เรียนรู้
- วิธีเริ่มต้นใช้งานไลบรารี OpenTelemetry Trace ในโปรเจ็กต์ Go
- วิธีสร้างระยะเวลาด้วยไลบรารี
- วิธีเผยแพร่บริบทสแปนไปตามสายไฟระหว่างส่วนประกอบต่างๆ ของแอป
- วิธีส่งข้อมูลการติดตามไปยัง Cloud Trace
- วิธีวิเคราะห์การติดตามใน Cloud Trace
Codelab นี้จะอธิบายถึงวิธีใช้ Microservice เพื่อให้เข้าใจง่าย ตัวอย่างนี้มีเพียง 3 คอมโพเนนต์ (ตัวสร้างโหลด ไคลเอ็นต์ และเซิร์ฟเวอร์) แต่คุณสามารถใช้กระบวนการเดียวกันกับที่อธิบายใน Codelab นี้กับระบบที่ซับซ้อนและมีขนาดใหญ่
สิ่งที่คุณต้องมี
- ความรู้พื้นฐานเกี่ยวกับ Go
- ความรู้พื้นฐานเกี่ยวกับ Kubernetes
2. การตั้งค่าและข้อกำหนด
การตั้งค่าสภาพแวดล้อมตามเวลาที่สะดวก
หากยังไม่มีบัญชี Google (Gmail หรือ Google Apps) คุณต้องสร้างบัญชีก่อน ลงชื่อเข้าใช้คอนโซล Google Cloud Platform ( console.cloud.google.com) และสร้างโปรเจ็กต์ใหม่
หากคุณมีโปรเจ็กต์อยู่แล้ว ให้คลิกเมนูแบบเลื่อนลงสำหรับการเลือกโปรเจ็กต์ที่ด้านซ้ายบนของคอนโซล
แล้วคลิก "โปรเจ็กต์ใหม่" ในกล่องโต้ตอบที่ปรากฏขึ้นเพื่อสร้างโปรเจ็กต์ใหม่ ดังนี้
หากคุณยังไม่มีโปรเจ็กต์ คุณจะเห็นกล่องโต้ตอบลักษณะนี้ให้สร้างโปรเจ็กต์แรก
กล่องโต้ตอบการสร้างโปรเจ็กต์ที่ตามมาจะให้คุณป้อนรายละเอียดของโปรเจ็กต์ใหม่:
โปรดจำรหัสโปรเจ็กต์ ซึ่งเป็นชื่อที่ไม่ซ้ำกันในโปรเจ็กต์ Google Cloud ทั้งหมด (ระบบใช้ชื่อด้านบนนี้ไปแล้ว และจะใช้ไม่ได้ ขออภัย) โดยจะเรียกใน Codelab ว่า PROJECT_ID ในภายหลัง
ขั้นตอนถัดไป คุณจะต้องเปิดใช้การเรียกเก็บเงินใน Developers Console เพื่อใช้ทรัพยากรของ Google Cloud และเปิดใช้ Cloud Trace API หากยังไม่ได้ดำเนินการ
การใช้งาน Codelab นี้น่าจะมีค่าใช้จ่ายไม่เกิน 2-3 ดอลลาร์ แต่อาจมากกว่านี้หากคุณตัดสินใจใช้ทรัพยากรเพิ่มหรือปล่อยให้ทำงาน (ดูส่วน "ล้างข้อมูล" ในตอนท้ายของเอกสารนี้) ราคาของ 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 จะทำงานจากระยะไกลจากแล็ปท็อปได้ แต่เราจะใช้ Google Cloud Shell ซึ่งเป็นสภาพแวดล้อมของบรรทัดคำสั่งที่ทำงานในระบบคลาวด์ใน Codelab
เครื่องเสมือนแบบ Debian นี้เต็มไปด้วยเครื่องมือการพัฒนาทั้งหมดที่คุณต้องการ โดยมีไดเรกทอรีหลักขนาด 5 GB ที่ทำงานอย่างต่อเนื่องใน Google Cloud ซึ่งจะช่วยเพิ่มประสิทธิภาพของเครือข่ายและการตรวจสอบสิทธิ์ได้อย่างมาก ซึ่งหมายความว่าสิ่งที่คุณต้องมีสำหรับ Codelab นี้คือเบราว์เซอร์ (ใช่แล้ว ทั้งหมดนี้ทำงานได้บน 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
คุณเลือกโซนต่างๆ ได้หลากหลาย ดูข้อมูลเพิ่มเติมได้ที่ภูมิภาคและ โซน
ตั้งค่าภาษา Go
ใน 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" และประเภทสถานที่ตั้งคือ "ภูมิภาค" เลือกภูมิภาคที่ใกล้เคียงกับภูมิภาคที่คุณตั้งค่าไว้สำหรับโซนเริ่มต้นของ Google Compute Engine เช่น ตัวอย่างนี้เลือก "us-central1-f" ด้านบน ดังนั้นตรงนี้เราจะเลือก "us-central1 (ไอโอวา)" จากนั้นคลิก "สร้าง"
ตอนนี้คุณจะเห็น "trace-codelab" ในเบราว์เซอร์ที่เก็บ
เราจะกลับมาที่นี่ในภายหลังเพื่อตรวจสอบเส้นทางรีจิสทรี
การตั้งค่า Skaf Fold
Skaffold เป็นเครื่องมือที่มีประโยชน์เมื่อคุณสร้าง Microservice ที่ทำงานบน 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 เพื่อใช้ Container Registry
- สร้างคลัสเตอร์ Kubernetes ที่ Codelab Microservice ทำงาน
ถัดไป
ในขั้นตอนถัดไป คุณจะสร้าง พุช และทำให้ Microservice ใช้งานได้บนคลัสเตอร์
3. สร้าง พุช และติดตั้งใช้งาน Microservice
ดาวน์โหลดเนื้อหา Codelab
ในขั้นตอนก่อนหน้า เราได้กำหนดข้อกำหนดเบื้องต้นทั้งหมดสำหรับ Codelab แล้ว ตอนนี้คุณพร้อมที่จะเรียกใช้ Microservice ทั้งระบบแล้ว เนื้อหา 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
- ไฟล์ Manifest: ไฟล์ 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 ในสภาพแวดล้อมของคุณและยืนยันว่าสเกฟโฟลด์ทำงานได้ตามที่คาดไว้
ถัดไป
ในขั้นตอนถัดไป คุณจะต้องแก้ไขซอร์สโค้ดของบริการ กำลังโหลดข้อมูลเพื่อใช้ข้อมูลการติดตาม
4. เครื่องมือสำหรับ HTTP
แนวคิดของการใช้เครื่องมือการติดตามและการเผยแพร่
ก่อนแก้ไขซอร์สโค้ด เราขออธิบายคร่าวๆ ว่าการติดตามแบบกระจายตัวทำงานอย่างไรในแผนภาพง่ายๆ
ในตัวอย่างนี้ เราใช้เครื่องมือของโค้ดเพื่อส่งออกข้อมูลการติดตามและข้อมูล Span ไปยัง Cloud Trace และเผยแพร่บริบทของการติดตามในคำขอจากบริการโหลดข้อมูลไปยังบริการเซิร์ฟเวอร์
แอปพลิเคชันต้องส่งข้อมูลเมตาของการติดตาม เช่น รหัสการติดตามและรหัส Span เพื่อให้ Cloud Trace รวมสแปนทั้งหมดที่มีรหัสการติดตามเดียวกันไว้ในการติดตามเดียว นอกจากนี้ แอปพลิเคชันจำเป็นต้องเผยแพร่บริบทการติดตาม (ชุดค่าผสมของรหัสการติดตามและรหัส Span ของสแปนหลัก) ในการขอบริการดาวน์สตรีม เพื่อให้แอปพลิเคชันรับรู้บริบทการติดตามที่กำลังจัดการอยู่
OpenTelemetry ช่วยคุณทำสิ่งต่อไปนี้
- เพื่อสร้างรหัสการติดตามและรหัส Span ที่ไม่ซ้ำกัน
- เพื่อส่งออกรหัสการติดตามและรหัส Span ไปยังแบ็กเอนด์
- เพื่อเผยแพร่บริบทการติดตามไปยังบริการอื่นๆ
- เพื่อฝังข้อมูลเมตาเพิ่มเติมที่จะช่วยวิเคราะห์การติดตาม
คอมโพเนนต์ใน OpenTelemetry Trace
กระบวนการในการใช้เครื่องมือการติดตามแอปพลิเคชันด้วย OpenTelemetry มีดังนี้
- สร้างผู้ส่งออก
- สร้าง TracerProvider ที่ใช้เชื่อมโยงผู้ส่งออกในขั้นตอนเดียวและตั้งค่าให้เป็นทั่วโลก
- ตั้งค่า TextMapPropagaror เพื่อตั้งค่าวิธีการนำไปใช้
- รับ Tracer จาก TracerProvider
- สร้างช่วงจากเครื่องมือติดตาม
ปัจจุบันคุณไม่จำเป็นต้องเข้าใจคุณสมบัติโดยละเอียดในแต่ละคอมโพเนนต์ แต่สิ่งสำคัญที่สุดที่ควรคำนึงถึงคือ
- ผู้ส่งออกที่นี่สามารถเสียบเข้ากับ TracerProvider ได้
- TracerProvider จะเก็บการกำหนดค่าทั้งหมดเกี่ยวกับการสุ่มตัวอย่างการติดตามและการส่งออก
- การติดตามทั้งหมดจะรวมอยู่ในออบเจ็กต์ Tracer
สำหรับความเข้าใจในเรื่องนี้ เรามาดูงานการเขียนโค้ดจริงกันดีกว่า
ช่วงแรกของเครื่องมือ
บริการสร้างโหลดเครื่องมือ
เปิด Cloud Shell Editor โดยกดปุ่มที่ด้านบนขวาของ Cloud Shell เปิด
step0/src/loadgen/main.go
จากเครื่องมือสำรวจในแผงด้านซ้าย และค้นหาฟังก์ชันหลัก
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
เพื่อสร้างช่วงเวลาที่กำหนดเองโดยใช้ OpenTelemetry และช่วงที่สร้างโดยอัตโนมัติจากไคลเอ็นต์ HTTP ที่กำหนดเอง สิ่งที่คุณต้องดำเนินการมีดังต่อไปนี้
- รับเครื่องมือติดตามจาก
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) }
ตอนนี้คุณก็ทำการติดตั้งใช้งานในตัวจัดสรรภาระงาน (แอปพลิเคชันไคลเอ็นต์ 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 )
อีกครั้ง เราต้องตั้งค่า OpenTelemtry เพียงคัดลอกและวางฟังก์ชัน initTracer
จากตัวจัดสรรภาระงาน แล้วเรียกใช้ในฟังก์ชัน 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 }
ตอนนี้ได้เวลาของ Span แล้ว เนื่องจากบริการไคลเอ็นต์ต้องยอมรับคำขอ HTTP จากบริการตัวจัดสรรภาระงาน บริการจึงต้องใช้เครื่องจัดการ เซิร์ฟเวอร์ HTTP ในบริการไคลเอ็นต์มีการใช้งานร่วมกับ net/http และคุณใช้แพ็กเกจ otelhttp
ได้เหมือนกับที่เราทำในโหลด
ก่อนอื่น เราแทนที่การลงทะเบียนตัวแฮนเดิลด้วยตัวแฮนเดิล otelhttp
ในฟังก์ชัน 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)
จากนั้น เราวัดระยะเวลาจริงภายในเครื่องจัดการ ค้นหาเครื่องจัดการฟังก์ (*clientService)() และเพิ่มการวัดระยะเวลาด้วย 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)) ...
การใช้เครื่องมือทั้งหมดข้างต้นช่วยให้คุณทำการใช้เครื่องมือการติดตามระหว่างภาระงานและไคลเอ็นต์ได้เสร็จสมบูรณ์ มาดูวิธีการทำงานของฟังก์ชันนี้กัน เรียกใช้โค้ดด้วย 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
จะส่งข้อความเหล่านี้ คุณจะเห็นว่าสแปนระดับบนของสแปนตามโหลดมี TraceID: 00000000000000000000000000000000
เพราะนี่คือสแปนราก ซึ่งก็คือสแปนแรกในการติดตาม นอกจากนี้ คุณยังพบว่าแอตทริบิวต์การฝัง "query"
มีสตริงคำค้นหาที่ส่งไปยังฝ่ายบริการลูกค้า
สรุป
ในขั้นตอนนี้ คุณได้ติดตั้งบริการตัวสร้างโหลดและบริการไคลเอ็นต์ที่สื่อสารใน HTTP และยืนยันว่าคุณสามารถเผยแพร่ Trace Context ไปยังบริการต่างๆ และส่งออกข้อมูล Span จากทั้ง 2 บริการไปยัง Sttdout ได้สำเร็จ
ถัดไป
ในขั้นตอนถัดไป คุณจะต้องใช้บริการลูกค้าและบริการเซิร์ฟเวอร์เพื่อยืนยันวิธีเผยแพร่บริบทการติดตามผ่าน gRPC
5. การวัดคุมสำหรับ gRPC
ในขั้นตอนก่อนหน้า เราได้บังคับใช้ช่วงครึ่งแรกของคำขอใน Microservice นี้ ในขั้นตอนนี้ เราพยายามวัดคุมการสื่อสารด้วย 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 ก่อน คล้ายกับที่เราทำสำหรับโหลดและให้บริการลูกค้า
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) ...
เรียกใช้ Microservice และยืนยันการติดตาม
จากนั้นเรียกใช้โค้ดที่แก้ไขแล้วด้วยคำสั่ง 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] } ...
คุณสังเกตเห็นว่าคุณไม่ได้ฝังชื่อสแปนใดๆ และระยะเวลาที่สร้างขึ้นด้วยตนเองโดยใช้ trace.Start()
หรือ span.SpanFromContext()
แต่คุณยังได้รับ Span จำนวนมากเนื่องจาก Interceptor ของ gRPC สร้างขึ้น
สรุป
ในขั้นตอนนี้ คุณใช้การสื่อสารที่ใช้ gRPC ด้วยการสนับสนุนจากไลบรารีระบบนิเวศ OpenTelemetry
ถัดไป
ในขั้นตอนถัดไป คุณจะแสดงภาพการติดตามด้วย Cloud Trace และดูวิธีวิเคราะห์ระยะเวลาที่เก็บรวบรวม
6. แสดงภาพการติดตามด้วย Cloud Trace
คุณมีการติดตามในเครื่องทั้งระบบด้วย OpenTelemetry คุณได้เรียนรู้วิธีใช้งานบริการ HTTP และ gRPC แล้ว แม้คุณจะเรียนรู้วิธีใช้งานเครื่องมือดังกล่าวไปแล้ว แต่ก็ยังไม่ได้เรียนรู้วิธีวิเคราะห์เครื่องมือ ในส่วนนี้ คุณจะใช้ผู้ส่งออก Cloud Trace แทนผู้ส่งออก stdout และดูวิธีวิเคราะห์การติดตาม
ใช้เครื่องมือส่งออก 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 }
คุณต้องแก้ไขฟังก์ชันเดียวกันในบริการไคลเอ็นต์และเซิร์ฟเวอร์ด้วย
เรียกใช้ Microservice และยืนยันการติดตาม
หลังจากแก้ไขแล้ว ให้เรียกใช้คลัสเตอร์ตามปกติด้วยคำสั่ง 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 ...
ตอนนี้ให้ยืนยันว่าส่ง Span ทั้งหมดไปยัง Cloud Trace อย่างถูกต้องหรือไม่ เข้าถึง Cloud Console แล้วไปที่ "รายการการติดตาม" คุณสามารถเข้าถึงได้ง่ายๆ จากช่องค้นหา หรือคลิกเมนูในแผงด้านซ้าย
จากนั้นคุณจะเห็นจุดสีน้ำเงินจำนวนมากกระจายอยู่ในกราฟเวลาในการตอบสนอง แต่ละจุดจะแสดงการติดตามเดียว
คลิกตัวเลือกที่ต้องการเพื่อดูรายละเอียดภายในการติดตาม
แม้จะดูง่ายๆ เพียงสั้นๆ นี้แล้ว คุณก็รู้ข้อมูลเชิงลึกมากมายอยู่แล้ว ตัวอย่างเช่น จากกราฟ Waterfall คุณจะเห็นว่าสาเหตุของเวลาในการตอบสนองนั้นส่วนใหญ่เกิดจากสแปนที่ชื่อว่า shakesapp.ShakespeareService/GetMatchCount
(ดู 1 ในรูปภาพด้านบน) คุณสามารถยืนยันได้จากตารางสรุป (คอลัมน์ด้านขวาสุดแสดงระยะเวลาของแต่ละช่วง) นอกจากนี้ การติดตามนี้ยังใช้สำหรับคำค้นหา "เพื่อน" (ดู 2 ในรูปภาพด้านบน)
จากการวิเคราะห์สั้นๆ เหล่านี้ คุณอาจพบว่าต้องการรู้ระยะเวลาที่ละเอียดขึ้นในเมธอด GetMatchCount
เมื่อเทียบกับข้อมูล stdout การแสดงภาพนั้นมีประสิทธิภาพมาก หากต้องการดูข้อมูลเพิ่มเติมเกี่ยวกับรายละเอียด Cloud Trace โปรดไปที่เอกสารอย่างเป็นทางการ
สรุป
ในขั้นตอนนี้ คุณได้แทนที่เครื่องมือส่งออก stdout ด้วย Cloud Trace 1 และแสดงภาพการติดตามบน Cloud Trace และคุณยังได้เรียนรู้วิธีเริ่มวิเคราะห์การติดตามด้วย
ถัดไป
ในขั้นตอนถัดไป คุณจะต้องแก้ไขซอร์สโค้ดของบริการเซิร์ฟเวอร์เพื่อเพิ่มช่วงเวลาย่อยใน GetMatchCount
7. เพิ่มช่วงย่อยเพื่อให้วิเคราะห์ได้ดีขึ้น
ในขั้นตอนก่อนหน้า คุณพบสาเหตุของระยะเวลาไป-กลับที่สังเกตจากการโหลดเกนมักเป็นกระบวนการภายในเมธอด GetMatchCount ซึ่งเป็นตัวแฮนเดิล gRPC ในบริการเซิร์ฟเวอร์ อย่างไรก็ตาม เนื่องจากเราไม่ได้ใช้เครื่องมืออื่นใดนอกเหนือจากเครื่องจัดการ ทำให้เราไม่สามารถดูข้อมูลเชิงลึกเพิ่มเติมจากกราฟ Waterfall ซึ่งเป็นกรณีที่พบบ่อยเมื่อเราเริ่มวัดคุม Microservice
ในส่วนนี้ เราจะกล่าวถึงช่วงย่อยที่เซิร์ฟเวอร์เรียกใช้ 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 ...
ทั้งหมดนี้คือวิธีเพิ่มระยะเวลาใหม่ มาดูกันว่าผลเป็นอย่างไรเมื่อเรียกใช้แอป
เรียกใช้ Microservice และยืนยันการติดตาม
หลังจากแก้ไขแล้ว ให้เรียกใช้คลัสเตอร์ตามปกติด้วยคำสั่ง Skaffold
skaffold dev
และเลือกการติดตาม 1 รายการที่ชื่อ query.request
จากรายการการติดตาม คุณจะเห็นกราฟ Waterfall การติดตามที่คล้ายกันยกเว้นระยะเวลาใหม่ที่ต่ำกว่า shakesapp.ShakespeareService/GetMatchCount
(ช่วงระยะเวลาที่อยู่ในรูปสี่เหลี่ยมผืนผ้าสีแดงด้านล่าง)
สิ่งที่คุณสามารถบอกได้จากกราฟนี้ในตอนนี้คือการเรียกภายนอกไปยัง Google Cloud Storage จะใช้เวลาในการตอบสนองปริมาณมาก แต่ยังคงมีสิ่งอื่นๆ ที่ทำให้เวลาในการตอบสนองส่วนใหญ่ยังคงเป็นอยู่
คุณได้รับข้อมูลเชิงลึกมากมายจากกราฟ Waterfall ของการติดตาม 2-3 รูปแบบแล้ว คุณจะได้รับรายละเอียดด้านประสิทธิภาพเพิ่มเติมในใบสมัครได้อย่างไร จากตรงนี้ เครื่องมือสร้างโปรไฟล์จะเข้ามา แต่ในตอนนี้เราจะทำให้ตอนท้ายของ Codelab นี้จบลง และนำเสนอบทแนะนำเครื่องมือสร้างโปรไฟล์ทั้งหมดในตอนที่ 2
สรุป
ในขั้นตอนนี้ คุณได้ใช้อีกช่วงเวลาหนึ่งในบริการเซิร์ฟเวอร์ และได้รับข้อมูลเชิงลึกเพิ่มเติมเกี่ยวกับเวลาในการตอบสนองของระบบ
8. ขอแสดงความยินดี
คุณสร้างการติดตามแบบกระจายด้วย OpenTelemery และยืนยันเวลาในการตอบสนองของคำขอสำหรับ Microservice บน Google Cloud Trace เรียบร้อยแล้ว
สำหรับการออกกำลังกายระยะยาว คุณลองทำตามหัวข้อต่อไปนี้ด้วยตนเองได้
- การใช้งานปัจจุบันจะส่ง Span ทั้งหมดที่สร้างโดยการตรวจสอบประสิทธิภาพการทำงาน (
grpc.health.v1.Health/Check
) คุณจะกรองระยะเวลาเหล่านั้นออกจาก Cloud Traces อย่างไร คำแนะนำอยู่ที่นี่ - เชื่อมโยงบันทึกเหตุการณ์กับระยะเวลา แล้วดูว่าบันทึกนั้นทำงานอย่างไรใน 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 & จากแผงเมนู ผู้ดูแลระบบ" "การตั้งค่า" แล้วคลิก "ปิด"
จากนั้นป้อนรหัสโปรเจ็กต์ (ไม่ใช่ชื่อโปรเจ็กต์) ในแบบฟอร์มในกล่องโต้ตอบและยืนยันการปิดเครื่อง