공부하고 기록하는, 경제학과 출신 개발자의 노트

학습일지/workflows

KubeCon2023 - Scaling Argo Events for Enterprise Scheduling: Case Study from intuit

inspirit941 2024. 7. 13. 00:37
반응형

https://youtu.be/ydpMkJMRuOU?si=NU37fI2NU4JQg-zE

 

 

스크린샷 2024-07-10 오후 3 13 53

 

작년 ArgoCon에서, Argo Event를 활용해서 Batch Processing with InterDependencies among different pipelines한 사례를 공유했음.

  • 특정 파이프라인 종료 이벤트 발생 -> 이벤트 받아서 downstream 파이프라인이 시작되는 형태.
  • scale up to 10,000 pipelines로 scalability 확보했던 사례 소개.

The Context

스크린샷 2024-07-10 오후 3 17 17

 

EventBus와 통신하는 컴포넌트는 크게 두 가지

  • EventSource (event bus로 이벤트 전달하는 Provider)
  • EventSensor (event bus로부터 필요한 이벤트를 전달받는 Subscriber)

사용자에게 SDK 제공

  • 사용자가 자신의 파이프라인을 구성하면, 필요한 Argo CR로 변환한다.
  • HA mode로 구성하기 때문에, 일반적으로 Sensor에 해당하는 pod는 두 개 이상 생성된다.

Problems

스크린샷 2024-07-10 오후 3 20 29

 

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

스크린샷 2024-07-10 오후 3 39 04

 

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 사용

스크린샷 2024-07-10 오후 3 42 36

 

Event Source: 외부로부터 이벤트 받아서 EventBus에 전달하는 Pod.

Event Sensor 내부에 2개의 handler 구현. 둘다 EventBus와 Async 통신.

  • condition handler: 이벤트 subscribe 조건과 일치하는 이벤트가 오면, trigger topic에 메시지 전달.
  • trigger handler: kafka 메시지 consuming해서 로직 수행.

Sensor는 External Store에 본인의 상태, metadata 등을 저장한다.

스크린샷 2024-07-10 오후 4 10 27

 

서비스 구조는 위와 같이 바뀌었음.

  • SDK로 파이프라인을 정의하면, Sensor를 직접 만드는 대신 metadata (specification) 정보만 추가.
  • 개별 pod가 하던 일은 Centralized Sensor (deployment) 에서 수행.

Performance Optimization

목적: up to 15,000 pipelines / 25,000 dependencies

EventSource Optimization

스크린샷 2024-07-10 오후 4 21 10

 

기존 구조는 Event Source 레벨에서 filter가 적용됨.

위 그림을 해석하자면

  • pipeline A에서 발생한 이벤트를 adapting + EventBus로 전달하는 EventSource A
    • EventSource A는 두 개의 eventing server가 있다.
      • 하나는 pipeline A로 이벤트 전달
      • 다른 하나는 pipeline C로 이벤트 전달.
  • 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 체크하고, 조건에 부합할 경우 실행한다

스크린샷 2024-07-10 오후 4 54 28

 

문제점은

  • 개별 event source 하나하나에 다 pod가 뜨게 되면 scalability가 높지 않다는 것.
  • event source를 Centralized deployment로 변경하면, 모든 이벤트가 동일한 eventSource / eventing server로 매핑된 채 Sensor로 전달된다.
    • 이러면, all dependencies have Same Key.
    • key값으로 빠르게 1차 필터링하고, key 조건에 걸린 애들만 세부 조건을 비교하는 식으로 동작해야 하는데 1차 필터링이 동작하지 않게 되는 것.
    • 따라서 throughput도 크게 떨어진다.

스크린샷 2024-07-10 오후 4 59 24

 

대충 이런 구조가 되는 것. 거대한 병목이 만들어지게 된다.

스크린샷 2024-07-10 오후 4 59 34스크린샷 2024-07-10 오후 5 01 36스크린샷 2024-07-10 오후 5 03 56

 

게이트는 하나로 두되, 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

스크린샷 2024-07-10 오후 5 07 09

 

Sensor Metadata와 Sensor Pod의 결합도는 낮추었다.

  • 따라서 sensor pod가 실행되는 시점의 최신 정보를 fetch하는 식으로 동작.

문제는, 이마저도 pod 개수가 많으면 k8s로부터 fetch하는 데에만 10초 정도의 시간이 걸린다.

  • 150 TPS, 15,000 Sensor Metadatas

스크린샷 2024-07-10 오후 5 08 17

 

SensorMetadata Controller를 따로 만들었다.

  • metadata CRUD 이벤트 받아서 업데이트하고
  • Sensor pod는 controller가 관리하는 Data store만 본다.

데모 시연

영상 참고.

스크린샷 2024-07-10 오후 5 14 25스크린샷 2024-07-10 오후 5 14 30

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.
반응형