Canaries
- 애플리케이션의 사용량이 많을수록 유용한 기능. 사용자가 많은 애플리케이션에서 새로운 버전을 100% 바로 배포할 경우 발생할 수 있는 리스크를 줄일 수 있음.
istio 없이 Kubernetes component로 Canary 적용하기
- Single Service에 multiple pod를 연결하는 것이 가능함. deployment에 Label 붙이고, service에서 matchLabel로 pod 선택하는 게 가능하기 때문
- k8s의 load balancing은 기본적으로 round robin.
- 따라서 위 그림과 같은 상황이라면, new version (staff-service:6)은 전체 트래픽의 33%를 점유하게 됨
- 대신, 이 방법은 traffic의 %를 변경하려면 그만큼 pod 비율을 늘려야 함. 33% -> 10%로 변경하고 싶으면, new version : old version 비율이 1:9가 되어야 함
deployment에서 label에 version을 키값으로 입력할 경우, kiali의 versioned app graph를 입력하면 version에 입력한 value값이 표시됨.
kiali에서 Service -> Details -> Create Weighted Routing 선택하면 traffic spliting이 가능함. VirtualService + DestinationRules object를 생성해야 하는 작업이지만, kiali에서 ui로 처리할 수 있음.
Istio VirtualService?
Traffic Customization을 가능하게 해 주는 istio의 CRD.
- Routing Configuration 또는 Custom Routing이 더 직관적인 리소스 이름이라고 생각함.
istio VirtualService가 적용된 Architecture 구조란?
k8s의 Service는 외부로부터의 요청을 받아서, labels / Selector 기준으로 내부의 여러 pod에 트래픽 routing을 수행함.
대략 동작방식을 요약하자면
- Client pod이 다른 pod를 호출하려 하면, 먼저 k8s 내부에서 DNS lookup을 수행한다.
- DNS 작업은 kubernetes built-in pod인 kube-system이 수행함.
- pod는 각각 private IP주소를 가지고 있고, Service에서 IP table + load balancing을 수행함.
istio를 적용하면, 대략 아래처럼 구조가 바뀐다.
- virtualService를 클러스터에 배포하면, istio의 control plane(istiod) (그림에서는 pilot) 으로 전달된다.
- control plane은 실시간으로 proxy configuring을 담당하는 컴포넌트.
- virtualService에 정의된 configuration이 실시간으로 istio proxy (envoy proxy) 옵션을 변경한다. custom Routing 로직이 주로 반영됨
- 따라서 k8s pod가 다른 Pod로 요청을 보내면, custom routing이 적용된 proxy를 통해서 요청이 전달된다.
envoy proxy가 traffic management 기능을 제공하고, istio는 여러 pod에 배포된 envoy proxy에 routing configuration을 실시간으로 변경할 수 있게 만들어준다.
따라서 kubernetes의 Service와 istio의 VirtualService는 이름과 달리 완전히 별개의 기능이라고 이해해도 됨.
- k8s Service는 individual pods의 private IP주소를 관리하는 게 주된 역할
- istio의 envoy proxy가 요청을 다른 pod의 proxy로 전달할 때 반드시 필요로 함.
- virtualService는 Canary Deployment같은 traffic management 기능을 on-the-fly로 반영할 수 있게 해 줌.
DestinationRules
VirtualService이 정상적으로 동작하기 위해 필요한 object. istio의 Load balancing에 적용하는 Rule이라고 생각하면 된다.
- Subset: 트래픽을 전달할 단위 (단일 pod이거나, label selector로 pod 그룹을 지정하거나)
- label의 key를 'version'으로 지정할 경우 kiali에서 "versioned app group" 설정을 활용할 수 있다.
- same Service, different Deployment
Configuration
VirtualService yaml 파일은 kiali UI에서 생성되는 걸 사용할 수도 있으나, 잘 쓰이지는 않음.
kind: VirtualService
apiVersion: networking.istio.io/v1alpha3
metadata:
name: a-set-of-routing-rules-we-can-call-this-anything # "just" a name for this virtualservice
namespace: default
spec:
hosts:
- fleetman-staff-service.default.svc.cluster.local # The Service DNS (i.e the regular K8S Service) name that we're applying routing rules to.
# 예시의 경우 routing에 사용할 DNS Lookup이 필요했으므로 k8s service가 매핑됨.
http:
- route:
- destination:
host: fleetman-staff-service.default.svc.cluster.local # The Target DNS name
subset: safe-group # The name defined in the DestinationRule
weight: 90
- destination:
host: fleetman-staff-service.default.svc.cluster.local # The Target DNS name
subset: risky-group # The name defined in the DestinationRule
weight: 10
---
kind: DestinationRule # Defining which pods should be part of each subset
apiVersion: networking.istio.io/v1alpha3
metadata:
name: grouping-rules-for-our-photograph-canary-release # This can be anything you like.
namespace: default
spec:
host: fleetman-staff-service # Service
subsets:
- labels: # SELECTOR.
version: safe # find pods with label "safe"
name: safe-group
- labels:
version: risky
name: risky-group
Load Balancing
istio에서 적용할 수 있는 load balancing 알고리즘. - stickyness.
istio의 traffic management 기능을 그대로 적용하면, 매번 request를 보낼 때마다 traffic split이 이루어진다.
한 번 connection + session이 생성되면, 해당 세션에 한 번 설정된 traffic split이 그대로 유지되도록 설정할 수도 있음.
- request의 특정 값을 hash key로 사용한 sticky session 기능이 있다.
예컨대 ip주소값을 hash key로 사용하는 sticky session의 경우
Load balancer 옵션은 아래 docs에서 확인할 수 있다.
Consistent Hash를 사용할 경우, hash key로 사용할 수 있는 값은 아래에서 확인할 수 있다.
cf. Weighted Traffic Management 기능에 sticky session을 쓰면 제대로 동작하지 않는 버그가 있다고 함.
- https://github.com/istio/istio/issues/9764
- envoy에서 기능지원을 하지 않았기에 생긴 버그이고, istio에서 제대로 해결되지 않은 듯.
- istio 내부적으로는 load balancing을 적용하기 전에 weight가 먼저 적용되기 때문이라고 하는데..
- 이슈에 사람들이 남긴 workaround를 참고해서 적용하면 될 거 같다.
httpHeaderName 값을 hash해서 load balancing에 사용하는 예시 yaml 파일.
kind: VirtualService
apiVersion: networking.istio.io/v1alpha3
metadata:
name: a-set-of-routing-rules-we-can-call-this-anything # "just" a name for this virtualservice
namespace: default
spec:
hosts:
- fleetman-staff-service.default.svc.cluster.local # The Service DNS (ie the regular K8S Service) name that we're applying routing rules to.
http:
- route:
- destination:
host: fleetman-staff-service.default.svc.cluster.local # The Target DNS name
subset: all-staff-service-pods # The name defined in the DestinationRule
# weight: 100 not needed if there's only one.
---
kind: DestinationRule # Defining which pods should be part of each subset
apiVersion: networking.istio.io/v1alpha3
metadata:
name: grouping-rules-for-our-photograph-canary-release # This can be anything you like.
namespace: default
spec:
host: fleetman-staff-service # Service
trafficPolicy:
loadBalancer:
consistentHash:
httpHeaderName: "x-myval"
subsets:
- labels: # SELECTOR.
app: staff-service # find pods with label "safe"
name: all-staff-service-pods
ingress Gateway
kubernetes ingress : HW load balancer 없이도 여러 service를 외부로 노출할 수 있게 해주는 k8s Object.
만약 위와 같은 Canary Deployment를 해야 한다면, istio의 traffic management 옵션만으로도 충분히 가능할 것처럼 보이지만 실제로는 그렇지 않다.
- 정확히는, pod에서 다른 pod으로 요청을 보낼 때의 traffic management는 정상 작동.
- browser (외부)에서 pod으로 요청을 보냈을 경우에는 traffic management가 동작하지 않음.
원인은 istio Proxy를 타고 요청이 전달되는지 여부.
- pod에서 다른 pod으로 요청을 보낼 경우에는 istio proxy를 거쳐서 전달되므로 traffic management 옵션이 반영된 채 request가 전달된다.
- cluster 외부로부터 들어오는 요청은 Cloud provider's Load balancer이거나, NodePort로부터 직접 트래픽이 전달됨. 별다른 lb 알고리즘 설정이 없을 경우 round robin 방식으로 pod에 트래픽이 전달됨.\
- 즉, container가 요청을 받은 후에야 proxy 로직이 동작하기 때문.
istio에서 제시하는 해결방안: Edge Proxy (= istio Gateway).
- cluster 외부로부터 pod 요청이 들어오면, istio proxy에 우선 트래픽이 전달됨.
- proxy를 거친 후에야 pod로 요청이 전달되는 식.
Istio ingress Gateway: 외부로부터 오는 트래픽에도 istio Proxy 로직을 적용하기 위한 object (edge Proxy)
- [container + istio proxy]로 이루어져 있는 일반 pod.
- 따라서 configuration 생성 / 수정 방식이 일반적인 istio proxy configuration과 동일함.
istio-ingressgateway pod과 istio-ingressgateway Service가 존재함.
- istio-ingressgateway Service의 타입은 LoadBalancer / NodePort 등 외부로부터 트래픽을 받을 수 있는 타입으로 되어 있음.
- 위 예시의 경우 NodePort 번호는 31119. 따라서 외부에서 ingress-gateway pod로 요청을 직접 보내려 할 경우 : 로 요청을 보내면 됨. 내부적으로는 80 포트로 변환한다.
- 하지만 ingress-gateway pod는 기본적으로 deny all traffic. 따라서 ingress-gateway pod의 80포트를 열어야 외부로부터 트래픽을 받을 수 있다.
- ingress-gateway pod가 외부 트래픽을 받을 수 있도록 설정하려면 istio의 Gateway라는 Object를 사용해야 함. 아래 예시는 http + 80포트로 들어오는 요청을 허용하는 configuration.
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: ingress-gateway-configuration
spec:
selector:
istio: ingressgateway # use Istio default gateway implementation
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*" # Domain name of the external website. gateway가 적용될 domain scope를 결정함.
# 특정 도메인으로 들어오는 트래픽에만 80포트를 열도록 허용할 수 있음. *는 모든 도메인을 허용한다는 뜻
외부 트래픽이 ingress gateway로 들어오는 설정을 완료했으니, ingress gateway pod에 있는 Proxy 설정은 아래와 같이 진행하면 된다.
- ingress gateway의 proxy에서 적용할 로직이므로
- virtualService의 host값은 Gateway object에서 정의한 host값과 동일하다.
- hosts가 리스트이므로, 만약 내부의 특정 서비스에는 별도의 로직을 적용하겠다면 다르게 설정할 수 있음
kind: VirtualService
apiVersion: networking.istio.io/v1alpha3
metadata:
name: fleetman-webapp
namespace: default
spec:
hosts: # which incoming host are we applying the proxy rules to???
- "*" # Copy the value in the gateway hosts - usually a Domain Name
gateways:
- ingress-gateway-configuration # 위에 정의한 Gateway object 이름을 넣어준다.
http:
- route:
- destination:
host: fleetman-webapp
subset: original
weight: 90
- destination:
host: fleetman-webapp
subset: experimental
weight: 10
---
kind: DestinationRule
apiVersion: networking.istio.io/v1alpha3
metadata:
name: fleetman-webapp
namespace: default
spec:
host: fleetman-webapp
subsets:
- labels:
version: original
name: original
- labels:
version: experimental
name: experimental
ingress Gateway가 적용된 이후 kiali에서 Graph를 확인해보면
- default Namespace로 정의한 그래프임에도 불구하고 incoming traffic의 맨 앞단이 istio-system Namespace의 ingressgateway로 되어 있다.
요약하자면
- ingress gateway가 istio에서 반드시 필요한 옵션은 아니다.
- ingress gateway는 custom traffic management를 service 맨 앞단 (edge) 에서부터 적용하고자 할 때 사용함.
- 예컨대 사용자가 직접 request를 요청하는 webApp.
Prefix-based Routing
https://istio.io/latest/docs/reference/config/networking/virtual-service/#VirtualService 문서에서 확인할 수 있음.
문법은 대략 아래와 같음.
- route의 indentation 유의.
- 문서에는 header based routing 기능이 있다.
kind: VirtualService
apiVersion: networking.istio.io/v1alpha3
metadata:
name: fleetman-webapp
namespace: default
spec:
hosts: # which incoming host are we applying the proxy rules to???
- "*"
gateways:
- ingress-gateway-configuration
http:
- match:
- uri: # IF
prefix: "/experimental"
- uri: # OR
prefix: "/canary"
route: # THEN
- destination:
host: fleetman-webapp
subset: experimental
- match:
- uri :
prefix: "/"
route:
- destination:
host: fleetman-webapp
subset: original
---
kind: DestinationRule
apiVersion: networking.istio.io/v1alpha3
metadata:
name: fleetman-webapp
namespace: default
spec:
host: fleetman-webapp
subsets:
- labels:
version: original
name: original
- labels:
version: experimental
name: experimental
Subdomain Routing
prefix 방식은 별개의 URL을 생성하는 방법으로는 그다지 좋지 못함. 여러 개의 Url 자체를 생성한다면 subdomain 방식이 낫다.
- subdomain을 생성했을 때 routing 방법은 아래 yaml에서 확인 가능.
- prefix 방식보다 virtualService의 필드가 간결함
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: ingress-gateway-configuration
spec:
selector:
istio: ingressgateway # use Istio default gateway implementation
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*.fleetman.com" # Domain name of the external website
- "fleetman.com"
---
kind: VirtualService
apiVersion: networking.istio.io/v1alpha3
metadata:
name: fleetman-webapp
namespace: default
spec:
hosts: # which incoming host are we applying the proxy rules to???
- "fleetman.com"
gateways:
- ingress-gateway-configuration
http:
- route:
- destination:
host: fleetman-webapp
subset: original
---
kind: VirtualService
apiVersion: networking.istio.io/v1alpha3
metadata:
name: fleetman-webapp-experiment
namespace: default
spec:
hosts: # which incoming host are we applying the proxy rules to???
- "experimental.fleetman.com"
gateways:
- ingress-gateway-configuration
http:
- route:
- destination:
host: fleetman-webapp
subset: experimental
---
kind: DestinationRule
apiVersion: networking.istio.io/v1alpha3
metadata:
name: fleetman-webapp
namespace: default
spec:
host: fleetman-webapp
subsets:
- labels:
version: original
name: original
- labels:
version: experimental
name: experimental
header based Routing
브라우저에서 custom header를 추가하려면 chrome extension의 ModHeader 를 사용하면 된다.
문서: https://istio.io/latest/docs/reference/config/networking/virtual-service/#HTTPMatchRequest
- prefix와 마찬가지로 virtualService의 indentation에 유의.
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: ingress-gateway-configuration
spec:
selector:
istio: ingressgateway # use Istio default gateway implementation
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*" # Domain name of the external website
---
kind: VirtualService
apiVersion: networking.istio.io/v1alpha3
metadata:
name: fleetman-webapp
namespace: default
spec:
hosts: # which incoming host are we applying the proxy rules to???
- "*"
gateways:
- ingress-gateway-configuration
http:
- match:
- headers: # IF
my-header:
exact: canary
route: # THEN
- destination:
host: fleetman-webapp
subset: experimental
- route: # CATCH ALL. matching rule이 없을 경우 default로 적용되는 옵션.
- destination:
host: fleetman-webapp
subset: original
---
kind: DestinationRule
apiVersion: networking.istio.io/v1alpha3
metadata:
name: fleetman-webapp
namespace: default
spec:
host: fleetman-webapp
subsets:
- labels:
version: original
name: original
- labels:
version: experimental
name: experimental
응용하기: Dark Release
header based routing이 가능하다는 건, 동일한 Url에서 header만 변경하는 것으로 New Release를 Live Cluster에서 띄울 수 있다는 뜻.
일반적으로 Live cluster에서 테스트하기 위한 환경으로 staging을 구축하지만, staging을 제대로 구축하려면 실제 prod 환경을 그대로 복제해야 한다. Running Cost가 두 배인 셈.
따라서 prod Live cluster에 new release를 배포하고, 테스트하는 것.
- risk-averse 프로젝트에서는 받아들여지지 않을 수 있다. 충분히 이해함.
- 다만 istio를 사용해서 얻을 수 있는 이점을 활용한 응용방법 중 하나임.
microService 형태로 수많은 pod가 live cluster에 있을 경우, 모든 microservices를 staging 환경에 동일하게 배포 / 관리하는 작업은 힘들 수 있음.
- live test용 microservice 앱을 dark release로 배포하고, header based routing으로 다른 microservice 컴포넌트와 연결하고 문제여부를 확인하는 테스트 진행.
- 문제가 없을 경우, header based routing 옵션을 변경해서 새로운 버전이 트래픽을 받을 수 있도록 변경.
istio configuration의 변경만으로도 가능함. 단, jaeger에서 언급한 header propagation 이슈 있음.
- 브라우저 / frontend webapp에서 header를 받았다고 해도, 로직상 microservice의 다른 pod를 호출하는 과정에서 header의 Propagation은 자동으로 만들어지지 않음
- istio에서 header propagation 기능을 제공하지 않고 있으므로, 모든 application code에 custom header 파라미터를 전달하고 받을 수 있도록 설정해야 함.
kiali ui로도 Matching Routing 설정이 가능해졌다.
# Dark release용 virtualService / DestinationRulekind: VirtualService
apiVersion: networking.istio.io/v1alpha3
metadata:
name: fleetman-staff-service
namespace: default
spec:
hosts:
- fleetman-staff-service
http:
- match:
- headers:
x-my-header:
exact: canary
route:
- destination:
host: fleetman-staff-service
subset: risky
- route:
- destination:
host: fleetman-staff-service
subset: safe
---
kind: DestinationRule
apiVersion: networking.istio.io/v1alpha3
metadata:
name: fleetman-staff-service
namespace: default
spec:
host: fleetman-staff-service
subsets:
- labels:
version: safe
name: safe
- labels:
version: risky
name: risky
'학습일지 > Service Mesh' 카테고리의 다른 글
ServiceMeshCon 2020 - istio Service Mesh Simplified Beyond a Single Cluster (0) | 2022.11.10 |
---|---|
KubeCon 2019 - Istio Multi-Cluster Service Mesh Patterns Explained (0) | 2022.11.02 |
istio 개념 정리 (3) - mTLS Security (0) | 2022.10.29 |
Deview 2021 - istio/Envoy로 Multi-IDC L7 로드밸런서 만들기 (0) | 2022.10.22 |
istio 개념 정리 (1) - Service Mesh와 istio (0) | 2022.10.06 |