Go K8s eBPFネットワークモニター:Ciliumオブザーバビリティの5つのコアパターン
問題提起:K8sネットワークモニタリングの4つのペインポイント
午前2時、本番サービスが全面的にタイムアウトしている。モニタリングダッシュボードを開くと——Serviceレベルのレイテンシカーブが真っ赤だが、どのPodが原因なのか?DNS解決が遅いのか、ネットワークポリシーがトラフィックを誤ってドロップしたのか?従来のモニタリングツールは「問題がある」ことは教えてくれるが、「どこに問題があるか」を特定できない。
K8sネットワークモニタリングの4つのコアペインポイント:
- モニタリング粒度が粗い:従来のモニタリングはServiceレベルにとどまり、Pod/Container次元へのドリルダウンができない。マイクロサービスのコールチェーンはブラックボックスのまま
- ServiceレベルでPodを特定できない:1つのServiceの背後には数十のPodがあるかもしれない——どのPodのレイテンシが異常なのか?従来のツールでは判断できない
- DNS解決レイテンシの追跡が困難:CoreDNS解決タイムアウトはK8sネットワーク問題の最大の原因だが、従来のツールでは個々のDNSクエリの完全なライフサイクルを追跡できない
- ネットワークポリシーの効果が見えない:CiliumNetworkPolicyが有効になった後、どの接続を拒否したのか?ポリシーが厳しすぎないか?可視化手段がなく検証できない
eBPFがカーネル層でネットワークイベントを収集する天然の利点と、CiliumおよびHubbleの組み合わせにより、K8sネットワークは「ブラックボックス」から「ガラスボックス」へと変わる。
コア概念クイックリファレンス
| 概念 | 説明 | コアバリュー |
|---|---|---|
| eBPF | Extended Berkeley Packet Filter、Linuxカーネルプログラマブルサンドボックス | カーネル修正なしでカーネル層のネットワークイベントを収集 |
| Cilium | eBPFベースのK8s CNIプラグイン | kube-proxyを代替、高性能データプレーンとオブザーバビリティ基盤を提供 |
| Hubble | Ciliumのネットワークオブザーバビリティプラットフォーム | サービス依存、トラフィックトポロジ、DNS解決のリアルタイム可視化 |
| ネットワークオブザーバビリティ | ネットワークトラフィックのフルリンク認識能力 | L3からL7までの完全なトラフィック可視性 |
| トラフィックトポロジ | サービス間呼び出し関係の可視化グラフ | 異常なコールチェーンとボトルネックノードをワンクリックで発見 |
| DNSモニタリング | DNSクエリ/レスポンスの追跡と測定 | ドメイン解決レイテンシとNXDOMAINエラーを特定 |
| L7/L4モニタリング | アプリケーション/トランスポート層プロトコル解析 | HTTPメソッド/パス/gRPCメソッドの細粒度オブザーバビリティ |
| ネットワークポリシー監査 | ポリシーによって拒否/許可された接続の記録 | ポリシー効果の検証、ポリシーチューニングの支援 |
問題分析:eBPFネットワークモニタリングの5つの課題
1. eBPFプログラム開発の複雑さ:eBPF Cプログラムは手動メモリ管理、境界チェック、ベリファイアルールへの準拠が必要。開発のハードルが高く、デバッグが困難。ポインタの範囲外アクセス1つでプログラムがロード不能になる。
2. カーネルバージョンの互換性:異なるeBPF機能は異なるカーネルバージョンに依存——ringbufは5.8+、bpf_skb_ecn_set_ceは5.1+が必要。マルチクラスタ環境でのカーネルバージョン不一致が機能の断片化を引き起こす。
3. モニタリングデータ量の爆発:大規模クラスタでは毎秒数十万のネットワークイベントが生成される。Hubble Flowログのストレージコストが線形増加。重要なイベントを失うことなくデータ量を制御するには?
4. パフォーマンスオーバーヘッドの制御:eBPFプログラムはカーネルのホットパスで動作し、すべてのパケットがeBPF処理を通過する。不適切な実装はCPUオーバーヘッドの急増を招き、ビジネススループットに影響する。
5. マルチクラスタオブザーバビリティ:本番環境は通常マルチクラスタアーキテクチャ。Hubbleはデフォルトでシングルクラスタビュー。クラスタ間トラフィックトポロジにはClusterMesh + Hubble Relayカスケーディングが必要で、設定が複雑。
パターン1:CiliumインストールとHubbleオブザーバビリティ設定
HubbleはCilium内蔵のネットワークオブザーバビリティコンポーネントで、SidecarなしでL3-L7フルリンクトラフィックを収集する。
helm repo add cilium https://helm.cilium.io/
helm repo update
helm install cilium cilium/cilium \
--namespace kube-system \
--set kubeProxyReplacement=strict \
--set hubble.enabled=true \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true \
--set hubble.metrics.enabled="{dns,drop,tcp,flow,http,port-distribution,icmp,httpV2}" \
--set operator.replicas=1 \
--set ipv4NativeRoutingCIDR="10.0.0.0/8"
cilium status
cilium connectivity test
Hubble UIを有効にしてトラフィックトポロジにアクセス:
kubectl port-forward -n kube-system svc/hubble-ui 12000:80
hubble observe --since 5m --namespace production
hubble observe --pod-name payment-service-7d9f8b6c4-x2k1j
hubble observe --protocol http --type trace --since 1m
Hubble FlowのPrometheusへのエクスポート:
apiVersion: v1
kind: ConfigMap
metadata:
name: hubble-metrics-config
namespace: kube-system
data:
hubble-metrics: |
dns:
enabled: true
drop:
enabled: true
tcp:
enabled: true
flow:
enabled: true
http:
enabled: true
httpV2:
enabled: true
labels:
source_pod: true
destination_pod: true
port-distribution:
enabled: true
パターン2:Go eBPFプログラム開発とネットワークイベント収集
Hubbleの内蔵メトリクスでカスタマイズ要件を満たせない場合、cilium/ebpfライブラリでカスタムネットワークイベントコレクタを開発する。
eBPF Cプログラム net_monitor.c:
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <bpf/bpf_helpers.h>
struct net_event {
__u32 src_ip;
__u32 dst_ip;
__u16 src_port;
__u16 dst_port;
__u8 protocol;
__u8 direction;
__u64 timestamp_ns;
__u32 pkt_len;
};
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 1 << 24);
} net_events SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1024);
__type(key, __u32);
__type(value, __u8);
} monitored_ns SEC(".maps");
SEC("tc")
int tc_monitor(struct __sk_buff *skb) {
void *data_end = (void *)(long)skb->data_end;
void *data = (void *)(long)skb->data;
struct ethhdr *eth = data;
if ((void *)(eth + 1) > data_end)
return TC_ACT_OK;
if (eth->h_proto != __builtin_bswap16(ETH_P_IP))
return TC_ACT_OK;
struct iphdr *ip = (void *)(eth + 1);
if ((void *)(ip + 1) > data_end)
return TC_ACT_OK;
__u32 dst_ip = ip->daddr;
__u8 *monitored = bpf_map_lookup_elem(&monitored_ns, &dst_ip);
if (!monitored)
return TC_ACT_OK;
struct net_event *e = bpf_ringbuf_reserve(&net_events, sizeof(*e), 0);
if (!e)
return TC_ACT_OK;
e->src_ip = ip->saddr;
e->dst_ip = dst_ip;
e->protocol = ip->protocol;
e->timestamp_ns = bpf_ktime_get_ns();
e->pkt_len = skb->len;
e->direction = 0;
if (ip->protocol == IPPROTO_TCP) {
struct tcphdr *tcp = (void *)(ip + 1);
if ((void *)(tcp + 1) <= data_end) {
e->src_port = __builtin_bswap16(tcp->source);
e->dst_port = __builtin_bswap16(tcp->dest);
}
} else if (ip->protocol == IPPROTO_UDP) {
struct udphdr *udp = (void *)(ip + 1);
if ((void *)(udp + 1) <= data_end) {
e->src_port = __builtin_bswap16(udp->source);
e->dst_port = __builtin_bswap16(udp->dest);
}
}
bpf_ringbuf_submit(e, 0);
return TC_ACT_OK;
}
char _license[] SEC("license") = "GPL";
Goユーザー空間コレクタ:
package main
import (
"bytes"
"encoding/binary"
"fmt"
"log"
"net"
"os"
"os/signal"
"syscall"
"time"
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/link"
"github.com/cilium/ebpf/ringbuf"
)
type netEvent struct {
SrcIP uint32
DstIP uint32
SrcPort uint16
DstPort uint16
Protocol uint8
Direction uint8
TimestampNs uint64
PktLen uint32
}
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -type net_event bpf ./net_monitor.c
func main() {
spec, err := ebpf.LoadCollectionSpec("bpf.o")
if err != nil {
log.Fatalf("load spec: %v", err)
}
coll, err := ebpf.NewCollection(spec)
if err != nil {
log.Fatalf("new collection: %v", err)
}
defer coll.Close()
iface, _ := net.InterfaceByName("eth0")
l, err := link.AttachTC(link.TCAttach{
Program: coll.Programs["tc_monitor"],
Interface: iface.Index,
})
if err != nil {
log.Fatalf("attach tc: %v", err)
}
defer l.Close()
rd, err := ringbuf.NewReader(coll.Maps["net_events"])
if err != nil {
log.Fatalf("ringbuf reader: %v", err)
}
defer rd.Close()
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
go func() {
for {
record, err := rd.Read()
if err != nil {
log.Printf("read error: %v", err)
continue
}
var e netEvent
if err := binary.Read(bytes.NewReader(record.RawSample), binary.LittleEndian, &e); err != nil {
continue
}
srcIP := intToIP(e.SrcIP)
dstIP := intToIP(e.DstIP)
proto := protoName(e.Protocol)
ts := time.Unix(0, int64(e.TimestampNs))
fmt.Printf("[%s] %s:%d -> %s:%d proto=%s len=%d\n",
ts.Format("15:04:05.000"), srcIP, e.SrcPort, dstIP, e.DstPort, proto, e.PktLen)
}
}()
<-sig
fmt.Println("shutting down...")
}
func intToIP(n uint32) net.IP {
ip := make(net.IP, 4)
binary.LittleEndian.PutUint32(ip, n)
return ip
}
func protoName(p uint8) string {
switch p {
case 6:
return "TCP"
case 17:
return "UDP"
case 1:
return "ICMP"
default:
return fmt.Sprintf("%d", p)
}
}
パターン3:DNS解決モニタリングとレイテンシ追跡
DNS解決レイテンシはK8sネットワーク問題の見えない殺人者だ。Hubbleの内蔵DNSモニタリングは、すべてのDNSクエリの完全なライフサイクルを追跡する。
hubble observe --protocol dns --since 5m
hubble observe --protocol dns --dns-response-code NXDomain
hubble observe --protocol dns --namespace kube-system --label k8s-app=kube-dns
Hubble gRPC APIでDNSメトリクスを収集するGoプログラム:
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/cilium/hubble/api/v1/observer"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
type dnsMetric struct {
queryName string
queryType string
latencyMs float64
responseCode string
sourcePod string
}
func monitorDNS(hubbleAddr string) error {
conn, err := grpc.NewClient(hubbleAddr,
grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return fmt.Errorf("connect hubble: %w", err)
}
defer conn.Close()
client := observer.NewObserverClient(conn)
stream, err := client.GetFlows(context.Background(),
&observer.GetFlowsRequest{
Whitelist: []*observer.FlowFilter{
{Protocol: []string{"dns"}},
},
})
if err != nil {
return fmt.Errorf("get flows: %w", err)
}
for {
resp, err := stream.Recv()
if err != nil {
return fmt.Errorf("recv: %w", err)
}
flow := resp.GetFlow()
if flow == nil || flow.GetDns() == nil {
continue
}
dns := flow.GetDns()
latency := float64(flow.GetTime().AsTime().Sub(
flow.GetTime().AsTime())) / float64(time.Millisecond)
m := dnsMetric{
queryName: dns.GetQuery(),
queryType: dns.GetQtypes()[0],
latencyMs: latency,
responseCode: dns.GetRcode(),
sourcePod: flow.GetSource().GetPodName(),
}
if m.latencyMs > 100 {
log.Printf("[DNS SLOW] pod=%s query=%s type=%s latency=%.1fms rcode=%s",
m.sourcePod, m.queryName, m.queryType, m.latencyMs, m.responseCode)
}
}
}
CoreDNSレイテンシPrometheusアラートルール:
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: dns-latency-alerts
namespace: kube-system
spec:
groups:
- name: dns.rules
rules:
- alert: DNSResolutionSlow
expr: histogram_quantile(0.99, sum(rate(hubble_dns_response_latency_seconds_bucket[5m])) by (le, source_pod)) > 0.1
for: 3m
labels:
severity: warning
annotations:
summary: "Pod {{ $labels.source_pod }} のDNS解決が遅い"
description: "DNS P99レイテンシが100msを超過"
- alert: DNSNXDomainSpike
expr: sum(rate(hubble_dns_response_total{rcode="NXDomain"}[5m])) / sum(rate(hubble_dns_response_total[5m])) > 0.05
for: 5m
labels:
severity: warning
annotations:
summary: "NXDomain応答率が5%を超過"
パターン4:L7トラフィックモニタリングとHTTP/gRPCオブザーバビリティ
HubbleはeBPFを通じてアプリケーション層プロトコルを解析し、SidecarなしでHTTPメソッド、パス、ステータスコード、gRPCメソッドを取得する。
hubble observe --protocol http --since 5m
hubble observe --protocol http --http-status 5xx
hubble observe --protocol grpc --since 5m
hubble observe --protocol http --namespace production --label app=api-gateway
HTTPトラフィックメトリクスを構造化ログで出力するGoプログラム:
package main
import (
"context"
"encoding/json"
"log"
"os"
"github.com/cilium/hubble/api/v1/observer"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
type httpFlowRecord struct {
Timestamp string `json:"timestamp"`
SourcePod string `json:"sourcePod"`
DestPod string `json:"destPod"`
Method string `json:"method"`
Path string `json:"path"`
StatusCode uint32 `json:"statusCode"`
LatencyNs uint64 `json:"latencyNs"`
Namespace string `json:"namespace"`
}
func monitorHTTP(hubbleAddr string) error {
conn, err := grpc.NewClient(hubbleAddr,
grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return err
}
defer conn.Close()
client := observer.NewObserverClient(conn)
stream, err := client.GetFlows(context.Background(),
&observer.GetFlowsRequest{
Whitelist: []*observer.FlowFilter{
{Protocol: []string{"http"}},
},
})
if err != nil {
return err
}
encoder := json.NewEncoder(os.Stdout)
for {
resp, err := stream.Recv()
if err != nil {
return err
}
flow := resp.GetFlow()
if flow == nil || flow.GetL7() == nil {
continue
}
l7 := flow.GetL7()
http := l7.GetHttp()
if http == nil {
continue
}
record := httpFlowRecord{
Timestamp: flow.GetTime().AsTime().Format("2006-01-02T15:04:05.000Z07:00"),
SourcePod: flow.GetSource().GetPodName(),
DestPod: flow.GetDestination().GetPodName(),
Method: http.GetMethod(),
Path: http.GetUrl(),
StatusCode: http.GetStatusCode(),
LatencyNs: l7.GetLatencyNs(),
Namespace: flow.GetSource().GetNamespace(),
}
if record.StatusCode >= 400 {
encoder.Encode(record)
}
}
}
gRPCメソッドレベルモニタリングのHubbleメトリクス設定:
apiVersion: v1
kind: ConfigMap
metadata:
name: hubble-l7-config
namespace: kube-system
data:
hubble-metrics: |
httpV2:
enabled: true
labels:
source_pod: true
destination_pod: true
source_namespace: true
destination_namespace: true
dns:
enabled: true
drop:
enabled: true
flow:
enabled: true
パターン5:ネットワークポリシー監査と可視化
ネットワークポリシー適用後のトラフィック監査は、K8sネットワークセキュリティの重要な要素だ。Hubbleはポリシーによって拒否された接続を記録し、ポリシー効果の検証を支援する。
hubble observe --type trace --verdict DROPPED --since 10m
hubble observe --type trace --verdict DROPPED --namespace production
hubble observe --type trace --drop-reason POLICY_DENIED
ポリシー監査イベントを収集するGoプログラム:
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/cilium/hubble/api/v1/observer"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
type policyAuditEvent struct {
timestamp time.Time
sourcePod string
destPod string
destPort uint32
protocol string
policyName string
action string
namespace string
}
func auditNetworkPolicy(hubbleAddr string) error {
conn, err := grpc.NewClient(hubbleAddr,
grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return err
}
defer conn.Close()
client := observer.NewObserverClient(conn)
stream, err := client.GetFlows(context.Background(),
&observer.GetFlowsRequest{
Whitelist: []*observer.FlowFilter{
{Verdict: []observer.Verdict{observer.Verdict_DROPPED}},
},
})
if err != nil {
return err
}
for {
resp, err := stream.Recv()
if err != nil {
return err
}
flow := resp.GetFlow()
if flow == nil {
continue
}
evt := policyAuditEvent{
timestamp: flow.GetTime().AsTime(),
sourcePod: flow.GetSource().GetPodName(),
destPod: flow.GetDestination().GetPodName(),
destPort: flow.GetDestination().GetPort(),
protocol: flow.GetType().String(),
action: flow.GetVerdict().String(),
namespace: flow.GetSource().GetNamespace(),
}
if flow.GetDropReason() != observer.DropReason_DROP_REASON_UNKNOWN {
evt.policyName = flow.GetDropReason().String()
}
log.Printf("[AUDIT] %s %s -> %s:%d proto=%s action=%s reason=%s ns=%s",
evt.timestamp.Format("15:04:05"),
evt.sourcePod, evt.destPod, evt.destPort,
evt.protocol, evt.action, evt.policyName, evt.namespace)
}
}
ポリシー監査Grafanaダッシュボードの主要PromQL:
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: policy-audit-alerts
namespace: kube-system
spec:
groups:
- name: policy.audit
rules:
- alert: ExcessivePolicyDrops
expr: sum(rate(hubble_drop_total{reason="policy_denied"}[5m])) by (source_pod, destination_pod) > 10
for: 2m
labels:
severity: warning
annotations:
summary: "{{ $labels.source_pod }} から {{ $labels.destination_pod }} への過度なポリシードロップ"
- alert: LegitimateTrafficDropped
expr: sum(rate(hubble_drop_total{reason="policy_denied"}[5m])) by (namespace) / sum(rate(hubble_flow_total[5m])) by (namespace) > 0.01
for: 5m
labels:
severity: critical
annotations:
summary: "ネームスペース {{ $labels.namespace }} で1%以上のトラフィックがポリシーによりドロップ"
落とし穴ガイド:5つのよくある罠
1. ❌ Hubbleはデフォルトで有効にならない → ✅ インストール時にhubble.enabled=trueとhubble.relay.enabled=trueを明示的に設定する必要がある。そうしないとhubble observeコマンドとUIが動作しない。
2. ❌ すべてのネームスペースのトラフィックをモニタリング → ✅ 大規模クラスタでのフルFlowログ収集はストレージ爆発を引き起こす。--namespaceと--labelフィルタで重要なネームスペースのみモニタリングすべき。
3. ❌ eBPFプログラムの命令数制限を無視 → ✅ カーネルベリファイアは単一eBPFプログラムを100万命令(5.2+)に制限している。複雑なロジックはbpf_tail_callで分割すべき。
4. ❌ RingBuffer読み取りがメインゴルーチンをブロック → ✅ RingBuffer.Read()はブロッキングコール——独立したゴルーチンで実行する必要がある。そうしないとプログラムの終了を妨げる。
5. ❌ Hubble Relay接続タイムアウト時のリトライなし → ✅ Hubble Relayの起動はCilium Agentの準備完了に依存する。Pod起動順序による接続失敗を避けるため、指数バックオフリトライを実装すべき。
トラブルシューティング:10のよくあるエラー
| エラーメッセージ | 原因 | 解決策 |
|---|---|---|
hubble observe: unable to connect to Hubble Relay |
Relayが有効でない、またはServiceが準備できていない | hubble.relay.enabled=trueを確認、kubectl get pods -n kube-system -l k8s-app=hubble-relayをチェック |
ringbuf: failed to read: ring buffer not available |
カーネルバージョンが5.8未満、BPF_MAP_TYPE_RINGBUF非対応 | カーネルを5.8+にアップグレード、またはBPF_MAP_TYPE_PERF_EVENT_ARRAYに変更 |
tc attach failed: cannot attach program to interface |
インターフェースに既にtc eBPFプログラムが存在 | 先にアンロード:tc filter del dev eth0 ingress、その後再アタッチ |
bpf verifier: unreachable instruction |
eBPFプログラムに到達不能コードが存在 | デッドコードと条件分岐を確認、すべてのパスが到達可能であることを保証 |
hubble flow: DNS query not visible |
Hubble DNSモニタリングが有効でない | --set hubble.metrics.enabled="{dns}"を設定、CoreDNS設定を確認 |
cilium/ebpf: collection load: invalid argument |
eBPFバイトコードがカーネルバージョンと非互換 | bpf2goで再コンパイル、ターゲットカーネルバージョンとの一致を確認 |
Hubble UI: no service map data |
L7プロトコル解析が有効でない | httpV2メトリクスを有効化:--set hubble.metrics.enabled="{httpV2}" |
grpc dial: connection refused to hubble-relay |
Relayポートが公開されていない、またはネットワークポリシーでブロック | hubble-relay ServiceとNetworkPolicyの許可ルールをチェック |
eBPF program too large: 1M instruction limit exceeded |
単一プログラムの命令数が制限を超過 | bpf_tail_callで複数のサブプログラムに分割 |
hubble observe: context deadline exceeded |
Flowデータ量が多すぎてRelay処理がタイムアウト | フィルタ条件を追加して範囲を絞り込む、またはRelayの--buffer-sizeを増やす |
高度な最適化のヒント
1. Hubble Flowサンプリング:大規模クラスタでFlowサンプリングを有効にし、1/Nのトラフィックイベントのみを記録。重要なイベント(DROPPED/ERROR)は全量保持:--set hubble.eventBufferCapacity=16384 --set hubble.eventQueueSize=8192。
2. eBPFプログラムTail Callチェーン:bpf_tail_callを使用してネットワークモニタリングをL3解析、L4解析、L7解析の3つのサブプログラムに分割。命令数制限を突破しつつ、プロトコルスタックのモジュール化を実現。
3. Hubble Metrics + Grafana連携:HubbleがエクスポートするPrometheusメトリクスをGrafanaに取り込み、ネットワークSLOダッシュボードを構築——DNS P99レイテンシ、HTTP 5xx率、ポリシー拒否率を一目で確認。
4. マルチクラスタHubble Relayカスケーディング:ClusterMesh経由で複数クラスタのHubble RelayをセントラルRelayにカスケーディングし、クラスタ間トラフィックトポロジ可視化を実現。hubble.relay.enabled=trueとclustermesh.enabled=trueを設定。
5. カスタムeBPF Map集約:eBPFプログラムのカーネル側でPer-CPU Hash Mapを使用してトラフィック統計を集約し、集約結果のみをユーザー空間に渡す——RingBufferデータ量を10倍以上削減。
比較分析:Cilium Hubble vs Istio Kiali vs Pixie vs DeepFlow
| 機能 | Cilium Hubble | Istio Kiali | Pixie | DeepFlow |
|---|---|---|---|---|
| データプレーン | eBPFカーネル層 | Sidecarプロキシ | eBPFカーネル層 | eBPFカーネル層 |
| Sidecar | ❌ 不要 | ✅ Envoy | ❌ 不要 | ❌ 不要 |
| L3/L4オブザーバビリティ | ✅ | ✅ | ✅ | ✅ |
| L7プロトコル解析 | HTTP/gRPC/Kafka | HTTP/gRPC | HTTP/gRPC/MySQL/Redis | 3000+プロトコル |
| DNSモニタリング | ✅ ネイティブ | ❌ | ✅ | ✅ |
| トラフィックトポロジ | ✅ サービス+Podレベル | ✅ サービスレベル | ✅ サービス+Podレベル | ✅ サービス+Podレベル |
| ネットワークポリシー監査 | ✅ ネイティブ | ❌ | ❌ | ❌ |
| パフォーマンスオーバーヘッド | <2% | 5-15% | <2% | <3% |
| マルチクラスタ | ✅ ClusterMesh | ✅ | ❌ | ✅ |
| オープンソース | ✅ | ✅ | ✅ | ✅ コミュニティ版 |
| 学習曲線 | 中 | 高 | 低 | 中 |
まとめと展望
eBPFはK8sネットワークオブザーバビリティの境界を再定義している。Cilium HubbleのゼロSidecarフルリンクモニタリング、Go eBPFプログラムによるカスタム収集、DNSレイテンシ追跡からL7トラフィックの細粒度オブザーバビリティ、ネットワークポリシー監査からクラスタ間トラフィックトポロジまで——5つのコアパターンが本番レベルのK8sネットワークモニタリングの完全な体系を構築する。2026年、eBPFがLinuxカーネルメインラインに入り、Hubbleのマルチクラスタ機能が成熟するにつれて、eBPFネットワークモニタリングはK8sオブザーバビリティのデファクトスタンダードとなる。今これらのパターンを習得することは、将来のクラウドネイティブネットワークアーキテクチャの強固な基盤を築くことだ。
オンラインツール推奨
-
JSONフォーマッター - Hubble Flow JSON出力をフォーマット・検証し、異常なトラフィックイベントを迅速に特定。
-
ハッシュエンコードツール - eBPF Mapのキーバリューハッシュを生成、またはネットワークポリシーのヘッダーマッチングルールのハッシュ値を計算。
-
cURL→コード変換ツール - Hubble gRPC APIのcURLリクエストをGo/Pythonコードに変換し、自動化モニタリングスクリプトへの統合を容易に。
ブラウザローカルツールを無料で試す →