Jak uruchomić Istio, używając Kubernetes w produkcji. Część 1

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

Można przeczytać o mechanizmie działania w oficjalnej dokumentacji. Istio to naprawdę potężne narzędzie, które pozwala rozwiązać wiele problemów i zadań. W tym artykule chciałbym odpowiedzieć na podstawowe pytania, które zazwyczaj pojawiają się na początku pracy z Istio. To pomoże Ci szybciej się z nim oswoić.

Jak uruchomić Istio, używając Kubernetes w produkcji. Część 1

Zasada działania

Istio składa się z dwóch głównych obszarów — control plane i data plane. Control plane zawiera podstawowe komponenty, które zapewniają prawidłowe działanie pozostałych. W obecnej wersji (1.0) control plane ma trzy główne komponenty: Pilot, Mixer, Citadel. Nie będziemy omawiać Citadel, ponieważ służy on do generacji certyfikatów zapewniających działanie mutual TLS między usługami. Przyjrzyjmy się bliżej konstrukcji i przeznaczeniu Pilot i Mixer.

Jak uruchomić Istio, używając Kubernetes w produkcji. Część 1

Pilot to główny komponent zarządzający, który rozprowadza wszystkie informacje o tym, co mamy w klastrze – usługi, ich endpointy i zasady routingu (na przykład zasady dla Canary deployment lub zasady circuit breaker).

Mixer to opcjonalny komponent control plane, który umożliwia zbieranie metryk, logów i wszelkich informacji o interakcjach sieciowych. Monitoruje również przestrzeganie zasad polityki i limity prędkości.

Data plane realizowane jest za pomocą kontenerów-proxy sidecar. Domyślnie używany jest potężny proxy server envoy. Może być zastąpiony inną implementacją, na przykład nginx (nginmesh).

Aby Istio działało całkowicie przejrzyście dla aplikacji, istnieje system automatycznego injectingu. Ostatnia implementacja jest odpowiednia dla wersji Kubernetes 1.9+ (mutational admission webhook). Dla wersji Kubernetes 1.7, 1.8 istnieje możliwość użycia Initializer.

Kontenery sidecar łączą się z Pilotem za pomocą protokołu GRPC, który pozwala zoptymalizować model wprowadzania zmian zachodzących w klastrze. GRPC zaczął być używany w Envoy od wersji 1.6, w Istio jest używany od wersji 0.8 i stanowi pilot-agent — nakładkę w golang nad envoyem, która konfiguruje parametry uruchomienia.

Pilot i Mixer są całkowicie komponentami stateless, wszystkie informacje przechowywane są w pamięci. Konfiguracja dla nich określana jest w postaci niestandardowych zasobów Kubernetes, które są zapisywane w etcd.
Istio-agent otrzymuje adres Pilota i otwiera strumień GRPC do niego.

Jak już wspomniałem, Istio implementuje całą funkcjonalność całkowicie przejrzyście dla aplikacji. Zobaczmy, jak to działa. Algorytm jest następujący:

  1. Wdrażamy nową wersję usługi.
  2. W zależności od podejścia do wstrzykiwania kontenera sidecar, kontener istio-init i kontener istio-agent (envoy) są dodawane na etapie stosowania konfiguracji, lub mogą być już ręcznie wstawione w opisie podu Kubernetes.
  3. Kontener istio-init to skrypt, który stosuje zasady iptables dla podu. Istnieją dwie opcje konfiguracji przekierowywania ruchu do kontenera istio-agent: użyć zasad przekierowania iptables, albo TPROXY. W momencie pisania tego artykułu domyślnie stosuje się podejście z zasadami przekierowań. W istio-init można skonfigurować, jaki ruch należy przechwytywać i kierować do istio-agent. Na przykład, aby przechwytywać cały ruch przychodzący i wychodzący, należy ustawić parametry -i i -b na wartość *. Można wskazać konkretne porty, które należy przechwytywać. Aby nie przechwytywać określonej podsieci, można ją wskazać za pomocą flagi -x.
  4. Po wykonaniu kontenerów init uruchamiane są kontenery główne, w tym pilot-agent (envoy). Łączy się on z już wdrożonym Pilotem przez GRPC i otrzymuje informacje o wszystkich istniejących usługach i politykach routingu w klastrze. Na podstawie uzyskanych danych konfiguruje klastry i przypisuje im bezpośrednie punkty końcowe naszych aplikacji w klastrze Kubernetes. Należy również zauważyć ważny wskaźnik: envoy dynamicznie konfiguruje listeners (pary IP, port), które zaczyna nasłuchiwać. Dlatego, gdy żądania docierają do podu, są przekierowywane za pomocą zasad przekierowania iptables do sidecara, envoy może już skutecznie obsługiwać te połączenia i rozumie, dokąd dalej przekierować ruch. Na tym etapie dochodzi również do wysyłania informacji do Mixera, który omówimy później, oraz wysyłania spenów śledzenia.

W rezultacie otrzymujemy całą sieć serwerów proxy envoy, które możemy konfigurować z jednego punktu (Pilot). Wszystkie żądania inbound i outbound przechodzą przez envoy. Ponadto, przechwytywany jest tylko ruch TCP. Oznacza to, że IP usługi Kubernetes jest rozwiązywane za pomocą kube-dns przez UDP bez zmian. Następnie po rozwiązaniu następuje przechwytywanie wychodzącego żądania i jego przetwarzanie przez envoy, który decyduje, do którego punktu końcowego należy wysłać żądanie (lub nie wysłać, w przypadku polityk dostępu lub działania algorytmu circuit breaker).

Zrozumieliśmy Pilota, teraz należy zrozumieć, jak działa Mixer i po co jest potrzebny. Oficjalną dokumentację na jego temat można przeczytać tutaj.

Mixer w obecnej postaci składa się z dwóch komponentów: istio-telemetry oraz istio-policy (do wersji 0.8 był to jeden komponent istio-mixer). Każdy z nich pełni swoją rolę. Istio telemetry odbiera za pomocą GRPC od kontenerów sidecar informacje o tym, kto gdzie idzie i z jakimi parametrami. Istio-policy odbiera zapytania Check, aby zweryfikować spełnienie zasad polityki. Sprawdzanie polityki nie odbywa się przy każdym żądaniu, lecz jest buforowane po stronie klienta (w sidecar) na określony czas. Raporty są wysyłane w partiach. Jak skonfigurować i jakie dokładnie parametry należy wysyłać, zobaczymy nieco później.

Mixer jest projektowany jako komponent o wysokiej dostępności, który zapewnia nieprzerwaną pracę przy zbieraniu i przetwarzaniu danych telemetrycznych. Ostatecznie system działa jak wielopoziomowy bufor. Początkowo dane są buforowane po stronie kontenerów sidecar, następnie po stronie mixer, a później wysyłane do tzw. backendów mixer. W efekcie, jeśli jakiś komponent systemu zawiedzie, bufor rośnie, a po przywróceniu systemu dane są opróżniane. Backend mixer to punkty końcowe do wysyłania danych telemetrii: statsd, newrelic itd. Można stworzyć własny backend, jest to dość proste, a my zobaczymy, jak to zrobić.

Jak uruchomić Istio, używając Kubernetes w produkcji. Część 1

Podsumowując, schemat działania z istio-telemetry wygląda następująco.

  1. Serwis 1 wysyła zapytanie do serwisu 2.
  2. Podczas wychodzenia z serwisu 1 zapytanie jest opakowywane w jego własny sidecar.
  3. Sidecar envoy monitoruje, jak przebiega zapytanie do serwisu 2 i przygotowuje niezbędne informacje.
  4. Następnie wysyła je do istio-telemetry za pomocą zapytania Report.
  5. Istio-telemetry decyduje, czy należy wysłać ten raport do backendów, do których dokładnie i jakie dane należy przesłać.
  6. Istio-telemetry wysyła dane Raport w backendzie, jeśli to konieczne.

Teraz zobaczmy, jak wdrożyć w systemie Istio składającym się tylko z podstawowych komponentów (Pilot i sidecar envoy).

Na początek przyjrzymy się głównej konfiguracji (mesh), którą odczytuje Pilot:

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

    # Na razie nie włączamy wysyłania informacji o śledzeniu (pilot skonfiguruje envoy’e w taki sposób, że wysyłka nie będzie miała miejsca)
    enableTracing: false

    # Na razie nie podajemy punktów końcowych mixer’a, aby kontenery sidecar nie wysyłały tam informacji
    #mixerCheckServer: istio-policy.istio-system:15004
    #mixerReportServer: istio-telemetry.istio-system:15004

    # Ustawiamy opóźnienie, z jakim envoy będzie ponownie pytał Pilota (dotyczy to starszej wersji proxy envoy)
    rdsRefreshDelay: 5s

    # Domyślna konfiguracja dla sidecara envoy
    defaultConfig:
      # Analogicznie jak rdsRefreshDelay
      discoveryRefreshDelay: 5s

      # Pozostawiamy domyślnie (ścieżka do konfiguracji i binarki envoy)
      configPath: "/etc/istio/proxy"
      binaryPath: "/usr/local/bin/envoy"

      # Domyślna nazwa uruchomionego kontenera sidecar (używana np. w nazwach usług przy wysyłaniu span’ów śledzenia)
      serviceCluster: istio-proxy

      # Czas, który będzie czekał envoy, zanim wymusi zakończenie wszystkich nawiązanych połączeń
      drainDuration: 45s
      parentShutdownDuration: 1m0s

      # Domyślnie używane są zasady REDIRECT iptables. Można to zmienić na TPROXY.
      #interceptionMode: REDIRECT

      # Port, na którym będzie uruchamiana panel administracyjny każdego kontenera sidecar (envoy)
      proxyAdminPort: 15000

      # Adres, na który będą wysyłane trace’y w protokole zipkin (na początku wyłączyliśmy samą wysyłkę, więc to pole obecnie nie będzie używane)
      zipkinAddress: tracing-collector.tracing:9411

      # Adres statsd do wysyłania metryk kontenerów envoy (wyłączamy)
      # statsdUdpAddress: aggregator:8126

      # Wyłączamy wsparcie dla opcji Mutual TLS
      controlPlaneAuthPolicy: NONE

      # Adres, na którym będzie nasłuchiwać istio-pilot, aby przekazywać informacje o odkrywaniu usług wszystkim kontenerom sidecar
      discoveryAddress: istio-pilot.istio-system:15007

Wszystkie główne komponenty zarządzania (control plane) umieścimy w namespace istio-system w Kubernetes.

Minimalnie musimy wdrożyć tylko Pilota. W tym celu skorzystamy z takiej konfiguracji.

I ręcznie skonfigurujemy wstrzykiwanie kontenera sidecar.

Kontener inicjujący:

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

I sidecar:

       nazwa: 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

Aby wszystko działało poprawnie, należy utworzyć ServiceAccount, ClusterRole, ClusterRoleBinding oraz CRD dla Pilota, których opisy można znaleźć tutaj.

W rezultacie usługa, do której wstrzykujemy sidecar z envoy, powinna uruchomić się pomyślnie, uzyskać wszystkie dane discovery z pilota i obsługiwać żądania.

Warto zrozumieć, że wszystkie komponenty kontrolnej warstwy są aplikacjami bezstanowymi i mogą być łatwo skalowane poziomo. Wszystkie dane przechowywane są w etcd w postaci dostosowanych opisów zasobów Kubernetes.

Istio (na razie eksperymentalnie) oferuje także możliwość uruchamiania poza klastrem oraz funkcję przeglądania i dzielenia się odkrywaniem usług między wieloma klastrami Kubernetes. Więcej na ten temat można przeczytać tutaj.

Przy instalacji wieloklasowej należy wziąć pod uwagę następujące ograniczenia:

  1. Pod CIDR i Service CIDR muszą być unikalne wśród wszystkich klastrów i nie mogą się nakładać.
  2. Wszystkie Pod CIDR powinny być dostępne ze wszystkich Pod CIDR między klastrami.
  3. Wszystkie serwery API Kubernetes muszą być dostępne nawzajem.

To wstępne informacje, które pomogą Ci rozpocząć pracę z Istio. Jednak istnieje wiele pułapek, takich jak szczegóły routingu ruchu zewnętrznego (na zewnątrz klastra), podejścia do debugowania sidecarów, profilowanie, konfiguracja mikserów oraz pisanie dostosowanego backendu miksera, konfiguracja mechanizmu śledzenia i jego wykorzystanie z envoy.
Omówimy to w następnych publikacjach. Zadawaj swoje pytania, postaram się je wyjaśnić.

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster