Integracja argo-rollouts z traffic splittingiem w ISTIO, cz. 2

Sławomir Wolak (Gimbus)
1. marca 2024
Reading time: 9 min
Integracja argo-rollouts z traffic splittingiem w ISTIO, cz. 2

Kontynuujemy omawianie integracji argo-rollouts z traffic splittingiem w ISTIO. W pierwszej części został omówiony temat Argo Rollouts bez ISTIO. Teraz czas na Argo Rollouts z ISTIO oraz opisanie subset leveli; Traffic-Splittingu; gateway, a wszystko to w formie testów.

W pierwszej części rozpocząłem wprowadzenie najpierw do zwykłego podejścia w ISTIO do Traffic Splitting i zwykłe (proste) podstawy Argo-Rollouts. Teraz wszystko to połączę w całość, co pozwoli osiągnąć ISTIO Traffic Splitting z Argo-Rollouts, właśnie. Argo-Rollouts będzie zaprezentowane w dwóch opcjach: Host-level Traffic Splitting i Subset-level Traffic Splitting.

Słowo od redakcji: Sławek, nasz spec od skomplikowanych rozważań technicznych, dzieli się wiedzą także poza GitHubem. Tym razem prezentuje swoje podejście do instalacji ISTIO z oraz bez Argo Rollouts. A wszystko to z autorskim komentarzem i listą dobrych prkatyk.

Argo Rollouts z ISTIO

Zacznijmy od tego że Argo Rollout w kontekście ISTIO dostarcza 2 odrębne podejścia: 

Istio provides two approaches for weighted traffic splitting, both approaches are available as options in Argo Rollouts:

Host-level traffic splitting

Subset-level traffic splitting

Subset-level

Subset-level jest bliższy klasycznemu podejściu ISTIO-way więc od niego zaczniemy

Folder w tym repo z plikami YAML:  AR-z-ISTIO-SubsetLevelTrafficSplitting

tworzymy osobny NS, labelujemy go celem istio-injection i zakładamy konieczne obiekty:

kubectl create ns test-ar-istio
kubectl config set-context --current --namespace=test-ar-istio
kubectl -n istio-system get pods -l app=istiod --show-labels | grep rev
kubectl label namespace test-ar-istio istio-injection- istio.io/rev=asm-1162-2 --overwrite
kubectl apply -f deploy-consumer.yaml
kubectl apply -f destination-rule-Server-v1-v2.yaml
kubectl apply -f rollout-deploy-server-ISTIO.yaml
kubectl apply -f service_clusterip-consumer.yaml
kubectl apply -f service_clusterip-Server-v1v2.yaml
kubectl apply -f virtual-service-Server-wagi.yaml

teraz można wykonywac operacje wkoło AR:

kubectl argo rollouts set image test-rollout-istio app07=gimboo/nginx_nonroot3
kubectl argo rollouts promote test-rollout-istio
kubectl argo rollouts abort test-rollout-istioi

podczas testów widać że po SET wagi ustawiane są na 5 do 95:

$ kk get vs
NAME                GATEWAYS                             HOSTS                                                          AGE
virtservice-app07   ["mesh","test-ar-istio/mygateway"]   ["app07.test-ar-istio.svc.cluster.local","uk.bookinfo7.com"]   9m41s 

$ kk get vs virtservice-app07 -o yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: virtservice-app07
  namespace: test-ar-istio
spec:
  gateways:
  - mesh
  - test-ar-istio/mygateway
  hosts:
  - app07.test-ar-istio.svc.cluster.local
  - uk.bookinfo7.com
  http:
  - name: primary
    route:
    - destination:
        host: app07.test-ar-istio.svc.cluster.local
        subset: version-v2
      weight: 5
    - destination:
        host: app07.test-ar-istio.svc.cluster.local
        subset: version-v1
      weight: 95

zaś po PROMOTE na 100 do 0 

$ kk get vs virtservice-app07 -o yaml[...] 
 http:
  - name: primary
    route:
    - destination:
        host: app07.test-ar-istio.svc.cluster.local
        subset: version-v2
      weight: 0
    - destination:
        host: app07.test-ar-istio.svc.cluster.local
        subset: version-v1
      weight: 100

dla przypomnienia w oryginalnym wsadowym pliku dla VS te wartości były zupełnie inne, ale nie ma to już znaczenia bo to AR zarządza od tej pory tymi wagami – innymi słowy modyfikuje nasze obiekty bez naszej wiedzy: 

$ kk get  vs  -o yaml | grep -v apiVer| grep weight
        weight: 100
        weight: 0

$ kubectl argo rollouts set image test-rollout-istio app07=gimboo/nginx_nonroot2
rollout "test-rollout-istio" image updated

$ kk get  vs  -o yaml | grep -v apiVer| grep weight
        weight: 95
        weight: 5

$ kubectl argo rollouts promote test-rollout-istio
rollout 'test-rollout-istio' promoted

$ kk get  vs  -o yaml | grep -v apiVer| grep weight
        weight: 100
        weight: 0

zmiany w Traffic-Splitting realizujemy poprzez edycję virtual-service-Server-wagi.yml

  http:
  - route:
    - destination:
        host: app07.slawek.svc.cluster.local
        subset: version-v2
      weight: 100
    - destination:
        host: app07.slawek.svc.cluster.local
        subset: version-v1
      weight: 0

to 95/0 bierze się oczwyiście stąd:


kind: Rollout
[...]      steps:
      - setWeight: 5

Powyższe to podstawy ISTIO-wych podstaw, ale trzeba to mieć przed oczami przy analizie modeli opartych na Argo-Rollout.

Dla przypomnienia – w podejściu klasycznym (bez ARGO-rollout tylko czyste ważenie na istio destination-rules/subsets) mechanizm jest następujący:

  1. są 2 x deploy-Server , jeden poza label name:app07 ma też label version v1 , drugi ma v2 
  2. jest destination-rule (dla tego deployu SERVER) która ma zdefiniowane 2 subsety – jeden subset żyje na labels:version:v1 a drugi na v2 i
  3. z kolei VirtualService (dla tego deployu SERVER) ma 1 route a w niej 2 destinations – jedna na subset1 a druga na subset2, jedna ma weight 100 a druga weight 0

w skrócie:

$ cat /virtual-service-Server-wagi.yml
[...]
  - route:
    - destination:
        host: app07.slawek.svc.cluster.local
        subset: version-v2
      weight: 100
    - destination:
        host: app07.slawek.svc.cluster.local
        subset: version-v1
      weight: 0

$ cat /destination-rule-Server-v1-v2.yml
[...]
  subsets:
  - labels:
      version: v1
    name: version-v1
  - labels:
      version: v2
    name: version-v2

Tu z kolei różnic między plikami VS i DestinationRule nie ma (pomijając że dla wersji z AR trzeba w DestRule wstawić jakiś label generyczny (np name=app07) +jest jedna drobna bo w VS jak jest route to tu ma name=primary) nie ma znaczenia jak ustawimy wagi w VS.yaml bo AR i tak zaraz przejmie sprawy 

po wgraniu naszego destination-rule zostaje ona natychmiast zmodyfikowana przez AR (patrzymy na rollouts-pod-template-hash):

$ kubectl get destinationrules destrule-app07 -o yamlapiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: destrule-app07
  namespace: test-ar-istio
spec:
  host: app07.test-ar-istio.svc.cluster.local
  subsets:
  - labels:
      name: app07
      rollouts-pod-template-hash: 5bfbf9599
    name: version-v1
  - labels:
      name: app07
      rollouts-pod-template-hash: 5bfbf9599
    name: version-v2

Co ważne – w classic-trybie-istio (gdzie mamy 2 osobne deploye – jeden ma labels na PODach version: v1 , drugi ma v2 ) to nasza DestinationRule jest oparta o te labele version-v1-v2i

tu w AR zaszła zmiana – skoro AR modyfikuje za naszymi plecami naszą dest-rule (dodając jej też extra labele rollouts-pod-template-hash) to wlaśnie na tej labeli różnicowane są PODy:

$ kk get po --show-labels
NAME                                  READY   STATUS    RESTARTS   AGE   LABELS
test-rollout-istio-5bfbf9599-dbcd6    2/2     Running   0          21m   name=app07,rollouts-pod-template-hash=5bfbf9599,security.istio.io/tlsMode=istio,service.istio.io/canonical-name=test-rollout-istio-5bfbf9599,service.istio.io/canonical-revision=latest,topology.istio.io/network=a-r-02-default
test-rollout-istio-746b5594b8-pv4wr   2/2     Running   0          11m   name=app07,rollouts-pod-template-hash=746b5594b8,security.istio.io/tlsMode=istio,service.istio.io/canonical-name=test-rollout-istio-746b5594b8,service.istio.io/canonical-revision=latest,topology.istio.io/network=a-r-02-default

Jednym słowem proces dla Traffic-Splitting wygląda nastepująco:

Dla wersji z gołym ISTIO:
  • administrator powołuje 2 deploymenty + przykrywający je k8s-svc. (każdy Deploy ma inny label version=v1/v2) 
  • do tego dodaje DestinationRule która definiuje 2 subsety bazujące na tych labelach z version
  • do tego na koniec dodaje VirtualService w którym w .route.destination odwołuje się do tych 2 subsetów i ma wstawione w te 2 miejsca wstępne wagi
  • admin zaczyna administrować TrafficSplitingiem poprzez edycję tego VS wsadzając wagi 100/0, 90/10 itd 

Dla wersji z ArgoRollouts-ISTIO 

  • administrator zamiast 2xDeploy powołuje ROLLOUT w którym:
    • wskazuje na VirtService którym AR będzie kręcił via wstawianie wag (dlatego podaje się tam nazwę route – np “primary”) 
    • wskazuje na DestRule (podaje dla AR jej nazwę, canarySubsetName version=v2 , stableSubsetName version=v1) 
    • określa steps dla promocji rolloutu oraz pause{} 
    • emuluje Deploy (wskazuje image, resources itd) 
  • admin powołuje DestRule (yaml wsadowy (czyli ten z day0) jest inny niż dla gołego ISTIO, nie ma różnicowania, obydwa subsety muszą byc zrobione na czymś co jest takie same dla nich dwóch – potem AR natychmiast i w trybie ciągłym zmienia mu subsety ktore bazują od teraz również na labelach rollouts-pod-template-hash=RANDOM_Number)
  • admin powołuje VirtService (również identyczny jak dla scenariusza z gołym ISTIO) ktorym już za chwilę AR będzie zarządzał – ale jedynie kręcąc w nim wagami 
  • to nie admin ale tym razem AR zaczyna zarządzać TraficSplitingiem poprzez ustawianie wag w VirtService – 100/0 i 95/5 ORAZ poprzez wstawanie do subsetów w naszej DestinationRule warunków opartych na rollouts-pod-template-hash

Tyle w cz. 2 tematu ISTIO. W kolejnej odsłonie wrócimy do zmian w rolloutach – do tej pory zmienialiśmy jedynie obraz (via kubectl argo rollouts set image test-rollout-istio app07=gimboo/nginx_nonroot3).

Sprawdzimy też jak to działa ale z podmianą CM, a zatem wprowadźmy nowy rollout zawierający odwołanie do CM + nowy obiekt CM

CDN…