Ruby rdkafka 설치 문제 해결: 지루한 컴파일 시간을 획기적으로 단축하다

The 60-Second Wait: How I Spent Months Solving the Ruby's Most Annoying Gem Installation Problem - Closer to Code

작성자
발행일
2025년 07월 17일

핵심 요약

  • 1 Ruby의 `rdkafka` 젬은 복잡한 의존성으로 인해 설치 시 60초 이상의 긴 컴파일 시간이 소요되어 개발자들에게 큰 불편을 초래했습니다.
  • 2 수백만 건의 다운로드마다 반복되는 비효율적인 컴파일 문제를 해결하기 위해, 동적 링크 대신 모든 의존성을 포함하는 정적 링크 방식의 사전 컴파일된 바이너리가 도입되었습니다.
  • 3 이로 인해 설치 시간이 5초 미만으로 단축되었으며, Docker 빌드 및 CI/CD 파이프라인 속도 향상과 함께 막대한 CPU 시간 및 에너지 절약 효과를 가져왔습니다.

도입

Ruby 개발자들에게 `rdkafka` 젬 설치는 오랫동안 고통스러운 과정이었습니다. 프로젝트 설정 시 `bundle install`을 실행하면 `rdkafka` 컴파일에 60초에서 90초가량 소요되어, 마치 멈춘 듯한 터미널을 하염없이 바라보며 시간을 낭비해야 했습니다. Karafka 생태계의 관리자로서 필자는 수년간 개발자들이 이 문제로 씨름하는 것을 지켜보았고, Docker 빌드 시간 증가, CI 파이프라인 지연, 그리고 수많은 컴파일 오류는 신규 개발자들의 진입 장벽이 되었습니다. 매월 백만 건 이상의 `rdkafka` 젬 다운로드와 각 설치에 소요되는 60초는 매월 6천만 초(거의 2년)에 달하는 CPU 시간을 낭비하는 결과를 초래했습니다. 이처럼 동일한 `librdkafka` 라이브러리와 부수적인 라이브러리들이 전 세계적으로 수백만 번씩 반복 컴파일되는 비효율성을 해결하기 위한 근본적인 변화가 필요했습니다.

이 문제의 해결은 ‘호환성 지옥’이라 불리는 rdkafka의 복잡한 의존성 구조 때문에 쉽지 않았습니다. rdkafka는 OpenSSL, Cyrus SASL, MIT Kerberos, 다양한 압축 라이브러리(zlib, zstd, lz4, snappy) 등 수많은 C 라이브러리에 의존하며, 이들 라이브러리의 버전은 각 Linux 배포판(Ubuntu, CentOS, Alpine Linux)과 macOS에서 상이하여 단일 바이너리를 만드는 것이 불가능해 보였습니다. 기존 시도들은 시스템 라이브러리에 동적으로 링크하여 배포 시스템의 라이브러리 버전이 다르면 문제가 발생하는 한계를 보였습니다.

필자는 nokogiri 젬이 사전 컴파일된 바이너리를 성공적으로 제공하여 XML 처리의 고질적인 컴파일 문제를 해결한 사례에서 영감을 얻어, ‘정적 링크’가 해답임을 깨달았습니다. 이는 모든 의존성을 소스 코드에서 컴파일하여 최종 라이브러리에 정적으로 포함함으로써, 시스템 라이브러리에 대한 의존성을 없애는 방식이었습니다. 첫 번째 성공은 Linux x86_64 GNU 시스템에서 나타났으며, 설치 시간은 60초 이상에서 5초 미만으로 극적으로 단축되었습니다. 그러나 Alpine Linux(musl libc 사용), macOS ARM64(Apple Silicon 아키텍처) 등 플랫폼별로 고유한 문제점들이 발생했습니다. 특히 Alpine Linux에서는 Cyrus SASL 라이브러리가 glibc 관련 함수를 계속 사용하려 하여 어려움을 겪었습니다.

보안 문제 또한 중요한 고려사항이었습니다. 사전 컴파일된 바이너리는 소스 코드보다 신뢰도가 낮다는 점을 인지하고, 모든 다운로드된 의존성에 대한 SHA256 검증, RubyGems Trusted Publishing을 통한 암호화 증명, 고정된 버전으로 재현 가능한 빌드, 악성 의존성에 대한 공급망 보호 등 포괄적인 보안 모델을 구현했습니다. 단일 네이티브 확장을 빌드하는 과정은 각 라이브러리가 올바른 순서와 플래그, 타겟 아키텍처로 컴파일되어야 하는 복잡한 ‘컴파일 발레’와 같았습니다. 2,000줄이 넘는 정교한 셸 코드로 구성된 빌드 스크립트를 개발했으며, 각 플랫폼의 미묘한 차이를 처리해야 했습니다.

CI/CD 파이프라인은 여러 플랫폼과 Ruby 버전(3.1, 3.2, 3.3, 3.4, 3.5)에 걸쳐 네이티브 확장을 테스트하는 ‘CI/CD 악몽’이었습니다. 이는 빌드 및 테스트 단계를 분리한 다단계 파이프라인으로 확장되었고, 각 플랫폼에 맞는 러너(Ubuntu Docker, macOS 러너)가 필요했습니다. 전체 시스템에 걸쳐 단 하나의 실패라도 발생하면 전체 빌드에 영향을 미쳤습니다. 각 플랫폼은 컴파일부터 Ruby 버전별 테스트까지 10개의 개별 CI 액션을 요구하며, 이는 총 30개의 복잡한 액션으로 구성된 파이프라인을 형성했습니다.

릴리스 프로세스 역시 혁신을 거쳤습니다. 이제 각 릴리스는 여러 플랫폼에서 동시에 빌드하고, 각 바이너리를 Ruby 버전별로 테스트하며, 여러 플랫폼별 젬의 릴리스를 조율해야 합니다. API 키 대신 암호화 토큰을 사용하는 RubyGems Trusted Publishing을 구현하여 보안을 강화하고 감사 추적 기능을 개선했습니다.

결론

수개월간의 노력 끝에 모든 주요 플랫폼에서 작동하는 네이티브 확장을 성공적으로 구현했으며, `gem install rdkafka` 명령 실행 시 60초 이상 걸리던 설치 시간이 단 3초로 단축되는 획기적인 결과를 얻었습니다. 이러한 변화는 단순한 설치 속도 향상을 넘어, Docker 빌드 시간을 크게 단축하고 CI 파이프라인의 응답성을 높였으며, 신규 기여자들의 개발 환경 설정 부담을 줄였습니다. 더 나아가, 매월 백만 건 이상의 다운로드를 기준으로 볼 때 60초의 컴파일 시간 단축은 매월 6천만 초(16,667시간)의 CPU 사용량 절감으로 이어져, 상당한 에너지 소비 및 CO2 배출량 감소라는 환경적 이점까지 가져왔습니다. 때로는 사용자조차 알아차리지 못하는 사소한 개선이 누적되어 훨씬 더 큰 가치를 창출한다는 교훈을 얻었습니다. `rdkafka` 컴파일을 기다리는 대신 개발자들은 더 중요한 작업에 집중할 수 있게 되었고, CI 파이프라인의 속도 향상은 더 많은 반복과 실험, 그리고 혁신으로 이어집니다. 60초 문제는 해결되었으며, 이제 `gem install rdkafka` 또는 `bundle install`은 빠르고 원활하게 작동합니다.

댓글 0

댓글 작성

0/1000
정중하고 건설적인 댓글을 작성해 주세요.

아직 댓글이 없습니다

첫 번째 댓글을 작성해보세요!