Не у всех есть необходимость в тонкой настройке access logging в Envoy, но если она всё-таки возникает, то могут понадобиться примеры, которых почему-то не очень много в документации. Поэтому мы сделали перевод статьи, где вы можете познакомиться с Envoy, узнать, как включить журнал доступа (access log) Envoy в Istio, и научиться настраивать фильтры.
Предполагается, что у вас есть базовые знания об Istio, но даже если у вас их нет, вы можете следовать этому руководству и попробовать настроить всё на своей локальной машине.
1. Краткое введение в Envoy
Envoy — это высокопроизводительная прокси и коммуникационная шина L7, разработанная на языке C++ и предназначенная для крупных современных сервис-ориентированных архитектур. Это самостоятельный процесс, предназначенный для работы рядом с любым сервером приложений.
По своей сути Envoy является сетевым прокси L3/L4. Подключаемый механизм цепочки фильтров позволяет писать фильтры для выполнения различных задач TCP/UDP-прокси и вставлять их в основной сервер.
Envoy поддерживает дополнительный уровень фильтров HTTP L7; HTTP-фильтры могут быть подключены к подсистеме управления HTTP-соединениями, выполняющей различные задачи, такие как буферизация, ограничение скорости, маршрутизация/переадресация и т.д.
При работе в режиме HTTP Envoy поддерживает подсистему маршрутизации, способную направлять и перенаправлять запросы на основе path, авторитета, типа содержимого, значений времени выполнения и т.д. Эта функциональность наиболее полезна при использовании Envoy в качестве front/edge proxy, но также может быть задействована при построении сетки "service-to-service".
Envoy обладает многими другими высокоуровневыми возможностями, но перечисленные выше делают его идеальным для использования в качестве sidecar proxy в service mesh.
С этим учебным пособием связаны два основных понятия о Envoy:
1.1 Управление HTTP-соединениями
HTTP является настолько важным компонентом современных сервис-ориентированных архитектур, что в Envoy реализовано большое количество специфической для HTTP функциональности.
Envoy имеет встроенный фильтр сетевого уровня — HTTP connection manager, который преобразует необработанные байты в сообщения и события уровня HTTP (например, полученные заголовки, полученные данные тела, полученные трейлеры и т.д.). Он также обрабатывает функции, общие для всех HTTP-соединений и запросов, такие как регистрация доступа, генерация и отслеживание идентификаторов запросов, работа с заголовками запросов/ответов, управление таблицами маршрутов и статистика.
1.2 Логирование доступа
При использовании в сценарии service-mesh, например, в Istio, простейшим видом протоколирования является протоколирование доступа Envoy.
Прокси-серверы Envoy выводят информацию о доступе в стандартный вывод. Команда kubectl logs может вывести стандартный вывод контейнеров Envoy.
2. Небольшое введение в Istio (и Service Mesh)
2.1 Service Mesh
Service Mesh — это специализированный инфраструктурный слой, добавляемый к распределенным микросервисам. Он позволяет прозрачно добавлять такие возможности, как:
наблюдаемость
управление трафиком
безопасность.
И все это достигается без изменения кода приложения.
По мере роста масштабов и сложности развертывания распределенных сервисов становится все труднее понимать и управлять всеми сервисами. Требуется обнаружение и балансировка нагрузки, восстановление после сбоев, метрики, мониторинг и т.д. Поэтому здесь очень важно эффективное межсервисное взаимодействие. Однако чем больше становится сервисов, тем сложнее заниматься маршрутизацией этого взаимодействия внутри и между кластерами приложений.
Service Mesh позволяет уменьшить боль от вышеупомянутых проблем и снизить нагрузку на команды разработчиков.
Кроме того service mesh может решать и более сложные операционные задачи, такие как A/B-тестирование, канареечное развертывание, ограничение скорости, контроль доступа, шифрование и сквозная аутентификация.
2.2 Istio
Istio — это service mesh с открытым исходным кодом, который прозрачно накладывается на существующие распределенные приложения. Istio обеспечивает унифицированный и более эффективный способ защиты, подключения и мониторинга сервисов. Istio — это путь к балансировке нагрузки, аутентификации между сервисами и мониторингу, при этом изменения кода сервисов практически не требуются. Он предоставляет такие важные возможности, как:
Безопасное взаимодействие между сервисами в кластере с помощью шифрования TLS, надежной аутентификации и авторизации на основе идентификационных данных.
Автоматическая балансировка нагрузки для трафика HTTP, gRPC, WebSocket и TCP.
Тонкий контроль поведения трафика с помощью богатых правил маршрутизации, повторных попыток, отказоустойчивости и инжекции ошибок.
Подключаемый уровень политик и API конфигурации, поддерживающий контроль доступа, ограничения скорости и квоты.
Автоматические метрики, журналы и трассировки для всего трафика в кластере, включая входящий и исходящий трафик кластера.
Istio масштабируемый и может использоваться для различных задач развертывания. Control plane Istio работает на базе Kubernetes. Вы можете добавлять в сетку приложения, развернутые в этом кластере, расширять сетку на другие кластеры или даже подключать ВМ или другие конечные точки за пределами Kubernetes.
Большая экосистема участников, партнеров, интеграторов и дистрибьюторов расширяет и использует Istio в самых разных сценариях. Вы можете установить Istio самостоятельно, а некоторые поставщики предлагают продукты, которые интегрируют Istio и управляют им за вас.
2.3 Istio и Envoy
Istio использует Envoy в качестве прокси-серверов, развернутых в качестве sidecars. Эти прокси-серверы являются посредниками и контролируют все сетевые взаимодействия между микросервисами. Они также собирают и сообщают телеметрию обо всем сетевом трафике.
Чтобы не быть голословным, отмечу, что помимо Istio существуют и другие варианты service mesh, некоторые из которых с определенной точки зрения даже лучше. Но поскольку Envoy и Istio тесно связаны, сегодня мы будем использовать Istio для демонстрации настроек журнала доступа Envoy.
Хорошо, теперь, когда мы разобрались с номенклатурой service mesh и Istio, давайте поиграем с журналом доступа Envoy. Для этого мы воспользуемся Istio.
3. Подготовка локального кластера Kubernetes с помощью Istio
3.1 Подготовка локального кластера Kubernetes
Одним из самых простых способов запуска локального кластера Kubernetes является использование minikube. Следуйте инструкциям, приведенным в официальной документации по установке, а затем выполните следующие действия:
minikube start
3.2 Установка Istio
Загрузите Istio, установите, а затем включите istio-injection в пространстве имен по умолчанию, выполнив следующие команды:
curl -L https://istio.io/downloadIstio | sh -
# your version might differ
cd istio-1.16.1
# for easier usage
export PATH=$PWD/bin:$PATH
# here, we use the minimal profile so that the access log isn't enabled by default, which we want to do ourselves
istioctl install - set profile=minimal -y
# enable injection in the default namespace
kubectl label namespace default istio-injection=enabled
3.3 Развертывание тестовых приложений
Сначала развернем несколько примеров приложений для тестирования:
kubectl apply -f samples/sleep/sleep.yaml
export SOURCE_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
kubectl apply -f samples/httpbin/httpbin.yaml
Этот набор команд развернет два приложения: одно из них — команда "curl" в поде, которую мы будем использовать для отправки HTTP-запросов, а другое — HTTP-сервер, принимающий запросы, который мы будем использовать для демонстрации журналов доступа Envoy.
4. Журналы доступа Envoy в Istio
4.1 Включение журналов доступа
Далее включим журналы доступа.
Istio предлагает несколько способов включения журналов доступа. Рекомендуется использовать Telemetry API:
Сначала создадим файл telemetry.yaml
со следующим содержимым:
apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
name: mesh-default
namespace: istio-system
spec:
accessLogging:
- providers:
- name: Envoy
Затем примените его:
kubectl apply -f telemetry.yaml
4.2 Тест
Сначала отправим запрос из sleep на httpbin:
kubectl exec “$SOURCE_POD” -c sleep — curl -sS -v httpbin:8000/status/418
Если мы проверим журнал httpbin, то увидим нечто похожее на следующее:
kubectl logs -l app=httpbin -c istio-proxy
[2020–11–25T21:26:18.409Z] "GET /status/418 HTTP/1.1" 418 - via_upstream - "-" 0 135 3 1 "-" "curl/7.73.0-DEV" "84961386–6d84–929d-98bd-c5aee93b5c88" "httpbin:8000" "127.0.0.1:80" inbound|8000|| 127.0.0.1:41854 10.44.1.27:80 10.44.1.23:37652 outbound_.8000_._.httpbin.foo.svc.cluster.local default
5. Фильтр журналов доступа Envoy
Теперь, когда мы включили журналы доступа для Envoy, давайте поиграем с ними.
5.1 Задача
Представьте себе следующую ситуацию: в вашем приложении есть конечные точки, например, /status
, /liveness
и /readiness
, которые вы не хотите логировать, поскольку там может быть множество запросов в минуту. Эти журналы логи статус чеков будут не очень хорошим решением с точки зрения использованием ресурсов для логирования.
Кроме того, вы можете настроить журналы доступа таким образом, чтобы различные запросы и ответы записывались в отдельные файлы журналов.
Можем ли мы этого добиться?
Да.
Envoy поддерживает несколько встроенных журналов доступа и фильтры расширений, регистрируемые во время выполнения программы. Теперь попробуем продемонстрировать функции фильтров журналов:
5.2 Загрузка Envoy и подготовка конфигурационного файла
Самый простой способ поиграть с конфигурацией Envoy — это запустить его локально, а не в качестве sidecar в составе Istio. Давайте сделаем следующее:
Установите Envoy:
brew update
brew install envoy
Скачать демонстрационную конфигурацию можно отсюда.
Затем запустим ее:
envoy -c envoy-demo.yaml
Затем откройте другую вкладку и убедитесь, что Envoy работает на порту 10000:
curl -v localhost:10000
Если мы вернемся на вкладку, где запущен Envoy, то увидим журнал доступа Envoy, который будет выглядеть следующим образом:
[2023–01–19T03:10:59.085Z] “GET / HTTP/1.1” 200–0 17304 747 467 “-” “curl/7.85.0” “4b35db72-baf8–4730–885e-3e628757f0e3” “www.envoyproxy.io" “34.143.223.220:443”
5.3 Фильтр журналов Envoy
Envoy предоставляет множество фильтров журналов, например, фильтр по status code, header filter (фильтр по заголовкам) и т.д. Более подробную информацию можно найти в официальной документации здесь.
Документация могла бы быть более подробной, в ней нет примеров использования каждого фильтра. Но это нам не помешает. После несложных поисков в Google (и на GitHub) попробуем этот кусок конфига:
...
access_log:
- name: Envoy.access_loggers.stdout
typed_config:
"@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
filter:
header_filter:
header:
name: :Path
string_match:
exact: /status
...
Понять это несложно: он работает по заголовку, и если путь чему-то соответствует, то фильтр это улавливает.
Если мы добавим этот кусок кода в файл envoy-demo.yaml
и обратимся к URL /status
:
curl -v localhost:10000/status
Будут вести такие журналы, как:
[2023–01–19T03:23:08.054Z] “GET /status HTTP/1.1” 404–0 3413 776 690 “-” “curl/7.85.0” “e507c86f-004f-4cba-b622–2004c7cf97c7” “www.envoyproxy.io" “34.126.184.144:443”
Но если мы обращаемся к URL, отличным от /status
, например, /
:
curl -v localhost:10000
Мы не получаем никаких журналов.
Хорошо, теперь фильтр журналов работает. За исключением того, что мы хотим отфильтровать журналы типа /status
.
5.4 Подробнее о фильтрах журналов Envoy
Хорошо, давайте вернемся к официальной документации и немного поищем в Google.
Нетрудно заметить, что некоторые фильтры типа "and_filter" полезны. Давайте попробуем это сделать:
...
filter:
and_filter:
filters:
- header_filter:
header:
name: :Path
string_match:
exact: /status
invert_match: true
- header_filter:
header:
name: :Path
string_match:
exact: /liveness
invert_match: true
- header_filter:
header:
name: :Path
string_match:
exact: /readiness
invert_match: true
...
Мы можем использовать and_filter
для объединения нескольких фильтров, а также инвертировать результат совпадения с помощью invert_match
.
Эта конфигурация, обновленная в envoy-demo.yaml
, будет показывать журналы только в том случае, если URL не соответствует /status
, /liveness
или /readiness
.
Итак, мы достигли своей цели: мы можем использовать фильтры для управления access logging Envoy. В приведенном выше примере мы использовали and_filter
и header_filter
для отсеивания определенных нежелательных записей в журнале. Если смешивать и сочетать все фильтры Envoy, то можно быстро добиться перенаправления определённых логов в определённые файлы или регистрировать только запросы 4xx и т.д.
Но (и это большое "но"), вышеописанная конфигурация предназначена только для локального использования или запуска Envoy как отдельного процесса.
Как поместить эти настройки фильтров в sidecar Envoy?
6. Telemetry API
Как уже говорилось ранее, Istio предлагает несколько способов включения журналов доступа. В разделе 4.1 мы использовали Telemetry API, поэтому давайте посмотрим, можно ли настроить Telemetry API для достижения цели фильтрации журналов.
Telemetry определяет, как генерируется телеметрия для рабочих нагрузок в сетке. В нем есть функция AccessLogging.Filter, в которой можно указать выражение в строке для достижения таких целей, как response.code >= 400
или connection.mtls && request.url_path.contains('v1beta3')
.
Согласно официальной документации (ссылка выше), выражение должно быть выражением CEL (подробнее об определении языка можно прочитать здесь.
Таким образом, если мы преобразуем написанные выше фильтры в CEL-выражение, то оно будет выглядеть следующим образом:
expression: “request.url_path != ‘/status’ && request.url_path != ‘/liveness’ && request.url_path != ‘/readiness’”
Если мы обновим файл telemetry.yaml
со следующим содержимым:
apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
name: mesh-default
namespace: istio-system
spec:
accessLogging:
- providers:
- name: Envoy
filter:
expression: "request.url_path != '/status' && request.url_path != '/liveness' && request.url_path != '/readiness'"
And apply it:
И применим его:
kubectl apply -f telemetry.yaml
Тогда istio-proxy sidecar (Envoy) будет регистрировать только те записи, в которых URL-адрес запроса не является /status
, /liveness
или /readiness
.
7. EnvoyFilter
EnvoyFilter предоставляет механизм для кастомизации Envoy. С помощью EnvoyFilter можно изменять значения определенных полей, добавлять специфические фильтры или даже добавлять совершенно новые listeners, кластеры и т.д. Использовать эту возможность следует осторожно, так как неправильная конфигурация может привести к дестабилизации всей сетки. В отличие от других сетевых объектов Istio, EnvoyFilters применяются аддитивно. Для данной рабочей нагрузки в конкретном пространстве имен может существовать любое количество EnvoyFilters. Порядок применения этих EnvoyFilters следующий: все EnvoyFilters в config root namespace, затем все соответствующие EnvoyFilters в workload’s namespace.
Мы можем использовать EnvoyFilter для настройки журналов доступа Envoy, хотя официально "Istio Telemetry API предоставляет первоклассный способ настройки журналов доступа и трассировки. Рекомендуется использовать этот способ".
Тем не менее давайте рассмотрим EnvoyFilter и то, как его использовать.
Для начала удалим созданный нами ранее Telemetry, чтобы отключить журнал доступа:
kubectl delete -f telemetry.yaml
Затем, следуя формату, приведенному в этой документации, мы можем создать файл envoyfilter.yaml
со следующим содержимым:
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: access-log
spec:
configPatches:
- applyTo: NETWORK_FILTER
match:
context: ANY
listener:
filterChain:
filter:
name: "Envoy.filters.network.http_connection_manager"
patch:
operation: MERGE
value:
typed_config:
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager"
access_log:
- name: Envoy.file_access_log
typed_config:
"@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog"
path: /dev/stdout
format: "[%START_TIME%] \"%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%\" %RESPONSE_CODE% %RESPONSE_CODE_DETAILS% \"%RESP(GRPC-STATUS)% %RESP(GRPC-MESSAGE)%\" %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION% %RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% \"%REQ(X-FORWARDED-FOR)%\" \"%REQ(USER-AGENT)%\" \"%REQ(X-REQUEST-ID)%\" \"%REQ(:AUTHORITY)%\" \"%UPSTREAM_HOST%\"\n"
filter:
and_filter:
filters:
- header_filter:
header:
name: :Path
string_match:
exact: /status
invert_match: true
- header_filter:
header:
name: :Path
string_match:
exact: /liveness
invert_match: true
- header_filter:
header:
name: :Path
string_match:
exact: /readiness
invert_match: true
Мы создаем объект вида "EnvoyFilter", и его содержимое выглядит так же, как и конфигурационный файл Envoy.
Если мы применим его:
kubectl apply -f envoyfilter.yaml
Тогда istio-proxy sidecar (Envoy) будет регистрировать только те записи, в которых путь URL запроса не является /status
, /liveness
или /readiness
, как и в случае с Telemetry.
8. Разборка
minikube delete
Заключение
В этой статье:
Мы познакомились с Envoy, servise mesh и Istio.
Затем мы создали локальный кластер Kubernetes и установили в него Istio.
Мы включили журналы доступа Envoy в Istio через Telemetry и поиграли с конфигурациями Envoy, чтобы добиться фильтрации журналов.
Мы также рассмотрели два способа настройки фильтрации журналов для Envoy sidecar в Istio (Telemetry и EnvoyFilter).
Приходите изучать Service mesh в Слёрм. Вы научитесь:
Внедрять эту технологию без костылей в архитектуре.
Выстраивать observability и сократите время на поиск и устранение проблем.
Унифицировать политики прокси Envoy.
Управлять входящим и исходящим трафиком в едином месте, а также тюнить систему.
Посмотреть программу и оставить заявку можно на нашем сайте.