IL-0174 Hyperconnect · 2023-05-02 (게시일)kubernetesobservability
하이퍼커넥트는 Kubernetes 클러스터에서 Fluent Bit 를 로그 수집 에이전트로 쓰고, ElasticSearch, Kibana 와 함께 EFK 스택을 꾸려 두었다. 보통 DaemonSet 으로 띄우지만 로그를 특히 많이 내는 애플리케이션에는 파드 사이드카로 붙인다. Fluent Bit 2.0 이 나오자 사용자 영향이 적은 서비스부터 사이드카를 올렸는데, Kafka output 플러그인을 쓰는 컨테이너만 SIGSEGV 로 재시작됐다.
팀은 core 파일을 얻으려고 파드에 hostPath 볼륨을 붙이고, 노드에서 커널 파라미터 core_pattern 을 /tmp/cores/core.%e.%p.%h.%t 로 바꿨다. 다음 날 core 파일을 확보했고, 같은 arm64 환경을 fluent/fluent-bit:2.0.3-debug 이미지로 로컬에 띄운 뒤 v2.0.3 소스를 받아 gdb 로 분석했다.
분석해 보니 produce_message 함수가 map 이어야 할 인자를 타입 확인 없이 map 으로 읽다가 segmentation fault 를 냈다. 이 함수를 부른 cb_kafka_flush 는 msgpack_unpack_next 로 꺼낸 레코드를 flb_time_pop_from_msgpack 에 넘겼는데, 이 레코드가 array 가 아니라 MSGPACK_OBJECT_POSITIVE_INTEGER 였다. event_chunk 데이터를 덤프해 Python msgpack 으로 풀어 보니 정상 메시지 뒤에 null 바이트가 대량으로 붙어 있었다. event_chunk 는 비동기로 처리돼 core 에 그때의 스택 프레임이 없었고, 그래서 데이터가 왜 깨졌는지는 끝내 찾지 못했다. 팀은 설정으로 보아 input_tail_file 과 filter_modify 플러그인이 데이터를 만든 시점부터 깨졌을 것으로 추정했다. 다른 output 플러그인들은 이 경우를 예외로 처리하고 있었고, Kafka output 플러그인에만 이 처리가 빠져 있었다.
같은 입력을 줘도 항상 재현되지는 않아 테스트 코드는 쓰지 못했다. 대신 map 이 아닌 데이터가 produce_message 에 들어가지 않도록 고친 커스텀 빌드를 사이드카로 적용해 segmentation fault 가 사라지는 것을 확인했다. 그리고 업스트림에 PR 을 냈다. PR 은 제출 8일 만에 병합됐다.
하이퍼커넥트는 운영 Kubernetes 클러스터의 로그 수집 에이전트인 Fluent Bit 를 2.0 으로 올렸다. 사용자 영향이 적은 서비스를 골라 Fluent Bit 사이드카부터 업그레이드하고 며칠 지켜보니, Kafka output 플러그인을 쓰는 컨테이너만 SIGSEGV 로 재시작되고 있었다.
크래시 때 함께 찍힌 스택트레이스를 보면 out_kafka 플러그인의 kafka.c 에서 크래시가 났다. 정확한 원인을 보려면 core 파일이 필요했지만, Fluent Bit 가 Kubernetes 환경에서 돌고 있어서 core 파일이 남지 않았다.
core 를 확보해 gdb 로 열어 보니 bt 결과의 #3 프레임이 produce_message 였고, kafka.c:189 의 key = map->via.map.ptr[i].key 에서 segmentation fault 가 났다. map 변수의 타입은 MSGPACK_OBJECT_FLOAT64 였는데 코드는 이를 map 으로 읽고 있었다. 그래서 map->via.map 의 size 가 1561354800 이라는 터무니없이 큰 값으로 나왔다. event_chunk 의 데이터 크기는 4,911 바이트였고, 오프셋 859 부터는 전부 null 바이트였다.
증상 ? 컨테이너 재시작 반복
원인 ? 소프트웨어 결함
업그레이드 뒤 며칠 동안 모니터링하다가 Kafka output 플러그인을 쓰는 컨테이너만 SIGSEGV 로 재시작되는 것을 발견했다.
–
Linux 는 segmentation fault 가 나면 core 를 덤프하고, 이 파일을 분석하면 버그를 찾을 수 있다. Kubernetes 환경에서는 core 가 저절로 남지 않으므로 볼륨과 core_pattern 을 미리 잡아 두어야 한다. 직접 분석하기 어려워 core 파일을 다른 사람에게 넘길 때는 조심해야 한다. core 에는 실행 중이던 프로세스의 메모리 일부가 들어 있어 민감 정보가 섞일 수 있다.
같은 원인 · 공통 텔레메트리 에이전트
같은 원인 · 공통 텔레메트리 에이전트