티스토리 뷰

서비스 운영 중에 모든 알림에 대해 발송이 안되거나 중복 발송되는 현상이 발생했다.

AWS MSK와 EKS 클러스터를 운영하고 있으며, 그 외의 내용은 임의로 가정한 환경으로 포스팅한다.

 

서비스 구성 환경

기존 시스템 구성도

MSA 기반의 여러 마이크로 서비스가 알림 발송을 위해 Kafka로 publish하고, 이를 알림 마이크로서비스가 subscribe하는 구조이다.

 

시스템 항목
Kafka Partition Count 24
Consumer Group Name gojs_group
Consumer
(알림 서버)
replicas 4
concurrency 3
max.poll.interval.ms 300000
max.poll.records 500
enable.auto.commit false
ackMode MANUAL

문제 해결에 필요했던 기본적인 설정값은 위와 같다.

 

1차 분석

우선 카프카 컨슈머 그룹의 랙을 확인해보고자 조회를 해보았더니, 각 파티션에 랙이 수 천 건 쌓여있고 컨슈머가 미할당된 상태였다.

이에 따라 왜 이런 현상이 발생했는지 확인하고자 하였으나, 별도로 저장하는 로그가 없어서 확인이 어려웠다.

AWS MSK의 모니터링 로그를 생성해서 CloudWatch에 적재되도록 조치하고, 컨슈머 서비스를 rollout하여 모니터링해보고자 했다.

 

rollout 수행 시 동작 순서는 아래와 같다.

1. 알림 서버 rollout
2. 리밸런싱 발생하여 gojs_group의 컨슈머 12개가 24개 파티션에 할당
3. LAG이 일부 빠짐
4. 5분 뒤 모든 Consumer가 LeaveGroup 처리되어 대상에서 빠짐
5. 일부 빠졌던 LAG이 원래대로 돌아옴

동작 순서 다이어그램

MSK의 로그 그룹에 적재되는 내용 중에 어떠한 사유로 LeaveGroup 처리되는지는 없었다.

그러나 여기에서 얻은 힌트는 5분 뒤 일괄적으로 빠져버렸다는 점이었다. 

 

이에 따라 아래와 같이 가설을 세웠다.

현재 max.poll.interval.ms가 5분으로 설정되어 있어서, poll 발생 후 5분 동안 다음 poll이 발생하지 않아서 LeaveGroup 처리될 수 있다.

그렇다면 Consumer의 로직 어딘가에 병목이 있어서 처리 시간이 5분이 넘게되어, 이와 같은 문제가 발생할 수 있을 것이다.

그리고 아마도 해당 로직 내에 실행 쿼리가 많기 때문에 특정 쿼리에서 병목이 발생했을 것이다.

그러나 조회 쿼리는 거의 동일한 데이터로 테스트해보았으나 병목 구간이 없었다.

(명령 쿼리는 운영 환경에서 테스트해볼 수가 없었다)

 

그래서 우선 아래와 같이 먼저 배포를 진행했다.

1. max.poll.interval.ms를 300000(5분) -> 600000(10분) 으로 변경

2. Consumer 로직 내 timestamp 로그 출력 로직 삽입

 

2차 분석

rolling deployment가 발생하고 이 시점에 현상을 분석해보았다.

1. 알림 서버 rolling deployment
2. 리밸런싱 발생하여 gojs_group의 컨슈머 12개가 24개 파티션에 할당
3. LAG이 일부 빠짐
4. 10분 뒤 모든 Consumer가 LeaveGroup 처리되어 대상에서 빠짐
5. 일부 빠졌던 LAG이 원래대로 돌아옴

동작 순서 다이어그램

배포된 버전에서는 LeaveGroup이 10분 뒤에 발생했다.

즉, 기존에 세웠던 가설대로 max.poll.interval.ms 설정에 따라 LeaveGroup이 발생한 것이고, 컨슈머에 병목이 생긴 것이다.

 

컨슈머 병목을 예측하여 같이 배포했던 로그 출력 로직을 기준으로 분석해보니 병목 구간은 FCM 발송 로직이었다.

 

조치 및 해결

결과적으로 발생한 현상의 흐름은 가설대로 아래와 같았다.

발송 불가 케이스
중복 발송 케이스

FCM SDK를 통한 Push 발송 전 인증 단계에서 호출하는 oauth2.googleapis.com 도메인에서 타임아웃이 발생하는 것이었다.

이 원인은 따로 분석하여 이슈를 해결했다.. (자세한 원인은 생략)

 

결론 및 회고

해당 이슈를 해결하며 가장 힘들었던 점은 모니터링 및 이슈 인지를 도울 수 있는 도구들이 마련되어있지 않았던 점이었다.

(결국 테스트 및 분석용 배포를 수행하여야했다)

 

Kafka에 대해서도 그랬고, Push에 대해서도 그랬다. 따라서 아래와 같이 추가 조치를 수행했다.

1. API 모니터링용 로그 데이터 적재 로직을 KafkaListener에도 적용

2. Kafka consumer group별 LAG 임계값에 따라 담당자 alert 정책 추가

3. Push 알림에 대한 리포트 발송 배치 추가 개발

 

그리고 컨슈머 어플리케이션을 설계하는 과정에서 설정하는 값들에 대해 조금 더 명확하게 이해할 수 있었다.

1. max.poll.interval.ms
  컨슈머에서 한 번 poll해서 처리하는 로직이 이 값을 넘으면 안된다.
  작게 잡아서 처리 시간이 설정 값을 넘게되면 유효하지 않은 컨슈머로 간주된다.
  크게 잡으면 리밸런싱이 지연된다.

2. max.poll.records
  컨슈머가 poll해서 처리/커밋하는 단위이다.
  작게 잡으면 커밋에 대한 통신이 많아져서 오버헤드가 발생한다.
  크게 잡으면 max.poll.interval.ms 임계값을 초과하기에 적정한 값을 잡아야한다.

 

이에 따라서 위 값들을 아래와 같이 튜닝했다.

1. max.poll.interval.ms: 10분 -> 5분 (일반적으로 한 번 poll하면 1분 내외로 걸리기 때문에 5분도 충분히 여유)

2. max.poll.records: 500개 -> 50개 (병목이 생기더라도 현상 재발하지 않을 정도의 크기)

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/08   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
글 보관함