Skip to content
Back to blog Cilium Service Mesh: Sidecar-Free with eBPF

Cilium Service Mesh: Sidecar-Free with eBPF

K8sNetworking

Cilium Service Mesh: Sidecar-Free with eBPF

Traditional service meshes inject sidecar proxies into every pod. Cilium does it differently - eBPF programs in the kernel handle mTLS, load balancing, and observability with zero sidecars.

This guide covers deploying Cilium service mesh and configuring traffic management, security policies, and observability.

TL;DR

  • Cilium mesh = eBPF-powered service mesh, no sidecars
  • Per-node Envoy for L7 processing (not per-pod)
  • Native mTLS with SPIFFE identities
  • Hubble for observability
  • 50% less resource overhead vs sidecar mesh

Why Sidecar-Free?

SIDECAR MESH (Istio/Linkerd)        CILIUM MESH
========================            ===========
Pod 1: App + Envoy (150MB)          Pod 1: App only
Pod 2: App + Envoy (150MB)          Pod 2: App only  
Pod 3: App + Envoy (150MB)          Pod 3: App only
                                    Node: Cilium Agent + Envoy
                                    
Memory: 450MB sidecars              Memory: ~200MB per node
Latency: 2 proxy hops               Latency: kernel-level

Install Cilium with Service Mesh

# Install Cilium CLI
CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)
curl -L --remote-name-all https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-amd64.tar.gz
sudo tar xzvfC cilium-linux-amd64.tar.gz /usr/local/bin

# Install with service mesh features
cilium install \
  --version 1.15.0 \
  --set kubeProxyReplacement=true \
  --set ingressController.enabled=true \
  --set ingressController.loadbalancerMode=shared

# Enable Hubble
cilium hubble enable --ui

# Verify
cilium status

Helm Installation

# cilium-values.yaml
kubeProxyReplacement: true

# Ingress controller
ingressController:
  enabled: true
  loadbalancerMode: shared

# L7 proxy (Envoy per node)
envoy:
  enabled: true

# mTLS
encryption:
  enabled: true
  type: wireguard  # or ipsec

# Hubble observability  
hubble:
  enabled: true
  relay:
    enabled: true
  ui:
    enabled: true
  metrics:
    enabled:
      - dns
      - drop
      - tcp
      - flow
      - port-distribution
      - httpV2:exemplars=true;labelsContext=source_ip,source_namespace,destination_ip,destination_namespace

# Gateway API (future of ingress)
gatewayAPI:
  enabled: true
helm repo add cilium https://helm.cilium.io
helm upgrade --install cilium cilium/cilium \
  --namespace kube-system \
  -f cilium-values.yaml

mTLS Encryption

Cilium provides transparent mTLS between pods:

# Enable WireGuard encryption (recommended)
cilium config set encryption-type wireguard

# Or IPsec
cilium config set encryption-type ipsec
cilium config set encryption-ipsec-key-file /path/to/key

Verify encryption:

# Check encryption status
cilium encrypt status

# See encrypted flows
hubble observe --protocol encrypted

Traffic Management

L7 Traffic Policies

# Retry and timeout policies
apiVersion: cilium.io/v2
kind: CiliumEnvoyConfig
metadata:
  name: api-server-config
  namespace: production
spec:
  services:
    - name: api-server
      namespace: production
  resources:
    - "@type": type.googleapis.com/envoy.config.route.v3.RouteConfiguration
      name: api-server-route
      virtual_hosts:
        - name: api-server
          domains: ["*"]
          routes:
            - match:
                prefix: "/"
              route:
                cluster: "production/api-server"
                timeout: 30s
                retry_policy:
                  retry_on: "5xx,reset,connect-failure"
                  num_retries: 3
                  per_try_timeout: 10s

Canary Deployments

apiVersion: cilium.io/v2
kind: CiliumEnvoyConfig
metadata:
  name: canary-routing
  namespace: production
spec:
  services:
    - name: api-server
      namespace: production
    - name: api-server-canary
      namespace: production
  resources:
    - "@type": type.googleapis.com/envoy.config.route.v3.RouteConfiguration
      name: canary-route
      virtual_hosts:
        - name: api
          domains: ["*"]
          routes:
            - match:
                prefix: "/"
                headers:
                  - name: "x-canary"
                    exact_match: "true"
              route:
                cluster: "production/api-server-canary"
            - match:
                prefix: "/"
              route:
                weighted_clusters:
                  clusters:
                    - name: "production/api-server"
                      weight: 90
                    - name: "production/api-server-canary"
                      weight: 10

Rate Limiting

apiVersion: cilium.io/v2
kind: CiliumEnvoyConfig
metadata:
  name: rate-limit
  namespace: production
spec:
  services:
    - name: api-server
      namespace: production
  resources:
    - "@type": type.googleapis.com/envoy.config.filter.http.local_ratelimit.v3.LocalRateLimit
      stat_prefix: http_local_rate_limiter
      token_bucket:
        max_tokens: 100
        tokens_per_fill: 100
        fill_interval: 1s
      filter_enabled:
        runtime_key: local_rate_limit_enabled
        default_value:
          numerator: 100
          denominator: HUNDRED

Ingress with Cilium

Cilium can replace your ingress controller:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-ingress
  annotations:
    ingress.cilium.io/loadbalancer-mode: shared
    ingress.cilium.io/tls-passthrough: "false"
spec:
  ingressClassName: cilium
  tls:
    - hosts:
        - api.company.com
      secretName: api-tls
  rules:
    - host: api.company.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: api-server
                port:
                  number: 8080
# Gateway class (created by Cilium)
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: production-gateway
  namespace: production
spec:
  gatewayClassName: cilium
  listeners:
    - name: https
      port: 443
      protocol: HTTPS
      hostname: "*.company.com"
      tls:
        mode: Terminate
        certificateRefs:
          - name: wildcard-tls
    - name: http
      port: 80
      protocol: HTTP
      hostname: "*.company.com"
      allowedRoutes:
        kinds:
          - kind: HTTPRoute

---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: api-route
  namespace: production
spec:
  parentRefs:
    - name: production-gateway
  hostnames:
    - "api.company.com"
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /v1
      backendRefs:
        - name: api-v1
          port: 8080
          weight: 100
    - matches:
        - path:
            type: PathPrefix
            value: /v2
      backendRefs:
        - name: api-v2
          port: 8080
          weight: 90
        - name: api-v2-canary
          port: 8080
          weight: 10

Observability with Hubble

Hubble provides deep network observability:

# Enable Hubble UI
cilium hubble enable --ui
kubectl port-forward -n kube-system svc/hubble-ui 12000:80

# CLI observability
hubble observe --namespace production

# Filter by verdict
hubble observe --verdict DROPPED

# Filter by HTTP
hubble observe --protocol http --http-status 500

# Export to JSON
hubble observe --output json > flows.json

Prometheus Metrics

# ServiceMonitor for Cilium
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: cilium
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: cilium-agent
  namespaceSelector:
    matchNames:
      - kube-system
  endpoints:
    - port: prometheus
      interval: 15s

Key Metrics

METRIC                              DESCRIPTION
======                              ===========
cilium_forward_count_total          Packets forwarded
cilium_drop_count_total             Packets dropped (with reason)
hubble_flows_processed_total        L7 flows observed
cilium_policy_verdict_total         Policy decisions
cilium_http_request_duration        HTTP latency

Network Policies (L7)

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: api-l7-policy
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: api-server
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: frontend
      toPorts:
        - ports:
            - port: "8080"
              protocol: TCP
          rules:
            http:
              - method: GET
                path: "/api/v1/public/.*"
              - method: POST
                path: "/api/v1/public/.*"
                headers:
                  - "Content-Type: application/json"
    
    - fromEndpoints:
        - matchLabels:
            app: admin
      toPorts:
        - ports:
            - port: "8080"
              protocol: TCP
          rules:
            http:
              - method: ".*"
                path: "/api/.*"

Migration from Istio

  1. Install Cilium alongside Istio
  2. Migrate namespace by namespace
  3. Remove Istio sidecars
  4. Remove Istio control plane
# Label namespace to disable Istio injection
kubectl label namespace production istio-injection=disabled

# Restart pods to remove sidecars
kubectl rollout restart deployment -n production

# Verify Cilium is handling traffic
hubble observe --namespace production

Troubleshooting

Pods can’t communicate:

cilium connectivity test
cilium status --verbose

L7 policies not working:

# Check Envoy is running
kubectl get pods -n kube-system -l k8s-app=cilium-envoy

# Check policy status
cilium policy get

High latency:

# Check for drops
hubble observe --verdict DROPPED

# Check Envoy metrics
curl -s localhost:9901/stats | grep latency

References

======================================== Cilium + eBPF + Service Mesh

No sidecars. Kernel-level networking.

Found this helpful?

Comments