Istio и Kubernetes в production. Часть 2. Tracing
В прошлой статье мы рассмотрели базовые компоненты Service Mesh Istio, познакомились с системой и ответили на основные вопросы, которые обычно возникают в начале работы с Istio. В этой части мы посмотрим на то, как организовать сбор tracing информации по сети.
![](https://webcf.waybackmachine.org/web/20190319152559im_/https://habrastorage.org/webt/dz/-l/6j/dz-l6j3yddyja_gjqgvw-3_da2m.png)
Первое, что приходит в голову многим разработчикам и системным администраторам, когда они слышат слова 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 в соответствии с протоколом, который в используется в сервисе. То есть, например, вот так:
Можно также использовать составные имена, например 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:
Хороший пример для понимания того, как работает tracing в envoy, есть тут.
Сам endpoint для отправки tracing span’ов необходимо также указать в флагах запуска envoy proxy, например:
К сожалению, это не так. Сложность внедрения зависит от того, каким образом у вас уже реализовано взаимодействие сервисов. Почему так?
Дело в том, что для того, чтобы istio-proxy смог понять соответствие входящих запросов в сервис с выходящими из этого же сервиса, недостаточно просто перехватывать весь трафик. Нужно иметь какой-то идентификатор связи. В HTTP envoy proxy используются специальные заголовки, по которым envoy понимает какой именно запрос к сервису порождает конкретные запросы в другие сервисы. Список таких заголовков:
Если у вас единая точка, например, базовый клиент, в котором можно добавить такую логику, то все отлично, вам лишь нужно будет дождаться обновления этой библиотеки у всех клиентов. Но если у вас очень гетерогенная система и нет унификации в походе из сервисов в сервисы по сети, то это скорее всего будет большой проблемой. Без добавления подобной логики вся tracing информация будет лишь «одноуровневой». То есть мы получим все межсервисные взаимодействия, но они не будут склеены в единые цепочки прохода по сети.
Istio предоставляет удобный инструмент для сбора tracing информации по сети, однако надо понимать, что для внедрения потребуется адаптировать свою систему и учесть особенности реализации Istio. В итоге нужно решить два основных момента: определение протокола прикладного уровня (который должен поддерживаться envoy proxy) и настройку проброса информации о связанности запросов в сервис от запросов из сервиса (с помощью header’ов, в случае HTTP протокола). Когда эти вопросы решены, мы получаем мощный инструмент, который позволяет прозрачно собирать информацию с сети даже в очень гетерогенных системах, написанных на множестве различных языков и фреймворков.
В следующей статье про Service Mesh рассмотрим одну из самых больших проблем Istio – большое потребление оперативной памяти каждым sidecar прокси контейнером и обсудим, как с ней можно бороться.
![](https://webcf.waybackmachine.org/web/20190319152559im_/https://habrastorage.org/webt/dz/-l/6j/dz-l6j3yddyja_gjqgvw-3_da2m.png)
Первое, что приходит в голову многим разработчикам и системным администраторам, когда они слышат слова 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 прокси контейнером и обсудим, как с ней можно бороться.
Комментарии 4
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.