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=truehubble.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=trueclustermesh.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オブザーバビリティのデファクトスタンダードとなる。今これらのパターンを習得することは、将来のクラウドネイティブネットワークアーキテクチャの強固な基盤を築くことだ。


オンラインツール推奨

  1. JSONフォーマッター - Hubble Flow JSON出力をフォーマット・検証し、異常なトラフィックイベントを迅速に特定。

  2. ハッシュエンコードツール - eBPF Mapのキーバリューハッシュを生成、またはネットワークポリシーのヘッダーマッチングルールのハッシュ値を計算。

  3. cURL→コード変換ツール - Hubble gRPC APIのcURLリクエストをGo/Pythonコードに変換し、自動化モニタリングスクリプトへの統合を容易に。

ブラウザローカルツールを無料で試す →

#eBPF网络监控#K8s网络#Cilium#网络可观测性#2026#云原生