KubeCon2023 - Scaling Argo Events for Enterprise Scheduling: Case Study from intuit
https://youtu.be/ydpMkJMRuOU?si=NU37fI2NU4JQg-zE
작년 ArgoCon에서, Argo Event를 활용해서 Batch Processing with InterDependencies among different pipelines한 사례를 공유했음.
- 특정 파이프라인 종료 이벤트 발생 -> 이벤트 받아서 downstream 파이프라인이 시작되는 형태.
- scale up to 10,000 pipelines로 scalability 확보했던 사례 소개.
The Context
EventBus와 통신하는 컴포넌트는 크게 두 가지
- EventSource (event bus로 이벤트 전달하는 Provider)
- EventSensor (event bus로부터 필요한 이벤트를 전달받는 Subscriber)
사용자에게 SDK 제공
- 사용자가 자신의 파이프라인을 구성하면, 필요한 Argo CR로 변환한다.
- HA mode로 구성하기 때문에, 일반적으로 Sensor에 해당하는 pod는 두 개 이상 생성된다.
Problems
NAT streams 방식의 Event Bus 사용. NAT stream의 경우 Persistent Store를 지원하지 않음.
- 이벤트 유실: 사용자가 Sensor 정의를 변경하면, 변경 과정에서 발생하는 데이터는 핸들링하지 못해서 유실된다.
- Sensor Definition과 Sensor Runtime이 결합되어 있음.
- 둘 중 하나라도 변경되면 Sensor Pod의 restart -> 이벤트 유실
- Capacity Limitation (pod 개수)
- EventSource도 1 pod, EventSensor도 1 pod. HA 활성화하면 최대 4개 pod가 필요.
- 주어진 k8s 환경에서 만들 수 있는 pod 개수는 최대 7200개
- 이벤트 발행하지 않는데도 유지되는 Idle pod의 존재
Solutions
Sensor Definition (metadata) 과 Sensor Runtime 분리
- 잦은 pod restart 발생하지 않도록.
1 Deployment of Sensor for all Events
- Centralized Pool handle All Events. 클러스터 리소스 효율화
Sensor State 정보를 클러스터 외부에 저장.
- sensor pod를 재시작해도 이벤트 유실이 발생하지 않도록 한다
EventBus로 Kafka implementation 사용
Event Source: 외부로부터 이벤트 받아서 EventBus에 전달하는 Pod.
Event Sensor 내부에 2개의 handler 구현. 둘다 EventBus와 Async 통신.
- condition handler: 이벤트 subscribe 조건과 일치하는 이벤트가 오면, trigger topic에 메시지 전달.
- trigger handler: kafka 메시지 consuming해서 로직 수행.
Sensor는 External Store에 본인의 상태, metadata 등을 저장한다.
서비스 구조는 위와 같이 바뀌었음.
- SDK로 파이프라인을 정의하면, Sensor를 직접 만드는 대신 metadata (specification) 정보만 추가.
- 개별 pod가 하던 일은 Centralized Sensor (deployment) 에서 수행.
Performance Optimization
목적: up to 15,000 pipelines / 25,000 dependencies
EventSource Optimization
기존 구조는 Event Source 레벨에서 filter가 적용됨.
위 그림을 해석하자면
- pipeline A에서 발생한 이벤트를 adapting + EventBus로 전달하는 EventSource A
- EventSource A는 두 개의 eventing server가 있다.
- 하나는 pipeline A로 이벤트 전달
- 다른 하나는 pipeline C로 이벤트 전달.
- EventSource A는 두 개의 eventing server가 있다.
- pipeline B에서 발생한 이벤트를 adapting + EventBus로 전달하는 EventSource B
Event Sensor의 경우
- eventSource Name + eventing ServerName를 조합해서 dependency를 구성한다.
여기서 pipeline A 이벤트가 들어온다고 하면
- EventSource B는 이벤트를 무시한다.
- EventSource A에서
- eventing server A만이 이벤트 받아서 Sensor로 보낸다
- eventing server C는 이벤트를 무시한다.
- EventSource에서 '어떤 event source + 어떤 eventing server에서 이벤트를 처리했는지' 정보가 추가된 채 EventBus를 거쳐 Sensor로 전달된다.
- Event Sensor는 이벤트로 들어온 정보 확인해서 Dependency 체크하고, 조건에 부합할 경우 실행한다
문제점은
- 개별 event source 하나하나에 다 pod가 뜨게 되면 scalability가 높지 않다는 것.
- event source를 Centralized deployment로 변경하면, 모든 이벤트가 동일한 eventSource / eventing server로 매핑된 채 Sensor로 전달된다.
- 이러면, all dependencies have Same Key.
- key값으로 빠르게 1차 필터링하고, key 조건에 걸린 애들만 세부 조건을 비교하는 식으로 동작해야 하는데 1차 필터링이 동작하지 않게 되는 것.
- 따라서 throughput도 크게 떨어진다.
대충 이런 구조가 되는 것. 거대한 병목이 만들어지게 된다.
게이트는 하나로 두되, filtering / routing을 쉽게 처리할 수 있도록 Uniqueness를 부여하면 된다.
- dependency에 별도의 key-value를 추가하는 식으로 해결. 아래 예시의
identifierValue: your-identifier-value
- 이걸 설정해주는 것만으로도 성능이 85% 향상되었다고 함.
...
spec:
template:
# ...
dependencies:
- name: name
eventSourceName: esName
eventName: eventName
identifierValue: your-identifier-value # here
cf. Knative Eventing의 MT-Adapter에서 /namespaces/{namespace}/githubsources/{gitbhubsource-crd-name} 형태로 routing하는 것과 비슷한 개념.
- knative Eventing은 uniqueness로 namespace와 CR name을 사용했다.
SensorMetadata Retrieval Optimization
Sensor Metadata와 Sensor Pod의 결합도는 낮추었다.
- 따라서 sensor pod가 실행되는 시점의 최신 정보를 fetch하는 식으로 동작.
문제는, 이마저도 pod 개수가 많으면 k8s로부터 fetch하는 데에만 10초 정도의 시간이 걸린다.
- 150 TPS, 15,000 Sensor Metadatas
SensorMetadata Controller를 따로 만들었다.
- metadata CRUD 이벤트 받아서 업데이트하고
- Sensor pod는 controller가 관리하는 Data store만 본다.
데모 시연
영상 참고.
Q&A
Q. etcd 부하도 혹시 있었는지? 있었다면 어떻게 해결했는지
- pipeline CRUD할 때는 etcd 이슈가 없었고, sensor metadata를 k8s로부터 가져올 때 있었음.
- 별도의 RDS? Redis?에서 관리하는 식으로 해결했다는 것 같음 (발음이 RDS인지 redis인지 헷갈림)
Q. Any issues in particular when adding so many pods at one point to the cluster?
- CIDER IP 방식이라서 배포할 수 있는 pod limit이 정해져 있었음.
Q. Sensor를 Centralized했다고 하는데, centralized deployment는 별도의 cluster에 배포했는지?
- different namespace, same cluster.