ArchyntArchynt.api
블로그로 돌아가기엔지니어링

우리가 직접 eBPF 센서를 만들지 않은 이유

Beyla는 이미 커널 레벨 트레이싱의 어렵고 지루하고 끝없는 부분을 해결하고 있습니다. 이를 다시 만드는 것은 우리가 절대 되돌릴 수 없는 유일한 자원을 소모하는 일이었을 것입니다.

2026년 6월 23일 · 6분 읽기

런타임 에이전트는 애플리케이션을 건드리지 않고도 서비스가 얼마나 빠르게 응답하는지, 얼마나 자주 실패하는지를 알아야 합니다. 피치덱에 쓰기엔 쉬운 문장입니다. 하지만 SDK도, 사이드카도, 재시작도 없이 이 정보를 살아있는 프로세스에서 뽑아내는 것은 완전히 다른 문제였고, 에이전트를 만들기 시작한 처음 몇 주 동안 저희는 이걸 직접 해결할 수 있을 거라고 생각했습니다.

계획은 종이 위에서는 단순했습니다: TLS 트래픽을 위해 SSL_write/SSL_read에 uprobe를 걸고, 나머지는 tcp_sendmsg에 kprobe를 걸어, 작은 eBPF 프로그램 안에서 HTTP와 gRPC 프레이밍을 파싱한 뒤, 대상 프로세스가 감시당하고 있다는 걸 전혀 모르는 상태로 RED 메트릭(rate, errors, duration)을 뽑아내는 것이었습니다. 일주일 만에 순수한 Go HTTP 서버를 대상으로 한 프로토타입은 작동했습니다. 하지만 실제 Spring Boot 서비스에 적용하자 곧바로 무너졌습니다.

실제로 무너지는 지점

eBPF verifier에서 무너진 게 아닙니다 — 이 부분은 정말 잘 설계되어 있어서, 루프에 빠지거나 크래시를 일으킬 수 있는 프로그램은 실행되기 한참 전에 커널이 거부합니다. 문제는 유저스페이스 경계에서 발생합니다. 아무도 콘퍼런스 발표에서 언급하지 않는 부분이죠: uprobe의 오프셋은 정확히 어떤 라이브러리 빌드인지에 따라 달라집니다. Ubuntu 22.04에서 OpenSSL 3.0.2의 SSL_write 오프셋은 6개월 뒤 같은 배포판의 OpenSSL 3.0.13에서는 다른 값이고, Alpine의 musl 링크 빌드에서는 또 다른 값이며, JVM이 시스템 프로바이더 대신 번들된 프로바이더로 자체 TLS를 처리할 때는 또 달라집니다. 여기에 모든 언어 런타임, 모든 컨테이너 베이스 이미지, 고객이 우연히 실행 중인 모든 마이너 버전을 곱하면, 끊임없이 다시 계산해야 하고 통제할 수 없는 커널을 위한 테스트 환경까지 필요한 오프셋 매트릭스가 만들어집니다.

이건 연구의 문제가 아닙니다. 영원히 끝나지 않는 유지보수의 문제이고, 한 번 배포한다고 끝나지 않습니다 — OpenSSL의 패치 버전이 나올 때마다 회귀 가능성이 생기는데, 그걸 자사 CI가 아니라 고객의 Slack 메시지로 알게 됩니다.

그래서 Beyla를 선택한 이유

Beyla(Grafana Labs의 eBPF 계측 프로젝트)는 이미 이 유지보수 부담을 오픈소스로 짊어지고 있으며, 커널과 라이브러리의 드리프트를 저희가 고객 한 명씩 발견해가는 대신 정확히 같은 환경들을 상대로 전업으로 추적하는 커뮤니티가 뒤에 있습니다. Beyla는 대상 애플리케이션에 코드를 전혀 넣지 않고도 서비스별 RED 메트릭 — 요청률, 에러율, p50/p99 지연시간 — 을 제공합니다. 덕분에 저희는 자체 eBPF 리소스를 진짜 저희 몫인 부분에 쓸 수 있게 됐습니다: procnet, /proc/<pid>/net/tcp를 읽어 실시간 TCP 연결을 컨테이너에 매핑하는 자체 컬렉터로, 커널에서 L7 프로토콜을 파싱하는 것보다 훨씬 좁고 안정적인 영역입니다.

이 결정의 솔직한 대가는, 지금 에이전트가 하나가 아니라 두 개의 컨테이너로 배포된다는 점입니다 — 저희 것과 Beyla의 것, 둘 다 호스트에서 상승된 권한을 가집니다. 이 마찰은 저희가 적극적으로 줄여나가고 있는 부분이고(다음 단계는 Beyla를 에이전트 바이너리의 감독 대상 자식 프로세스로 번들링하는 것입니다), 이건 계속 움직이는 목표물을 상대로 이겨야 하는 엔지니어링 도박이 아니라 풀어야 할 UX 문제입니다. 만들지도 않은 걸 만든 척하기보다는, 이 마찰을 솔직하게 드러내는 쪽을 택했습니다.

결국 도달한 원칙

진짜 자신의 제품인 것만 직접 만들어야 합니다. 저희에게 그건 그래프입니다: Dockerfile에서 보든, 실행 중인 컨테이너에서 보든, TCP 연결에서 보든 "orders 서비스"에 동일한 정체성을 부여하는 엔티티 해석, 그리고 어떤 의존성이 진짜인지 판단하는 상관관계 로직입니다. 고객이 실행할 수 있는 모든 언어와 모든 컨테이너 베이스 이미지를 아우르는 커널 ABI 경계에서의 eBPF는, 이미 다른 누군가의 전업 업무이며 — 그들은 그 일을 아주 잘 합니다.

직접 시스템에서 확인해보고 싶으신가요?

정적 그래프에는 리포지토리 URL 하나면 충분합니다. 런타임 에이전트는 docker-compose 파일 하나만 있으면 됩니다.