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 więc, 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 dokuentem
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, gdzie omówiono 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 dodać tenże gateway:
$ kk apply -f EXPOSE_gateway_simple_MATCH_URI_PREFIX.yaml
Faktycznie, 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 ażeby z nim porozmawiać, to zaczyna to powoli nie mieć sensu.
To samo podejście jest prezentowane tu – podsumowano 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 się 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.