Engineering Note
Consumer Lag은 건강 점수가 아니다: Kafka Consuming Pressure라는 관점
Kafka Consumer Lag을 절대값으로만 판단하지 않고, producer 유입량과 consumer group 처리 능력을 함께 보는 운영 관점을 정리합니다.
Kafka를 운영할 때 consumer lag은 가장 먼저 보게 되는 지표 중 하나입니다. lag이 늘어나면 메시지가 밀리고 있다는 뜻이고, 메시지가 밀리면 처리 지연, 알림 지연, 데이터 반영 지연으로 이어질 수 있습니다.
그래서 많은 서비스와 시스템 운영자가 consumer lag을 모니터링합니다. 이 선택은 맞습니다. 문제는 그 다음입니다.
consumer lag이 중요하다고 해서, 그 숫자 하나만으로 서비스 품질이나 시스템 안정성을 판단할 수 있을까요?
제 답은 “아니오”에 가깝습니다. consumer lag은 중요한 신호지만, 단독 판정 기준으로 쓰기에는 부족합니다. lag은 결과에 가깝고, 운영자가 알고 싶은 것은 보통 이것입니다.
지금 이 consumer group은 들어오는 메시지 압력을 감당하고 있는가?
이 글에서는 이 관점을 Consuming Pressure라고 부르겠습니다.
먼저 가져갈 결론
Kafka consumer lag을 볼 때는 다음을 함께 봐야 합니다.
- lag의 절대값보다 변화 추세가 중요합니다.
- lag 증가는 consumer 문제일 수도 있고 producer 유입량 변화일 수도 있습니다.
- producer rate가 consumer group의 안정 처리량을 넘으면 lag은 자연스럽게 증가합니다.
- consumer group의 최대 처리량 대비 현재 유입량 비율을 보면 상황을 더 빨리 이해할 수 있습니다.
- 이 비율을 1분, 5분, 15분 평균처럼 보면 Unix/Linux load average와 비슷한 운영 감각을 줄 수 있습니다.
정리하면 이렇습니다.
Consuming Pressure = producer rate / consumer group sustainable throughput
consumer lag은 지금 쌓인 결과를 보여줍니다. Consuming Pressure는 지금 시스템이 받고 있는 압력을 보여줍니다.
Consumer Lag은 무엇을 말해주나
Kafka에서 consumer lag은 단순화하면 “topic partition의 최신 offset과 consumer group이 처리했다고 커밋한 offset의 차이”입니다.
lag = log end offset - committed offset
lag이 크면 consumer group이 아직 처리하지 못한 메시지가 많다는 뜻입니다. 운영 관점에서는 아주 유용합니다.
- 처리 지연이 발생하고 있는지 확인할 수 있습니다.
- consumer 장애나 중단을 빠르게 감지할 수 있습니다.
- 재처리, 배치 유입, 트래픽 피크 이후 회복 상태를 볼 수 있습니다.
- 특정 partition만 밀리는 skew 문제를 찾는 출발점이 됩니다.
그러나 lag 숫자 자체는 맥락을 많이 생략합니다.
lag이 100,000이라고 해서 항상 장애는 아닙니다. 초당 50,000건을 안정적으로 처리하는 consumer group에게는 잠깐의 backlog일 수 있습니다. 반대로 lag이 1,000이어도 초당 10건밖에 처리하지 못하고 oldest message age가 계속 늘어난다면 사용자에게는 이미 문제가 될 수 있습니다.
절대값보다 추세를 먼저 본다
consumer lag에서 먼저 볼 것은 숫자의 크기보다 방향입니다.
| 상태 | 해석 |
|---|---|
| lag이 잠깐 증가한 뒤 줄어든다 | 일시적인 유입 증가를 처리하고 회복 중일 가능성이 큽니다. |
| lag이 계속 증가한다 | 현재 처리량이 유입량을 따라가지 못하고 있을 가능성이 큽니다. |
| lag이 일정 수준에서 유지된다 | 유입량과 처리량이 비슷하거나, 특정 지점에서 처리 흐름이 제한되고 있을 수 있습니다. |
| 일부 partition만 증가한다 | partition skew, key 분포, 특정 메시지 처리 비용을 의심해야 합니다. |
여기서 중요한 질문은 “lag이 얼마인가?”가 아니라 “lag이 왜 이 방향으로 움직이는가?”입니다.
lag이 계속 증가한다면 consumer group에 문제가 있다고 판단하기 쉽습니다. 실제로 consumer thread가 죽었거나, downstream DB가 느려졌거나, retry가 폭증했을 수 있습니다.
하지만 원인이 항상 consumer 쪽에 있는 것은 아닙니다.
메시지는 Producer에서 시작한다
consumer가 처리해야 하는 메시지는 producer에서 시작합니다. producer가 평소보다 훨씬 많은 메시지를 흘려보내면, consumer가 정상이어도 lag은 늘어날 수 있습니다.
예를 들어 consumer group이 안정적으로 초당 10,000건을 처리할 수 있다고 해보겠습니다.
| producer rate | consumer capacity | 예상되는 lag |
|---|---|---|
| 6,000/sec | 10,000/sec | 쌓인 lag이 있다면 줄어듭니다. |
| 10,000/sec | 10,000/sec | lag을 더 줄이기 어렵습니다. |
| 14,000/sec | 10,000/sec | lag이 초당 약 4,000건씩 증가합니다. |
세 번째 상황에서 consumer lag이 증가하는 것은 맞습니다. 하지만 이것을 consumer 장애라고만 보면 원인 분석이 틀어집니다. consumer는 자기 최대 처리량만큼 일하고 있을 수 있습니다. 문제는 현재 producer 유입량이 그 처리량을 초과했다는 데 있습니다.
그래서 consumer lag을 볼 때는 producer rate를 옆에 놓아야 합니다.
Consuming Pressure
제가 발표에서 Consuming Pressure라고 부른 개념은 간단합니다.
Consuming Pressure = producer rate / consumer group sustainable throughput
여기서 producer rate는 단위 시간 동안 topic에 새로 들어오는 메시지 양입니다. consumer group sustainable throughput은 해당 consumer group이 운영 환경에서 안정적으로 처리할 수 있는 처리량입니다.
해석은 직관적입니다.
| Consuming Pressure | 의미 |
|---|---|
< 1.0 | 현재 유입량은 처리 가능 범위 안에 있습니다. |
= 1.0 | 처리 한계에 가까워 lag을 줄이기 어렵습니다. |
> 1.0 | 유입량이 처리 능력을 넘고 있어 지속되면 lag이 증가합니다. |
이 값은 CPU 사용률처럼 “높으면 나쁘다”로 끝나는 숫자가 아닙니다. 운영 상황을 읽기 위한 압력 지표입니다.
pressure가 1.2라면 현재 유입량이 안정 처리량보다 20% 높다는 뜻입니다. 이 상태가 30초라면 큰 문제가 아닐 수 있습니다. 하지만 10분, 30분 지속된다면 backlog는 계속 쌓입니다.
Load average처럼 보기
Unix/Linux의 load average는 보통 1분, 5분, 15분 평균으로 봅니다. 숫자 하나만 보지 않고, 짧은 변화와 긴 흐름을 같이 봅니다.
Consuming Pressure도 비슷하게 볼 수 있습니다.
Consuming Pressure
1m: 1.35
5m: 1.12
15m: 0.84
이 값은 “방금 producer spike가 있었고, 5분 평균도 조금 밀리고 있지만, 15분 기준으로는 아직 장기 과부하는 아닐 수 있다”는 식으로 읽을 수 있습니다.
반대로 이런 값이라면 느낌이 다릅니다.
Consuming Pressure
1m: 1.08
5m: 1.11
15m: 1.16
현재 순간의 숫자는 더 낮아 보이지만, 장기 평균이 계속 1을 넘고 있습니다. 이 경우에는 consumer group이 꾸준히 감당 범위를 넘는 일을 받고 있다고 볼 수 있습니다.
load average를 CPU 코어 수와 함께 봐야 하듯, Consuming Pressure도 consumer group의 구조와 함께 봐야 합니다. partition 수, consumer 인스턴스 수, batch size, downstream 처리량, 메시지 크기, 메시지별 처리 비용이 모두 영향을 줍니다.
처리량은 어떻게 잡아야 하나
여기서 가장 조심할 부분은 consumer group sustainable throughput입니다. 이 값을 너무 낙관적으로 잡으면 pressure가 낮아 보이고, 너무 보수적으로 잡으면 매번 위험하게 보입니다.
실무에서는 다음 기준을 섞어 잡는 편이 좋습니다.
- 최근 정상 구간에서 consumer group이 안정적으로 처리한 최대 records/sec
- downstream DB, 외부 API, object storage 같은 의존 시스템이 버틴 처리량
- error rate와 retry가 급증하지 않는 처리량
- rebalance나 GC pause가 과도하게 늘어나지 않는 처리량
- peak 구간 이후 lag을 실제로 줄였던 처리량
메시지 수만으로 부족한 경우도 많습니다. 어떤 메시지는 1건 처리에 2ms가 걸리고, 어떤 메시지는 외부 API 호출 때문에 200ms가 걸릴 수 있습니다. 이 경우 records/sec만 보지 말고 bytes/sec, 처리 시간, 메시지 타입별 비율도 함께 봐야 합니다.
필요하다면 pressure를 하나가 아니라 여러 개로 나눌 수 있습니다.
record pressure = incoming records/sec / sustainable records/sec
byte pressure = incoming bytes/sec / sustainable bytes/sec
cost pressure = incoming estimated work / sustainable work capacity
처음부터 완벽한 모델을 만들 필요는 없습니다. 단순한 records/sec 비율만으로도 lag 숫자만 볼 때보다 훨씬 좋은 대화를 시작할 수 있습니다.
Lag과 Pressure를 같이 읽기
consumer lag과 Consuming Pressure를 함께 보면 상황이 더 선명해집니다.
| lag | pressure | 해석 |
|---|---|---|
| 증가 중 | > 1.0 | 현재 유입량이 처리 능력을 넘고 있습니다. producer spike, capacity 부족, scaling 필요성을 봅니다. |
| 증가 중 | < 1.0 | 계산된 capacity가 틀렸거나 consumer 내부 병목, partition skew, retry, rebalance를 의심합니다. |
| 감소 중 | < 1.0 | backlog를 회복하고 있습니다. 회복 예상 시간을 볼 수 있습니다. |
| 높지만 일정 | ~ 1.0 | 더 밀리지는 않지만 backlog를 줄일 여유도 없습니다. |
| 낮음 | > 1.0 | 아직 lag으로 보이지 않는 초기 과부하일 수 있습니다. 지속 시간을 봅니다. |
특히 마지막 경우가 중요합니다. lag이 아직 낮다고 해서 안전하다고 단정하면 안 됩니다. pressure가 계속 1을 넘고 있다면 lag은 곧 늘어날 가능성이 큽니다.
알람은 어떻게 잡을까
consumer lag 알람은 여전히 필요합니다. 다만 lag 절대값 하나로만 알람을 만들면 잡음이 많아집니다.
더 나은 알람은 보통 조건을 조합합니다.
lag is increasing
AND consuming pressure > 1.0 for 5 minutes
AND oldest message age is above service tolerance
또는 이런 식입니다.
lag is high
AND consuming pressure < 1.0
AND lag is not decreasing
첫 번째는 현재 유입량이 처리 능력을 넘는 상황을 찾습니다. 두 번째는 유입량은 감당 가능해 보이는데 lag이 줄지 않는 이상 상황을 찾습니다. 이때는 consumer thread 정지, 특정 partition skew, poison message, retry loop, downstream 병목 같은 문제를 봐야 합니다.
운영 대시보드에는 최소한 다음을 같이 두는 편이 좋습니다.
- consumer lag total
- partition별 lag
- lag 증가율
- producer records/sec 또는 bytes/sec
- consumer records/sec 또는 처리 완료 rate
- oldest message age
- consumer error rate와 retry count
- rebalance count
- downstream latency
- Consuming Pressure 1분, 5분, 15분 평균
하나의 지표로 시스템을 판단하지 않는다
consumer lag은 중요합니다. Kafka 운영에서 빼기 어려운 지표입니다.
하지만 lag은 혼자서는 충분하지 않습니다. lag은 “얼마나 밀렸는가”를 말해주지만, “왜 밀렸는가”와 “지금 감당 가능한가”를 직접 말해주지는 않습니다.
producer가 얼마나 밀어 넣고 있는지, consumer group이 안정적으로 얼마나 처리할 수 있는지, backlog가 회복되고 있는지, 사용자에게 의미 있는 지연이 생겼는지를 함께 봐야 합니다.
Consuming Pressure는 이 관계를 보기 위한 작은 렌즈입니다.
consumer lag tells us what has accumulated.
consuming pressure tells us what the system is currently being asked to absorb.
운영 지표는 숫자 하나로 끝나지 않습니다. 좋은 판단은 지표들의 관계에서 나옵니다. lag은 그 관계를 읽기 위한 출발점이어야지, 시스템 안정성을 단독으로 판정하는 마지막 문장이 되어서는 안 됩니다.
Kafka를 운영할 때도 같은 관점이 필요합니다. Konduo는 Kafka 같은 클러스터형 리소스를 플러그인으로 연결해 consumer lag, 처리량, 리소스 상태, 알림 대응을 하나의 운영 흐름에서 함께 볼 수 있도록 설계된 통합 관리 운영 플랫폼입니다.
마지막 체크리스트
Kafka consumer lag 알람이나 대시보드를 만들 때는 이 질문을 같이 확인합니다.
- lag 절대값만 보고 있지 않은가?
- lag 증가율을 보고 있는가?
- producer rate와 consumer 처리율을 같은 화면에서 보고 있는가?
- consumer group의 안정 처리량 기준을 갖고 있는가?
- pressure가 1을 넘는 시간이 얼마나 지속되는지 보고 있는가?
- partition별 lag skew를 확인할 수 있는가?
- oldest message age가 서비스 허용 지연을 넘는지 보고 있는가?
- retry, error, rebalance, downstream latency를 함께 보고 있는가?
- lag이 높을 때 “consumer 문제”라고 바로 결론 내리지 않는가?
consumer lag은 좋은 경보입니다. 하지만 좋은 경보는 좋은 질문으로 이어져야 합니다.
함께 읽기 좋은 글
이 글의 관점과 이어지는 주제를 더 보려면 아래 글도 함께 읽어볼 수 있습니다.
- 겉보기엔 균형 잡힌 Kafka, Broker Metrics는 다르게 말한다 - leader 수보다 실제 broker 부하를 봐야 하는 이유를 다룹니다.
- Consumer Group만으로는 Queue가 되지 않는다: Kafka Share Group의 의미 - Kafka share group이 기존 consumer group과 어떻게 다른지 정리합니다.
- Kafka Client Metrics는 관측의 빈틈을 메울 수 있을까 - Kafka client metrics가 관측 빈틈을 얼마나 메울 수 있는지 검토합니다.