Go'da uygulamanızda daha iyi performans elde etmeye yönelik araç (2. bölüm: Profiler)

1. Giriş

e0509e8a07ad5537.png

Son Güncelleme: 14.07.2022

Uygulamanın gözlemlenebilirliği

Gözlemlenebilirlik ve Sürekli Profil Aracı

Gözlemlenebilirlik, bir sistemin özelliklerini tanımlamak için kullanılan terimdir. Gözlemlenebilirliğe sahip bir sistem, ekiplerin sistemlerinde etkin bir şekilde hata ayıklamasına olanak tanır. Bu bağlamda, gözlemlenebilirliğin üç ayağı; günlükler, metrikler ve izler, sistemin gözlemlenebilirlik elde etmesi için temel araçlardır.

Gözlemlenebilirliğin üç temel dayanağına ek olarak, sürekli profil çıkarma gözlemlenebilirlik açısından başka bir temel bileşendir ve sektördeki kullanıcı tabanını genişletmektedir. Kaynaklardan biri olan Cloud Profiler, uygulama çağrı yığınlarındaki performans metriklerini ayrıntılı olarak incelemek için kolay bir arayüz sunar.

Bu codelab, serinin 2. bölümüdür ve sürekli profil aracı aracı için enstrümantasyon yöntemini kapsar. 1. bölümde OpenTelemetry ve Cloud Trace ile dağıtılmış izlemeyi ele alacağız. 1. bölümde ise mikro hizmetlerin performans sorunlarını daha iyi tespit etme hakkında daha fazla bilgi edineceksiniz.

Oluşturacaklarınız

Bu codelab'de, "Shakespeare uygulamasının" sunucu hizmetindeki araç sürekli profil oluşturucu aracısını kullanacaksınız. (diğer adıyla Shakesapp) olan bir uygulamadır. Shakesapp'in mimarisi aşağıda açıklandığı gibidir:

44e243182ced442f.png

  • Loadgen, HTTP'de istemciye bir sorgu dizesi gönderir
  • İstemciler sorguyu gRPC'deki yük kaynağından sunucuya iletir
  • Sunucu; istemciden gelen sorguyu kabul eder, tüm Shakespare eserlerini Google Cloud Storage'dan metin biçiminde getirir, sorguyu içeren satırları arar ve istemciyle eşleşen satırın numarasını döndürür.

1. bölümde, performans sorununun sunucu hizmetinin bir yerinde olduğunu tespit ettiniz, ancak nedenini tam olarak belirleyemediniz.

Neler öğreneceksiniz?

  • Profil oluşturucu aracısı yerleştirme
  • Cloud Profiler'da performans sorunlarını inceleme

Bu codelab'de, uygulamanızda sürekli profil aracı aracının nasıl kullanılacağı açıklanmaktadır.

Gerekenler

  • Go hakkında temel düzeyde bilgi
  • Kubernetes hakkında temel bilgiler

2. Kurulum ve Gereksinimler

Kendi hızınızda ortam kurulumu

Google Hesabınız (Gmail veya Google Apps) yoksa bir hesap oluşturmanız gerekir. Google Cloud Platform konsolunda ( console.cloud.google.com) oturum açın ve yeni bir proje oluşturun.

Zaten bir projeniz varsa konsolun sol üst köşesindeki proje seçimi açılan menüsünü tıklayın:

7a32e5469db69e9.png

Sonra ‘YENİ PROJE’yi tıklayın. düğmesini tıklayın:

7136b3ee36ebaf89.png

Henüz projeniz yoksa ilk projenizi oluşturmak için şuna benzer bir iletişim kutusu görmeniz gerekir:

870a3cbd6541ee86.png

Sonraki proje oluşturma iletişim kutusu yeni projenizin ayrıntılarını girmenize olanak tanır:

affdc444517ba805.png

Tüm Google Cloud projeleri için benzersiz bir ad olan proje kimliğini unutmayın (yukarıdaki ad daha önce alınmış ve size uygun olmayacaktır!). Bu kod, bu codelab'in ilerleyen bölümlerinde PROJECT_ID olarak adlandırılacaktır.

Sonraki adımda, henüz yapmadıysanız Developers Console'da faturalandırmayı etkinleştirmeniz ve Google Cloud kaynaklarını kullanmak ve Cloud Trace API'yi etkinleştirmek için yapmanız gerekir.

15d0ef27a8fbab27.png

Bu codelab'i çalıştırmanın maliyeti birkaç dolardan fazla değildir. Ancak daha fazla kaynak kullanmaya karar verirseniz veya bu kaynakları çalışır durumda bırakırsanız daha yüksek ücret ödemeniz gerekebilir (bu belgenin sonundaki "temizlik" bölümüne bakın). Google Cloud Trace, Google Kubernetes Engine ve Google Artifact Registry'nin fiyatları resmi belgelerde belirtilmiştir.

Yeni Google Cloud Platform kullanıcıları, bu codelab'i tamamen ücretsiz hale getirecek 300 ABD doları değerindeki ücretsiz denemeden yararlanabilir.

Google Cloud Shell Kurulumu

Google Cloud ve Google Cloud Trace, dizüstü bilgisayarınızdan uzaktan çalıştırılabilse de bu codelab'de Cloud'da çalışan bir komut satırı ortamı olan Google Cloud Shell'i kullanacağız.

Bu Debian tabanlı sanal makine, ihtiyacınız olan tüm geliştirme araçlarıyla yüklüdür. 5 GB boyutunda kalıcı bir ana dizin sunar ve Google Cloud'da çalışarak ağ performansını ve kimlik doğrulamasını büyük ölçüde iyileştirir. Yani bu codelab'de ihtiyacınız olan tek şey bir tarayıcıdır (Evet, Chromebook'ta çalışır).

Cloud Console'dan Cloud Shell'i etkinleştirmek için Cloud Shell'i Etkinleştir'i gcLMt5IuEcJJNnMId-Bcz3sxCd0rZn7IzT_r95C8UZeqML68Y1efBG_B0VRp7hc7qiZTLAF-TXD7SsOadxn8uadgHhaLeASnVS3ZHK39eOlKJOgj9SJua_oeGhMxRrbOg3qigddS2A tıklamanız yeterlidir (sağlanması ve ortama bağlanması birkaç dakika sürer).

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

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

Cloud Shell'e bağlandıktan sonra kimliğinizin doğrulandığını ve projenin PROJECT_ID olarak ayarlanmış olduğunu göreceksiniz.

gcloud auth list

Komut çıkışı

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

Komut çıkışı

[core]
project = <PROJECT_ID>

Herhangi bir nedenle proje ayarlanmamışsa şu komutu vermeniz yeterlidir:

gcloud config set project <PROJECT_ID>

PROJECT_ID cihazınızı mı arıyorsunuz? Kurulum adımlarında kullandığınız kimliği kontrol edin veya Cloud Console kontrol panelinden arayın:

158fNPfwSxsFqz9YbtJVZes8viTS3d1bV4CVhij3XPxuzVFOtTObnwsphlm6lYGmgdMFwBJtc-FaLrZU7XHAg_ZYoCrgombMRR3h-eolLPcvO351c5iBv506B3ZwghZoiRg6cz23Qw

Cloud Shell bazı ortam değişkenlerini de varsayılan olarak ayarlar. Bu değişkenler, gelecekte komut çalıştırdığınızda işinize yarayabilir.

echo $GOOGLE_CLOUD_PROJECT

Komut çıkışı

<PROJECT_ID>

Son olarak, varsayılan alt bölgeyi ve proje yapılandırmasını ayarlayın.

gcloud config set compute/zone us-central1-f

Çeşitli farklı alt bölgeler seçebilirsiniz. Daha fazla bilgi için Bölgeler ve Bölgeler.

Go dil kurulumu

Bu codelab'de tüm kaynak kodları için Go'yu kullanacağız. Cloud Shell'de aşağıdaki komutu çalıştırın ve Go sürümünün 1.17 veya üzeri bir sürüm olup olmadığını onaylayın.

go version

Komut çıkışı

go version go1.18.3 linux/amd64

Google Kubernetes Kümesi oluşturma

Bu codelab'de, Google Kubernetes Engine'de (GKE) bir mikro hizmet kümesi çalıştıracaksınız. Bu codelab'de süreç aşağıdaki gibidir:

  1. Temel projeyi Cloud Shell'e indirme
  2. Container'larda mikro hizmetler derleme
  3. Kapsayıcıları Google Artifact Registry'ye (GAR) yükleme
  4. Container'ları GKE'ye dağıtma
  5. İzleme araçları için hizmetlerin kaynak kodunu değiştirin
  6. 2. adıma git

Kubernetes Engine'i etkinleştirme

İlk olarak Shakesapp'in GKE'de çalıştığı bir Kubernetes kümesi oluşturmak için GKE'yi etkinleştirmemiz gerekiyor. "Kubernetes Engine" menüsüne gidin ETKİNLEŞTİR düğmesine basın.

548cfd95bc6d344d.png

Artık Kubernetes kümesi oluşturmaya hazırsınız.

Kubernetes kümesi oluşturma

Cloud Shell'de, Kubernetes kümesi oluşturmak için aşağıdaki komutu çalıştırın. Lütfen Artifact Registry deposu oluşturmak için kullanacağınız bölge değerinin altında olduğunu onaylayın. Depo bölgeniz, alt bölgeyi kapsamıyorsa us-central1-f alt bölge değerini değiştirin.

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

Komut çıkışı

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 ve skaffold kurulumu

Artık dağıtıma hazır bir Kubernetes kümemiz var. Şimdi, container'ları push ve dağıtma işlemleri için container kaydı için hazırlayacağız. Bu adımlar için bir Artifact Registry (GAR) oluşturmamız ve bunu kullanmak için kaykay yapmamız gerekir.

Artifact Registry kurulumu

"Artifact Registry" menüsüne gidin ETKİNLEŞTİR düğmesine basın.

45e384b87f7cf0db.png

Bir süre sonra GAR'nin depo tarayıcısını göreceksiniz. "REPOSITORY OLUŞTUR"u tıklayın. düğmesini tıklayın ve deponun adını girin.

d6a70f4cb4ebcbe3.png

Bu codelab'de yeni depoyu trace-codelab olarak adlandırıyorum. Yapının biçimi "Docker"dır ve konum türü "Bölge" ise. Google Compute Engine varsayılan alt bölgesi için belirlediğinize yakın bölgeyi seçin. Örneğin, bu örnek "us-central1-f"yi seçti Burada "us-central1 (Iowa)"yı seçiyoruz. Ardından "OLUŞTUR"u tıklayın. düğmesini tıklayın.

9c2d1ce65258ef70.png

Artık "trace-codelab"i görüyorsunuz. kod deposunda toplanır.

7a3c1f47346bea15.png

Kayıt defteri yolunu kontrol etmek için daha sonra buraya geri döneceğiz.

Skaffold kurulumu

Skaffold, Kubernetes üzerinde çalışan mikro hizmetler oluştururken kullanabileceğiniz pratik bir araçtır. Uygulama container'larını oluşturma, aktarma ve dağıtma iş akışını küçük bir komut grubuyla yönetir. Skaffold, container kayıt defteri olarak varsayılan olarak Docker Registry'yi kullanır. Bu nedenle, container'lara aktarılırken GAR'yi tanımak için skaffold'u yapılandırmanız gerekir.

Cloud Shell'i tekrar açın ve skaffold yüklü olup olmadığını onaylayın. (Cloud Shell'i varsayılan olarak ortama yükler.) Aşağıdaki komutu çalıştırarak skaffold sürümünü görebilirsiniz.

skaffold version

Komut çıkışı

v1.38.0

Şimdi, varsayılan depoyu skaf Fold'un kullanması için kaydedebilirsiniz. Kayıt defteri yolunu almak için Artifact Registry kontrol paneline gidin ve önceki adımda ayarladığınız deponun adını tıklayın.

7a3c1f47346bea15.png

Ardından, sayfanın üst kısmında içerik haritası yollarını görürsünüz. Kayıt defteri yolunu panoya kopyalamak için e157b1359c3edc06.png simgesini tıklayın.

e0f2ae2144880b8b.png

Kopyala düğmesini tıkladığınızda, tarayıcının en altında aşağıdakine benzer bir mesaj içeren bir iletişim kutusu görürsünüz:

&quot;us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab&quot; kopyalandı

Cloud Shell'e geri dönün. Kontrol panelinden kopyaladığınız değerle skaffold config set default-repo komutunu çalıştırın.

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

Komut çıkışı

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

Ayrıca, kayıt defterini Docker yapılandırmasına göre yapılandırmanız gerekir. Aşağıdaki komutu çalıştırın:

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

Komut çıkışı

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

Artık GKE'de Kubernetes container'ı oluşturma adımına geçebilirsiniz.

Özet

Bu adımda codelab ortamınızı kuracaksınız:

  • Cloud Shell'i kurma
  • Container kayıt defteri için Artifact Registry deposu oluşturuldu
  • Container kayıt defterini kullanmak için skaffold'u ayarlama
  • codelab mikro hizmetlerinin çalıştırıldığı bir Kubernetes kümesi oluşturuldu

Sonraki bölüm

Sonraki adımda sunucu hizmetinde sürekli profil aracı aracısını kuracaksınız.

3. Mikro hizmetleri derleme, aktarma ve dağıtma

Codelab materyalini indirin

Önceki adımda bu codelab için tüm ön koşulları ayarladık. Artık tüm mikro hizmetleri bunların üzerinde çalıştırmaya hazırsınız. Codelab materyali GitHub'da barındırıldığından aşağıdaki git komutuyla verileri Cloud Shell ortamına indirin.

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

Projenin dizin yapısı aşağıdaki gibidir:

.
├── 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'ler: Kubernetes manifest dosyaları
  • proto: istemci ve sunucu arasındaki iletişimin proto tanımı
  • src: her hizmetin kaynak kodu için dizinler
  • skaffold.yaml: Skaffold için yapılandırma dosyası

Bu codelab'de, step4 klasörünün altında bulunan kaynak kodunu güncelleyeceksiniz. Baştaki değişiklikler için step[1-6] klasöründeki kaynak koduna da bakabilirsiniz. (1. Bölüm 0. adımdan 4. adıma, 2. Bölüm 5. ve 6. adımları kapsar)

skaffold komutunu çalıştırma

Artık tüm içeriği derlemeye, aktarmaya ve az önce oluşturduğunuz Kubernetes kümesine dağıtmaya hazırsınız. Bu araç birden fazla adım içeriyormuş gibi görünüyor, ancak gerçek kaykay, her şeyi sizin için yapıyor. Bunu aşağıdaki komutla deneyelim:

cd step4
skaffold dev

Komutu çalıştırır çalıştırmaz docker build günlük çıkışını görürsünüz ve bunların kayıt defterine başarıyla aktarıldığını onaylayabilirsiniz.

Komut çıkışı

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

Tüm hizmet container'ları aktarıldıktan sonra Kubernetes dağıtımları otomatik olarak başlar.

Komut çıkışı

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

Dağıtımdan sonra her container'da stdout'a yayınlanan gerçek uygulama günlüklerini aşağıdaki gibi görürsünüz:

Komut çıkışı

[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

Bu aşamada, sunucudan gelen tüm iletileri görmek istediğinizi unutmayın. Tamam, nihayet hizmetlerin dağıtılmış izlemesi için OpenTelemetry ile uygulamanıza enstrümantasyon yapmaya hazırsınız.

Hizmetin enstrümantasyonuna başlamadan önce lütfen Ctrl-C tuşlarına basarak kümenizi kapatın.

Komut çıkışı

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

Özet

Bu adımda, codelab materyalini ortamınızda hazırladınız ve patenin beklendiği gibi çalıştığını onayladınız.

Sonraki bölüm

Sonraki adımda iz bilgilerini kullanmak için loadgen hizmetinin kaynak kodunu değiştireceksiniz.

4. Cloud Profiler aracısının kullanımı

Sürekli profil çıkarma kavramı

Sürekli profil çıkarma kavramını açıklamadan önce, öncelikle profil çıkarma kavramını anlamamız gerekir. Profil oluşturma, uygulamayı dinamik olarak analiz etmenin yollarından biridir (dinamik program analizi) ve genellikle yük testi gibi uygulama geliştirme süreçlerinde gerçekleştirilir. Bu, belirli bir dönemdeki CPU ve bellek kullanımları gibi sistem metriklerini ölçmeye yönelik tek bir çekim etkinliğidir. Geliştiriciler, profil verilerini topladıktan sonra bu verileri kod dışında analiz eder.

Sürekli profil oluşturma, normal profil çıkarma için kullanılan genişletilmiş bir yaklaşımdır: Uzun süredir çalışan uygulamada düzenli olarak kısa pencere profilleri çalıştırır ve bir dizi profil verisi toplar. Ardından, uygulamanın belirli bir özelliğine (ör. sürüm numarası, dağıtım bölgesi, ölçüm zamanı) göre istatistiksel analiz otomatik olarak oluşturulur. Bu kavram hakkında ayrıntılı bilgiyi belgelerimizde bulabilirsiniz.

Hedef çalışan bir uygulama olduğundan, profil verilerini düzenli olarak toplamanın ve istatistiksel verileri sonradan işleyen bir arka uca göndermenin bir yolu vardır. Bu, Cloud Profiler aracısıdır. Bu aracı yakında sunucu hizmetine yerleştireceksiniz.

Cloud Profiler aracısı yerleştirme

Cloud Shell'in sağ üst tarafında bulunan düğmeye 776a11bfb2122549.pngbasarak Cloud Shell Düzenleyici'yi açın. Sol bölmedeki gezginden step4/src/server/main.go uygulamasını açın ve ana işlevi bulun.

step4/src/server/main.go

func main() {
        ...
        // step2. setup OpenTelemetry
        tp, err := initTracer()
        if err != nil {
                log.Fatalf("failed to initialize TracerProvider: %v", err)
        }
        defer func() {
                if err := tp.Shutdown(context.Background()); err != nil {
                        log.Fatalf("error shutting down TracerProvider: %v", err)
                }
        }()
        // step2. end setup

        svc := NewServerService()
        // step2: add interceptor
        interceptorOpt := otelgrpc.WithTracerProvider(otel.GetTracerProvider())
        srv := grpc.NewServer(
                grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor(interceptorOpt)),
                grpc.StreamInterceptor(otelgrpc.StreamServerInterceptor(interceptorOpt)),
        )
        // step2: end adding interceptor
        shakesapp.RegisterShakespeareServiceServer(srv, svc)
        healthpb.RegisterHealthServer(srv, svc)
        if err := srv.Serve(lis); err != nil {
                log.Fatalf("error serving server: %v", err)
        }
}

main işlevinde, codelab'in 1. bölümünde yapılan OpenTelemetry ve gRPC için bazı kurulum kodları görürsünüz. Şimdi, buraya Cloud Profiler aracısı için araçlar ekleyeceksiniz. Tıpkı initTracer() için yaptığımız gibi, okunabilirlik için initProfiler() adlı bir işlev yazabilirsiniz.

step4/src/server/main.go

import (
        ...
        "cloud.google.com/go/profiler" // step5. add profiler package
        "cloud.google.com/go/storage"
        ...
)

// step5: add Profiler initializer
func initProfiler() {
        cfg := profiler.Config{
                Service:              "server",
                ServiceVersion:       "1.0.0",
                NoHeapProfiling:      true,
                NoAllocProfiling:     true,
                NoGoroutineProfiling: true,
                NoCPUProfiling:       false,
        }
        if err := profiler.Start(cfg); err != nil {
                log.Fatalf("failed to launch profiler agent: %v", err)
        }
}

profiler.Config{} nesnesinde belirtilen seçeneklere yakından bakalım.

  • Hizmet: Seçip profil aracı kontrol panelini etkinleştirebileceğiniz hizmet adı
  • ServiceVersion: Hizmet sürümünün adı. Profil veri kümelerini bu değere göre karşılaştırabilirsiniz.
  • NoHeapProfiling: Bellek tüketimi profili oluşturmayı devre dışı bırakın
  • NoAllocProfiling: Bellek ayırma profili oluşturmayı devre dışı bırak
  • NoGoroutineProfiling: Gorutin profili oluşturmayı devre dışı bırakın
  • NoCPUProfiling: CPU profili oluşturmayı devre dışı bırakın

Bu codelab'de yalnızca CPU profili oluşturmayı etkinleştireceğiz.

Şimdi yapmanız gereken, bu işlevi main işlevinde çağırmaktır. Cloud Profiler paketini içe aktarma bloğunda içe aktardığınızdan emin olun.

step4/src/server/main.go

func main() {
        ...
        defer func() {
                if err := tp.Shutdown(context.Background()); err != nil {
                        log.Fatalf("error shutting down TracerProvider: %v", err)
                }
        }()
        // step2. end setup

        // step5. start profiler
        go initProfiler()
        // step5. end

        svc := NewServerService()
        // step2: add interceptor
        ...
}

initProfiler() işlevini go anahtar kelimesiyle çağırdığınızı unutmayın. Çünkü profiler.Start() blokları nedeniyle başka bir gorutte çalıştırmanız gerekiyor. Artık yapı oluşturmaya hazır. go mod tidy uygulamasını dağıtımdan önce çalıştırdığınızdan emin olun.

go mod tidy

Şimdi kümenizi yeni sunucu hizmetinizle dağıtın.

skaffold dev

Cloud Profiler'da flame grafiğini görmek genellikle birkaç dakika sürer. "Profil oluşturucu" yazın yazıp Profil Aracı simgesini tıklayın.

3d8ca8a64b267a40.png

Aşağıdaki flame grafiğini görürsünüz.

7f80797dddc0128d.png

Özet

Bu adımda, sunucu hizmetine Cloud Profiler aracısını yerleştirdiniz ve bu aracın flame grafiği oluşturduğunu onayladınız.

Sonraki bölüm

Bir sonraki adımda, alev grafiğiyle uygulamadaki performans sorununun nedenini araştıracaksınız.

5. Cloud Profiler flame grafiğini analiz etme

Flame Graph nedir?

Flame Graph, profil verilerini görselleştirmenin yollarından biridir. Ayrıntılı açıklama için lütfen dokümanımıza göz atın. Ancak kısa özet:

  • Her çubuk, uygulamadaki yöntemi/işlev çağrısını ifade eder
  • Dikey yön, çağrı yığınıdır. çağrı yığını yukarıdan aşağıya doğru büyür
  • Yatay yön, kaynak kullanımını gösterir. ne kadar uzun olursa o kadar kötü olur.

Bunu göz önünde bulundurarak, elde edilen flame grafiğine bakalım.

7f80797dddc0128d.png

Alev Grafiği Analizi

Önceki bölümde, flame grafiğindeki her çubuğun işlev/yöntem çağrısını ifade ettiğini ve bu çağrının uzunluğunun işlev/yöntemdeki kaynak kullanımını ifade ettiğini öğrendiniz. Cloud Profiler'ın flame grafiği, çubuğu azalan düzende veya uzunluğu soldan sağa doğru sıralar. Önce grafiğin sol üst kısmına bakmaya başlayabilirsiniz.

6d90760c6c1183cd.png

Örneğimizde, en çok CPU zamanının grpc.(*Server).serveStreams.func1.2 olduğu ve çağrı yığınına yukarıdan aşağıya doğru bakıldığında, çoğu zaman sunucu hizmetindeki gRPC sunucu işleyici olan main.(*serverService).GetMatchCount ürününde geçirildiği açıkça görülmektedir.

GetMatchCount altında bir dizi regexp işlevi görürsünüz: regexp.MatchString ve regexp.Compile. Bu ürünler standart paket kapsamındadır. Yani, performans dahil olmak üzere birçok açıdan iyi test edilmelidir. Ancak buradaki sonuç regexp.MatchString ve regexp.Compile için CPU zaman kaynağı kullanımının yüksek olduğunu gösterir. Bu gerçekler ışığında, regexp.MatchString kullanımının performans sorunlarıyla ilgili olduğu varsayılmıştır. Şimdi işlevin kullanıldığı kaynak kodunu okuyalım.

step4/src/server/main.go

func (s *serverService) GetMatchCount(ctx context.Context, req *shakesapp.ShakespeareRequest) (*shakesapp.ShakespeareResponse, error) {
        resp := &shakesapp.ShakespeareResponse{}
        texts, err := readFiles(ctx, bucketName, bucketPrefix)
        if err != nil {
                return resp, fmt.Errorf("fails to read files: %s", err)
        }
        for _, text := range texts {
                for _, line := range strings.Split(text, "\n") {
                        line, query := strings.ToLower(line), strings.ToLower(req.Query)
                        isMatch, err := regexp.MatchString(query, line)
                        if err != nil {
                                return resp, err
                        }
                        if isMatch {
                                resp.MatchCount++
                        }
                }
        }
        return resp, nil
}

regexp.MatchString bu yere çağrılıyor. Kaynak kodunu okuduğunuzda işlevin iç içe yerleştirilmiş "for-loop"un içinde çağrıldığını görebilirsiniz. Bu nedenle bu işlevin kullanımı yanlış olabilir. regexp'in GoDoc bölümünü arayalım.

80b8a4ba1931ff7b.png

Belgeye göre regexp.MatchString, her çağrıda normal ifade kalıbını derler. Yani yüksek kaynak tüketiminin nedeni bu şekildedir.

Özet

Bu adımda flame grafiğini analiz ederek kaynak tüketiminin nedeninin varsayımını yaptınız.

Sonraki bölüm

Sonraki adımda sunucu hizmetinin kaynak kodunu güncelleyecek ve 1.0.0 sürümündeki değişikliği onaylayacaksınız.

6. Kaynak kodu güncelleme ve flame grafiklerini karşılaştırma

Kaynak kodu güncelleme

Önceki adımda, regexp.MatchString kullanımının büyük miktarda kaynak tüketimiyle ilişkili olduğunu varsaydınız. Bu sorunu çözelim. Kodu açın ve o kısmı biraz değiştirin.

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
}

Gördüğünüz gibi, regexp kalıbı derleme işlemi regexp.MatchString öğesinden alınıp iç içe yerleştirilmiş "for" döngüsünden çıkarılır.

Bu kodu dağıtmadan önce initProfiler() işlevindeki sürüm dizesini güncellediğinizden emin olun.

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

Şimdi, bunun nasıl çalıştığına bakalım. Kümeyi skaffold komutuyla dağıtın.

skaffold dev

Bir süre sonra Cloud Profiler kontrol panelini yeniden yükleyerek durumu inceleyin.

283cfcd4c13716ad.png

Yalnızca 1.1.0 sürümündeki profilleri görmek için sürümü "1.1.0" olarak değiştirdiğinizden emin olun. Sizin de bildiğiniz gibi, GetMatchCount çubuğunun uzunluğu ve CPU süresinin kullanım oranı azaldı (yani, çubuk kısaldı).

e3a1456b4aada9a5.png

Sadece tek bir sürümün flame grafiğine bakarak değil, iki sürüm arasındaki farkları da karşılaştırabilirsiniz.

841dec77d8ba5595.png

"Şununla karşılaştır" değerini değiştirme açılır listeden "Sürüm"e ve "Karşılaştırılan sürüm" değerini değiştirebilirsiniz. "1.0.0" olan yeni bir sürüme güncelleyin.

5553844292d6a537.png

Bu tür bir flame grafiği görürsünüz. Grafiğin şekli 1.1.0 ile aynıdır ancak renklendirme farklıdır. Karşılaştırma modunda renk şu anlama gelir:

  • Mavi: Azalan değer (kaynak tüketimi)
  • Turuncu: Kazanılan değer (kaynak tüketimi)
  • Gri: nötr

Açıklamaları göz önünde bulundurarak, işleve daha yakından bakalım. Yakınlaştırmak istediğiniz çubuğu tıklayarak grubun içindeki daha fazla ayrıntıyı görebilirsiniz. Lütfen main.(*serverService).GetMatchCount çubuğu tıklayın. Ayrıca, fareyle çubuğun üzerine geldiğinizde karşılaştırmanın ayrıntılarını görürsünüz.

ca08d942dc1e2502.png

Burada toplam CPU süresinin 5,26 saniyeden 2,88 saniyeye düşürüldüğü yazıyor (toplam 10 sn = örnekleme aralığı). Bu muazzam bir gelişme.

Artık profil verilerinin analizinden uygulamanızın performansını artırabilirsiniz.

Özet

Bu adımda, sunucu hizmetinde bir düzenleme yaptınız ve Cloud Profiler'ın karşılaştırma modundaki iyileştirmeyi onayladınız.

Sonraki bölüm

Sonraki adımda sunucu hizmetinin kaynak kodunu güncelleyecek ve 1.0.0 sürümündeki değişikliği onaylayacaksınız.

7. Ek adım: Trace şelalesinde iyileştirmeyi onaylayın

Dağıtılmış izleme ve sürekli profil oluşturma arasındaki fark

Codelab'in 1. bölümünde, bir istek yolu için mikro hizmetler genelinde performans sorunu hizmetini çözebileceğinizi ve ilgili hizmetteki performans sorununun tam nedenini çözemediğinizi onayladınız. Bu 2. bölümde codelab'de, sürekli profil çıkarmanın tek bir hizmetteki sorunu çağrı yığınlarından tespit etmenize olanak tanıdığını öğrendiniz.

Bu adımda, dağıtılmış izdeki (Cloud Trace) şelale grafiğini inceleyip sürekli profil çıkarma ile arasındaki farkı inceleyelim.

Bu şelale grafiği, "aşk" sorgusunu içeren izlerden biridir. Toplamda yaklaşık 6,7 sn (6.700 ms) sürüyor.

e2b7dec25926ee51.png

Bu adım, aynı sorgu için yapılan iyileştirme sonrasında gelir. Sizin de bildiğiniz gibi toplam gecikme 1, 5 sn (1500 ms) oldu.Bu değer, önceki uygulamaya kıyasla büyük bir iyileşmedir.

feeb7207f36c7e5e.png

Buradaki önemli nokta, araç dünyanın her yerine yayıldığı sürece dağıtılmış iz şelale grafiğinde çağrı yığını bilgilerinin kullanılamamasıdır. Ayrıca, dağıtılmış izler yalnızca hizmetlerdeki gecikmeye odaklanırken, sürekli profil çıkarma işlemi tek bir hizmetin bilgisayar kaynaklarına (CPU, bellek, işletim sistemi iş parçacıkları) odaklanır.

Bir başka açıdan bakıldığında, dağıtılmış iz etkinlik temelidir, sürekli profil ise istatistikseldir. Her iz farklı bir gecikme grafiğine sahiptir ve gecikme değişikliklerinin trendini öğrenmek için dağıtım gibi farklı bir biçime ihtiyacınız vardır.

Özet

Bu adımda, dağıtılmış izleme ve sürekli profil oluşturma arasındaki farkı kontrol ettiniz.

8. Tebrikler

OpenTelemery ile dağıtılmış izleri başarıyla oluşturdunuz ve Google Cloud Trace'te mikro hizmetteki istek gecikmelerini onayladınız.

Genişletilmiş egzersizler için aşağıdaki konuları kendiniz deneyebilirsiniz.

  • Mevcut uygulama, durum denetimi tarafından oluşturulan tüm aralıkları gönderir. (grpc.health.v1.Health/Check) Bu aralıkları Cloud Traces'te nasıl filtrelersiniz? İpucuna buradan ulaşabilirsiniz.
  • Olay günlüklerini aralıklarla ilişkilendirin ve Google Cloud Trace ve Google Cloud Logging'de nasıl çalıştığını öğrenin. İpucuna buradan ulaşabilirsiniz.
  • Bazı hizmetleri başka bir dildeki bir hizmetle değiştirin ve söz konusu dil için OpenTelemetry ile enstrümantasyon yöntemini kullanmayı deneyin.

Ayrıca, bundan sonra profil oluşturucu hakkında daha fazla bilgi edinmek isterseniz lütfen 2. bölüme geçin. Bu durumda, aşağıdaki temizleme bölümünü atlayabilirsiniz.

Temizleme

Bu codelab'den sonra Google Kubernetes Engine, Google Cloud Trace ve Google Artifact Registry'de beklenmedik ücretlerle karşılaşmamak için lütfen Kubernetes kümesini durdurun ve projeyi sildiğinizden emin olun.

İlk olarak kümeyi silin. Kümeyi skaffold dev ile çalıştırıyorsanız Ctrl-C tuşlarına basmanız yeterlidir. Kümeyi skaffold run ile çalıştırıyorsanız aşağıdaki komutu çalıştırın:

skaffold delete

Komut çıkışı

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

Kümeyi sildikten sonra menü bölmesinden "IAM ve Yönetici" > "Ayarlar"ı, ardından "KAPALI"yı tıklayın. düğmesini tıklayın.

45aa37b7d5e1ddd1.png

Ardından iletişim kutusundaki forma Proje Kimliğini (Proje Adı'nı değil) girin ve kapatmayı onaylayın.