Search
moon
sun

Flink Autoscaler와 parallelism

제목
Tag
작성일
지난 글에서 Flink가 Kafka를 Partition·Key-Group·Offset이라는 세 층위로 읽는 구조를 정리했다. 이번엔 그 구조 위에서 Autoscaler가 무엇을 관측해 parallelism을 자동으로 조정하는지를 다룬다.
Autoscaler의 판단은 지금 subtask들이 얼마나 바쁘게 일하고 있는지를 보고 판단한다. 그 포화도를 이해하려면 먼저 Backpressure와 Busy라는 두 개념부터 짚어야 한다.

Flink Job 의 Operator와 Subtask

Flink Operator: 스트리밍 데이터를 처리하는 하나의 연산 단위로 아래 항목이 각각 Operator에 해당
•
Kafka에서 데이터를 읽는 Source
•
데이터를 변환하는 map/filter
•
데이터를 keyBy() 기준으로 분할하거나 집계하는 연산
•
최종적으로 데이터를 외부 시스템에 전달하는 Sink
Operator는 설정된 parallelism에 따라 여러 개의 subtask로 실행된다. 예를 들어 어떤 Operator의 parallelism이 4라면 동일한 연산을 수행하는 subtask 4개가 실행되며 각 subtask는 TaskManager의 Task Slot에 배치되어 실제 데이터를 처리한다.
Kafka ↓ Source Operator ├─ Source Subtask 1 ├─ Source Subtask 2 ├─ Source Subtask 3 └─ Source Subtask 4 ↓ Transform Operator ├─ Subtask 1 ├─ Subtask 2 ├─ Subtask 3 └─ Subtask 4 ↓ Sink Operator
Plain Text
복사

Backpressure

스트리밍 파이프라인의 Operator들은 버퍼로 연결되어 있다.
어떤 operator가 유입 속도를 처리 속도로 따라가지 못하면 그 앞단 버퍼가 채워지고, 버퍼가 가득 차면 업스트림 operator도 더 이상 데이터를 내보내지 못해 처리를 늦춘다.
이 지연이 파이프라인을 거슬러 올라가 결국 source의 소비 속도까지 떨어뜨리는 현상이 Backpressure이다.
Source Operator │ │ 데이터 전달 ▼ Transform Operator │ │ 처리 속도 ↓ ▼ [버퍼 가득 참] │ │ 데이터 전달 불가 ▼ Source도 대기 │ ▼ Kafka 소비 속도 ↓ │ ▼ Kafka Lag 증가
JavaScript
복사
•
Backpressure = 다운스트림이 병목이라는 신호
•
어떤 Operator의 처리 속도가 느려지면 그 영향이 업스트림으로 전파될 수 있음
•
Backpressure가 지속되면 Source가 Kafka에서 읽는 속도도 느려지고 결국 consumer lag으로 이어질 수 있음
즉 Backpressure는 단순한 성능 지표가 아니라 파이프라인에서 어느 지점이 병목인지 알려주는 진단 신호다.

Busy / Idle / Backpressured: subtask의 시간 회계

Flink는 각 subtask가 1초 동안 어떤 상태로 시간을 보냈는지를 측정한다. 1초(1000ms)를 busy, backpressured, idle 세가지 상태로 나눠보는 방식이다.
지표
의미
busyTimeMsPerSecond
실제 레코드를 처리한 시간
backPressuredTimeMsPerSecond
다운스트림이 안 받아줘서 대기한 시간
idleTimeMsPerSecond
유입이 없어 입력을 기다린 시간
세 값의 합은 대략 1000ms/s이고, 각각을 10으로 나누면 백분율로 볼 수 있다.
이 셋을 함께 보면 subtask의 상태가 해석된다.
•
Busy 높음 → 해당 Operator가 실제 처리에 많은 시간을 사용하고 있음. 처리 능력을 더 줘야 함(scale up 후보)
•
Backpressured 높음 → 자신은 여유 있지만 다운스트림이 병목. 자신의 parallelism보다 아래쪽을 확인해야 함
•
Idle 높음 → 유입 자체가 적음. 과할당 상태(scale down 후보)
Autoscaler는 이 중 Busy 비율을 핵심 신호로 사용하고, Backpressured와 Idle은 어느 지점을 늘리거나 줄일지 판단하는 보조 정보로 활용한다.

Utilization 기반 판단

Autoscaler는 각 subtask가 얼마나 바쁘게 일하고 있는지(Busy)와 얼마나 많은 데이터가 들어오고 있는지(유입률)를 바탕으로 현재 처리 능력을 추정한다.
그리고 각 subtask를 목표 사용률로 유지하려면 parallelism이 몇 개 필요한지를 역산한다:
목표 parallelism ≈ 유입율 / (subtask당 처리능력 × targetUtilization)
예를 들어 targetUtilization = 0.7이면 각 subtask가 약 70% 정도 바쁘게 일하도록 parallelism을 조정한다.
•
70%보다 지속적으로 더 바쁨 → 처리 능력이 부족하다고 판단 → Scale Up
•
목표 범위 안 → 현재 parallelism 유지
•
훨씬 한가함 → parallelism이 과할 수 있음 → Scale Down
여기에 이미 쌓여 있는 Kafka lag(backlog)도 함께 고려된다. catchUpDuration 안에 backlog까지 처리해야 하므로, 현재 사용률이 목표에 도달하지 않았더라도 backlog가 크면 parallelism을 상향할 수 있다.
다만 순간적인 Busy 값만 보고 바로 Scale하면 parallelism이 계속 늘었다 줄었다 하는 flapping이 발생할 수 있기 때문에, Autoscaler에는 이를 막기 위한 안정화 설정이 함께 적용된다.
설정
역할
targetUtilization
subtask를 몇 % 정도 바쁘게 유지할지 정하는 기준 (예: 0.7)
utilizationBoundary
목표 사용률에서 얼마나 벗어나야 Scale할지 정하는 범위 (예: 0.6 ~ 0.8)
metricsWindow
순간값이 아니라 이 창(예: 10분)의 평균으로 판단
stabilizationInterval
Scale한 뒤 얼마 동안 추가 Scale을 막을지 정하는 시간
catchUpDuration
쌓여 있는 backlog(lag)를 얼마 안에 따라잡을지 정하는 시간

Rescale: 판단이 실제로 반영되는 순간

Autoscaler가 parallelism 변경을 결정하면, Adaptive Scheduler를 통해 Job이 새로운 parallelism으로 재구성된다.
이 과정에서는 기존에 실행 중인 Job을 재시작해야 하기 때문에 잠깐 Kafka 소비가 멈출 수 있다. 이때 발생하는 흐름은 다음과 같다.
Autoscaler가 Scale 결정 ↓ Parallelism 변경 ↓ Job 재구성 / 재시작 ↓ 잠깐 Kafka 소비 중단 ↓ Lag 일시적으로 증가 ↓ 새 parallelism으로 정상 처리 ↓ 쌓인 Lag 회복
Plain Text
복사
•
재시작 동안 잠깐 소비가 멈춰 lag이 순간적으로 상승
•
재기동 후 catch-up.duration 내에 밀린 만큼을 소진하며 회복

Scale Limit

Autoscaler의 Scale Up에는 상한이 존재한다.
•
maxParallelism: Autoscaler가 Scale Up할 때 사용할 수 있는 parallelism의 최대값
•
Kafka Partition 수: Source는 파티션 수보다 많은 subtask를 만들어도 각 subtask에 할당할 파티션이 없기 때문에 남는 subtask는 아무 일도 하지 않고 대기한다.
즉 State를 나누는 Key-Group과 입력을 나누는 Kafka Partition이라는 두 축이 Autoscaling의 상한과 연결된다.

관측

•
busy 70% 넘어 스케일 업 트리거 되어 TaskManager가 1에서 2로 증설된 추이 볼 수 있는 그래프
•
증설 직후 Job 재구성으로 잠깐 소비 멈춰 컨슈머 lag가 쌓이지만 새 parallelism으로 회복

정리

Autoscaler의 동작은 다음 한 줄로 요약된다.
•
관측: subtask의 Busy 시간과 밀린 backlog
•
판단: 목표 사용률(targetUtilization)을 맞추는 parallelism 역산 + utilizationBoundary·metricsWindow 등으로 안정화
•
실행: Adaptive Scheduler를 통해 새 parallelism으로 Rescale → Job 재시작 과정에서 Checkpoint/Savepoint의 State와 Kafka Offset 복원
•
상한: maxParallelism과 Kafka Partition 수에 의해 제한