Istio і Kubernetes у виробництві. Частина 2. Tracing

У минулій статті ми розглянули базові компоненти Service Mesh Istio, познайомилися із системою та відповіли на основні питання, які зазвичай виникають на початку роботи з Istio. У цій частині ми подивимося, як організувати збір tracing інформації по мережі.

Istio і Kubernetes у виробництві. Частина 2. Tracing

Перше, що спадає на думку багатьом розробникам і системним адміністраторам, коли вони чують слова Service Mesh — це tracing. Ми додаємо в кожен вузол мережі спеціальний проксі-сервер, через який проходить весь TCP-трафік. Здається, що тепер можна легко надсилати інформацію про всі мережеві взаємодії в мережі. На жаль, насправді з'являється безліч нюансів, які необхідно враховувати. Давайте розглянемо їх.

Помилка номер один: ми можемо безкоштовно отримати дані про походи через мережу

Насправді відносно безкоштовно ми можемо лише отримати з'єднані стрілками вузли нашої системи та rate даних, що проходить між сервісами (по суті — лише кількість байтів в одиницю часу). Однак у більшості випадків наші сервіси спілкуються за якимось протоколом прикладного рівня, таким як HTTP, gRPC, Redis і так далі. І, звичайно, ми хочемо бачити трейсинг інформацію саме з цих протоколів, хочемо бачити rate запитів, а не rate даних. Хочемо розуміти latency запитів за нашим протоколом. Нарешті ми хочемо бачити повний шлях, який проходить запит від входу в нашу систему до отримання відповіді користувачем. Це завдання вирішується не так просто.

Спочатку давайте розглянемо як виглядає відправка tracing span'ів з погляду архітектури у Istio. Як ми пам'ятаємо з першої частини, для збору телеметрії Istio є окремий компонент, який називається Mixer. Однак, у поточній версії 1.0.* відправка здійснюється безпосередньо з проксі серверів, а саме з envoy proxy. Envoy proxy підтримує відправку tracing span'ів протоколом zipkin з коробки. Інші протоколи можна підключити, але тільки через плагін. З Istio ми відразу отримуємо зібраний та налаштований envoy proxy, у якому підтримується тільки zipkin протокол. Якщо ми хочемо використовувати, наприклад, Jaeger протокол і відправляти tracing span'и UDP, то нам потрібно буде зібрати свій istio-proxy образ. Підтримка кастомних плагінів для istio-proxy є, проте вона досі в alpha версії. Тому, якщо ми хочемо обійтися без великої кількості кастомних налаштувань, коло використовуваних технологій для зберігання та прийому tracing span'ів зменшується. З основних систем, по суті, зараз можна використовувати сам Zipkin або Jaeger, але відправляти туди все по zipkin сумісному протоколу (що значно менш ефективно). Сам zipkin протокол передбачає відправку всієї tracing інформації на колектори по протоколу HTTP, що досить накладно.

Як я вже сказав, ми хочемо протоколи прикладного рівня. А це означає, що proxy сервери, які стоять поряд з кожним сервісом, повинні розуміти, яка саме взаємодія відбувається зараз. За замовчуванням, Istio налаштовує для всіх портів plain TCP тип, що означає, що ніяких трейсів не буде відправлятися. Для того, щоб трейси відправлялися, потрібно, по-перше, включити таку опцію в головному mesh config'і і, що дуже важливо, назвати всі порти у service сутностей kubernetes відповідно до протоколу, який використовується в сервісі. Тобто, наприклад, ось так:

apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  ports:
  - port: 80
    targetPort: 80
    name: http
  selector:
    app: nginx

Можна також використовувати складові імена, наприклад, http-magic (Istio побачить http і розпізнає цей порт як http endpoint). Формат такий: proto-extra.

Для того, щоб не патчити величезну кількість конфігурацій для визначення протоколу, можна скористатися брудним workaround'ом: пропатчити Pilot компонент у момент, коли він саме виконує логіку визначення протоколу. У результаті, звичайно, потрібно буде змінити цю логіку на стандартну і перейти на конвенцію найменування всіх портів.

Для того, щоб зрозуміти чи дійсно протокол визначено правильно, потрібно зайти в будь-який з sidecar контейнерів з envoy proxy і зробити запит на порт admin інтерфейсу envoy з location /config_dump. У конфігурації, що вийшла, потрібно подивитися у потрібного сервісу поле operation. Воно використовується в Istio як ідентифікатор того, куди запит. Для того, щоб кастомізувати в Istio значення цього параметра (ми потім будемо бачити його в нашій tracing системі), необхідно на етапі запуску sidecar контейнера вказати прапор serviceCluster. Наприклад, його можна ось так обчислювати зі змінних, отриманих із downward API kubernetes:

--serviceCluster ${POD_NAMESPACE}.$(echo ${POD_NAME} | sed -e 's/-[a-z0-9]*-[a-z0-9]*$//g')

Хороший приклад для розуміння того, як працює tracing в envoy, є тут.

Сам endpoint для відправки tracing span'ів необхідно також вказати у прапорах запуску envoy proxy, наприклад: --zipkinAddress tracing-collector.tracing:9411

Помилка номер два: ми можемо недорого отримати повні трейси проходу запитів по системі коробки

На жаль, це негаразд. Складність застосування залежить від цього, як у вас вже реалізовано взаємодія сервісів. Чому так?

Справа в тому, що для того, щоб istio-proxy зміг зрозуміти відповідність вхідних запитів у сервіс з тими, що виходять з цього ж сервісу, недостатньо просто перехоплювати весь трафік. Потрібно мати якийсь ідентифікатор зв'язку. У HTTP envoy proxy використовуються спеціальні заголовки, якими envoy розуміє який саме запит до сервісу породжує конкретні запити до інших сервісів. Список таких заголовків:

  • x-request-id,
  • x-b3-traceid,
  • x-b3-spanid,
  • x-b3-parentspanid,
  • x-b3-sampled,
  • x-b3-flags,
  • x-ot-span-context.

Якщо у вас єдина точка, наприклад, базовий клієнт, в якому можна додати таку логіку, то все чудово, вам потрібно буде лише дочекатися оновлення цієї бібліотеки у всіх клієнтів. Але якщо у вас дуже гетерогенна система і немає уніфікації в поході з сервісів до сервісів мережі, то це швидше за все буде великою проблемою. Без додавання подібної логіки вся tracing інформація буде лише однорівневою. Тобто ми отримаємо всі міжсервісні взаємодії, але вони не будуть склеєні в єдині ланцюжки проходу через мережу.

Висновок

Istio надає зручний інструмент для збору tracing інформації по мережі, однак треба розуміти, що для впровадження потрібно адаптувати свою систему та врахувати особливості реалізації Istio. У результаті потрібно вирішити два основні моменти: визначення протоколу прикладного рівня (який повинен підтримуватися envoy proxy) та налаштування прокидання інформації про пов'язаність запитів у сервіс від запитів із сервісу (за допомогою header'ів, у разі протоколу HTTP). Коли ці питання вирішені, ми отримуємо потужний інструмент, який дозволяє прозоро збирати інформацію з мережі навіть у гетерогенних системах, написаних на безлічі різних мов і фреймворків.

У наступній статті про Service Mesh розглянемо одну з найбільших проблем Istio – велике споживання оперативної пам'яті кожним sidecar проксі контейнером та обговоримо, як з нею можна боротися.

Джерело: habr.com

Додати коментар або відгук