Search
Duplicate
📌

Istio Internals by xDS

Category
S/W 엔지니어
Tags
Service Mesh
Istio
xDS
Envoy
Kubernetes
Created time
2024/05/04

Introduction

Istio의 dynamic configuration의 핵심인 xDS에 관한 요약으로, Istio in Action을 주로 참고한 글이다. 참고로 dynamic configuration는 Istio Envoy proxy에 동적으로 설정되는 데이터로, 여기에는 Istio Envoy proxy가 노출할 port 부터 routing 규칙 및 upstream 서비스까지 모두 포함된다.

Motivation

Istio의 Namespace isolation을 사용하지 않으면, pod가 많아질 수록 Istio sidecar가 사용하는 메모리 크기가 커진다. 이로 인해 OOMKilled나 connection drop 등 다양한 오류가 발생하는데, 알고보니 주된 범인은 xDS 데이터였다. xDS는 Istio/Envoy dynamic configuration의 핵심이다.

xDS란

xDS는 eXtensible Discovery Services의 약자로 dynamic configuration 관리 서버, 즉 istiod 가 노출하는 API 및 이들 서비스를 가리킨다. Envoy는 istiod 를 ‘구독’하여 최종적 일관성(eventual consistency)에 기반하여 configuration을 상시 동기화한다(출처). 참고로 이 동기화에는 Istio의 경우 gRPC를 사용하는데, Envoy는 이외에도 HTTP long polling과 특정 filesystem watch 기법도 제공한다고.
xDS의 x에는 대표적으로 Listener(LDS), Route(RDS), Cluster(CDS), Endpoint(EDS)가 있는데, 이들 각각은 순서대로, Envoy가 노출할 port, 인입된 트래픽을 어떤 upstream 서비스로 보낼지 지정하는 규칙, upstream 서비스, 마지막으로 upstream 서비스의 주소를 가리킨다.
또한 위 그림에는 없지만 Istio가 사용하는 xDS에는 Secret(SDS), Aggregate(ADS), Name(NDS)도 있으며, 이 중 NDS는 Istio 전용으로 Envoy와 별도로 동작하는 Istio DNS Proxy를 위한 것이다.
아래는 Envoy xDS의 전체 목록이다.

xDS 상세

istioctl proxy-config command는 Istio proxy의 Envoy xDS API에 기반한 configuration 정보를 보여준다. 아래는 command 형식.
istioctl proxy-config <clusters|listeners|routes|endpoints|bootstrap|log|secret> <pod-name[.namespace]> -o <short|yaml|json>
주로 참고한 자료는 Istio in Action 이외에 아래 링크다.

Listener discovery service (LDS)

Listener는 Istio Proxy가 downstream traffic을 위해 어떤 IP, port 등을 열어야 하는지, listener로 들어온 traffic을 어느 Route로 보내야하는지 등을 정의한다. 또한, network filter chain이 위치하여 각종 전처리를 담당하기도 한다. EnvoyFilter 를 통해 custom filter를 이 chain에 넣을 수도.
아래는 앞서 설명한 command의 listener flag 기반 응답의 예로, 예컨데 첫 line은 80 port로 들어온 traffic은 이름이 http.80인 Route로 전달한다는 뜻이다(network filter chain을 보려면 -o yaml 또는 -o json 을 사용해야 한다).
# -o short flag 사용 ADDRESSES PORT MATCH DESTINATION 0.0.0.0 80 ALL Route: http.80 0.0.0.0 443 SNI: *.anyflow.net Route: https.443.default.default-gateway-istio-autogenerated-k8s-gateway-https-anyflow-net.cluster 0.0.0.0 15021 ALL Inline Route: /healthz/ready* 0.0.0.0 15090 ALL Inline Route: /stats/prometheus*
Bash
복사

Route discovery service (RDS)

Route는 Listener를 통해 들어온 traffic을 어떤 Cluster로 보내야할지를 지정하며, Kubernetes Gateway API의 HttpRoute 나 Istio의 VirtualService 로 지정한 routing rule이 반영된다. 참고로 여기서 Cluster는 Deployment, Daemonset 등의 동일 pod로 구성된 집합을 의미한다.
아래는 앞서 설명한 command의 route-o yaml flag 응답의 예로, 앞선 Listener의 예에서 확인한 http.80 Route의 상세이다.
해당 HttpRoute 설정은 path가 /dockebi 로 시작하고, X-Color header value가 blue 일 경우 dockebi-tick service로 보내는 내용인데, 이에 대한 내용이 그대로 담겨 있다. 또한 해당 조건에서 이름이 outbound|8080||dockebi-tick.cluster.svc.cluster.local 인 Cluster로 traffic을 보내도록 설정되어 있다.
# -o yaml flag 사용 - ignorePortInHostMatching: true maxDirectResponseBodySizeBytes: 1048576 name: http.80 validateClusters: false virtualHosts: - domains: - api.anyflow.net includeRequestAttemptCount: true name: api.anyflow.net:80 routes: - decorator: operation: dockebi-tick.cluster.svc.cluster.local:8080/* match: caseSensitive: true headers: - name: X-Color stringMatch: exact: blue pathSeparatedPrefix: /dockebi metadata: ... name: cluster.dockebi.0 route: cluster: outbound|8080||dockebi-tick.cluster.svc.cluster.local ... ...
YAML
복사

Cluster discovery service (CDS)

Cluster는 upstream 대상의 Deployment, Daemonset 등의 동일 pod로 구성된 집합을 의미하여, 각 pod의 endpoint를 지칭하는 Endpoint를 포함한다. 아래는 cluster flag 기반 응답의 예로, 앞선 Route에서 논한 outbound|8080||dockebi-tick.cluster.svc.cluster.local Cluster가 보인다. 참고로 SUBSET과 DESTINATION RULE이 비어있는데, 이는 dockebi-tick 에 대한 DestinationRule Istio resource를 지정하지 않아서 그런 듯.
# -o short flag 사용 SERVICE FQDN PORT SUBSET DIRECTION TYPE DESTINATION RULE BlackHoleCluster - - - STATIC dockebi-tick.cluster.svc.cluster.local 8080 - outbound EDS ...
Bash
복사

Endpoint discovery service (EDS)

Cluster에 속하는 endpoint 정보로, pod 수준에서의 IP, port 정보를 포함한다. 아래는 앞서 설명한 command의 endpoint flag 기반 응답의 예로, outbound|8080||dockebi-tick.cluster.svc.cluster.local Cluster의 사항만 남겼다.
# -o short flag 사용 ENDPOINT STATUS OUTLIER CHECK CLUSTER 10.244.1.220:8080 HEALTHY OK outbound|8080||dockebi-tick.cluster.svc.cluster.local ...
Bash
복사

Secret discovery service (SDS)

인증서 배포에 사용. 아래는 앞서 설명한 command의 secret flag 기반 응답의 예이다.
# -o short flag 사용 RESOURCE NAME TYPE STATUS VALID CERT SERIAL NUMBER NOT AFTER NOT BEFORE default Cert Chain ACTIVE true 4369fd4091de50ac891dc6f6514f271 2024-05-04T19:45:02Z 2024-05-03T19:43:02Z ROOTCA CA ACTIVE true 4bc95c93a6ee95043734d2fa5f677fa5 2034-03-15T05:19:41Z 2024-03-17T05:19:41Z
Bash
복사
-o yaml flag 사용 시

Aggregate discovery service (ADS)

xDS configuration에 대한 변경분을 나타내는 serialized stream으로, ADS를 쓰면 타 xDS configuration의 변경을 일일이 추적할 필요 없다고.

Name discovery service (NDS)

Kubernetes service 및 Istio ServiceEntry resource에 변경이 있을 때마다 DN(Domain Name) 정보를 동기화한다. 앞선 API와는 달리 istiod 의 debug endpoint를 사용. Command: k exec -n istio-system {istiod pod name} -- curl "localhost:8080/debug/ndsz?proxyID={pod name}.{namespace}"
{ "resource": { "@type": "type.googleapis.com/istio.networking.nds.v1.NameTable", "table": { "dockebi-tick.cluster.svc.cluster.local": { "ips": [ "10.106.182.113" ], "registry": "Kubernetes", "shortname": "dockebi-tick", "namespace": "cluster" }, ... } } }
JSON
복사

Appendix: Istio sidecar의 memory consumption 이슈에 관하여

Motivation 에서 논했듯, pod가 많아질 수록 Istio sidecar가 사용하는 메모리 크기가 커진다. 이로 인해 OOMKilled나 connection drop 등 다양한 오류가 발생하는 경우가 있다. 내가 경험한 상황과 유사한 보고가 상당수 보였는데 아래는 그 예이다.
Istio 공식 성능 테스트 결과(Istio version 1.21.2)인 1000RPS 부하에서의 0.5 core / 50M에는 상당히 반하는 결과다.
이는 Namepace isolation 적용 유무에 따른 것으로, Namepace isolation를 적용하면 각 pod는 Sidecar 에 지정된 namespace의 Cluster 및 Endpoint configuration만 memory에 유지하므로, Kubernetes cluster 전체에 대한 configuration을 갖는 이전과는 큰 차이를 보이게 되며, 타 namespace의 확장과도 무관해진다.
아래는 Sidecar resource definition의 한 예이다. 이는 pod 자신의 namespace 및 istio-system namespace의 pod로만 outbound traffic을 보낸다는 의미로, 이외의 namespace에 대한 Istio Cluter, Endpoint configuration은 memory에 보유하지 않는다.
apiVersion: networking.istio.io/v1beta1 kind: Sidecar metadata: name: default namespace: cluster # cluster namespace내 pod에 적용 spec: egress: - hosts: - "./*" # 자신의 namespace내 모든 pod로 outbound traffic을 보낼 수 있음을 의미 - "istio-system/*" # istio-system namespace내 모든 pod로 outbound traffic을 보낼 수 있음을 의미
YAML
복사
이외에 ServiceEntryVirtualserviceexportTo field를 통해서도 위와 동일 효과가 가능하다고.
여담으로, memory consumption 이슈는 상당히 큰 문제이기에 namespace isolation은 사실 상 필수 설정인데, Istio 공식 문서는 이에 대한 언급이 매우 부족해 보인다. 내 경우 한참이나 고생하다가 힌트를 Istio 공식 성능 테스트 결과(Istio version 1.21.2)에서 우연찮이 얻었다. 이 문서 조차 적용 사유 등에 대해서는 언급 없이 단순히 테스트 조건으로서의 namespace isolation 명칭과 Sidecar 로의 링크만 제공한다.

References