- Authors
- Name
- はじめに:なぜeBPFベースのゼロインストルメンテーションか
- eBPF技術概要:カーネルレベルインストルメンテーションの原理
- Cilium Hubbleアーキテクチャ:ネットワークオブザーバビリティの中核
- Hubbleのインストールと設定:Helmチャートベースのデプロイ
- Grafana Beyla自動インストルメンテーション:ゼロコードAPM
- Beylaのデプロイと設定:Kubernetes DaemonSet構成
- Grafanaスタック統合:Tempo、Mimir、Loki
- eBPF vs サイドカー vs エージェント比較
- 運用上の考慮事項
- 障害事例と復旧手順
- 運用チェックリスト
- 参考文献
- クイズ

はじめに:なぜeBPFベースのゼロインストルメンテーションか
Kubernetes環境でオブザーバビリティを実現する従来のアプローチは2つあります。第一に、アプリケーションコードにSDKを組み込んでメトリクスやトレースを生成する明示的インストルメンテーション。第二に、各PodにEnvoyベースのサイドカープロキシを注入してL7トラフィックを観測するサービスメッシュアプローチです。両方のアプローチは本番環境で実績がありますが、根本的な限界があります。
SDKベースのインストルメンテーションは、すべてのサービスにライブラリを追加しインストルメンテーションコードを書く必要があります。数百のマイクロサービスを持つ大規模クラスターでこれを一貫して維持するには膨大なエンジニアリングコストがかかります。サイドカープロキシアプローチはコード変更なしでL7の可視性を得られますが、Podごとに追加コンテナをデプロイするためノードあたりのメモリ使用量が数百MB増加し、ネットワークホップの追加によりP99レイテンシが通常2~5ms上昇します。
eBPF(extended Berkeley Packet Filter)は両方のアプローチの限界を同時に解決します。Linuxカーネル内でサンドボックス化されたプログラムを実行することで、アプリケーションコードの変更なしにネットワークフロー、HTTP/gRPCリクエスト、DNSクエリ、TCP接続状態をカーネルレベルで観測できます。サイドカーコンテナが不要なためリソースオーバーヘッドが極めて低く、データをカーネルから直接収集するためアプリケーション性能への影響もほとんどありません。
本記事ではeBPFベースオブザーバビリティの中核ツールであるCilium HubbleとGrafana Beylaに焦点を当て、Kubernetesクラスターにおけるコード変更ゼロでのネットワーク可視性とアプリケーションパフォーマンスモニタリング(APM)構築の詳細な実践運用ガイドを提供します。
eBPF技術概要:カーネルレベルインストルメンテーションの原理
eBPFプログラムの実行フロー
eBPFはLinuxカーネル4.xから本格的に導入された技術で、カーネルモジュールを直接変更することなくカーネル空間でプログラムを実行できます。eBPFプログラムはユーザー空間で記述され、カーネルのVerifierを通過した後、JIT(Just-In-Time)コンパイラによりネイティブコードに変換されます。Verifierは無限ループの防止、境界外メモリアクセスのブロック、許可されたヘルパー関数呼び出しのみの許可によってカーネルの安定性を確保します。
eBPFプログラムをアタッチできるフックポイントは非常に多様です。ネットワーキングにはTC(Traffic Control)、XDP(eXpress Data Path)、ソケットレベルのフックがあります。システム全体の観測にはkprobes(カーネル関数エントリ/エグジット)、tracepoints(事前定義された観測ポイント)、uprobes(ユーザー空間関数トレーシング)が使用されます。Cilium Hubbleは主にTCとソケットレベルのフックを使用してネットワークフローを観測し、Grafana Beylaはuprobsとkprobesを活用してHTTP/gRPCリクエストからRED(Rate、Error、Duration)メトリクスを自動抽出します。
カーネルバージョン別のeBPF機能サポート
eBPFの機能サポートはカーネルバージョンによって異なります。Cilium Hubbleの完全な機能にはカーネル4.19以上が必要で、Grafana Beylaの自動インストルメンテーション機能はカーネル5.8以上で安定動作します。本番環境ではカーネル5.15 LTS以上を推奨します。具体的には、BTF(BPF Type Format)サポートが有効なカーネルでは、eBPFプログラムをCO-RE(Compile Once, Run Everywhere)モードでデプロイでき、多様なノード環境間の互換性が確保されます。
# 現在のノードのカーネルバージョンと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のオブザーバビリティレイヤーで、3つのコアコンポーネントで構成されています。第一に、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アイデンティティ(名前空間、Pod、サービスラベル)に自動マッピングされ、サービス間の通信パターンを明確に把握できます。
サービスマップとフローの可視性
Hubbleの最も強力な機能の一つは自動生成されるサービスマップです。従来のサービスメッシュでは、EnvoyサイドカーがPodのインバウンド/アウトバウンドトラフィックをプロキシしてサービストポロジーを構築しますが、HubbleはカーネルeBPFプログラムが収集したネットワークフローデータからサービス依存関係グラフを自動構築します。これにより、サービスメッシュをデプロイすることなく、どのサービスがどのサービスと通信し、その通信成功率やレイテンシがどの程度かをリアルタイムで確認できます。
Hubbleのインストールと設定:Helmチャートベースのデプロイ
Cilium + Hubble統合インストール
HubbleはCilium CNIの一部としてデプロイされます。既にCiliumを使用している場合はHubble機能を有効にするだけです。新規クラスターの場合はCiliumインストール時にHubbleを有効にします。以下は本番環境に適したHelmバリュー設定です。
# cilium-hubble-values.yaml
# Cilium + Hubble本番デプロイ用Helmバリュー
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 Indicators)を定義できます。以下は本番環境でよく使用される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クエリ失敗率TOP10ワークロード
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としてデプロイされ、uprobsを通じて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はプロセスを自動検出してインストルメンテーション対象を特定します。検出は2つの方法で行われます。第一に、実行バイナリのシンボルテーブルを分析し、HTTPサーバーやgRPCサーバー関連の関数が含まれているかを確認します。Goバイナリの場合、net/http.(*Server).Serveやgoogle.golang.org/grpc.(*Server).Serveなどのシンボルを検出します。第二に、リスニングソケットを監視し、特定ポートでTCP接続を受け付けているプロセスを特定します。これら2つの方法を組み合わせることで、どの言語で書かれたサービスでも自動的にインストルメンテーション対象に含められます。
Beylaのデプロイと設定:Kubernetes DaemonSet構成
DaemonSetベースのデプロイ
KubernetesにBeylaをデプロイする最も一般的な方法は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 # すべてのノードにデプロイ(masterを含む)
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フローログ)を単一ビューで相関分析できます。トレースからメトリクス、メトリクスからログへと自由にナビゲートできることで、インシデント時の根本原因分析(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自動(コンテキスト伝播に制限) | 自動(完全な伝播) | 完全制御 |
| DNS観測 | Hubble組み込み | Envoy DNSプロキシ | 別途設定が必要 |
| カーネルバージョン依存性 | 5.8以上推奨 | なし | なし |
| セキュリティ権限要件 | privilegedまたはCAP_BPF | NET_ADMIN | 一般ユーザー |
| サービスメッシュ機能(mTLS、トラフィック制御) | Cilium経由で部分サポート | 完全サポート | なし |
| 運用の複雑さ | 中 | 高 | 低(ただしSDK管理コストが高い) |
| 多言語サポート | 言語非依存 | 言語非依存 | 言語別SDKが必要 |
選択ガイド
eBPFベースのアプローチは以下の場合に適しています。数百のマイクロサービスに一貫した監視を適用する必要があるが、各サービスにSDKを統合するエンジニアリングリソースが不足している場合。サイドカープロキシのリソースオーバーヘッドを吸収しにくい高密度ノードクラスター。レガシーサービスを含めてコード変更なしで監視を実現する必要がある移行期。DNS、TCP接続状態、パケットドロップなどきめ細かなネットワークレベルの可視性が重要なインフラ中心の運用。
一方、サイドカープロキシの方が適している場合もあります。mTLS、トラフィック分割、サーキットブレーカーなどサービスメッシュのトラフィック制御機能が必要な場合。完全な分散トレースのコンテキスト伝播が不可欠な場合。カーネル5.8未満のノードで運用する必要がある環境。eBPFのprivilegedコンテナ権限を許可できないセキュリティポリシーを持つクラスター。
運用上の考慮事項
カーネルバージョンとディストリビューションの互換性
eBPFベースツールの運用で最も一般的な障害原因はカーネルバージョンの不一致です。Cilium Hubbleの基本機能はカーネル4.19以上で動作しますが、L7プロトコル解析と高度なメトリクスにはカーネル5.4以上が必要です。Grafana Beylaのuprobsベースの自動インストルメンテーションはカーネル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コンテナとして実行することですが、本番環境では最小権限の原則に従い必要なcapabilitiesのみを付与することを推奨します。
# 最小権限セキュリティコンテキスト(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プロファイル)ではこれらのcapabilitiesの追加自体が拒否される場合があります。その場合、Beyla DaemonSet用の個別の例外ポリシーを設定する必要があります。また、eBPFプログラムはカーネル空間で実行されるため、Beylaイメージのソースと完全性を検証する必要があります。署名付きイメージを使用し、イメージプルポリシーをAlwaysに設定して改ざんされたイメージのデプロイを防止してください。
リソース管理とスケーリング
大規模クラスター(500ノード以上)でHubbleとBeylaを運用する場合、リソース使用量を注意深く監視する必要があります。Hubble Relayはクラスター内の全ノードからイベントを集約するため、メモリとCPU使用量はノード数に比例して増加します。100ノードあたりRelayインスタンス1つをベースラインとし、500ノード以上のクラスターではRelayを5インスタンス以上に水平スケールしてロードバランシングを設定してください。
BeylaのメモリUsageはノードあたりのインストルメンテーション対象プロセス数に応じて変動します。プロセスあたり約2~5MBの追加メモリが必要なため、Pod密度の高いノード(50+ Pod)ではBeylaのメモリ上限を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がメモリ制限を超過した場合に発生します。
症状: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のメモリ上限を引き上げます(512Miから1Gi以上)。中期的にはCiliumのbpf.monitorAggregation設定をmediumまたはhighに調整してイベント量を削減します。長期的にはRelayインスタンスを水平スケールします。
事例3:Beylaメトリクスの欠落
Beylaは正常に動作しているが、特定サービスのメトリクスが収集されない。
症状:特定サービスのREDメトリクスがGrafanaダッシュボードに表示されない。Beylaログに該当サービスのinstrumentationメッセージが出現しない。
診断手順:サービスのバイナリ形式を確認します。Goサービスが-ldflags="-s -w"オプションでシンボルをストリップしてビルドされている場合、Beylaが関数シンボルを検出できない可能性があります。非標準ポートやカスタムHTTPフレームワークを使用している場合も自動検出に失敗する可能性があります。
復旧方法:Goバイナリの場合、-ldflags="-s -w"を使用せずシンボルを保持するか、Beylaの設定でBEYLA_OPEN_PORT環境変数に該当サービスのポートを明示的に追加します。非標準フレームワークの場合、BeylaのジェネリックHTTPトレーシングモードを有効にしてソケットレベルでHTTPパターンを検出させます。
事例4:HubbleとBeylaのメトリクス不一致
HubbleのネットワークレベルHTTPメトリクスとBeylaのアプリケーションレベルHTTPメトリクスで異なる数値が表示される事例。
症状:Hubbleが報告するHTTPリクエスト数がBeylaの報告値より多いまたは少ない。
根本原因分析:HubbleはネットワークパケットレベルでHTTPを解析するため、ヘルスチェック、Kubernetesプローブ、サイドカー間通信を含むすべてのHTTPトラフィックをキャプチャします。一方、BeylaはアプリケーションプロセスのFunction Callをトレースするため、アプリケーションが実際に処理したリクエストのみをインストルメンテーションします。この不一致は正常な動作であり、両方のメトリクスを合わせて分析することでインフラトラフィックとアプリケーショントラフィックを区別するのに有用です。
運用チェックリスト
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が期待されるサービスを自動検出していることを確認
- HubbleとBeylaのメトリクスがMimir/Prometheusに正常に収集されていることを確認
- BeylaのトレースがTempoに正常に送信されていることを確認
監視アラート:
- Hubble Relayのメモリ使用率が80%を超えた場合にアラート
- Beyla DaemonSetのReady Pod数がノード数と一致していることを確認
- eBPFプログラムのロード失敗イベントを検出
- Hubbleメトリクス収集遅延(scrape duration)を監視
- BeylaのOTLPエクスポート失敗率を監視
定期メンテナンス:
- 月次:カーネルセキュリティパッチ後のeBPFプログラム互換性を確認
- 四半期:Cilium/Beylaバージョンアップグレードのレビューとテスト
- 半期:カーネルバージョンアップグレードの計画(LTSカーネルの追跡)
参考文献
- Grafana Beyla - eBPFベース自動インストルメンテーション - Beylaの公式ドキュメントとスタートガイド。サポート言語、メトリクス仕様、設定リファレンスを含む。
- Cilium Hubble - GitHubリポジトリ - Hubbleのソースコード、CLI使用法、APIリファレンス。イシュートラッカーで既知の問題と解決策を確認可能。
- Cilium Hubbleを使用したeBPFベースのネットワークオブザーバビリティ(CloudRaft) - Cilium Hubbleのアーキテクチャと実践的ユースケースを解説する技術ブログ。
- Amazon EKSにおけるeBPFによるKubernetesオブザーバビリティの強化(AWSブログ) - EKS環境でのeBPFツールのデプロイと運用に関する公式ガイド。
- Grafana Tempo - 分散トレーシングバックエンド - Beylaのトレースを保存・クエリするTempoの公式ドキュメント。TraceQL構文とアーキテクチャを含む。
- Cilium公式ドキュメント - Hubble設定リファレンス - HubbleのHelmバリュー全体、メトリクス設定、TLS設定の公式リファレンス。
- eBPF.io - eBPF技術概要 - eBPF技術の基本原理、カーネルバージョン別の機能サポート状況、エコシステムツール一覧を提供するコミュニティサイト。
クイズ
Q1: 「eBPFベースのゼロインストルメンテーションKubernetesオブザーバビリティ:Cilium HubbleとGrafana Beyla実践ガイド」の主なトピックは何ですか?
eBPFベースのCilium Hubbleによるネットワークオブザーバビリティと、Grafana Beylaの自動インストルメンテーションを活用した、コード変更不要のKubernetes監視アーキテクチャの実践運用ガイドです。
Q2: Cilium Hubbleアーキテクチャ:ネットワークオブザーバビリティの中核について説明してください。
Hubbleの内部構造 Cilium HubbleはCilium CNIのオブザーバビリティレイヤーで、3つのコアコンポーネントで構成されています。第一に、Hubble Serverは各ノード上でCilium Agentに組み込まれて動作し、eBPFデータプレーンからネットワークイベントを収集します。第二に、Hubble Relayはクラスター全体のHubble Serverからイベントを集約し、単一のAPIエンドポイントで提供します。第三に、Hubble UIはサービスマップとネットワークフローを視覚的に表現します。
Q3: Hubbleのインストールと設定:Helmチャートベースのデプロイの主な手順は何ですか?
Cilium + Hubble統合インストール HubbleはCilium CNIの一部としてデプロイされます。既にCiliumを使用している場合はHubble機能を有効にするだけです。新規クラスターの場合はCiliumインストール時にHubbleを有効にします。以下は本番環境に適したHelmバリュー設定です。 HubbleメトリクスベースのPromQLクエリ Hubbleが生成したメトリクスがPrometheusに収集されると、さまざまなネットワークレベルのSLI(Service Level Indicators)を定義できます。
Q4: Grafana Beyla自動インストルメンテーション:ゼロコードAPMの主な特徴は何ですか?
Beylaの動作原理 Grafana BeylaはeBPFを使用した自動インストルメンテーションツールで、アプリケーションコードの変更なしにHTTP/HTTPS、gRPC、SQLリクエストのRED(Rate、Error、Duration)メトリクスと分散トレースを自動生成します。Beylaは各ノードにDaemonSetとしてデプロイされ、uprobsを通じてGo、Java、Python、Node.js、Rust、.NETなど各種ランタイムのHTTPハンドラーとgRPCサーバー関数を自動検出してフックします。
Q5: Beylaのデプロイと設定:Kubernetes DaemonSet構成の主な手順は何ですか?
DaemonSetベースのデプロイ KubernetesにBeylaをデプロイする最も一般的な方法はDaemonSetです。各ノードでホストPID名前空間にアクセスし、ノード上のすべてのプロセスを検出してインストルメンテーションします。