Python에서 Protobuf 파싱하기 (새 탭에서 열림)
Datadog은 Kubernetes 메트릭 수집 효율을 높이기 위해 kube-state-metrics의 프로토콜 버퍼(Protobuf) 지원 기능을 도입하고 그 성능을 검증했습니다. 이 글은 텍스트 형식 대비 Protobuf의 효율성을 정량적으로 확인하기 위한 과정과, 특히 파이썬 환경에서 다중 메시지 스트리밍을 처리하는 구체적인 기술적 구현 방법을 다룹니다. 결론적으로 대규모 데이터 통신에서 이진 포맷이 제공하는 속도와 자원 효율성 이점을 실무에 어떻게 적용할 수 있는지에 대한 가이드를 제공합니다.
프로토콜 버퍼의 기초와 데이터 직렬화
- 프로토콜 버퍼(Protobuf)는 구조화된 데이터를 빠르고 효율적으로 이진 스트림으로 직렬화하는 방식으로, 머신 간 통신 및 RPC(원격 프로시저 호출)에 최적화되어 있습니다.
- 데이터의 구조를 정의하는
.proto파일을 작성한 후,protoc컴파일러를 통해 파이썬 등 다양한 언어에 맞는 소스 코드를 생성하여 사용할 수 있습니다. - 텍스트 기반 형식과 달리 엄격한 타입 정의를 통해 데이터의 일관성을 보장하며, 페이로드 크기를 획기적으로 줄여 HTTP API 성능을 개선합니다.
다중 메시지 스트리밍의 기술적 과제
- Protobuf는 자체 구분자(Self-delimiting)가 없는 설계 특성상, 여러 개의 메시지를 하나의 파일이나 소켓 스트림에 연속적으로 담을 때 각 메시지의 경계를 식별하기 어렵습니다.
- 이 문제를 해결하기 위해 각 메시지를 직렬화하기 전, 해당 메시지의 크기(Size) 정보를 머리말(Prefix)로 추가하는 방식이 권장됩니다.
- Java 구현체는
parseDelimitedFrom과 같은 내장 메서드를 통해 이를 지원하지만, 파이썬 표준 라이브러리는 이러한 기능을 기본적으로 제공하지 않아 별도의 구현이 필요합니다.
파이썬에서의 Varint 기반 스트리밍 구현
- 메시지 크기를 기록할 때는 작은 정수에 더 적은 바이트를 사용하는 가변 길이 정수 인코딩 방식인 'Varint'를 사용하며, 이는 효율적인 이진 통신을 가능하게 합니다.
- 파이썬에서는
google.protobuf.internal패키지에 포함된 내부 함수인_VarintBytes(인코딩용)와_DecodeVarint32(디코딩용)를 활용하여 Java와 호환되는 스트리밍 구조를 만들 수 있습니다. - 직렬화 시에는 메시지의
ByteSize()를 측정해 Varint로 먼저 쓰고 데이터를 기록하며, 역직렬화 시에는 버퍼에서 Varint를 읽어 메시지 길이를 파악한 후 해당 바이트만큼 데이터를 추출하여 파싱합니다.
데이터 전송 효율이 중요한 대규모 Kubernetes 모니터링 환경에서는 텍스트 포맷보다 Protobuf를 사용하는 것이 성능상 매우 유리합니다. 특히 파이썬 환경에서 다수의 메트릭을 스트리밍할 때는 표준 라이브러리 내부의 Varint 유틸리티를 활용하여 데이터 경계를 구분함으로써, Java 시스템과 완벽히 호환되면서도 고성능인 데이터 처리 파이프라인을 구축할 것을 권장합니다.