В эпоху, когда крупные компании используют более 500 микросервисов одновременно, важно иметь возможность быстро выяснить, из-за чего произошел сбой или снизилась производительность. Без специализированных инструментов это может быть похоже на поиск иголки в стоге сена.
Распределённая трассировка (Distributed Tracing) - это метод, используемый для мониторинга приложений. Для микросервисов он просто незаменим.
В качестве примера мы будем использовать приложение Meow-Micro, специально созданное для этой статьи. Но вы можете развернуть собственные приложения, если захотите.
Что нам потребуется?
Начальные знания GoLang
Понимание принципов Kubernetes
Опыт работы с NGINX / Ingress-NGINX
Установить Docker-Desktop
Установить Helm v3
В этом руководстве предполагается, что вы понимаете код, написанный на Go, умеете использовать Ingress-nginx и знаете, как работают основные объекты Kubernetes, такие как Service и Deployment.
Если вы хотите освежить знания, воспользуйтесь этими материалами:
Запуск Kubernetes в Docker Desktop
Начнём с установки Docker Desktop, который позволяет не только запускать контейнеры, но и создать локальный кластер Kubernetes.
После установки Docker Desktop выполните следующие действия, чтобы создать кластер Kubernetes:
Нажмите на иконку Preferences
2. Выберете вкладку Kubernetes, установите флажок Enable Kubernetes и нажмите кнопку Apply & Restart
3. В появившемся окне нажмите кнопку Install и дождитесь пока установка завершится
4. Выберите пункт Kubernetes в панели задач
5. В контекстном меню выберите docker-desktop
6. Проверьте, что вы подключены к нужному кластеру
$ kubectl cluster-info
Kubernetes master is running at
https://kubernetes.docker.internal:6443
KubeDNS is running at https://kubernetes.docker.internal:6443/api/v1/
namespaces/kube-system/services/kube-dns:dns/proxy
Установка Ingress-NGINX Controller
1. Воспользуйтесь командой из официального руководства для установки Ingress-NGINX Controller.
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.45.0/deploy/static/provider/cloud/deploy.yaml
namespace/ingress-nginx created
serviceaccount/ingress-nginx created
configmap/ingress-nginx-controller created
clusterrole.rbac.authorization.k8s.io/ingress-nginx created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
role.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
service/ingress-nginx-controller-admission created
service/ingress-nginx-controller created\
deployment.apps/ingress-nginx-controller created
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created
serviceaccount/ingress-nginx-admission created]
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
role.rbac.authorization.k8s.io/ingress-nginx-admission created
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
job.batch/ingress-nginx-admission-create created\
job.batch/ingress-nginx-admission-patch created
2. Проверьте, что установка Ingress Сontroller прошла успешно. Pods должны быть запущены и находятся в статусе Ready.
$ kubectl get pods -n ingress-nginx
NAME READY STATUS RESTARTS
ingress-nginx-admission-create-52jsl 0/1 Completed 0
ingress-nginx-admission-patch-78fkc 0/1 Completed 0
ingress-nginx-controller-6f5454cbfb-qsfnn 1/1 Running 0
Важно: Если Pod находится в статусе Pending из-за нехватки ресурсов CPU/Memory, вы можете добавить ресурсы вашему кластеру в настройках Docker Desktop.
Установка Jaeger и настройка Ingress Controller
Jaeger - это платформа распределенная трассировки, которую мы будем использовать для мониторинга наших микросервисов. Установим Jaeger и включим трассировку на уровне Ingress Controller.
1. Вначале склонируйте репозиторий meow-micro, этот проект мы будем использовать во всех примерах.
$ git clone https://github.com/diazjf/meow-micro.git
Cloning into 'meow-micro'...
remote: Enumerating objects: 105, done.
...
$ cd meow-micro
2. В репозитории вы найдёте манифесты для jaeger-all-in-one. Установим Jaeger из этих манифестов.
$ kubectl apply -f jaeger/jaeger-all-in-one.yaml
deployment.apps/jaeger created
service/jaeger-query created
service/jaeger-collector created
service/jaeger-agent created
service/zipkin created
3. Убедимся, что Jaeger запущен и готов к работе.
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
jaeger-6f6b5d8689-8gccp 1/1 Running 0 17s
4. Пришло время настроить совместную работу Ingress-NGINX and Jaeger, для этого необходимо добавить параметры enable-opentracing и jaeger-collector-host в ingress-nginx-controller ConfigMap. В параметре jaeger-collector-host указываем имя сервиса Jaeger.
$ echo '
apiVersion: v1
kind: ConfigMap
data:
enable-opentracing: "true"
jaeger-collector-host: jaeger-agent.default.svc.cluster.local
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
' | kubectl replace -f -
configmap/ingress-nginx-controller replaced
5. Убедимся, что в настройках Ingress Controller включен и настроен opentracing.
$ kubectl get pods -n ingress-nginx | grep controller
ingress-nginx-controller-6f5454cbfb-qptxt 1/1 Running 0 8m56s
$ kubectl exec -it ingress-nginx-controller-6f5454cbfb-qptxt -n ingress-nginx -- bash -c "cat nginx.conf | grep ngx_http_opentracing_module.so"
load_module /etc/nginx/modules/ngx_http_opentracing_module.so;
$ kubectl exec -it ingress-nginx-controller-6f5454cbfb-qptxt -n ingress-nginx -- bash -c "cat nginx.conf | grep jaeger"
opentracing_load_tracer /usr/local/lib/libjaegertracing_plugin.so /etc/nginx/opentracing.json;
$ kubectl exec -it ingress-nginx-controller-6f5454cbfb-qptxt -n ingress-nginx -- bash -c "cat /etc/nginx/opentracing.json"
{
"service_name": "nginx",
"propagation_format": "jaeger",
"sampler": {
"type": "const",
"param": 1,
"samplingServerURL": "http://127.0.0.1:5778/sampling"
},
"reporter": {
"endpoint": "",
"localAgentHostPort": "jaeger-agent.default.svc.cluster.local:6831"
},
"headers": {
"TraceContextHeaderName": "",
"jaegerDebugHeader": "",
"jaegerBaggageHeader": "",
"traceBaggageHeaderPrefix": ""
}
}
Jaeger и Ingress Controller успешно установлены и настроены. Самое время развернуть наши микросервисы!
Разворачиваем тестовое приложение
Тестовое приложение meow-micro состоит из двух микросервисов. Клиент meow-client - принимает REST запрос и отправляет информацию сервису meow-server через GRPC.
Подробная информация об использовании REST и GRPC с GoLang:
Тестовое приложение также содержит несколько дополнительных инструментов:
tracing.go
Получает параметры для настройки Jaeger из окружения, в котором запускается Helm для установки meow-client and meow-server.
cfg, err := config.FromEnv()
if err != nil {
panic(fmt.Sprintf("Could not parse Jaeger env vars: %s", err.Error()))
}
tracer, closer, err := cfg.NewTracer()
if err != nil {
panic(fmt.Sprintf("Could not initialize jaeger tracer: %s", err.Error()))
}
client.go
Выполняет конфигурирование клиента - задает service name для trace и определяет span.
Span — базовый элемент распределенной трассировки. Представляет собой описание некоего рабочего процесса (например, запроса к базе данных). Span'ы обычно содержат ссылки на другие span'ы, что позволяет объединять множество span'ов в Trace.
Trace — визуализация жизни запроса в процессе его перемещения по распределенной системе.
Подробную информацию о понятиях trace и span можно посмотреть в официальной документации.
// main function span
os.Setenv("JAEGER_SERVICE_NAME", "meow-client")
tracer, closer := tracing.Init()
defer closer.Close()
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
spanCtx, _ := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header))
span := tracer.StartSpan("send-meow-communication", ext.RPCServerOption(spanCtx))
defer span.Finish()
...
// sleep function span
os.Setenv("JAEGER_SERVICE_NAME", "meow-client")
tracer, closer := tracing.Init()
defer closer.Close()
span := tracer.StartSpan("sleep")
defer span.Finish()
Установка тестового приложения
Установить Helm v3 можно на любую операционную систему, например на macOS с помощью brew. Теперь мы готовы развернуть микросервисы в нашем кластере.
$ brew install helm
...
==> Downloading https://ghcr.io/v2/homebrew/core/helm/manifests/3.5.4
######################################################################## 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/helm/blobs/sha256:5dac5803c1ad2db3a91b0928fc472aaf80a4
==> Downloading from https://pkg-containers-az.githubusercontent.com/ghcr1/blobs/sha256:5dac5803c1ad2db
######################################################################## 100.0%
==> Pouring helm--3.5.4.big_sur.bottle.tar.gz
...
$ helm version
version.BuildInfo{Version:"v3.3.4", GitCommit:"a61ce5633af99708171414353ed49547cf05013d", GitTreeState:"clean", GoVersion:"go1.14.9"}
Воспользуемся Makefile из репозитория, чтобы развернуть тестовое приложение в кластере Kubernetes.
# Build client and server from Dockerfile
$ make build
docker build -t meow-client:1.0 -f client/Dockerfile .
[+] Building 17.1s (10/10) FINISHED
...
docker build -t meow-server:1.0 -f server/Dockerfile .
[+] Building 0.5s (10/10) FINISHED
...
# Install Microservices into Kubernetes via Helm
$ make install
helm install -f helm/Values.yaml meow-micro ./helm
NAME: meow-micro
LAST DEPLOYED: Mon Apr 26 13:42:38 2021
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
Проверим, что Pods обоих сервисов запустились и работают.
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
jaeger-6f6b5d8689-s7cln 1/1 Running 0 26m
meow-client-8b974778c-85896 1/1 Running 0 15m
meow-server-56f559db44-5mvgp 1/1 Running 0 15m
Просмотр данных трассировки
А теперь самое интересное! Взглянем на трассировку.
Откройте консоль Jaeger, указав в браузере адрес http://localhost:8081.
2. Отправим запрос нашему приложению.
$ curl http://localhost/meow -X POST -d '{"name": "Meow-Mixer"}'
200 - Meow sent: Meow-Mixer
3. Обновите страницу браузера. В меню Service выберите - nginx.
4. Нажмите кнопку Find Traces.
5. Отобразятся все трассировки для nginx. Каждую из них можно развернуть, чтобы получить больше информации.
6. Теперь вы можете отслеживать, сколько времени занял каждый запрос.
И выводить дополнительную информацию по каждому запросу.
Не смотря на то, что этот небольшой пример даёт представление о том, что такое распределенная трассировка, увидеть всю её мощь можно только на реальных приложениях под нагрузкой.
Другие системы трассировки
Мы рассмотрели как использовать Jaeger для трассировки запросов Ingress-Nginx. Для этих целей можно также использовать Zipkin или DataDog.