A Tale of Two Flink Autoscalers
Author: Samuel Yeboah, Francesco Di Chiara, Mingliang Liu (Netflix) | Source: netflixtechblog.com | Published: 2026-08-22
한 줄 요약
컨테이너 지표를 밖에서 보는 사내 오토스케일러는 클러스터 전체를 하나의 손잡이로만 조절할 수 있어 stateful 다중 연산자 DAG를 다루지 못했고, 각 연산자의 true processing rate를 안에서 추론하는 오픈소스 오토스케일러가 그 격차를 메웠다.
핵심 주장/내용
- 왜 오토스케일링이 필수인가: 2026년 기준 여러 AWS 리전에 걸쳐 3만 개 이상의 Flink 잡을 운영한다. 대부분이 손으로 배포되지 않고 관리형 플랫폼 Data Mesh가 생성하므로 대다수 사용자는 Flink 잡을 직접 건드리지 않는다. 작지만 늘어나는 집합은 개인화·Ads·Live 이벤트 같은 유스케이스를 위해 팀들이 만들고 운영하는 커스텀 잡으로, Kafka 토픽 사이로 레코드를 나르는 단일 연산자 잡부터 분기·조인·테라바이트 상태를 가진 stateful 파이프라인까지 다양하고 부하가 일일 주기·런칭·리전 failover에 따라 흔들린다
- 모든 잡을 피크에 맞춰 프로비저닝하면 낭비고 평균에 맞추면 급증 시 lag이 생긴다. 그리고 이 플랫폼에서 스케일링 액션은 공짜가 아니다 — 기본적으로 savepoint를 뜨고 잡을 우아하게 멈추고 새 크기로 재시작하는 것이라 큰 stateful 잡은 몇 분이 걸릴 수 있다
- 첫 번째 오토스케일러 (2019년경) — 밖에서 보기: 스트림 처리 잡 형태로 만들어 Mantis에서 돌며 텔레메트리 플랫폼 Atlas에서 모든 잡의 CPU·네트워크·Kafka lag·input rate·consume rate 라이브 피드를 소비했다. lag 유래 catch-up 시간, CPU/네트워크 활용 임계, 관측된 성능 이력, 최근 input rate에 대한 회귀를 결합해 스케일 업 시점이나 더 작은 클러스터가 lookahead 윈도우를 감당할 수 있는지 결정
- Flink 플랫폼과 독립적으로 동작하므로 Flink 자체 문제에 영향받지 않는다. 스트리밍 잡으로 만든 덕에 확장도 쉬웠다 — 각 오토스케일러 노드가 Flink 잡의 부분집합 지표를 처리했고 커스텀 샤딩이나 조율 로직을 쓸 필요가 전혀 없었다. 수천 개 관리형 파이프라인에서 리소스 사용을 25~45% 안정적으로 절감
- 밖에서 보는 것에는 천장이 있다. 시스템이 거친 컨테이너 지표로 클러스터 전체를 추론했고 손잡이 하나(총 TaskManager 수)만 스케일해 잡의 모든 연산자가 함께 움직였다. 만들어진 대상인 단순한 단일 연산자 파이프라인에는 맞았지만, Ads·추천·게임을 위해 팀들이 점점 가져오던 다중 연산자 stateful DAG에는 맞지 않았다. 새 케이스를 지원하려면 일반적 능력이 아니라 커스텀 로직이 더 필요했다
- 오토스케일러는 그 아래의 외부 시스템이 제공하는 지표만큼만 좋다. 그 지표가 진짜 문제를 놓칠 수 있다 — 잡이 완전히 바쁜데 그것이 CPU 활용률로 전혀 나타나지 않아 스케일러가 볼 방법 없이 저하된 상태에 갇힐 수 있다. 최근 네트워킹 마이그레이션이 일부 트래픽 보고 방식을 조용히 바꿔 스케일러가 의존하던 Atlas 지표 일부가 정확히 포착하기를 멈췄는데, 그 격차가 훨씬 나중에 프로덕션에서 표면화될 때까지 보이지 않았다
- 두 번째 오토스케일러 — 안에서 추론하기: 시작할 때는 커뮤니티에 성숙한 오토스케일러가 없었지만 재평가할 때는 있었다(Apache Flink Autoscaler). 컨테이너를 밖에서 보는 대신 잡 안에서 추론한다
- 핵심 아이디어는 각 연산자의 true processing rate(TPR) — 완전히 바쁘다면 지속할 수 있을 처리량 — 을 추정하는 것. Flink가 subtask별로 매 초 중 실제 작업에 쓴 비율을 backpressure나 idle 시간과 분리해 보고하므로, 관측된 처리량을 그 busy 비율로 나누면 완전 활용으로 외삽된다. 70% 바쁘면서 초당 700 레코드를 처리하는 연산자의 TPR은 700 / 0.7 = 초당 1,000 레코드
- source에서 시작해 job graph를 걸으며 각 연산자의 TPR·입출력 비율·목표 활용률로 어떤 연산자도 병목이 되지 않도록 각 vertex에 필요한 parallelism을 계산한다. 클러스터 전체를 단위로 리사이징하는 대신
- 결정적 차이 두 가지: OSS 오토스케일러는 사내 시스템이 못 하던 stateful 다중 연산자 잡을 정확히 스케일할 수 있고, 각 잡이 자기 설정(stabilization period, 임계, 기타 스케일링 동작)을 워크로드에 맞게 튜닝해 지닐 수 있게 한다
- Netflix 규모에서 돌리기 — 알고리즘 채택은 쉬웠고 안정적으로 돌리는 게 일이었다:
- OSS 오토스케일러는 원래 Flink Kubernetes Operator 안에 있도록 설계되었지만 Netflix의 Flink 플랫폼은 그 operator가 아니라 자체 control plane에서 돈다. 커뮤니티가 핵심 로직을 독립 라이브러리로 유지하는 훌륭한 결정을 내려 4개의 일반 인터페이스(잡 메타데이터·REST API 정보를 나르는 context, state store, event handler, 스케일링 결정을 적용하는 realizer)로 리팩터링했고 사내 생태계에 직접 플러그인하기 쉬워졌다
- 그 서비스는 오케스트레이션이 Temporal(durable 워크플로 엔진)에서 도는 Spring Boot 앱이다. orchestrator 워크플로가 약 1분마다 control plane에 오토스케일링이 켜진 잡을 폴링해 잡당 하나의 장기 워크플로를 시작하고, 각 워크플로가 그 잡의 vertex별 지표를 JobManager에서 가져와 OSS 평가 알고리즘을 돌리고 결정이 나오면 realizer에 넘긴다
- workflow-per-job 설계는 고통에 대한 직접적 대응이었다. 처음에는 전체 잡 집합에 대한 단일 배치 루프에서 평가를 돌렸는데 취약했다 — 느리거나 오작동하는 잡 하나가 뒤의 모든 잡에 대한 지표 수집과 스케일링을 stall시킬 수 있었다. 잡마다 자기 durable 워크플로를 주니 blast radius가 격리되어 문제 잡 하나가 이제 혼자 실패하고 재시도하며 잡을 온보딩할수록 런타임이 확장된다
- “커뮤니티에서 동작”과 “Netflix 규모에서 동작” 사이의 세 가지 엔지니어링 격차:
- 고 parallelism에서의 지표 수집 — 큰 잡에서 JobManager로부터 지표를 끌어오는 것이 병목이 되었고 원인 일부가 Flink 런타임에 있었다. JobManager를 바꿔 일시적 지표 이름을 캐시하고 매 fetch마다 재스캔하는 대신 한 번 정리하게 했고, 오토스케일러가 필요한 지표만 요청하도록 서버 측 필터링을 추가했다. 덕분에 이전에 약 1,000 subtask 위에서 고전하던 것이 최대 3,000 Flink subtask 잡에서 작동하게 되었다
- forward chaining 보존 — forward 연결로 이어진 두 vertex는 레코드가 고정된 로컬 채널로 메모리에서 넘겨지므로 같은 parallelism으로 돌아야 한다. 하나만 스케일하면 Flink가 실패하지 않고 조용히 그 엣지를 네트워크 셔플로 변환한다. fork가 forward 연결된 서브그래프를 탐지해 각각을 단위로 스케일
- sink 한계 존중 — 일부 sink는 유한한 쓰기 용량을 가지므로 async-sink backpressure 탐지를 추가해 더 흡수할 수 없는 sink로 잡을 스케일 업하지 않게 했다
- realizer가 실행 전에 안전 체크 세트를 돌린다 — 전사 리전 failover 중 evacuate되는 리전의 잡은 스케일 다운을 거부하고, 새 클러스터가 잡의 체크포인트 상태를 담을 디스크가 충분한지 검증하고, 더 큰 클러스터에는 작은 standby 버퍼를 더한다
- 너무 열심히 스케일 다운하는 것도 함정: 너무 깊게 자르면 CPU가 포화되고 lag이 급증하는데 지표 윈도우와 stabilization period가 재시작마다 다시 쌓여야 해서 시스템이 즉시 반응할 수 없다. 그래서 커뮤니티 기본값 0.7보다 낮은 목표 활용률 0.45로 돌려 의도적으로 약간의 효율을 안정성과 교환한다. 큰 stateful 잡에는 적고 차분한 rescale이 marginal 비용만큼의 가치가 있다
- 남은 가장 큰 비용은 스케일러 로직이 아니라 재시작과 상태 복구 과정 자체다. Flink 2가 disaggregated state 아키텍처로 상태를 로컬 디스크가 아니라 외부 스토리지에 두어 rescale·복구가 총 상태 크기에 얼마나 의존하는지를 크게 줄일 수 있다
주요 수치 / 사실
- 2017년부터 Flink로 스트림 처리, 2026년 기준 3만 개 이상 잡
- 1세대 오토스케일러가 수천 개 관리형 파이프라인에서 리소스 25~45% 절감
- OSS 기반 오토스케일러가 커스텀 잡에 GA된 뒤 클라이언트 텔레메트리·로깅 팀이 연환산 Flink 컴퓨트 지출 58% 감소, 연 약 $110만 절약. 세 요인 — 정적 프로비저닝은 항상 피크를 감안해야 하지만 오토스케일링은 일일 주기에 동적 적응해 야간·주말 트래픽 하락을 포착 / 성능 개선이나 연휴 후 둔화 뒤에 팀이 수동으로 리소스를 최적화하기를 기대하는 대신 오토스케일러가 지속적으로 용량 조정 / 균일한 컨테이너 차원 채택이 더 나은 bin-packing과 더 세밀한 스케일링 증분을 가능하게
- 일반화되는 세 가지 교훈: 지표 선택이 알고리즘 정교함보다 중요하다 — 가장 유용한 디버깅은 좀처럼 스케일링 수학에 대한 것이 아니라 어느 신호를 가장 신뢰할지에 대한 것이었다. 알고리즘을 튜닝하기 전에 지표를 이해하라 / 합리적 기본값을 두되 튜닝 여지를 남겨라 — 관리형 잡은 충분히 비슷해서 좋은 기본값 하나가 대부분을 손대지 않고 커버하고 그것이 플랫폼의 요점이다. 하지만 모든 잡에 단일 설정을 강요하면 맞지 않는 잡을 벌하므로 기본값과 잡별 오버라이드를 짝짓고 깊은 전문성이 필요한 손잡이는 의도적으로 숨긴다. 대부분의 팀은 오토스케일러를 생각할 필요조차 없어야 한다 / 채택한 다음 확장하라 — 2019년에는 맞는 성숙한 것이 없어서 사내에 만들었다. 강한 커뮤니티 프로젝트가 나타났을 때 올바른 수는 투자를 영원히 방어하는 것도 하룻밤에 뜯어내는 것도 아니라, 새 워크로드에 채택하고 수정을 기여하고 의도적 마이그레이션을 계획하는 것
관련 위키
- Real-Time Stream Processing
- Spot Instance Management
- Data Engineering FinOps
- Distributed Systems Reliability
- Open Source Data Stack
- Workflow Orchestration
- Durable Execution
Source: 원문 보기