Omawiamy tu mechanizm integracji argo-rollouts z traffic splittingiem w ISTIO
Celem wprowadzenia przedstawione będzie najpierw zwykłe podejście w ISTIO do Traffic Splitting i zwykłe (proste) podstawy Argo-Rollouts
Następnie wszystko to zostanie połączone w całość co pozwoli osiągnąć ISTIO Traffic Splitting z Argo-Rollouts
Argo-Rollouts będzie w 2 opcjach - Host-level Traffic Splitting i Subset-level Traffic Splitting
$ kubectl create namespace argo-rollouts
namespace/argo-rollouts created
$ kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml
customresourcedefinition.apiextensions.k8s.io/analysisruns.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/analysistemplates.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/clusteranalysistemplates.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/experiments.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/rollouts.argoproj.io created
serviceaccount/argo-rollouts created
clusterrole.rbac.authorization.k8s.io/argo-rollouts created
clusterrole.rbac.authorization.k8s.io/argo-rollouts-aggregate-to-admin created
clusterrole.rbac.authorization.k8s.io/argo-rollouts-aggregate-to-edit created
clusterrole.rbac.authorization.k8s.io/argo-rollouts-aggregate-to-view created
clusterrolebinding.rbac.authorization.k8s.io/argo-rollouts created
configmap/argo-rollouts-config created
secret/argo-rollouts-notification-secret created
service/argo-rollouts-metrics created
deployment.apps/argo-rollouts created
curl -LO https://github.com/argoproj/argo-rollouts/releases/latest/download/kubectl-argo-rollouts-linux-amd64
chmod +x ./kubectl-argo-rollouts-darwin-amd64
sudo mv ./kubectl-argo-rollouts-darwin-amd64 /usr/local/bin/kubectl-argo-rollouts
Zasady działania klasycznego Traffic Splitting w ISTIO
w katalogu ISTIO-BASE tego repozytorium są odpowiednie YAMLe do wdrożenia:
na klastrze k8s gdzie mamy postawione ISTIO tworzymy dowolny namespace
kubectl create namespace test-istio
kubectl config set-context --current --namespace=test-istio
nastepnie labelujemy (tu 3 przykłady z różnych środowisk) ten namespace aby działał autoinject dla envoy-proxies:
kubectl label namespace test-istio istio-injection=enabled --overwrite
kubectl label namespace test-istio istio-injection- istio.io/rev=asm-182-2 --overwrite
kubectl label namespace test-istio istio-injection- istio.io/rev=asm-1162-2 --overwrite
i wgrywamy wszystkie obiekty do testu :
kubectl apply -f deploy-consumer.yaml
kubectl apply -f deploy-server-v1.yaml
kubectl apply -f deploy-server-v2.yaml
kubectl apply -f service_clusterip-consumer.yaml
kubectl apply -f service_clusterip-Server-v1v2.yaml
kubectl apply -f virtual-service-Server-wagi.yml
kubectl apply -f destination-rule-Server-v1-v2.yml
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
Mechanizm w skrócie polega na tym że są 2 x deploy-Server , jeden poza label name:app07 ma też label version v1 , drugi ma v2
DestinationRule (dla tego deployu SERVER) ma zdefiniowane 2 subsety - jeden subset żyje na labels:version:v1 a drugi na v2
z kolei VirtualService (dla tego deployu SERVER) ma 1 route a w niej 2 destinations - jedna na subset1 a druga na subset2
Administrator kręci wagami wstawiając 100_do_0, 5_do_95 itp
Powyższe to podstawy istiowych podstaw ale trzeba to mieć przed oczami przy analizie modeli opartych na Argo-Rollout
Zasady działania AR , chwilowo bez istio
To również podstawy podstaw AR ale trzeba je zrozumieć żeby potem łączyć AR+ISTIO
A Rollout is Kubernetes workload resource which is equivalent to a Kubernetes Deployment object. It is intended to replace a Deployment object in scenarios when more advanced deployment or progressive delivery functionality is needed
Katalog z plikami YAML w tym repo: AR-bez-ISTIO
Tworzymy namespace i obiekty w nim:
$ kk create ns test-ar
namespace/test-ar created
$ kk -n test-ar apply -f rollout-deploy-server-v1.yaml
rollout.argoproj.io/test-rollout created
$ kk -n test-ar apply -f service_clusterip-Server-v1v2.yaml
service/app07 created
$ kk -n test-ar apply -f deploy-consumer.yaml #ten deploy nie należy do zestawu, tworzymy go dla wygody żeby mieć obok PODa z curlem
deployment.apps/consumer created
$ kk -n test-ar get all
NAME READY STATUS RESTARTS AGE
pod/test-rollout-746b5594b8-5mspv 1/1 Running 0 112s
pod/test-rollout-746b5594b8-5rp7k 1/1 Running 0 112s
pod/test-rollout-746b5594b8-6qbn4 1/1 Running 0 112s
pod/test-rollout-746b5594b8-rbrn4 1/1 Running 0 112s
pod/test-rollout-746b5594b8-wck6b 1/1 Running 0 112spod/consumer-58f6fd4c95-lzcvg 1/1 Running 0 40s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/app07 ClusterIP 10.108.8.117 <none> 8080/TCP 3s
NAME DESIRED CURRENT READY AGE
replicaset.apps/test-rollout-746b5594b8 5 5 5 113s
zmieniamy w naszym rollout obraz
$ kubectl argo rollouts set image test-rollout app07=gimboo/nginx_nonroot2 -n test-ar
rollout "test-rollout" image updated
$ kubectl argo rollouts get rollout test-rollout -n test-ar
Name: test-rollout
Namespace: test-ar
Status: ॥ Paused
Message: CanaryPauseStep
Strategy: Canary
Step: 1/8
SetWeight: 20
ActualWeight: 20
Images: gimboo/nginx_nonroot (stable)
gimboo/nginx_nonroot2 (canary)
Replicas:
Desired: 5
Current: 5
Updated: 1
Ready: 5
Available: 5
NAME KIND STATUS AGE INFO
⟳ test-rollout Rollout ॥ Paused 13m
├──# revision:2
│ └──⧉ test-rollout-dd4cc6cb4 ReplicaSet Healthy 69s canary
│ └──□ test-rollout-dd4cc6cb4-vrd4x Pod Running 69s ready:1/1
└──# revision:1
└──⧉ test-rollout-746b5594b8 ReplicaSet Healthy 13m stable
├──□ test-rollout-746b5594b8-5mspv Pod Running 13m ready:1/1
├──□ test-rollout-746b5594b8-5rp7k Pod Running 13m ready:1/1
├──□ test-rollout-746b5594b8-rbrn4 Pod Running 13m ready:1/1
└──□ test-rollout-746b5594b8-wck6b Pod Running 13m ready:1/1
Jak widać AR zblokował się na 20% (tak jest w definicji) i czeka na dalsze kroki z naszej strony
When the demo rollout reaches the second step, we can see from the plugin that the Rollout is in a paused state, and now has 1 of 5 replicas running the new version of the pod template, and 4 of 5 replicas running the old version. This equates to the 20% canary weight as defined by the setWeight: 20 step.
spec:
replicas: 5
strategy:
canary:
steps:
- setWeight: 20
- pause: {}
- setWeight: 40
- pause: {duration: 10}
- setWeight: 60
- pause: {duration: 10}
- setWeight: 80
- pause: {duration: 10}
aby pchnąć wdrożenie do przodu trzeba wykonać promote, aby cofnąć należy wykonać abort:
kubectl argo rollouts promote test-rollout -n test-ar
kubectl argo rollouts abort test-rollout -n test-ar
Tu ważna uwaga - po cofnięciu mimo że stan jest ok/stabilny to i tak wpadamy w stan Degraded (no bo degraded bo framework zakłada że się nie udało - zalegalizować można robiąc set na stary-stabilny konf)
testy oczywiście realizujemy via POD consumera:
$ kk -n test-ar exec -ti consumer-58f6fd4c95-lzcvg bash
nginx@consumer-58f6fd4c95-lzcvg:/$ curl app07:8080
test-rollout-dd4cc6cb4-mmc2s
<br>2
nginx@consumer-58f6fd4c95-lzcvg:/$ curl app07:8080
test-rollout-dd4cc6cb4-88nhd
<br>2
i to tyle podstawowej teorii o AR
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 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
to 95/0 bierze się oczwyiście stąd:
kind: Rollout
[...] steps:
- setWeight: 5
Dla przypomnienia - w podejściu klasycznym (bez ARGO-rollout tylko czyste ważenie na istio destination-rules/subsets) mechanizm jest następujący:
- są 2 x deploy-Server , jeden poza label name:app07 ma też label version v1 , drugi ma v2
- 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
- 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
- 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
- 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
Wróćmy do zmian w rolloutach - do tej pory zmienialiśmy jedynie obraz (via kubectl argo rollouts set image test-rollout-istio app07=gimboo/nginx_nonroot3)
sprawdźmy jak to działa ale z podmianą CM, a zatem wprowadźmy nowy rollout zawierający odwołanie do CM + nowy obiekt CM
rollout-deploy-server-ISTIO-ConfigMap.yaml
config-map-01.yaml
różnica w kontekście deploymentu (a właściwie oczywiście jego emulacji) jest następująca:
$ diff rollout-deploy-server-ISTIO.yaml rollout-deploy-server-ISTIO-ConfigMap.yaml.optional
35c35,43
<
---
> volumeMounts:
> - name: config
> mountPath: "/config"
> readOnly: true
> volumes:
> - name: config
> configMap:
> # Provide the name of the ConfigMap you want to mount.
> name: cm-01
dodajemy:
kk apply -f rollout-deploy-server-ISTIO-ConfigMap.yaml
kk apply -f config-map-01.yaml
Po wgraniu nowej wersji rolloutu (to oczywiście ten sam AR ale z jedną małą zmianą bo ten AR używa od tej pory CM) pojawia sie nowy rollout , zaś jak się wejdzie na PODa to widać zmienne z ConfigMapy:
nginx@test-rollout-istio-8675765c8c-4ffmt:/$ cat /config/key01 ; echo
alamakota
nginx@test-rollout-istio-8675765c8c-4ffmt:/$ cat /config/key02 ; echo
ma
nginx@test-rollout-istio-8675765c8c-4ffmt:/$ cat /config/key03 ; echo
kota
podmieńmy teraz ale znowu nie obraz ale ConfigMmapę - załadujmy kolejną nową:
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-02
data:
key01: "alamakota 2"
key02: "ma 2 "
key03: "kota 2"
$ kk apply -f config-map-02.yaml
configmap/cm-02 created
$ kk get cm
NAME DATA AGE
cm-01 3 17m
cm-02 3 6s
kube-root-ca.crt 1 5h
idąc za: https://argo-rollouts.readthedocs.io/en/stable/getting-started/#2-updating-a-rollout
Just as with Deployments, any change to the Pod template field (spec.template) results in a new version (i.e. ReplicaSet) to be deployed. * Updating a Rollout involves modifying the rollout spec, typically changing the container image field with a new version, and then running kubectl apply against the new manifest. As a convenience, the rollouts plugin provides a set image command, which performs these steps against the live rollout object in-place*
widać że jakakolwiek modyfikacja AR via set image czy jakiekolwiek zmiany w definicji AR powodują uruchomienie mechanizmu rolloutu
Jedno co dziwi to jak widać do set image dorobiono CLI (kubectl argo rollouts set image test-rollout-istio app07=gimboo/nginx_nonroot2) a do innych modyfikacji już nie
Zatem musimy sami sobie zmienić w rollout.yaml wskazanie na inną config-mapę
$ diff rollout-deploy-server-ISTIO-ConfigMap.yaml rollout-deploy-server-ISTIO-ConfigMap-02.yaml
43c43
< name: cm-01
---
> name: cm-02
$ kk apply -f rollout-deploy-server-ISTIO-ConfigMap-02.yaml
rollout.argoproj.io/test-rollout-istio configured
pojawił się nowy POD i pojawił się nowy ROLLOUT niby mała zmiana (zamontowanie innej CM) a jednak jest zmianą - więc AR powołało nową revision i wstrzymało ją z wagą na 5% w VS
$ kubectl argo rollouts get rollout test-rollout-istio
Name: test-rollout-istio
Namespace: test-ar-istio
Status: ॥ Paused
Message: CanaryPauseStep
Strategy: Canary
Step: 1/2
SetWeight: 5
ActualWeight: 5
Images: gimboo/nginx_nonroot (canary, stable)
Replicas:
Desired: 1
Current: 2
Updated: 1
Ready: 2
Available: 2
NAME KIND STATUS AGE INFO
⟳ test-rollout-istio Rollout ॥ Paused 5h4m
├──# revision:12
│ └──⧉ test-rollout-istio-549bbd66c6 ReplicaSet Healthy 18s canary
│ └──□ test-rollout-istio-549bbd66c6-h5km8 Pod Running 18s ready:2/2
├──# revision:11
│ └──⧉ test-rollout-istio-8675765c8c ReplicaSet Healthy 21m stable
│ └──□ test-rollout-istio-8675765c8c-4ffmt Pod Running 21m ready:2/2
$ kk exec -ti test-rollout-istio-8675765c8c-4ffmt -- cat /config/key01 ; echo
alamakota
$ kk exec -ti test-rollout-istio-549bbd66c6-h5km8 -- cat /config/key01 ; echo
alamakota 2
warto zaznaczyć że w AR da się podmieniać via CLI tylko image - jakiekolwiek inne zmiany trzeba robić ręcznie modyfikując rollout spec
zaś modyfikując AR otrzymujemy nową rewizję AR
Updating a Rollout involves modifying the rollout spec
wykonuje sie to normalnie , jak w klasyku ISTIO, czyli dodając sekcje gateway do definicji VS:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: virtservice-app07
spec:
hosts:
- app07.test-ar-istio.svc.cluster.local
- uk.bookinfo7.com
gateways:
- mesh
- test-ar-istio/mygateway
http:
- route:
- destination:
host: app07.test-ar-istio.svc.cluster.local
subset: version-v2
weight: 50
- destination:
host: app07.test-ar-istio.svc.cluster.local
subset: version-v1
weight: 50
name: primary
ogólnie można sobie wygodnie porównać zwykłe wdrożenie Traffic-Splittingu via ISTIO-bez-AR z takim gdzie to AR zarządza ISTIO
w repo w katalogu AR-z-ISTIO-SubsetLevelTrafficSplitting jest podfolder ISTIO-classic a w nim wszystkie obiekty do wdrożenia drugiego zestawu usług - jak sie wdroży obiekty z niego to mamy stan gdzie app07 jest oparte o ArgoRollouts a app08 to gołe ISTIO
$ kk get vs
NAME GATEWAYS HOSTS AGE
virtservice-app07 ["mesh","test-ar-istio/mygateway"] ["app07.test-ar-istio.svc.cluster.local","uk.bookinfo7.com"] 7h51m
virtservice-app08 ["mesh","test-ar-istio/mygateway"] ["app08.test-ar-istio.svc.cluster.local","uk.bookinfo8.com"] 36m
$ kk get dr
NAME HOST AGE
destrule-app07 app07.test-ar-istio.svc.cluster.local 7h52m
destrule-app08 app08.test-ar-istio.svc.cluster.local 37m
$ kk get gateway
NAME AGE
mygateway 66m
(uwaga, gateway występuje 2 razy, w 2 plikach - są niemal identyczne ale ten drugi uzupełnia GW o dodatkowe hosty dla app08 (wystarczy zrobić diff na tych 2 plikach) )
18,19c18,19
< #- app08.test-ar-istio.svc.cluster.local
< #- uk.bookinfo8.com
---
> - app08.test-ar-istio.svc.cluster.local
> - uk.bookinfo8.com
testy na k8s-svc (wewnątrz klastra) wykonujemy z PODa consumera a testy połączenia via GW z jakiejś stacji na zewnątrz klastra (np z GCE stojącej obok węzłów GKE)
nginx@consumer-58f6fd4c95-gnrmt:/$ curl app07:8080
test-rollout-istio-dd4cc6cb4-lvjw2
<br>2
nginx@consumer-58f6fd4c95-gnrmt:/$ curl app07:8080
test-rollout-istio-dd4cc6cb4-lvjw2
<br>2
$ kk get svc -n istio-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-ingressgateway LoadBalancer 10.108.1.52 10.128.0.5 15020:32604/TCP,443:30467/TCP,80:30423/TCP 5h45m
istiod ClusterIP 10.108.5.9 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP 5h45m
istiod-asm-1162-2 ClusterIP 10.108.13.34 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP 5h45m
slawek@instance-1:~$ curl -k --resolve uk.bookinfo7.com:80:10.128.0.5 http://uk.bookinfo7.com
test-rollout-istio-dd4cc6cb4-lvjw2
<br>2
slawek@instance-1:~$
wróćmy do metody omawianej w getting-started: https://argo-rollouts.readthedocs.io/en/stable/getting-started/istio/
omawiają tu bowiem nieco inną koncepcję - Host-level Traffic Splitting
The first approach to traffic splitting using Argo Rollouts and Istio, is splitting between two hostnames, or Kubernetes Services: a canary Service and a stable Service
Folder w tym repo z plikami YAML: AR-z-ISTIO-HostLevel-TrafficSplitting
kubectl create ns test-ar-istio-2
kubectl config set-context --current --namespace=test-ar-istio-2
kubectl -n istio-system get pods -l app=istiod --show-labels | grep rev
kubectl label namespace test-ar-istio-2 istio-injection- istio.io/rev=asm-1162-2 --overwrite
Koncepcja ta bazuje na AR który definiuje :
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: test-rollout-istio
spec:
replicas: 1
strategy:
canary:
canaryService: canary-service
stableService: stable-service
trafficRouting:
istio:
virtualServices:
- name: virtservice-app07
routes:
- primary
steps:
- setWeight: 5
- pause: {}
revisionHistoryLimit: 2
selector:
matchLabels:
name: app07
template:
metadata:
labels:
name: app07
spec:
containers:
- image: gimboo/nginx_nonroot
name: app07
resources: {}
w definicji AR jak widać jest wskazanie na dwa k8s-svc (canary-service i stable-service) oraz na virt-service
w wyniku działania AR powstaje 1 POD (lub 2 PODy jesli następuje wdrożenie nowej wersji rolloutu)
PODy te mają dodawane rollouts-pod-template-hash
jednocześnie AR modyfikuje nasze k8s-SVC tak że ich selectory poza oryginalnym selector-name-app07 mają również
selector:
name: app07
rollouts-pod-template-hash: dd4cc6cb4
w chwili gdy jest tylko jedna rewizja rolloutu te SVC wskazują na to samo:
$ kk get po -o wide --show-labels
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
test-rollout-istio-dd4cc6cb4-bjzkv 2/2 Running 0 22m 10.104.0.23 gke-central-default-pool-6ac74c87-ldjn <none> <none> name=app07,rollouts-pod-template-hash=dd4cc6cb4,security.istio.io/tlsMode=istio,service.istio.io/canonical-name=test-rollout-istio-dd4cc6cb4,service.istio.io/canonical-revision=latest,topology.istio.io/network=a-r-008-default
$ kk get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
canary-service ClusterIP 10.108.8.99 <none> 8080/TCP 43m
stable-service ClusterIP 10.108.6.132 <none> 8080/TCP 43m
$ kk get svc stable-service -o yaml
[...]
selector:
name: app07
rollouts-pod-template-hash: dd4cc6cb4
[...]
$ kk get svc canary-service -o yaml
[...]
selector:
name: app07
rollouts-pod-template-hash: dd4cc6cb4
[...]
$ kk get ep
NAME ENDPOINTS AGE
canary-service 10.104.0.23:8080 43m
stable-service 10.104.0.23:8080 43m
z kolei serce układu czyli VirtService ma 2 destination , każda na inny k8s-svc , na stable-service ma w=100, na canary-service w=0
$ kk get vs
NAME GATEWAYS HOSTS AGE
virtservice-app07 ["app07-vs","uk.bookinfo7.com"] 42m
$ kk get vs virtservice-app07 -o yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: virtservice-app07
namespace: test-ar-istio-2
spec:
hosts:
- app07-vs
- uk.bookinfo7.com
http:
- name: primary
route:
- destination:
host: stable-service
weight: 100
- destination:
host: canary-service
weight: 0
zaraz po wykonaniu modyfikacji :
$ kubectl argo rollouts set image test-rollout-istio app07=gimboo/nginx_nonroot3
rollout "test-rollout-istio" image updated
pojawia się drugi POD z innym rollouts-pod-template-hash :
$ kk get po -o wide --show-labels
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
test-rollout-istio-5bfbf9599-xl7mv 2/2 Running 0 10s 10.104.1.23 gke-central-default-pool-6ac74c87-w6d2 <none> <none> 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-008-default
test-rollout-istio-dd4cc6cb4-bjzkv 2/2 Running 0 26m 10.104.0.23 gke-central-default-pool-6ac74c87-ldjn <none> <none> name=app07,rollouts-pod-template-hash=dd4cc6cb4,security.istio.io/tlsMode=istio,service.istio.io/canonical-name=test-rollout-istio-dd4cc6cb4,service.istio.io/canonical-revision=latest,topology.istio.io/network=a-r-008-default
k8s-SVC już wyglądają inaczej (tzn stable-service wygląda tak samo, ale canary-service zmienił sie tak że jego selector wyłapuje nowego PODa) :
$ kk get svc stable-service -o yaml | grep rollouts-pod-template-hash
rollouts-pod-template-hash: dd4cc6cb4
$ kk get svc canary-service -o yaml | grep rollouts-pod-template-hash
rollouts-pod-template-hash: 5bfbf9599
$ kk get ep
NAME ENDPOINTS AGE
canary-service 10.104.1.23:8080 49m
stable-service 10.104.0.23:8080 49m
Virt-service zmienił wagi:
$ kk get vs virtservice-app07 -o yaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: virtservice-app07
namespace: test-ar-istio-2
spec:
hosts:
- app07-vs
- uk.bookinfo7.com
http:
- name: primary
route:
- destination:
host: stable-service
weight: 95
- destination:
host: canary-service
weight: 5
Niestety nie do końca wiadomo jak to wszystko przetestować w środku klastra bo mamy 2 różne k8s-svc , a próba łączenia się na hostname z VS kończy się błędem (zresztą próba ta sensu nie miała bo nikt jej w DNS nie założył)
nginx@consumer-58f6fd4c95-fq2r7:/$ curl app07-vs:8080
curl: (6) Could not resolve host: app07-vs
być może jedyną opcją na dostanie się do usługi (i obejrzenie że scenariusz 95/5 działa) jest dostęp via gateway
trzeba odkomentować sekcje dla gateway w definicji VS:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: virtservice-app07
#namespace: istio-system
spec:
hosts:
- app07-vs
- uk.bookinfo7.com
gateways:
- mygateway
http:
- route:
- destination:
host: stable-service #Should match rollout.spec.strategy.canary.stableService
weight: 50
- destination:
host: canary-service #Should match rollout.spec.strategy.canary.canaryService
weight: 50
name: primary # Should match rollout.spec.strategy.canary.trafficRouting.istio.virtualServices.routes
i dodac tenże gateway:
$ kk apply -f EXPOSE_gateway_simple_MATCH_URI_PREFIX.yaml
faktycznie ważenie wydaje się że działa
nginx@consumer-58f6fd4c95-fq2r7:/$ curl -k --resolve uk.bookinfo7.com:80:10.128.0.5 http://uk.bookinfo7.com
no ale jeśli do VS trzeba dorzucać gateway żeby z nim porozmawiać to zaczyna to powoli nie mieć sensu
- 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 2 x k8s-SVC (podaje tylko ich nazwy)
- określa steps dla promocji rolloutu oraz pause{}
- emuluje Deploy (wskazuje image, resources itd)
- admin powołuje 2 x k8s-SVC ( w selectory tych 2xk8s_svc wstawia coś co będzie wspólne dla 2 wersji PODów - np label=name=app07), potem AR natychmiast i w trybie ciągłym kręci selectorami tych 2 k8s-svc wstawiając im tam rollouts-pod-template-hash)
- admin powołuje VirtService (definiuje tam 2 x destination, każdy z nich wskazuje na osobny k8s-svc) - tym VS też już za chwilę zarządzał będzie AR - ale jedynie kręcąc w nim wagami
- to nie admin ale oczywiście AR zaczyna zarządzać TraficSplitingiem poprzez ustawianie wag w VirtService - 100/0 i 95/5 ORAZ poprzez modyfikacje selectorów w 2 x k8s-svc
to samo podejście jest prezentowane tu: https://argo-rollouts.readthedocs.io/en/stable/features/traffic-management/istio/#host-level-traffic-splitting podsumowano tu zresztą dosyć analitycznie cały mechanizm host-level-traffic-splitting:
During the lifecycle of a Rollout update, Argo Rollouts will continuously:
modify the canary Service spec.selector to contain the rollouts-pod-template-hash label of the canary ReplicaSet modify the stable Service spec.selector to contain the rollouts-pod-template-hash label of the stable ReplicaSet modify the VirtualService spec.http[].route[].weight to match the current desired canary weight
podejście Subset-level Traffic Splitting wydaje się być ISTIO-way i jest naturalnym powieleniem dotychczasowych mechanizmów dla ważenia ruchu w ISTIO
podejście Host-level Traffic Splitting w wydaje się być nienaturalne i uciążliwe w próbie nałożenia go na klasyczne schematy ISTIO , dodatkowo nie da się łatwo korzystać z serwisów bez użycia gatewaya , deploymenty chcące łączyć sie wprost do canary/stable nie będą mogły zrobić tego naraz tylko muszą sobie wybrać do której konkretnie wersji chcą się łączyć
gdy poczyta się co nieco na ten temat to okazuje się że początkowo AR-ISTIO bazowały wyłącznie na Host-level Traffic Splitting które było krytykowane po jakimś czasie pojawiło sie podejście oparte o Subset-level Traffic Splitting - dużo naturalniejsze i łatwe do zmapowania ze zwykłego Traffic Splittingu opartego na ISTIO
bardzo trafnie oceniono to tutaj (maj 2020) : argoproj/argo-rollouts#617
Currently, according to https://argoproj.github.io/argo-rollouts/features/traffic-management/istio/, Istio canary deployments require separate services for stable and canary. This is undesirable, as I understand in-mesh communications won't hit the virtualservice directly (there's no DNS entry added by default for those in the Kubernetes cluster). This forces microservice-to-microservice communication to choose whether to hit the stable service or the canary (hardcoded), or go to the gateway, or DNS entries to be added for the virtualservices, which is cumbersome and poorly documented.
All Istio canary examples I could find use a single service, but have a destinationrule that splits them into 2 groups based on label selectors (for canary vs stable, or per version), then the virtualservice links this single service and splits the traffic accordingly
finalnie na prośbę community w okolicach Q1 2021 dodano obsługę Subset-level Traffic Splitting pozostawiając również na prośbę niezbyt udane Host-level Traffic Splitting argoproj/argo-rollouts#985