Розподілені трейси з OBI
Вступ
OBI підтримує розподілені трейси для застосунків з деякими обмеженнями та обмеженнями версій ядра.
Розподілене трасування реалізується через поширення значення заголовка W3C traceparent. OBI зчитує вхідний контекст автоматично. Вихідне поширення контексту на мережевому рівні зазвичай вимкнено і має бути увімкнене, як описано нижче.
З увімкненим відповідним режимом поширення OBI зчитує вхідний контекст трасування, відстежує виконання програми та додає traceparent до вихідних HTTP або gRPC запитів. Якщо застосунок вже додав traceparent, OBI використовує це значення замість свого власного згенерованого контексту. Якщо OBI не може знайти вхідний контекст, він генерує його відповідно до специфікації W3C.
Для отримання детальної інформації про те, як OBI обирає батьківський запит, коли робота переміщується між потоками, goroutines, задачами або циклами подій, дивіться асоціація контексту трасування.
Сумісність
OBI підтримує розподілене трасування та поширення контексту в наступних конфігураціях:
| Область | Підтримувані версії або середовища | Примітки |
|---|---|---|
| Поширення на мережевому рівні HTTP/1 | Середовища Linux, які відповідають вимогам сумісності OBI | Працює для різних мов програмування. Для HTTPS поширення обмежене іншими сервісами, інструментованими OBI, і може бути порушене проксі або балансувальниками навантаження L7. |
| Поширення на мережевому рівні gRPC | gRPC 1.0+ через HTTP/2 | Використовує потокові заголовки HPACK traceparent для різних мов. Постійні зʼєднання не-Go, встановлені до запуску OBI, можуть не бути розпізнані. |
| Поширення контексту на рівні бібліотеки Go | Go 1.18+ | Підтримує поширення контексту goroutine до 6 вкладених рівнів goroutine. Ця функція розподіленого трасування має вищу мінімальну версію, ніж загальна інструментація на рівні бібліотеки Go. |
| Node.js async hooks | Node.js 8.0+ | Налаштування обробки SIGUSR1 може заважати поширенню контексту. |
| Ruby Puma | Ruby застосунки, що обслуговуються Puma 5.0+ | Підтримка поширення контексту вимагає сервера Puma. |
| Java thread pools | JDK 8+ | Додаткові обмеження часу виконання не документуються. |
| Python asyncio | Python 3.9+ з uvloop | Підтримка поширення контексту вимагає циклу подій uvloop. |
Версії, зазначені тут, є версіями, які OBI явно підтримує для функцій розподіленого трасування. Інші версії також можуть працювати, але вони не входять до документованого обсягу підтримки, якщо не зазначено інше. Зокрема, вимога Go 1.18+ тут застосовується до розподіленого трасування та поширення контексту; інша інструментація на рівні бібліотеки Go OBI має нижчу мінімальну версію.
Реалізація
Поширення контексту трасування реалізується двома різними способами:
- Через запис інформації про вихідні заголовки на мережевому рівні
- Через запис інформації про заголовки на рівні бібліотеки для Go
Залежно від мови програмування, якою написано ваш сервіс, OBI використовує один або обидва підходи до поширення контексту. Ми використовуємо ці кілька підходів для реалізації поширення контексту, оскільки запис памʼяті за допомогою eBPF залежить від конфігурації ядра та можливостей системи Linux, наданих OBI. Для отримання додаткової інформації про цю тему дивіться нашу доповідь на KubeCon NA 2024 So You Want to Write Memory with eBPF?.
Поширення контексту на мережевому рівні стандартно вимкнено і може бути увімкнено шляхом встановлення змінної середовища OTEL_EBPF_BPF_CONTEXT_PROPAGATION=all або шляхом зміни файлу конфігурації OBI:
ebpf:
context_propagation: 'all'
Поширення контексту на мережевому рівні
Поширення контексту на мережевому рівні реалізується шляхом запису інформації про контекст трасування у вихідні HTTP заголовки, а також на рівні TCP/IP пакетів. Поширення контексту HTTP повністю сумісне з будь-якою іншою бібліотекою трасування на базі OpenTelemetry. Це означає, що сервіси, інструментовані OBI, правильно передають інформацію про трасування, коли надсилають запити до та отримують відповіді від сервісів, інструментованих SDK OpenTelemetry. Ми використовуємо Linux Traffic Control (TC) для виконання коригування мережевих пакетів, що вимагає, щоб інші eBPF програми, які використовують Linux Traffic Control, правильно поєднувалися з OBI. Для спеціальних міркувань щодо Cilium CNI зверніться до нашого Посібника з сумісності Cilium.
Для трафіку, зашифрованого за допомогою TLS (HTTPS), OBI не може вставити інформацію про трасування у вихідні HTTP заголовки і натомість вставляє інформацію на рівні TCP/IP пакетів. Через це обмеження OBI може надсилати інформацію про трасування лише іншим сервісам, інструментованим OBI. Проксі L7 та балансувальники навантаження порушують поширення контексту TCP/IP, оскільки оригінальні пакети скидаються та відтворюються далі за течією. Парсинг вхідної інформації про контекст трасування з сервісів, інструментованих OpenTelemetry SDK, все ще працює.
Для gRPC OBI вставляє заголовок HPACK traceparent для кожного потоку. Це працює у всіх мовах програмування і зберігає окремі контексти трасування для паралельних потоків HTTP/2. OBI не використовує опції TCP для gRPC, оскільки опції TCP діють у межах одного з’єднання і не можуть представляти кілька мультиплексованих потоків. Загальне поширення контексту HTTP/2, що не стосується gRPC, залишається обмеженим інструментацією бібліотеки Go.
Якщо вам потрібен більш точний контроль, context_propagation також приймає значення headers, tcp та headers,tcp. Колишній псевдонім http було видалено. Застаріле значення ip не має ефекту.
Цей тип поширення контексту працює для будь-якої мови програмування і не вимагає, щоб OBI працював у режимі privileged або мав надану можливість CAP_SYS_ADMIN. Для отримання додаткової інформації дивіться розділ Розподілені трасування та поширення контексту конфігурації.
Налаштування Kubernetes
Рекомендований спосіб розгортання OBI на Kubernetes з підтримкою розподілених трасувань на мережевому рівні - це DaemonSet.
Наступна конфігурація Kubernetes повинна бути використана:
- OBI повинен бути розгорнутий як
DaemonSetз доступом до мережі хосту (hostNetwork: true). - Шлях
/sys/fs/cgroupз хосту повинен бути змонтований як локальний/sys/fs/cgroupшлях. - Можливість
CAP_NET_ADMINповинна бути надана контейнеру OBI.
Наступний фрагмент YAML показує приклад конфігурації розгортання OBI:
spec:
serviceAccount: obi
hostPID: true # <-- Важливо. Потрібно в режимі DaemonSet, щоб OBI міг виявити всі контрольовані процеси
hostNetwork: true # <-- Важливо. Потрібно в режимі DaemonSet, щоб OBI міг бачити всі мережеві пакети
dnsPolicy: ClusterFirstWithHostNet
containers:
- name: obi
resources:
limits:
memory: 120Mi
terminationMessagePolicy: FallbackToLogsOnError
image: 'docker.io/otel/ebpf-instrument:main'
imagePullPolicy: 'Always'
env:
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: 'http://otelcol:4318'
- name: OTEL_EBPF_KUBE_METADATA_ENABLE
value: 'autodetect'
- name: OTEL_EBPF_CONFIG_PATH
value: '/config/obi-config.yml'
securityContext:
runAsUser: 0
readOnlyRootFilesystem: true
capabilities:
add:
- BPF # <-- Важливо. Потрібно для більшості eBPF проб, щоб вони працювали правильно.
- SYS_PTRACE # <-- Важливо. Дозволяє OBI отримувати доступ до простору імен контейнера та перевіряти виконувані файли.
- NET_RAW # <-- Важливо. Дозволяє OBI використовувати фільтри сокетів для http запитів.
- CHECKPOINT_RESTORE # <-- Важливо. Дозволяє OBI відкривати ELF файли.
- DAC_READ_SEARCH # <-- Важливо. Дозволяє OBI відкривати ELF файли.
- PERFMON # <-- Важливо. Дозволяє OBI завантажувати BPF застосунки.
- NET_ADMIN # <-- Важливо. Дозволяє OBI впроваджувати інформацію про контекст HTTP та TCP.
volumeMounts:
- name: cgroup
mountPath: /sys/fs/cgroup # <-- Важливо. Дозволяє OBI моніторити всі нові сокети для відстеження вихідних запитів.
- mountPath: /config
name: obi-config
tolerations:
- effect: NoSchedule
operator: Exists
- effect: NoExecute
operator: Exists
volumes:
- name: obi-config
configMap:
name: obi-config
- name: cgroup
hostPath:
path: /sys/fs/cgroup
Якщо /sys/fs/cgroup не змонтовано як локальний шлях тому для OBI DaemonSet, деякі запити можуть не мати свого контексту. Ми використовуємо цей шлях тому, щоб слухати новостворені сокети.
Обмеження версії ядра
Поширення контексту на мережевому рівні та розбір вхідних заголовків зазвичай вимагає ядра 5.17 або новішого для додавання та використання BPF-циклів.
Деякі полатані ядра, такі як RHEL 9.2, можуть мати цю функціональність перенесену на старіші ядра. Встановлення OTEL_EBPF_OVERRIDE_BPF_LOOP_ENABLED пропускає перевірки ядра у випадку, якщо ваше ядро включає цю функціональність, але є нижчим за 5.17.
Поширення контексту Go шляхом інструментування на рівні бібліотеки
Цей тип поширення контексту підтримується лише для Go-застосунків і використовує підтримку запису памʼяті користувача eBPF (bpf_probe_write_user). Перевагою цього підходу є те, що він працює для HTTP та HTTPS. Для HTTP/2 та gRPC OBI може впроваджувати контекст на нових та повторно використаних з’єднаннях, коли HTTPS не використовується. Використання bpf_probe_write_user вимагає надання OBI CAP_SYS_ADMIN або його налаштування для роботи як privileged контейнер.
Інструментування застосунків, що використовують Go Trace API
Починаючи з OBI v0.11.0, OBI може інструментувати застосунки, що використовують OpenTelemetry Go Trace API без реєстрації SDK. Коли ця інтеграція активна, OBI виявляє виклики Trace API та експортує отримані ручні відрізки поруч зі своїми eBPF відрізками. Застосунки, що вже реєструють OpenTelemetry SDK, продовжують керувати та експортувати власну телеметрію SDK. Реєстрація будь-якого глобального TracerProvider, включаючи виклик otel.SetTracerProvider(auto.TracerProvider()), запобігає цій автоматичній активації.
OBI активує інтеграцію лише коли виконуються всі наступні умови:
- Застосунок використовує підтримувану комбінацію версії та контрольної суми OpenTelemetry модуля, без заміни модуля.
- Виконавчий файл та хост використовують підтримувану 64-бітну архітектуру.
- OBI може розв’язати необхідні символи та макети полів.
- OBI має дозвіл на використання
bpf_probe_write_user.
Якщо будь-яка перевірка не проходить, Auto SDK залишається неактивним, а відрізки, створені через глобальний Trace API,залишаються без запису. eBPF інструментування OBI продовжує працювати незалежно. Коли OBI може виявити виклики Trace API, він може експортувати часткові синтетичні відрізки, що містять імʼя відрізка, батьківські звʼязки, статус та деякі примітивні атрибути. Ці відрізки не включають область інструментування, події чи запитаний тип відрізка.
У OBI v0.11.0, закодоване навантаження для кожного відрізка, експортованого через Auto SDK, не може перевищувати 16 KiB. OBI не видає метрику чи повідомлення логу при активації інтеграції чи відкиданні надмірного навантаження.
Відомі обмеження та подальша робота включають head sampling, передачу контексту, зовнішні та віддалені пращури та TraceState, більші навантаження та відкидання спостережуваності та збагачення логів.
Для підтримуваних комбінацій версії модуля, контрольної суми та архітектури дивіться матрицю працездатності активації. Ви також можете переглянути upstream приклад Go Trace API та документацію по Auto SDK.
Обмеження режиму цілісності ядра
Для того, щоб записати значення traceparent у вихідні заголовки HTTP/gRPC запитів, OBI потрібно записати в памʼять процесу, використовуючи bpf_probe_write_user eBPF helper. Оскільки ядро 5.14 (з виправленнями, перенесеними на серію 5.10) цей helper захищений (і недоступний для BPF застосунків), якщо Linux Kernel працює в режимі integrity lockdown. Режим цілісності ядра зазвичай стандартно увімкнено, якщо ядро має Secure Boot увімкнено, але його також можна увімкнути вручну.
OBI автоматично перевіряє, чи може він використовувати helper bpf_probe_write_user, і активує поширення контексту лише в тому випадку, якщо це дозволено конфігурацією ядра. Перевірте режим lockdown ядра Linux, виконавши наступну команду:
cat /sys/kernel/security/lockdown
Якщо цей файл існує і режим є будь-яким іншим, ніж [none], OBI не може виконати поширення контексту, і розподілене трасування вимкнено.
Розподілене трасування для Go в контейнеризованих середовищах (включаючи Kubernetes)
Тому через обмеження режиму lockdown ядра, файли конфігурації Docker і Kubernetes повинні монтувати том /sys/kernel/security/ для контейнера OBI з хост-системи. Таким чином OBI може правильно визначити режим lockdown ядра Linux. Ось приклад конфігурації Docker compose, яка забезпечує OBI достатньою інформацією для визначення режиму lockdown:
services:
...
obi:
image: 'docker.io/otel/ebpf-instrument:main'
environment:
OTEL_EBPF_CONFIG_PATH: "/configs/obi-config.yml"
volumes:
- /sys/kernel/security:/sys/kernel/security
- /sys/fs/cgroup:/sys/fs/cgroup
Якщо том /sys/kernel/security/ не змонтовано, OBI вважає, що ядро Linux не працює в режимі підтримки цілісності.
Звʼязки відрізків каналів Go
OBI генерує експериментальні звʼязки відрізків на стороні отримувача для підтримуваних передач роботи через канали Go. Коли і сторона надсилання, і сторона отримувача мають активні відрізки, згенеровані OBI, відрізок отримувача посилається на відрізок відправника. OBI не змінює ідентифікатори трейсів, звʼязки батько-нащадок або відрізок відправника.
Ця поведінка вмикається автоматично з Go-специфічним трасуванням, коли OBI може розпізнати зсуви каналів цільового бінарного файлу. Підтримуються прямі небуферизовані та буферизовані передачі через runtime.chansend1, runtime.chanrecv1 та runtime.chanrecv2. Операції з каналами через select не підтримуються. Вимкніть Go-специфічні трасувальники, щоб вимкнути ці зонди; немає окремої опції для звʼязків каналів.
OBI дотримується OTEL_SPAN_LINK_COUNT_LIMIT та відкидає недійсні, дубльовані звʼязки та звʼязки-самопосилання.
Захоплення ручних відрізків Node.js
Починаючи з OBI v0.12.1, OBI може захоплювати відрізки, які Node.js застосунок створює через @opentelemetry/api, коли застосунок не зареєстрував OpenTelemetry SDK. OBI експортує ці ручні відрізки через свій trace pipeline та корелює їх з автоматично захопленими серверними відрізками. Якщо застосунок реєструє SDK, OBI залишає створення та експорт відрізків цьому SDK.
Ця функція стандартно вимкнена. Наявні розгортання Config v1 можуть увімкнути її через nodejs.manual_spans: true або OTEL_EBPF_NODEJS_MANUAL_SPANS=true. Config v2 не експонує еквівалентного поля у v0.12.1. Продовжуйте мігрувати розгортання на Config v2 замість утримання Config v1 виключно для цієї функції.
OBI повинен мати доступ до інспектора Node.js, а процес не повинен мати власний обробник SIGUSR1. Екземпляри @opentelemetry/api, що входять до складу пакунка, до яких завантажувач CommonJS не має доступу, не захоплюються. Автоматичні клієнтські відрізки наразі є рівноправними елементами поруч із ручними відрізками в межах одного серверного відрізка, а не дочірніми елементами активного ручного відрізка.
Python asyncio з uvloop
Починаючи з v0.7.0, OBI підтримує поширення контексту для робочих навантажень Python asyncio, що працюють на uvloop. Це дозволяє отримувати розподілене трасування асинхронних Python-сервісів, які використовують цикл подій uvloop, на додачу до стандартної підтримки asyncio.
Поширення контексту на мережевому рівні застосовується до Python-застосунків, що працюють на uvloop, дозволяючи OBI автоматично інструментувати та поширювати контекст трасування для асинхронних операцій. Додаткова конфігурація не потрібна, окрім увімкнення поширення контексту, як описано у вступі.
Щоб використовувати OBI з Python asyncio та uvloop, переконайтеся, що ваш Python-застосунок налаштований на використання uvloop як реалізації циклу подій.
Зворотний зв’язок
Чи була ця сторінка корисною?
Дякуємо. Ми цінуємо ваші відгуки!
Будь ласка, дайте нам знати як ми можемо покращити цю сторінку. Ми цінуємо ваші відгуки!