1. Введение

Последнее обновление: 15.07.2022
Наблюдаемость приложения
Наблюдаемость и OpenTelemetry
Наблюдаемость — это термин, используемый для описания атрибута системы. Система, обладающая наблюдаемостью, позволяет командам активно отлаживать свою систему. В этом контексте три основных компонента наблюдаемости — журналы, метрики и трассировки — являются фундаментальными инструментами для обеспечения наблюдаемости системы.
OpenTelemetry — это набор спецификаций, библиотек и агентов, которые ускоряют сбор и экспорт телеметрических данных (журналов, метрик и трассировок), необходимых для обеспечения наблюдаемости. OpenTelemetry — это открытый стандарт и проект, управляемый сообществом в рамках CNCF. Используя библиотеки, предоставляемые проектом и его экосистемой, разработчики могут внедрять инструменты в свои приложения независимым от поставщика способом и для различных архитектур.
Помимо трех основных составляющих — наблюдаемости, — непрерывный профилирование является еще одним ключевым компонентом , расширяющим базу пользователей в отрасли. Cloud Profiler — один из первопроходцев в этой области, предоставляющий простой интерфейс для детального анализа показателей производительности в стеках вызовов приложений.
Данный практический урок является первой частью серии и посвящен инструментированию распределенной трассировки в микросервисах с помощью OpenTelemetry и Cloud Trace. Во второй части будет рассмотрено непрерывное профилирование с помощью Cloud Profiler.
Распределенная трассировка
Среди логов, метрик и трассировок, трассировка — это телеметрия, которая показывает задержку конкретной части процесса в системе. Особенно в эпоху микросервисов распределенная трассировка является мощным инструментом для выявления узких мест задержки в распределенной системе в целом.
При анализе распределенных трассировок визуализация данных трассировки является ключом к быстрому пониманию общих задержек системы. В распределенной трассировке мы обрабатываем набор вызовов для выполнения одного запроса к точке входа в систему в виде трассировки, содержащей несколько сегментов (Span).
Спан представляет собой отдельную единицу работы, выполняемой в распределенной системе, с указанием времени начала и окончания. Спаны часто имеют иерархические связи друг с другом — на рисунке ниже все меньшие спаны являются дочерними спанами большого спана /messages и объединены в один трассировочный файл (Trace), показывающий путь работы в системе.

Google Cloud Trace — один из вариантов распределенной системы трассировки, хорошо интегрированный с другими продуктами Google Cloud.
Что вы построите
В этом практическом задании вы будете отслеживать информацию о трассировке в сервисе под названием "приложение Шекспира" (или Shakesapp), работающем в кластере Google Kubernetes Engine. Архитектура Shakesapp описана ниже:

- Loadgen отправляет клиенту строку запроса по протоколу HTTP.
- Клиенты передают запрос от генератора нагрузки на сервер по протоколу gRPC.
- Сервер принимает запрос от клиента, извлекает все произведения Шекспира в текстовом формате из Google Cloud Storage, ищет строки, содержащие запрос, и возвращает клиенту номер строки, которая соответствует запросу.
Вы будете собирать информацию о трассировке по всему запросу. После этого вы внедрите агент профилирования на сервер и исследуете узкое место.
Что вы узнаете
- Как начать работу с библиотеками OpenTelemetry Trace в проекте Go
- Как создать элемент `<span>` с помощью библиотеки
- Как передавать контексты Span по сети между компонентами приложения
- Как отправить данные трассировки в Cloud Trace
- Как анализировать трассировку в Cloud Trace
В этом практическом занятии объясняется, как инструментировать ваши микросервисы. Для простоты понимания этот пример содержит всего 3 компонента (генератор нагрузки, клиент и сервер), но вы можете применить тот же процесс, описанный в этом занятии, к более сложным и крупным системам.
Что вам понадобится
- Базовые знания игры Го.
- Базовые знания Kubernetes.
2. Настройка и требования
Настройка среды для самостоятельного обучения
Если у вас еще нет учетной записи Google (Gmail или Google Apps), вам необходимо ее создать . Войдите в консоль Google Cloud Platform ( console.cloud.google.com ) и создайте новый проект.
Если у вас уже есть проект, щелкните раскрывающееся меню выбора проекта в левом верхнем углу консоли:

и нажмите кнопку «СОЗДАТЬ ПРОЕКТ» в появившемся диалоговом окне, чтобы создать новый проект:

Если у вас ещё нет проекта, вы увидите диалоговое окно, подобное этому, для создания вашего первого проекта:

В появившемся диалоговом окне создания проекта вы можете ввести подробные сведения о вашем новом проекте:

Запомните идентификатор проекта (Project ID), который является уникальным именем для всех проектов Google Cloud (указанное выше имя уже занято и вам не подойдёт, извините!). В дальнейшем в этом практическом занятии он будет обозначаться как PROJECT_ID.
Далее, если вы еще этого не сделали, вам необходимо включить оплату в консоли разработчика, чтобы использовать ресурсы Google Cloud и активировать API Cloud Trace .

Выполнение этого практического задания не должно обойтись вам дороже нескольких долларов, но может обойтись дороже, если вы решите использовать больше ресурсов или оставите их запущенными (см. раздел «очистка» в конце этого документа). Цены на Google Cloud Trace, Google Kubernetes Engine и Google Artifact Registry указаны в официальной документации.
- Цены на пакет инструментов для управления операциями Google Cloud | Operations Suite
- Цены | Документация Kubernetes Engine
- Цены на реестр артефактов | Документация по реестру артефактов
Новые пользователи Google Cloud Platform могут воспользоваться бесплатной пробной версией стоимостью 300 долларов , что сделает этот практический семинар совершенно бесплатным.
Настройка Google Cloud Shell
Хотя Google Cloud и Google Cloud Trace можно запускать удаленно с ноутбука, в этом практическом занятии мы будем использовать Google Cloud Shell — среду командной строки, работающую в облаке.
Эта виртуальная машина на базе Debian содержит все необходимые инструменты разработки. Она предоставляет постоянный домашний каталог размером 5 ГБ и работает в облаке Google, что значительно повышает производительность сети и аутентификацию. Это означает, что для выполнения этого практического задания вам понадобится только браузер (да, он работает и на Chromebook).
Для активации Cloud Shell из консоли Cloud Console просто нажмите кнопку «Активировать Cloud Shell».
(Подготовка и подключение к среде займут всего несколько минут).


После подключения к Cloud Shell вы увидите, что ваша аутентификация пройдена и проект уже настроен на ваш PROJECT_ID .
gcloud auth list
вывод команды
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
вывод команды
[core] project = <PROJECT_ID>
Если по какой-либо причине проект не создан, просто выполните следующую команду:
gcloud config set project <PROJECT_ID>
Ищете свой PROJECT_ID ? Проверьте, какой ID вы использовали на этапах настройки, или найдите его на панели управления Cloud Console:

Cloud Shell также по умолчанию устанавливает некоторые переменные среды, которые могут быть полезны при выполнении будущих команд.
echo $GOOGLE_CLOUD_PROJECT
вывод команды
<PROJECT_ID>
Наконец, установите зону по умолчанию и конфигурацию проекта.
gcloud config set compute/zone us-central1-f
Вы можете выбрать различные зоны. Для получения дополнительной информации см. раздел «Регионы и зоны» .
Настройка языка Go
В этом практическом занятии мы используем Go для всего исходного кода. Выполните следующую команду в Cloud Shell и убедитесь, что версия Go — 1.17 или выше.
go version
вывод команды
go version go1.18.3 linux/amd64
Настройка кластера Google Kubernetes
В этом практическом занятии вы запустите кластер микросервисов на платформе Google Kubernetes Engine (GKE). Процесс выполнения этого практического занятия выглядит следующим образом:
- Загрузите базовый проект в Cloud Shell.
- Внедряйте микросервисы в контейнеры.
- Загрузите контейнеры в реестр артефактов Google (GAR).
- Развертывание контейнеров в GKE
- Измените исходный код сервисов для трассировочного мониторинга.
- Перейдите к шагу 2
Включить Kubernetes Engine
Сначала настроим кластер Kubernetes, в котором Shakesapp будет работать на GKE, поэтому нам нужно включить GKE. Перейдите в меню "Kubernetes Engine" и нажмите кнопку "Включить".

Теперь вы готовы создать кластер Kubernetes.
Создание кластера Kubernetes
В Cloud Shell выполните следующую команду для создания кластера Kubernetes. Убедитесь, что значение зоны находится в регионе , который вы будете использовать для создания репозитория Artifact Registry. Измените значение зоны us-central1-f , если ваш регион репозитория не охватывает эту зону.
gcloud container clusters create otel-trace-codelab2 \ --zone us-central1-f \ --release-channel rapid \ --preemptible \ --enable-autoscaling \ --max-nodes 8 \ --no-enable-ip-alias \ --scopes cloud-platform
вывод команды
Note: Your Pod address range (`--cluster-ipv4-cidr`) can accommodate at most 1008 node(s). Creating cluster otel-trace-codelab2 in us-central1-f... Cluster is being health-checked (master is healthy)...done. Created [https://container.googleapis.com/v1/projects/development-215403/zones/us-central1-f/clusters/otel-trace-codelab2]. To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/us-central1-f/otel-trace-codelab2?project=development-215403 kubeconfig entry generated for otel-trace-codelab2. NAME: otel-trace-codelab2 LOCATION: us-central1-f MASTER_VERSION: 1.23.6-gke.1501 MASTER_IP: 104.154.76.89 MACHINE_TYPE: e2-medium NODE_VERSION: 1.23.6-gke.1501 NUM_NODES: 3 STATUS: RUNNING
Настройка реестра артефактов и Skaffold.
Теперь у нас есть кластер Kubernetes, готовый к развертыванию. Далее мы подготовим реестр контейнеров для отправки и развертывания контейнеров. Для этих шагов нам необходимо настроить реестр артефактов (GAR) и Skaffold для его использования.
Настройка реестра артефактов
Перейдите в меню «Реестр артефактов» и нажмите кнопку «Включить».

Через несколько мгновений вы увидите браузер репозиториев GAR. Нажмите кнопку "СОЗДАТЬ РЕПОЗИТОРИЙ" и введите имя репозитория.

В этом практическом занятии я называю новый репозиторий trace-codelab . Формат артефакта — "Docker", а тип местоположения — "Регион". Выберите регион, близкий к тому, который вы установили для зоны по умолчанию Google Compute Engine. Например, в этом примере был выбран "us-central1-f", поэтому здесь мы выберем "us-central1 (Айова)". Затем нажмите кнопку "СОЗДАТЬ".

Теперь в браузере репозитория вы видите "trace-codelab".

Мы вернемся сюда позже, чтобы проверить путь в реестре.
установка строительных лесов
Skaffold — удобный инструмент при работе над созданием микросервисов, работающих на Kubernetes. Он управляет процессом сборки, отправки и развертывания контейнеров приложений с помощью небольшого набора команд. По умолчанию Skaffold использует Docker Registry в качестве реестра контейнеров, поэтому вам необходимо настроить Skaffold для распознавания GAR при отправке контейнеров.
Откройте Cloud Shell еще раз и убедитесь, что skaffold установлен. (Cloud Shell устанавливает skaffold в среду по умолчанию.) Выполните следующую команду, чтобы увидеть версию skaffold.
skaffold version
вывод команды
v1.38.0
Теперь вы можете зарегистрировать репозиторий по умолчанию для использования Skaffold. Чтобы получить путь к реестру, перейдите на панель управления Реестр артефактов и щелкните имя репозитория, который вы только что настроили на предыдущем шаге.

Затем вы увидите дорожки с хлебными крошками в верхней части страницы. Нажмите на них.
значок для копирования пути к файлу реестра в буфер обмена.

При нажатии на кнопку копирования внизу браузера появится диалоговое окно с сообщением следующего вида:
"us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab" был скопирован.
Вернитесь в облачную оболочку. Выполните команду ` skaffold config set default-repo , указав значение, которое вы только что скопировали с панели управления.
skaffold config set default-repo us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab
вывод команды
set value default-repo to us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab for context gke_stackdriver-sandbox-3438851889_us-central1-b_stackdriver-sandbox
Кроме того, необходимо настроить реестр для работы с Docker. Выполните следующую команду:
gcloud auth configure-docker us-central1-docker.pkg.dev --quiet
вывод команды
{
"credHelpers": {
"gcr.io": "gcloud",
"us.gcr.io": "gcloud",
"eu.gcr.io": "gcloud",
"asia.gcr.io": "gcloud",
"staging-k8s.gcr.io": "gcloud",
"marketplace.gcr.io": "gcloud",
"us-central1-docker.pkg.dev": "gcloud"
}
}
Adding credentials for: us-central1-docker.pkg.dev
Теперь вы можете перейти к следующему шагу — настройке контейнера Kubernetes в GKE.
Краткое содержание
На этом этапе вы настраиваете среду для работы с кодовой лабораторией:
- Настройка Cloud Shell
- Создан репозиторий Artifact Registry для реестра контейнеров.
- Настройте Skaffold для использования реестра контейнеров.
- Создан кластер Kubernetes, в котором работают микросервисы из Codelab.
Далее
На следующем этапе вы создадите, отправите и развернете свои микросервисы в кластере.
3. Создайте, разверните и создайте микросервисы.
Скачайте материалы для практического занятия.
На предыдущем шаге мы подготовили все необходимые условия для этого практического занятия. Теперь вы готовы запустить на их основе целые микросервисы. Материалы для практического занятия размещены на GitHub, поэтому загрузите их в среду Cloud Shell с помощью следующей команды git.
cd ~ git clone https://github.com/ymotongpoo/opentelemetry-trace-codelab-go.git cd opentelemetry-trace-codelab-go
Структура каталогов проекта выглядит следующим образом:
.
├── README.md
├── step0
│ ├── manifests
│ ├── proto
│ ├── skaffold.yaml
│ └── src
├── step1
│ ├── manifests
│ ├── proto
│ ├── skaffold.yaml
│ └── src
├── step2
│ ├── manifests
│ ├── proto
│ ├── skaffold.yaml
│ └── src
├── step3
│ ├── manifests
│ ├── proto
│ ├── skaffold.yaml
│ └── src
├── step4
│ ├── manifests
│ ├── proto
│ ├── skaffold.yaml
│ └── src
├── step5
│ ├── manifests
│ ├── proto
│ ├── skaffold.yaml
│ └── src
└── step6
├── manifests
├── proto
├── skaffold.yaml
└── src
- манифесты: файлы манифестов Kubernetes
- proto: определение протокола для обмена данными между клиентом и сервером.
- src: каталоги с исходным кодом каждого сервиса
- skaffold.yaml: Конфигурационный файл для skaffold
В этом практическом задании вам нужно будет обновить исходный код, расположенный в папке step0 . Вы также можете обратиться к исходному коду в папках step[1-6] для получения ответов на следующие шаги. (Часть 1 охватывает шаги с step0 по step4, а Часть 2 — шаги 5 и 6)
Выполните команду skaffold
Наконец, вы готовы собрать, отправить и развернуть весь контент в только что созданном кластере Kubernetes. Кажется, что это включает в себя несколько шагов, но на самом деле Skaffold делает всё за вас. Давайте попробуем это сделать с помощью следующей команды:
cd step0 skaffold dev
Сразу после выполнения команды вы увидите лог-вывод команды docker build и сможете убедиться, что файлы успешно загружены в реестр.
вывод команды
... ---> Running in c39b3ea8692b ---> 90932a583ab6 Successfully built 90932a583ab6 Successfully tagged us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/serverservice:step1 The push refers to repository [us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/serverservice] cc8f5a05df4a: Preparing 5bf719419ee2: Preparing 2901929ad341: Preparing 88d9943798ba: Preparing b0fdf826a39a: Preparing 3c9c1e0b1647: Preparing f3427ce9393d: Preparing 14a1ca976738: Preparing f3427ce9393d: Waiting 14a1ca976738: Waiting 3c9c1e0b1647: Waiting b0fdf826a39a: Layer already exists 88d9943798ba: Layer already exists f3427ce9393d: Layer already exists 3c9c1e0b1647: Layer already exists 14a1ca976738: Layer already exists 2901929ad341: Pushed 5bf719419ee2: Pushed cc8f5a05df4a: Pushed step1: digest: sha256:8acdbe3a453001f120fb22c11c4f6d64c2451347732f4f271d746c2e4d193bbe size: 2001
После отправки всех контейнеров сервисов развертывание Kubernetes запускается автоматически.
вывод команды
sha256:b71fce0a96cea08075dc20758ae561cf78c83ff656b04d211ffa00cedb77edf8 size: 1997 Tags used in deployment: - serverservice -> us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/serverservice:step4@sha256:8acdbe3a453001f120fb22c11c4f6d64c2451347732f4f271d746c2e4d193bbe - clientservice -> us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/clientservice:step4@sha256:b71fce0a96cea08075dc20758ae561cf78c83ff656b04d211ffa00cedb77edf8 - loadgen -> us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/loadgen:step4@sha256:eea2e5bc8463ecf886f958a86906cab896e9e2e380a0eb143deaeaca40f7888a Starting deploy... - deployment.apps/clientservice created - service/clientservice created - deployment.apps/loadgen created - deployment.apps/serverservice created - service/serverservice created
После развертывания вы увидите фактические логи приложения, выводимые в стандартный поток вывода каждого контейнера, примерно такого вида:
вывод команды
[client] 2022/07/14 06:33:15 {"match_count":3040}
[loadgen] 2022/07/14 06:33:15 query 'love': matched 3040
[client] 2022/07/14 06:33:15 {"match_count":3040}
[loadgen] 2022/07/14 06:33:15 query 'love': matched 3040
[client] 2022/07/14 06:33:16 {"match_count":3040}
[loadgen] 2022/07/14 06:33:16 query 'love': matched 3040
[client] 2022/07/14 06:33:19 {"match_count":463}
[loadgen] 2022/07/14 06:33:19 query 'tear': matched 463
[loadgen] 2022/07/14 06:33:20 query 'world': matched 728
[client] 2022/07/14 06:33:20 {"match_count":728}
[client] 2022/07/14 06:33:22 {"match_count":463}
[loadgen] 2022/07/14 06:33:22 query 'tear': matched 463
Обратите внимание, что на данном этапе вам необходимо видеть все сообщения с сервера. Итак, наконец, вы готовы начать инструментирование вашего приложения с помощью OpenTelemetry для распределенной трассировки сервисов.
Перед началом мониторинга сервиса, пожалуйста, выключите кластер с помощью Ctrl-C.
вывод команды
...
[client] 2022/07/14 06:34:57 {"match_count":1}
[loadgen] 2022/07/14 06:34:57 query 'what's past is prologue': matched 1
^CCleaning up...
- W0714 06:34:58.464305 28078 gcp.go:120] WARNING: the gcp auth plugin is deprecated in v1.22+, unavailable in v1.25+; use gcloud instead.
- To learn more, consult https://cloud.google.com/blog/products/containers-kubernetes/kubectl-auth-changes-in-gke
- deployment.apps "clientservice" deleted
- service "clientservice" deleted
- deployment.apps "loadgen" deleted
- deployment.apps "serverservice" deleted
- service "serverservice" deleted
Краткое содержание
На этом этапе вы подготовили материалы для лабораторной работы в своей среде и подтвердили, что Skaffold работает должным образом.
Далее
На следующем шаге вы измените исходный код службы loadgen, чтобы добавить инструменты для сбора информации трассировки.
4. Инструментарий для HTTP
Концепция трассировочной аппаратуры и распространения
Прежде чем редактировать исходный код, позвольте мне кратко объяснить принцип работы распределенных трассировок на простой схеме.

В этом примере мы добавляем в код инструменты для экспорта информации о трассировке и сегментации в Cloud Trace и распространения контекста трассировки по запросу от службы генерации нагрузки к серверной службе.
Приложениям необходимо отправлять метаданные трассировки, такие как идентификатор трассировки (Trace ID) и идентификатор сегмента (Span ID), чтобы Cloud Trace мог объединить все сегменты с одинаковым идентификатором трассировки в одну трассировку. Кроме того, приложению необходимо передавать контексты трассировки (комбинацию идентификатора трассировки и идентификатора сегмента родительского сегмента) при запросе к нижестоящим сервисам, чтобы те могли знать, какой контекст трассировки они обрабатывают.
OpenTelemetry поможет вам:
- для генерации уникальных идентификаторов трассировки (Trace ID) и диапазона (Span ID)
- экспортировать идентификаторы трассировки (Trace ID) и идентификаторы трассировки (Span ID) в бэкэнд.
- для распространения контекстов трассировки на другие сервисы
- для внедрения дополнительных метаданных, которые помогают анализировать трассировки
Компоненты в OpenTelemetry Trace

Процесс добавления инструментов трассировки приложений в OpenTelemetry выглядит следующим образом:
- Создать экспортер
- Создайте объект TracerProvider, привязывающий экспортер в пункте 1, и установите его глобальным.
- Установите параметр TextMapPropagaror, чтобы задать метод распространения.
- Получите трассировщик от поставщика трассировки (TracerProvider).
- Сгенерировать диапазон из трассировщика
На данный момент вам не нужно разбираться в детальных свойствах каждого компонента, но самое важное, что следует помнить:
- Здесь экспортер подключается к TracerProvider.
- TracerProvider содержит все настройки, касающиеся сбора и экспорта данных трассировки.
- Все трассировки объединены в объект Tracer.
Разобравшись в этом, перейдём к собственно работе над кодом.
Инструментальный первый пролет
Обслуживание генераторов с измерительной нагрузкой
Откройте редактор Cloud Shell, нажав кнопку.
В правом верхнем углу Cloud Shell откройте step0/src/loadgen/main.go в проводнике в левой панели и найдите функцию main.
step0/src/loadgen/main.go
func main() {
...
for range t.C {
log.Printf("simulating client requests, round %d", i)
if err := run(numWorkers, numConcurrency); err != nil {
log.Printf("aborted round with error: %v", err)
}
log.Printf("simulated %d requests", numWorkers)
if numRounds != 0 && i > numRounds {
break
}
i++
}
}
В основной функции вы видите цикл, вызывающий функцию, которая в ней run . В текущей реализации этот раздел содержит две строки лога, которые записывают начало и конец вызова функции. Теперь давайте добавим информацию о Span для отслеживания задержки вызова функции.
Во-первых, как отмечалось в предыдущем разделе, давайте настроим все параметры OpenTelemetry. Добавьте пакеты OpenTelemetry следующим образом:
step0/src/loadgen/main.go
import (
"context" // step1. add packages
"encoding/json"
"fmt"
"io"
"log"
"math/rand"
"net/http"
"net/url"
"time"
// step1. add packages
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/propagation"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
"go.opentelemetry.io/otel/trace"
// step1. end add packages
)
Для повышения читаемости кода мы создаём функцию инициализации под названием initTracer и вызываем её в main функции.
step0/src/loadgen/main.go
// step1. add OpenTelemetry initialization function
func initTracer() (*sdktrace.TracerProvider, error) {
// create a stdout exporter to show collected spans out to stdout.
exporter, err := stdout.New(stdout.WithPrettyPrint())
if err != nil {
return nil, err
}
// for the demonstration, we use AlwaysSmaple sampler to take all spans.
// do not use this option in production.
tp := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithBatcher(exporter),
)
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.TraceContext{})
return tp, nil
}
Как вы, возможно, заметили, процедура настройки OpenTelemetry описана в предыдущем разделе. В данной реализации мы используем экспортер stdout , который выводит всю информацию трассировки в stdout в структурированном формате.
Затем вызовите его из главной функции. Вызовите initTracer() и обязательно вызовите TracerProvider.Shutdown() при закрытии приложения.
step0/src/loadgen/main.go
func main() {
// step1. setup OpenTelemetry
tp, err := initTracer()
if err != nil {
log.Fatalf("failed to initialize TracerProvider: %v", err)
}
defer func() {
if err := tp.Shutdown(context.Background()); err != nil {
log.Fatalf("error shutting down TracerProvider: %v", err)
}
}()
// step1. end setup
log.Printf("starting worder with %d workers in %d concurrency", numWorkers, numConcurrency)
log.Printf("number of rounds: %d (0 is inifinite)", numRounds)
...
После завершения настройки необходимо создать Span с уникальными идентификаторами трассировки (Trace ID) и Span ID. OpenTelemetry предоставляет для этого удобную библиотеку. Добавьте дополнительные новые пакеты в HTTP-клиент инструмента.
step0/src/loadgen/main.go
import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"math/rand"
"net/http"
"net/http/httptrace" // step1. add packages
"net/url"
"time"
// step1. add packages
"go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
// step1. end add packages
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/propagation"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
"go.opentelemetry.io/otel/trace"
)
Поскольку генератор нагрузки вызывает клиентскую службу по протоколу HTTP с использованием net/http в функции runQuery , мы используем пакет contrib для net/http и включаем инструментирование с помощью расширения пакетов httptrace и otelhttp .
Сначала добавим глобальную переменную пакета httpClient для вызова HTTP-запросов через инструментированный клиент.
step0/src/loadgen/main.go
var httpClient = http.Client{
Transport: otelhttp.NewTransport(http.DefaultTransport)
}
Далее добавьте в функцию runQuery инструментарий для создания пользовательского диапазона с использованием OpenTelemetry и автоматически сгенерированного диапазона из пользовательского HTTP-клиента. Вам нужно будет сделать следующее:
- Получите трассировщик из глобального
TracerProviderс помощьюotel.Tracer() - Создайте корневой сегмент с помощью метода
Tracer.Start() - Завершите корневой сегмент в произвольное время (в данном случае, в конце функции
runQuery).
step0/src/loadgen/main.go
reqURL.RawQuery = v.Encode()
// step1. replace http.Get() with custom client call
// resp, err := http.Get(reqURL.String())
// step1. instrument trace
ctx := context.Background()
tr := otel.Tracer("loadgen")
ctx, span := tr.Start(ctx, "query.request", trace.WithAttributes(
semconv.TelemetrySDKLanguageGo,
semconv.ServiceNameKey.String("loadgen.runQuery"),
attribute.Key("query").String(s),
))
defer span.End()
ctx = httptrace.WithClientTrace(ctx, otelhttptrace.NewClientTrace(ctx))
req, err := http.NewRequestWithContext(ctx, "GET", reqURL.String(), nil)
if err != nil {
return -1, fmt.Errorf("error creating HTTP request object: %v", err)
}
resp, err := httpClient.Do(req)
// step1. end instrumentation
if err != nil {
return -1, fmt.Errorf("error sending request to %v: %v", reqURL.String(), err)
}
Теперь вы завершили настройку параметров в loadgen (клиентское HTTP-приложение). Пожалуйста, обновите файлы go.mod и go.sum с помощью команды go mod .
go mod tidy
Обслуживание клиентов Instrument
В предыдущем разделе мы настроили инструментарий для части, заключенной в красный прямоугольник на рисунке ниже. Мы настроили инструментарий для информации о трассировке в службе генератора нагрузки. Аналогично службе генератора нагрузки, теперь нам необходимо настроить инструментарий для клиентской службы. Отличие от службы генератора нагрузки заключается в том, что клиентская служба должна извлекать информацию об идентификаторе трассировки (Trace ID), передаваемую из службы генератора нагрузки в заголовке HTTP, и использовать этот идентификатор для генерации трассировок.

Откройте редактор Cloud Shell и добавьте необходимые пакеты, как мы это сделали для службы генератора нагрузки.
step0/src/client/main.go
import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/url"
"os"
"time"
"opentelemetry-trace-codelab-go/client/shakesapp"
// step1. add new import
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/propagation"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
// step1. end new import
)
Опять же, нам нужно настроить OpenTelemtry. Просто скопируйте и вставьте функцию initTracer из loadgen и вызовите её также в main функции клиентского сервиса.
step0/src/client/main.go
// step1. add OpenTelemetry initialization function
func initTracer() (*sdktrace.TracerProvider, error) {
// create a stdout exporter to show collected spans out to stdout.
exporter, err := stdout.New(stdout.WithPrettyPrint())
if err != nil {
return nil, err
}
// for the demonstration, we use AlwaysSmaple sampler to take all spans.
// do not use this option in production.
tp := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithBatcher(exporter),
)
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.TraceContext{})
return tp, nil
}
Теперь пришло время инструментировать трассировки. Поскольку клиентская служба должна принимать HTTP-запросы от службы генерации нагрузки, ей необходимо инструментировать обработчик. HTTP-сервер в клиентской службе реализован с использованием net/http, и вы можете использовать пакет otelhttp , как мы это делали в случае с генерацией нагрузки.
Сначала заменим регистрацию обработчика на otelhttp Handler. В функции main найдите строки, где обработчик HTTP регистрируется с помощью http.HandleFunc() .
step0/src/client/main.go
// step1. change handler to intercept OpenTelemetry related headers
// http.HandleFunc("/", svc.handler)
otelHandler := otelhttp.NewHandler(http.HandlerFunc(svc.handler), "client.handler")
http.Handle("/", otelHandler)
// step1. end intercepter setting
http.HandleFunc("/_genki", svc.health)
Затем мы добавляем инструменты для отслеживания фактического диапазона внутри обработчика. Находим функцию (*clientService) handler() и добавляем инструменты отслеживания диапазона с помощью trace.SpanFromContext() .
step0/src/client/main.go
func (cs *clientService) handler(w http.ResponseWriter, r *http.Request) {
...
ctx := r.Context()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
// step1. instrument trace
span := trace.SpanFromContext(ctx)
defer span.End()
// step1. end instrument
...
С помощью этой инструментации вы получаете данные от начала метода handler до его конца. Чтобы упростить анализ этих данных, добавьте к запросу дополнительный атрибут, хранящий количество совпадений. Непосредственно перед строкой лога добавьте следующий код.
func (cs *clientService) handler(w http.ResponseWriter, r *http.Request) {
...
// step1. add span specific attribute
span.SetAttributes(attribute.Key("matched").Int64(resp.MatchCount))
// step1. end adding attribute
log.Println(string(ret))
...
С учетом всех вышеперечисленных настроек, вы завершили трассировку между loadgen и клиентом. Давайте посмотрим, как это работает. Запустите код с помощью skaffold еще раз.
skaffold dev
Спустя некоторое время после запуска служб в кластере GKE вы увидите огромное количество сообщений в логах, подобных этому:
вывод команды
[loadgen] {
[loadgen] "Name": "query.request",
[loadgen] "SpanContext": {
[loadgen] "TraceID": "cfa22247a542beeb55a3434392d46b89",
[loadgen] "SpanID": "18b06404b10c418b",
[loadgen] "TraceFlags": "01",
[loadgen] "TraceState": "",
[loadgen] "Remote": false
[loadgen] },
[loadgen] "Parent": {
[loadgen] "TraceID": "00000000000000000000000000000000",
[loadgen] "SpanID": "0000000000000000",
[loadgen] "TraceFlags": "00",
[loadgen] "TraceState": "",
[loadgen] "Remote": false
[loadgen] },
[loadgen] "SpanKind": 1,
[loadgen] "StartTime": "2022-07-14T13:13:36.686751087Z",
[loadgen] "EndTime": "2022-07-14T13:14:31.849601964Z",
[loadgen] "Attributes": [
[loadgen] {
[loadgen] "Key": "telemetry.sdk.language",
[loadgen] "Value": {
[loadgen] "Type": "STRING",
[loadgen] "Value": "go"
[loadgen] }
[loadgen] },
[loadgen] {
[loadgen] "Key": "service.name",
[loadgen] "Value": {
[loadgen] "Type": "STRING",
[loadgen] "Value": "loadgen.runQuery"
[loadgen] }
[loadgen] },
[loadgen] {
[loadgen] "Key": "query",
[loadgen] "Value": {
[loadgen] "Type": "STRING",
[loadgen] "Value": "faith"
[loadgen] }
[loadgen] }
[loadgen] ],
[loadgen] "Events": null,
[loadgen] "Links": null,
[loadgen] "Status": {
[loadgen] "Code": "Unset",
[loadgen] "Description": ""
[loadgen] },
[loadgen] "DroppedAttributes": 0,
[loadgen] "DroppedEvents": 0,
[loadgen] "DroppedLinks": 0,
[loadgen] "ChildSpanCount": 5,
[loadgen] "Resource": [
[loadgen] {
[loadgen] "Key": "service.name",
[loadgen] "Value": {
[loadgen] "Type": "STRING",
[loadgen] "Value": "unknown_service:loadgen"
...
Экспортёр stdout выдаёт эти сообщения. Вы заметите, что родительские элементы всех трассировочных сегментов, созданных с помощью loadgen, имеют TraceID: 00000000000000000000000000000000 , поскольку это корневой сегмент, то есть первый сегмент в трассировке. Также вы обнаружите, что атрибут встраивания "query" содержит строку запроса, которая передаётся в клиентскую службу.
Краткое содержание
На этом этапе вы настроили службу генератора нагрузки и клиентскую службу, взаимодействующие по протоколу HTTP, и подтвердили возможность успешного распространения контекста трассировки между службами, а также экспорта информации о диапазоне трассировки из обеих служб в стандартный вывод.
Далее
На следующем этапе вы добавите инструменты для клиентской и серверной служб, чтобы подтвердить, как передавать контекст трассировки через gRPC.
5. Приборы для gRPC
На предыдущем этапе мы инструментировали первую половину запроса в этом микросервисе. На этом этапе мы пытаемся инструментировать gRPC-связь между клиентским и серверным сервисами. (Зеленый и фиолетовый прямоугольники на рисунке ниже)

Предварительно настроенная инструментация для gRPC-клиента
Экосистема OpenTelemetry предлагает множество удобных библиотек, которые помогают разработчикам инструментировать приложения. На предыдущем шаге мы использовали предварительно собранную инструментацию для пакета net/http . На этом шаге, поскольку мы пытаемся передать контекст трассировки через gRPC, мы используем для этого соответствующую библиотеку.
Сначала необходимо импортировать предварительно собранный пакет gRPC под названием otelgrpc .
step0/src/client/main.go
import (
"context"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/url"
"os"
"time"
"opentelemetry-trace-codelab-go/client/shakesapp"
// step2. add prebuilt gRPC package (otelgrpc)
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/propagation"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
В этот раз клиентская служба представляет собой gRPC-клиент, взаимодействующий с серверной службой, поэтому вам необходимо инструментировать gRPC-клиент. Найдите функцию mustConnGRPC и добавьте gRPC-перехватчики, которые будут инструментировать новые сегменты каждый раз, когда клиент отправляет запросы к серверу.
step0/src/client/main.go
// Helper function for gRPC connections: Dial and create client once, reuse.
func mustConnGRPC(ctx context.Context, conn **grpc.ClientConn, addr string) {
var err error
// step2. add gRPC interceptor
interceptorOpt := otelgrpc.WithTracerProvider(otel.GetTracerProvider())
*conn, err = grpc.DialContext(ctx, addr,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor(interceptorOpt)),
grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor(interceptorOpt)),
grpc.WithTimeout(time.Second*3),
)
// step2: end adding interceptor
if err != nil {
panic(fmt.Sprintf("Error %s grpc: failed to connect %s", err, addr))
}
}
Поскольку вы уже настроили OpenTelemetry в предыдущем разделе, вам не нужно этого делать.
Предварительно настроенная инструментальная среда для gRPC-сервера
Как и в случае с gRPC-клиентом, мы вызываем предварительно настроенную инструментацию для gRPC-сервера. Добавьте новый пакет в раздел импорта следующим образом:
step0/src/server/main.go
import (
"context"
"fmt"
"io/ioutil"
"log"
"net"
"os"
"regexp"
"strings"
"opentelemetry-trace-codelab-go/server/shakesapp"
"cloud.google.com/go/storage"
// step2. add OpenTelemetry packages including otelgrpc
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"go.opentelemetry.io/otel"
stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/propagation"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"google.golang.org/api/iterator"
"google.golang.org/api/option"
"google.golang.org/grpc"
healthpb "google.golang.org/grpc/health/grpc_health_v1"
)
Поскольку вы впервые используете инструментарий сервера, вам необходимо сначала настроить OpenTelemetry, аналогично тому, как мы это делали для генерации нагрузки и клиентских служб.
step0/src/server/main.go
// step2. add OpenTelemetry initialization function
func initTracer() (*sdktrace.TracerProvider, error) {
// create a stdout exporter to show collected spans out to stdout.
exporter, err := stdout.New(stdout.WithPrettyPrint())
if err != nil {
return nil, err
}
// for the demonstration, we use AlwaysSmaple sampler to take all spans.
// do not use this option in production.
tp := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithBatcher(exporter),
)
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.TraceContext{})
return tp, nil
}
func main() {
...
// step2. setup OpenTelemetry
tp, err := initTracer()
if err != nil {
log.Fatalf("failed to initialize TracerProvider: %v", err)
}
defer func() {
if err := tp.Shutdown(context.Background()); err != nil {
log.Fatalf("error shutting down TracerProvider: %v", err)
}
}()
// step2. end setup
...
Далее необходимо добавить перехватчики сервера. В функции main найдите место вызова grpc.NewServer() и добавьте в эту функцию перехватчики.
step0/src/server/main.go
func main() {
...
svc := NewServerService()
// step2: add interceptor
interceptorOpt := otelgrpc.WithTracerProvider(otel.GetTracerProvider())
srv := grpc.NewServer(
grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor(interceptorOpt)),
grpc.StreamInterceptor(otelgrpc.StreamServerInterceptor(interceptorOpt)),
)
// step2: end adding interceptor
shakesapp.RegisterShakespeareServiceServer(srv, svc)
...
Запустите микросервис и подтвердите трассировку.
Затем запустите измененный код с помощью команды skaffold.
skaffold dev
И снова вы видите множество информации о диапазоне в стандартном выводе.
вывод команды
...
[server] {
[server] "Name": "shakesapp.ShakespeareService/GetMatchCount",
[server] "SpanContext": {
[server] "TraceID": "89b472f213a400cf975e0a0041649667",
[server] "SpanID": "96030dbad0061b3f",
[server] "TraceFlags": "01",
[server] "TraceState": "",
[server] "Remote": false
[server] },
[server] "Parent": {
[server] "TraceID": "89b472f213a400cf975e0a0041649667",
[server] "SpanID": "cd90cc3859b73890",
[server] "TraceFlags": "01",
[server] "TraceState": "",
[server] "Remote": true
[server] },
[server] "SpanKind": 2,
[server] "StartTime": "2022-07-14T14:05:55.74822525Z",
[server] "EndTime": "2022-07-14T14:06:03.449258891Z",
[server] "Attributes": [
...
[server] ],
[server] "Events": [
[server] {
[server] "Name": "message",
[server] "Attributes": [
...
[server] ],
[server] "DroppedAttributeCount": 0,
[server] "Time": "2022-07-14T14:05:55.748235489Z"
[server] },
[server] {
[server] "Name": "message",
[server] "Attributes": [
...
[server] ],
[server] "DroppedAttributeCount": 0,
[server] "Time": "2022-07-14T14:06:03.449255889Z"
[server] }
[server] ],
[server] "Links": null,
[server] "Status": {
[server] "Code": "Unset",
[server] "Description": ""
[server] },
[server] "DroppedAttributes": 0,
[server] "DroppedEvents": 0,
[server] "DroppedLinks": 0,
[server] "ChildSpanCount": 0,
[server] "Resource": [
[server] {
...
[server] ],
[server] "InstrumentationLibrary": {
[server] "Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
[server] "Version": "semver:0.33.0",
[server] "SchemaURL": ""
[server] }
[server] }
...
Вы замечаете, что не встраивали имена сегментов и создавали их вручную с помощью trace.Start() или span.SpanFromContext() . Тем не менее, вы получаете большое количество сегментов, потому что их сгенерировали перехватчики gRPC.
Краткое содержание
На этом этапе вы настроили связь на основе gRPC с использованием библиотек экосистемы OpenTelemetry.
Далее
На следующем этапе вы наконец визуализируете трассировку с помощью Cloud Trace и научитесь анализировать собранные фрагменты данных.
6. Визуализация трассировки с помощью Cloud Trace
Вы настроили трассировку всей системы с помощью OpenTelemetry. Вы уже научились настраивать HTTP и gRPC сервисы. Однако, несмотря на это, вы еще не освоили их анализ. В этом разделе вы замените экспортеры stdout на экспортеры Cloud Trace и научитесь анализировать ваши трассировки.
Используйте экспортер Cloud Trace.
Одной из мощных характеристик OpenTelemetry является возможность её расширения. Для визуализации всех трассировок, собранных вашим оборудованием, достаточно заменить экспортер stdout на экспортер Cloud Trace.
Откройте файлы main.go каждого сервиса и найдите функцию initTracer() . Удалите строку, которая генерирует экспортер stdout, и создайте вместо неё экспортер Cloud Trace.
step0/src/loadgen/main.go
import (
...
// step3. add OpenTelemetry for Cloud Trace package
cloudtrace "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace"
)
// step1. add OpenTelemetry initialization function
func initTracer() (*sdktrace.TracerProvider, error) {
// step3. replace stdout exporter with Cloud Trace exporter
// cloudtrace.New() finds the credentials to Cloud Trace automatically following the
// rules defined by golang.org/x/oauth2/google.findDefaultCredentailsWithParams.
// https://pkg.go.dev/golang.org/x/oauth2/google#FindDefaultCredentialsWithParams
exporter, err := cloudtrace.New()
// step3. end replacing exporter
if err != nil {
return nil, err
}
// for the demonstration, we use AlwaysSmaple sampler to take all spans.
// do not use this option in production.
tp := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithBatcher(exporter),
)
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.TraceContext{})
return tp, nil
}
Вам необходимо отредактировать одну и ту же функцию как в клиентской, так и в серверной части сервиса.
Запустите микросервис и подтвердите трассировку.
После внесения изменений просто запустите кластер как обычно с помощью команды skaffold.
skaffold dev
Теперь вы не видите много информации о трассировке в структурированном формате логов на стандартном выводе, потому что заменили экспортер на Cloud Trace.
вывод команды
[loadgen] 2022/07/14 15:01:07 simulated 20 requests
[loadgen] 2022/07/14 15:01:07 simulating client requests, round 37
[loadgen] 2022/07/14 15:01:14 query 'sweet': matched 958
[client] 2022/07/14 15:01:14 {"match_count":958}
[client] 2022/07/14 15:01:14 {"match_count":3040}
[loadgen] 2022/07/14 15:01:14 query 'love': matched 3040
[client] 2022/07/14 15:01:15 {"match_count":349}
[loadgen] 2022/07/14 15:01:15 query 'hello': matched 349
[client] 2022/07/14 15:01:15 {"match_count":484}
[loadgen] 2022/07/14 15:01:15 query 'faith': matched 484
[loadgen] 2022/07/14 15:01:15 query 'insolence': matched 14
[client] 2022/07/14 15:01:15 {"match_count":14}
[client] 2022/07/14 15:01:21 {"match_count":484}
[loadgen] 2022/07/14 15:01:21 query 'faith': matched 484
[client] 2022/07/14 15:01:21 {"match_count":728}
[loadgen] 2022/07/14 15:01:21 query 'world': matched 728
[client] 2022/07/14 15:01:22 {"match_count":484}
[loadgen] 2022/07/14 15:01:22 query 'faith': matched 484
[loadgen] 2022/07/14 15:01:22 query 'hello': matched 349
[client] 2022/07/14 15:01:22 {"match_count":349}
[client] 2022/07/14 15:01:23 {"match_count":1036}
[loadgen] 2022/07/14 15:01:23 query 'friend': matched 1036
[loadgen] 2022/07/14 15:01:28 query 'tear': matched 463
...
Теперь давайте убедимся, что все трассировки корректно отправлены в Cloud Trace. Откройте консоль Cloud и перейдите к разделу «Список трассировок». Найти его легко через поле поиска. Или же вы можете щелкнуть меню в левой панели. 
Затем вы видите множество синих точек, распределенных по графику задержки. Каждая точка представляет собой отдельную трассу.

Щёлкните по одному из них, и вы увидите подробности внутри трассировки. 
Даже беглый взгляд позволяет сделать множество выводов. Например, на диаграмме «водопад» видно, что основной причиной задержки является вызов с именем shakesapp.ShakespeareService/GetMatchCount (см. 1 на изображении выше). Это можно подтвердить и в сводной таблице (в самом правом столбце показана продолжительность каждого вызова). Кроме того, эта трассировка относится к запросу "friend" (см. 2 на изображении выше).
На основе этих кратких анализов вы можете понять, что вам необходимы более детальные данные внутри метода GetMatchCount . По сравнению с информацией из стандартного вывода, визуализация является мощным инструментом. Чтобы узнать больше о Cloud Trace, посетите нашу официальную документацию .
Краткое содержание
На этом этапе вы заменили экспортер стандартного вывода на экспортер Cloud Trace и визуализировали трассировки в Cloud Trace. Также вы научились начинать анализировать трассировки.
Далее
На следующем шаге вам нужно будет изменить исходный код серверной службы, чтобы добавить подпункт в функцию GetMatchCount.
7. Добавьте подобласть для более качественного анализа.
На предыдущем шаге вы обнаружили, что причиной наблюдаемого времени отклика, определяемого функцией loadgen, в основном является процесс внутри метода GetMatchCount, обработчик gRPC, в серверной службе. Однако, поскольку мы не инструментировали ничего, кроме обработчика, мы не можем получить дополнительную информацию из диаграммы водопада. Это распространенная ситуация, когда мы начинаем инструментировать микросервисы.

В этом разделе мы проведем мониторинг участка сети, где сервер обращается к Google Cloud Storage, поскольку это распространенная ситуация, когда внешние сетевые операции ввода-вывода занимают много времени, и важно определить, является ли этот вызов причиной.
Внедрить инструментарий для создания поддиапазона на сервере.
Откройте main.go на сервере и найдите функцию readFiles . Эта функция отправляет запрос в Google Cloud Storage для получения всех текстовых файлов произведений Шекспира. В этой функции вы можете создать подтег, как это делалось для мониторинга HTTP-сервера в клиентском сервисе.
step0/src/server/main.go
func readFiles(ctx context.Context, bucketName, prefix string) ([]string, error) {
type resp struct {
s string
err error
}
// step4: add an extra span
span := trace.SpanFromContext(ctx)
span.SetName("server.readFiles")
span.SetAttributes(attribute.Key("bucketname").String(bucketName))
defer span.End()
// step4: end add span
...
На этом добавление нового элемента span завершается. Давайте посмотрим, что получится, запустив приложение.
Запустите микросервис и подтвердите трассировку.
После внесения изменений просто запустите кластер как обычно с помощью команды skaffold.
skaffold dev
Выберите из списка трассировок одну трассировку с именем query.request . Вы увидите похожий график трассировки в виде водопада, но с новым элементом в разделе shakesapp.ShakespeareService/GetMatchCount (элемент, заключенный в красный прямоугольник ниже).

Из этого графика видно, что внешний запрос к Google Cloud Storage занимает значительную часть времени задержки, но основная часть задержки приходится на другие процессы.
Вы уже получили много полезной информации, просто взглянув на график трассировки стека. Как получить более подробные сведения о производительности вашего приложения? Здесь на помощь приходит профилировщик, но на этом пока давайте закончим этот практический урок и перенесем все уроки по профилировщику во вторую часть.
Краткое содержание
На этом этапе вы добавили еще один сегмент в серверную службу и получили дополнительные сведения о задержке системы.
8. Поздравляем!
Вы успешно создали распределенные трассировки с помощью OpenTelemery и подтвердили задержки запросов в микросервисе в Google Cloud Trace.
Для более развернутых упражнений вы можете самостоятельно изучить следующие темы.
- Текущая реализация отправляет все трассировки, сгенерированные проверкой работоспособности (
grpc.health.v1.Health/Check). Как отфильтровать эти трассировки из Cloud Traces? Подсказка здесь . - Сопоставьте журналы событий с данными о сегментах сети и посмотрите, как это работает в Google Cloud Trace и Google Cloud Logging. Подсказка здесь .
- Замените какой-либо сервис на сервис на другом языке и попробуйте использовать для его работы OpenTelemetry для этого языка.
Также, если после этого вы захотите узнать больше о профилировщике, пожалуйста, перейдите ко второй части. В этом случае вы можете пропустить раздел об очистке ниже.
Уборка
После выполнения этого практического задания остановите кластер Kubernetes и обязательно удалите проект, чтобы избежать непредвиденных расходов на Google Kubernetes Engine, Google Cloud Trace и Google Artifact Registry.
Сначала удалите кластер. Если вы запускаете кластер с помощью skaffold dev , просто нажмите Ctrl-C. Если вы запускаете кластер с помощью skaffold run , выполните следующую команду:
skaffold delete
вывод команды
Cleaning up... - deployment.apps "clientservice" deleted - service "clientservice" deleted - deployment.apps "loadgen" deleted - deployment.apps "serverservice" deleted - service "serverservice" deleted
После удаления кластера в панели меню выберите «IAM и администрирование» > «Настройки», а затем нажмите кнопку «Выключить».

Затем введите идентификатор проекта (а не название проекта) в форму в диалоговом окне и подтвердите завершение работы.