Công cụ giúp cải thiện hiệu suất trong ứng dụng trong Go (phần 2: trình phân tích tài nguyên)

1. Giới thiệu

e0509e8a07ad5537.png

Lần cập nhật gần đây nhất: ngày 14 tháng 7 năm 2022

Khả năng ghi nhận của ứng dụng

Khả năng quan sát và Trình phân tích tài nguyên liên tục

Khả năng ghi nhận là thuật ngữ dùng để mô tả một thuộc tính của hệ thống. Một hệ thống có khả năng quan sát cho phép các nhóm chủ động gỡ lỗi hệ thống của họ. Trong bối cảnh đó, 3 trụ cột của khả năng ghi nhận (nhật ký, chỉ số và dấu vết) là khả năng đo lường cơ bản để hệ thống có được khả năng ghi nhận.

Ngoài 3 trụ cột của khả năng ghi nhận, việc lập hồ sơ liên tục cũng là một thành phần quan trọng khác của khả năng ghi nhận và đang mở rộng cơ sở người dùng trong ngành. Cloud Profiler là một trong những công cụ khởi tạo và cung cấp một giao diện dễ dàng để đi sâu vào các chỉ số hiệu suất trong ngăn xếp lệnh gọi ứng dụng.

Đây là phần 2 của loạt lớp học lập trình này, trong đó trình bày cách đo lường một tác nhân trình phân tích tài nguyên liên tục. Phần 1 đề cập đến tính năng theo dõi phân tán bằng OpenTelemetry và Cloud Trace, đồng thời bạn sẽ tìm hiểu thêm về cách xác định điểm tắc nghẽn của các vi dịch vụ một cách hiệu quả hơn bằng phần 1.

Sản phẩm bạn sẽ tạo ra

Trong lớp học lập trình này, bạn sẽ đo lường tác nhân trình phân tích tài nguyên liên tục trong dịch vụ máy chủ của "ứng dụng Shakespeare" (còn gọi là Shakesapp) chạy trên một cụm Google Kubernetes Engine. Kiến trúc của Shakesapp như mô tả bên dưới:

44e243182ced442f.png

  • Loadgen gửi một chuỗi truy vấn đến máy khách trong HTTP
  • Các ứng dụng truyền truy vấn từ loadgen đến máy chủ trong gRPC
  • Máy chủ chấp nhận truy vấn từ máy khách, tìm nạp tất cả các tác phẩm của Shakespeare ở định dạng văn bản từ Google Cloud Storage, tìm kiếm các dòng có chứa truy vấn và trả về số dòng khớp với máy khách

Trong phần 1, bạn nhận thấy rằng có một điểm tắc nghẽn ở đâu đó trong dịch vụ máy chủ, nhưng bạn không thể xác định chính xác nguyên nhân.

Kiến thức bạn sẽ học được

  • Cách nhúng tác nhân lập hồ sơ
  • Cách điều tra nút thắt cổ chai trên Cloud Profiler

Lớp học lập trình này giải thích cách đo lường một tác nhân trình phân tích tài nguyên liên tục trong ứng dụng của bạn.

Bạn cần có

  • Kiến thức cơ bản về Go
  • Kiến thức cơ bản về Kubernetes

2. Thiết lập và yêu cầu

Thiết lập môi trường theo tốc độ của riêng bạn

Nếu chưa có Tài khoản Google (Gmail hoặc Google Apps), bạn phải tạo một tài khoản. Đăng nhập vào bảng điều khiển Google Cloud Platform ( console.cloud.google.com) rồi tạo một dự án mới.

Nếu bạn đã có dự án, hãy nhấp vào trình đơn thả xuống chọn dự án ở phía trên bên trái của bảng điều khiển:

7a32e5469db69e9.png

rồi nhấp vào nút "DỰ ÁN MỚI" trong hộp thoại xuất hiện để tạo một dự án mới:

7136b3ee36ebaf89.png

Nếu chưa có dự án, bạn sẽ thấy một hộp thoại như thế này để tạo dự án đầu tiên:

870a3cbd6541ee86.png

Hộp thoại tạo dự án tiếp theo cho phép bạn nhập thông tin chi tiết về dự án mới:

affdc444517ba805.png

Hãy nhớ mã dự án. Đây là tên duy nhất trên tất cả các dự án Google Cloud (tên ở trên đã được sử dụng và sẽ không hoạt động đối với bạn, xin lỗi!). Sau này trong lớp học lập trình này, chúng ta sẽ gọi đó là PROJECT_ID.

Tiếp theo, nếu chưa làm, bạn cần phải bật tính năng thanh toán trong Play Console để sử dụng các tài nguyên của Google Cloud và bật Cloud Trace API.

15d0ef27a8fbab27.png

Việc thực hiện lớp học lập trình này sẽ không tốn của bạn quá vài đô la, nhưng có thể tốn nhiều hơn nếu bạn quyết định sử dụng nhiều tài nguyên hơn hoặc nếu bạn để các tài nguyên đó chạy (xem phần "dọn dẹp" ở cuối tài liệu này). Giá của Google Cloud Trace, Google Kubernetes Engine và Google Artifact Registry được ghi trong tài liệu chính thức.

Người dùng mới của Google Cloud Platform đủ điều kiện dùng thử miễn phí trị giá 300 USD. Nhờ đó, bạn có thể hoàn toàn miễn phí tham gia lớp học lập trình này.

Thiết lập Google Cloud Shell

Mặc dù bạn có thể vận hành Google Cloud và Google Cloud Trace từ xa trên máy tính xách tay, nhưng trong lớp học lập trình này, chúng ta sẽ sử dụng Google Cloud Shell, một môi trường dòng lệnh chạy trên đám mây.

Máy ảo dựa trên Debian này được trang bị tất cả các công cụ phát triển mà bạn cần. Nền tảng này cung cấp một thư mục chính có dung lượng 5 GB và chạy trong Google Cloud, giúp tăng cường đáng kể hiệu suất mạng và hoạt động xác thực. Điều này có nghĩa là bạn chỉ cần một trình duyệt (có, trình duyệt này hoạt động trên Chromebook) cho lớp học lập trình này.

Để kích hoạt Cloud Shell từ Bảng điều khiển Cloud, bạn chỉ cần nhấp vào biểu tượng Kích hoạt Cloud Shell gcLMt5IuEcJJNnMId-Bcz3sxCd0rZn7IzT_r95C8UZeqML68Y1efBG_B0VRp7hc7qiZTLAF-TXD7SsOadxn8uadgHhaLeASnVS3ZHK39eOlKJOgj9SJua_oeGhMxRrbOg3qigddS2A (mất vài phút để cung cấp và kết nối với môi trường).

JjEuRXGg0AYYIY6QZ8d-66gx_Mtc-_jDE9ijmbXLJSAXFvJt-qUpNtsBsYjNpv2W6BQSrDc1D-ARINNQ-1EkwUhz-iUK-FUCZhJ-NtjvIEx9pIkE-246DomWuCfiGHK78DgoeWkHRw

Screen Shot 2017-06-14 at 10.13.43 PM.png

Sau khi kết nối với Cloud Shell, bạn sẽ thấy rằng mình đã được xác thực và dự án đã được đặt thành PROJECT_ID.

gcloud auth list

Đầu ra của lệnh

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

Đầu ra của lệnh

[core]
project = <PROJECT_ID>

Nếu vì lý do nào đó mà dự án chưa được thiết lập, bạn chỉ cần đưa ra lệnh sau:

gcloud config set project <PROJECT_ID>

Bạn đang tìm PROJECT_ID? Kiểm tra mã nhận dạng bạn đã dùng trong các bước thiết lập hoặc tìm mã nhận dạng đó trong trang tổng quan của Cloud Console:

158fNPfwSxsFqz9YbtJVZes8viTS3d1bV4CVhij3XPxuzVFOtTObnwsphlm6lYGmgdMFwBJtc-FaLrZU7XHAg_ZYoCrgombMRR3h-eolLPcvO351c5iBv506B3ZwghZoiRg6cz23Qw

Cloud Shell cũng đặt một số biến môi trường theo mặc định, có thể hữu ích khi bạn chạy các lệnh trong tương lai.

echo $GOOGLE_CLOUD_PROJECT

Đầu ra của lệnh

<PROJECT_ID>

Cuối cùng, hãy đặt cấu hình dự án và vùng mặc định.

gcloud config set compute/zone us-central1-f

Bạn có thể chọn nhiều múi giờ khác nhau. Để biết thêm thông tin, hãy xem phần Khu vực và vùng.

Chuyển đến phần thiết lập ngôn ngữ

Trong lớp học lập trình này, chúng ta sử dụng Go cho tất cả mã nguồn. Chạy lệnh sau trên Cloud Shell và xác nhận xem phiên bản Go có phải là 1.17 trở lên hay không

go version

Đầu ra của lệnh

go version go1.18.3 linux/amd64

Thiết lập một Cụm Kubernetes của Google

Trong lớp học lập trình này, bạn sẽ chạy một cụm vi dịch vụ trên Google Kubernetes Engine (GKE). Quy trình của lớp học lập trình này như sau:

  1. Tải dự án cơ sở xuống Cloud Shell
  2. Tạo các vi dịch vụ vào vùng chứa
  3. Tải vùng chứa lên Google Artifact Registry (GAR)
  4. Triển khai vùng chứa trên GKE
  5. Sửa đổi mã nguồn của các dịch vụ để theo dõi khả năng đo lường
  6. Chuyển đến bước 2

Bật Kubernetes Engine

Trước tiên, chúng ta thiết lập một cụm Kubernetes nơi Shakesapp chạy trên GKE, vì vậy, chúng ta cần bật GKE. Chuyển đến trình đơn "Kubernetes Engine" rồi nhấn nút BẬT.

548cfd95bc6d344d.png

Bây giờ, bạn đã sẵn sàng tạo một cụm Kubernetes.

Tạo cụm Kubernetes

Trên Cloud Shell, hãy chạy lệnh sau để tạo một cụm Kubernetes. Vui lòng xác nhận giá trị của vùng nằm trong khu vực mà bạn sẽ dùng để tạo kho lưu trữ Artifact Registry. Thay đổi giá trị khu vực us-central1-f nếu khu vực kho lưu trữ của bạn không bao gồm khu vực đó.

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

Đầu ra của lệnh

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

Thiết lập Artifact Registry và Skaffold

Giờ đây, chúng ta đã có một cụm Kubernetes sẵn sàng để triển khai. Tiếp theo, chúng ta chuẩn bị cho một sổ đăng ký vùng chứa để đẩy và triển khai vùng chứa. Đối với các bước này, chúng ta cần thiết lập Artifact Registry (GAR) và skaffold để sử dụng.

Thiết lập Artifact Registry

Chuyển đến trình đơn "Artifact Registry" rồi nhấn vào nút BẬT.

45e384b87f7cf0db.png

Sau vài phút, bạn sẽ thấy trình duyệt kho lưu trữ của GAR. Nhấp vào nút "TẠO KHO LƯU TRỮ" rồi nhập tên của kho lưu trữ.

d6a70f4cb4ebcbe3.png

Trong lớp học lập trình này, tôi đặt tên cho kho lưu trữ mới là trace-codelab. Định dạng của cấu phần phần mềm là "Docker" và loại vị trí là "Khu vực". Chọn khu vực gần với khu vực bạn đặt cho vùng mặc định của Google Compute Engine. Ví dụ: ví dụ này chọn "us-central1-f" ở trên, vì vậy ở đây chúng ta chọn "us-central1 (Iowa)". Sau đó, hãy nhấp vào nút "TẠO".

9c2d1ce65258ef70.png

Giờ đây, bạn sẽ thấy "trace-codelab" trên trình duyệt kho lưu trữ.

7a3c1f47346bea15.png

Chúng ta sẽ quay lại đây sau để kiểm tra đường dẫn sổ đăng ký.

Thiết lập Skaffold

Skaffold là một công cụ hữu ích khi bạn làm việc để tạo các dịch vụ vi mô chạy trên Kubernetes. Công cụ này xử lý quy trình tạo, đẩy và triển khai các vùng chứa ứng dụng bằng một nhóm nhỏ các lệnh. Theo mặc định, Skaffold sử dụng Docker Registry làm sổ đăng ký vùng chứa, vì vậy, bạn cần định cấu hình Skaffold để nhận dạng GAR khi đẩy vùng chứa đến.

Mở lại Cloud Shell và xác nhận xem skaffold đã được cài đặt hay chưa. (Theo mặc định, Cloud Shell sẽ cài đặt skaffold vào môi trường.) Chạy lệnh sau và xem phiên bản skaffold.

skaffold version

Đầu ra của lệnh

v1.38.0

Giờ đây, bạn có thể đăng ký kho lưu trữ mặc định để skaffold sử dụng. Để lấy đường dẫn đăng ký, hãy chuyển đến trang tổng quan Artifact Registry rồi nhấp vào tên kho lưu trữ mà bạn vừa thiết lập ở bước trước.

7a3c1f47346bea15.png

Sau đó, bạn sẽ thấy đường dẫn breadcrumb ở đầu trang. Nhấp vào biểu tượng e157b1359c3edc06.png để sao chép đường dẫn đăng ký vào bảng nhớ tạm.

e0f2ae2144880b8b.png

Khi nhấp vào nút sao chép, bạn sẽ thấy hộp thoại ở cuối trình duyệt có thông báo như sau:

Đã sao chép "us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab"

Quay lại Cloud Shell. Chạy lệnh skaffold config set default-repo bằng giá trị mà bạn vừa sao chép từ trang tổng quan.

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

Đầu ra của lệnh

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

Ngoài ra, bạn cần định cấu hình sổ đăng ký cho cấu hình Docker. Chạy lệnh sau:

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

Đầu ra của lệnh

{
  "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

Giờ đây, bạn đã sẵn sàng chuyển sang bước tiếp theo để thiết lập một vùng chứa Kubernetes trên GKE.

Tóm tắt

Ở bước này, bạn sẽ thiết lập môi trường lớp học lập trình:

  • Thiết lập Cloud Shell
  • Đã tạo một kho lưu trữ Artifact Registry cho sổ đăng ký vùng chứa
  • Thiết lập skaffold để sử dụng sổ đăng ký vùng chứa
  • Tạo một cụm Kubernetes nơi các vi dịch vụ của lớp học lập trình chạy

Tiếp theo

Trong bước tiếp theo, bạn sẽ đo lường tác nhân trình phân tích tài nguyên liên tục trong dịch vụ máy chủ.

3. Tạo, chuyển và triển khai các vi dịch vụ

Tải tài liệu của lớp học lập trình xuống

Ở bước trước, chúng ta đã thiết lập tất cả các điều kiện tiên quyết cho lớp học lập trình này. Giờ đây, bạn đã sẵn sàng chạy toàn bộ các vi dịch vụ trên các vùng này. Tài liệu của lớp học lập trình được lưu trữ trên GitHub, vì vậy, hãy tải tài liệu xuống môi trường Cloud Shell bằng lệnh git sau.

cd ~
git clone https://github.com/ymotongpoo/opentelemetry-trace-codelab-go.git
cd opentelemetry-trace-codelab-go

Cấu trúc thư mục của dự án như sau:

.
├── 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: Tệp kê khai Kubernetes
  • proto: định nghĩa proto cho hoạt động giao tiếp giữa ứng dụng và máy chủ
  • src: thư mục cho mã nguồn của từng dịch vụ
  • skaffold.yaml: Tệp cấu hình cho skaffold

Trong lớp học lập trình này, bạn sẽ cập nhật mã nguồn nằm trong thư mục step4. Bạn cũng có thể tham khảo mã nguồn trong các thư mục step[1-6] để biết những thay đổi từ đầu. (Phần 1 bao gồm bước 0 đến bước 4, còn Phần 2 bao gồm bước 5 và 6)

Chạy lệnh skaffold

Cuối cùng, bạn đã sẵn sàng tạo, truyền và triển khai toàn bộ nội dung lên cụm Kubernetes mà bạn vừa tạo. Có vẻ như quy trình này có nhiều bước, nhưng thực tế là Skaffold sẽ làm mọi thứ cho bạn. Hãy thử làm việc đó bằng lệnh sau:

cd step4
skaffold dev

Ngay sau khi chạy lệnh, bạn sẽ thấy thông tin xuất trong nhật ký của docker build và có thể xác nhận rằng các thông tin này đã được chuyển thành công vào sổ đăng ký.

Đầu ra của lệnh

...
---> 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

Sau khi đẩy tất cả các vùng chứa dịch vụ, các hoạt động triển khai Kubernetes sẽ tự động bắt đầu.

Đầu ra của lệnh

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

Sau khi triển khai, bạn sẽ thấy nhật ký ứng dụng thực tế được phát ra stdout trong mỗi vùng chứa như sau:

Đầu ra của lệnh

[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

Xin lưu ý rằng tại thời điểm này, bạn muốn xem mọi thông báo từ máy chủ. Được rồi, cuối cùng bạn đã sẵn sàng bắt đầu đo lường ứng dụng của mình bằng OpenTelemetry để theo dõi phân tán các dịch vụ.

Trước khi bắt đầu đo lường dịch vụ, vui lòng tắt cụm bằng Ctrl-C.

Đầu ra của lệnh

...
[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

Tóm tắt

Ở bước này, bạn đã chuẩn bị tài liệu của lớp học lập trình trong môi trường của mình và xác nhận rằng skaffold chạy như mong đợi.

Tiếp theo

Trong bước tiếp theo, bạn sẽ sửa đổi mã nguồn của dịch vụ loadgen để đo lường thông tin theo dõi.

4. Khả năng đo lường của nhân viên hỗ trợ Cloud Profiler

Khái niệm về lập hồ sơ liên tục

Trước khi giải thích khái niệm về việc lập hồ sơ liên tục, chúng ta cần hiểu rõ khái niệm về việc lập hồ sơ. Lập hồ sơ là một trong những cách để phân tích ứng dụng một cách linh hoạt (phân tích chương trình linh hoạt) và thường được thực hiện trong quá trình phát triển ứng dụng trong quá trình kiểm thử tải, v.v. Đây là một hoạt động đơn lẻ để đo lường các chỉ số hệ thống, chẳng hạn như mức sử dụng CPU và bộ nhớ, trong một khoảng thời gian cụ thể. Sau khi thu thập dữ liệu hồ sơ, nhà phát triển sẽ phân tích dữ liệu đó bên ngoài mã.

Lập hồ sơ liên tục là phương pháp mở rộng của việc lập hồ sơ thông thường: Phương pháp này chạy các hồ sơ cửa sổ ngắn đối với ứng dụng chạy trong thời gian dài theo định kỳ và thu thập một loạt dữ liệu hồ sơ. Sau đó, công cụ này sẽ tự động tạo phân tích thống kê dựa trên một thuộc tính nhất định của ứng dụng, chẳng hạn như số phiên bản, vùng triển khai, thời gian đo lường, v.v. Bạn sẽ tìm thấy thêm thông tin chi tiết về khái niệm này trong tài liệu của chúng tôi.

Vì đích đến là một ứng dụng đang chạy, nên có một cách để định kỳ thu thập dữ liệu hồ sơ và gửi dữ liệu đó đến một số phần phụ trợ xử lý dữ liệu thống kê sau. Đó là tác nhân Cloud Profiler và bạn sẽ sớm nhúng tác nhân này vào dịch vụ máy chủ.

Nhúng tác nhân Cloud Profiler

Mở Cloud Shell Editor bằng cách nhấn vào nút 776a11bfb2122549.png ở trên cùng bên phải của Cloud Shell. Mở step4/src/server/main.go trong trình khám phá ở ngăn bên trái và tìm hàm chính.

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)
        }
}

Trong hàm main, bạn sẽ thấy một số mã thiết lập cho OpenTelemetry và gRPC. Mã này đã được thực hiện trong phần 1 của lớp học lập trình. Bây giờ, bạn sẽ thêm khả năng đo lường cho tác nhân Cloud Profiler tại đây. Giống như những gì chúng ta đã làm cho initTracer(), bạn có thể viết một hàm có tên là initProfiler() để dễ đọc.

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)
        }
}

Hãy xem xét kỹ các lựa chọn được chỉ định trong đối tượng profiler.Config{}.

  • Dịch vụ: Tên dịch vụ mà bạn có thể chọn và bật trên trang tổng quan của trình phân tích tài nguyên
  • ServiceVersion: Tên phiên bản dịch vụ. Bạn có thể so sánh các tập dữ liệu hồ sơ dựa trên giá trị này.
  • NoHeapProfiling: tắt chế độ phân tích mức tiêu thụ bộ nhớ
  • NoAllocProfiling: tắt tính năng phân bổ bộ nhớ
  • NoGoroutineProfiling: tắt tính năng lập hồ sơ goroutine
  • NoCPUProfiling: tắt tính năng lập hồ sơ CPU

Trong lớp học lập trình này, chúng ta chỉ bật tính năng lập hồ sơ CPU.

Bây giờ, bạn chỉ cần gọi hàm này trong hàm main. Nhớ nhập gói Cloud Profiler vào khối nhập.

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
        ...
}

Xin lưu ý rằng bạn đang gọi hàm initProfiler() bằng từ khoá go. Vì profiler.Start() chặn nên bạn cần chạy mã này trong một goroutine khác. Giờ đây, bạn đã sẵn sàng xây dựng. Hãy nhớ chạy go mod tidy trước khi triển khai.

go mod tidy

Bây giờ, hãy triển khai cụm bằng dịch vụ máy chủ mới.

skaffold dev

Thường thì bạn sẽ thấy biểu đồ ngọn lửa trên Cloud Profiler sau vài phút. Nhập "profiler" vào hộp tìm kiếm ở trên cùng rồi nhấp vào biểu tượng của Profiler.

3d8ca8a64b267a40.png

Sau đó, bạn sẽ thấy biểu đồ ngọn lửa sau.

7f80797dddc0128d.png

Tóm tắt

Trong bước này, bạn đã nhúng tác nhân Cloud Profiler vào dịch vụ máy chủ và xác nhận rằng tác nhân này tạo ra biểu đồ ngọn lửa.

Tiếp theo

Trong bước tiếp theo, bạn sẽ tìm hiểu nguyên nhân gây ra tình trạng tắc nghẽn trong ứng dụng bằng biểu đồ hình ngọn lửa.

5. Phân tích biểu đồ hình ngọn lửa của Cloud Profiler

Biểu đồ hình ngọn lửa là gì?

Biểu đồ ngọn lửa là một trong những cách để trực quan hoá dữ liệu hồ sơ. Để biết nội dung giải thích chi tiết, vui lòng tham khảo tài liệu của chúng tôi, nhưng nội dung tóm tắt ngắn gọn là:

  • Mỗi thanh biểu thị lệnh gọi phương thức/hàm trong ứng dụng
  • Hướng dọc là ngăn xếp lệnh gọi; ngăn xếp lệnh gọi tăng từ trên xuống dưới
  • Hướng ngang là mức sử dụng tài nguyên; càng dài thì càng tệ.

Hãy xem biểu đồ hình ngọn lửa thu được.

7f80797dddc0128d.png

Phân tích biểu đồ ngọn lửa

Trong phần trước, bạn đã biết rằng mỗi thanh trong biểu đồ ngọn lửa thể hiện lệnh gọi hàm/lệnh gọi phương thức và độ dài của thanh đó có nghĩa là mức sử dụng tài nguyên trong hàm/phương thức. Biểu đồ ngọn lửa của Cloud Profiler sắp xếp các thanh theo thứ tự giảm dần hoặc độ dài từ trái sang phải, bạn có thể bắt đầu xem ở phía trên cùng bên trái của biểu đồ trước.

6d90760c6c1183cd.png

Trong trường hợp này, rõ ràng là grpc.(*Server).serveStreams.func1.2 đang tiêu tốn phần lớn thời gian CPU và bằng cách xem xét ngăn xếp lệnh gọi từ trên xuống, phần lớn thời gian được sử dụng trong main.(*serverService).GetMatchCount, đây là trình xử lý máy chủ gRPC trong dịch vụ máy chủ.

Trong GetMatchCount, bạn sẽ thấy một loạt hàm regexp: regexp.MatchStringregexp.Compile. Chúng thuộc gói tiêu chuẩn, tức là chúng phải được kiểm thử kỹ lưỡng ở nhiều khía cạnh, bao gồm cả hiệu suất. Nhưng kết quả ở đây cho thấy mức sử dụng tài nguyên thời gian CPU cao trong regexp.MatchStringregexp.Compile. Với những dữ kiện đó, giả định ở đây là việc sử dụng regexp.MatchString có liên quan đến các vấn đề về hiệu suất. Vì vậy, hãy đọc mã nguồn nơi hàm này được dùng.

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
}

Đây là nơi gọi regexp.MatchString. Bằng cách đọc mã nguồn, bạn có thể nhận thấy rằng hàm này được gọi bên trong vòng lặp for lồng nhau. Vì vậy, việc sử dụng hàm này có thể không chính xác. Hãy tìm GoDoc của regexp.

80b8a4ba1931ff7b.png

Theo tài liệu này, regexp.MatchString sẽ biên dịch mẫu biểu thức chính quy trong mọi lệnh gọi. Vì vậy, nguyên nhân dẫn đến mức tiêu thụ tài nguyên lớn có vẻ như sau.

Tóm tắt

Trong bước này, bạn đã giả định nguyên nhân tiêu thụ tài nguyên bằng cách phân tích biểu đồ hình ngọn lửa.

Tiếp theo

Trong bước tiếp theo, bạn sẽ cập nhật mã nguồn của dịch vụ máy chủ và xác nhận thay đổi từ phiên bản 1.0.0.

6. Cập nhật mã nguồn và so sánh các biểu đồ ngọn lửa

Cập nhật mã nguồn

Ở bước trước, bạn đã giả định rằng việc sử dụng regexp.MatchString có liên quan đến mức tiêu thụ tài nguyên lớn. Vậy hãy giải quyết vấn đề này. Mở mã và thay đổi một chút phần đó.

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
}

Như bạn thấy, hiện tại quy trình biên dịch mẫu biểu thức chính quy được trích xuất từ regexp.MatchString và được di chuyển ra khỏi vòng lặp for lồng nhau.

Trước khi triển khai mã này, hãy nhớ cập nhật chuỗi phiên bản trong hàm 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)
        }
}

Bây giờ, hãy xem cách hoạt động của tính năng này. Triển khai cụm bằng lệnh skaffold.

skaffold dev

Sau một lúc, hãy tải lại trang tổng quan Cloud Profiler và xem trang này trông như thế nào.

283cfcd4c13716ad.png

Hãy nhớ thay đổi phiên bản thành "1.1.0" để bạn chỉ thấy các hồ sơ từ phiên bản 1.1.0. Như bạn có thể thấy, độ dài của thanh GetMatchCount đã giảm và tỷ lệ sử dụng thời gian CPU cũng giảm (tức là thanh ngắn hơn).

e3a1456b4aada9a5.png

Không chỉ xem biểu đồ ngọn lửa của một phiên bản, bạn cũng có thể so sánh sự khác biệt giữa hai phiên bản.

841dec77d8ba5595.png

Thay đổi giá trị của danh sách thả xuống "So sánh với" thành "Phiên bản" và thay đổi giá trị của "Phiên bản được so sánh" thành "1.0.0", phiên bản ban đầu.

5553844292d6a537.png

Bạn sẽ thấy biểu đồ ngọn lửa như thế này. Hình dạng của biểu đồ giống như 1.1.0 nhưng màu sắc thì khác. Trong chế độ so sánh, ý nghĩa của màu sắc là:

  • Màu xanh dương: giá trị (mức tiêu thụ tài nguyên) giảm
  • Màu cam: giá trị (mức tiêu thụ tài nguyên) đạt được
  • Xám: trung tính

Dựa vào chú thích, hãy xem xét kỹ hơn về chức năng này. Khi nhấp vào thanh mà bạn muốn phóng to, bạn có thể xem thêm thông tin chi tiết bên trong ngăn xếp. Vui lòng nhấp vào thanh main.(*serverService).GetMatchCount. Ngoài ra, khi di chuột lên cột, bạn sẽ thấy thông tin chi tiết về kết quả so sánh.

ca08d942dc1e2502.png

Theo đó, tổng thời gian CPU giảm từ 5,26 giây xuống 2,88 giây (tổng thời gian là 10 giây = cửa sổ lấy mẫu). Đây là một bước tiến lớn!

Giờ đây, bạn có thể cải thiện hiệu suất ứng dụng thông qua việc phân tích dữ liệu hồ sơ.

Tóm tắt

Trong bước này, bạn đã chỉnh sửa dịch vụ máy chủ và xác nhận sự cải thiện ở chế độ so sánh của Cloud Profiler.

Tiếp theo

Trong bước tiếp theo, bạn sẽ cập nhật mã nguồn của dịch vụ máy chủ và xác nhận thay đổi từ phiên bản 1.0.0.

7. Bước bổ sung: Xác nhận mức độ cải thiện trong thác nước Dấu vết

Sự khác biệt giữa tính năng theo dõi phân tán và tính năng lập hồ sơ liên tục

Trong phần 1 của lớp học lập trình, bạn đã xác nhận rằng bạn có thể tìm ra điểm tắc nghẽn dịch vụ trong các dịch vụ vi mô cho một đường dẫn yêu cầu và bạn không thể tìm ra nguyên nhân chính xác gây tắc nghẽn trong dịch vụ cụ thể. Trong lớp học lập trình phần 2 này, bạn đã tìm hiểu rằng việc lập hồ sơ liên tục cho phép bạn xác định điểm tắc nghẽn bên trong một dịch vụ duy nhất từ các ngăn xếp lệnh gọi.

Trong bước này, hãy xem xét biểu đồ thác nước từ dấu vết phân tán (Cloud Trace) và xem sự khác biệt so với việc lập hồ sơ liên tục.

Biểu đồ thác nước này là một trong những dấu vết có truy vấn "love". Tổng thời gian là khoảng 6,7 giây (6700 mili giây).

e2b7dec25926ee51.png

Đây là sau khi cải thiện cho cùng một cụm từ tìm kiếm. Như bạn thấy, tổng độ trễ hiện là 1,5 giây (1.500 mili giây), đây là một bước cải thiện lớn so với cách triển khai trước đó.

feeb7207f36c7e5e.png

Điểm quan trọng ở đây là trong biểu đồ thác nước dấu vết phân tán, thông tin ngăn xếp lệnh gọi sẽ không có sẵn trừ phi bạn đo lường các khoảng ở mọi nơi. Ngoài ra, các dấu vết phân tán chỉ tập trung vào độ trễ trên các dịch vụ, trong khi hoạt động lập hồ sơ liên tục tập trung vào tài nguyên máy tính (CPU, bộ nhớ, luồng hệ điều hành) của một dịch vụ.

Về một khía cạnh khác, dấu vết phân tán là cơ sở sự kiện, hồ sơ liên tục là thống kê. Mỗi dấu vết có một biểu đồ độ trễ riêng và bạn cần một định dạng khác, chẳng hạn như phân phối để nắm được xu hướng thay đổi độ trễ.

Tóm tắt

Trong bước này, bạn đã kiểm tra sự khác biệt giữa tính năng theo dõi phân tán và tính năng lập hồ sơ liên tục.

8. Xin chúc mừng

Bạn đã tạo thành công các dấu vết phân tán bằng OpenTelemetry và xác nhận độ trễ của yêu cầu trên các vi dịch vụ trên Google Cloud Trace.

Đối với các bài tập mở rộng, bạn có thể tự mình thử các chủ đề sau.

  • Quy trình triển khai hiện tại sẽ gửi tất cả các khoảng thời gian do quy trình kiểm tra tình trạng tạo ra. (grpc.health.v1.Health/Check) Làm cách nào để lọc những khoảng thời gian đó khỏi Cloud Trace? Gợi ý tại đây.
  • Tương quan nhật ký sự kiện với khoảng thời gian và xem cách hoạt động của nhật ký trên Google Cloud Trace và Google Cloud Logging. Gợi ý tại đây.
  • Thay thế một số dịch vụ bằng dịch vụ bằng ngôn ngữ khác và thử đo lường dịch vụ đó bằng OpenTelemetry cho ngôn ngữ đó.

Ngoài ra, nếu bạn muốn tìm hiểu về trình phân tích tài nguyên sau phần này, vui lòng chuyển sang phần 2. Trong trường hợp đó, bạn có thể bỏ qua phần dọn dẹp bên dưới.

Dọn dẹp

Sau khi hoàn thành lớp học lập trình này, vui lòng dừng cụm Kubernetes và nhớ xoá dự án để bạn không bị tính phí ngoài dự kiến trên Google Kubernetes Engine, Google Cloud Trace, Google Artifact Registry.

Trước tiên, hãy xoá cụm ảnh. Nếu đang chạy cụm bằng skaffold dev, bạn chỉ cần nhấn Ctrl-C. Nếu bạn đang chạy cụm bằng skaffold run, hãy chạy lệnh sau:

skaffold delete

Đầu ra của lệnh

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

Sau khi xoá cụm, trên ngăn trình đơn, hãy chọn "IAM & Admin" (IAM và Quản trị) > "Settings" (Cài đặt), rồi nhấp vào nút "SHUT DOWN" (TẮT).

45aa37b7d5e1ddd1.png

Sau đó, hãy nhập Mã dự án (không phải Tên dự án) vào biểu mẫu trong hộp thoại rồi xác nhận tắt.