Введение
Мы в Shopify занялись развертыванием Istio в качестве service mesh. В принципе все устраивает, кроме одной вещи: это дорого.
В опубликованных бенчмарках для Istio говорится:
С Istio 1.1 прокси потребляет примерно 0,6 vCPU (виртуальных ядер) на 1000 запросов в секунду.
Для первого региона в service mesh (по 2 прокси с каждой стороны соединения) у нас будет 1200 ядер только для прокси, из расчета один миллион запросов в секунду. Согласно калькулятору стоимости от Google получается примерно $40/месяц/ядро для конфигурации n1-standard-64
, то есть один этот регион будет стоить нам больше 50 тыс. долларов в месяц за 1 млн запросов в секунду.
Айвен Сим (Ivan Sim) наглядно сравнил задержки service mesh в прошлом году и обещал то же самое для памяти и процессора, но не получилось:
Судя по всему, values-istio-test.yaml серьезно увеличит запросы к процессору. Если я все правильно посчитал, нужно примерно 24 процессорных ядра для панели управления и 0,5 ЦП для каждого прокси. У меня столько нету. Я повторю тесты, когда мне выделят больше ресурсов.
Я хотел сам убедиться, насколько показатели Istio схожи с другой service mesh с открытым кодом: Linkerd.
Установка service mesh
Первым делом я установил в кластере SuperGloo:
$ supergloo init
installing supergloo version 0.3.12
using chart uri https://storage.googleapis.com/supergloo-helm/charts/supergloo-0.3.12.tgz
configmap/sidecar-injection-resources created
serviceaccount/supergloo created
serviceaccount/discovery created
serviceaccount/mesh-discovery created
clusterrole.rbac.authorization.k8s.io/discovery created
clusterrole.rbac.authorization.k8s.io/mesh-discovery created
clusterrolebinding.rbac.authorization.k8s.io/supergloo-role-binding created
clusterrolebinding.rbac.authorization.k8s.io/discovery-role-binding created
clusterrolebinding.rbac.authorization.k8s.io/mesh-discovery-role-binding created
deployment.extensions/supergloo created
deployment.extensions/discovery created
deployment.extensions/mesh-discovery created
install successful!
Я использовал SuperGloo, потому что он значительно упрощает начальную загрузку service mesh. Мне почти ничего не пришлось делать. В продакшене мы не используем SuperGloo, но для подобной задачи он подходит идеально. Пришлось применить буквально пару команд на каждую service mesh. Я использовал два кластера для изоляции — по одному для Istio и Linkerd.
Эксперимент проводился на Google Kubernetes Engine. Я использовал Kubernetes 1.12.7-gke.7
и пул нод n1-standard-4
с автоматическим масштабированием нод (минимум 4, максимум 16).
Потом я установил обе service mesh из командной строки.
Сначала Linkerd:
$ supergloo install linkerd --name linkerd
+---------+--------------+---------+---------------------------+
| INSTALL | TYPE | STATUS | DETAILS |
+---------+--------------+---------+---------------------------+
| linkerd | Linkerd Mesh | Pending | enabled: true |
| | | | version: stable-2.3.0 |
| | | | namespace: linkerd |
| | | | mtls enabled: true |
| | | | auto inject enabled: true |
+---------+--------------+---------+---------------------------+
Потом Istio:
$ supergloo install istio --name istio --installation-namespace istio-system --mtls=true --auto-inject=true
+---------+------------+---------+---------------------------+
| INSTALL | TYPE | STATUS | DETAILS |
+---------+------------+---------+---------------------------+
| istio | Istio Mesh | Pending | enabled: true |
| | | | version: 1.0.6 |
| | | | namespace: istio-system |
| | | | mtls enabled: true |
| | | | auto inject enabled: true |
| | | | grafana enabled: true |
| | | | prometheus enabled: true |
| | | | jaeger enabled: true |
+---------+------------+---------+---------------------------+
Crash-loop занял несколько минут, а потом панели управления стабилизировались.
(Примечание. SuperGloo пока поддерживает только Istio 1.0.x. Я повторил эксперимент с Istio 1.1.3, но никакой ощутимой разницы не заметил.)
Настройка автоматического внедрения Istio
Чтобы Istio установил sidecar Envoy, мы используем sidecar-инжектор — MutatingAdmissionWebhook
. Мы не будем говорить о нем в этой статье. Скажу только, что это контроллер, который следит за доступом всех новых pod’ов и динамически добавляет sidecar и initContainer, который отвечает за задачи iptables
.
Мы в Shopify написали свой контроллер доступа для внедрения sidecar-ов, но в этом бенчмарке я взял контроллер, который поставляется с Istio. Контроллер по умолчанию внедряет sidecar-ы, когда в пространстве имен есть ярлык istio-injection: enabled
:
$ kubectl label namespace irs-client-dev istio-injection=enabled
namespace/irs-client-dev labeled
$ kubectl label namespace irs-server-dev istio-injection=enabled
namespace/irs-server-dev labeled
Настройка автоматического внедрения Linkerd
Чтобы настроить внедрение sidecar-ов Linkerd, мы используем аннотации (я добавил их вручную через kubectl edit
):
metadata:
annotations:
linkerd.io/inject: enabled
$ k edit ns irs-server-dev
namespace/irs-server-dev edited
$ k get ns irs-server-dev -o yaml
apiVersion: v1
kind: Namespace
metadata:
annotations:
linkerd.io/inject: enabled
name: irs-server-dev
spec:
finalizers:
- kubernetes
status:
phase: Active
Симулятор отказоустойчивости Istio
Мы сделали симулятор отказоустойчивости Istio, чтобы поэкспериментировать с трафиком, уникальным для Shopify. Нам нужен был инструмент, чтобы создавать произвольную топологию, которая будет представлять определенную часть графа нашего сервиса с динамической настройкой для моделирования конкретных рабочих нагрузок.
Инфраструктура Shopify испытывает большую нагрузку во время флэш-распродаж. При этом Shopify рекомендует продавцам почаще проводить такие распродажи. Крупные клиенты иногда предупреждают о запланированной флэш-распродаже. Другие проводят их неожиданно для нас в любое время дня и ночи.
Мы хотели, чтобы наш симулятор отказоустойчивости моделировал рабочие процессы, которые соответствуют топологиям и рабочим нагрузкам, приводившим к перегрузке инфраструктуры Shopify в прошлом. Основная цель использования service mesh — нам нужна надежность и отказоустойчивость на уровне сети, и нам важно, чтобы service mesh эффективно справлялась с нагрузками, которые раньше нарушали работу сервисов.
В основе симулятора отказоустойчивости лежит рабочий узел, который действует как узел service mesh. Рабочий узел можно настроить статически при запуске или динамически через REST API. Мы используем динамическую настройку рабочих узлов, чтобы создавать рабочие процессы в виде регрессивных тестов.
Вот пример такого процесса:
- Запускаем 10 серверов в качестве
bar
сервиса, который возвращает ответ200/OK
через 100 мс. - Запускаем 10 клиентов — каждый отправляет 100 запросов в секунду к
bar
. - Каждые 10 секунд убираем 1 сервер, мониторим ошибки
5xx
на клиенте.
В конце рабочего процесса мы изучаем логи и метрики и проверяем, пройден ли тест. Так мы узнаем о производительности нашей service mesh и проводим регрессивный тест, чтобы проверить наши предположения об отказоустойчивости.
(Примечание. Мы подумываем открыть исходный код симулятора отказоустойчивости Istio, но еще не готовы к этому.)
Симулятор отказоустойчивости Istio для бенчмарка service mesh
Мы настраиваем несколько рабочих нод симулятора:
irs-client-loadgen
: 3 реплики, которые отправляют по 100 запросов в секунду вirs-client
.irs-client
: 3 реплики, которые получают запрос, ждут 100 мс и перенаправляют запрос наirs-server
.irs-server
: 3 реплики, которые возвращают200/OK
через 100 мс.
С такой конфигурацией мы можем измерить стабильный поток трафика между 9 конечными точками. Sidecar-ы в irs-client-loadgen
и irs-server
получают по 100 запросов в секунду, а irs-client
— 200 (входящие и исходящие).
Мы отслеживаем использование ресурсов через DataDog, потому что у нас нет кластера Prometheus.
Результаты
Панели управления
Сначала мы изучили потребление ЦП.
Панель управления Linkerd ~22 миллиядра
Панель управления Istio: ~750 миллиядер
Панель управления Istio использует примерно в 35 раз больше процессорных ресурсов, чем Linkerd. Конечно, все поставлено по-умолчанию, и много процессорных ресурсов здесь потребляет istio-telemetry (его можно отключить, отказавшись от некоторых функций). Если убрать этот компонент, все равно получается больше 100 миллиядер, то есть в 4 раза больше, чем у Linkerd.
Sidecar прокси
Затем мы проверили использование прокси. Тут должна быть линейная зависимость от количества запросов, но для каждого sidecar-а есть некоторые накладные расходы, которые влияют на кривую.
Linkerd: ~100 миллиядер для irs-client, ~50 миллиядер для irs-client-loadgen
Результаты выглядят логично, ведь прокси client получает в два раза больше трафика, чем прокси loadgen: на каждый исходящий запрос от loadgen у client приходится один входящий и один исходящий.
Istio/Envoy: ~155 миллиядер для irs-client, ~75 миллиядер для irs-client-loadgen
Мы видим похожие результаты для sidecar-ов Istio.
Но в целом прокси Istio/Envoy потребляют примерно на 50% больше процессорных ресурсов, чем Linkerd.
Ту же схему мы видим на стороне сервера:
Linkerd: ~50 миллиядер для irs-server
Istio/Envoy: ~80 миллиядер для irs-server
На стороне сервера sidecar Istio/Envoy потребляет примерно на 60% больше процессорных ресурсов, чем Linkerd.
Заключение
Прокси Istio Envoy потребляет на 50+% больше ЦП, чем Linkerd, на нашей смоделированной рабочей нагрузке. Панель управления Linkerd потребляет гораздо меньше ресурсов, чем Istio, особенно это касается основных компонентов.
Мы все еще думаем над тем, как сократить эти издержки. Если у вас есть идеи, поделитесь!