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

1. Giriş

e0509e8a07ad5537.png

Son güncelleme: 2022-07-14

Uygulamanın gözlemlenebilirliği

Gözlemlenebilirlik ve Sürekli Profil Oluşturucu

Gözlemlenebilirlik, bir sistemin özelliğini tanımlamak için kullanılan terimdir. Gözlemlenebilirlik özelliği olan bir sistem, ekiplerin sistemlerinde aktif olarak hata ayıklamasına olanak tanır. Bu bağlamda, gözlemlenebilirliğin üç temel unsuru olan günlükler, metrikler ve izler, sistemin gözlemlenebilirlik kazanması için temel araçlardır.

Ayrıca, gözlemlenebilirlik için üç temel unsura ek olarak sürekli profilleme de bir diğer önemli bileşendir ve sektörde kullanıcı tabanını genişletmektedir. Cloud Profiler, bu özelliği ilk sunanlardan biridir ve uygulama çağrı yığınlarındaki performans metriklerini ayrıntılı olarak incelemek için kolay bir arayüz sağlar.

Bu codelab, serinin 2. bölümüdür ve sürekli profil oluşturma aracısı oluşturmayı kapsar. 1. bölümde OpenTelemetry ve Cloud Trace ile dağıtılmış izleme ele alınmaktadır. 1. bölümü okuyarak mikro hizmetlerdeki performans sorununu daha iyi tanımlama hakkında daha fazla bilgi edinebilirsiniz.

Ne oluşturacaksınız?

Bu codelab'de, Google Kubernetes Engine kümesinde çalışan "Shakespeare uygulaması"nın (diğer adıyla Shakesapp) sunucu hizmetinde sürekli profiler aracını kullanacaksınız. Shakesapp'in mimarisi aşağıda açıklanmıştır:

44e243182ced442f.png

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

1. bölümde, darboğazın sunucu hizmetinde bir yerde olduğunu ancak tam nedeni belirleyemediğinizi gördünüz.

Neler öğreneceksiniz?

  • Profiler aracısını yerleştirme
  • Cloud Profiler'da darboğazı inceleme

Bu codelab'de, uygulamanızda sürekli profil oluşturma aracısını nasıl kullanacağınız açıklanmaktadır.

Gerekenler

  • Go hakkında temel bilgiler
  • Kubernetes hakkında temel bilgiler

2. Kurulum ve Gereksinimler

Yönlendirmesiz ortam kurulumu

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

Önceden oluşturduğunuz bir projeniz varsa konsolun sol üst kısmındaki proje seçimi açılır menüsünü tıklayın:

7a32e5469db69e9.png

ve yeni bir proje oluşturmak için açılan iletişim kutusunda "YENİ PROJE" düğmesini tıklayın:

7136b3ee36ebaf89.png

Henüz bir projeniz yoksa ilk projenizi oluşturmak için aşağıdaki gibi bir iletişim kutusu görürsünüz:

870a3cbd6541ee86.png

Sonraki proje oluşturma iletişim kutusunda yeni projenizin ayrıntılarını girebilirsiniz:

affdc444517ba805.png

Tüm Google Cloud projelerinde benzersiz bir ad olan proje kimliğini unutmayın (Yukarıdaki ad zaten alınmış olduğundan sizin için çalışmayacaktır). Bu codelab'in ilerleyen bölümlerinde PROJECT_ID olarak adlandırılacaktır.

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

15d0ef27a8fbab27.png

Bu codelab'i tamamlamak size birkaç dolardan fazla maliyet getirmemelidir. Ancak daha fazla kaynak kullanmaya veya kaynakları çalışır durumda bırakmaya karar verirseniz maliyet artabilir (bu belgenin sonundaki "temizleme" bölümüne bakın). Google Cloud Trace, Google Kubernetes Engine ve Google Artifact Registry'nin fiyatlandırması resmi dokümanlarda belirtilmiştir.

Google Cloud Platform'un yeni kullanıcıları, bu codelab'i tamamen ücretsiz hale getirecek 300 ABD doları değerinde ücretsiz deneme sürümünden yararlanabilir.

Google Cloud Shell Kurulumu

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

Bu Debian tabanlı sanal makine, ihtiyaç duyacağınız tüm geliştirme araçlarını içerir. 5 GB boyutunda kalıcı bir ana dizin bulunur ve Google Cloud'da çalışır. Bu sayede ağ performansı ve kimlik doğrulama önemli ölçüde güçlenir. Bu nedenle, bu codelab için ihtiyacınız olan tek şey bir tarayıcıdır (Chromebook'ta da çalışır).

Cloud Shell'i Cloud Console'dan etkinleştirmek için Cloud Shell'i Etkinleştir'i gcLMt5IuEcJJNnMId-Bcz3sxCd0rZn7IzT_r95C8UZeqML68Y1efBG_B0VRp7hc7qiZTLAF-TXD7SsOadxn8uadgHhaLeASnVS3ZHK39eOlKJOgj9SJua_oeGhMxRrbOg3qigddS2A tıklamanız yeterlidir (ortamın sağlanması ve bağlantının kurulması yalnızca birkaç saniye 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'nize ayarlandığını görürsünüz.

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 verin:

gcloud config set project <PROJECT_ID>

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

158fNPfwSxsFqz9YbtJVZes8viTS3d1bV4CVhij3XPxuzVFOtTObnwsphlm6lYGmgdMFwBJtc-FaLrZU7XHAg_ZYoCrgombMRR3h-eolLPcvO351c5iBv506B3ZwghZoiRg6cz23Qw

Cloud Shell, gelecekteki komutları çalıştırırken faydalı olabilecek bazı ortam değişkenlerini de varsayılan olarak ayarlar.

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 bölgeler arasından seçim yapabilirsiniz. Daha fazla bilgi için Bölgeler ve Alt Bölgeler başlıklı makaleyi inceleyin.

Dil kurulumuna gitme

Bu codelab'de tüm kaynak kodları için Go kullanılır. Cloud Shell'de aşağıdaki komutu çalıştırın ve Go sürümünün 1.17 veya daha yeni olduğunu doğrulayın.

go version

Komut çıkışı

go version go1.18.3 linux/amd64

Google Kubernetes kümesi oluşturma

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

  1. Temel proje dosyasını Cloud Shell'e indirin.
  2. Mikro hizmetleri container'lara yerleştirme
  3. Google Artifact Registry'ye (GAR) kapsayıcı yükleme
  4. Container'ları GKE'ye dağıtma
  5. İzleme enstrümantasyonu için hizmetlerin kaynak kodunu değiştirme
  6. 2. adıma geçin.

Kubernetes Engine'i etkinleştirme

Öncelikle, Shakesapp'in GKE'de çalıştığı bir Kubernetes kümesi oluşturuyoruz. Bu nedenle GKE'yi etkinleştirmemiz gerekiyor. "Kubernetes Engine" menüsüne gidin ve 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 bölge değerinin, Artifact Registry deposu oluşturmak için kullanacağınız bölgenin altında olduğunu onaylayın. Depo bölgeniz bölgeyi kapsamıyorsa bölge değerini us-central1-f olarak 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. Ardından, kapsayıcıları aktarmak ve dağıtmak için bir kapsayıcı kayıt defteri hazırlıyoruz. Bu adımlar için bir Artifact Registry (GAR) oluşturmamız ve bunu kullanmak üzere Skaffold'u ayarlamamız gerekir.

Artifact Registry kurulumu

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

45e384b87f7cf0db.png

Bir süre sonra GAR'ın depo tarayıcısını görürsünüz. "CREATE REPOSITORY" (DEPO OLUŞTUR) düğmesini tıklayın ve deponun adını girin.

d6a70f4cb4ebcbe3.png

Bu codelab'de yeni depoya trace-codelab adını veriyorum. Yapının biçimi "Docker", konum türü ise "Bölge" olmalıdır. Google Compute Engine varsayılan bölgesi için ayarladığınız bölgeye yakın bir bölge seçin. Örneğin, yukarıdaki örnekte "us-central1-f" seçildiğinden burada "us-central1 (Iowa)" seçilir. Ardından "OLUŞTUR" düğmesini tıklayın.

9c2d1ce65258ef70.png

Artık "trace-codelab"i depo tarayıcısında görebilirsiniz.

7a3c1f47346bea15.png

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

Skaffold kurulumu

Skaffold, Kubernetes'te çalışan mikro hizmetler oluştururken kullanışlı bir araçtır. Küçük bir komut grubuyla uygulama kapsayıcılarını oluşturma, gönderme ve dağıtma iş akışını yönetir. Skaffold, varsayılan olarak Docker Registry'yi container kayıt defteri olarak kullandığından, container'ları gönderirken GAR'ı tanıyacak şekilde Skaffold'u yapılandırmanız gerekir.

Cloud Shell'i tekrar açın ve skaffold'un yüklü olup olmadığını doğrulayın. (Cloud Shell, skaffold'u ortama varsayılan olarak yükler.) Aşağıdaki komutu çalıştırıp Skaffold sürümünü görün.

skaffold version

Komut çıkışı

v1.38.0

Artık, skaffold'un kullanacağı varsayılan veri deposunu kaydedebilirsiniz. Kayıt yolu 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 alt kısmında aşağıdaki gibi bir mesaj içeren iletişim kutusu görürsünüz:

"us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab" kopyalandı

Cloud Shell'e geri dönün. skaffold config set default-repo komutunu, kontrol panelinden kopyaladığınız değerle birlikte ç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 kapsayıcısı oluşturma işleminin bir sonraki adımına geçebilirsiniz.

Özet

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

  • Cloud Shell'i ayarlama
  • Container Registry için bir Artifact Registry deposu oluşturduysanız
  • Container Registry'yi kullanmak için Skaffold'u ayarlama
  • Codelab mikro hizmetlerinin çalıştığı bir Kubernetes kümesi oluşturduysanız

Sıradaki

Sonraki adımda, sunucu hizmetinde sürekli profiler aracını kullanacaksınız.

3. Mikro hizmetleri oluşturma, 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 Cloud Shell kabuk 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
  • manifests: Kubernetes manifest dosyaları
  • proto: İstemci ile sunucu arasındaki iletişim için proto tanımı
  • src: Her hizmetin kaynak kodunun bulunduğu dizinler
  • skaffold.yaml: Skaffold'un yapılandırma dosyası

Bu codelab'de, step4 klasöründeki kaynak kodu güncelleyeceksiniz. Başlangıçtan itibaren yapılan değişiklikler için step[1-6] klasörlerindeki kaynak koduna da bakabilirsiniz. (1. bölümde 0. adımdan 4. adıma kadar olanlar, 2. bölümde ise 5. ve 6. adımlar ele alınır)

Skaffold komutunu çalıştırma

Son olarak, tüm içeriği oluşturmaya, aktarmaya ve yeni oluşturduğunuz Kubernetes kümesine dağıtmaya hazırsınız. Bu işlem birden fazla adım içeriyormuş gibi görünse de aslında Skaffold her şeyi sizin için yapar. Bunu aşağıdaki komutla deneyelim:

cd step4
skaffold dev

Komutu çalıştırdığınız anda docker build öğesinin günlük çıkışını görür ve bunların kayıt defterine başarıyla gönderildiğini 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 kapsayıcıları gönderildikten 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 kapsayıcıda stdout'a gönderilen gerçek uygulama günlüklerini şu şekilde 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 noktada, sunucudan gelen tüm mesajları görmek istediğinizi unutmayın. Tamam, son olarak hizmetlerin dağıtılmış izlenmesi için uygulamanızı OpenTelemetry ile izlemeye başlamaya hazırsınız.

Hizmeti izlemeye başlamadan önce lütfen Ctrl-C ile 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, ortamınızda codelab materyalini hazırladınız ve skaffold'un beklendiği gibi çalıştığını onayladınız.

Sıradaki

Bir sonraki adımda, izleme bilgilerini ölçmek için loadgen hizmetinin kaynak kodunu değiştireceksiniz.

4. Cloud Profiler aracısının enstrümantasyonu

Sürekli profil oluşturma kavramı

Sürekli profilleme kavramını açıklamadan önce profilleme kavramını anlamamız gerekir. Profillendirme, uygulamayı dinamik olarak analiz etmenin (dinamik program analizi) yollarından biridir ve genellikle yük testi gibi süreçlerde uygulama geliştirme sırasında gerçekleştirilir. Bu, belirli bir dönemde CPU ve bellek kullanımı gibi sistem metriklerini ölçmek için tek seferlik bir etkinliktir. Geliştiriciler, profil verilerini topladıktan sonra kodun dışında analiz eder.

Sürekli profil oluşturma, normal profil oluşturmanın genişletilmiş yaklaşımıdır: Uzun süredir çalışan uygulamaya karşı kısa pencere profilleri oluşturur ve düzenli olarak bir dizi profil verisi toplar. Ardından, uygulamanın sürüm numarası, dağıtım bölgesi, ölçüm zamanı gibi belirli bir özelliğine göre istatistiksel analiz otomatik olarak oluşturulur. Bu kavramla ilgili daha fazla bilgiyi dokümanlarımızda bulabilirsiniz.

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

Cloud Profiler aracısını yerleştirme

Cloud Shell'in sağ üst kısmındaki 776a11bfb2122549.png düğmesine basarak Cloud Shell Düzenleyici'yi açın. Soldaki bölmede bulunan Gezgin'den step4/src/server/main.go simgesini 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, OpenTelemetry ve gRPC için bazı kurulum kodları görürsünüz. Bu kodlar, codelab 1. bölümünde oluşturulmuştur. Şimdi Cloud Profiler aracısı için enstrümantasyon ekleyeceksiniz. 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çenekleri yakından inceleyelim.

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

Bu codelab'de yalnızca CPU profil oluşturmayı etkinleştiriyoruz.

Şimdi yapmanız gereken tek şey bu işlevi main işlevinde çağırmaktır. İçe aktarma bloğuna Cloud Profiler paketini 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. profiler.Start() engellediği için başka bir goroutine'de çalıştırmanız gerekir. Artık oluşturmaya hazırsınız. Dağıtımdan önce go mod tidy komutunu çalıştırdığınızdan emin olun.

go mod tidy

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

skaffold dev

Alev grafiğinin Cloud Profiler'da görünmesi genellikle birkaç dakika sürer. En üstteki arama kutusuna "profiler" yazın ve Profiler simgesini tıklayın.

3d8ca8a64b267a40.png

Ardından aşağıdaki alev grafiğini görürsünüz.

7f80797dddc0128d.png

Özet

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

Sıradaki

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

5. Cloud Profiler flame grafiğini analiz etme

Flame grafiği nedir?

Flame Graph, profil verilerini görselleştirmenin yollarından biridir. Ayrıntılı açıklama için lütfen belgemize bakın. Kısa özet ise şöyledir:

  • Her çubuk, uygulamadaki yöntem/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ıdır. Ne kadar uzun olursa o kadar kötü olur.

Bu nedenle, elde edilen flame grafiğine bakalım.

7f80797dddc0128d.png

Flame grafiğini analiz etme

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

6d90760c6c1183cd.png

Bizim durumumuzda, CPU süresinin büyük bir kısmının grpc.(*Server).serveStreams.func1.2 tarafından kullanıldığı açıkça görülüyor. Çağrı yığınına yukarıdan aşağıya doğru baktığımızda ise sürenin büyük bir kısmının, sunucu hizmetindeki gRPC sunucu işleyicisi olan main.(*serverService).GetMatchCount içinde harcandığı görülüyor.

GetMatchCount altında bir dizi regexp işlevi görürsünüz: regexp.MatchString ve regexp.Compile. Bunlar standart pakette yer alır. Yani performans da dahil olmak üzere birçok açıdan iyi bir şekilde test edilmiş olmalıdır. Ancak buradaki sonuç, regexp.MatchString ve regexp.Compile'de CPU süresi kaynak kullanımının yüksek olduğunu gösteriyor. Bu bilgiler ışığında, regexp.MatchString kullanımının performans sorunlarıyla ilişkili olduğu varsayılır. Şimdi işlevin kullanıldığı kaynak kodu 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
}

Burası, regexp.MatchString adlı yerin bulunduğu yerdir. Kaynak kodu okuduğunuzda, işlevin iç içe yerleştirilmiş for döngüsünde çağrıldığını fark edebilirsiniz. Bu nedenle, bu işlevin kullanımı yanlış olabilir. regexp'in GoDoc'una bakalım.

80b8a4ba1931ff7b.png

Dokümana göre, regexp.MatchString her çağrıda normal ifade modelini derler. Bu nedenle, yüksek kaynak tüketiminin nedeni şu şekilde açıklanabilir.

Özet

Bu adımda, alev grafiğini analiz ederek kaynak tüketiminin nedenini varsaydınız.

Sıradaki

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 kodunu güncelleme ve alev grafiklerini karşılaştırma

Kaynak kodunu güncelleme

Önceki adımda, regexp.MatchString kullanımının yüksek kaynak tüketimiyle ilgili olduğu varsayımı yapıldı. Şimdi bu sorunu çözelim. Kodu açıp ilgili bölümü 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, normal ifade kalıbı derleme süreci artık regexp.MatchString'dan çıkarılıp iç içe yerleştirilmiş for döngüsünün dışına taşındı.

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 de nasıl çalıştığına bakalım. Skaffold komutuyla kümeyi dağıtın.

skaffold dev

Bir süre sonra Cloud Profiler kontrol panelini yeniden yükleyin ve nasıl göründüğüne bakın.

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. Gördüğünüz gibi, GetMatchCount çubuğunun uzunluğu azaldı ve CPU süresinin kullanım oranı (yani çubuk) kısaldı.

e3a1456b4aada9a5.png

Yalnızca tek bir sürümün alev grafiğine bakmakla kalmaz, iki sürüm arasındaki farklılıkları da karşılaştırabilirsiniz.

841dec77d8ba5595.png

"Şununla karşılaştır" açılır listesinin değerini "Sürüm" olarak, "Karşılaştırılan sürüm" değerini ise orijinal sürüm olan "1.0.0" olarak değiştirin.

5553844292d6a537.png

Aşağıdaki gibi bir alev 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 renklerin anlamı:

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

Açıklama göz önüne alındığında, işlevi daha yakından inceleyelim. Yakınlaştırmak istediğiniz çubuğu tıklayarak yığının içindeki daha fazla ayrıntıyı görebilirsiniz. Lütfen main.(*serverService).GetMatchCount çubuğunu tıklayın. Ayrıca çubuğun üzerine gelerek karşılaştırmanın ayrıntılarını da görebilirsiniz.

ca08d942dc1e2502.png

Toplam CPU süresinin 5,26 saniyeden 2,88 saniyeye düştüğü belirtiliyor (toplam 10 saniye = örnekleme penceresi). Bu çok büyük bir gelişme.

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

Özet

Bu adımda, sunucu hizmetinde bir düzenleme yaptınız ve Cloud Profiler'ın karşılaştırma modunda iyileştirme olduğunu onayladınız.

Sıradaki

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: İzleme şelalesindeki 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 arasındaki performans sorunu hizmetini belirleyebildiğinizi ancak belirli hizmetteki performans sorununun tam nedenini belirleyemediğinizi doğruladınız. Bu 2. bölüm codelab'inde, sürekli profil oluşturmanın, tek bir hizmetteki darboğazı çağrı yığınlarından tanımlamanıza olanak tanıdığını öğrendiniz.

Bu adımda, dağıtılmış izlemedeki (Cloud Trace) basamaklı grafiği inceleyelim ve sürekli profil oluşturma ile arasındaki farkı görelim.

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

e2b7dec25926ee51.png

Bu sonuç, aynı sorgu için yapılan iyileştirmeden sonraki sonuçtur. Belirttiğiniz gibi, toplam gecikme süresi artık 1,5 saniye (1.500 ms). Bu, önceki uygulamaya kıyasla büyük bir iyileşme.

feeb7207f36c7e5e.png

Buradaki önemli nokta, dağıtılmış izleme şelale grafiğinde, her yerde aralıklar oluşturmadığınız sürece çağrı yığını bilgilerinin kullanılamamasıdır. Ayrıca dağıtılmış izlemeler yalnızca hizmetler arasındaki gecikmeye odaklanırken sürekli profilleme, tek bir hizmetin bilgisayar kaynaklarına (CPU, bellek, işletim sistemi iş parçacıkları) odaklanır.

Diğer bir açıdan, dağıtılmış izleme etkinlik tabanlıdır, sürekli profil ise istatistikseldir. Her izlemenin farklı bir gecikme grafiği vardır ve gecikme değişikliklerinin trendini görmek için dağıtım gibi farklı bir biçim kullanmanız gerekir.

Özet

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

8. Tebrikler

OpenTelemetry ile dağıtılmış izlemeler oluşturmayı başardınız ve Google Cloud Trace'te mikro hizmet genelindeki istek gecikmelerini onayladınız.

Daha kapsamlı alıştırmalar için aşağıdaki konuları kendi başınıza deneyebilirsiniz.

  • Mevcut uygulama, durum denetimi tarafından oluşturulan tüm kapsamları gönderir. (grpc.health.v1.Health/Check) Bu aralıkları Cloud Trace'lerden nasıl filtreleyebilirsiniz? İpucunu burada bulabilirsiniz.
  • Etkinlik günlüklerini aralıklarla ilişkilendirin ve Google Cloud Trace ile Google Cloud Logging'de nasıl çalıştığını görün. İpucunu burada bulabilirsiniz.
  • Bir hizmeti başka dildeki bir hizmetle değiştirin ve bu dili kullanarak OpenTelemetry ile izlemeye çalışın.

Ayrıca, bu işlemden sonra profiler hakkında 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 lütfen Kubernetes kümesini durdurun ve Google Kubernetes Engine, Google Cloud Trace, Google Artifact Registry'de beklenmedik ücretler almamak için projeyi sildiğinizden emin olun.

Öncelikle kümeyi silin. Küme skaffold dev ile çalışıyorsa 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üme silindikten sonra menü bölmesinde "IAM ve Yönetici" > "Ayarlar"ı seçin ve ardından "KAPAT" düğmesini tıklayın.

45aa37b7d5e1ddd1.png

Ardından, iletişim kutusundaki forma proje kimliğini (proje adını değil) girin ve kapatma işlemini onaylayın.