Skip to content
Published on

Cilium Service Mesh:eBPFベースのサイドカーレスサービスメッシュ構築・運用ガイド

Authors
  • Name
    Twitter

Cilium Service Mesh eBPF

はじめに

サービスメッシュ(Service Mesh)は、マイクロサービス間の通信を安全かつ観測可能にするインフラストラクチャレイヤーです。Istio、Linkerdなどの従来のサービスメッシュソリューションは、各Podにサイドカープロキシを注入する方式を採用しています。このアプローチは機能しますが、各サイドカーが追加のCPUとメモリを消費し、Podの起動時間が長くなり、ネットワークホップの追加によりレイテンシが増加するという問題があります。

Cilium Service Meshはこのパラダイムを根本的に変革します。eBPFを活用してカーネルレベルでL4トラフィックを処理し、L7機能が必要な場合にのみノード単位の共有Envoyプロキシ(DaemonSet)を使用します。サイドカーがないためリソースオーバーヘッドが大幅に削減され、Pod単位のプロキシ管理の複雑さが解消されます。

本記事では、Cilium Service Meshのアーキテクチャ原理からインストール、mTLS設定、トラフィック管理、性能比較、そしてプロダクションで発生しうる障害事例と復旧手順までを総合的に解説します。

アーキテクチャとコアコンセプト

従来のサイドカーモデルの限界

従来のサービスメッシュでは、すべてのPodにEnvoyサイドカーが注入されます。100個のPodがあれば、100個の追加Envoyインスタンスが実行されることになります。

サイドカーモデルのコスト

  • Pod当たりEnvoyサイドカーが約50〜100MBのメモリを消費
  • サイドカー初期化によるPod起動遅延(2〜5秒追加)
  • すべてのトラフィックがサイドカーを経由することによる追加レイテンシ(約1ms)
  • サイドカーアップグレード時に全Podのローリングリスタートが必要

Cilium Service Meshのサイドカーレスアーキテクチャ

Cilium Service Meshは2つのレイヤーでサービスメッシュ機能を実装します。

L4レイヤー(eBPF):TCP接続管理、ロードバランシング、mTLS暗号化・復号、ネットワークポリシー適用などをカーネル内のeBPFプログラムで処理します。サイドカーなしでカーネルから直接動作するため、オーバーヘッドは極めて小さくなります。

L7レイヤー(共有Envoy):HTTPルーティング、ヘッダーベースのトラフィック分割、gRPCフィルタリングなどL7機能が必要な場合にのみ、ノード当たり1つの共有Envoyプロキシ(DaemonSet)がトラフィックを処理します。Pod単位ではなくノード単位であるため、Envoyインスタンス数が大幅に削減されます。

コアコンポーネント

  • Cilium Agent(DaemonSet):各ノードでeBPFプログラムをロード・管理します。サービスメッシュのデータプレーンの役割を果たします。
  • Cilium Operator(Deployment):クラスタレベルのリソース管理、IPプール割り当て、CRD同期を担当します。
  • Envoy DaemonSet:L7ポリシーが適用されたトラフィックのみを処理する共有プロキシです。Cilium Agentが自動的にEnvoy設定を注入します。
  • Hubble:すべてのネットワークフローをリアルタイムで監視する組み込みのオブザーバビリティツールです。

インストールとサービスメッシュの有効化

前提条件

# カーネルバージョン確認(5.10以上必須、6.1以上推奨)
uname -r

# eBPFサポート確認
cat /boot/config-$(uname -r) | grep CONFIG_BPF
# CONFIG_BPF=y
# CONFIG_BPF_SYSCALL=y
# CONFIG_BPF_JIT=y

# Cilium CLIインストール
CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)
curl -L --fail --remote-name-all \
  https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-amd64.tar.gz
sudo tar xzvf cilium-linux-amd64.tar.gz -C /usr/local/bin

Helmを使ったサービスメッシュのインストール

# Helm repoの追加
helm repo add cilium https://helm.cilium.io/
helm repo update

# Cilium Service Meshインストール(kube-proxy置換 + サービスメッシュ有効化)
helm install cilium cilium/cilium --version 1.19.0 \
  --namespace kube-system \
  --set kubeProxyReplacement=true \
  --set k8sServiceHost="API_SERVER_IP" \
  --set k8sServicePort=6443 \
  --set envoyConfig.enabled=true \
  --set ingressController.enabled=true \
  --set ingressController.loadbalancerMode=shared \
  --set hubble.enabled=true \
  --set hubble.relay.enabled=true \
  --set hubble.ui.enabled=true \
  --set encryption.enabled=true \
  --set encryption.type=wireguard \
  --set authentication.mutual.spire.enabled=true \
  --set authentication.mutual.spire.install.enabled=true

この設定の主要パラメータについて説明します:

  • envoyConfig.enabled=true:CiliumEnvoyConfig CRDによるL7トラフィック管理を有効化
  • ingressController.enabled=true:CiliumをKubernetes Ingressコントローラーとして使用
  • authentication.mutual.spire.enabled=true:SPIREベースのmTLS認証を有効化
  • encryption.type=wireguard:WireGuardベースのノード間透過暗号化

インストールの確認

# Cilium全体ステータス確認
cilium status --wait

# 期待される出力:
#     /¯¯\
#  /¯¯\__/¯¯\    Cilium:             OK
#  \__/¯¯\__/    Operator:           OK
#  /¯¯\__/¯¯\    Envoy DaemonSet:    OK
#  \__/¯¯\__/    Hubble Relay:       OK
#     \__/        ClusterMesh:        disabled
#                 SPIRE Server:       OK
#                 SPIRE Agent:        OK

# サービスメッシュ機能の確認
cilium config view | grep -E "envoy|mesh|mutual"

# 接続テスト(サービスメッシュを含む)
cilium connectivity test

mTLS設定:SPIREベースの相互認証

mTLSが必要な理由

サービスメッシュのコア機能の1つは、サービス間通信の**相互認証(mTLS)**です。mTLSはクライアントとサーバーの両方が証明書を提示して互いの身元を検証します。これにより中間者攻撃(MITM)を防止し、ネットワークポリシーとは別にワークロードアイデンティティベースのセキュリティを実現します。

CiliumはSPIFFE/SPIREフレームワークを使用してmTLSを実装します。各ワークロードにSPIFFE IDが自動的に割り当てられ、証明書の発行と更新が透過的に行われます。

mTLS認証ポリシーの適用

# mtls-authentication-policy.yaml
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: require-mutual-auth
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: payment-service
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: order-service
      authentication:
        mode: required # mTLS認証必須
      toPorts:
        - ports:
            - port: '8080'
              protocol: TCP

このポリシーは、payment-serviceへのインバウンドトラフィックにmTLS認証を必須とします。order-serviceが有効なSPIFFE IDを持っていない場合、接続は拒否されます。

クラスタ全体のmTLS強制

# cluster-wide-mtls.yaml
apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
  name: enforce-mtls-cluster-wide
spec:
  endpointSelector:
    matchExpressions:
      - key: io.kubernetes.pod.namespace
        operator: NotIn
        values:
          - kube-system
  ingress:
    - fromEndpoints:
        - {}
      authentication:
        mode: required

注意事項:クラスタ全体のmTLSを有効化する前に、すべてのワークロードがSPIREに登録されていることを確認してください。未登録のワークロードは直ちに通信がブロックされます。必ずステージング環境で先にテストし、プロダクションにはネームスペースごとに段階的に適用してください。

# SPIREに登録されたワークロードの確認
kubectl exec -n kube-system spire-server-0 -- \
  /opt/spire/bin/spire-server entry show

# mTLS認証ステータスの確認
cilium-dbg identity list | grep -i auth
hubble observe --namespace production --verdict DROPPED -o json | \
  jq 'select(.drop_reason_desc == "Authentication required")'

L4/L7トラフィック管理

CiliumEnvoyConfigによるL7ルーティング

Cilium Service MeshはCiliumEnvoyConfig CRDを通じてL7トラフィック管理を行います。これはIstioのVirtualService/DestinationRuleと同様の役割を果たします。

# l7-traffic-split.yaml
apiVersion: cilium.io/v2
kind: CiliumEnvoyConfig
metadata:
  name: api-traffic-split
  namespace: production
spec:
  services:
    - name: api-service
      namespace: production
  backendServices:
    - name: api-service-v1
      namespace: production
    - name: api-service-v2
      namespace: production
  resources:
    - '@type': type.googleapis.com/envoy.config.listener.v3.Listener
      name: api-listener
      filter_chains:
        - filters:
            - name: envoy.filters.network.http_connection_manager
              typed_config:
                '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                stat_prefix: api-traffic
                route_config:
                  name: api-routes
                  virtual_hosts:
                    - name: api-host
                      domains: ['*']
                      routes:
                        - match:
                            prefix: '/'
                            headers:
                              - name: 'x-canary'
                                exact_match: 'true'
                          route:
                            cluster: 'production/api-service-v2'
                        - match:
                            prefix: '/'
                          route:
                            weighted_clusters:
                              clusters:
                                - name: 'production/api-service-v1'
                                  weight: 90
                                - name: 'production/api-service-v2'
                                  weight: 10
                http_filters:
                  - name: envoy.filters.http.router
                    typed_config:
                      '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

この設定は2つのルーティングルールを定義しています:

  • x-canary: trueヘッダーが含まれるリクエストはv2にルーティング
  • 残りのリクエストはv1に90%、v2に10%の重みで分散(カナリーデプロイメント)

L4ロードバランシングポリシー

L4レベルのロードバランシングはeBPFで直接処理されるため、Envoyを経由せず非常に効率的です。

# l4-lb-policy.yaml
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: backend-l4-policy
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: backend-service
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: api-gateway
      toPorts:
        - ports:
            - port: '8080'
              protocol: TCP
            - port: '9090'
              protocol: TCP
  egress:
    - toEndpoints:
        - matchLabels:
            app: database
      toPorts:
        - ports:
            - port: '5432'
              protocol: TCP
    - toEndpoints:
        - matchLabels:
            app: cache
      toPorts:
        - ports:
            - port: '6379'
              protocol: TCP

L7 HTTPポリシーとRate Limiting

# l7-rate-limiting.yaml
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: api-l7-with-ratelimit
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: public-api
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: frontend
      toPorts:
        - ports:
            - port: '8080'
              protocol: TCP
          rules:
            http:
              - method: GET
                path: '/api/v1/products.*'
              - method: GET
                path: '/api/v1/categories.*'
              - method: POST
                path: '/api/v1/orders'
                headers:
                  - 'Content-Type: application/json'

性能比較:Cilium vs Istio vs Linkerd

サービスメッシュ選定において最も重要な判断基準の1つが性能です。以下は同一ワークロード(gRPCマイクロサービス、100 RPS)で測定したベンチマーク結果です。

項目Cilium Service MeshIstio(サイドカー)Linkerd
アーキテクチャサイドカーレス(eBPF + ノードEnvoy)Pod単位サイドカーEnvoyPod単位サイドカーlinkerd2-proxy
P50レイテンシ追加約0.1ms(L4)、約0.3ms(L7)約1.0ms約0.5ms
P99レイテンシ追加約0.3ms(L4)、約0.8ms(L7)約3.0ms約1.5ms
Pod当たりメモリオーバーヘッド0MB(L4)/ ノード当たり約150MB共有約50〜100MB約20〜30MB
Pod当たりCPUオーバーヘッドほぼなし(L4)約10〜50m約5〜20m
Pod起動遅延0秒(サイドカーなし)2〜5秒(サイドカー注入)1〜3秒
mTLSSPIRE/WireGuard組み込み(Citadel)組み込み(独自PKI)
L7機能CiliumEnvoyConfigVirtualService/DestinationRuleHTTPRoute/ServiceProfile
オブザーバビリティHubble(組み込み)Kiali、Jaeger(別途)Linkerd Viz(組み込み)
Gateway APIサポートネイティブネイティブネイティブ
コミュニティCNCF GraduatedCNCF GraduatedCNCF Graduated
学習コスト中程度(eBPFの理解が必要)高い(複雑なCRD体系)低い

ベンチマークの実行方法

# fortioを使った性能測定
kubectl run fortio-client --rm -it --image=fortio/fortio -- \
  load -c 50 -qps 1000 -t 60s -json - \
  http://api-service.production:8080/api/v1/health

# 分析のポイント:
# - P50、P90、P99レイテンシ
# - 最大QPS(Queries Per Second)
# - エラー率
# - CPU/メモリ使用量(kubectl top podsで別途測定)

# HubbleメトリクスでL7レイテンシを確認
hubble observe --namespace production --protocol http -o json | \
  jq '.l7.latency_ns / 1000000'

重要ポイント:Cilium Service MeshはL4トラフィック処理時にサイドカーモデルと比較して3〜10倍低いレイテンシを示します。L7機能を使用しても、共有Envoyモデルのおかげでpod当たりのメモリオーバーヘッドはゼロであり、ノード当たり1つのEnvoyだけでそのノードのすべてのL7トラフィックを処理します。

トラブルシューティング:障害事例と復旧

事例1:mTLS認証失敗によるサービス間通信不可

症状:特定のサービスが突然他のサービスと通信できなくなり、HubbleでAuthentication requiredドロップが観測される

# 認証失敗トラフィックの確認
hubble observe --namespace production --verdict DROPPED -o compact

# SPIREエージェントステータスの確認
kubectl get pods -n kube-system -l app=spire-agent
kubectl logs -n kube-system -l app=spire-agent --tail=50

# 特定ワークロードのSVID(証明書)ステータス確認
kubectl exec -n kube-system spire-server-0 -- \
  /opt/spire/bin/spire-server entry show -selector k8s:ns:production

# Ciliumエンドポイントの認証ステータス確認
kubectl exec -n kube-system ds/cilium -- \
  cilium-dbg endpoint list -o json | jq '.[].status.policy.realized.auth'

原因と解決策

  1. SPIREエージェントのOOMKilled:SPIREエージェントのメモリlimitを引き上げます。大規模クラスタでは512Mi以上を推奨
  2. 証明書の期限切れ:SPIREのSVID TTLのデフォルトは1時間です。SPIREサーバーがダウンすると証明書が更新されず、1時間後に通信が断絶します。SPIREサーバーの高可用性を必ず確保してください
  3. セレクターの誤りCiliumNetworkPolicyauthentication.mode: requiredが意図しない範囲に適用されている可能性があります。まず特定のネームスペースだけを対象にテストしてください

事例2:Envoy DaemonSet障害によるL7ポリシー動作不能

症状:L7 HTTPポリシー(パス・ヘッダーベースのフィルタリング)が動作せず、L4ポリシーのみが適用される

# Envoy DaemonSetステータスの確認
kubectl get ds -n kube-system cilium-envoy
kubectl describe ds -n kube-system cilium-envoy

# 特定ノードのEnvoyログ確認
kubectl logs -n kube-system -l k8s-app=cilium-envoy --tail=100

# Cilium AgentとEnvoy間の接続確認
kubectl exec -n kube-system ds/cilium -- \
  cilium-dbg status --verbose | grep -A5 "Envoy"

# CiliumEnvoyConfigステータスの確認
kubectl get cec -n production -o yaml

原因と解決策

  1. Envoy Podのリソース不足:ノードのL7トラフィックが多い場合、Envoyのメモリが不足する可能性があります。Helm valuesでenvoy.resources.limits.memoryを引き上げてください
  2. 無効なCiliumEnvoyConfig:Envoy設定の文法エラーがある場合、該当リスナーがロードされません。cilium-dbg envoy configコマンドで実際にロードされた設定を確認してください
  3. Node Affinityの問題:特定のノードでEnvoyがスケジューリングされない場合、そのノードのL7ポリシーが動作しません

事例3:サービスメッシュアップグレード後の接続断

症状:Ciliumバージョンアップグレード後、一部のPod間通信が断続的に失敗する

# Cilium Agentローリングリスタートステータスの確認
kubectl rollout status ds/cilium -n kube-system

# eBPFマップ同期ステータスの確認
kubectl exec -n kube-system ds/cilium -- \
  cilium-dbg bpf endpoint list

# エンドポイント復旧待ち
kubectl exec -n kube-system ds/cilium -- \
  cilium-dbg endpoint list | grep -v ready

復旧手順

  1. まず、Cilium Agentがすべてのノードで正常にリスタートされたことを確認します
  2. eBPFマップが正常にリロードされたかをcilium-dbg bpf endpoint listで確認します
  3. 問題が継続する場合、影響を受けたPodをリスタートしてエンドポイントを再登録します
  4. 最終手段としてcilium-dbg endpoint regenerate --allを実行し、すべてのエンドポイントのeBPFプログラムを再生成します

注意:アップグレード時は必ずhelm diff upgradeで変更内容を事前に確認し、一度に1つのマイナーバージョンずつアップグレードしてください。Cilium 1.17から1.19へのスキップはサポートされていません。

事例4:Hubble観測データが収集されない

症状hubble observeコマンドが空の結果を返すか、タイムアウトする

# Hubble Relayステータスの確認
kubectl get pods -n kube-system -l k8s-app=hubble-relay
kubectl logs -n kube-system -l k8s-app=hubble-relay --tail=50

# Hubble接続テスト
cilium hubble port-forward &
hubble status

# Cilium AgentのHubbleモニタリングステータス
kubectl exec -n kube-system ds/cilium -- \
  cilium-dbg monitor --type drop --type trace

解決策:最も一般的な原因はHubble RelayのgRPC接続の切断です。kubectl rollout restart deployment hubble-relay -n kube-systemでRelayをリスタートしてください。

運用ノート

IstioからCilium Service Meshへのマイグレーション

既存のIstio環境からCilium Service Meshへマイグレーションする場合、一括切り替えではなく段階的に進めてください。

フェーズ1 - 共存(2〜4週間):CiliumをCNIとしてインストールしますが、サービスメッシュ機能は無効のままにします。Istioサイドカーは引き続き動作します。

フェーズ2 - ネームスペースごとの切り替え:非クリティカルなネームスペースからIstioサイドカー注入を無効化し、CiliumのmTLSとL7ポリシーを有効化します。

フェーズ3 - 完全切り替え:すべてのネームスペースからIstioを削除し、Cilium Service Meshに完全移行します。

# ネームスペースごとにIstioサイドカー注入を無効化
kubectl label namespace staging istio-injection-
kubectl rollout restart deployment -n staging

# Cilium mTLSポリシーの適用
kubectl apply -f cilium-mtls-policy-staging.yaml

# 切り替え後の確認
hubble observe --namespace staging --protocol http

リソースサイジングガイドライン

Cilium Agent(DaemonSet)

  • 小規模クラスタ(ノード10台以下):CPU 200m / メモリ 256Mi
  • 中規模クラスタ(ノード50台以下):CPU 500m / メモリ 512Mi
  • 大規模クラスタ(ノード100台以上):CPU 1000m / メモリ 1Gi

Envoy DaemonSet

  • L7トラフィックが少ない場合:CPU 100m / メモリ 128Mi
  • L7トラフィックが多い場合:CPU 500m / メモリ 512Mi
  • 非常に高いL7スループット:CPU 1000m / メモリ 1Gi

SPIRE Server

  • ワークロード1,000件以下:CPU 200m / メモリ 256Mi
  • ワークロード5,000件以上:CPU 500m / メモリ 512Mi、HA構成が必須

必須モニタリングメトリクス

# Prometheusで収集すべき主要メトリクス

# 1. Cilium Agentステータス
# cilium_agent_api_process_time_seconds - API処理時間
# cilium_agent_bootstrap_seconds - Agent起動時間
# cilium_bpf_map_ops_total - BPFマップ操作数

# 2. サービスメッシュ関連
# cilium_proxy_upstream_reply_seconds - L7プロキシアップストリーム応答時間
# cilium_proxy_redirects - L7プロキシにリダイレクトされた接続数
# cilium_auth_map_entries - mTLS認証マップエントリ数

# 3. Hubbleオブザーバビリティ
# hubble_flows_processed_total - 処理済みフロー数
# hubble_tcp_flags_total - TCPフラグ別カウント

# Grafanaダッシュボードのインポート
# Cilium公式ダッシュボード:https://grafana.com/grafana/dashboards/16611

運用チェックリスト

デプロイ前チェックリスト

  • カーネルバージョンが5.10以上であることを確認(6.1以上推奨)
  • CONFIG_BPFCONFIG_BPF_SYSCALLCONFIG_BPF_JITが有効であることを確認
  • kube-proxy置換モードでインストールする場合、既存のkube-proxyが削除されていることを確認
  • SPIREサーバーがHA構成でデプロイされていることを確認(プロダクション必須)
  • CiliumNetworkPolicyが既存のKubernetes NetworkPolicyと競合しないことを確認
  • PodDisruptionBudgetがCilium DaemonSetとSPIREに設定されていることを確認
  • cilium connectivity testが成功することを確認

アップグレードチェックリスト

  • Ciliumリリースノートで必ずBreaking Changesを確認
  • 1つのマイナーバージョンずつ順次アップグレード(1.17 -> 1.18 -> 1.19)
  • helm diff upgradeで変更内容を事前確認
  • ステージング環境で先にアップグレードし、最低24時間観察
  • アップグレード中はcilium statusでAgentローリングリスタートをモニタリング
  • アップグレード後にcilium connectivity testを再実行

障害対応チェックリスト

  • Cilium Agent障害時:該当ノードのPodは既存のeBPFマップで通信を継続可能ですが、ポリシー更新は停止します
  • Envoy DaemonSet障害時:L7ポリシーのみ影響を受け、L4ポリシーはeBPFで引き続き動作します
  • SPIREサーバー障害時:既存証明書のTTL(デフォルト1時間)が期限切れになる前に復旧が必須です
  • etcd障害時(Cilium KVStore):Cilium Agentがローカルキャッシュで動作しますが、新しいポリシーの反映はできません

参考資料

  1. Cilium公式ドキュメント - Service Mesh
  2. Cilium公式ドキュメント - Mutual Authentication (mTLS)
  3. Cilium GitHub - Service Mesh Examples
  4. Cilium公式ドキュメント - CiliumEnvoyConfig
  5. SPIFFE/SPIRE公式ドキュメント
  6. Kubernetes公式ブログ - Service Mesh Interface
  7. Isovalent - Cilium Service Mesh Performance Benchmark
  8. Cilium公式ドキュメント - Hubble Observability