- Published on
eBPF 기반 제로 계측 Kubernetes 옵저버빌리티: Cilium Hubble과 Grafana Beyla 실전 가이드
- Authors
- Name
- 들어가며: 왜 eBPF 기반 제로 계측인가
- eBPF 기술 개요: 커널 레벨 계측의 원리
- Cilium Hubble 아키텍처: 네트워크 옵저버빌리티의 핵심
- Hubble 설치와 설정: Helm Chart 기반 배포
- Grafana Beyla 자동 계측: 제로 코드 APM
- Beyla 배포와 설정: Kubernetes DaemonSet 구성
- Grafana 스택 통합: Tempo, Mimir, Loki
- eBPF vs 사이드카 vs 에이전트 비교
- 운영 시 주의사항
- 실패 사례와 복구 절차
- 운영 체크리스트
- 참고자료

들어가며: 왜 eBPF 기반 제로 계측인가
Kubernetes 환경에서 옵저버빌리티를 확보하기 위해 전통적으로 사용되어 온 방식은 크게 두 가지다. 첫째, 애플리케이션 코드에 SDK를 삽입하여 메트릭과 트레이스를 생성하는 명시적 계측(Explicit Instrumentation) 방식이다. 둘째, Envoy 기반 사이드카 프록시를 각 Pod에 주입하여 L7 트래픽을 관측하는 서비스 메시 방식이다. 두 방식 모두 프로덕션 환경에서 검증된 접근이지만 근본적인 한계가 존재한다.
SDK 기반 계측은 모든 서비스에 라이브러리를 추가하고 계측 코드를 작성해야 한다. 수백 개의 마이크로서비스가 동작하는 대규모 클러스터에서 이를 일관되게 유지하는 것은 막대한 엔지니어링 비용을 수반한다. 사이드카 프록시 방식은 코드 변경 없이 L7 가시성을 확보할 수 있지만, Pod당 추가 컨테이너가 배포되면서 노드당 메모리 사용량이 수백 MB씩 증가하고, 네트워크 홉이 추가되어 P99 레이턴시가 2~5ms 상승하는 것이 일반적이다.
eBPF(extended Berkeley Packet Filter)는 이 두 가지 방식의 한계를 동시에 해결한다. 리눅스 커널 내부에서 샌드박스화된 프로그램을 실행하여, 애플리케이션 코드 변경 없이 커널 레벨에서 네트워크 흐름, HTTP/gRPC 요청, DNS 쿼리, TCP 연결 상태를 관측할 수 있다. 사이드카 컨테이너가 필요 없으므로 리소스 오버헤드가 극히 낮으며, 커널에서 직접 데이터를 수집하기 때문에 애플리케이션의 성능에 거의 영향을 미치지 않는다.
이 글에서는 eBPF 기반 옵저버빌리티의 핵심 도구인 Cilium Hubble과 Grafana Beyla를 중심으로, 제로 코드 변경으로 Kubernetes 클러스터에 네트워크 가시성과 애플리케이션 성능 모니터링(APM)을 구축하는 방법을 실전 운영 관점에서 상세히 다룬다.
eBPF 기술 개요: 커널 레벨 계측의 원리
eBPF 프로그램의 실행 흐름
eBPF는 리눅스 커널 4.x부터 본격적으로 도입된 기술로, 커널 모듈을 직접 수정하지 않고도 커널 공간에서 프로그램을 실행할 수 있게 한다. eBPF 프로그램은 사용자 공간에서 작성되어 커널의 Verifier를 통과한 후 JIT(Just-In-Time) 컴파일러에 의해 네이티브 코드로 변환된다. Verifier는 무한 루프 방지, 메모리 범위 초과 접근 차단, 허용된 헬퍼 함수만 호출 가능하도록 보장하여 커널 안정성을 유지한다.
eBPF 프로그램이 부착(attach)될 수 있는 훅 포인트는 매우 다양하다. 네트워크 관련으로는 TC(Traffic Control), XDP(eXpress Data Path), socket 레벨 훅이 있고, 시스템 전반의 관측을 위해서는 kprobes(커널 함수 진입/종료), tracepoints(미리 정의된 관측 지점), uprobes(사용자 공간 함수 추적)가 활용된다. Cilium Hubble은 주로 TC와 socket 레벨 훅을 사용하여 네트워크 흐름을 관측하고, Grafana Beyla는 uprobes와 kprobes를 활용하여 HTTP/gRPC 요청의 RED(Rate, Error, Duration) 메트릭을 자동으로 추출한다.
커널 버전별 eBPF 기능 지원
eBPF 기능은 커널 버전에 따라 지원 범위가 달라진다. Cilium Hubble의 완전한 기능을 활용하려면 최소 커널 4.19 이상이 필요하며, Grafana Beyla의 자동 계측 기능은 커널 5.8 이상에서 안정적으로 동작한다. 프로덕션 환경에서는 커널 5.15 LTS 이상을 권장한다. 특히 BTF(BPF Type Format) 지원이 활성화된 커널이어야 CO-RE(Compile Once, Run Everywhere) 방식으로 eBPF 프로그램을 배포할 수 있어 다양한 노드 환경에서의 호환성이 보장된다.
# 현재 노드의 커널 버전 및 BTF 지원 여부 확인
uname -r
# 출력 예: 5.15.0-91-generic
# BTF 지원 확인
ls /sys/kernel/btf/vmlinux
# 파일이 존재하면 BTF 지원 활성화 상태
# eBPF 기능 프로브 (bpftool 사용)
bpftool feature probe kernel | grep -E "map_type|program_type|helper"
# Kubernetes 노드의 커널 버전 일괄 확인
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.nodeInfo.kernelVersion}{"\n"}{end}'
Cilium Hubble 아키텍처: 네트워크 옵저버빌리티의 핵심
Hubble의 내부 구조
Cilium Hubble은 Cilium CNI의 옵저버빌리티 레이어로, 세 가지 핵심 컴포넌트로 구성된다. 첫째, 각 노드에서 동작하는 Hubble Server가 Cilium Agent에 내장되어 eBPF 데이터플레인에서 네트워크 이벤트를 수집한다. 둘째, Hubble Relay가 클러스터 전체의 Hubble Server로부터 이벤트를 집계하여 단일 API 엔드포인트로 제공한다. 셋째, Hubble UI가 서비스 맵과 네트워크 흐름을 시각적으로 표현한다.
Hubble이 관측할 수 있는 네트워크 이벤트의 범위는 다음과 같다. L3/L4 레벨에서는 TCP/UDP/ICMP 패킷의 소스/목적지 IP, 포트, 프로토콜 정보와 함께 패킷 드롭 사유를 제공한다. L7 레벨에서는 HTTP 요청/응답의 메서드, 경로, 상태 코드, DNS 쿼리와 응답의 도메인, 타입, 응답 코드, 그리고 Kafka 메시지의 토픽과 API 키를 파싱한다. 이 모든 정보가 Kubernetes의 Identity(네임스페이스, Pod, 서비스 레이블)와 자동으로 매핑되어 서비스 간 통신 패턴을 명확하게 파악할 수 있다.
서비스 맵과 플로우 가시성
Hubble의 가장 강력한 기능 중 하나는 자동 생성되는 서비스 맵이다. 전통적인 서비스 메시에서는 Envoy 사이드카가 각 Pod의 인바운드/아웃바운드 트래픽을 프록시하면서 서비스 토폴로지를 구성하지만, Hubble은 커널의 eBPF 프로그램이 수집한 네트워크 흐름 데이터로부터 서비스 간 의존성 그래프를 자동으로 구축한다. 이를 통해 서비스 메시를 도입하지 않고도 어떤 서비스가 어떤 서비스와 통신하는지, 통신 성공률과 지연 시간은 어떤지를 실시간으로 확인할 수 있다.
Hubble 설치와 설정: Helm Chart 기반 배포
Cilium + Hubble 통합 설치
Hubble은 Cilium CNI의 일부로 배포된다. 이미 Cilium을 사용하고 있다면 Hubble 기능을 활성화하기만 하면 되고, 새로 구축하는 클러스터라면 Cilium 설치 시 Hubble을 함께 활성화한다. 아래는 프로덕션 환경에 적합한 Helm values 설정이다.
# cilium-hubble-values.yaml
# Cilium + Hubble 프로덕션 배포를 위한 Helm values
hubble:
enabled: true
relay:
enabled: true
replicas: 3 # 고가용성을 위한 Relay 복제본
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
retryTimeout: 30s
sortBufferLenMax: 5000
sortBufferDrainTimeout: 1s
ui:
enabled: true
replicas: 2
ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- hubble.internal.example.com
tls:
- secretName: hubble-ui-tls
hosts:
- hubble.internal.example.com
metrics:
enableOpenMetrics: true
enabled:
- dns
- drop
- tcp
- flow
- port-distribution
- icmp
- httpV2:exemplars=true;labelsContext=source_ip,source_namespace,source_workload,destination_ip,destination_namespace,destination_workload,traffic_direction
serviceMonitor:
enabled: true # Prometheus Operator ServiceMonitor 자동 생성
interval: 15s
tls:
enabled: true # Hubble Server-Relay 간 mTLS 활성화
auto:
enabled: true
method: certmanager
certManagerIssuerRef:
group: cert-manager.io
kind: ClusterIssuer
name: hubble-issuer
# Cilium Agent 최적화 설정
operator:
replicas: 2
resources:
requests:
cpu: 100m
memory: 128Mi
# 모니터 버퍼 크기 (노드당 이벤트 캐시)
bpf:
monitorAggregation: medium # none/low/medium/high - 집계 수준
monitorInterval: 5s
monitorFlags: all
mapDynamicSizeRatio: 0.0025
# Cilium + Hubble 설치 (Helm)
helm repo add cilium https://helm.cilium.io
helm repo update
helm upgrade --install cilium cilium/cilium \
--version 1.16.5 \
--namespace kube-system \
--values cilium-hubble-values.yaml \
--wait
# 설치 검증
cilium status --wait
cilium hubble port-forward &
# Hubble CLI로 클러스터 전체 흐름 확인
hubble observe --since 1m --output compact
# 특정 네임스페이스의 HTTP 트래픽만 필터링
hubble observe \
--namespace production \
--protocol http \
--http-status 500-599 \
--output json | jq '{
source: .flow.source.labels,
destination: .flow.destination.labels,
http: .flow.l7.http
}'
# 서비스 간 드롭 패킷 분석
hubble observe \
--verdict DROPPED \
--namespace production \
--output table
Hubble 메트릭 기반 PromQL 쿼리
Hubble이 생성하는 메트릭을 Prometheus로 수집하면 다양한 네트워크 수준의 SLI(Service Level Indicator)를 정의할 수 있다. 아래는 프로덕션에서 자주 사용하는 PromQL 쿼리 모음이다.
# 서비스별 HTTP 요청 성공률 (SLI)
(
sum(rate(hubble_http_requests_total{http_status_code=~"2.."}[5m])) by (destination_workload, destination_namespace)
/
sum(rate(hubble_http_requests_total[5m])) by (destination_workload, destination_namespace)
) * 100
# 네트워크 정책에 의해 드롭된 패킷 비율
sum(rate(hubble_drop_total{reason="POLICY_DENIED"}[5m])) by (source_workload, destination_workload)
/ on(source_workload) group_left
sum(rate(hubble_flows_processed_total[5m])) by (source_workload)
# TCP 연결 설정 P99 지연 시간 (서비스별)
histogram_quantile(0.99,
sum(rate(hubble_tcp_connect_duration_seconds_bucket[5m])) by (le, destination_workload, destination_namespace)
)
# DNS 쿼리 실패율 상위 10개 워크로드
topk(10,
sum(rate(hubble_dns_responses_total{rcode!="No Error"}[5m])) by (source_workload, source_namespace, qtypes, rcode)
)
# 네임스페이스 간 트래픽 볼륨 매트릭스
sum(rate(hubble_flows_processed_total[5m])) by (source_namespace, destination_namespace)
Grafana Beyla 자동 계측: 제로 코드 APM
Beyla의 동작 원리
Grafana Beyla는 eBPF를 활용한 자동 계측 도구로, 애플리케이션 코드를 전혀 변경하지 않고 HTTP/HTTPS, gRPC, SQL 요청에 대한 RED(Rate, Error, Duration) 메트릭과 분산 트레이스를 자동으로 생성한다. Beyla는 각 노드에 DaemonSet으로 배포되며, uprobes를 통해 Go, Java, Python, Node.js, Rust, .NET 등 다양한 런타임의 HTTP 핸들러와 gRPC 서버 함수를 자동으로 탐지하여 훅을 설치한다.
Beyla가 생성하는 핵심 메트릭은 다음과 같다. http.server.request.duration은 HTTP 서버의 요청 처리 시간을 히스토그램으로 제공하고, http.client.request.duration은 HTTP 클라이언트의 외부 호출 시간을 추적한다. rpc.server.duration과 rpc.client.duration은 gRPC 서버와 클라이언트의 호출 시간을 각각 측정한다. 이 메트릭들은 OpenTelemetry 시맨틱 컨벤션을 따르며, OTLP 프로토콜로 직접 내보내거나 Prometheus Remote Write로 전송할 수 있다.
Hubble이 네트워크 레이어(L3/L4/L7)의 가시성을 제공한다면, Beyla는 애플리케이션 레이어의 성능 메트릭과 트레이스를 보완한다. 두 도구를 함께 사용하면 인프라 네트워크부터 애플리케이션 성능까지 전 계층을 코드 변경 없이 관측할 수 있는 완전한 옵저버빌리티 스택이 구성된다.
Beyla의 자동 서비스 탐지
Beyla는 프로세스를 자동으로 탐지하여 계측 대상을 식별한다. 탐지 방식은 두 가지로 나뉜다. 첫째, 실행 바이너리의 심볼 테이블을 분석하여 HTTP 서버나 gRPC 서버 관련 함수가 포함되어 있는지 확인한다. Go 바이너리의 경우 net/http.(*Server).Serve, google.golang.org/grpc.(*Server).Serve 등의 심볼을 탐지한다. 둘째, 리스닝 소켓을 모니터링하여 특정 포트에서 TCP 연결을 수락하는 프로세스를 식별한다. 이 두 방식을 조합하여 어떤 언어로 작성된 서비스든 자동으로 계측 대상에 포함시킬 수 있다.
Beyla 배포와 설정: Kubernetes DaemonSet 구성
DaemonSet 기반 배포
Beyla를 Kubernetes에 배포하는 가장 일반적인 방식은 DaemonSet이다. 각 노드에서 호스트 PID 네임스페이스에 접근하여 노드의 모든 프로세스를 탐지하고 계측한다.
# beyla-daemonset.yaml
# Grafana Beyla DaemonSet 배포 매니페스트
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: beyla
namespace: monitoring
labels:
app.kubernetes.io/name: beyla
app.kubernetes.io/component: auto-instrumentation
spec:
selector:
matchLabels:
app.kubernetes.io/name: beyla
template:
metadata:
labels:
app.kubernetes.io/name: beyla
annotations:
prometheus.io/scrape: 'true'
prometheus.io/port: '9090'
prometheus.io/path: '/metrics'
spec:
serviceAccountName: beyla
hostPID: true # 호스트 PID 네임스페이스 접근 필수
tolerations:
- operator: Exists # 모든 노드(마스터 포함)에 배포
containers:
- name: beyla
image: grafana/beyla:1.8.2
securityContext:
privileged: true # eBPF 프로그램 로드를 위해 필요
runAsUser: 0
ports:
- containerPort: 9090
name: metrics
protocol: TCP
env:
- name: BEYLA_OPEN_PORT
value: '80,443,8080,8443,3000,5000,9090'
- name: BEYLA_SERVICE_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: BEYLA_KUBE_METADATA_ENABLE
value: 'autodetect'
volumeMounts:
- name: beyla-config
mountPath: /config
- name: sys-kernel-security
mountPath: /sys/kernel/security
readOnly: true
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
volumes:
- name: beyla-config
configMap:
name: beyla-config
- name: sys-kernel-security
hostPath:
path: /sys/kernel/security
---
apiVersion: v1
kind: ConfigMap
metadata:
name: beyla-config
namespace: monitoring
data:
beyla-config.yml: |
# Beyla 메인 설정 파일
log_level: info
# 서비스 자동 탐지 설정
discovery:
services:
- k8s_namespace: "production|staging"
k8s_pod_labels:
app.kubernetes.io/part-of: ".*"
- k8s_namespace: ".*"
k8s_deployment_name: ".*"
# 메트릭 내보내기 설정
otel_metrics_export:
endpoint: http://mimir-distributor.monitoring:4317
protocol: grpc
interval: 15s
features:
- application
- application_process
- application_service_graph
histograms:
- explicit:
boundaries:
- 0.005
- 0.01
- 0.025
- 0.05
- 0.1
- 0.25
- 0.5
- 1
- 2.5
- 5
- 10
# 트레이스 내보내기 설정
otel_traces_export:
endpoint: http://tempo-distributor.monitoring:4317
protocol: grpc
sampler:
name: parentbased_traceidratio
arg: "0.1" # 10% 샘플링
# Prometheus 엔드포인트 설정
prometheus_export:
port: 9090
path: /metrics
features:
- application
- application_process
- application_service_graph
# 네트워크 메트릭 (실험적 기능)
network:
enable: true
cidrs:
- 10.0.0.0/8
- 172.16.0.0/12
# 속성 설정
attributes:
kubernetes:
enable: true
host_id:
fetch_timeout: 5s
select:
beyla_network_flow_bytes:
include:
- k8s.src.namespace
- k8s.dst.namespace
- direction
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: beyla
namespace: monitoring
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: beyla
rules:
- apiGroups: ['']
resources: ['pods', 'nodes', 'services', 'replicationcontrollers']
verbs: ['get', 'list', 'watch']
- apiGroups: ['apps']
resources: ['deployments', 'replicasets', 'statefulsets', 'daemonsets']
verbs: ['get', 'list', 'watch']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: beyla
subjects:
- kind: ServiceAccount
name: beyla
namespace: monitoring
roleRef:
kind: ClusterRole
name: beyla
apiGroup: rbac.authorization.k8s.io
# Beyla DaemonSet 배포
kubectl apply -f beyla-daemonset.yaml
# 배포 상태 확인
kubectl rollout status daemonset/beyla -n monitoring
# Beyla 로그에서 자동 탐지된 서비스 확인
kubectl logs -n monitoring -l app.kubernetes.io/name=beyla --tail=50 | grep "instrumenting"
# 출력 예:
# msg="instrumenting process" pid=12345 service=frontend-deployment
# msg="instrumenting process" pid=12346 service=api-server-deployment
# msg="instrumenting process" pid=12347 service=payment-service-deployment
# Beyla가 생성하는 메트릭 확인
kubectl exec -n monitoring $(kubectl get pod -n monitoring -l app.kubernetes.io/name=beyla -o jsonpath='{.items[0].metadata.name}') \
-- wget -qO- http://localhost:9090/metrics | head -40
# eBPF 프로그램 로드 상태 확인
kubectl exec -n monitoring $(kubectl get pod -n monitoring -l app.kubernetes.io/name=beyla -o jsonpath='{.items[0].metadata.name}') \
-- bpftool prog list | grep beyla
Grafana 스택 통합: Tempo, Mimir, Loki
통합 아키텍처
Cilium Hubble과 Grafana Beyla가 생성하는 텔레메트리 데이터를 Grafana LGTM(Loki, Grafana, Tempo, Mimir) 스택과 통합하면 제로 계측 기반의 완전한 옵저버빌리티 플랫폼이 구성된다. 데이터 흐름은 다음과 같다.
Hubble 메트릭은 Prometheus ServiceMonitor를 통해 수집되어 Mimir에 장기 저장된다. Hubble의 네트워크 흐름 로그는 Fluentd 또는 Vector를 통해 Loki로 전송된다. Beyla가 생성하는 RED 메트릭은 OTLP 프로토콜로 Mimir Distributor에 직접 전송되거나 Prometheus Remote Write를 통해 저장된다. Beyla의 분산 트레이스는 OTLP를 통해 Tempo Distributor로 전송된다.
Grafana 대시보드에서는 Mimir의 메트릭(Hubble 네트워크 + Beyla APM), Tempo의 트레이스(Beyla 자동 생성), Loki의 로그(Hubble 플로우 로그)를 단일 뷰에서 상호 연관(Correlate)시킬 수 있다. 트레이스에서 메트릭으로, 메트릭에서 로그로의 자유로운 탐색이 가능하여 장애 원인 분석(RCA) 시간을 대폭 단축할 수 있다.
Grafana 데이터소스 및 대시보드 설정
# grafana-datasources.yaml
# Grafana 데이터소스 ConfigMap (Hubble + Beyla 통합)
apiVersion: v1
kind: ConfigMap
metadata:
name: grafana-datasources
namespace: monitoring
labels:
grafana_datasource: '1'
data:
datasources.yaml: |
apiVersion: 1
datasources:
# Mimir - Hubble 네트워크 메트릭 + Beyla APM 메트릭 통합 저장
- name: Mimir
type: prometheus
url: http://mimir-query-frontend.monitoring:8080/prometheus
access: proxy
isDefault: true
jsonData:
timeInterval: 15s
exemplarTraceIdDestinations:
- name: traceID
datasourceUid: tempo
urlDisplayLabel: View Trace
httpMethod: POST
prometheusType: Mimir
# Tempo - Beyla 자동 생성 트레이스 저장
- name: Tempo
type: tempo
uid: tempo
url: http://tempo-query-frontend.monitoring:3200
access: proxy
jsonData:
tracesToMetrics:
datasourceUid: mimir
spanStartTimeShift: "-1h"
spanEndTimeShift: "1h"
tags:
- key: service.name
value: service
- key: http.method
value: method
queries:
- name: Request Rate
query: "sum(rate(http_server_request_duration_seconds_count{service=\"${__span.tags.service.name}\"}[5m]))"
- name: Error Rate
query: "sum(rate(http_server_request_duration_seconds_count{service=\"${__span.tags.service.name}\",http_status_code=~\"5..\"}[5m]))"
tracesToLogs:
datasourceUid: loki
spanStartTimeShift: "-5m"
spanEndTimeShift: "5m"
filterByTraceID: true
filterBySpanID: false
tags:
- key: k8s.namespace.name
value: namespace
- key: k8s.pod.name
value: pod
serviceMap:
datasourceUid: mimir
nodeGraph:
enabled: true
search:
hide: false
lokiSearch:
datasourceUid: loki
# Loki - Hubble 플로우 로그 + 애플리케이션 로그
- name: Loki
type: loki
uid: loki
url: http://loki-read.monitoring:3100
access: proxy
jsonData:
derivedFields:
- datasourceUid: tempo
matcherRegex: "traceID=(\\w+)"
name: TraceID
url: "$${__value.raw}"
eBPF vs 사이드카 vs 에이전트 비교
eBPF 기반 옵저버빌리티, 사이드카 프록시 기반 서비스 메시, 전통적인 에이전트 기반 모니터링의 세 가지 접근 방식을 운영 관점에서 비교한다.
| 비교 항목 | eBPF (Hubble + Beyla) | 사이드카 (Istio/Envoy) | 에이전트 (SDK 계측) |
|---|---|---|---|
| 코드 변경 필요 | 없음 | 없음 (자동 주입) | SDK 통합 필요 |
| Pod당 추가 컨테이너 | 없음 | 1개 (Envoy) | 없음 |
| 노드당 메모리 오버헤드 | ~300MB (Cilium+Beyla) | ~100MB x Pod 수 | ~50MB (에이전트) |
| P99 레이턴시 영향 | 0.1ms 미만 | 2~5ms | 0.5~1ms |
| L3/L4 네트워크 가시성 | 매우 상세 | 제한적 | 없음 |
| L7 HTTP/gRPC 가시성 | Beyla로 RED 메트릭 | 매우 상세 | SDK 의존 |
| 분산 트레이스 | Beyla 자동 (context propagation 제한) | 자동 (완전한 propagation) | 완전한 제어 |
| DNS 관측 | Hubble 기본 제공 | Envoy DNS 프록시 | 별도 구성 필요 |
| 커널 버전 의존성 | 5.8+ 권장 | 없음 | 없음 |
| 보안 권한 요구 | privileged 또는 CAP_BPF | NET_ADMIN | 일반 사용자 |
| 서비스 메시 기능 (mTLS, 트래픽 제어) | Cilium으로 부분 지원 | 완전 지원 | 없음 |
| 운영 복잡도 | 중간 | 높음 | 낮음 (SDK 관리 비용은 높음) |
| 다중 언어 지원 | 언어 무관 | 언어 무관 | 언어별 SDK 필요 |
선택 가이드
eBPF 기반 접근이 적합한 경우는 다음과 같다. 수백 개의 마이크로서비스에 일관된 모니터링을 적용해야 하지만 각 서비스에 SDK를 통합할 엔지니어링 자원이 부족한 경우. 사이드카 프록시의 리소스 오버헤드를 감당하기 어려운 노드 밀도가 높은 클러스터. 레거시 서비스를 포함하여 코드 변경 없이 모니터링을 확보해야 하는 마이그레이션 기간. 네트워크 레벨의 세밀한 가시성(DNS, TCP 연결 상태, 패킷 드롭)이 중요한 인프라 중심 운영 환경.
반면 사이드카 프록시가 더 적합한 경우도 있다. mTLS, 트래픽 분할, 서킷 브레이킹 등 서비스 메시의 트래픽 제어 기능이 필요한 경우. 완전한 분산 트레이스 context propagation이 필수적인 경우. 커널 5.8 미만의 노드를 운영해야 하는 환경. eBPF의 privileged 컨테이너 권한을 허용할 수 없는 보안 정책이 적용된 클러스터.
운영 시 주의사항
커널 버전과 배포판 호환성
eBPF 기반 도구 운영에서 가장 흔한 장애 원인은 커널 버전 불일치다. Cilium Hubble은 커널 4.19 이상에서 기본 기능이 동작하지만, L7 프로토콜 파싱과 고급 메트릭은 커널 5.4 이상이 필요하다. Grafana Beyla의 uprobes 기반 자동 계측은 커널 5.8 이상에서만 안정적으로 동작하며, 특히 BPF ring buffer를 활용하는 기능은 5.8이 최소 요구사항이다.
Amazon EKS의 경우 AL2023 AMI가 커널 6.1을 기본 제공하므로 문제가 없지만, AL2 AMI는 커널 5.10으로 대부분의 기능을 지원하되 일부 고급 기능에 제약이 있을 수 있다. GKE는 Container-Optimized OS(COS)가 커널 5.15 이상을 제공하며, Ubuntu 노드 이미지도 5.15 이상이다. AKS는 Ubuntu 22.04 기반 노드가 커널 5.15를 사용한다.
보안 고려사항
Beyla는 eBPF 프로그램을 커널에 로드하기 위해 CAP_SYS_ADMIN 또는 CAP_BPF + CAP_PERFMON 권한이 필요하다. 가장 간단한 방법은 privileged 컨테이너로 실행하는 것이지만, 프로덕션 환경에서는 최소 권한 원칙에 따라 필요한 capability만 부여하는 것을 권장한다.
# 최소 권한 보안 컨텍스트 (privileged 대신)
securityContext:
privileged: false
runAsUser: 0
capabilities:
add:
- BPF # eBPF 프로그램 로드
- PERFMON # perf 이벤트 접근
- NET_RAW # raw 소켓 접근 (네트워크 관측)
- SYS_PTRACE # 프로세스 추적 (uprobes)
- DAC_READ_SEARCH # 파일 시스템 탐색
drop:
- ALL
그러나 일부 Kubernetes 보안 정책(Pod Security Standards의 Restricted 프로파일)에서는 이러한 capability 추가 자체가 거부될 수 있다. 이 경우 Beyla DaemonSet에 대한 예외 정책을 별도로 구성해야 한다. 또한 eBPF 프로그램은 커널 공간에서 실행되므로, Beyla 이미지의 출처와 무결성을 반드시 검증해야 한다. 서명된 이미지를 사용하고, 이미지 풀 정책을 Always로 설정하여 변조된 이미지가 배포되지 않도록 해야 한다.
리소스 관리와 스케일링
대규모 클러스터(500+ 노드)에서 Hubble과 Beyla를 운영할 때는 리소스 사용량을 면밀히 모니터링해야 한다. Hubble Relay는 클러스터의 모든 노드에서 이벤트를 집계하므로, 노드 수가 증가할수록 Relay의 메모리와 CPU 사용량이 선형적으로 증가한다. 노드 100대당 Relay 인스턴스 1개를 기준으로, 500대 이상의 클러스터에서는 Relay를 5개 이상으로 수평 확장하고 로드밸런싱을 구성해야 한다.
Beyla는 노드당 계측 대상 프로세스 수에 따라 메모리 사용량이 변동한다. 프로세스당 약 2~5MB의 추가 메모리가 필요하므로, Pod 밀도가 높은 노드(50+ Pod)에서는 Beyla의 메모리 limit을 1Gi 이상으로 상향 조정해야 할 수 있다. 또한 discovery 설정에서 계측 대상을 특정 네임스페이스나 레이블로 제한하여 불필요한 리소스 소비를 방지하는 것이 좋다.
실패 사례와 복구 절차
사례 1: eBPF 프로그램 로드 실패
가장 흔한 실패 유형은 커널 버전 불일치로 인한 eBPF 프로그램 로드 실패다. 노드 업그레이드 과정에서 일부 노드만 새 커널로 전환되었을 때 발생하기 쉽다.
증상: Beyla Pod이 CrashLoopBackOff 상태에 빠지며, 로그에 failed to load eBPF program: invalid argument 또는 kernel doesn't support bpf_ringbuf 에러가 출력된다.
진단 절차: 먼저 해당 노드의 커널 버전을 확인한다. kubectl get node <node-name> -o jsonpath='{.status.nodeInfo.kernelVersion}'으로 커널 버전을 조회하고, Beyla가 요구하는 최소 버전(5.8+)과 비교한다. BTF 지원 여부도 확인해야 한다. 노드에 접속하여 /sys/kernel/btf/vmlinux 파일의 존재를 확인한다.
복구 방법: 커널 버전이 낮은 노드에는 nodeSelector 또는 nodeAffinity를 사용하여 Beyla DaemonSet이 스케줄되지 않도록 설정한다. 장기적으로는 해당 노드의 OS 이미지를 업그레이드한다.
사례 2: Hubble Relay 메모리 부족(OOMKilled)
Hubble Relay가 대량의 네트워크 흐름을 처리하면서 메모리 limit을 초과하여 OOMKilled되는 경우다.
증상: Hubble Relay Pod이 반복적으로 재시작되며, kubectl describe pod에서 OOMKilled 종료 사유가 확인된다. Hubble CLI와 UI에서 간헐적으로 연결이 끊어진다.
진단 절차: kubectl top pod -n kube-system -l k8s-app=hubble-relay로 현재 메모리 사용량을 확인한다. 또한 hubble_relay_received_flows_total 메트릭을 통해 Relay가 처리하는 이벤트의 초당 수량을 파악한다.
복구 방법: 단기적으로는 Relay의 메모리 limit을 증가시킨다(512Mi에서 1Gi 이상). 중기적으로는 Cilium의 bpf.monitorAggregation 설정을 medium 또는 high로 조정하여 이벤트 수를 줄인다. 장기적으로는 Relay 인스턴스를 수평 확장한다.
사례 3: Beyla 메트릭 누락
Beyla가 정상적으로 실행되고 있지만 특정 서비스의 메트릭이 수집되지 않는 경우다.
증상: Grafana 대시보드에서 특정 서비스의 RED 메트릭이 표시되지 않는다. Beyla 로그에 해당 서비스의 계측 메시지가 없다.
진단 절차: 해당 서비스의 바이너리 형식을 확인한다. Go로 작성된 서비스가 -ldflags="-s -w" 옵션으로 심볼을 제거(strip)하여 빌드되었다면, Beyla가 함수 심볼을 탐지하지 못할 수 있다. 또한 서비스가 비표준 포트를 사용하거나, 커스텀 HTTP 프레임워크를 사용하는 경우에도 자동 탐지가 실패할 수 있다.
복구 방법: Go 바이너리의 경우 -ldflags="-s -w" 대신 심볼을 유지하거나, Beyla 설정에서 BEYLA_OPEN_PORT 환경변수에 해당 서비스의 포트를 명시적으로 추가한다. 비표준 프레임워크의 경우 Beyla의 generic HTTP tracing 모드를 활성화하여 소켓 레벨에서 HTTP 패턴을 감지하도록 설정한다.
사례 4: Hubble과 Beyla 간 메트릭 불일치
Hubble의 네트워크 레벨 HTTP 메트릭과 Beyla의 애플리케이션 레벨 HTTP 메트릭의 수치가 불일치하는 경우다.
증상: Hubble이 보고하는 HTTP 요청 수가 Beyla가 보고하는 수보다 많거나 적다.
원인 분석: Hubble은 네트워크 패킷 레벨에서 HTTP를 파싱하므로 헬스체크, 쿠버네티스 프로브, 사이드카 간 통신 등 모든 HTTP 트래픽을 캡처한다. 반면 Beyla는 애플리케이션 프로세스의 함수 호출을 추적하므로 실제 애플리케이션이 처리한 요청만 계측한다. 이 차이는 정상적인 동작이며, 두 메트릭을 함께 분석하면 인프라 트래픽과 애플리케이션 트래픽을 구분하는 데 유용하다.
운영 체크리스트
프로덕션에 eBPF 기반 옵저버빌리티를 도입할 때 확인해야 할 항목 목록이다.
배포 전 검증:
- 모든 노드의 커널 버전이 5.8 이상인지 확인
- BTF(BPF Type Format) 지원이 활성화되어 있는지 확인 (
/sys/kernel/btf/vmlinux존재) - 노드의 보안 정책(Pod Security Standards)이 privileged 또는 CAP_BPF를 허용하는지 확인
- 관리형 Kubernetes(EKS, GKE, AKS)의 경우 CNI 플러그인 교체 가능 여부 확인
- Cilium이 기존 CNI와 충돌하지 않는지 검증 (kube-proxy 교체 모드 포함)
배포 후 검증:
- Cilium Agent가 모든 노드에서 Ready 상태인지 확인 (
cilium status) - Hubble Relay가 모든 노드의 Hubble Server와 연결되었는지 확인
- Beyla가 예상 서비스를 자동 탐지했는지 로그 확인
- Mimir/Prometheus에 Hubble 및 Beyla 메트릭이 정상 수집되는지 확인
- Tempo에 Beyla 트레이스가 정상 전송되는지 확인
모니터링 경보:
- Hubble Relay의 메모리 사용률이 80% 초과 시 경보
- Beyla DaemonSet의 Ready Pod 수가 노드 수와 일치하는지 확인
- eBPF 프로그램 로드 실패 이벤트 감지
- Hubble 메트릭 수집 지연(scrape duration) 모니터링
- Beyla의 OTLP export 실패율 모니터링
정기 점검:
- 월간: 커널 보안 패치 적용 후 eBPF 프로그램 호환성 검증
- 분기별: Cilium/Beyla 버전 업그레이드 검토 및 테스트
- 반기별: 커널 버전 업그레이드 계획 수립 (LTS 커널 추적)
참고자료
- Grafana Beyla - eBPF 기반 자동 계측 - Beyla의 공식 문서와 시작 가이드. 지원 언어, 메트릭 스펙, 설정 레퍼런스를 포함한다.
- Cilium Hubble - GitHub 리포지토리 - Hubble의 소스 코드, CLI 사용법, API 레퍼런스. 이슈 트래커에서 알려진 문제와 해결 방법을 확인할 수 있다.
- eBPF 기반 네트워크 옵저버빌리티 with Cilium Hubble (CloudRaft) - Cilium Hubble의 아키텍처와 실전 사용 사례를 다룬 기술 블로그.
- Amazon EKS에서 eBPF 기반 옵저버빌리티 구축 (AWS Blog) - EKS 환경에서 eBPF 도구를 배포하고 운영하는 공식 가이드.
- Grafana Tempo - 분산 트레이싱 백엔드 - Beyla의 트레이스를 저장하고 쿼리하는 Tempo의 공식 문서. TraceQL 문법과 아키텍처를 포함한다.
- Cilium 공식 문서 - Hubble 설정 레퍼런스 - Hubble의 전체 Helm values, 메트릭 설정, TLS 구성에 대한 공식 레퍼런스.
- eBPF.io - eBPF 기술 개요 - eBPF 기술의 근본 원리, 커널 버전별 기능 지원 현황, 생태계 도구 목록을 제공하는 커뮤니티 사이트.