Istio- одна из реализацией концепии Service Mesh, позволяющая реализовать Service Discovery, Load Balancing, контроль над трафиком, canary rollouts и blue-green deployments, мониторить трафик между приложениями.
Мы будем использовать Istio в AWS Elastic Kubernetes Service для мониторинга трафика, в роли API gateway, разграничения трафика и, возможно, для реализации различных deployment strategies.
В этом посте рассмотрим что такое Service mesh вообще, общую архитектуру и компоненты Istio, его установку и настройку тестового приложения.
Перед тем, как рассматривать Istio – давайте разберёмся, что такое Service Mesh.
Содержание
Service Mesh
По сути, это менеджер прокси-сервисов, таких как NGINX, HAProxy или Envoy, система, работающая на Layer 7 модели OSI и позволяющая динамически управлять трафиком и настраивать коммуникацию между приложениями.
Service mesh занимается обнаружением новых сервисов/приложений, балансировкой нагрузки, аутентификацией и шифрованием трафика.
Для контроля трафика в service mesh для каждого приложения, или в случае с Kubernetes – для каждого пода, запускается прокси-сервис называемый sidecar, и эти sidecar-сервисы проксируют и управляют трафиком.
Вместе эти sidecar-контейнеры представляют собой Data Plane.
Для их конфигурирования и управления существует другая группа процессов или сервисов, называемых Control Plane. Они занимаются обнаружением новых приложений, обеспечивают управление ключами шифрования, сбором метрик и т.д.
Схематично сервис меш можно отобразить так:
Среди Service mesh решений можно выделить:
Почитать по теме:
- Что такое Service Mesh?
- Service Mesh: что нужно знать каждому Software Engineer о самой хайповой технологии
- Что такое service mesh и почему он мне нужен [для облачного приложения с микросервисами]?
- Service mesh — это всё ещё сложно
Архитектура Istio
Итак, Istio как service mesh состоит из двух основных частей – Data plane и Control plane:
- Data plane (“уровень данных”): состоит из набора прокси-сервисов, sidecar-контейнеров в каждом поде, для которых использует расширенную версию прокси-сервера Envoy. Эти сайдакры связывают и контролируют трафик между приложениями, собирают и отправляют метрики
- Control plane (“уровень управления”): управляет и настраивает сайдкары, управляет метриками, TLS-сертификатами
В целом схема Istio выглядит так:
Или другая схема:
Control Plane
Istio включает в себя четыре основных компонента:
- Pilot: центральный контроллер, отвечающий за коммуникацию с сайдкарами используя Envoy API. Он считывает правила, описанные в манифестах istio, и отправляет их в Envoy для настройки прокси-сервисов. Отвечает за service discovery, управление трафиком, роутинг, устойчивостью сети с помощью таймаутов и circuit breaking.
- Citadel: Identity and Access management – шифрование трафика, аутентификация сервисов и пользователей, управление ключами шифрования. См. Istio Security.
- Galley: управление конфигурацией – валдиация, обработка новых настроек, отправка их остальным компонентам системы
- Mixer: мониторинг, метрики, логи, контроль трафика
В версии 1.9.1 все они, кроме Pilot, который работает в отдельном Docker-контейнере, объединены в отдельный бинарный файл istiod
, плюс Ingress и Egress Controllers – контроллеры входящего и исходящего трафика
Data Plane
Состоит из sidecar-контейнеров, которые в Kubernetes запускаются внутри подов через kube-inject, см. Installing the Sidecar.
Контейнеры являются инстансами Envoy-прокси, которые позволяют:
- Dynamic service discovery
- Load balancing
- TLS termination
- HTTP/2 and gRPC proxies
- Circuit breakers
- Health checks
- Deployment staged rollouts
- Rich metrics
Почитать по теме:
Модель сети Istio
Перед тем, как приступать к запуску и настройке приложения и Istio – кратко рассмотрим ресурсы, которые участвуют в управлении трафиком.
При установке Istio создаёт ресурс Ingress Gateway (и Egress Gateway, если было определено при установке) – новый объект в Kubernetes, который описывается в Kubernetes CRD при установке Istio.
В AWS при настройках по-умолчанию Ingress Gateway создаёт AWS Classic LoadBalancer, т.к. Ingress Gateway является Kubernetes-объектом Ingress с типом LoadBalancer.
“За” Ingress Gateway создаётся ресурс Gateway, который также описывается в Kubernetes CRD во время установки Istio и описывает хосты и порты, на которые будет отправляться трафик через этот Gateway.
Далее следует ещё один ресурс – VirtualService, который описывает маршрутизацию трафика, прошёдшего через Gateway, и отправляет его на Kubernetes Services следуя заданным Destination rules.
Ingress Gateway состоит из двух компонентов – Kubernetes Pod с инстансом Envoy, который управляет входящим трафиком, и Kubernetes Ingress, который принимает подключения.
В свою очередь Gateway и VirtualService управляют конфигурацией Envoy, который является Ingress Gateway controller.
Т.е. в целом схема прохождения пакета выглядит так:
- пакет попадает на порт внешнего LoadBalancer, который шлёт его на порт Kubernetes WorkerNode
- там пакет попадает к Istio IngressGateway Service
- перенаправляется на Istio IngressGateway Pod
- этот под настраивается через Gateway и VirtualService
- Gateway описывает порты, протоколы, SSL-сертификаты
- VirtualService описывает роутинг пакета к нужному Kubernetes Service нашего приложения
- Istio IngressGateway Pod отправляет пакет на Service приложения
- сервис приложения отправляет пакет на под с приложением
Почитать по теме:
Запуск Istio в Kubernetes
Istio поддерживает различные Deployment models. В нашем случае используется один EKS-кластер, а поды в нём работают в общей VPC сети.
Кроме того, Istio поддерживает различные Config Profiles, из которых нам может быть интересен default – установка istiod
и istio-ingressgateway
, demo – аналогично, но с запуском istio-egressgateway
и preview – для исследования новых возможностей, не включенных пока в основной релиз Istio.
Установить Istio можно разными способами – с помощью istioctl
из манифест-файлов, из Helm-чарта, либо с помощью Ansible.
Также, стоит обратить внимание на Mutual TLS (mTLS) – см. Permissive mode. Если кратко, то по-умолчанию Istio устанавливается в Permissive mode, что позволяет уже существующим приложениям продолжать коммуникацию используя plaintext-трафик. При этом новые подключения через Envoy-контейнеры, sidecars, уже будут выполняться с TLS-шированием.
Пока выполним установку руками, а на Дев и Прод кластера Кубера скорее всего будем инсталить через Helm.
См. Install with Istioctl и Installation Guides.
Загружаем Istio:
[simterm]
$ curl -L https://istio.io/downloadIstio | sh - $ cd istio-1.9.1/
[/simterm]
istioctl
использует файл ~/.kube/config
, который можно переопределить с помощью --kubeconfig
или указать нужный контекст через --context
.
В загруженном только что каталоге он находится в папке bin
– добавляем его в $PATH
:
[simterm]
$ export PATH=$PWD/bin:$PATH
[/simterm]
Проверяем:
[simterm]
$ istioctl version no running Istio pods in "istio-system" 1.9.1
[/simterm]
Генерируем kubeconfig для нового тестового кластера (для AWS EKS, если используется Minikube – он при старте сгенерирует конфиг сам):
[simterm]
$ aws eks update-kubeconfig --name bttrm-eks-test-1-18 --alias iam-bttrm-eks-root-role-kubectl@bttrm-eks-test-1-18 Added new context iam-bttrm-eks-root-role-kubectl@bttrm-eks-test-1-18 to /home/setevoy/.kube/config
[/simterm]
Устанавливаем Istio с профилем default:
[simterm]
$ istioctl install --set profile=default -y ✔ Istio core installed ✔ Istiod installed ✔ Ingress gateways installed ✔ Installation complete
[/simterm]
Проверяем версию ещё раз:
[simterm]
$ istioctl version client version: 1.9.1 control plane version: 1.9.1 data plane version: 1.9.1 (1 proxies)
[/simterm]
Поды Istio:
[simterm]
$ kubectl -n istio-system get pod NAME READY STATUS RESTARTS AGE istio-ingressgateway-d45fb4b48-jsz9z 1/1 Running 0 64s istiod-7475457497-6xskm 1/1 Running 0 77s
[/simterm]
Далее задеплоим тестовое приложение, и настроим роутинг через Istio Ingress Gateway.
Тестовое приложение
Не будем использовать Bookinfo приложение из Istio Gettings Started, а напишем свой велосипед создадим свой Namespace, Deployment с одним подом с NGINX, и к нему Service – эмулируем миграцию уже имеющихся у нас приложений под управление Istio.
Кроме того, сейчас специально не будем настраваить добавление sidecar-контейнеров в приложение – вернёмся к этому вопросу чуть позже.
Манифест сейчас выглядит так:
--- apiVersion: v1 kind: Namespace metadata: name: test-namespace --- apiVersion: apps/v1 kind: Deployment metadata: name: test-deployment namespace: test-namespace labels: app: test version: v1 spec: replicas: 1 selector: matchLabels: app: test template: metadata: labels: app: test version: v1 spec: containers: - name: web image: nginx ports: - containerPort: 80 resources: requests: cpu: 100m memory: 100Mi readinessProbe: httpGet: path: / port: 80 nodeSelector: role: common-workers --- apiVersion: v1 kind: Service metadata: name: test-svc namespace: test-namespace spec: selector: app: test ports: - name: http protocol: TCP port: 80 targetPort: 80
Рекомендуется использовать тег version
в приложениях – позже это позволит использовать canary и blue-green deployment, см . How To Do Canary Deployments With Istio and Kubernetes и Traffic Management.
Деплоим:
[simterm]
$ kubectl apply -f test-istio.yaml namespace/test-namespace created deployment.apps/test-deployment created service/test-svc created
[/simterm]
Проверим контейнеры пода:
[simterm]
$ kk -n tkk -n test-namespace get pod -o jsonpath={.items[*].spec.containers[*].name} web
[/simterm]
Один контейнер, как и описывали.
Через kubectl port-forward
подключимся к сервису:
[simterm]
$ kubectl -n test-namespace port-forward services/test-svc 8080:80 Forwarding from 127.0.0.1:8080 -> 80 Forwarding from [::1]:8080 -> 80
[/simterm]
И проверим работу приложения в поде:
[simterm]
$ curl localhost:8080 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> ...
[/simterm]
Окей – тут всё, кроме Envoy-контейнеров, работает.
Istio Ingress Gateway
Итак, у нас есть Istio Ingress Gateway созданный во время установки Istio, который представляет собой AWS Classic LoadBalancer:
Если обратиться к его URL сейчас – то получим ошибку подключения т.к. шлюз не знает, куда ему направлять трафик:
[simterm]
$ curl a6f***037.eu-west-3.elb.amazonaws.com curl: (52) Empty reply from server
[/simterm]
Что бы разрешить трафик – опишем Istio Gateway.
Gateway
Gateway описывает настройки Istio Ingress Gateway – на какие порты и какой принимать трафик. Плюс тут же можно выполнить SSL termination (но в нашем случае это будет AWS LoadBalancer).
Добавляем в наш файл test-istio.yaml
ресурс Gateway:
--- apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: test-gateway namespace: test-namespace spec: selector: istio: ingressgateway servers: - port: number: 80 name: http protocol: HTTP hosts: - "*"
Тут в spec.selector.istio
указываем Istio Ingress Gateway, к которому будет применён этот манифест.
Обратите внимание, что наше приложение живёт в отдельном Namespace, поэтому и Gateway и VirtualService (см. ниже) создаём в нём.
Создаём его:
[simterm]
$ kubectl apply -f test-istio.yaml namespace/test-namespace unchanged deployment.apps/test-deployment unchanged service/test-svc unchanged gateway.networking.istio.io/test-gateway created
[/simterm]
Проверяем:
[simterm]
$ kubectl -n test-namespace get gateways NAME AGE test-gateway 17s
[/simterm]
VirtualService
Теперь добавляем VirtualService, в котором опишем “бекенд”, на который требуется слать трафик.
В роли бекенда тут выступает уже обычный Service нашего приложения – test-svc:
[simterm]
$ kk -n test-namespace get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE test-svc NodePort 172.20.195.107 <none> 80:31581/TCP 15h
[/simterm]
Описываем VirtualService в том же namespace, где ижвёт наше приложение и в котором создавали Gateway:
--- apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: test-virtualservice namespace: test-namespace spec: hosts: - "*" gateways: - test-gateway http: - match: - uri: prefix: / route: - destination: host: test-svc port: number: 80
Создаём его:
[simterm]
$ kubectl apply -f test-istio.yaml namespace/test-namespace unchanged deployment.apps/test-deployment unchanged service/test-svc unchanged gateway.networking.istio.io/test-gateway unchanged virtualservice.networking.istio.io/test-virtualservice created
[/simterm]
Проверяем:
[simterm]
$ kubectl -n test-namespace get virtualservice NAME GATEWAYS HOSTS AGE test-virtualservice [test-gateway] [*] 42s
[/simterm]
И ещё раз выполняем запрос к URL лоад-балансера Istio Ingress Gateway:
[simterm]
$ curl a6f***037.eu-west-3.elb.amazonaws.com <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> ...
[/simterm]
Заметьте, что мы всё ещё работаем без Envoy-прокси в поде нашего приложения:
[simterm]
$ kk -n tkk -n test-namespace get pod -o jsonpath={.items[*].spec.containers[*].name} web
[/simterm]
Чуть позже разберёмся почему всё работает.
Kiali – наблюдение за трафиком
К Istio имеется множество аддонов – Prometheus для сбора метрик, Grafana для визуализации графиков, Jaeger для трассировки вызовов, и Kiali для построения карты сети и отображения карты трафика, см. Integrations.
Устанавливаем все аддоны:
[simterm]
$ kubectl apply -f samples/addons
[/simterm]
И выполняем istioctl dashboard kiali
– Kiali откроется в дефолтном браузере:
Но если мы перейдём в Applications, то увидим, что у нашего приложения в Details указано “Missing Sidecar“:
А в Graph нет карты сервисов:
Sidecar – Envoy-прокси
Итак, у нас в поде нет контейнеров с Envoy, хотя сеть работает, т.к. сейчас Istio через цепочку правил iptables шлёт трафик напрямую в контейнер с NGINX, см. Traffic flow from application container to sidecar proxy.
Правила iptalbes настраиваются дополнительным InitContainer – istio-init
во время запуска пода, а сейчас они дефолтные, настроенные kube-proxy
во время деплоя нашего тестового приложения, см. Kubernetes: Service, балансировка нагрузки, kube-proxy и iptables
В следующем посте мы копнём поглубже в работу сети в Istio, а пока просто добавим автоматическую вставку sidecar в поды неймспейса и сравним цепочки iptables до и после.
Istio и iptables
Посмотрим правила iptables до вставки sidecar и istio-init
.
По SSH подключаемся на WorkerNode, находим контйнер с nginx:
[simterm]
[root@ip-10-22-35-66 ec2-user]# docker ps | grep nginx 22d64b132490 nginx "/docker-entrypoint.…" 3 minutes ago Up 3 minutes k8s_web_test-deployment-6864c5bf84-mk98r_test-namespace_8b88caf0-237a-4c94-ac71-186f8e701a7c_0
[/simterm]
Находим PID процесса в этом контейнере:
[simterm]
[root@ip-10-22-35-66 ec2-user]# docker top k8s_web_test-deployment-6864c5bf84-mk98r_test-namespace_8b88caf0-237a-4c94-ac71-186f8e701a7c_0 UID PID PPID C STIME TTY TIME CMD root 31548 31517 0 10:36 ? 00:00:00 nginx: master process nginx -g daemon off; 101 31591 31548 0 10:36 ? 00:00:00 nginx: worker process
[/simterm]
Используя nsenter
– проверяем правила iptables в network-неймспейсе процесса 31548 – пока тут ничего необычного, весь трафик идёт на наш контейнер:
[simterm]
[root@ip-10-22-35-66 ec2-user]# nsenter -t 31548 -n iptables -t nat -L Chain PREROUTING (policy ACCEPT) target prot opt source destination Chain INPUT (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination Chain POSTROUTING (policy ACCEPT) target prot opt source destination
[/simterm]
Запуск Sidecar
Документация – Installing the Sidecar.
Для автоматической вставки Envoy-контейнеров – выполняем:
[simterm]
$ kubectl label namespace test-namespace istio-injection=enabled namespace/test-namespace labeled
[/simterm]
Проверяем:
[simterm]
$ kubectl get namespace test-namespace --show-labels NAME STATUS AGE LABELS test-namespace Active 11m istio-injection=enabled
[/simterm]
Но контейнеры будут добавлены только для новых ресурсов в этом неймспейсе.
Можем добавить сайдкар вручную в запущенный под, обновив его манифест с помощью kube-inject
, либо просто пересоздать под:
[simterm]
$ kubectl -n test-namespace scale deployment test-deployment --replicas=0 deployment.apps/test-deployment scaled $ kubectl -n test-namespace scale deployment test-deployment --replicas=1 deployment.apps/test-deployment scaled
[/simterm]
Проверяем контейнеры ещё раз:
[simterm]
$ kubectl -n test-namespace get pod -o jsonpath={.items[*].spec.containers[*].name} web istio-proxy
[/simterm]
Тут istio-proxy
– и есть sidecar-контейнер с Envoy.
Проверяем initContainers
нашего пода:
[simterm]
$ kk -n test-namespace get pod -o jsonpath={.items[*].spec.initContainers[*].name} istio-init
[/simterm]
И проверим правила iptables снова – ещё раз находим контейнер и его PID, смотрим:
[simterm]
[root@ip-10-22-35-66 ec2-user]# nsenter -t 4194 -n iptables -t nat -L Chain PREROUTING (policy ACCEPT) target prot opt source destination ISTIO_INBOUND tcp -- anywhere anywhere Chain INPUT (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination ISTIO_OUTPUT tcp -- anywhere anywhere Chain POSTROUTING (policy ACCEPT) target prot opt source destination Chain ISTIO_INBOUND (1 references) target prot opt source destination RETURN tcp -- anywhere anywhere tcp dpt:15008 RETURN tcp -- anywhere anywhere tcp dpt:ssh RETURN tcp -- anywhere anywhere tcp dpt:15090 RETURN tcp -- anywhere anywhere tcp dpt:15021 RETURN tcp -- anywhere anywhere tcp dpt:15020 ISTIO_IN_REDIRECT tcp -- anywhere anywhere Chain ISTIO_IN_REDIRECT (3 references) target prot opt source destination REDIRECT tcp -- anywhere anywhere redir ports 15006 Chain ISTIO_OUTPUT (1 references) target prot opt source destination RETURN all -- ip-127-0-0-6.eu-west-3.compute.internal anywhere ISTIO_IN_REDIRECT all -- anywhere !localhost owner UID match 1337 RETURN all -- anywhere anywhere ! owner UID match 1337 RETURN all -- anywhere anywhere owner UID match 1337 ISTIO_IN_REDIRECT all -- anywhere !localhost owner GID match 1337 RETURN all -- anywhere anywhere ! owner GID match 1337 RETURN all -- anywhere anywhere owner GID match 1337 RETURN all -- anywhere localhost ISTIO_REDIRECT all -- anywhere anywhere Chain ISTIO_REDIRECT (1 references) target prot opt source destination REDIRECT tcp -- anywhere anywhere redir ports 15001
[/simterm]
Теперь тут появились дополнительные цепочки, созданные Istio, которые отправлят трафик в контейнер с Envoy и от него в контейнер с приложением, и обратно.
Возвращаемся к Kiali, проверяем карту:
И трассировку вызовов:
Traces доступны через Jaeger, который тоже можно открыть через istioctl dashboard jaeger
:
Также с помощью istioctl dashboard prometheus
можно запустить Prometheus, и посмотреть его метрики, см. Querying Metrics from Prometheus:
Собственно, на этом пока всё.
Далее будем знакомиться с интеграцией AWS Application LoadBalncer, деплоем и настройкой Istio в AWS Elastic Kubernetes Service, настройкой Gateway и VirtualServices.
Ссылки по теме
-
- Learn Istio using Interactive Browser-Based Scenarios – базовый курс знакомства с Istio на Katacoda
- Istio Service Mesh Workshop – знакомство с Istio
- Service Mesh with Istio – ещё один workshop, от AWS EKS
- How Istio Works Behind the Scenes on Kubernetes – архитектура, компоненты
- Debugging Envoy and Istiod – дебаг Istio с istioctl
- Starting with Istio, см. также Istio, Part II и Istio, Part III, why use it? – обзор, архитектура, сеть Istio
- North-South Traffic Management of Istio Gateways (with Answers from Service Mesh Experts) – сеть в Istio
- A Crash Course For Running Istio – обзор Istio, Envoy, iptables, компоненты сети Istio, очень толковый пост
- How to Make Istio Work with Your Apps – troubleshooting и примеры proxy-status
- Reducing Istio proxy resource consumption with outbound traffic restrictions – ресурсы в Istio и тюнинг sidecars
- Life of a Packet through Istio – вроде бы неплохой обзор работы сети в Istio, но видео не смотрел
- Sidecar injection and transparent traffic hijacking process in Istio explained in detail – sidecards, iptables и роутинг пакетов, весьма занимательный материал
- An in-depth intro to Istio Ingress – Ingress, Gateway и VirtualService, примеры
- Understanding Istio Ingress Gateway in Kubernetes – аналогично
- Istio Gateway – аналогично
- Getting started with Istio и подолжение – Istio in Practice – Ingress Gateway, Istio in Practice – Routing with VirtualService
- 4 Istio Gateway: getting traffic into your cluster – и ещё про Gateway и VirtualService