eBPF 관측성 도구는 사실 이렇게 동작한다 — Hubble, Pixie, Beyla 내부 흐름
요즘 새 클러스터 세팅할 때마다 CNI를 Cilium으로 깔고, 그 다음날부터 "Hubble UI 켜주세요" 요청이 들어온다. Pixie도 붙여봤고, 최근에는 Grafana Beyla까지 검토 목록에 올렸다. 세 도구 다 "사이드카 없이 관측성 확보"라는 같은 문구를 쓰는데, 사실 내부적으로는 노드에서 하는 일이 꽤 다르다. 이번 주에 팀 세미나 준비하면서 각 도구가 어디에 eBPF 프로그램을 붙이는지, 데이터는 어떻게 흘러 나가는지 다시 정리했다.
이 글은 그 정리 노트다. 사용법 튜토리얼은 아니고, "왜 이 도구는 HTTP 헤더까지 보이는데 저 도구는 IP:포트만 보이지?" 같은 궁금증에 답이 되도록 쓴다.
eBPF 훅 포인트가 결정하는 관측 범위
관측성 도구가 뭘 볼 수 있는지는 결국 커널의 어느 지점에 프로그램을 붙였느냐에 달려 있다. 크게 네 가지 위치가 자주 나온다.
첫째는 XDP와 TC(traffic control) 훅이다. 네트워크 드라이버 바로 위, 소켓보다 훨씬 아래 계층이다. 여기서는 이더넷 프레임과 IP 패킷 헤더가 보인다. 페이로드럄 파싱할 수는 있는데, TLS 이후 데이터는 어차피 암호문이라 의미가 없다. Cilium이 L3/L4 정책과 로드밸런싱을 이 계층에서 처리하고, Hubble의 기본 flow 이벤트도 여기서 나온다.
둘째는 kprobe/tracepoint다. tcp_connect, tcp_close, sys_execve 같은 커널 함수 진입/종료 시점에 후킹한다. 프로세스 컨텍스트가 살아 있어서 어떤 PID의 어떤 컨테이너에서 발생한 이벤트인지 알 수 있다. Tetragon과 Falco eBPF driver가 이 계층을 주로 쓴다.
셋째는 uprobe다. 유저스페이스 함수에 후킹한다. 예를 들어 OpenSSL의 SSL_read/SSL_write를 uprobe로 잡으면, TLS 암호화 직전/직후 평문 페이로드를 볼 수 있다. Pixie가 HTTP 요청/응답 바디, Redis 커맨드, PostgreSQL 쿼리 문자열까지 뽑아낼 수 있는 근거가 이것이다. libc의 함수, Go 런타임의 함수, JVM의 함수 등을 대상으로 삼는다.
넷째는 최근 뜨는 sock_ops와 cgroup/skb 훅이다. 소켓 상태 변화와 cgroup 단위 트래픽을 잡는데, 컨테이너별 통계를 낼 때 유용하다. Cilium이 sidecar 없이 mTLS를 하려고 이 계층을 활용한다.
정리하면 IP:포트만 필요하면 TC/XDP, 프로세스 단위 이벤트가 필요하면 kprobe, 애플리케이션 프로토콜 페이로드가 필요하면 uprobe다. Hubble·Pixie·Beyla가 각기 다른 조합을 쓰기 때문에 보이는 것도 다르다.
Cilium Hubble — 노드 로컬 링버퍼에서 flow가 흘러나오는 경로
Hubble의 흐름을 순서대로 따라가보면 이렇다. 파드가 패킷을 보내면 Cilium이 pod veth 앞단의 TC ingress/egress에 걸어둔 eBPF 프로그램이 먼저 실행된다. 이 프로그램은 identity(파드에 할당된 숫자 ID) 기반 정책을 평가하고, 통과시키면서 동시에 metadata를 perf ring buffer에 밀어넣는다.
노드에서 돌아가는 cilium-agent 프로세스가 이 링버퍼를 poll하고, 파싱해서 flow 이벤트로 만든다. 이때 파드 라벨, 네임스페이스, identity, verdict(FORWARDED/DROPPED/REDIRECTED) 같은 정보가 붙는다. hubble-relay는 각 노드의 agent에 gRPC로 접속해서 flow를 스트리밍 수집하고, 이걸 Hubble UI나 hubble observe CLI가 소비한다.
여기서 중요한 특성 두 가지. 우선 Hubble은 기본적으로 flow를 저장하지 않는다. 각 노드 agent가 링버퍼 크기만큼(기본 4095개)만 순환 저장하기 때문에, 오래된 이벤트는 그냥 덮어써진다. 장기 보관하려면 Timescale이나 S3로 export하는 별도 설정이 필요하다. "어제 오후에 뭐가 dropped 됐지?"라는 질문에 기본 세팅으로는 답을 못 한다.
두 번째, L7 가시성을 얻으려면 명시적으로 l7Visibility 어노테이션을 붙이거나 Cilium Network Policy에서 L7 규칙을 켜야 한다. 이때 트래픽이 노드 내 Envoy로 우회되면서 HTTP 메서드/경로가 보이기 시작한다. 즉 Hubble의 L7 이벤트는 사실 eBPF만으로 뽑는 게 아니라 Envoy가 파싱해서 다시 넘겨주는 거다. 이 부분 오해하는 팀이 은근 많다.
Pixie — uprobe와 커널 헤더 파싱을 함께 굴리는 방식
Pixie(현재 CNCF Sandbox, New Relic이 오픈소스로 유지)는 접근 방식이 조금 다르다. 노드마다 pem(Pixie Edge Module) 데몬셋이 뜨고, 이게 여러 종류의 eBPF 프로그램을 동시에 로드한다.
L4 트래픽 메타데이터는 kprobe로 tcp_sendmsg/tcp_recvmsg 근처에서 뽑는다. HTTP는 조금 특이한데, 커널 소켓 버퍼를 훑어서 페이로드 앞부분을 파싱한다. 평문 HTTP는 이걸로 잡힌다.
TLS를 뚫는 부분이 Pixie의 시그니처 기능이다. OpenSSL의 SSL_write 진입점에 uprobe를 걸어두면, 커널 아래로 내려가 암호화되기 직전의 평문이 그대로 보인다. 응답도 마찬가지로 SSL_read 반환 지점에서 잡는다. Go 앱은 Go 런타임의 crypto/tls 심볼을, Node.js는 v8 관련 심볼을 대상으로 삼는다. 그래서 "Pixie는 코드 수정 없이 HTTP 트레이스를 뽑는다"는 문구는 사실이다 — 다만 정적 링킹된 바이너리, 심볼 stripping된 바이너리, musl libc 기반 이미지에서는 uprobe attach가 실패하는 케이스가 종종 있다.
수집된 이벤트는 노드 로컬 인메모리 DB(엄밀히는 시계열 컬럼 스토어)에 들어간다. Pixie는 원격 저장소로 flow를 밀지 않는 게 원칙이다. 대신 PxL이라는 쿼리 언어로 노드 로컬 데이터를 훑는다. 이 아키텍처가 GDPR 관점에서 유리한 반면, 데이터 보존은 노드 메모리 크기에 묶이는 트레이드오프가 있다.
Grafana Beyla — 훨씬 가벼운 대신 잃는 것도 있다
Beyla는 최근에 검토하고 있는 도구다. Cilium이나 Pixie만큼 야심 차진 않고, "각 애플리케이션 프로세스에 붙어서 RED 메트릭(Rate, Errors, Duration)과 분산 트레이스만 뽑는" 좁은 목표를 가진다.
동작 원리는 Pixie와 겹치는 부분이 있다. HTTP/gRPC 라이브러리의 진입/반환 지점에 uprobe를 걸고, 요청 시작 시각과 종료 시각을 잡아 latency를 계산한다. 다만 언어 런타임별로 필요한 심볼 세트가 다르기 때문에, Go는 표준 라이브러리 net/http, Node.js는 특정 버전 이상, Java는 최근 몇 개월 사이에 추가된 지원 정도로 커버리지 편차가 있다. 우리 팀에서 Kotlin 서비스에 붙여보니 클래스로더 이슈로 attach가 불안정했다.
Beyla의 장점은 데이터가 곧바로 OTLP로 나간다는 것. 별도의 컨트롤 플레인 없이 Prometheus/Tempo로 바로 보낼 수 있다. 대신 Hubble이 주는 flow-level 관측이나 Pixie가 주는 페이로드 인스펙션은 못 한다. 딱 goldens signals만 필요한 팀에게 적당하다.
그래서 뭘 언제 쓰나
정답은 "겹쳐서 쓴다"에 가까웠다. Cilium을 CNI로 쓰는 이상 Hubble은 사실상 공짜(리소스 비용은 있다)라 무조건 켠다. HTTP 페이로드 디버깅이 필요한 순간이 잦은 팀은 Pixie를 인시던트 대응용 툴로 상시 배포한다. RED 메트릭만 필요한 서비스는 Beyla로 가볍게 계측한다.
사실 세 도구가 노드에서 하는 일을 겹쳐 보면 CPU 오버헤드가 무시할 수준은 아니다. 우리 스테이징 클러스터에서 세 개 다 켰을 때 노드당 평균 CPU 사용률이 6~9% 정도 늘었다. 프로덕션 도입 전엔 반드시 소규모 노드풀에서 하루 정도 부하 흘려보면서 관측해보길 권한다.
아직 검증 중인 부분도 있다. Beyla의 Kotlin/JVM 커버리지, Hubble flow의 장기 보관 스킴, Pixie가 커널 6.x 최신 배포판에서 uprobe attach하는 안정성. 다음 글에서는 세 도구 다 켰을 때 실제 리소스 프로파일을 시계열로 뽑아본 자료로 이어가려 한다.
혹시 다른 조합 쓰시는 분 있으면 어떤 워크로드에서 어떻게 셋업했는지 댓글로 남겨주시면 좋겠다.
