일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 오운완
- Zone2
- 트럼프2.0시대
- 런데이애플워치
- 이코노미스트한국구독센터
- 마법의연금굴리기
- 여행
- apollo-server-v3
- 가람집옹심이
- parquet
- 중사랑
- 루스틱
- 일권하는사회
- 송고버섯피자
- 한국걱정
- sparksql
- 달리기
- kafka-connect
- 잘쉬어야지
- 런데이
- 티지아이포럼
- neovim
- schema-registry
- 플라스틱은 어떻게 브랜드의 무기가 되는가
- 마연굴
- apollo-sandbox
- 강릉여행
- deepseek
- 저동하녹
- 여니브레드
- Today
- Total
해뜨기전에자자
LINE에서 Kafka를 사용하는 방법 요약 본문
이 글은 아래 글을 요약하여 재구성한 내용이다.
https://engineering.linecorp.com/ko/blog/how-to-use-kafka-in-line-1/
https://engineering.linecorp.com/ko/blog/how-to-use-kafka-in-line-2/
https://engineering.linecorp.com/ko/blog/how-to-use-kafka-in-line-3/
LINE에서 Kafka를 사용하면서, produce API중 일부가 매우 느려지는 현상을 퍼센타일 그래프를 통해 알아내고, 그를 해결하기 위한 내부 동작들을 분석하고 가정을 세우며 프로덕션에 적용해도 무리가 없는 systemTap라는 툴을 통해 증상을 확인하는 과정을 포함하고 있다. 또, 그 부분을 해결하기 위해 구조적으로 변경하는 pr로 Kafka에 기여했던 경험에 대한 내용도 포함하고 있다. 해당 패치 내용은 2018년 10월 쯤 적용되어 최종적으로 0.10.2.1부터 적용되었으니 좀 오래된 내용이긴 하다. 하지만 percentile을 이용해 분석하고, systemTab을 사용하는 방법 등을 소개하고 있어 소프트웨어 분석을 하는 접근 방법에 대해 참조할 만하고, 내부 구조에 대해 간략하게 설명하고 있어 카프카의 이해도를 높이는데 도움이 되었다.
Apache Kafka
라인의 경우 매일 2500억 건의 레코드가 입력되고, 데이터 양은 일일 210테라바이트. 가장 많을 때는 4GB/sec를 넘는 데이터가 입력된다. 50여 개의 독립적인 서비스와 시스템에서 사용하는 데이터.
- Middleware for streaming data
- High scalable/available
- Data persistency
- Supports Pub-Sub model
- 대용량의 데이터를 아주 잘 다룰 수 있도록 설계된 소프트웨어
- Kafka 자체에 캐시 레이어가 없다. 대신 OS 별로 제공되는 페이지 캐시에 캐시 기능을 전적으로 의존하고 있어서, 대용량 데이터가 앱 메모리와 OS의 페이지 캐시에 중복으로 캐시되는 것을 방지함
- Kf cli는 batching 기능을 기본적으로 지원. 여러 레코드를 하나의 큰 요청으로 묶어 전달 ⇒ 데이터 양이나 레코드 수가 늘어나도 요청 수 증가를 억제할 수 있어 요청 별로 발생하는 오버 헤드를 방지할 수 있다.
사용 방법
- 단순 분산 큐잉 시스템
- 데이터 허브
- 해당 데이터를 사용하는 다른 여러 서비스에 전파
하나의 클러스터에 데이터를 집중시키는 이유
- 데이터 허브
- 운영 효율
높은 신뢰성과 성능을 확보하기 위해서는
- 클러스터를 가혹한 작업 부하(workload)에서 보호할 수 있어야 한다
- 사내용이므로 악의적 공격은 고려하지 않아도 된다
- 하지만 사용자가 설정하거나 배포에서 실수하면 예기치 않은 작업 부하가 생길 수 있다
- 어느 요청이 어느 클라이언트에서 온 것인지 정확히 파악해야 한다
- 예기치 못한 자원 활동이 감지 되었을 때, 원인이 되는 클라이언트를 신속하게 찾아 해결해야 함
- 클러스터 간 일정 수준의 작업 부하 격리를 유지해야 한다
- 어떤 클라이언트가 그 자체의 작업 부하로 응답 시간이 느려 졌을 때, 같은 클러스터에 접속하고 있는 전혀 관련 없는 클라이언트가 같은 이유로 응답 시간이 저하되면 안된다.
가혹한 작업 부하에서 보호하기
- 데이터 양 보다 요청 수를 제어하는 것이 중요하다 ⇒ Kafka의 request quota를 사용한다
- Request quota
- 각 클라이언트가 사용할 수 있는 브로커의 자원, 즉 브로커의 스레드 시간을 제한
- 하나의 클라이언트가 예기치 못하게 모든 브로커 리소스를 사용하는 것을 막는다.
클러스터의 성능 저하 (feat. 운영 환경에서 발생한 문제)
-
Produce API 응답시간의 99번째 퍼센타일이 평소보다 50배에서 100배까지 느려짐 조사한 결과 두 가지 사실을 알게 되었음.
- 해당 시간대에 브로커 기기에 매우 많은 양의 disk read가 발생했다
- I/O를 담당하는 '네트워크 스레드'라는 이름의 스레드 이용률utilization이 매우 높아졌다
-
네트워크 스레드 이용률 증가 현상을 일으킬 만한 가능성이 있는 동작
-
실제로 처리해야 하는 요청 수가 많아져서? ⇒ 요청 수에 변동이 없었다
-
네트워크 스레드가 처리를 수행하는 과정인 이벤트 루프 내의 특정 처리에서 차단되었다? ⇒ fetch API의 sendfile이 문제일 것이다 가정, system call 처리 시간을 조사 ⇒ systemtap을 이용하여 sendfile 시스템 콜의 처리 시간 측정 및 히스토그램을 얻음
-
⇒ 브로커가 Fetch 요청을 처리 → sendfile 호출을 하면서 디스크 읽기가 필요해 네트워크 스레드의 이벤트 루프가 차단 → 같은 네트워크 스레드로 처리되어야 하는 후속 응답, 다른 관련 없는 API의 응답이 모두 차단
⇒ 해결: sendfile이 네트워크 스래드 내에서 호출될 경우 대상 데이터가 반드시 페이지 캐시에 존재하도록 개선.
- 요청 핸들러에서 디스크 데이터 로딩을 해 page cache에 올라가도록 함. 요청 핸들러 스레드는 하나의 큐를 전체가 폴링하는 모델이기 때문에 차단이 발생해도 다른 스레드에 전혀 영향을 주지 않아 다른 스레드는 그동안 계속 후속 요청을 처리할 수 있음.
- Page warm-up을 어떤 방식으로 처리할까?
- 읽기 ⇒ 사용자 공간의 버퍼에 복사되므로 kf가 가지는 이점이 사라짐
- sendfile /dev/null ⇒ 리눅스 커널의 sendfile은 /dev/null을 목적지로 호출되었을 때 메모리 복사를 하지 않게 구현되어 있음.
- sendfile → splice
- splice는 디바이스 별로 다르게 구현
- /dev/null splice\_write\_null > pipe\_to\_null
아래는 Kafka Internal에 대한 자세한 내용이다.
Kafka 의 요청 처리 방식
-
Kafka가 요청을 처리할 때 크게 두 개의 스레드 레이어를 사용함
-
네트워크 스레드
- 클라이언트와의 이벤트 기반 비동기 I/O처리를 수행
- 클라이언트 소켓에 도착한 request을 가져와서 request 객체를 생성하고, 준비된 response 객체를 클라이언트 소켓에 입력하는 역할
- 이벤트 루프를 처리하는 스레드
- 여러 개의 소켓을 다중화Multiplex해서 IO 준비가 완료된 소켓을 순차적으로 처리하는 작업을 계속 반복 수행
- I/O 완료를 기다리기 위해 block하지 않음.
- 여러 개의 소켓을 다중화Multiplex해서 IO 준비가 완료된 소켓을 순차적으로 처리하는 작업을 계속 반복 수행
-
요청 핸들러 스레드
- 네트워크 스레드가 가져온 요청의 내용을 처리해서 필요한 응답 객체를 네트워크 스레드에 반환하는 역할
-
-
전체적인 흐름
-
요청 읽기
-
클라이언트 소켓에 요청 도착
-
네트워크 스레드가 그 요청을 가져와서 request 객체를 생성하여 request Queue에 저장 이때, request Queue는 브로커 내에 하나만 존재함.
-
모든 요청 핸들러 스레드가 request Queue를 폴링해서 요청을 가져옴
-
-
요청 처리
- 요청 핸들러 스레드 하나가 큐에서 요청을 취득하면 처리가 진행됨. API 유형에 따라 로컬 디스크로 I/O가 실행되는 경우가 있고, response 객체가 생성된다. response 객체는 response Queue에 저장
- 요청 핸들러 스레드 하나가 큐에서 요청을 취득하면 처리가 진행됨. API 유형에 따라 로컬 디스크로 I/O가 실행되는 경우가 있고, response 객체가 생성된다. response 객체는 response Queue에 저장
-
응답 쓰기
- 네트워크 스레드가 response Queue에서 response를 가져와 클라이언트 소켓에 입력해서 처리를 완료함. 네트워크 스레드 하나 당 response Queue가 하나씩 존재.
- 네트워크 스레드가 response Queue에서 response를 가져와 클라이언트 소켓에 입력해서 처리를 완료함. 네트워크 스레드 하나 당 response Queue가 하나씩 존재.
-
API의 응답 처리 방식
- Fetch를 제외한 다른 API의 경우,
- 네트워크 스레드가 response Queue에서 response 객체를 취득한 시점에 클라이언트 소켓에 입력해야할 데이터가 모두 메모리에 저장되어 있다. 그래서 네트워크 스레드는 저장되어있는 데이터를 클라이언트 소켓에 그냥 복사하면 됨
- Fetch API의 경우,
- sendfile이라는 시스템콜을 이용하여 로컬 디스크에 저장 되어있는 topic의 데이터를 클라이언트 소켓에 복사한다. (zero copy transfer to client socket) 데이터 세그먼트가 userpace메모리에 한 번 복사하는 오버헤드를 막을 수 있어서 매우 효율적이라고 알려져있다. Kafka는 설계상 이 기능에 크게 의존함
- sendfile을 처리할 때 대상 데이터가 페이지 케시에 있으면 리눅스 커널이 데이터를 클라이언트 소켓에 복사. 만약 페이지 캐시에 데이터가 없다면 로컬 디스크에서 데이터 로딩.
- 페이지 캐시에 있다면 보통 수십~수백 us 소요된다고 알려져 있음.
- 디스크에서 가져오면 수~수십 ms 소요된다고 알려져 있음.
- sendfile을 처리할 때 대상 데이터가 페이지 케시에 있으면 리눅스 커널이 데이터를 클라이언트 소켓에 복사. 만약 페이지 캐시에 데이터가 없다면 로컬 디스크에서 데이터 로딩.
- sendfile이라는 시스템콜을 이용하여 로컬 디스크에 저장 되어있는 topic의 데이터를 클라이언트 소켓에 복사한다. (zero copy transfer to client socket) 데이터 세그먼트가 userpace메모리에 한 번 복사하는 오버헤드를 막을 수 있어서 매우 효율적이라고 알려져있다. Kafka는 설계상 이 기능에 크게 의존함
'개발 > kafka' 카테고리의 다른 글
kafka upgrade issue from 0.10.1.1 to 0.11.0.2: offsets.topic.replication.factor (0) | 2018.03.20 |
---|