Назад до мікросервісів разом із Istio. Частина 1

Назад до мікросервісів разом із Istio. Частина 1

Прим. перев.: Service mesh'і безперечно стали актуальним рішенням у сучасній інфраструктурі для додатків, що наслідують мікросервісну архітектуру. Хоча Istio може бути на слуху у багатьох DevOps-інженерів, це досить новий продукт, який, будучи комплексним у сенсі можливостей, може потребувати значного часу для знайомства. Німецький інженер Rinor Maloku, який відповідає за хмарні обчислення для великих клієнтів у телекомунікаційній компанії Orange Networks, написав чудовий цикл матеріалів, що дозволяють досить швидко і глибоко зануритися в Istio. Починає він свою розповідь з того, що взагалі вміє Istio і як на це можна швидко подивитися на власні очі.

Істіо — Open Source-проект, розроблений за співпраці команд з Google, IBM та Lyft. Він вирішує складності, що виникають у додатках, заснованих на мікросервісах, наприклад, такі як:

  • Управління трафіком: таймаути, повторні спроби, балансування навантаження;
  • Безпека: аутентифікація та авторизація кінцевого користувача;
  • Спостережуваність: трасування, моніторинг, логування.

Всі вони можуть бути вирішені на рівні програми, проте після цього ваші послуги перестануть бути «мікро». Всі додаткові зусилля щодо вирішення цих проблем є зайвою витратою ресурсів компанії, які могли б використовуватися безпосередньо для бізнес-цінностей. Розглянемо приклад:

Менеджер проектів: Як довго додавати можливість зворотного зв'язку?
Розробник: Два спринти.

МП: Що?.. Адже це всього лише CRUD!
Р: Зробити CRUD - проста частина завдання, але нам ще потрібно аутентифікувати та авторизувати користувачів та сервіси. Оскільки мережа ненадійна, потрібно буде реалізувати повторні запити, а також патерн circuit breaker у клієнтах. Ще щоб переконатися, що вся система не впала, знадобляться таймаути і перегородки (Докладніше про обох згаданих патернах див. далі в статті — прим. перекл.), А для того, щоб виявляти проблеми, знадобиться моніторинг, трасування, […]

МП: Ох, давайте тоді просто вставимо цю фічу в сервіс Product.

Думаю, ідея зрозуміла: обсяг кроків та зусиль, які потрібні для додавання одного сервісу, величезний. У цій статті ми розглянемо, як Istio усуває всі згадані вище складності (що не є цільовими для бізнес-логіки) із сервісів.

Назад до мікросервісів разом із Istio. Частина 1

Примітка: Стаття передбачає, що у вас є практичні знання з Kubernetes В іншому випадку рекомендую прочитати моє введення в Kubernetes і лише після цього продовжити читання цього матеріалу.

Ідея Istio

У світі без Istio один сервіс робить прямі запити до іншого, а у разі збою сервіс повинен сам обробити його: зробити нову спробу, передбачити тайм-аут, відкрити circuit breaker і т.п.

Назад до мікросервісів разом із Istio. Частина 1
Мережевий трафік у Kubernetes

Istio пропонує спеціалізоване рішення, повністю відокремлене від сервісів і функціонує шляхом втручання в мережеву взаємодію. І таким чином воно реалізує:

  • Відмовостійкість: спираючись на код статусу у відповіді, воно розуміє, чи стався збій у запиті, і виконує його повторно.
  • Канаркові викати: перенаправляє на нову версію сервісу лише фіксовану відсотком кількість запитів.
  • Моніторинг та метрики: за який час відповів сервіс?
  • Трасування та спостереження: додає спеціальні заголовки в кожен запит і виконує їх трасування в кластері.
  • Безпека: витягує JWT-токен, аутентифікує та авторизує користувачів.

Це лише деякі з можливостей (справді лише деякі!), щоб зацікавити вас. А тепер давайте поринемо в технічні подробиці!

Архітектура Istio

Istio перехоплює весь мережевий трафік і застосовує до нього набір правил, вставляючи в кожен pod розумний проксі у вигляді sidecar-контейнера. Проксі, які активують усі можливості, утворюють собою Площина даних, і вони можуть динамічно налаштовуватися за допомогою Лінія управління.

Площина даних

Проксі, що вставляються в pod'и, дозволяють Istio з легкістю домогтися відповідності потрібним нам вимогам. Наприклад, перевіримо функції повторних спроб та circuit breaker.

Назад до мікросервісів разом із Istio. Частина 1
Як retries та circuit breaking реалізовані у Envoy

Підсумуємо:

  1. Посланець (Мова про проксі, що знаходиться в sidecar-контейнері, який поширюється і як окремий продукт - прим. перев.) надсилає запит першому екземпляру сервісу B і відбувається збій.
  2. Envoy Sidecar робить повторну спробу (Retry). (1)
  3. Запит зі збоєм повертається проксі, що викликав його.
  4. Так відкривається Circuit Breaker та відбувається виклик наступного сервісу для наступних запитів. (2)

Це означає, що вам не доведеться використовувати чергову бібліотеку Retry, не доведеться робити свою реалізацію Circuit Breaking і Service Discovery мовою програмування X, Y або Z. Все це і багато іншого доступно з коробки в Istio і не вимагає ніяких змін у коді.

Чудово! Тепер ви можете захотіти вирушити у вояж із Istio, але все ще є якісь сумніви, відкриті запитання. Якщо це універсальне рішення на всі випадки в житті, то у вас виникає закономірна підозра: адже всі такі рішення насправді не підходять ні для якого випадку.

І ось нарешті ви запитаєте: "Вона налаштовується?"

Тепер ви готові до морської подорожі - і давайте познайомимося з Control Plane.

Лінія управління

Він складається з трьох компонентів: Пілот, Змішувач и Цитадель, які спільними зусиллями налаштовують Envoy'и для маршрутизації трафіку, застосовують політики і збирають телеметричні дані. Схематично все це виглядає так:

Назад до мікросервісів разом із Istio. Частина 1
Взаємодія Control Plane із Data Plane

Envoy'і (тобто data plane) налаштовані за допомогою Kubernetes CRD (Custom Resource Definitions), визначеними Istio та спеціально призначеними для цієї мети. Для вас це означає, що вони є черговим ресурсом в Kubernetes зі знайомим синтаксисом. Після створення цей ресурс буде підібраний control plane'ом і застосований до Envoy'ів.

Відношення сервісів до Istio

Ми описали ставлення Istio до сервісів, але з протилежне: як сервіси ставляться до Istio?

Чесно кажучи, про присутність Istio сервісів відомо так само добре, як рибам — про воду, коли вони запитують себе: «Що таке вода?».

Назад до мікросервісів разом із Istio. Частина 1
Ілюстрація Victoria Dimitrakopoulos: - Як вам вода? - Що взагалі таке вода?

Таким чином, ви можете взяти робочий кластер і після деплою компонентів Istio сервіси, що знаходяться в ньому, продовжать працювати, а після усунення цих компонентів знову буде добре. Зрозуміло, що при цьому ви втратите можливості, що надаються Istio.

Достатньо теорії — давайте перенесемо це знання у практику!

Istio на практиці

Istio вимагає кластера Kubernetes, у якому як мінімум доступні 4 vCPU та 8 Гб RAM. Щоб швидко підняти кластер і дотримуватися вказівок зі статті, рекомендую скористатися Google Cloud Platform, яка пропонує новим користувачам безкоштовні $300.

Після створення кластера та налаштування доступу до Kubernetes через консольну утиліту можна встановити Istio через пакетний менеджер Helm.

Установка Helm

Встановіть клієнт Helm на своєму комп'ютері, як розповідають у офіційної документації. Його ми будемо використовувати для створення шаблонів для встановлення Istio в наступному розділі.

Встановлення Istio

Завантажте ресурси Istio з останнього релізу (оригінальне авторське посилання на версію 1.0.5 змінено на актуальну, тобто 1.0.6 - прим. перекл.), вийміть вміст в одну директорію, яку я буду надалі називати [istio-resources].

Для простоти ідентифікації ресурсів Istio створіть у кластері K8s простір імен istio-system:

$ kubectl create namespace istio-system

Завершіть встановлення, перейшовши до каталогу [istio-resources] та виконавши команду:

$ helm template install/kubernetes/helm/istio 
  --set global.mtls.enabled=false 
  --set tracing.enabled=true 
  --set kiali.enabled=true 
  --set grafana.enabled=true 
  --namespace istio-system > istio.yaml

Ця команда виведе ключові компоненти Istio у файл istio.yaml. Ми змінили стандартний шаблон під себе, вказавши такі параметри:

  • global.mtls.enabled встановлено в false (тобто mTLS-автентифікація відключена - прим перекл.)щоб спростити наш процес знайомства;
  • tracing.enabled включає трасування запитів за допомогою Jaeger;
  • kiali.enabled встановлює Kiali у кластер для візуалізації сервісів та трафіку;
  • grafana.enabled встановлює Grafana для візуалізації зібраних метриків.

Застосуємо згенеровані ресурси командою:

$ kubectl apply -f istio.yaml

Встановлення Istio у кластер завершено! Дочекайтеся, поки всі pod'и у просторі імен istio-system виявляться в стані Running або Completed, виконавши команду нижче:

$ kubectl get pods -n istio-system

Тепер ми готові продовжити в наступному розділі, де піднімемо та запустимо програму.

Архітектура програми Sentiment Analysis

Скористаємося прикладом мікросервісної програми Sentiment Analysis, використаної у вже згаданій статті-введення в Kubernetes. Воно досить складне, щоб показати можливості Istio на практиці.

Додаток складається з чотирьох мікросервісів:

  1. Сервіс SA-Frontend, який обслуговує фронтенд програми на Reactjs;
  2. Сервіс SA-WebApp, що обслуговує запити Sentiment Analysis;
  3. Сервіс SA-Logic, який виконує сам сентимент-аналіз;
  4. Сервіс SA-Feedback, що отримує від користувачів зворотний зв'язок про точність проведеного аналізу.

Назад до мікросервісів разом із Istio. Частина 1

На цій схемі крім сервісів ми бачимо також Ingress Controller, який у Kubernetes маршрутизує запити на відповідні сервіси. У Istio використовується подібна концепція в рамках Ingress Gateway, подробиці про які йдуть.

Запуск програми з проксі від Istio

Для подальших операцій, що згадуються у статті, склонуйте собі репозиторій istio-mastery. У ньому містяться додаток та маніфести для Kubernetes та Istio.

Вставка sidecar'ів

Вставка може бути зроблена автоматично або вручну. Для автоматичної вставки sidecar-контейнерів потрібно виставити простору імен лейбл istio-injection=enabled, Що робиться наступною командою:

$ kubectl label namespace default istio-injection=enabled
namespace/default labeled

Тепер кожен pod, який буде розгортатися у просторі за промовчанням (default) отримає свій sidecar-контейнер. Щоб переконатися в цьому, давайте задеплоїмо тестовий додаток, перейшовши до кореневого каталогу репозиторію. [istio-mastery] та виконавши наступну команду:

$ kubectl apply -f resource-manifests/kube
persistentvolumeclaim/sqlite-pvc created
deployment.extensions/sa-feedback created
service/sa-feedback created
deployment.extensions/sa-frontend created
service/sa-frontend created
deployment.extensions/sa-logic created
service/sa-logic created
deployment.extensions/sa-web-app created
service/sa-web-app created

Розгорнувши сервіси, перевіримо, що у pod'ів по два контейнери (з самим сервісом та його sidecar'ом), виконавши команду kubectl get pods і переконавшись, що під стовпцем READY вказано значення 2/2, що символізує, що обидва контейнери запущені:

$ kubectl get pods
NAME                           READY     STATUS    RESTARTS   AGE
sa-feedback-55f5dc4d9c-c9wfv   2/2       Running   0          12m
sa-frontend-558f8986-hhkj9     2/2       Running   0          12m
sa-logic-568498cb4d-2sjwj      2/2       Running   0          12m
sa-logic-568498cb4d-p4f8c      2/2       Running   0          12m
sa-web-app-599cf47c7c-s7cvd    2/2       Running   0          12m

Візуально це видається так:

Назад до мікросервісів разом із Istio. Частина 1
Проксі Envoy в одному з pod'ів

Тепер, коли програма піднята і функціонує, нам потрібно буде дозволити вхідному трафіку приходити в програму.

Ingress Gateway

Найкраща практика досягти цього (дозволити трафік у кластері) — через Ingress Gateway в Istio, що знаходиться біля «кордону» кластера і дозволяє включати для вхідного трафіку такі функції Istio, як маршрутизація, балансування навантаження, безпека та моніторинг.

Компонент Ingress Gateway та сервіс, який прокидає його назовні, були встановлені в кластер під час інсталяції Istio. Щоб дізнатися зовнішню IP-адресу сервісу, виконайте:

$ kubectl get svc -n istio-system -l istio=ingressgateway
NAME                   TYPE           CLUSTER-IP     EXTERNAL-IP
istio-ingressgateway   LoadBalancer   10.0.132.127   13.93.30.120

Ми будемо звертатися до додатку з цього IP і далі (я посилатимуся на нього як EXTERNAL-IP), тому для зручності запишемо значення в змінну:

$ EXTERNAL_IP=$(kubectl get svc -n istio-system 
  -l app=istio-ingressgateway 
  -o jsonpath='{.items[0].status.loadBalancer.ingress[0].ip}')

Якщо спробуєте зараз зайти цей IP через браузер, то отримаєте помилку Service Unavailable, т.к. за замовчуванням Istio блокує весь вхідний трафік, доки не визначено Gateway.

Ресурс Gateway

Gateway - це CRD (Custom Resource Definition) у Kubernetes, який визначається після встановлення Istio в кластері і активує можливість вказувати порти, протокол і хости, для яких ми хочемо дозволити вхідний трафік.

У нашому випадку ми хочемо дозволити HTTP-трафік на 80 порт для всіх хостів. Завдання реалізується наступним визначенням (http-gateway.yaml):

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: http-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
- "*"

Така конфігурація не потребує пояснень за винятком селектора istio: ingressgateway. За допомогою цього селектора ми можемо вказати, який Ingress Gateway застосувати конфігурацію. У нашому випадку таким є контролер Ingress Gateway, який за умовчанням встановлено в Istio.

Конфігурація застосовується викликом наступної команди:

$ kubectl apply -f resource-manifests/istio/http-gateway.yaml gateway.networking.istio.io/http-gateway created

Тепер шлюз дозволяє доступу до порту 80, але не має уявлення про те, куди маршрутизувати запити. Для цього знадобляться Віртуальні послуги.

Ресурс VirtualService

VirtualService вказує Ingress Gateway'ю, як маршрутизувати запити, які дозволені всередині кластера.

Запити до нашої програми, що надходять через http-gateway, повинні бути надіслані в сервіси sa-frontend, sa-web-app та sa-feedback:

Назад до мікросервісів разом із Istio. Частина 1
Маршрути, які необхідно налаштувати з VirtualServices

Розглянемо запити, які мають надсилатися на SA-Frontend:

  • Точний збіг шляхом / має відправлятися до SA-Frontend для отримання index.html;
  • Шляхи із префіксом /static/* повинні відправлятися до SA-Frontend для отримання статичних файлів, що використовуються у фронтенді, таких як CSS та JavaScript;
  • Шляхи, що підпадають під регулярний вираз '^.*.(ico|png|jpg)$', повинні вирушати SA-Frontend, т.к. це зображення, що відображаються на сторінці.

Реалізація досягається наступною конфігурацією (sa-virtualservice-external.yaml):

kind: VirtualService
metadata:
  name: sa-external-services
spec:
  hosts:
  - "*"
  gateways:
  - http-gateway                      # 1
  http:
  - match:
    - uri:
        exact: /
    - uri:
        exact: /callback
    - uri:
        prefix: /static
    - uri:
        regex: '^.*.(ico|png|jpg)$'
    route:
    - destination:
        host: sa-frontend             # 2
        port:
number: 80

Важливі моменти:

  1. Цей VirtualService відноситься до запитів, що надходять через http-gateway;
  2. В destination визначається сервіс, куди надсилаються запити.

Примітка: Конфігурація вище зберігається у файлі sa-virtualservice-external.yaml, який також містить налаштування для маршрутизації в SA-WebApp та SA-Feedback, але був скорочений тут у статті для лаконічності.

Застосуємо VirtualService викликом:

$ kubectl apply -f resource-manifests/istio/sa-virtualservice-external.yaml
virtualservice.networking.istio.io/sa-external-services created

Примітка: Коли ми застосовуємо ресурси Istio, Kubernetes API Server створює подію, яку отримує Istio Control Plane, і вже після цього нова конфігурація застосовується до проксі-серверів Envoy кожного pod'а. А контролер Ingress Gateway є ще одним Envoy, налаштованим в Control Plane. Все це на схемі виглядає так:

Назад до мікросервісів разом із Istio. Частина 1
Конфігурація Istio-IngressGateway для маршрутизації запитів

Додаток Sentiment Analysis став доступним за http://{EXTERNAL-IP}/. Не хвилюйтесь, якщо ви отримуєте статус Not Found: іноді потрібно трохи більше часу для того, щоб конфігурація набула чинності і кеші Envoy оновилися.

Перед тим, як продовжити, попрацюйте з додатком, щоб згенерувати трафік (його наявність необхідна наочності у наступних діях — прим. перекл.).

Kiali: спостережуваність

Щоб потрапити до адміністративного інтерфейсу Kiali, виконайте таку команду:

$ kubectl port-forward 
    $(kubectl get pod -n istio-system -l app=kiali 
    -o jsonpath='{.items[0].metadata.name}') 
    -n istio-system 20001

… і відкрийте http://localhost:20001/, залогінившись під admin/admin. Тут ви знайдете безліч корисних можливостей, наприклад, для перевірки конфігурації компонентів Istio, візуалізації сервісів за інформацією, зібраною під час перехоплення мережевих запитів, отримання відповідей на запитання «Хто до кого звертається?», «Яка версія сервісу виникає збої?» і т.п. Загалом, вивчіть можливості Kiali перед тим, як рухатися далі – візуалізації метрик з Grafana.

Назад до мікросервісів разом із Istio. Частина 1

Grafana: візуалізація метрик

Зібрані в Istio метрики потрапляють у Prometheus та візуалізуються з Grafana. Щоб потрапити до адміністративного інтерфейсу Grafana, виконайте нижченаведену команду, після чого відкрийте http://localhost:3000/:

$ kubectl -n istio-system port-forward 
    $(kubectl -n istio-system get pod -l app=grafana 
    -o jsonpath={.items[0].metadata.name}) 3000

Натисніть на меню Головна зліва зверху і вибравши Istio Service Dashboard у лівому верхньому кутку, почніть із сервісу sa-web-app, щоб подивитися на зібрані метрики:

Назад до мікросервісів разом із Istio. Частина 1

Тут на нас чекає порожня і зовсім нудна вистава — керівництво ніколи таке не схвалить. Давайте створимо невелике навантаження наступною командою:

$ while true; do 
    curl -i http://$EXTERNAL_IP/sentiment 
    -H "Content-type: application/json" 
    -d '{"sentence": "I love yogobella"}'; 
    sleep .8; done

Ось тепер у нас набагато більш симпатичні графіки, а на додаток до них — чудові інструменти Prometheus для моніторингу та Grafana для візуалізації метрик, що дозволять нам дізнатися про продуктивність, стан здоров'я, поліпшення/деградацію в роботі сервісів протягом часу.

Зрештою, подивимося на трасування запитів у сервісах.

Jaeger : трасування

Трасування нам знадобиться, тому що чим більше у нас сервісів, тим складніше дістатися причини збою. Подивимося на простий випадок із картинки нижче:

Назад до мікросервісів разом із Istio. Частина 1
Типовий приклад випадкового невдалого запиту

Запит приходить, падає. у чому причина? Перший сервіс? Чи другий? Винятки є в обох — подивимося на логи кожного. Як часто ви ловили себе за таким заняттям? Наша робота більше схожа на детективів програмного забезпечення, а не на розробників...

Це поширена проблема в мікросервісах і вирішується вона розподіленими системами трасування, у яких послуги передають один одному унікальний заголовок, після чого ця інформація перенаправляється в систему трасування, де вона зіставляється з даними запиту. Ось ілюстрація:

Назад до мікросервісів разом із Istio. Частина 1
Для ідентифікації запиту використовується TraceId

В Istio використовується Jaeger Tracer, який реалізує незалежний від вендорів фреймворк OpenTracing API. Отримати доступ до інтерфейсу користувача Jaeger можна наступною командою:

$ kubectl port-forward -n istio-system 
    $(kubectl get pod -n istio-system -l app=jaeger 
    -o jsonpath='{.items[0].metadata.name}') 16686

Тепер зайдіть на http://localhost:16686/ та виберіть сервіс sa-web-app. Якщо сервіс не показаний у меню, що випадає, проявіть/згенеруйте активність на сторінці та оновіть інтерфейс. Після цього натисніть кнопку Find Traces, яка покаже останні трейси - виберіть будь-який - здасться деталізована інформація по всіх трейсах:

Назад до мікросервісів разом із Istio. Частина 1

Цей трейс показує:

  1. Запит приходить у istio-ingressgateway (це перша взаємодія з одним із сервісів, і для запиту генерується Trace ID), після чого шлюз надсилає запит у сервіс sa-web-app.
  2. У сервісі sa-web-app запит підхоплюється Envoy sidecar'ом, створюється «дитина» в span'і (тому ми бачимо її в трейсах) і перенаправляється в контейнер sa-web-app. (Проліт - логічна одиниця роботи в Jaeger, що має назву, час початок операції та її тривалість. Span'и можуть бути вкладеними та впорядкованими. Орієнтований ациклічний граф із span'ів утворює trace. - прим. перев.)
  3. Тут запит обробляється методом sentimentAnalysis. Ці трейси згенеровані додатком, тобто. їм знадобилися зміни у коді.
  4. З цього моменту ініціюється POST-запит у sa-logic. Trace ID повинен бути прокинутий з sa-web-app.
  5. ...

Примітка: На 4 кроці програма повинна побачити заголовки, згенеровані Istio, і передати їх у наступні запити, як показано на зображенні нижче:

Назад до мікросервісів разом із Istio. Частина 1
(A) За прокидання заголовків відповідає Istio; (B) За заголовки відповідають послуги

Istio виконує основну роботу, т.к. генерує заголовки для вхідних запитів, створює нові span'и в кожному sidecare'і та прокидає їх. Однак без роботи із заголовками всередині сервісів повний шлях трасування запиту буде втрачено.

Необхідно враховувати (прокидати) такі заголовки:

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

Це нескладне завдання, проте для спрощення її реалізації вже існує безліч бібліотек — наприклад, у сервісі sa-web-app клієнт RestTemplate прокидає ці заголовки, якщо просто додати бібліотеки Jaeger та OpenTracing у його залежності.

Зауважте, що програма Sentiment Analysis демонструє реалізації на Flask, Spring та ASP.NET Core.

Тепер, коли стало зрозуміло, що ми отримуємо з коробки (або майже «з коробки»), розглянемо питання маршрутизації, що тонко налаштовується, управління мережевим трафіком, безпеки тощо!

Прим. перев.: про це читайте в наступній частині матеріалів по Istio від Rinor Maloku, переклади яких відбудуться у нашому блозі найближчим часом. ОНОВЛЕННЯ (14 березня): Друга частина вже опубліковано.

PS від перекладача

Читайте також у нашому блозі:

Джерело: habr.com

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