1. Wprowadzenie
Ostatnia aktualizacja: 15.07.2022 r.
Dostrzegalność aplikacji
Dostrzegalność i OpenTelemetry
Dostrzegalność to termin opisujący atrybut układu. System z obserwowalnością umożliwia zespołom aktywne debugowanie systemu. W tym kontekście 3 filary dostrzegalności: logi, wskaźniki i logi czasu to podstawowe narzędzia umożliwiające systemowi uzyskiwanie dostrzegalności.
OpenTelemetry to zestaw specyfikacji, bibliotek i agentów, które przyspieszają instrumentację i eksportowanie danych telemetrycznych (logów, wskaźników i śladów), których wymaga dostrzegalność. OpenTelemetry to projekt otwarty standardowo i prowadzony przez społeczność w ramach CNCF. Korzystając z bibliotek dostępnych w projekcie i jego ekosystemie, deweloperzy mogą dostosowywać swoje aplikacje w sposób neutralny od dostawcy i w związku z kilkoma architekturą.
Profilowanie ciągłe jest również kolejnym kluczowym elementem dostrzegalności, które oprócz 3 filarów dostrzegalności zwiększa bazę użytkowników w branży. Cloud Profiler to jeden z pierwszych aplikacji, który zapewnia łatwy w obsłudze interfejs służący do analizy wskaźników wydajności w stosach wywołań aplikacji.
Ćwiczenie w Codelabs jest częścią 1 tej serii i obejmuje instrumentowanie rozproszonych logów czasu w mikroserwisach za pomocą OpenTelemetry i Cloud Trace. W części 2 omówimy profilowanie ciągłe za pomocą Cloud Profiler.
Rozproszony log czasu
Śledzenie wśród logów, wskaźników i logów czasu to dane telemetryczne, które informują o czasie oczekiwania w określonej części procesu w systemie. Szczególnie w erze mikroserwisów rozproszony log czasu jest silnym czynnikiem wpływającym na wykrycie wąskich gardeł czasu oczekiwania w całym systemie rozproszonym.
Przy analizowaniu rozproszonych logów czasu wizualizacja danych logu czasu jest kluczem do szybkiego zrozumienia ogólnych czasów oczekiwania systemu. W rozproszonym logu czasu obsługujemy zbiór wywołań w celu przetworzenia jednego żądania do punktu wejścia systemu w formie logu czasu zawierającego wiele spadów.
Rozpiętość reprezentuje pojedynczą jednostkę pracy wykonanej w systemie rozproszonym, rejestrując czasy rozpoczęcia i zatrzymania. Rozpiętości często są ułożone hierarchicznie między sobą – na poniższym obrazku wszystkie mniejsze rozpiętości to rozpiętości podrzędne dużego rozpiętości /messages i są połączone w jeden ślad, który pokazuje ścieżkę pracy w systemie.
Google Cloud Trace to jedna z opcji dla rozproszonego backendu logu czasu. Jest ona dobrze zintegrowana z innymi usługami Google Cloud.
Co utworzysz
W ramach tego ćwiczenia w Codelabs wykorzystasz dane śledzenia w usługach o nazwie „Aplikacja Szekspira”. (inaczej Shakesapp), która działa w klastrze Google Kubernetes Engine. Architektura Shakesapp:
- Loadgen wysyła ciąg zapytania do klienta w HTTP
- Klienty przekazują zapytanie z polecenia loadgen do serwera w gRPC
- Serwer akceptuje zapytanie od klienta, pobiera z Google Cloud Storage wszystkie dzieła Shakespare w formacie tekstowym, przeszukuje wiersze zawierające zapytanie i zwraca numer wiersza pasującego do klienta.
Dane z logu czasu będziesz modyfikować w żądaniu. Następnie umieścisz na serwerze agenta usługi profiler i zbadasz wąskie gardło.
Czego się nauczysz
- Jak zacząć korzystać z bibliotek śledzenia OpenTelemetry w projekcie Go
- Jak utworzyć span za pomocą biblioteki
- Jak propagować konteksty spanów w obrębie przewodu między komponentami aplikacji
- Jak wysłać dane logu czasu do Cloud Trace
- Jak przeanalizować log czasu w Cloud Trace
Z tego ćwiczenia w Codelabs dowiesz się, jak dostosować działanie mikroserwisów. Ten przykład zawiera tylko 3 komponenty (generator obciążenia, klient i serwer), ale w bardziej złożonych i dużych systemach można zastosować proces opisany w tym ćwiczeniu w Codelabs.
Czego potrzebujesz
- Podstawowa znajomość języka Go
- Podstawowa znajomość technologii Kubernetes
2. Konfiguracja i wymagania
Samodzielne konfigurowanie środowiska
Jeśli nie masz jeszcze konta Google (w Gmailu lub Google Apps), musisz je utworzyć. Zaloguj się w konsoli Google Cloud Platform ( console.cloud.google.com) i utwórz nowy projekt.
Jeśli masz już projekt, kliknij menu wyboru projektu w lewym górnym rogu konsoli:
i kliknij „NOWY PROJEKT”. w wyświetlonym oknie, aby utworzyć nowy projekt:
Jeśli nie masz jeszcze projektu, zobaczysz takie okno dialogowe umożliwiające utworzenie pierwszego:
W kolejnym oknie tworzenia projektu możesz wpisać szczegóły nowego projektu:
Zapamiętaj identyfikator projektu, który jest niepowtarzalną nazwą we wszystkich projektach Google Cloud (powyższa nazwa jest już zajęta i nie będzie Ci odpowiadać). W dalszej części tego ćwiczenia z programowania będzie on określany jako PROJECT_ID.
Następnie musisz włączyć płatności w Developers Console, aby korzystać z zasobów Google Cloud i włączyć Cloud Trace API.
Wykonanie tych ćwiczeń w programie nie powinno kosztować więcej niż kilka dolarów, ale może być droższe, jeśli zdecydujesz się na więcej zasobów lub pozostawisz je włączone (patrz sekcja „Czyszczenie” na końcu tego dokumentu). Ceny Google Cloud Trace, Google Kubernetes Engine i Google Artifact Registry podano w oficjalnej dokumentacji.
- Cennik pakietu operacyjnego Google Cloud | Pakiet operacyjny
- Ceny | Dokumentacja Kubernetes Engine
- Cennik Artifact Registry | Dokumentacja Artifact Registry
Nowi użytkownicy Google Cloud Platform mogą skorzystać z bezpłatnego okresu próbnego w wysokości 300 USD, dzięki czemu te ćwiczenia z programowania są całkowicie bezpłatne.
Konfiguracja Google Cloud Shell
Usługi Google Cloud i Google Cloud Trace można obsługiwać zdalnie z poziomu laptopa, ale w tym ćwiczeniu z programowania wykorzystamy Google Cloud Shell – środowisko wiersza poleceń działające w chmurze.
Ta maszyna wirtualna oparta na Debianie zawiera wszystkie potrzebne narzędzia dla programistów. Zawiera stały katalog domowy o pojemności 5 GB i działa w Google Cloud, co znacznie zwiększa wydajność sieci i uwierzytelnianie. Oznacza to, że do tego ćwiczenia z programowania wystarczy przeglądarka (tak, działa ona na Chromebooku).
Aby aktywować Cloud Shell z poziomu konsoli Cloud, kliknij Aktywuj Cloud Shell (udostępnienie środowiska i połączenie z nim powinno zająć tylko chwilę).
Po nawiązaniu połączenia z Cloud Shell powinno pojawić się potwierdzenie, że użytkownik jest już uwierzytelniony, a projekt jest już ustawiony na PROJECT_ID
.
gcloud auth list
Dane wyjściowe polecenia
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
Dane wyjściowe polecenia
[core] project = <PROJECT_ID>
Jeśli z jakiegoś powodu projekt nie jest skonfigurowany, uruchom po prostu to polecenie:
gcloud config set project <PROJECT_ID>
Szukasz urządzenia PROJECT_ID
? Sprawdź identyfikator użyty w krokach konfiguracji lub wyszukaj go w panelu Cloud Console:
Cloud Shell ustawia też domyślnie niektóre zmienne środowiskowe, które mogą być przydatne podczas uruchamiania kolejnych poleceń.
echo $GOOGLE_CLOUD_PROJECT
Dane wyjściowe polecenia
<PROJECT_ID>
Na koniec ustaw domyślną strefę i konfigurację projektu.
gcloud config set compute/zone us-central1-f
Możesz wybrać różne strefy. Więcej informacji znajdziesz w artykule Regiony i Strefy.
Skonfiguruj język
W tym ćwiczeniu w Codelabs cały kod źródłowy używamy w języku Go. Uruchom to polecenie w Cloud Shell i sprawdź, czy wersja Go to 1.17 lub nowsza
go version
Dane wyjściowe polecenia
go version go1.18.3 linux/amd64
Konfigurowanie klastra Google Kubernetes
W ramach tego ćwiczenia w Codelabs uruchomisz klaster mikroserwisów w Google Kubernetes Engine (GKE). Procedura w ramach ćwiczenia z programowania wygląda tak:
- Pobierz projekt bazowy do Cloud Shell
- Tworzenie mikroserwisów w kontenerach
- Przesyłanie kontenerów do Google Artifact Registry (GAR)
- Wdrażanie kontenerów w GKE
- Modyfikowanie kodu źródłowego usług na potrzeby narzędzi śledzenia
- Przejdź do kroku 2
Włączanie Kubernetes Engine
Najpierw skonfigurowaliśmy klaster Kubernetes, w którym aplikacja Shakesapp działa w GKE, więc musimy włączyć GKE. Przejdź do menu „Kubernetes Engine” i naciśnij przycisk WŁĄCZ.
Teraz możesz utworzyć klaster Kubernetes.
Tworzenie klastra Kubernetes
Aby utworzyć klaster Kubernetes, uruchom w Cloud Shell to polecenie. Potwierdź, że wartość strefy znajduje się w regionie, który będzie używany do tworzenia repozytorium Artifact Registry. Zmień wartość strefy us-central1-f
, jeśli region repozytorium nie obejmuje tej strefy.
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
Dane wyjściowe polecenia
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
Konfiguracja Artifact Registry i skaffold
Klaster Kubernetes jest teraz gotowy do wdrożenia. W kolejnym kroku przygotujemy rejestr kontenerów do wypychania i wdrażania kontenerów. Aby wykonać te czynności, musimy skonfigurować Artifact Registry (GAR) i skaffold, aby z niego korzystać.
Konfiguracja Artifact Registry
Przejdź do menu „Artifact Registry” i naciśnij przycisk WŁĄCZ.
Po chwili zobaczysz przeglądarkę repozytoriów GAR. Kliknij „UTWÓRZ REPOZYTORIUM”. i wpisz nazwę repozytorium.
W ramach tego ćwiczenia w Codelabs nadam nowemu repozytorium nazwę trace-codelab
. Format artefaktu to „Docker” a typ lokalizacji to „Region”. Wybierz region bliski temu, który został ustawiony dla domyślnej strefy Google Compute Engine. W tym przykładzie wybrano „us-central1-f”. powyżej, więc tutaj wybieramy „us-central1 (Iowa)”. Następnie kliknij przycisk „UTWÓRZ”, Przycisk
Teraz widzisz „trace-codelab”. w przeglądarce repozytoriów.
Wrócimy tu później, aby sprawdzić ścieżkę rejestru.
Konfiguracja Skaffold
Skaffold to przydatne narzędzie podczas tworzenia mikroserwisów działających w Kubernetes. Obsługuje przepływ pracy związany z tworzeniem, wypychaniem i wdrażaniem kontenerów aplikacji za pomocą niewielkiego zestawu poleceń. Skaffold domyślnie używa Docker Registry jako rejestru kontenerów, dlatego musisz skonfigurować skaffold tak, aby rozpoznawał GAR przy przekazywaniu kontenerów do kontenerów.
Ponownie otwórz Cloud Shell i sprawdź, czy narzędzie skaffold jest zainstalowane. Cloud Shell domyślnie instaluje skaffold w środowisku. Uruchom następujące polecenie, aby wyświetlić wersję skaffold.
skaffold version
Dane wyjściowe polecenia
v1.38.0
Teraz możesz zarejestrować domyślne repozytorium, którego chcesz używać dla skaffold. Aby uzyskać ścieżkę rejestru, przejdź do panelu Artifact Registry i kliknij nazwę repozytorium utworzonego w poprzednim kroku.
Następnie na górze strony zobaczysz ścieżki menu nawigacyjnego. Kliknij ikonę , aby skopiować ścieżkę rejestru do schowka.
Po kliknięciu przycisku kopiowania na dole okna przeglądarki pojawi się okno z komunikatem takim jak:
"us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab" została skopiowana
Wróć do Cloud Shell. Uruchom polecenie skaffold config set default-repo
z wartością skopiowaną właśnie z panelu.
skaffold config set default-repo us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab
Dane wyjściowe polecenia
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
Musisz też skonfigurować rejestr pod kątem konfiguracji Dockera. Uruchom to polecenie:
gcloud auth configure-docker us-central1-docker.pkg.dev --quiet
Dane wyjściowe polecenia
{ "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
Teraz możesz przejść do następnego kroku konfigurowania kontenera Kubernetes w GKE.
Podsumowanie
W tym kroku skonfigurujesz środowisko ćwiczeń z programowania:
- Konfigurowanie Cloud Shell
- Utworzono repozytorium Artifact Registry dla rejestru kontenerów
- Skonfiguruj skaffold, aby korzystać z rejestru kontenerów
- Utworzono klaster Kubernetes, w którym działają mikroserwisy ćwiczeń z programowania
Dalsze czynności
W następnym kroku skompilujesz, wypchniesz i wdrożysz mikroserwisy w klastrze.
3. Tworzenie, przekazywanie i wdrażanie mikroserwisów
Pobierz materiały do ćwiczeń z programowania
W poprzednim kroku skonfigurowaliśmy wszystkie wymagania wstępne tego ćwiczenia z programowania. Teraz możesz uruchamiać na nich całe mikroserwisy. Materiały do ćwiczeń z programowania są hostowane na GitHubie, więc pobierz je do środowiska Cloud Shell za pomocą tego polecenia git.
cd ~ git clone https://github.com/ymotongpoo/opentelemetry-trace-codelab-go.git cd opentelemetry-trace-codelab-go
Struktura katalogów projektu wygląda tak:
. ├── 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
- pliki manifestu: pliki manifestu Kubernetes.
- proto: definicja protokołu dla komunikacji między klientem a serwerem
- src: katalogi kodu źródłowego poszczególnych usług
- skaffold.yaml: plik konfiguracji dla skaffold
W ramach tego ćwiczenia w Codelabs zaktualizujesz kod źródłowy znajdujący się w folderze step0
. Możesz też skorzystać z kodu źródłowego w folderach step[1-6]
, aby znaleźć odpowiedzi w poniższych krokach. (część 1 dotyczy kroków od kroków 0 do kroków 4, a część 2 dotyczy kroków 5 i 6);
Uruchom polecenie skaffold
Możesz też zacząć kompilować, przenosić i wdrażać całe treści w utworzonym właśnie klastrze Kubernetes. Wygląda na to, że składa się z wielu kroków, ale w rzeczywistości skaffold robi wszystko za Ciebie. Spróbujmy to zrobić za pomocą tego polecenia:
cd step0 skaffold dev
Natychmiast po uruchomieniu polecenia zostaną wyświetlone dane wyjściowe dziennika docker build
i będziesz mieć pewność, że zostały one przekazane do rejestru.
Dane wyjściowe polecenia
... ---> 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
Po przekazaniu wszystkich kontenerów usługi wdrożenia Kubernetes uruchamiają się automatycznie.
Dane wyjściowe polecenia
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
Po wdrożeniu zobaczysz rzeczywiste logi aplikacji wysyłane do stdout w poszczególnych kontenerach w następujący sposób:
Dane wyjściowe polecenia
[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
Na tym etapie chcesz wyświetlić wszystkie wiadomości z serwera. OK, na koniec możesz zacząć instrumentować swoją aplikację za pomocą OpenTelemetry na potrzeby rozproszonego śledzenia usług.
Zanim zaczniesz instrumentować usługę, wyłącz klaster, naciskając klawisze Ctrl + C.
Dane wyjściowe polecenia
... [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
Podsumowanie
Na tym etapie masz już przygotowane materiały z ćwiczeń z programowania w swoim środowisku oraz potwierdzone uruchomienia skaffold zgodnie z oczekiwaniami.
Dalsze czynności
W następnym kroku zmodyfikujesz kod źródłowy usługi loadgen, aby dostosować dane śledzenia.
4. Narzędzia dla HTTP
Koncepcja narzędzi do śledzenia i rozpowszechniania
Przed edycją kodu źródłowego omówię pokrótce, jak działają rozproszone ślady na prostym diagramie.
W tym przykładzie opracowujemy kod do eksportowania informacji logu czasu i spanu do Cloud Trace oraz rozpowszechniania kontekstu logu czasu w żądaniu z usługi Loadgen do usługi serwera.
Aplikacje muszą wysyłać metadane logu czasu, takie jak identyfikator logu czasu i identyfikator spanu, aby usługa Cloud Trace połączyła w jeden log czasu wszystkie spany o tym samym identyfikatorze. Aplikacja musi też rozpowszechniać konteksty logu czasu (kombinację identyfikatora logu czasu i identyfikatora spanu spanu nadrzędnego) w żądaniach kolejnych usług, aby wiedziały, z jakim kontekstem logu czasu są obsługiwane.
OpenTelemetry pomaga:
- do wygenerowania unikalnego identyfikatora logu czasu i identyfikatora spanu
- do wyeksportowania identyfikatora logu czasu i identyfikatora spanu do backendu
- do rozpowszechniania kontekstu logu czasu w innych usługach
- aby osadzić dodatkowe metadane, które pomagają analizować ślady
Komponenty w logu czasu OpenTelemetry
Proces instrumentowania logu czasu aplikacji za pomocą OpenTelemetry wygląda tak:
- Tworzenie eksportera
- Utwórz powiązanie TracerProvider z eksporterem w zakresie 1 i ustaw je jako globalne.
- Ustaw TextMapPropagaror, aby ustawić metodę rozpowszechniania
- Pobieranie elementu śledzącego z obiektu TracerProvider
- Wygeneruj span ze śledzenia
Na razie nie musisz znać szczegółowych właściwości każdego komponentu. Najważniejszą rzeczą, o której musisz pamiętać, jest:
- eksportera w tym miejscu można podłączyć do TracerProvider
- TracerProvider przechowuje całą konfigurację dotyczącą próbkowania i eksportowania logów czasu
- wszystkie ślady są zgrupowane w obiekcie Tracer
Teraz, gdy już wiesz, co to jest, przejdźmy do kodowania.
Instrument – pierwszy span
Usługa generatora obciążenia instrumentu
Otwórz edytor Cloud Shell, naciskając przycisk w prawym górnym rogu Cloud Shell. Otwórz
step0/src/loadgen/main.go
w eksploratorze w panelu po lewej stronie i znajdź funkcję główną.
step0/src/loadgen/main.go
func main() { ... for range t.C { log.Printf("simulating client requests, round %d", i) if err := run(numWorkers, numConcurrency); err != nil { log.Printf("aborted round with error: %v", err) } log.Printf("simulated %d requests", numWorkers) if numRounds != 0 && i > numRounds { break } i++ } }
W funkcji głównej widać pętlę wywołującą w niej funkcję run
. W obecnej implementacji sekcja zawiera 2 wiersze dziennika, które rejestrują początek i koniec wywołania funkcji. Teraz zastosujmy informacje o spanie, aby śledzić opóźnienie wywołania funkcji.
Najpierw, jak wspomnieliśmy w poprzedniej sekcji, skonfigurujmy całe konfiguracje OpenTelemetry. Dodaj pakiety OpenTelemetry w ten sposób:
step0/src/loadgen/main.go
import ( "context" // step1. add packages "encoding/json" "fmt" "io" "log" "math/rand" "net/http" "net/url" "time" // step1. add packages "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/propagation" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.10.0" "go.opentelemetry.io/otel/trace" // step1. end add packages )
Aby zapewnić czytelność, tworzymy funkcję konfiguracji o nazwie initTracer
i wywołujemy ją w funkcji main
.
step0/src/loadgen/main.go
// step1. add OpenTelemetry initialization function func initTracer() (*sdktrace.TracerProvider, error) { // create a stdout exporter to show collected spans out to stdout. exporter, err := stdout.New(stdout.WithPrettyPrint()) if err != nil { return nil, err } // for the demonstration, we use AlwaysSmaple sampler to take all spans. // do not use this option in production. tp := sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithBatcher(exporter), ) otel.SetTracerProvider(tp) otel.SetTextMapPropagator(propagation.TraceContext{}) return tp, nil }
Może się okazać, że procedura konfigurowania OpenTelemetry jest taka sama jak opisana w poprzedniej sekcji. W tej implementacji korzystamy z eksportera stdout
, który eksportuje wszystkie informacje logu czasu do pliku stdout w uporządkowanym formacie.
Następnie wywołasz ją z funkcji głównej. Wywołaj initTracer()
i nie zapomnij wywołać TracerProvider.Shutdown()
po zamknięciu aplikacji.
step0/src/loadgen/main.go
func main() { // step1. setup OpenTelemetry tp, err := initTracer() if err != nil { log.Fatalf("failed to initialize TracerProvider: %v", err) } defer func() { if err := tp.Shutdown(context.Background()); err != nil { log.Fatalf("error shutting down TracerProvider: %v", err) } }() // step1. end setup log.Printf("starting worder with %d workers in %d concurrency", numWorkers, numConcurrency) log.Printf("number of rounds: %d (0 is inifinite)", numRounds) ...
Po zakończeniu konfiguracji musisz utworzyć span z unikalnym identyfikatorem logu czasu i identyfikatorem spanu. OpenTelemetry ma przydatną bibliotekę. Dodaj nowe pakiety do klienta HTTP instrumentu.
step0/src/loadgen/main.go
import ( "context" "encoding/json" "fmt" "io" "log" "math/rand" "net/http" "net/http/httptrace" // step1. add packages "net/url" "time" // step1. add packages "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" // step1. end add packages "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/propagation" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.10.0" "go.opentelemetry.io/otel/trace" )
Ponieważ generator obciążenia wywołuje usługę klienta w formacie HTTP z funkcją net/http
w funkcji runQuery
, używamy pakietu Contrib dla net/http
i włączamy instrumentację z rozszerzeniem httptrace
i otelhttp
.
Najpierw dodaj zmienną globalną pakietu httpClient w celu wywoływania żądań HTTP za pośrednictwem zinstruowanego klienta.
step0/src/loadgen/main.go
var httpClient = http.Client{ Transport: otelhttp.NewTransport(http.DefaultTransport) }
Następnie dodaj instrumentację w funkcji runQuery
, aby utworzyć niestandardowy span za pomocą OpenTelemetry i spanu wygenerowanego automatycznie z niestandardowego klienta HTTP. Wykonaj te czynności:
- Pobierz tracker z globalnego
TracerProvider
dziękiotel.Tracer()
- Utwórz span główny za pomocą metody
Tracer.Start()
- Koniec spanu pierwiastka w dowolnym czasie (w tym przypadku na końcu funkcji
runQuery
)
step0/src/loadgen/main.go
reqURL.RawQuery = v.Encode() // step1. replace http.Get() with custom client call // resp, err := http.Get(reqURL.String()) // step1. instrument trace ctx := context.Background() tr := otel.Tracer("loadgen") ctx, span := tr.Start(ctx, "query.request", trace.WithAttributes( semconv.TelemetrySDKLanguageGo, semconv.ServiceNameKey.String("loadgen.runQuery"), attribute.Key("query").String(s), )) defer span.End() ctx = httptrace.WithClientTrace(ctx, otelhttptrace.NewClientTrace(ctx)) req, err := http.NewRequestWithContext(ctx, "GET", reqURL.String(), nil) if err != nil { return -1, fmt.Errorf("error creating HTTP request object: %v", err) } resp, err := httpClient.Do(req) // step1. end instrumentation if err != nil { return -1, fmt.Errorf("error sending request to %v: %v", reqURL.String(), err) }
To już koniec instrumentacji w loadgen (aplikacja kliencka HTTP). Pamiętaj, aby zaktualizować go.mod
i go.sum
za pomocą polecenia go mod
.
go mod tidy
Dostosowywanie działania usługi klienckiej
W poprzedniej sekcji opracowaliśmy numer części zawartej w czerwonym prostokącie na rysunku poniżej. Informacje o spanach zostały skonfigurowane w usłudze generatora obciążenia. Podobnie jak w przypadku generatora obciążenia, teraz musimy dostosować obsługę klienta. Różnica w stosunku do usługi generatora obciążenia polega na tym, że usługa kliencka musi wyodrębniać informacje o identyfikatorach śledzenia przekazywane z usługi generatora obciążenia w nagłówku HTTP i używać tego identyfikatora do generowania zakresów.
Otwórz edytor Cloud Shell i dodaj wymagane pakiety, tak jak w przypadku usługi generatora obciążenia.
step0/src/client/main.go
import ( "context" "encoding/json" "fmt" "io" "log" "net/http" "net/url" "os" "time" "opentelemetry-trace-codelab-go/client/shakesapp" // step1. add new import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/propagation" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/trace" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" // step1. end new import )
Jeszcze raz musimy skonfigurować OpenTelemtry. Wystarczy skopiować i wkleić funkcję initTracer
z loadgen i wywołać ją także w funkcji main
usługi klienta.
step0/src/client/main.go
// step1. add OpenTelemetry initialization function func initTracer() (*sdktrace.TracerProvider, error) { // create a stdout exporter to show collected spans out to stdout. exporter, err := stdout.New(stdout.WithPrettyPrint()) if err != nil { return nil, err } // for the demonstration, we use AlwaysSmaple sampler to take all spans. // do not use this option in production. tp := sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithBatcher(exporter), ) otel.SetTracerProvider(tp) otel.SetTextMapPropagator(propagation.TraceContext{}) return tp, nil }
Teraz czas na instrumenty spanów. Usługa kliencka musi akceptować żądania HTTP z usługi loadgen, dlatego musi dostosować moduł obsługi. Serwer HTTP w usłudze klienta jest zaimplementowany za pomocą protokołu net/http i możesz używać pakietu otelhttp
w taki sam sposób jak w loadgen.
Najpierw zastępujemy rejestrację modułu obsługi modułem otelhttp
. W funkcji main
odszukaj wiersze, w których moduł obsługi HTTP jest zarejestrowany w usłudze http.HandleFunc()
.
step0/src/client/main.go
// step1. change handler to intercept OpenTelemetry related headers // http.HandleFunc("/", svc.handler) otelHandler := otelhttp.NewHandler(http.HandlerFunc(svc.handler), "client.handler") http.Handle("/", otelHandler) // step1. end intercepter setting http.HandleFunc("/_genki", svc.health)
Następnie instrumentujemy rzeczywisty zakres w module obsługi. Znajdź funkcję obsługi funkcji (*clientService) i dodaj instrumentację spanu za pomocą funkcji trace.SpanFromContext()
.
step0/src/client/main.go
func (cs *clientService) handler(w http.ResponseWriter, r *http.Request) { ... ctx := r.Context() ctx, cancel := context.WithCancel(ctx) defer cancel() // step1. instrument trace span := trace.SpanFromContext(ctx) defer span.End() // step1. end instrument ...
W przypadku tej instrumentacji parametry są podawane od początku do końca metody handler
. Aby ułatwić analizę spanów, dodaj dodatkowy atrybut, który przechowuje w zapytaniu liczbę dopasowanych wartości. Tuż przed wierszem dziennika dodaj następujący kod.
func (cs *clientService) handler(w http.ResponseWriter, r *http.Request) { ... // step1. add span specific attribute span.SetAttributes(attribute.Key("matched").Int64(resp.MatchCount)) // step1. end adding attribute log.Println(string(ret)) ...
W przypadku wszystkich powyższych narzędzi została ukończona instrumentacja śledzenia między skryptem loadgen a klientem. Zobaczmy, jak to działa. Ponownie uruchom kod za pomocą skaffold.
skaffold dev
Po pewnym czasie od uruchomienia usług w klastrze GKE zobaczysz ogromną liczbę komunikatów logu podobnych do tych:
Dane wyjściowe polecenia
[loadgen] { [loadgen] "Name": "query.request", [loadgen] "SpanContext": { [loadgen] "TraceID": "cfa22247a542beeb55a3434392d46b89", [loadgen] "SpanID": "18b06404b10c418b", [loadgen] "TraceFlags": "01", [loadgen] "TraceState": "", [loadgen] "Remote": false [loadgen] }, [loadgen] "Parent": { [loadgen] "TraceID": "00000000000000000000000000000000", [loadgen] "SpanID": "0000000000000000", [loadgen] "TraceFlags": "00", [loadgen] "TraceState": "", [loadgen] "Remote": false [loadgen] }, [loadgen] "SpanKind": 1, [loadgen] "StartTime": "2022-07-14T13:13:36.686751087Z", [loadgen] "EndTime": "2022-07-14T13:14:31.849601964Z", [loadgen] "Attributes": [ [loadgen] { [loadgen] "Key": "telemetry.sdk.language", [loadgen] "Value": { [loadgen] "Type": "STRING", [loadgen] "Value": "go" [loadgen] } [loadgen] }, [loadgen] { [loadgen] "Key": "service.name", [loadgen] "Value": { [loadgen] "Type": "STRING", [loadgen] "Value": "loadgen.runQuery" [loadgen] } [loadgen] }, [loadgen] { [loadgen] "Key": "query", [loadgen] "Value": { [loadgen] "Type": "STRING", [loadgen] "Value": "faith" [loadgen] } [loadgen] } [loadgen] ], [loadgen] "Events": null, [loadgen] "Links": null, [loadgen] "Status": { [loadgen] "Code": "Unset", [loadgen] "Description": "" [loadgen] }, [loadgen] "DroppedAttributes": 0, [loadgen] "DroppedEvents": 0, [loadgen] "DroppedLinks": 0, [loadgen] "ChildSpanCount": 5, [loadgen] "Resource": [ [loadgen] { [loadgen] "Key": "service.name", [loadgen] "Value": { [loadgen] "Type": "STRING", [loadgen] "Value": "unknown_service:loadgen" ...
Eksporter stdout
wysyła te komunikaty. Zauważysz, że elementy nadrzędne wszystkich spanów według parametru loadgen mają wartość TraceID: 00000000000000000000000000000000
, ponieważ jest to span, czyli pierwszy span w śledzeniu. Zauważysz też, że atrybut umieszczania "query"
zawiera ciąg zapytania, który jest przekazywany do usługi klienta.
Podsumowanie
W tym kroku masz zinstrumentowaną usługę generatora obciążenia i usługę klienta, które komunikują się przez HTTP, i potwierdziłeś, że udało Ci się rozpowszechnić kontekst logu czasu między usługami i wyeksportować informacje spanu z obu usług do standardu stdout.
Dalsze czynności
W następnym kroku skonfigurujesz usługę klienta i usługę serwera, aby potwierdzić sposób rozpowszechnienia kontekstu śledzenia za pomocą gRPC.
5. Narzędzia dla gRPC
W poprzednim kroku zdefiniowaliśmy pierwszą połowę żądania w tych mikroserwisach. Na tym etapie próbujemy instrumentować komunikację gRPC między usługą klienta a usługą serwera. (Zielony i fioletowy prostokąt na ilustracji poniżej)
Instrumentacja narzędzia do wstępnej kompilacji dla klienta gRPC
Ekosystem OpenTelemetry zawiera wiele przydatnych bibliotek, które pomagają deweloperom dostosowywać aplikacje. W poprzednim kroku użyliśmy instrumentacji wstępnej kompilacji dla pakietu net/http
. Na tym etapie, gdy próbujemy przekazać kontekst logu czasu za pomocą gRPC, użyjemy do tego biblioteki.
Najpierw zaimportujesz gotowy pakiet gRPC o nazwie otelgrpc
.
step0/src/client/main.go
import ( "context" "encoding/json" "fmt" "io" "log" "net/http" "net/url" "os" "time" "opentelemetry-trace-codelab-go/client/shakesapp" // step2. add prebuilt gRPC package (otelgrpc) "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/propagation" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/trace" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" )
Tym razem usługa klienta jest klientem gRPC względem usługi serwera, więc musisz dopasować do niej klienta gRPC. Znajdź funkcję mustConnGRPC
i dodaj elementy przechwytujące gRPC, które instrumentują nowe spany za każdym razem, gdy klient wysyła żądania do serwera.
step0/src/client/main.go
// Helper function for gRPC connections: Dial and create client once, reuse. func mustConnGRPC(ctx context.Context, conn **grpc.ClientConn, addr string) { var err error // step2. add gRPC interceptor interceptorOpt := otelgrpc.WithTracerProvider(otel.GetTracerProvider()) *conn, err = grpc.DialContext(ctx, addr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor(interceptorOpt)), grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor(interceptorOpt)), grpc.WithTimeout(time.Second*3), ) // step2: end adding interceptor if err != nil { panic(fmt.Sprintf("Error %s grpc: failed to connect %s", err, addr)) } }
Usługa OpenTelemetry została już skonfigurowana w poprzedniej sekcji, więc nie musisz tego robić.
Gotowa instrumentacja dla serwera gRPC
Podobnie jak w przypadku klienta gRPC, nazywamy gotową instrumentację dla serwera gRPC. Dodaj nowy pakiet do sekcji importowania, np.:
step0/src/server/main.go
import ( "context" "fmt" "io/ioutil" "log" "net" "os" "regexp" "strings" "opentelemetry-trace-codelab-go/server/shakesapp" "cloud.google.com/go/storage" // step2. add OpenTelemetry packages including otelgrpc "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" "go.opentelemetry.io/otel" stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" "go.opentelemetry.io/otel/propagation" sdktrace "go.opentelemetry.io/otel/sdk/trace" "google.golang.org/api/iterator" "google.golang.org/api/option" "google.golang.org/grpc" healthpb "google.golang.org/grpc/health/grpc_health_v1" )
Ponieważ jest to pierwszy raz, gdy przeprowadzamy konfigurację serwera, musisz najpierw skonfigurować OpenTelemetry, podobnie jak w przypadku loadgena i usług klienta.
step0/src/server/main.go
// step2. add OpenTelemetry initialization function func initTracer() (*sdktrace.TracerProvider, error) { // create a stdout exporter to show collected spans out to stdout. exporter, err := stdout.New(stdout.WithPrettyPrint()) if err != nil { return nil, err } // for the demonstration, we use AlwaysSmaple sampler to take all spans. // do not use this option in production. tp := sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithBatcher(exporter), ) otel.SetTracerProvider(tp) otel.SetTextMapPropagator(propagation.TraceContext{}) return tp, nil } func main() { ... // step2. setup OpenTelemetry tp, err := initTracer() if err != nil { log.Fatalf("failed to initialize TracerProvider: %v", err) } defer func() { if err := tp.Shutdown(context.Background()); err != nil { log.Fatalf("error shutting down TracerProvider: %v", err) } }() // step2. end setup ...
Następnie musisz dodać moduły przechwytujące serwer. W funkcji main
znajdź miejsce, w którym wywoływana jest funkcja grpc.NewServer()
, i dodaj do niej elementy przechwytujące.
step0/src/server/main.go
func main() { ... svc := NewServerService() // step2: add interceptor interceptorOpt := otelgrpc.WithTracerProvider(otel.GetTracerProvider()) srv := grpc.NewServer( grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor(interceptorOpt)), grpc.StreamInterceptor(otelgrpc.StreamServerInterceptor(interceptorOpt)), ) // step2: end adding interceptor shakesapp.RegisterShakespeareServiceServer(srv, svc) ...
Uruchamianie mikroserwisu i potwierdzenie śledzenia
Następnie uruchom zmodyfikowany kod za pomocą polecenia skaffold.
skaffold dev
Zobaczysz informacje o spanach o stdout.
Dane wyjściowe polecenia
... [server] { [server] "Name": "shakesapp.ShakespeareService/GetMatchCount", [server] "SpanContext": { [server] "TraceID": "89b472f213a400cf975e0a0041649667", [server] "SpanID": "96030dbad0061b3f", [server] "TraceFlags": "01", [server] "TraceState": "", [server] "Remote": false [server] }, [server] "Parent": { [server] "TraceID": "89b472f213a400cf975e0a0041649667", [server] "SpanID": "cd90cc3859b73890", [server] "TraceFlags": "01", [server] "TraceState": "", [server] "Remote": true [server] }, [server] "SpanKind": 2, [server] "StartTime": "2022-07-14T14:05:55.74822525Z", [server] "EndTime": "2022-07-14T14:06:03.449258891Z", [server] "Attributes": [ ... [server] ], [server] "Events": [ [server] { [server] "Name": "message", [server] "Attributes": [ ... [server] ], [server] "DroppedAttributeCount": 0, [server] "Time": "2022-07-14T14:05:55.748235489Z" [server] }, [server] { [server] "Name": "message", [server] "Attributes": [ ... [server] ], [server] "DroppedAttributeCount": 0, [server] "Time": "2022-07-14T14:06:03.449255889Z" [server] } [server] ], [server] "Links": null, [server] "Status": { [server] "Code": "Unset", [server] "Description": "" [server] }, [server] "DroppedAttributes": 0, [server] "DroppedEvents": 0, [server] "DroppedLinks": 0, [server] "ChildSpanCount": 0, [server] "Resource": [ [server] { ... [server] ], [server] "InstrumentationLibrary": { [server] "Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc", [server] "Version": "semver:0.33.0", [server] "SchemaURL": "" [server] } [server] } ...
Zauważasz, że nie zostały osadzone żadne nazwy spanów ani zostały utworzone ręcznie spany ze znacznikami trace.Start()
lub span.SpanFromContext()
. Mimo to otrzymasz dużą liczbę spanów, ponieważ zostały wygenerowane przez moduły przechwytujące gRPC.
Podsumowanie
W tym kroku zinstrumentowaliśmy komunikację opartą na gRPC przy użyciu bibliotek ekosystemu OpenTelemetry.
Dalsze czynności
W następnym kroku na końcu zwizualizujesz log czasu za pomocą Cloud Trace i nauczysz się analizować zebrane spany.
6. Wizualizacja logu czasu za pomocą Cloud Trace
OpenTelemetry pozwala Ci z instrumentować ślady w całym systemie. Wiesz już, jak instrumentować usługi HTTP i gRPC. Chociaż umiesz już je instrumentować, nadal nie wiesz, jak je analizować. W tej sekcji zastąpisz eksportery stdout eksporterami Cloud Trace i dowiesz się, jak analizować logi czasu.
Używanie eksportera Cloud Trace
Jedną z zalet OpenTelemetry jest łatwość ładowania. Aby wizualizować wszystkie spany zebrane przez Twoją instrumentację, wystarczy zastąpić eksporter stdout eksporterem Cloud Trace.
Otwórz pliki main.go
każdej usługi i znajdź funkcję initTracer()
. Usuń wiersz, aby wygenerować eksporter stdout, a zamiast tego utworzyć eksportera Cloud Trace.
step0/src/loadgen/main.go
import ( ... // step3. add OpenTelemetry for Cloud Trace package cloudtrace "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace" ) // step1. add OpenTelemetry initialization function func initTracer() (*sdktrace.TracerProvider, error) { // step3. replace stdout exporter with Cloud Trace exporter // cloudtrace.New() finds the credentials to Cloud Trace automatically following the // rules defined by golang.org/x/oauth2/google.findDefaultCredentailsWithParams. // https://pkg.go.dev/golang.org/x/oauth2/google#FindDefaultCredentialsWithParams exporter, err := cloudtrace.New() // step3. end replacing exporter if err != nil { return nil, err } // for the demonstration, we use AlwaysSmaple sampler to take all spans. // do not use this option in production. tp := sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithBatcher(exporter), ) otel.SetTracerProvider(tp) otel.SetTextMapPropagator(propagation.TraceContext{}) return tp, nil }
Tę samą funkcję musisz zmodyfikować także w usługach klienta i serwera.
Uruchamianie mikroserwisu i potwierdzenie śledzenia
Po wprowadzeniu zmian uruchom klaster w zwykły sposób za pomocą polecenia skaffold.
skaffold dev
Teraz nie widzisz zbyt wielu informacji o spanach w formacie uporządkowanych logów na stdout, ponieważ eksporter został zastąpiony przez Cloud Trace.
Dane wyjściowe polecenia
[loadgen] 2022/07/14 15:01:07 simulated 20 requests [loadgen] 2022/07/14 15:01:07 simulating client requests, round 37 [loadgen] 2022/07/14 15:01:14 query 'sweet': matched 958 [client] 2022/07/14 15:01:14 {"match_count":958} [client] 2022/07/14 15:01:14 {"match_count":3040} [loadgen] 2022/07/14 15:01:14 query 'love': matched 3040 [client] 2022/07/14 15:01:15 {"match_count":349} [loadgen] 2022/07/14 15:01:15 query 'hello': matched 349 [client] 2022/07/14 15:01:15 {"match_count":484} [loadgen] 2022/07/14 15:01:15 query 'faith': matched 484 [loadgen] 2022/07/14 15:01:15 query 'insolence': matched 14 [client] 2022/07/14 15:01:15 {"match_count":14} [client] 2022/07/14 15:01:21 {"match_count":484} [loadgen] 2022/07/14 15:01:21 query 'faith': matched 484 [client] 2022/07/14 15:01:21 {"match_count":728} [loadgen] 2022/07/14 15:01:21 query 'world': matched 728 [client] 2022/07/14 15:01:22 {"match_count":484} [loadgen] 2022/07/14 15:01:22 query 'faith': matched 484 [loadgen] 2022/07/14 15:01:22 query 'hello': matched 349 [client] 2022/07/14 15:01:22 {"match_count":349} [client] 2022/07/14 15:01:23 {"match_count":1036} [loadgen] 2022/07/14 15:01:23 query 'friend': matched 1036 [loadgen] 2022/07/14 15:01:28 query 'tear': matched 463 ...
Teraz sprawdźmy, czy wszystkie spany zostały prawidłowo wysłane do Cloud Trace. Otwórz konsolę Cloud i przejdź do sekcji „Lista logów czasu”. Dostęp z pola wyszukiwania jest łatwy. Możesz też kliknąć menu w panelu po lewej stronie.
Następnie zobaczysz, jak wiele niebieskich punktów jest rozłożonych na wykresie czasu oczekiwania. Każde miejsce to jeden ślad.
Kliknij 1 z nich, aby zobaczyć szczegóły w śladzie.
Nawet ten prosty krótki podgląd pozwala na zebranie wielu przydatnych informacji. Na przykład z wykresu kaskadowego możesz zauważyć, że przyczyną opóźnień jest głównie span o nazwie shakesapp.ShakespeareService/GetMatchCount
. (Patrz: 1 na grafice powyżej). Możesz to potwierdzić w tabeli podsumowania. (Pierwsza kolumna po prawej stronie pokazuje czas trwania każdego rozpiętości). Ten ślad dotyczył również zapytania „znajomy”. (zobacz 2 na grafice powyżej).
Po tych krótkich analizach może się okazać, że potrzebujesz bardziej szczegółowych informacji o spanach wewnątrz metody GetMatchCount
. W porównaniu z informacjami standardowymi wizualizacja jest skuteczna. Więcej informacji o Cloud Trace znajdziesz w naszej oficjalnej dokumentacji.
Podsumowanie
W tym kroku zastąpiliśmy eksporter stdout usługą Cloud Trace 1 i zwizualizowaliśmy logi czasu w Cloud Trace. Wiesz już też, jak zacząć analizować logi czasu.
Dalsze czynności
W następnym kroku zmodyfikujesz kod źródłowy usługi serwera, aby dodać zakres podrzędny w GetMatchCount.
7. Dodaj podspan, aby uzyskać lepszą analizę
W poprzednim kroku zauważyliśmy, że przyczyną czasu przesyłania danych w obie strony zaobserwowanego przez loadgen jest głównie proces wewnątrz metody GetMatchCount (moduł obsługi gRPC) w usłudze serwera. Ponieważ jednak nie korzystamy z żadnych elementów innych niż moduł obsługi, nie możemy znaleźć dalszych statystyk z wykresu kaskadowego. Jest to typowy przypadek, gdy zaczynamy instrumentować mikroserwisy.
W tej sekcji zajmiemy się podspanem, w którym serwer wywołuje Google Cloud Storage. Jest to częsty przypadek, gdy operacje wejścia-wyjścia w zewnętrznej sieci zabierają dużo czasu i ważne jest ustalenie przyczyny wywołania.
Dopasowywanie podspanu na serwerze
Otwórz main.go
na serwerze i znajdź funkcję readFiles
. Ta funkcja wywołuje żądanie do Google Cloud Storage w celu pobrania wszystkich plików tekstowych dzieł Szekspira. W tej funkcji możesz utworzyć zakres podrzędny, podobnie jak w przypadku instrumentacji serwera HTTP w usłudze klienta.
step0/src/server/main.go
func readFiles(ctx context.Context, bucketName, prefix string) ([]string, error) { type resp struct { s string err error } // step4: add an extra span span := trace.SpanFromContext(ctx) span.SetName("server.readFiles") span.SetAttributes(attribute.Key("bucketname").String(bucketName)) defer span.End() // step4: end add span ...
To wszystko, jeśli chodzi o dodanie nowego spanu. Sprawdźmy, jak to działa, jeśli uruchomisz aplikację.
Uruchamianie mikroserwisu i potwierdzenie śledzenia
Po wprowadzeniu zmian uruchom klaster w zwykły sposób za pomocą polecenia skaffold.
skaffold dev
Z listy logów czasu wybierz 1 ślad o nazwie query.request
. Zobaczysz podobny wykres kaskadowy logu czasu z wyjątkiem nowego spanu poniżej shakesapp.ShakespeareService/GetMatchCount
. (Rozpiętość oznaczoną poniżej czerwonym prostokątem)
Na podstawie tego wykresu można teraz wywnioskować, że zewnętrzne wywołanie wysłane do Google Cloud Storage wiąże się z dużym opóźnieniem, ale to też inne czynniki powodują jego większość.
Udało Ci się już zebrać sporo informacji z kilku widoków z wykresu kaskady logu czasu. Jak uzyskujesz w zgłoszeniu dostęp do szczegółowych informacji o skuteczności? Włączył się program profilujący, ale na razie zakończmy ten ćwiczenie z programowania i przekażemy wszystkie samouczki dotyczące narzędzia do profilowania do części 2.
Podsumowanie
W tym kroku zorganizowaliśmy inny zakres w usłudze serwera i uzyskaliśmy dodatkowe informacje na temat czasu oczekiwania w systemie.
8. Gratulacje
Udało Ci się utworzyć rozproszone logi czasu przy użyciu OpenTelemery i potwierdzone opóźnienia żądań w mikroserwisie w Google Cloud Trace.
W przypadku dłuższych ćwiczeń możesz samodzielnie wypróbować poniższe tematy.
- Obecna implementacja wysyła wszystkie spany wygenerowane przez kontrolę stanu. (
grpc.health.v1.Health/Check
) W jaki sposób odfiltrowujesz te spany z Cloud Trace? Wskazówka znajdziesz tutaj. - Skoreluj logi zdarzeń ze spanami i sprawdź, jak działa to w Google Cloud Trace i Google Cloud Logging. Wskazówka znajdziesz tutaj.
- Zastąp jakąś usługę usługą w innym języku i spróbuj dopasować ją za pomocą OpenTelemetry dla tego języka.
Jeśli chcesz dowiedzieć się więcej o narzędziu do profilowania, przejdź do części 2. W takim przypadku możesz pominąć sekcję Czyszczenie znajdującą się poniżej.
Czyszczenie danych
Po ukończeniu tego ćwiczenia w Codelabs zatrzymaj klaster Kubernetes i pamiętaj o usunięciu projektu, aby nie otrzymywać nieoczekiwanych opłat w Google Kubernetes Engine, Google Cloud Trace czy Google Artifact Registry.
Najpierw usuń klaster. Jeśli używasz klastra skaffold dev
, wystarczy nacisnąć Ctrl + C. Jeśli używasz klastra z wersją skaffold run
, uruchom to polecenie:
skaffold delete
Dane wyjściowe polecenia
Cleaning up... - deployment.apps "clientservice" deleted - service "clientservice" deleted - deployment.apps "loadgen" deleted - deployment.apps "serverservice" deleted - service "serverservice" deleted
Po usunięciu klastra w panelu menu wybierz „Uprawnienia Administrator > „Ustawienia”, a następnie kliknij „WYŁĄCZ” Przycisk
Następnie wpisz identyfikator projektu (nie nazwę projektu) w formularzu w oknie i potwierdź zamknięcie.