Korzystanie z klastra Istio Multicluster do kumulowania zadań między klastrami

1. Witamy

Dziękujemy za udział w ćwiczeniach z wykorzystaniem narzędzia Istio Multi Cloud Burst, które przygotował zespół Google.Ten moduł wymaga praktycznej znajomości Kubernetes, Node i Go na poziomie początkującym.

Co będzie potrzebne

  • Konto Google Cloud Platform (użyj istniejącego konta lub otrzymasz bezpłatne konto)
  • laptop (zainstaluj „kubectl”, „gcloud” itp.) lub Google Cloud Shell;

Czego się nauczysz

  • Jak utworzyć klaster Kubernetes w GKE
  • Instalowanie Istio w klastrze Kubernetes za pomocą Helm
  • Instalowanie Istio Multicluster za pomocą Helm
  • Wdrażanie aplikacji internetowej z źródła do Kubernetes
  • Tworzenie i stosowanie reguł routingu ruchu w Istio
  • Wskaźniki Prometheus
  • Tworzenie i przesyłanie obrazów kontenerów w klastrze Kubernetes

2. Konfiguracja

Możesz wykonać to ćwiczenie w jednym z tych formatów:

  • Google Cloud Shell (zalecane): powłoka w przeglądarce z zainstalowanymi narzędziami.
  • laptopa (postępuj zgodnie z instrukcjami poniżej)

Pierwsze kroki z Google Cloud Platform

  1. Jeśli nie masz konta Google Cloud Platform, poproś instruktora o kartę użytkownika bezpłatnego konta.
  2. Otwórz konsolę Google Cloud i kliknij „Wybierz projekt”:5c2d9bf74c78f7e4.png
  3. Zanotuj gdzieś „identyfikator” projektu, a potem kliknij projekt, aby go wybrać: ecc5e8e97bfa6559.png

Cloud Shell udostępnia w przeglądarce powłokę wiersza poleceń z zainstalowanymi narzędziami i automatycznie uwierzytelnioną na koncie Google Cloud Platform. (jeśli nie chcesz wykonywać tego ćwiczenia w Cloud Shell, przejdź do następnej sekcji).

Otwórz konsolę Google Cloud i na pasku narzędzi w prawym górnym rogu kliknij „Aktywuj Cloud Shell”:

68a17b036ce24ccb.png

Dodawanie narzędzi do Cloud Shell

  1. Zainstalujkubectx****: pobierz skrypty basha z tutaj do dowolnej lokalizacji w ścieżce $PATH.
  2. Zainstalujhelm****: postępuj zgodnie z tymi instrukcjami.

Możesz też użyć tych poleceń, aby zainstalować oba programy w katalogu ~/.bin i dodać je do zmiennej środowiskowej $PATH:

mkdir -p ~/.bin && \
cd ~/.bin && \
curl -LO https://raw.githubusercontent.com/ahmetb/kubectx/master/kubectx && \
chmod +x kubectx && \
curl -LO https://raw.githubusercontent.com/ahmetb/kubectx/master/kubens && \
chmod +x kubens && \
curl -LO  https://storage.googleapis.com/kubernetes-helm/helm-v2.12.0-linux-amd64.tar.gz && \
tar xzf helm-v2.12.0-linux-amd64.tar.gz && \
rm helm-v2.12.0-linux-amd64.tar.gz && \
mv linux-amd64/helm ./helm && \
rm -r linux-amd64 && \
export PATH=${HOME}/.bin:${PATH}

Oto kilka szybkich wskazówek, które ułatwią korzystanie z Cloud Shell:

1. Odłączanie powłoki do nowego okna:

2. Za pomocą edytora plików: kliknij ikonę ołówka w prawym górnym rogu, aby uruchomić edytor plików w przeglądarce. Przyda Ci się to, gdy będziemy kopiować fragmenty kodu do plików.

3. Uruchom nowe karty: jeśli potrzebujesz więcej niż jednego prompta terminala.

4. Powiększ tekst: domyślny rozmiar czcionki w Cloud Shell może być zbyt mały, aby był czytelny.

Ctrl-+ w systemie Linux lub Windows oraz ⌘-+ w systemie macOS.

Jeśli wolisz korzystać z własnego środowiska stacji roboczej niż z Cloud Shell, skonfiguruj te narzędzia:

  1. Zainstalujgcloud: (zainstalowany wstępnie w Cloud Shell). Aby zainstalować gcloud na swojej platformie, postępuj zgodnie z instrukcjami. Użyjemy go do utworzenia klastra Kubernetes.
  2. Zainstalujkubectl:(zainstalowany wstępnie w Cloud Shell). Aby zainstalować:
gcloud components install kubectl

Aby uwierzytelnić się w gcloud, uruchom to polecenie: Pojawi się prośba o zalogowanie się na konto Google. Następnie wybierz utworzony wcześniej projekt (widoczny powyżej) jako projekt domyślny. (możesz pominąć konfigurowanie strefy obliczeniowej):

gcloud init
  1. Zainstalujcurl: Wstępnie zainstalowany w większości systemów Linux/macOS. Prawdopodobnie już go masz. W przeciwnym razie wyszukaj w internecie instrukcje instalacji.
  2. Zainstalujkubectx****: pobierz skrypty basha z tutaj do katalogu w $PATH.
  3. Zainstalujhelm****: postępuj zgodnie z tymi instrukcjami.

3. Konfigurowanie projektu GCP

Włącz w projekcie interfejsy GKE (Google Kubernetes Engine), GCR (Google Container Registry) i GCB (Google Cloud Build):

gcloud services enable \
  cloudapis.googleapis.com \
  container.googleapis.com \
  containerregistry.googleapis.com \
  cloudbuild.googleapis.com

Konfigurowanie zmiennych środowiskowych

Podczas konfiguracji będziemy intensywnie korzystać z naszego projektu Google Cloud. Aby mieć szybki dostęp do zmiennej środowiskowej,

export GCLOUD_PROJECT=$(gcloud config get-value project)

Podczas tego warsztatu będziemy tworzyć kod i pliki konfiguracji, więc utwórzmy katalog projektu i przejdźmy do niego.

mkdir -p src/istio-burst && \
cd src/istio-burst && \
export proj=$(pwd)

4. Tworzenie „podstawowego” klastra Kubernetes

Za pomocą Google Kubernetes Engine (GKE) możesz łatwo utworzyć zarządzany klaster Kubernetes.

Poniższe polecenie spowoduje utworzenie klastra Kubernetes:

  • o nazwie „primary”,
  • w strefie us-west1-a,
  • najnowsza dostępna wersja Kubernetes,
  • z 4 pierwszymi węzłami
export cluster=primary
export zone=us-west1-a
gcloud container clusters create $cluster --zone $zone --username "admin" \
--cluster-version latest --machine-type "n1-standard-2" \
--image-type "COS" --disk-size "100" \
--scopes "https://www.googleapis.com/auth/compute",\
"https://www.googleapis.com/auth/devstorage.read_only",\
"https://www.googleapis.com/auth/logging.write",\
"https://www.googleapis.com/auth/monitoring",\
"https://www.googleapis.com/auth/servicecontrol",\
"https://www.googleapis.com/auth/service.management.readonly",\
"https://www.googleapis.com/auth/trace.append" \
--num-nodes "4" --network "default" \
--enable-cloud-logging --enable-cloud-monitoring --enable-ip-alias

(może to potrwać około 5 minut). Możesz obserwować tworzenie klastra w konsoli Google Cloud.

Po utworzeniu klastra Kubernetes usługa gcloud konfiguruje kubectl za pomocą danych uwierzytelniających wskazujących na klaster.

gcloud container clusters get-credentials $cluster --zone=$zone

Teraz możesz używać kubectl z nowym klastrem.

Aby wyświetlić listę węzłów Kubernetes klastra (powinny mieć stan „Gotowe”), uruchom to polecenie:

kubectl get nodes

Modyfikowanie nazw plików kubeconfig w celu ułatwienia ich używania

Będziemy często przełączać się między kontekstami, więc krótki alias dla naszych klastrów będzie przydatny.

To polecenie zmieni nazwę utworzonego właśnie wpisu kubeconfig na primary.

kubectx ${cluster}=gke_${GCLOUD_PROJECT}_${zone}_${cluster}

Ustaw uprawnienia:

Aby wdrożyć Istio, musisz być administratorem klastra. To polecenie skonfiguruje adres e-mail powiązany z Twoim kontem Google Cloud jako administratora klastra.

kubectl create clusterrolebinding cluster-admin-binding \
    --clusterrole=cluster-admin \
    --user=$(gcloud config get-value core/account)

5. Tworzenie klastra „burst”

Poniższe polecenie spowoduje utworzenie klastra Kubernetes:

  • o nazwie „burst”,
  • w strefie us-west1-a,
  • najnowsza dostępna wersja Kubernetes,
  • Z 1 węzłem początkowym
  • Autoskalowanie włączone do 5 węzłów
export cluster=burst
export zone=us-west1-a
gcloud container clusters create $cluster --zone $zone --username "admin" \
--cluster-version latest --machine-type "n1-standard-2" \
--image-type "COS" --disk-size "100" \
--scopes "https://www.googleapis.com/auth/compute",\
"https://www.googleapis.com/auth/devstorage.read_only",\
"https://www.googleapis.com/auth/logging.write",\
"https://www.googleapis.com/auth/monitoring",\
"https://www.googleapis.com/auth/servicecontrol",\
"https://www.googleapis.com/auth/service.management.readonly",\
"https://www.googleapis.com/auth/trace.append" \
--num-nodes "1" --enable-autoscaling --min-nodes=1 --max-nodes=5 \
--network "default" \
--enable-cloud-logging --enable-cloud-monitoring --enable-ip-alias

(może to potrwać około 5 minut). Możesz obserwować tworzenie klastra w konsoli Google Cloud.

Po utworzeniu klastra Kubernetes usługa gcloud konfiguruje usługę kubectl, podając dane uwierzytelniające wskazujące klaster.

gcloud container clusters get-credentials $cluster --zone=$zone

Teraz możesz używać kubectl z nowym klastrem.

Aby wyświetlić listę węzłów Kubernetes klastra (powinny mieć stan „Gotowe”), uruchom to polecenie:

kubectl get nodes

Modyfikowanie nazw plików kubeconfig w celu ułatwienia ich używania

To polecenie zmodyfikuje właśnie utworzony wpis kubeconfig na burst.

kubectx ${cluster}=gke_${GCLOUD_PROJECT}_${zone}_${cluster}

Ustaw uprawnienia:

Aby wdrożyć Istio Remote, musisz być administratorem klastra. To polecenie spowoduje ustawienie adresu e-mail powiązanego z Twoim kontem Google Cloud jako administratora klastra.

kubectl create clusterrolebinding cluster-admin-binding \
    --clusterrole=cluster-admin \
    --user=$(gcloud config get-value core/account)

6. Zastosuj reguły zapory sieciowej

Aby 2 klastery mogły się ze sobą komunikować, musimy utworzyć regułę zapory sieciowej.

Uruchom te polecenia, aby utworzyć na Google Cloud Platform regułę zapory sieciowej, która pozwoli naszym klasterom na komunikację

function join_by { local IFS="$1"; shift; echo "$*"; }
ALL_CLUSTER_CIDRS=$(gcloud container clusters list \
--filter="(name=burst OR name=primary) AND zone=$zone" \
--format='value(clusterIpv4Cidr)' | sort | uniq)
ALL_CLUSTER_CIDRS=$(join_by , $(echo "${ALL_CLUSTER_CIDRS}"))
ALL_CLUSTER_NETTAGS=$(gcloud compute instances list \
--filter="(metadata.cluster-name=burst OR metadata.cluster-name=primary) AND metadata.cluster-location=us-west1-a" \
--format='value(tags.items.[0])' | sort | uniq)
ALL_CLUSTER_NETTAGS=$(join_by , $(echo "${ALL_CLUSTER_NETTAGS}"))
gcloud compute firewall-rules create istio-multicluster-test-pods \
  --allow=tcp,udp,icmp,esp,ah,sctp \
  --direction=INGRESS \
  --priority=900 \
  --source-ranges="${ALL_CLUSTER_CIDRS}" \
  --target-tags="${ALL_CLUSTER_NETTAGS}" --quiet

Oba klastry są skonfigurowane i gotowe do wdrożenia aplikacji oraz usługi Istio.

7. Wprowadzenie do Istio

Co to jest Istio?

Istio to platforma sterująca siatką usług, której celem jest „łączenie, zabezpieczanie, kontrolowanie i obserwowanie usług”. Działa on w różne sposoby, ale przede wszystkim poprzez wstawianie kontenera proxy ( Envoy) do każdego z wdrożonych podów Kubernetes. Kontener proxy kontroluje całą komunikację sieciową między mikrousługami w połączeniu z zasadami ogólnymi i hubem telemetrycznym ( Mixer).

a25613cd581825da.png

Te zasady można stosować niezależnie od wdrożeń i usług Kubernetesa, co oznacza, że operator sieci może obserwować aktywność sieci, ograniczać, przekierowywać lub zmieniać zasady sieci bez ponownego wdrażania powiązanych aplikacji.

Oto niektóre funkcje zarządzania ruchem obsługiwane przez Istio:

  • Wyłączniki obwodu
  • Podział ruchu na podstawie procentów
  • Przepisywanie adresów URL
  • zakończenie TLS
  • Kontrole stanu
  • Równoważenie obciążenia

Na potrzeby tego warsztatu skupimy się na dzieleniu ruchu na podstawie odsetka.

Warunki usługi Istio, z którymi będziemy pracować

VirtualService

VirtualService definiuje zestaw reguł routingu ruchu, które mają zastosowanie, gdy adresowany jest host.

Brama

Brama to system równoważenia obciążenia działający na krawędzi sieci mesh, który odbiera przychodzące lub wychodzące połączenia HTTP/TCP. Bramy mogą określać porty, konfiguracje SNI itp.

DestinationRule

Element DestinationRule definiuje zasady stosowane do ruchu przeznaczonego dla usługi po dokonaniu routingu. Określają one konfigurację równoważenia obciążenia, rozmiar puli połączeń z modułu dodatkowego i ustawienia wykrywania wyników odstających.

Istio Multicluster

Podczas tworzenia naszych 2 klastrów zauważyliśmy, że klaster primary miał 4 węzły bez autoskalowania, a klaster burst miał 1 węzeł z autoskalowaniem do 5 węzłów.

Ta konfiguracja ma 2 przyczyny.

Najpierw chcemy zasymulować przeniesienie środowiska lokalnego do chmury. W środowisku lokalnym nie masz dostępu do klastrów z autoskalowaniem, ponieważ masz stałą infrastrukturę.

Po drugie, konfiguracja z 4 węzłami (zdefiniowana powyżej) to minimalne wymagania do uruchamiania Istio. W związku z tym pojawia się pytanie: jeśli Istio wymaga co najmniej 4 węzłów, jak klaster burst może uruchamiać Istio z 1 węzłem? Odpowiedź brzmi: Istio Multicluster instaluje znacznie mniejszy zestaw usług Istio i komunikuje się z instalacją Istio w klastrze głównym, aby pobrać reguły zasad i opublikować informacje telemetrii.

8. Omówienie architektury aplikacji

Omówienie komponentów

Będziemy wdrażać aplikację 3-poziomową za pomocą NodeJS i Redis.

Worker

Aplikacja robocza została napisana w NodeJS. Nasłuchuje przychodzących żądań HTTP POST, wykonuje na nich operację szyfrowania i, jeśli zdefiniowana jest zmienna środowiskowa o nazwie PREFIX, dołącza do hasha tę wartość. Po obliczeniu wartości szyfru aplikacja wysyła wynik w kanale „calculation” na wskazanym serwerze Redis.

Aby zademonstrować funkcję obsługi wielu klastrów, użyjemy później zmiennej środowiskowej PREFIX.

Informacyjnie: to pakiety używane przez aplikację.

  • body-parser: Pozwala na analizowanie żądań HTTP
  • cors: Zezwalanie na korzystanie z mechanizmu CORS
  • dotenv: Łatwe parsowanie zmiennych środowiskowych
  • express: Łatwy hosting stron internetowych
  • ioredis: Biblioteka klienta do komunikacji z bazami danych Redis
  • morgan: Dostarcza uporządkowanego dziennika

Frontend

Nasz front-end to także aplikacja NodeJS, która hostuje stronę internetową za pomocą express. Użytkownik podaje częstotliwość, z jaką ma być wysyłana aplikacja worker. Ta aplikacja subskrybuje też wiadomości na kanale Redis o nazwie „calculation” i wyświetla wyniki na stronie internetowej.

Aplikacja korzysta z tych zależności:

  • body-parser: Pozwala na analizowanie żądań HTTP
  • dotenv: Łatwe parsowanie zmiennych środowiskowych
  • express: Łatwy hosting stron internetowych
  • ioredis: Biblioteka klienta do komunikacji z bazami danych Redis
  • morgan: Dostarcza uporządkowanych logów
  • request: Zezwalanie na wysyłanie żądań HTTP
  • socket.io: Umożliwia dwukierunkową komunikację ze strony internetowej na serwer

Ta strona internetowa używa Bootstrap do stylizacji i po uruchomieniu wygląda tak:

e5e3b9cbede4cac4.png

Diagram architektury

7ae4bc22a58f80a6.png

Diagram wdrożeniowy

Ostateczną wersję aplikacji wdrożymy na 2 klastrach, które utworzyliśmy. W klastrze primary będą zainstalowane wszystkie komponenty (frontend, worker i Redis), ale w klastrze burst będzie zainstalowana tylko aplikacja worker.

Oto diagram przedstawiający 2 klastry. Pola obrysowane na czerwono to usługi Kubernetes, a pola obrysowane na niebiesko to wdrożenia Kubernetes. Żółte pola oznaczają naszą instalację Istio.

561db37c510944bd.png

Zwróć uwagę, że klaster burst nadal ma wdrożone usługi Redis, mimo że w klastrze nie ma żadnego wdrożenia Redis. Musimy mieć tę usługę w klastrze, aby Kubernetes DNS mógł rozpoznać żądanie, ale gdy żądanie zostanie faktycznie wysłane, Istio Proxy przekieruje je do wdrożenia Redis w klastrze primary.

Ostateczna aplikacja będzie miała dodatkowe wdrożenie działające w klastrze primary o nazwie istiowatcher.. Umożliwi to dynamiczne przekierowywanie ruchu do burst automatycznie, gdy ruch przekroczy określony próg.

8f6183bdfc3f813c.png

9. Tworzenie plików wdrożenia aplikacji

Aby wdrożyć aplikację, musimy utworzyć zestaw plików manifestu Kubernetes.

Przejdź do katalogu głównego projektu i utwórz nowy folder o nazwie kubernetes.

mkdir ${proj}/kubernetes && cd ${proj}/kubernetes

Tworzenie pliku frontend.yaml

Spowoduje to utworzenie zarówno wdrożenia Kubernetes, jak i usługi, aby uzyskać dostęp do obrazu frontendu.

Wstaw te informacje do pliku frontend.yaml.

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: frontend-deployment
  labels:
    app: frontend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: gcr.io/istio-burst-workshop/frontend
        ports:
        - containerPort: 8080
        readinessProbe:
            initialDelaySeconds: 10
            httpGet:
              path: "/_healthz"
              port: 8080
              httpHeaders:
              - name: "Cookie"
                value: "istio_session-id=x-readiness-probe"
        livenessProbe:
          initialDelaySeconds: 10
          httpGet:
            path: "/"
            port: 8080
            httpHeaders:
            - name: "Cookie"
              value: "istio_session-id=x-liveness-probe"
        env:
        - name: PORT
          value: "8080"
        - name: PROCESSOR_URL
          value: "http://worker-service"
        - name: REDIS_URL
          value: "redis-cache-service:6379"
---
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  type: ClusterIP
  selector:
    app: frontend
  ports:
  - name: http
    port: 80
    targetPort: 8080

Najważniejsze informacje w Deployment

  • Port, na którym ma działać aplikacja, to 8080
  • Adres pracownika został ustawiony jako „http://worker-service”, a Kubernetes użyje wbudowanej funkcji DNS, aby rozpoznać powstałą usługę.
  • Adresem naszego REDIS_URL jest „redis-cache-service:6379”, a do rozwiązywania adresów IP będziemy używać wbudowanej w Kubernetes funkcję DNS.
  • W kontenerze skonfigurowaliśmy też sondy livenessreadiness, aby poinformować Kubernetes, że kontener jest uruchomiony.

Tworzenie pliku worker-service.yaml

Definicję usługi Kubernetes zapisujemy w osobnym pliku niż definicja wdrożenia, ponieważ będziemy używać tej usługi w różnych klastrach, ale wdrożenie będzie inne dla każdego klastra.

Wstaw te informacje do pliku worker-service.yaml

apiVersion: v1
kind: Service
metadata:
 name: worker-service
spec:
 type: ClusterIP
 selector:
   app: worker
 ports:
 - name: http
   port: 80
   targetPort: 8081

Tworzenie pliku worker-primary.yaml

Będzie to wdrożenie worker, które prześlemy do klastra głównego.

Wstaw te informacje do pliku worker-primary.yaml.

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
 name: worker-deployment
 labels:
   app: worker
spec:
 replicas: 1
 selector:
   matchLabels:
     app: worker
 template:
   metadata:
     labels:
       app: worker
       cluster-type: primary-cluster
   spec:
     containers:
     - name: worker
       image: gcr.io/istio-burst-workshop/worker
       imagePullPolicy: Always
       ports:
       - containerPort: 8081
       readinessProbe:
           initialDelaySeconds: 10
           httpGet:
             path: "/_healthz"
             port: 8081
             httpHeaders:
             - name: "Cookie"
               value: "istio_session-id=x-readiness-probe"
       livenessProbe:
         initialDelaySeconds: 10
         httpGet:
           path: "/"
           port: 8081
           httpHeaders:
           - name: "Cookie"
             value: "istio_session-id=x-liveness-probe"
       env:
       - name: PORT
         value: "8081"
       - name: REDIS_URL
         value: "redis-cache-service:6379"

Zwróć uwagę, że używamy tu tego samego wzorca, podając sondy livenessreadiness oraz podając zmienne środowiskowe PORTREDIS_URL do użycia przez naszą aplikację.

Kolejną rzeczą, na którą warto zwrócić uwagę w tym wdrożeniu, jest brak zmiennej środowiskowej PREFIX. Oznacza to, że wyniki naszych obliczeń będą zawierać tylko hasze (bez prefiksów).

Ostatnim ważnym punktem tego wdrożenia jest etykieta cluster-type: primary-cluster. Użyjemy tego później, gdy będziemy kierować ruch w wielu klastrach Istio.

Tworzenie pliku redis.yaml

Komunikacja z naszego workera z frontendem odbywa się przez kanał Redis, dlatego musimy wdrożyć aplikację Redis w naszym klastrze.

Wstaw te informacje do pliku redis.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
 name: redis-cache
spec:
 template:
   metadata:
     labels:
       app: redis-cache
   spec:
     containers:
     - name: redis
       image: redis:alpine
       ports:
       - containerPort: 6379
       readinessProbe:
         periodSeconds: 5
         tcpSocket:
           port: 6379
       livenessProbe:
         periodSeconds: 5
         tcpSocket:
           port: 6379
       volumeMounts:
       - mountPath: /data
         name: redis-data
       resources:
         limits:
           memory: 256Mi
           cpu: 125m
         requests:
           cpu: 70m
           memory: 200Mi
     volumes:
     - name: redis-data
       emptyDir: {}

To jest wdrożenie półstandardowe aplikacji Redis. Tworzy kontener na podstawie obrazu redis:alpine, udostępnia odpowiednie porty i ustawia sensowne limity zasobów.

Tworzenie pliku redis-service.yaml

Potrzebujemy usługi Kubernetes, aby komunikować się z naszą aplikacją Redis

Wstaw te informacje do pliku redis-service.yaml

apiVersion: v1
kind: Service
metadata:
 name: redis-cache-service
spec:
 type: ClusterIP
 selector:
   app: redis-cache
 ports:
 - port: 6379
   targetPort: 6379

Usługa o nazwie redis-cache-service ma teraz dostęp do naszego wdrożenia Redis.

10. Wdrażanie aplikacji

Obrazy zostały przesłane do GCR, a pliki manifestu Kubernetes zostały utworzone, więc teraz możesz wdrożyć aplikację i sprawdzić, jak działa.

Aby wdrożyć aplikację, uruchom te polecenia

  1. Upewnij się, że jesteśmy w prawidłowym gromadzie
kubectx primary
  1. Wdrażanie pamięci podręcznej Redis
kubectl apply -f redis.yaml
  1. Wdrażanie usługi Redis
kubectl apply -f redis-service.yaml
  1. Wdrażanie frontendu
kubectl apply -f frontend.yaml
  1. Wdrażanie pracownika
kubectl apply -f worker-primary.yaml
  1. Wdrażanie usługi worker
kubectl apply -f worker-service.yaml

Wdrożyliśmy naszą aplikację w GKE. Gratulacje!

Test

Poczekaj, aż pody będą dostępne online

kubectl get pods -w

Gdy wszystkie pody są w stanie „Uruchomione”, naciśnij Ctrl + C.

NAME                                   READY     STATUS    RESTARTS   AGE
frontend-deployment-695d95fbf7-76sd8   1/1       Running   0          2m
redis-cache-7475999bf5-nxj8x           1/1       Running   0          2m
worker-deployment-5b9cf9956d-g975p     1/1       Running   0          2m

Zauważysz, że nie udostępniliśmy interfejsu frontendowego za pomocą LoadBalancera. Dzieje się tak, ponieważ później będziemy uzyskiwać dostęp do aplikacji za pomocą Istio. Aby sprawdzić, czy wszystko działa prawidłowo, użyjemy usługi kubectl port-forward.. Uruchom to polecenie, aby przekierować port 8080 na komputerze lokalnym (lub w Cloud Shell) na port 8080, na którym działa wdrożenie frontend.

kubectl port-forward \
$(kubectl get pods -l app=frontend -o jsonpath='{.items[0].metadata.name}') \
8080:8080

Jeśli używasz wersji lokalnej: otwórz przeglądarkę i przejdź do adresu http://localhost:8080.

Jeśli używasz Cloud Shell: kliknij przycisk „Podgląd w przeglądarce” i wybierz „Podgląd na porcie 8080”.

bdb5dc75f415be11.png

Powinieneś zobaczyć interfejs. Jeśli w polu „Częstotliwość” wpiszesz liczbę, powinny zacząć pojawiać się znaki ukośnikowe.

1caafaffab26897a.png

Gratulacje! Wszystko działa.

Kliknij Ctrl+C, aby zatrzymać przekierowywanie portu.

11. Czyszczenie wdrożonej aplikacji

Zaczniemy stosować Istio w klastrze, a następnie ponownie wdrożymy aplikację, więc najpierw ją wyczyśćmy.

Aby usunąć wszystkie utworzone właśnie wdrożenia i usługi, uruchom te polecenia

  1. Usuń: redis-cache-service
kubectl delete -f redis-service.yaml
  1. Usuń: redis
kubectl delete -f redis.yaml
  1. Usuń: frontend
kubectl delete -f frontend.yaml
  1. Usuń: worker
kubectl delete -f worker-primary.yaml
  1. Usuń: worker-service
kubectl delete -f worker-service.yaml

12. Instalowanie Istio w klastrze głównym

Pobierz Istio

Wersje Istio są hostowane w GitHub. Poniższe polecenia pobiorą i rozpakują wersję 1.0.0 pakietu istio.

  1. Przejście do katalogu głównego projektu
cd ${proj}
  1. Pobieranie archiwum
curl -LO https://github.com/istio/istio/releases/download/1.0.0/istio-1.0.0-linux.tar.gz
  1. wyodrębnij i usuń archiwum.
tar xzf istio-1.0.0-linux.tar.gz && rm istio-1.0.0-linux.tar.gz

Tworzenie szablonu Istio

Uruchomienie tego polecenia Helm spowoduje utworzenie szablonu do zainstalowania Istio w klastrze.

helm template istio-1.0.0/install/kubernetes/helm/istio \
--name istio --namespace istio-system \
--set prometheus.enabled=true \
--set servicegraph.enabled=true  > istio-primary.yaml

W bieżącym katalogu zostanie utworzony plik o nazwie istio-primary.yaml, który zawiera wszystkie definicje i specyfikacje potrzebne do wdrażania i uruchamiania Istio.

Zwróć uwagę na 2 parametry --set. Dodają one obsługę Prometheus i ServiceGraph do systemu Istio. Użyjemy jej później w tym laboratorium.

Wdrażanie Istio

Aby wdrożyć istio, musisz najpierw utworzyć przestrzeń nazw o nazwie istio-system, w której będą mogły działać wdrożenia i usługi Istio.

kubectl create namespace istio-system

Na koniec zastosuj plik istio-primary.yaml utworzony za pomocą Helm.

kubectl apply -f istio-primary.yaml

Etykieta domyślnej przestrzeni nazw

Istio działa poprzez wstrzyknięcie pomocniczej usługi proxy do każdej z Twoich implementacji. Jest to działanie opcjonalne, dlatego musimy oznaczyć naszą przestrzeń nazw default etykietą istio-injection=enabled, aby Istio mogło automatycznie wstrzyknąć sidecar.

kubectl label namespace default istio-injection=enabled

Gratulacje! Mamy uruchomiony klaster z Istio, który jest gotowy do wdrożenia aplikacji.

13. Wdrażanie aplikacji z zarządzaniem ruchem Istio

Tworzenie plików konfiguracji zarządzania ruchem Istio

Istio działa podobnie do Kubernetes, ponieważ do konfiguracji używa plików yaml. W tym celu musimy utworzyć zestaw plików, które informują Istio, jak udostępniać i przekierowywać ruch.

Utwórz katalog o nazwie istio-manifests i przejdź do niego

mkdir ${proj}/istio-manifests && cd ${proj}/istio-manifests

Tworzenie pliku frontend-gateway.yaml

Ten plik udostępnia klaster Kubernetes w sposób podobny do systemu równoważenia obciążenia GKE i kieruje cały ruch przychodzący do naszej usługi front-end.

Utwórz plik o nazwie frontend-gateway.yaml i wstaw ten kod.

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
 name: frontend-gateway
spec:
 selector:
   istio: ingressgateway # use Istio default gateway implementation
 servers:
 - port:
     number: 80
     name: http
     protocol: HTTP
   hosts:
   - "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: frontend-ingress-virtual-service
spec:
 hosts:
 - "*"
 gateways:
 - frontend-gateway
 http:
 - route:
   - destination:
       host: frontend-service
       port:
         number: 80

Tworzenie pliku redis-virtualservice.yaml

Utwórz plik o nazwie redis-virtualservice.yaml i wstaw ten kod

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: redis-virtual-service
spec:
 hosts:
 - redis-cache-service
 gateways:
 - mesh
 tcp:
 - route:
   - destination:
       host: redis-cache-service.default.svc.cluster.local

Tworzenie pliku worker-virtualservice.yaml

Utwórz plik o nazwie worker-virtualservice.yaml i wstaw ten kod

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: worker-virtual-service
spec:
 hosts:
 - worker-service
 gateways:
 - mesh
 http:
 - route:
   - destination:
       host: worker-service.default.svc.cluster.local   
       port:
         number: 80

Wdrażanie zasad zarządzania ruchem Istio

Wdrażanie zasad Istio odbywa się w taki sam sposób jak w przypadku innych zasobów Kubernetes, z kubectl apply

  1. Zastosowanie bramy
kubectl apply -f frontend-gateway.yaml
  1. Zastosuj naszą usługę Redis VirtualService
kubectl apply -f redis-virtualservice.yaml
  1. Zastosuj naszą usługę wirtualną Worker
kubectl apply -f worker-virtualservice.yaml

Wdrażanie aplikacji

  1. Wróć do katalogu kubernetes
cd ${proj}/kubernetes
  1. Wdrażanie pamięci podręcznej Redis
kubectl apply -f redis.yaml
  1. Wdrażanie usługi Redis
kubectl apply -f redis-service.yaml
  1. Wdrażanie frontendu
kubectl apply -f frontend.yaml
  1. Wdrażanie pracownika
kubectl apply -f worker-primary.yaml
  1. Wdrażanie usługi worker
kubectl apply -f worker-service.yaml

Potwierdź

W tej chwili ponownie wdrożyliśmy naszą aplikację w klastrze z Istio i zasadami zarządzania ruchem.

Poczekajmy, aż wszystkie zadania zostaną uruchomione

Gdy wszystkie urządzenia są online, pobierz IngressGateway skonfigurowany w frontend-ingressgateway.yaml

$ kubectl -n istio-system get svc istio-ingressgateway
NAME                   TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)                                                                                                     AGE
istio-ingressgateway   LoadBalancer   10.36.3.112   35.199.158.10   80:31380/TCP,

Przejdź do adresu <EXTERNAL-IP> lub użyj polecenia curl, aby wyświetlić frontend.

$ curl 35.199.158.10
<!doctype html>
<html>

<head>
    <title>String Hashr</title>
    <!-- Bootstrap -->
...

14. Instalowanie Istio w klastrze „burst”

Poświęciliśmy dużo czasu na konfigurowanie i wdrażanie na klastrze primary, ale mamy jeszcze cały inny klaster do wdrożenia.

W tej sekcji musimy pobrać zmienne konfiguracji z obu klastrów, więc zwróć uwagę, do którego klastra odwołuje się każde polecenie.

Tworzenie pliku manifestu Istio Remote

Podobnie jak w przypadku wdrożenia Istio do klastra primary, użyjemy Helma do stworzenia szablonu wdrożenia usługi istio remote do klastra burst. Zanim to zrobimy, musimy uzyskać informacje o naszym klastrze primary

Zbieranie informacji o klastrze podstawowym

Zmień klaster na primary

kubectx primary

Poniższe polecenia umożliwiają pobranie adresów IP różnych podów w klastrze głównym. Są one używane przez Istio Remote do komunikacji z klastrem głównym.

export PILOT_POD_IP=$(kubectl -n istio-system get pod -l istio=pilot -o jsonpath='{.items[0].status.podIP}')
export POLICY_POD_IP=$(kubectl -n istio-system get pod -l istio-mixer-type=policy -o jsonpath='{.items[0].status.podIP}')
export STATSD_POD_IP=$(kubectl -n istio-system get pod -l istio=statsd-prom-bridge -o jsonpath='{.items[0].status.podIP}')
export TELEMETRY_POD_IP=$(kubectl -n istio-system get pod -l istio-mixer-type=telemetry -o jsonpath='{.items[0].status.podIP}')
export ZIPKIN_POD_IP=$(kubectl -n istio-system get pod -l app=jaeger -o jsonpath='{range .items[*]}{.status.podIP}{end}')

Tworzenie szablonu zdalnego

Teraz użyjemy helm do utworzenia pliku o nazwie istio-remote-burst.yaml, który następnie możemy wdrożyć do klastra burst.

Zmiana katalogu głównego projektu

cd $proj
helm template istio-1.0.0/install/kubernetes/helm/istio-remote --namespace istio-system \
--name istio-remote \
--set global.remotePilotAddress=${PILOT_POD_IP} \
--set global.remotePolicyAddress=${POLICY_POD_IP} \
--set global.remoteTelemetryAddress=${TELEMETRY_POD_IP} \
--set global.proxy.envoyStatsd.enabled=true \
--set global.proxy.envoyStatsd.host=${STATSD_POD_IP} \
--set global.remoteZipkinAddress=${ZIPKIN_POD_IP} > istio-remote-burst.yaml

Instalowanie Istio Remote w klastrze burst

Aby zainstalować Istio w klastrze burst, musimy wykonać te same czynności, co w przypadku instalacji w klastrze primary, ale zamiast pliku istio-remote-burst.yaml musimy użyć pliku istio-remote-burst.yaml.

Zmiana kubecontext na burst

kubectx burst

Tworzenie przestrzeni nazw istio-system

kubectl create ns istio-system

Zastosuj plik istio-burst.yaml

kubectl apply -f istio-remote-burst.yaml

Domyślna przestrzeń nazw etykiety

Ponownie musimy oznaczyć przestrzeń nazw default, aby można było automatycznie wstrzyknąć serwer proxy.

kubectl label namespace default istio-injection=enabled

Gratulacje! W tym momencie skonfigurowaliśmy Istio Remote w klastrze burst. W tej chwili klastry nadal nie mogą się komunikować. Musimy wygenerować plik kubeconfig dla klastra burst, który możemy wdrożyć w klastrze primary, aby je połączyć.

Tworzenie pliku kubeconfig dla klastra „burst”

Przełączanie się na klaster burst

kubectx burst

Konfigurowanie środowiska

Aby utworzyć plik kubeconfig dla klastra, musimy zebrać informacje o tym klastrze.

  1. Pobieranie nazwy klastra
CLUSTER_NAME=$(kubectl config view --minify=true -o "jsonpath={.clusters[].name}")
  1. Pobieranie nazwy serwera klastra
SERVER=$(kubectl config view --minify=true -o "jsonpath={.clusters[].cluster.server}")
  1. Pobierz nazwę obiektu tajnego urzędu certyfikacji konta usługi istio-multi
SECRET_NAME=$(kubectl get sa istio-multi -n istio-system -o jsonpath='{.secrets[].name}')
  1. Pobierz dane urzędu certyfikacji zapisane w poprzednim słowie tajnym.
CA_DATA=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['ca\.crt']}")
  1. Pobierz token zapisany w poprzednim słowie tajnym.
TOKEN=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['token']}" | base64 --decode)

Tworzenie pliku kubeconfig

Po ustawieniu wszystkich zmiennych środowiskowych musimy utworzyć plik kubeconfig.

cat <<EOF > burst-kubeconfig
apiVersion: v1
clusters:
   - cluster:
       certificate-authority-data: ${CA_DATA}
       server: ${SERVER}
     name: ${CLUSTER_NAME}
contexts:
   - context:
       cluster: ${CLUSTER_NAME}
       user: ${CLUSTER_NAME}
     name: ${CLUSTER_NAME}
current-context: ${CLUSTER_NAME}
kind: Config
preferences: {}
users:
   - name: ${CLUSTER_NAME}
     user:
       token: ${TOKEN}
EOF

W bieżącym katalogu zostanie utworzony nowy plik o nazwie burst-kubeconfig, którego klaster primary może używać do uwierzytelniania i zarządzania klastrem burst.

Powrót do klastra podstawowego

kubectx primary

Zastosuj plik kubeconfig do „burstu”, tworząc obiekt tajny i oznaczając go

kubectl create secret generic burst-kubeconfig --from-file burst-kubeconfig -n istio-system

Oznacz tajny klucz etykietą, aby Istio wiedziało, że ma go używać do uwierzytelniania w wielu klastrach.

kubectl label secret burst-kubeconfig istio/multiCluster=true -n istio-system

Gratulacje! Oba klastry są uwierzytelniane i komunikują się ze sobą za pomocą usługi Istio Multicluster. Wdróż aplikację w wielu klastrach

15. Wdrażanie aplikacji w wielu klastrach

Tworzenie wdrożeń

Przejdź do katalogu kubernetes.

cd ${proj}/kubernetes

Utwórz wdrożenie workera dla klastra „burst”: worker-burst.yaml

Utwórz plik o nazwie worker-burst.yaml i wstaw do niego ten kod:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: worker-deployment
  labels:
    app: worker
spec:
  replicas: 1
  selector:
    matchLabels:
      app: worker
  template:
    metadata:
      labels:
        app: worker
        cluster-type: burst-cluster
    spec:
      containers:
      - name: worker
        image: gcr.io/istio-burst-workshop/worker
        imagePullPolicy: Always
        ports:
        - containerPort: 8081
        readinessProbe:
            initialDelaySeconds: 10
            httpGet:
              path: "/_healthz"
              port: 8081
              httpHeaders:
              - name: "Cookie"
                value: "istio_session-id=x-readiness-probe"
        livenessProbe:
          initialDelaySeconds: 10
          httpGet:
            path: "/"
            port: 8081
            httpHeaders:
            - name: "Cookie"
              value: "istio_session-id=x-liveness-probe"
        env:
        - name: PORT
          value: "8081"
        - name: REDIS_URL
          value: "redis-cache-service:6379"
        - name: PREFIX
          value: "bursty-"

Zauważ, że jest to prawie identyczne z plikiem worker-primary.yaml, który utworzyliśmy wcześniej. Istnieją 2 kluczowe różnice.

Pierwszą kluczową różnicą jest dodanie zmiennej środowiskowej PREFIX o wartości „bursty-”.

env:
- name: PORT
  value: "8081"
- name: REDIS_URL
  value: "redis-cache-service:6379"
- name: PREFIX
  value: "bursty-"

Oznacza to, że nasz pracownik w klastrze burst będzie dodawał do wszystkich wysyłanych przez siebie haszy prefiks „bursty-”. Dzięki temu będziemy wiedzieć, że nasza aplikacja działa w wielu klastrach.

Drugą ważną różnicą jest to, że zmieniliśmy etykietę cluster-type w tym wdrożeniu z primary-cluster na burst-cluster.

labels:
  app: worker
  cluster-type: burst-cluster

Użyjemy tej etykiety później, gdy zaktualizujemy usługę VirtualService.

Modyfikowanie usług Istio

Obecnie nasze usługi Istio nie korzystają z obu naszych wdrożeń. 100% naszego ruchu jest kierowane do klastra „primary” (głównego). Zmieńmy to.

Przejdź do katalogu istio-manifests

cd ${proj}/istio-manifests

Edytowanie pliku worker-virtualservice.yaml w celu uwzględnienia reguł docelowych

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: worker-virtual-service
spec:
  hosts:
  - worker-service
  gateways:
  - mesh
  http:
  - route:
    - destination:
        host: worker-service.default.svc.cluster.local    
        subset: primary
        port:
          number: 80        
      weight: 50
    - destination:
        host: worker-service.default.svc.cluster.local     
        subset: burst  
        port:
          number: 80        
      weight: 50
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: worker-destination-rule
spec:
  host: worker-service
  trafficPolicy:
    loadBalancer:
      simple: RANDOM
  subsets:
  - name: primary
    labels:
      cluster-type: primary-cluster
  - name: burst
    labels:
     cluster-type: burst-cluster

Dodaliśmy 2 miejsca docelowe do usługi VirtualService. Nadal odwołuje się do tego samego hosta (worker-service.default.svc.cluster.local)), ale 50% ruchu jest kierowane do podzbioru primary, a drugie 50% – do podzbioru burst.

Podzbiór primary to wdrożenia o etykiecie cluster-type: primary-cluster, a podzbiór burst to wdrożenia o etykiecie cluster-type: burst-cluster.

W ten sposób ruch jest dzielony po równo między 2 klastery.

Wdróż na klastrze

Wdróż plik redis-service.yaml do klastra burst

Zmień na plik kubeconfig burst

kubectx burst

Zmień katalog główny projektu

cd ${proj}

Następnie wdróż

Wdróż plik redis-service.yaml w klastrze burst

kubectl apply -f kubernetes/redis-service.yaml

Wdróż plik worker-burst.yaml w klastrze burst

kubectl apply -f kubernetes/worker-burst.yaml

Wdróż plik worker-service.yaml w klastrze burst

kubectl apply -f kubernetes/worker-service.yaml

Stosowanie VirtualServices usługi Istio

Zmień na plik kubeconfig primary

kubectx primary

Następnie Wdróż

kubectl apply -f istio-manifests/worker-virtualservice.yaml

Sprawdzanie działania

Aby sprawdzić, czy działa, otwórz punkt Istio Ingress i zwróć uwagę, że około 50% haszy ma przedrostek „burst-”.

78fb6e235e9f4a07.png

Oznacza to, że rozmawiamy między klastrami. Spróbuj zmienić wagi różnych usług i zastosować plik worker-virtualservice.yaml. To świetny sposób na zrównoważenie ruchu między klastrami, ale co, jeśli możemy zrobić to automatycznie?

16. Korzystanie ze wskaźników Prometheus

Wprowadzenie do Prometheus

Prometheus to pakiet narzędzi do monitorowania i tworzenia alertów systemowych typu open source, który został pierwotnie opracowany w SoundCloud. Utrzymuje on wielowymiarowy model danych z danymi z ciągów czasowych zidentyfikowanymi za pomocą nazwy danych i par klucz-wartość.

Oto schemat architektury Prometheus:

601e1155a825e0c2.png

Wdrożony z Prometheusem Istio automatycznie przesyła różne dane do serwera Prometheus. Możemy używać tych danych do dynamicznego zarządzania klastrami.

Poznawanie wskaźników Prometheus

Na początek musimy udostępnić wdrożenie Prometheus.

W GKE otwórz kartę Zadania i przejdź do zadania „prometheus”.

b4a7a3cd67db05b3.png

Gdy wyświetlasz szczegóły wdrożenia, kliknij Działania > Udostępnij.

c04a482e55bdfd41.png

Wybierz przekierowanie na port 9090 i wpisz „System równoważenia obciążenia”.

d5af3ba22a7a6ebb.png

I wybierz „Odsłonięcie”.

Spowoduje to utworzenie usługi na publicznym adresie IP, którego możemy używać do przeglądania danych Prometheus.

Poczekaj, aż punkt końcowy zacznie działać, a następnie kliknij adres IP obok „Punkty końcowe zewnętrzne”. b1e40ad90851da29.png

Powinien być teraz widoczny interfejs Prometheus.

ed273552270337ec.png

Prometheus dostarcza wystarczającej ilości danych, aby stanowił samodzielne narzędzie. Na razie jednak zaczniemy od wskaźnika istio_requests_total.

Wykonywanie tego zapytania zwraca wiele danych. To dane dotyczące wszystkich żądań, które przechodzą przez siatkę usług Istio, a jest ich naprawdę dużo. Zmieńmy wyrażenie, aby zawęzić zakres wyszukiwania do tego, co nas naprawdę interesuje:

żądania, w których usługa docelowa to worker-service.default.svc.cluster.local, a źródło to frontend-deployment, ograniczone do ostatnich 15 sekund;

Zapytanie wygląda tak:

istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s]

Dzięki temu mamy do czynienia z łatwiejszym w obsługiwaniu zbiorem danych.

19d551fd5eac3785.png

Ale jest trochę gęsty. Chodzi nam o żądania na sekundę, a nie o wszystkie żądania.

Aby to zrobić, możemy użyć wbudowanej funkcji rate.

rate(istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s])

dbb9dc063a18da9b.png

To nas zbliża do celu, ale te dane musimy jeszcze bardziej zgrupować w logiczną grupę.

Aby to zrobić, możemy użyć słów kluczowych sumby do zgrupowania i zsumowania wyników.

sum(rate(istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s])) by (source_workload,
source_app, destination_service)

898519966930ec56.png

Super! Z serwera Prometheus możemy pobierać dokładne dane, których potrzebujemy.

Nasze ostatnie zapytanie Prometheus

Na podstawie wszystkich informacji, które udało nam się uzyskać, ostatnie zapytanie, jakie musimy przesłać do Prometheus, to

sum(rate(istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s])) by (source_workload,
source_app, destination_service)

Teraz możemy używać ich interfejsu HTTP API do uzyskiwania danych.

Możemy wysłać do niego zapytanie, przesyłając żądanie HTTP GET w taki sposób.

curl http://<prometheus-ip-here>/api/v1/query?query=sum\(rate\(istio_requests_total%7Breporter%3D%22destination%22%2C%0Adestination_service%3D%22worker-service.default.svc.cluster.local%22%2C%0Asource_workload%3D%22frontend-deployment%22%7D%5B15s%5D\)\)%20by%20\(source_workload%2C%0Asource_app%2C%20destination_service\)

Oto przykład odpowiedzi:

{
    "status": "success",
    "data": {
        "resultType": "vector",
        "result": [
            {
                "metric": {
                    "destination_service": "worker-service.default.svc.cluster.local",
                    "source_app": "frontend",
                    "source_workload": "frontend-deployment"
                },
                "value": [
                    1544404907.503,
                    "18.892886390062788"
                ]
            }
        ]
    }
}

Teraz możemy wyodrębnić wartość danych z pliku JSON.

Czyszczenie

Musimy usunąć usługę, której właśnie użyliśmy do udostępnienia Prometheus. W konsoli Google Cloud Console przejdź do góry utworzonej właśnie usługi i kliknij „Usuń”.

d58cb51b4c922751.png

Dalsze kroki:

Po znalezieniu sposobu na wykrycie, jak ruch przemieszcza się przez klaster i z jaką szybkością, następnym krokiem jest napisanie małego pliku binarnego, który okresowo wysyła zapytania do Prometheus. Jeśli liczba żądań na sekundę do worker przekroczy pewien próg, usługa wirtualna pracownika zastosuje różne wagi docelowe w celu wysłania całego ruchu do klastra burst. Gdy liczba żądań na sekundę spadnie poniżej niższego progu, prześlij cały ruch z powrotem do primary.

17. Tworzenie przepustowości w wielu klastrach

Konfiguracja

Ustawianie całego ruchu usługi worker na klastrze głównym

Uważamy, że cały ruch kierowany do worker-service i przekierowywany do klastra primary jest „domyślnym” stanem naszej aplikacji.

Zmień $proj/istio-manifests/worker-virtualservice.yaml tak, aby wyglądał tak:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: worker-virtual-service
spec:
  hosts:
  - worker-service
  gateways:
  - mesh
  http:
  - route:
    - destination:
        host: worker-service.default.svc.cluster.local    
        subset: primary
        port:
          number: 80        
      weight: 100
    - destination:
        host: worker-service.default.svc.cluster.local     
        subset: burst  
        port:
          number: 80        
      weight: 0
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: worker-destination-rule
spec:
  host: worker-service
  trafficPolicy:
    loadBalancer:
      simple: RANDOM
  subsets:
  - name: primary
    labels:
      cluster-type: primary-cluster
  - name: burst
    labels:
     cluster-type: burst-cluster

Sprawdź, czy masz połączenie z klastrem primary

kubectx primary

Zastosuj istio-manifests/worker-virtualservice.yaml

kubectl apply -f istio-manifests/worker-virtualservice.yaml

Tworzenie demona istiowatcher

Użyjemy Go do napisania tej usługi ze względu na szybkość i przenośność. Ogólny przebieg działania aplikacji będzie polegał na uruchamianiu i co 2 sekundy wysyłaniu zapytania do Prometheusa.

Utwórz w katalogu src nowy katalog o nazwie istiowatcher.

mkdir -p ${proj}/src/istiowatcher && cd ${proj}/src/istiowatcher

Będziemy wywoływać istioctl z poziomu kontenera, aby manipulować płaszczyzną sterowania Istio z poziomu klastra.

Tworzenie pliku istiowatcher.go

Utwórz w tym katalogu plik o nazwie istiowatcher.go i wklej w nim ten kod:

package main

import (
        "github.com/tidwall/gjson"
        "io/ioutil"
        "log"
        "net/http"
        "os/exec"
        "time"
)

func main() {
        //These are in requests per second
        var targetLow float64 = 10
        var targetHigh float64 = 15
        // This is for the ticker in milliseconds
        ticker := time.NewTicker(1000 * time.Millisecond)

        isBurst := false

        // Our prometheus query
        reqQuery := `/api/v1/query?query=sum(rate(istio_requests_total{reporter="destination",destination_service="worker-service.default.svc.cluster.local",source_workload="frontend-deployment"}[15s]))by(source_workload,source_app,destination_service)`

        for t := range ticker.C {
                log.Printf("Checking Prometheus at %v", t)

                // Check prometheus
                // Note that b/c we are querying over the past 5 minutes, we are getting a very SLOW ramp of our reqs/second
                // If we wanted this to be a little "snappier" we can scale it down to say 30s
                resp, err := http.Get("http://prometheus.istio-system.svc.cluster.local:9090" + reqQuery)
                if err != nil {
                        log.Printf("Error: %v", err)
                        continue
                }
                defer resp.Body.Close()
                body, _ := ioutil.ReadAll(resp.Body)

                val := gjson.Get(string(body), "data.result.0.value.1")
                log.Printf("Value: %v", val)

                currentReqPerSecond := val.Float()
                log.Printf("Reqs per second %f", currentReqPerSecond)

                if currentReqPerSecond > targetHigh && !isBurst {
                        applyIstio("burst.yaml")
                        log.Println("Entering burst mode")
                        isBurst = true
                } else if currentReqPerSecond < targetLow && isBurst {
                        applyIstio("natural.yaml")
                        log.Println("Returning to natural state.")
                        isBurst = false
                }
        }
}

func applyIstio(filename string) {
        cmd := exec.Command("istioctl", "replace", "-f", filename)
        if err := cmd.Run(); err != nil {
                log.Printf("Error hit applying istio manifests: %v", err)
        }
}

Tworzenie pliku Dockerfile

Utwórz nowy plik o nazwie Dockerfile i wstaw do niego ten kod.

FROM golang:1.11.2-stretch as base

FROM base as builder

WORKDIR /workdir
RUN curl -LO https://github.com/istio/istio/releases/download/1.0.0/istio-1.0.0-linux.tar.gz
RUN tar xzf istio-1.0.0-linux.tar.gz
RUN cp istio-1.0.0/bin/istioctl ./istioctl

FROM base 

WORKDIR /go/src/istiowatcher
COPY . .
COPY --from=builder /workdir/istioctl /usr/local/bin/istioctl

RUN go get -d -v ./...
RUN go install -v ./...

CMD ["istiowatcher"]

Ten wieloetapowy plik Dockerfile pobiera i wyodrębnia wersję 1.0.0 usługi Istio na pierwszym etapie. Drugi etap kopiuje wszystko z naszego katalogu do obrazu, a potem kopiuje istioctl z etapu kompilacji do /usr/local/bin (aby można było go wywołać z naszej aplikacji), pobiera zależności, kompiluje kod i ustawia CMD na „istiowatcher”.

Tworzenie pliku burst.yaml

Ten plik istiowatcher zostanie zastosowany, gdy liczba żądań na sekundę do usługi worker z usługi frontend przekroczy 15.

Utwórz nowy plik o nazwie burst.yaml i wstaw do niego ten kod.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: worker-virtual-service
spec:
 hosts:
 - worker-service
 gateways:
 - mesh
 http:
 - route:
   - destination:
       host: worker-service.default.svc.cluster.local   
       subset: primary
       port:
         number: 80       
     weight: 0
   - destination:
       host: worker-service.default.svc.cluster.local    
       subset: burst 
       port:
         number: 80       
     weight:  100

Tworzenie pliku natural.yaml

Uważamy, że jest to „naturalny” stan, do którego wracamy, gdy liczba żądań na sekundę w zakresie od frontend do worker spadnie poniżej 10. W tym stanie 100% ruchu jest kierowane do klastra primary.

Utwórz nowy plik o nazwie natural.yaml i wstaw do niego ten kod:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: worker-virtual-service
spec:
 hosts:
 - worker-service
 gateways:
 - mesh
 http:
 - route:
   - destination:
       host: worker-service.default.svc.cluster.local   
       subset: primary
       port:
         number: 80       
     weight: 100
   - destination:
       host: worker-service.default.svc.cluster.local    
       subset: burst 
       port:
         number: 80       
     weight: 0

Kompilowanie i wypychanie istiowatcher

Uruchom te polecenia, aby wysłać bieżący katalog do Google Cloud Build (GCB), który utworzy obraz i oznaczy go w GCR.

gcloud builds submit -t gcr.io/${GCLOUD_PROJECT}/istiowatcher

Wdrażanie istiowatcher

Przejdź do katalogu kubernetes

cd ${proj}/kubernetes/

Utwórz plik wdrożenia: istiowatcher.yaml

Utwórz plik o nazwie istiowatcher.yaml i wstaw następujący kod (zastąp parametr <your-project-id>).

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: istiowatcher-deployment
  labels:
    app: istiowatcher
spec:
  replicas: 1
  selector:
    matchLabels:
      app: istiowatcher
  template:
    metadata:
      labels:
        app: istiowatcher
    spec:
      serviceAccountName: istio-pilot-service-account
      automountServiceAccountToken: true
      containers:
      - name: istiowatcher
        image: gcr.io/<your-project-id>/istiowatcher
        imagePullPolicy: Always

Wdróż

Upewnij się, że działamy w klastrze głównym

kubectx primary

Wdróż istiowatcher.yaml w przestrzeni nazw istio-system

kubectl apply -n istio-system -f istiowatcher.yaml

Zwróć uwagę na dyrektywy serviceAccountNameautomountServiceAccountToken w pliku yaml. Dzięki temu mamy dane logowania potrzebne do uruchamiania istioctl w klastrze.

Musimy też wdrożyć to w przestrzeni nazw istio-system, aby mieć pewność, że mamy dane uwierzytelniające dla usługi istio-pilot-service-account. (nie istnieje w przestrzeni nazw default).

Obserwuj automatyczne przełączanie ruchu.

Teraz czas na magiczny moment. Przejdźmy do front-endu i zwiększ liczbę żądań na sekundę do 20.

Zajmie to kilka sekund, ale potem będzie już szybciej. Wszystkie nasze hasze będą miały przedrostek „bursty”.

Wynika to z tego, że próbkujemy dane z Prometheus na podstawie zakresu 15s, co powoduje pewne opóźnienie w czasie odpowiedzi. Jeśli chcemy uzyskać znacznie węższy zakres, możemy zmienić zapytanie w Prometheusie na 5s.

18. Co dalej?

Czyszczenie

Jeśli używasz tymczasowego konta udostępnionego na potrzeby warsztatów, nie musisz niczego usuwać.

Możesz usunąć klastry Kubernetes, regułę zapory sieciowej i obrazy w GCR.

gcloud container clusters delete primary --zone=us-west1-a
gcloud container clusters delete burst --zone=us-west1-a
gcloud compute firewall-rules delete istio-multicluster-test-pods 
gcloud container images delete gcr.io/$GCLOUD_PROJECT/istiowatcher

Co dalej