Sitemap

Exploring Cilium Service Mesh — From eBPF-powered Networking to Advanced L7 Traffic Management

The Gashida Study on week 6

--

Service mesh has become the de facto solution for managing microservice communication in cloud-native environments. While traditional service meshes like Istio rely on sidecar proxies that add significant overhead, Cilium takes a fundamentally different approach by leveraging eBPF technology in the Linux kernel. This article explores Cilium’s Service Mesh capabilities through hands-on examples.

Press enter or click to view image in full size
https://docs.cilium.io/en/stable/network/ebpf/ https://shinminjin.github.io/posts/cilium06/

1. Setting Up the Lab Environment

We’ll use Vagrant with VirtualBox to create a Kubernetes cluster with Cilium pre-configured for service mesh functionality. The setup includes a control plane node (k8s-ctr), a worker node (k8s-w1), and a router VM for external connectivity testing.

# Initialize the environment
mkdir cilium-lab && cd cilium-lab
curl -O https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/6w/Vagrantfile
vagrant up

The Vagrantfile provisions VMs with 4 vCPUs and 2560MB RAM for each Kubernetes node. Cilium with Envoy requires more resources than a basic CNI setup. The initialization script also installs pwru (Packet, Where Are You?), an eBPF-based packet tracing tool.

Once the VMs are up, verify Cilium’s installation:

cilium config view | grep -E '^loadbalancer|l7'

Output:

enable-l7-proxy                                   true
loadbalancer-l7 envoy
loadbalancer-l7-algorithm round_robin
loadbalancer-l7-ports

Deploy a sample application to verify connectivity:

cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: webpod
spec:
replicas: 2
selector:
matchLabels:
app: webpod
template:
metadata:
labels:
app: webpod
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- webpod
topologyKey: "kubernetes.io/hostname"
containers:
- name: webpod
image: traefik/whoami
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: webpod
spec:
selector:
app: webpod
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
EOF

2. Understanding Cilium’s Service Mesh Architecture

Traditional service meshes inject a sidecar container into each pod. This sidecar intercepts all network traffic, but packets must traverse the TCP/IP stack three times before leaving the pod: from the application container to the sidecar, from the sidecar to the kernel, and finally out through the network interface.

Press enter or click to view image in full size
https://sweetlittlebird.github.io/posts/2025-08-24-Cilium-Week6/

Cilium eliminates this overhead by using eBPF programs that run directly in the kernel. For L3/L4 traffic (IP, TCP, UDP), Cilium handles everything in kernel space without any userspace proxy involvement. For L7 traffic (HTTP, gRPC, etc.), Cilium uses a per-node Envoy proxy rather than per-pod sidecars.

Examine how this works:

# Check the Cilium-managed Envoy deployment
kubectl get ds -n kube-system cilium-envoy -owide

Output:

NAME           DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE   CONTAINERS     IMAGES
cilium-envoy 2 2 2 2 2 kubernetes.io/os=linux 15m cilium-envoy quay.io/cilium/cilium-envoy:v1.34.4...
kubectl get pod -n kube-system -l k8s-app=cilium-envoy -owide

Output:

NAME                 READY   STATUS    RESTARTS   AGE   IP               NODE      
cilium-envoy-jhcs2 1/1 Running 0 15m 192.168.10.101 k8s-w1
cilium-envoy-txqxb 1/1 Running 0 15m 192.168.10.100 k8s-ctr

Envoy runs as a DaemonSet with one instance per node, dramatically reducing resource consumption in large clusters.

# Inspect the Envoy configuration
kubectl exec -it -n kube-system ds/cilium-envoy -- ls -al /var/run/cilium/envoy/sockets

Output:

srw-rw---- 1 root 1337   0 Aug 19 22:27 access_log.sock
srwxr-xr-x 1 root root 0 Aug 19 22:27 admin.sock
drwxr-xr-x 3 root root 60 Aug 19 22:27 envoy
srw-rw---- 1 root 1337 0 Aug 19 22:27 xds.sock

3. Kubernetes Ingress Support with Cilium

Press enter or click to view image in full size
https://sweetlittlebird.github.io/posts/2025-08-24-Cilium-Week6/

Cilium can act as a Kubernetes Ingress controller, integrating directly with the CNI layer. This means ingress traffic benefits from the same eBPF acceleration as pod-to-pod communication.

Set up IP address management for LoadBalancer services:

# Configure IP pool for LoadBalancer services
cat << EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumLoadBalancerIPPool
metadata:
name: "cilium-lb-ippool"
spec:
blocks:
- start: "192.168.10.211"
stop: "192.168.10.215"
EOF

Output:

ciliumloadbalancerippool.cilium.io/cilium-lb-ippool created
# Enable L2 announcements for the IP pool
cat << EOF | kubectl apply -f -
apiVersion: "cilium.io/v2alpha1"
kind: CiliumL2AnnouncementPolicy
metadata:
name: policy1
spec:
interfaces:
- eth1
externalIPs: true
loadBalancerIPs: true
EOF

Output:

ciliuml2announcementpolicy.cilium.io/policy1 created

The L2 announcement policy allows Cilium to respond to ARP requests for LoadBalancer IPs, making them reachable from outside the cluster without a cloud load balancer.

Verify IP allocation:

kubectl get ippool

Output:

NAME               DISABLED   CONFLICTING   IPS AVAILABLE   AGE
cilium-lb-ippool false False 4 16s
LBIP=$(kubectl get svc -n kube-system cilium-ingress -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo $LBIP

Output:

192.168.10.211
arping -i eth1 $LBIP -c 2

Output:

ARPING 192.168.10.211
60 bytes from 08:00:27:92:a6:9d (192.168.10.211): index=0 time=499.579 usec
60 bytes from 08:00:27:92:a6:9d (192.168.10.211): index=1 time=586.194 usec

--- 192.168.10.211 statistics ---
2 packets transmitted, 2 packets received, 0% unanswered (0 extra)

Deploy the Bookinfo application and create an Ingress resource:

# Deploy Bookinfo without Istio
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.26/samples/bookinfo/platform/kube/bookinfo.yaml

Output:

service/details created
serviceaccount/bookinfo-details created
deployment.apps/details-v1 created
service/ratings created
serviceaccount/bookinfo-ratings created
deployment.apps/ratings-v1 created
service/reviews created
serviceaccount/bookinfo-reviews created
deployment.apps/reviews-v1 created
deployment.apps/reviews-v2 created
deployment.apps/reviews-v3 created
service/productpage created
serviceaccount/bookinfo-productpage created
deployment.apps/productpage-v1 created
# Create Ingress resource
cat << EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: basic-ingress
spec:
ingressClassName: cilium
rules:
- http:
paths:
- backend:
service:
name: details
port:
number: 9080
path: /details
pathType: Prefix
- backend:
service:
name: productpage
port:
number: 9080
path: /
pathType: Prefix
EOF

Output:

ingress.networking.k8s.io/basic-ingress created

Test the ingress:

curl -s http://$LBIP/details/1 | jq

Output:

{
"id": 1,
"author": "William Shakespeare",
"year": 1595,
"type": "paperback",
"pages": 200,
"publisher": "PublisherA",
"language": "English",
"ISBN-10": "1234567890",
"ISBN-13": "123-1234567890"
}
curl -s http://$LBIP/productpage | head -20

Output:

<!DOCTYPE html>
<html>
<head>
<title>Simple Bookstore App</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">

When traffic arrives at the LoadBalancer IP, Cilium’s eBPF programs intercept it at the kernel level. Using TPROXY (Transparent Proxy), the traffic is redirected to the per-node Envoy proxy. Envoy then applies L7 routing rules and forwards the traffic to the appropriate backend pods.

Trace this path using pwru:

# On the node where productpage pod is running
kubectl get pod -l app=productpage -owide

Output:

NAME                              READY   STATUS    RESTARTS   AGE   IP             NODE
productpage-v1-54bb874995-58dcm 1/1 Running 0 5m 172.20.1.147 k8s-w1
# SSH to that node and find the veth interface
PROID=172.20.1.147
ip route | grep $PROID

Output:

172.20.1.147 dev lxcc960423e84e9 proto kernel scope link
PROVETH=lxcc960423e84e9
# Capture traffic on the veth interface
ngrep -tW byline -d $PROVETH '' 'tcp port 9080'

When you make a request from outside the cluster, you’ll see the X-Forwarded-For header containing the original client IP. This is how Cilium preserves source IP visibility without requiring externalTrafficPolicy: Local.

4. Advanced Network Policies with Ingress

Cilium’s CiliumNetworkPolicies can inspect L7 protocols, unlike standard Kubernetes NetworkPolicies.

Implement a security policy that restricts external access:

# Block all external traffic by default
cat << EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumClusterwideNetworkPolicy
metadata:
name: "external-lockdown"
spec:
description: "Block all traffic from outside the cluster"
endpointSelector: {}
ingress:
- fromEntities:
- cluster
EOF

Output:

ciliumclusterwidenetworkpolicy.cilium.io/external-lockdown created

Test the policy:

curl --fail -v http://$LBIP/details/1

Output:

< HTTP/1.1 403 Forbidden

Monitor the drops with Hubble:

Press enter or click to view image in full size
https://juyeon22.tistory.com/39 https://gnobaaaaar.tistory.com/103
hubble observe -f --identity ingress

Output:

127.0.0.1:36726 (ingress) -> 127.0.0.1:15778 (world) http-request DROPPED

Allow specific IPs:

cat << EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumClusterwideNetworkPolicy
metadata:
name: "allow-cidr"
spec:
endpointSelector:
matchExpressions:
- key: reserved:ingress
operator: Exists
ingress:
- fromCIDRSet:
- cidr: 192.168.10.200/32
- cidr: 127.0.0.1/32
EOF

Output:

ciliumclusterwidenetworkpolicy.cilium.io/allow-cidr created

Cilium assigns a special “ingress” identity to traffic coming through the Ingress controller. This allows you to write policies that specifically target ingress traffic, separate from regular pod-to-pod communication.

5. TLS Termination with Cilium Ingress

Press enter or click to view image in full size
https://devlos.tistory.com/111

For production deployments, TLS is essential. Cilium Ingress supports TLS termination at the edge using mkcert for local development:

# Install mkcert and generate certificates
apt install mkcert -y
mkcert '*.cilium.rocks'

Output:

Created a new local CA 💥
Created a new certificate valid for the following names 📜
- "*.cilium.rocks"
The certificate is at "./_wildcard.cilium.rocks.pem" and the key at "./_wildcard.cilium.rocks-key.pem" ✅
# Create Kubernetes secret with the certificates
kubectl create secret tls demo-cert \
--key=_wildcard.cilium.rocks-key.pem \
--cert=_wildcard.cilium.rocks.pem

Output:

secret/demo-cert created
# Install the CA into system trust store
mkcert -install

Output:

The local CA is now installed in the system trust store! ⚡️

Create a TLS-enabled Ingress:

cat << EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-ingress
spec:
ingressClassName: cilium
rules:
- host: bookinfo.cilium.rocks
http:
paths:
- backend:
service:
name: productpage
port:
number: 9080
path: /
pathType: Prefix
tls:
- hosts:
- bookinfo.cilium.rocks
secretName: demo-cert
EOF

Output:

ingress.networking.k8s.io/tls-ingress created

Test with TLS:

LBIP=$(kubectl get ingress tls-ingress -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl -s --resolve bookinfo.cilium.rocks:443:${LBIP} https://bookinfo.cilium.rocks/productpage | head -10

Output:

<!DOCTYPE html>
<html>
<head>
<title>Simple Bookstore App</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">

6. Gateway API

While Ingress works well for HTTP(S) traffic, the Gateway API provides more flexibility and power. It separates concerns between infrastructure providers (GatewayClass), cluster operators (Gateway), and application developers (Routes).

Install the Gateway API CRDs:

kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_gatewayclasses.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_gateways.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_httproutes.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_referencegrants.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_grpcroutes.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/experimental/gateway.networking.k8s.io_tlsroutes.yaml

Enable Gateway API in Cilium (note: this disables Ingress support):

helm upgrade cilium cilium/cilium --version 1.18.1 --namespace kube-system --reuse-values \
--set ingressController.enabled=false --set gatewayAPI.enabled=true

kubectl -n kube-system rollout restart deployment/cilium-operator
kubectl -n kube-system rollout restart ds/cilium

Create a Gateway and HTTPRoute:

cat << EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: my-gateway
spec:
gatewayClassName: cilium
listeners:
- protocol: HTTP
port: 80
name: web-gw
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http-app-1
spec:
parentRefs:
- name: my-gateway
namespace: default
rules:
- matches:
- path:
type: PathPrefix
value: /details
backendRefs:
- name: details
port: 9080
- matches:
- headers:
- type: Exact
name: magic
value: foo
queryParams:
- type: Exact
name: great
value: example
path:
type: PathPrefix
value: /
method: GET
backendRefs:
- name: productpage
port: 9080
EOF

Output:

gateway.gateway.networking.k8s.io/my-gateway created
httproute.gateway.networking.k8s.io/http-app-1 created

The Gateway API is more expressive than Ingress. We can match on headers, query parameters, and HTTP methods all in the same rule:

GATEWAY=$(kubectl get gateway my-gateway -o jsonpath='{.status.addresses[0].value}')
# Regular request
curl -s http://$GATEWAY/details/1 | jq

Output:

{
"id": 1,
"author": "William Shakespeare",
"year": 1595,
"type": "paperback",
"pages": 200,
"publisher": "PublisherA",
"language": "English",
"ISBN-10": "1234567890",
"ISBN-13": "123-1234567890"
}
# Request with specific headers and query params
curl -v -H 'magic: foo' http://$GATEWAY\?great\=example

Output:

< HTTP/1.1 200 OK

7. TLS Passthrough with Gateway API

Sometimes you need end-to-end encryption where the backend service handles TLS termination:

# Create an HTTPS backend service
cat <<'EOF' > nginx.conf
events {}
http {
server {
listen 443 ssl;
server_name nginx.cilium.rocks;
ssl_certificate /etc/nginx-server-certs/tls.crt;
ssl_certificate_key /etc/nginx-server-certs/tls.key;
root /usr/share/nginx/html;
index index.html;
}
}
EOF

kubectl create configmap nginx-configmap --from-file=nginx.conf=./nginx.conf

Output:

configmap/nginx-configmap created
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: my-nginx
spec:
ports:
- port: 443
protocol: TCP
selector:
run: my-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 1
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 443
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx
readOnly: true
- name: nginx-server-certs
mountPath: /etc/nginx-server-certs
readOnly: true
volumes:
- name: nginx-config
configMap:
name: nginx-configmap
- name: nginx-server-certs
secret:
secretName: demo-cert
EOF

Output:

service/my-nginx created
deployment.apps/my-nginx created

Create a TLS passthrough Gateway:

cat << EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: cilium-tls-gateway
spec:
gatewayClassName: cilium
listeners:
- name: https
hostname: "nginx.cilium.rocks"
port: 443
protocol: TLS
tls:
mode: Passthrough
allowedRoutes:
namespaces:
from: All
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TLSRoute
metadata:
name: nginx
spec:
parentRefs:
- name: cilium-tls-gateway
hostnames:
- "nginx.cilium.rocks"
rules:
- backendRefs:
- name: my-nginx
port: 443
EOF

Output:

gateway.gateway.networking.k8s.io/cilium-tls-gateway created
tlsroute.gateway.networking.k8s.io/nginx created

The Gateway reads the SNI (Server Name Indication) from the TLS ClientHello to route traffic but doesn’t decrypt it. The backend nginx pod handles the actual TLS termination.

8. L7-Aware Traffic Management

Cilium’s L7 capabilities extend beyond simple routing. You can implement sophisticated traffic management policies using CiliumEnvoyConfig resources. Enable this feature:

helm upgrade cilium cilium/cilium --version 1.18.1 --namespace kube-system --reuse-values \
--set ingressController.enabled=true --set gatewayAPI.enabled=false \
--set envoyConfig.enabled=true --set loadBalancer.l7.backend=envoy

kubectl -n kube-system rollout restart deployment/cilium-operator
kubectl -n kube-system rollout restart ds/cilium
kubectl -n kube-system rollout restart ds/cilium-envoy

Deploy a test application with multiple backend services:

kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/1.18.1/examples/kubernetes/servicemesh/envoy/test-application.yaml

Output:

configmap/coredns-configmap created
deployment.apps/client created
deployment.apps/client2 created
deployment.apps/echo-service-1 created
deployment.apps/echo-service-2 created
service/echo-service-1 created
service/echo-service-2 created
export CLIENT2=$(kubectl get pods -l name=client2 -o jsonpath='{.items[0].metadata.name}')
echo $CLIENT2

Output:

client2-c97ddf6cf-mwwpr

Apply L7 network policies that inspect HTTP traffic:

kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/1.18.1/examples/kubernetes/servicemesh/envoy/client-egress-l7-http.yaml
kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/1.18.1/examples/kubernetes/servicemesh/envoy/client-egress-only-dns.yaml

Output:

ciliumnetworkpolicy.cilium.io/client-egress-l7-http created
ciliumnetworkpolicy.cilium.io/client-egress-only-dns created

Test the policies:

# This succeeds (GET / is allowed)
kubectl exec -it $CLIENT2 -- curl -v echo-service-1:8080/

Output:

< HTTP/1.1 200 OK
# This fails (GET /foo is not allowed)
kubectl exec -it $CLIENT2 -- curl -v echo-service-2:8080/foo

Output:

< HTTP/1.1 403 Forbidden

Monitor with Hubble to see L7 details:

hubble observe --from-pod $CLIENT2 -f

Output:

Aug 23 16:23:59.172: default/client2-c97ddf6cf-mwwpr:57844 (ID:20242) -> default/echo-service-2-5df858689b-7gjxk:8080 (ID:14786) http-request DROPPED (HTTP/1.1 GET http://echo-service-2:8080/foo)

Implement advanced traffic management with load balancing and URL rewriting:

kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/1.18.1/examples/kubernetes/servicemesh/envoy/envoy-traffic-management-test.yaml

Output:

ciliumclusterwideenvoyconfig.cilium.io/envoy-lb-listener created

This CiliumClusterwideEnvoyConfig creates:

  • 50/50 load balancing between echo-service-1 and echo-service-2
  • URL rewriting that transforms /foo to /
  • Retry policies for failed requests
  • Circuit breaking with outlier detection

Test the configuration:

# The /foo path now works due to rewriting
kubectl exec -it $CLIENT2 -- curl -v echo-service-1:8080/foo

Output:

< HTTP/1.1 200 OK
# But /bar still fails (no rewrite rule)
kubectl exec -it $CLIENT2 -- curl -v echo-service-1:8080/bar

Output:

< HTTP/1.1 403 Forbidden

9. Debugging with pwru

When things don’t work as expected, pwru is invaluable for tracing packets through the kernel:

# Block traffic to 1.1.1.1 for testing
iptables -t filter -I OUTPUT 1 -m tcp --proto tcp --dst 1.1.1.1/32 -j DROP

# Start pwru monitoring
pwru 'dst host 1.1.1.1 and tcp and dst port 80'

In another terminal, try to connect:

curl 1.1.1.1 -v

Output from pwru:

0xffff000004cb58e8 3 curl:8493 4026531840 0 0 0x0800 1500 60 10.0.2.15:60880->1.1.1.1:80(tcp) nf_hook_slow
0xffff000004cb58e8 3 curl:8493 4026531840 0 0 0x0800 1500 60 10.0.2.15:60880->1.1.1.1:80(tcp) kfree_skb_reason(SKB_DROP_REASON_NETFILTER_DROP)

This shows exactly where the packet was dropped (NETFILTER_DROP) and helps identify network policy issues.

10. Key Architectural Insights

The eBPF foundation means Cilium can make routing decisions in kernel space without context switches to userspace. This is why Cilium can handle millions of connections with minimal CPU overhead.

The per-node Envoy model strikes a balance between the feature richness of a full proxy and the efficiency of kernel-based networking. L7 features are available when needed, but L3/L4 traffic bypasses Envoy entirely.

The identity-based security model assigns every workload a cryptographic identity. This identity follows the workload regardless of its IP address, making policies portable across clusters and clouds.

The TPROXY mechanism preserves the original destination, allowing the proxy to see the real target. This is crucial for implementing transparent proxies that don’t break application assumptions.

The performance difference between Cilium and traditional service meshes is significant. In sidecar-based meshes, every request involves multiple kernel-userspace transitions. With Cilium, L3/L4 traffic stays entirely in kernel space. Even for L7 traffic, there’s only one proxy hop (to the per-node Envoy) instead of two (in and out of a sidecar).

Memory usage is also significantly lower. Instead of hundreds of Envoy instances (one per pod), you have just one per node. In a cluster with 1000 pods across 10 nodes, that’s 10 Envoys instead of 1000.

When deploying Cilium Service Mesh in production, resource allocation for the cilium-envoy DaemonSet requires careful planning. Unlike sidecars where you can set per-pod limits, the shared Envoy must handle all L7 traffic for its node.

The choice between dedicated and shared LoadBalancer mode for Ingress affects IP address consumption and traffic isolation. Shared mode is more efficient but requires careful path management to avoid conflicts.

Network policies need thorough testing, especially the interaction between ingress identity and backend services. Remember that policies apply at two points: world-to-ingress and ingress-to-backend.

Observability through Hubble should be configured with appropriate retention and export settings. L7 visibility generates significant data that needs proper management.

Conclusion

Cilium Service Mesh represents a fundamental rethink of how service mesh should work in Kubernetes. By leveraging eBPF for the data plane and using a per-node proxy model, it delivers enterprise features without enterprise overhead. The integration between CNI, Ingress/Gateway API, and service mesh capabilities creates a unified networking layer that’s both powerful and efficient.

The journey from basic connectivity through Ingress, Gateway API, network policies, and L7 traffic management shows how Cilium builds sophisticated capabilities on a solid eBPF foundation. Whether you’re running a handful of services or thousands, Cilium’s architecture scales efficiently while maintaining the observability and control that modern applications demand.

--

--