Как запустить Istio, используя Kubernetes в production. Часть 1

Что такое Istio? Это так называемый Service mesh, технология, которая добавляет уровень абстракции над сетью. Мы перехватываем весь или часть трафика в кластере и производим определенный набор операций с ним. Какой именно? Например, делаем умный роутинг, или реализуем подход circuit breaker, можем организовывать «canary deployment», частично переключая трафик на новую версию сервиса, а можем ограничивать внешние взаимодействия и контролировать все походы из кластера во внешнюю сеть. Есть возможность задавать policy правила для контроля походов между разными микросервисами. Наконец, мы можем получить всю карту взаимодействия по сети и сделать унифицированный сбор метрик полностью прозрачно для приложений.

Про механизм работы можно прочитать в официальной документации. Istio — это действительно мощный инструмент, который позволяет решить множество задач и проблем. В этой статье я бы хотел ответить на основные вопросы, которые обычно возникают в начале работы с Istio. Это поможет вам разобраться с ним быстрее.

Как запустить Istio, используя Kubernetes в production. Часть 1

Принцип работы

Istio состоит из двух основных зон — control plane и data plane. Control plane содержит в себе основные компоненты, которые обеспечивают корректную работу остальных. В текущей версии (1.0) control plane имеет три основных компонента: Pilot, Mixer, Citadel. Citadel мы рассматривать не будем, он нужен для генерации сертификатов для обеспечения работы mutual TLS между сервисами. Давайте посмотрим подробнее на устройство и предназначение Pilot и Mixer.

Как запустить Istio, используя Kubernetes в production. Часть 1

Pilot — это главный управляющий компонент, который распространяет всю информацию о том, что у нас есть в кластере – сервисы, их endpoint’ы и routing правила (например, правила для Canary deployment или правила circuit breaker).

Mixer — опциональный компонент control plane, который предоставляет возможность сбора метрик, логов и любой информации о сетевом взаимодействии. Также он следит за соблюдением Policy правил и соблюдением rate limit’ов.

Data plane реализуется с помощью sidecar контейнеров-прокси. По умолчанию используется мощный прокси-сервер envoy. Он может быть заменен на другую реализацию, например nginx (nginmesh).

Для того, чтобы Istio работал полностью прозрачно для приложений, есть система автоматического inject’инга. Последняя реализация подходит для версий Kubernetes 1.9+ (mutational admission webhook). Для Kubernetes версий 1.7, 1.8 есть возможность использовать Initializer.

Sidecar контейнеры соединяются с Pilot по протоколу GRPC, который позволяет оптимизировать модель пушинга изменений, происходящих в кластере. GRPC начал использоваться в Envoy начиная с версии 1.6, в Istio он используется с версии 0.8 и представляет из себя pilot-agent — обертку на golang над envoy, которая конфигурирует параметры запуска.

Pilot и Mixer являются полностью stateless компонентами, все состояние держат в памяти. Конфигурация для них задается в виде Kubernetes Custom Resources, которые сохраняются в etcd.
Istio-agent получает адрес Pilot и открывает GRPC stream к нему.

Как я уже сказал, Istio реализует всю функциональность полностью прозрачно для приложений. Давайте разберемся как. Алгоритм такой:

  1. Деплоим новую версию сервиса.
  2. В зависимости от подхода injecting’а sidecar контейнера добавляются istio-init контейнер и istio-agent контейнер (envoy) на этапе применения конфигурации, либо они могут быть уже вручную вставлены в описание Pod сущности Kubernetes.
  3. istio-init контейнер представляет из себя скрипт, который применяет правила iptables для пода. Есть два варианта для настройки заворачивания трафика в istio-agent контейнер: использовать redirect правила iptables, либо TPROXY. На момент написания статьи по умолчанию используется подход с redirect правилами. В istio-init есть возможность настроить какой именно трафик нужно перехватывать и направлять в istio-agent. Например, для того, чтобы перехватывать весь входящий и весь исходящий трафик, нужно установить параметры -i и -b в значение *. Можно указать конкретные порты, которые нужно перехватывать. Для того, чтобы не перехватывать определенную подсеть, можно указать ее с помощью флага -x.
  4. После исполнения init контейнеров, запускаются основные, и в том числе pilot-agent (envoy). Он подключается к уже развернутому Pilot по GRPC и получает информацию о всех существующих сервисах и routing политиках в кластере. По полученным данным, он конфигурирует cluster’ы и прописывает им непосредственно endpoint’ы наших приложений в Kubernetes кластере. Также необходимо отметить важный момент: envoy динамически настраивает listeners (пары IP, port), которые начинает слушать. Поэтому, когда запросы входят в pod, перенаправляются с помощью redirect iptables правил в sidecar, envoy уже успешно может обрабатывать эти соединения и понимать, куда нужно дальше проксировать трафик. Также на этом этапе происходит отправка информации в Mixer, который мы рассмотрим позже, и отправка tracing span’ов.

В итоге мы получаем целую сеть envoy прокси-серверов, которые мы можем настраивать из одной точки (Pilot). Все inbound и outbound запросы проходят через envoy. Причем, перехватывается только TCP трафик. Это значит, что Kubernetes service IP резолвится с помощью kube-dns по UDP без изменения. Затем уже после резолва происходит перехват исходящего запроса и обработка envoy’ем, который уже решает на какой endpoint нужно отправить запрос (или не отправить, в случае политик доступа или сработки circuit breaker алгоритма).

С Pilot разобрались, теперь нужно понять как работает Mixer и зачем он нужен. Прочитать официальную документацию по нему можно тут.

Mixer в текущем виде представляет из себя два компонента: istio-telemetry, istio-policy (до версии 0.8 это был один компонент istio-mixer). И тот, и другой представляют из себя mixer, каждый из которых отвечают за свою задачу. Istio telemetry принимает по GRPC от sidecar контейнеров Report информацию о том, кто куда идет и с какими параметрами. Istio-policy принимает Check запросы для проверки удовлетворения Policy правилам. Poilicy check проводятся, конечно, не на каждый запрос, а кэшируются на клиенте (в sidecar) на определенное время. Report check’и отправляются batch запросами. Как настраивать и какие именно параметры нужно отсылать посмотрим чуть позже.

Mixer предполагается как высокодоступный компонент, который обеспечивает бесперебойную работу по сборке и обработке telemetry данных. Система получается в итоге как многоуровневый буфер. Изначально данные буферизируются на стороне sidecar контейнеров, затем на стороне mixer и потом отправляются в так называемые mixer backend’ы. В итоге, если какой-то из компонентов системы отказывает, буфер растет и после восстановления системы флашится. Mixer backend’ы представляют из себя конечные точки отправки данных о телеметрии: statsd, newrelic и тд. Можно написать свой backend, это достаточно просто, и мы посмотрим как это сделать.

Как запустить Istio, используя Kubernetes в production. Часть 1

Если подытожить, схема работы с istio-telemetry такова.

  1. Сервис 1 посылает запрос в сервис 2.
  2. При выходе из сервиса 1, запрос заворачивается в его же sidecar.
  3. Sidecar envoy следит за тем, как проходит запрос в сервис 2 и подготавливает необходимую информацию.
  4. Затем отправляет её в istio-telemetry с помощью Report запроса.
  5. Istio-telemetry определяет, нужно ли отправлять этот Report в backend’ы, в какие именно и какие данные нужно отправлять.
  6. Istio-telemetry отправляет Report данные в backend если это необходимо.

Теперь посмотрим, как развернуть в системе Istio, состоящий только из основных компонентов (Pilot и sidecar envoy).

Для начала посмотрим на основную конфигурацию (mesh), которую читает Pilot:

apiVersion: v1
kind: ConfigMap
metadata:
  name: istio
  namespace: istio-system
  labels:
    app: istio
    service: istio
data:
  mesh: |-

    # пока что не включаем отправку tracing информации (pilot настроит envoy’и таким образом, что отправка не будет происходить)
    enableTracing: false

    # пока что не указываем mixer endpoint’ы, чтобы sidecar контейнеры не отправляли информацию туда
    #mixerCheckServer: istio-policy.istio-system:15004
    #mixerReportServer: istio-telemetry.istio-system:15004

    # ставим временной промежуток, с которым будет envoy переспрашивать Pilot (это для старой версии envoy proxy)
    rdsRefreshDelay: 5s

    # default конфигурация для envoy sidecar
    defaultConfig:
      # аналогично как rdsRefreshDelay
      discoveryRefreshDelay: 5s

      # оставляем по умолчанию (путь к конфигурации и бинарю envoy)
      configPath: "/etc/istio/proxy"
      binaryPath: "/usr/local/bin/envoy"

      # дефолтное имя запущенного sidecar контейнера (используется, например, в именах сервиса при отправке tracing span’ов)
      serviceCluster: istio-proxy

      # время, которое будет ждать envoy до того, как он принудительно завершит все установленные соединения
      drainDuration: 45s
      parentShutdownDuration: 1m0s

      # по умолчанию используются REDIRECT правила iptables. Можно изменить на TPROXY.
      #interceptionMode: REDIRECT

      # Порт, на котором будет запущена admin панель каждого sidecar контейнера (envoy)
      proxyAdminPort: 15000

      # адрес, по которому будут отправляться trace’ы по zipkin протоколу (в начале мы отключили саму отправку, поэтому это поле сейчас не будет использоваться)
      zipkinAddress: tracing-collector.tracing:9411

      # statsd адрес для отправки метрик envoy контейнеров (отключаем)
      # statsdUdpAddress: aggregator:8126

      # выключаем поддержку опции Mutual TLS
      controlPlaneAuthPolicy: NONE

      # адрес, на котором будет слушать istio-pilot для того, чтобы сообщать информацию о service discovery всем sidecar контейнерам
      discoveryAddress: istio-pilot.istio-system:15007

Все основные компоненты управления (control plane) расположим в namespace istio-system в Kubernetes.

Минимально нам нужно развернуть только Pilot. Для этого воспользуемся такой конфигурацией.

И настроим вручную injecting sidecar контейнера.

Init container:

initContainers:
 - name: istio-init
   args:
   - -p
   - "15001"
   - -u
   - "1337"
   - -m
   - REDIRECT
   - -i
   - '*'
   - -b
   - '*'
   - -d
   - ""
   image: istio/proxy_init:1.0.0
   imagePullPolicy: IfNotPresent
   resources:
     limits:
       memory: 128Mi
   securityContext:
     capabilities:
       add:
       - NET_ADMIN

И sidecar:

       name: istio-proxy
       args:
         - "bash"
         - "-c"
         - |
           exec /usr/local/bin/pilot-agent proxy sidecar 
           --configPath 
           /etc/istio/proxy 
           --binaryPath 
           /usr/local/bin/envoy 
           --serviceCluster 
           service-name 
           --drainDuration 
           45s 
           --parentShutdownDuration 
           1m0s 
           --discoveryAddress 
           istio-pilot.istio-system:15007 
           --discoveryRefreshDelay 
           1s 
           --connectTimeout 
           10s 
           --proxyAdminPort 
           "15000" 
           --controlPlaneAuthPolicy 
           NONE
         env:
         - name: POD_NAME
           valueFrom:
             fieldRef:
               fieldPath: metadata.name
         - name: POD_NAMESPACE
           valueFrom:
             fieldRef:
               fieldPath: metadata.namespace
         - name: INSTANCE_IP
           valueFrom:
             fieldRef:
               fieldPath: status.podIP
         - name: ISTIO_META_POD_NAME
           valueFrom:
             fieldRef:
               fieldPath: metadata.name
         - name: ISTIO_META_INTERCEPTION_MODE
           value: REDIRECT
         image: istio/proxyv2:1.0.0
         imagePullPolicy: IfNotPresent
         resources:
           requests:
             cpu: 100m
             memory: 128Mi
           limits:
             memory: 2048Mi
         securityContext:
           privileged: false
           readOnlyRootFilesystem: true
           runAsUser: 1337
         volumeMounts:
         - mountPath: /etc/istio/proxy
           name: istio-envoy

Для того, чтобы все успешно запустилось, нужно завести ServiceAccount, ClusterRole, ClusterRoleBinding, CRD для Pilot, описания которых можно найти тут.

В итоге сервис, в который мы inject’им sidecar с envoy, должен успешно запуститься, получить весь discovery из пилота и обрабатывать запросы.

Важно понимать, что все компоненты control plane являются stateless приложениями и могут быть без проблем горизонтально масштабированы. Все данные лежат в etcd в виде кастомных описаний ресурсов Kubernetes.

Также у Istio (пока что экспериментально) есть возможность запуска вне кластера и возможность смотреть и шарить service discovery между несколькими Kubernetes кластерами. Подробнее об этом можно почитать тут.

При мультикластерной установке следует учитывать следующие ограничения:

  1. Pod CIDR и Service CIDR должны быть уникальны по всем кластерам и не должны пересекаться.
  2. Все Pod CIDR должны быть доступны от любых Pod CIDR между кластерами.
  3. Все Kubernetes API серверы должны быть доступны друг для друга.

Это начальные сведения, которые помогут вам приступить к работе с Istio. Однако есть еще множество подводных камней. Например, особенности роутинга внешнего трафика (наружу кластера), подходы к отладке sidecar’ов, профилирование, настройка mixer и написание кастомного mixer backend, настройка tracing механизма и его работа с помощью envoy.
Всё это мы рассмотрим в следующих публикациях. Задавайте ваши вопросы, постараюсь их осветить.

Источник: habr.com