Introduction
In the dynamic world of Kubernetes, understanding the intricate web of network communication between your services is paramount. As applications scale and microservices proliferate, traditional network monitoring tools often fall short, struggling to provide granular, real-time insights into pod-to-pod, pod-to-external, and even internal node traffic. This visibility gap can lead to significant challenges in debugging connectivity issues, enforcing security policies, and optimizing application performance.
Enter Cilium and its powerful companion, Hubble. Cilium, a CNI (Container Network Interface) powered by eBPF, revolutionizes Kubernetes networking by providing advanced features like identity-based security, transparent encryption, and efficient load balancing. Hubble, built on top of Cilium’s eBPF data plane, unlocks unparalleled network observability. It transforms raw network events into actionable insights, offering a comprehensive view of all network flows within your cluster. Imagine a real-time map of your Kubernetes network, showing every connection, its status, and even application-layer protocols – that’s the power of Hubble.
This guide will walk you through the process of installing Cilium with Hubble, exploring its core features, and leveraging its capabilities for network visualization and troubleshooting. We’ll delve into practical examples, from basic flow inspection to advanced policy debugging, ensuring you gain a deep understanding of how to make your Kubernetes network truly transparent. For those interested in deeper eBPF insights, our article on eBPF Observability: Building Custom Metrics with Hubble provides further context.
TL;DR: Cilium Hubble Network Observability
Cilium with Hubble provides deep network observability for Kubernetes using eBPF. It allows you to visualize network flows, troubleshoot connectivity, and monitor policy enforcement in real-time.
Key Commands:
- Install Cilium with Hubble:
helm repo add cilium https://helm.cilium.io/
helm install cilium cilium/cilium --version 1.15.4 \
--namespace kube-system \
--set hubble.enabled=true \
--set hubble.ui.enabled=true \
--set operator.replicas=1
curl -L --remote-name-all https://github.com/cilium/hubble/releases/latest/download/hubble-linux-amd64.tar.gz
sudo tar xzF hubble-linux-amd64.tar.gz -C /usr/local/bin
rm hubble-linux-amd64.tar.gz
cilium hubble enable
hubble ui
hubble observe
hubble observe -n my-app-namespace
Prerequisites
Before diving into the installation and configuration of Cilium Hubble, ensure you have the following prerequisites in place:
- Kubernetes Cluster: A running Kubernetes cluster (version 1.20+ recommended). This guide assumes you have
kubectlconfigured and pointing to your cluster. You can use any Kubernetes distribution, including Kind, K3s, Minikube, or a cloud-managed service like GKE, EKS, or AKS. - Helm: Helm 3 installed on your local machine. We will use Helm for installing Cilium.
- kubectl: The Kubernetes command-line tool (
kubectl) installed and configured to interact with your cluster. - Basic Kubernetes Knowledge: Familiarity with Kubernetes concepts such as Pods, Deployments, Services, and Namespaces.
- Basic Networking Knowledge: An understanding of fundamental networking concepts like TCP/IP, ports, and protocols will be helpful.
Step-by-Step Guide
1. Install Cilium with Hubble
The first step is to install Cilium as your CNI plugin and enable Hubble components during the installation. We’ll use Helm for a straightforward deployment. It’s crucial to enable hubble.enabled and hubble.ui.enabled to get the full observability suite.
For production environments, consider adjusting resource requests/limits and replica counts for the Cilium and Hubble components based on your cluster size and traffic patterns. You can find more advanced configuration options in the official Cilium documentation.
helm repo add cilium https://helm.cilium.io/
helm repo update
helm install cilium cilium/cilium --version 1.15.4 \
--namespace kube-system \
--set hubble.enabled=true \
--set hubble.ui.enabled=true \
--set operator.replicas=1 \
--set ipam.mode=clusterPool \
--set cluster.name=kubezilla-cluster \
--set k8sServiceHost=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}') \
--set k8sServicePort=$(kubectl get service kubernetes -n default -o jsonpath='{.spec.ports[0].port}')
Explanation:
helm repo add cilium https://helm.cilium.io/: Adds the Cilium Helm repository.helm repo update: Updates your local Helm chart repository cache.helm install cilium cilium/cilium --version 1.15.4: Installs the Cilium chart. We specify a version for reproducibility; always check the latest stable version.--namespace kube-system: Deploys Cilium into thekube-systemnamespace, which is standard for CNI plugins.--set hubble.enabled=true: Enables the Hubble server, which collects and stores network flow data.--set hubble.ui.enabled=true: Deploys the Hubble UI, a web-based graphical interface for visualizing flows.--set operator.replicas=1: Sets the Cilium Operator replica count. For high availability, you might increase this.--set ipam.mode=clusterPool: Configures Cilium’s IP Address Management mode.clusterPoolis a common choice for simplicity.--set cluster.name=kubezilla-cluster: Assigns a name to your cluster, useful in multi-cluster environments.--set k8sServiceHostandk8sServicePort: These ensure Cilium can connect to the Kubernetes API server. The provided commands dynamically fetch the internal IP and port of the Kubernetes API service.
Verify:
Check if all Cilium and Hubble pods are running in the kube-system namespace. It might take a few minutes for all pods to transition to the Running state.
kubectl get pods -n kube-system -l k8s-app=cilium
Expected Output:
NAME READY STATUS RESTARTS AGE
cilium-xxxx 1/1 Running 0 2m
cilium-operator-xxxx 1/1 Running 0 2m
hubble-relay-xxxx 1/1 Running 0 2m
hubble-ui-xxxx 1/1 Running 0 2m
You should see cilium daemonset pods (one per node), the cilium-operator, hubble-relay, and hubble-ui pods all in the Running state.
2. Install Hubble CLI
The Hubble CLI (hubble command) is an essential tool for interacting with Hubble. It allows you to query network flows, observe real-time traffic, and debug connectivity directly from your terminal. This client-side tool connects to the hubble-relay service running in your cluster.
curl -L --remote-name-all https://github.com/cilium/hubble/releases/latest/download/hubble-linux-amd64.tar.gz
sudo tar xzF hubble-linux-amd64.tar.gz -C /usr/local/bin
rm hubble-linux-amd64.tar.gz
For macOS, replace hubble-linux-amd64.tar.gz with hubble-darwin-amd64.tar.gz or hubble-darwin-arm64.tar.gz depending on your architecture. Refer to the Hubble installation guide for other architectures.
Explanation:
curl -L --remote-name-all ...: Downloads the latest Hubble CLI binary for Linux AMD64 from the official Hubble GitHub releases.sudo tar xzF ... -C /usr/local/bin: Extracts the downloaded archive and places thehubbleexecutable in/usr/local/bin, making it accessible in your system’s PATH.rm ...: Cleans up the downloaded archive.
Verify:
Check if the Hubble CLI is installed correctly by running the hubble version command.
hubble version
Expected Output:
hubble v0.12.0 (hubble-cli)
commit: d8a9f0e
goarch: amd64
goos: linux
built: 2024-04-20T10:00:00Z
The version number might differ based on the latest release, but you should see similar output confirming the CLI is installed.
3. Enable Hubble Relay
While we enabled Hubble during the Cilium installation, the hubble CLI needs to know how to connect to the hubble-relay service. The cilium hubble enable command sets up the necessary port-forwarding or configuration for the CLI to communicate with the relay.
The hubble-relay component aggregates flow data from all Cilium agents in the cluster, providing a single endpoint for the Hubble UI and CLI to query. This is essential for cluster-wide network observability.
cilium hubble enable
Explanation:
cilium hubble enable: This command ensures that thehubble-relayservice is accessible. By default, it might set up a local proxy or configure your~/.hubble/config.yamlto point to the service.
Verify:
Confirm that Hubble Relay is accessible by listing active flows. It might show no flows initially if there’s no traffic yet.
hubble observe --last 5
Expected Output (example, might be empty if no traffic):
Apr 22 10:30:01.000: default/pod-a:8080 <-> default/pod-b:5432 (TCP)
Apr 22 10:30:02.000: kube-system/hubble-relay:443 <-> kube-system/hubble-ui:8080 (TCP)
...
If you see some output, even internal Cilium/Hubble traffic, it means the relay is working. If you get an error, ensure all Hubble pods are running and try again.
4. Deploy Sample Applications
To generate some network traffic and effectively demonstrate Hubble’s capabilities, let’s deploy a simple client-server application. We’ll use a curl client and a netcat server in separate namespaces to simulate inter-service communication.
This setup will allow us to observe traffic between different namespaces, which is a common scenario in Kubernetes. For more advanced networking scenarios, consider exploring Kubernetes Gateway API for traffic management.
# server.yaml
apiVersion: v1
kind: Namespace
metadata:
name: server-ns
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: netcat-server
namespace: server-ns
spec:
replicas: 1
selector:
matchLabels:
app: netcat-server
template:
metadata:
labels:
app: netcat-server
spec:
containers:
- name: netcat-server
image: alpine/git
command: ["sh", "-c"]
args: ["apk add --no-cache netcat-openbsd && while true; do echo 'Hello from Netcat Server!' | nc -lp 8080; done"]
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: netcat-service
namespace: server-ns
spec:
selector:
app: netcat-server
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: ClusterIP
# client.yaml
apiVersion: v1
kind: Namespace
metadata:
name: client-ns
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: curl-client
namespace: client-ns
spec:
replicas: 1
selector:
matchLabels:
app: curl-client
template:
metadata:
labels:
app: curl-client
spec:
containers:
- name: curl-client
image: curlimages/curl
command: ["sleep", "3600"] # Keep the pod running
ports:
- containerPort: 80
kubectl apply -f server.yaml
kubectl apply -f client.yaml
Explanation:
- The
server.yamldefines aserver-nsnamespace, aDeploymentrunning analpine/gitimage that installsnetcat-openbsdand listens on port 8080, and aServiceto expose it on port 80. - The
client.yamldefines aclient-nsnamespace and aDeploymentrunning acurlimages/curlimage. This pod will be used to send requests to the server.
Verify:
Check if the pods are running in their respective namespaces.
kubectl get pods -n server-ns
kubectl get pods -n client-ns
Expected Output:
NAME READY STATUS RESTARTS AGE
netcat-server-xxxx 1/1 Running 0 1m
NAME READY STATUS RESTARTS AGE
curl-client-xxxx 1/1 Running 0 1m
5. Observe Network Flows with Hubble CLI
Now that we have applications generating traffic, we can use the Hubble CLI to observe these network flows in real-time. This is where Hubble’s power truly shines, providing granular details about every connection.
Let’s first generate some traffic from our client to the server and then observe it.
# Get the client pod name
CLIENT_POD=$(kubectl get pod -n client-ns -l app=curl-client -o jsonpath='{.items[0].metadata.name}')
# Send a request from the client to the server
kubectl exec -it $CLIENT_POD -n client-ns -- curl -m 3 netcat-service.server-ns.svc.cluster.local
Expected Output from curl command:
Hello from Netcat Server!
Now, let’s observe the flows:
hubble observe -f --last 5 --namespace client-ns,server-ns
Explanation:
hubble observe: Starts observing network flows.-f: Follows the output, streaming new flows as they occur.--last 5: Shows the last 5 flows when starting.--namespace client-ns,server-ns: Filters flows to only show those originating from or destined for pods in these namespaces.
Expected Output (truncated example):
Apr 22 10:35:10.123: client-ns/curl-client:34567 <=> server-ns/netcat-server:8080 L3/L4 ACCEPTED (TCP)
Apr 22 10:35:10.124: client-ns/curl-client:34567 <-> server-ns/netcat-service/80 (TCP)
Apr 22 10:35:10.125: client-ns/curl-client:34567 <-> server-ns/netcat-server:8080 L7 OK (HTTP/1.1)
...
You should see flows indicating the communication between curl-client and netcat-server, potentially including DNS lookups and TCP connections. Hubble shows the source and destination pods, ports, protocols, and the verdict (e.g., ACCEPTED) from Cilium’s policy engine.
6. Visualize Flows with Hubble UI
While the CLI is powerful, the Hubble UI provides an intuitive graphical interface for visualizing network flows, services, and even policy enforcement. It’s excellent for getting a high-level overview or drilling down into specific connections.
hubble ui
Explanation:
hubble ui: This command automatically sets up a local port-forward to thehubble-uiservice in your cluster and opens your web browser to the UI. It typically runs onhttp://localhost:12000.
Verify:
Your web browser should open to the Hubble UI. You’ll see a dashboard with various views:
- Flows: A real-time stream of network flows, similar to
hubble observebut with more details and filtering options. - Service Map: A graphical representation of services and their dependencies, showing communication patterns. This is incredibly useful for understanding microservice interactions.
- Policies: View active Cilium Network Policies and their enforcement status.
Navigate to the “Flows” tab and observe the traffic between client-ns/curl-client and server-ns/netcat-server. You can filter by namespace, source/destination, and verdict. The “Service Map” will show connections between the curl-client and netcat-service.
For more detailed information on network policies and their security implications, refer to our Kubernetes Network Policies: Complete Security Hardening Guide.
7. Filter and Analyze Flows
Hubble’s filtering capabilities are crucial for pinpointing specific traffic. You can filter by source/destination, namespace, IP, port, protocol, and even DNS names.
Let’s make another request and then filter specifically for flows related to our netcat-server.
# Get the client pod name
CLIENT_POD=$(kubectl get pod -n client-ns -l app=curl-client -o jsonpath='{.items[0].metadata.name}')
# Send another request to generate traffic
kubectl exec -it $CLIENT_POD -n client-ns -- curl -m 3 netcat-service.server-ns.svc.cluster.local
Now, filter flows using the CLI:
hubble observe --last 10 --pod netcat-server -n server-ns
Explanation:
--pod netcat-server: Filters flows where the source or destination pod has “netcat-server” in its name.-n server-ns: Further restricts the search to theserver-nsnamespace.
Expected Output (example):
Apr 22 10:40:05.123: client-ns/curl-client:45678 <=> server-ns/netcat-server:8080 L3/L4 ACCEPTED (TCP)
Apr 22 10:40:05.124: client-ns/curl-client:45678 <-> server-ns/netcat-service/80 (TCP)
Apr 22 10:40:05.125: client-ns/curl-client:45678 <-> server-ns/netcat-server:8080 L7 OK (HTTP/1.1)
...
You should see flows specifically related to the netcat-server, including the incoming connection from the curl-client. The L7 OK (HTTP/1.1) indicates that Hubble can even parse application-layer protocols, providing deep insights.
8. Debugging Network Policies with Hubble
One of Hubble’s most powerful features is its ability to visualize and debug Cilium Network Policies. When a policy drops traffic, Hubble provides a clear “DENIED” verdict and the reason for the drop. This is invaluable for troubleshooting connectivity issues caused by misconfigured policies.
Let’s create a Cilium Network Policy that denies ingress traffic to our netcat-server from the client-ns namespace.
# deny-policy.yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: deny-ingress-to-netcat
namespace: server-ns
spec:
endpointSelector:
matchLabels:
app: netcat-server
ingress:
- fromEndpoints:
- matchLabels:
io.kubernetes.pod.namespace: client-ns # Selects pods in client-ns
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
- {} # Deny all HTTP requests to port 8080 from client-ns
kubectl apply -f deny-policy.yaml
Explanation:
- This policy, named
deny-ingress-to-netcat, targets pods with the labelapp: netcat-serverin theserver-nsnamespace. - The
ingressrule specifies that trafficfromEndpointswith the labelio.kubernetes.pod.namespace: client-ns(i.e., pods in theclient-nsnamespace) to port8080TCP should be subject to HTTP rules. An emptyhttp: - {}rule effectively denies all HTTP traffic.
Verify:
Now, try to access the server again from the client, and observe the flows with Hubble.
# Get the client pod name
CLIENT_POD=$(kubectl get pod -n client-ns -l app=curl-client -o jsonpath='{.items[0].metadata.name}')
# Attempt to send a request (it should fail)
kubectl exec -it $CLIENT_POD -n client-ns -- curl -m 3 netcat-service.server-ns.svc.cluster.local
Expected Output from curl command:
curl: (28) Connection timed out after 3001 milliseconds
Now, observe the flows with Hubble, specifically looking for denied flows:
hubble observe --last 10 --pod netcat-server -n server-ns --verdict DENIED
Expected Output (example):
Apr 22 10:45:01.123: client-ns/curl-client:56789 <=> server-ns/netcat-server:8080 L3/L4 DENIED (TCP) policy-denied
Policy: deny-ingress-to-netcat (server-ns)
Labels: {k8s:app=netcat-server k8s:io.kubernetes.pod.namespace=server-ns}
Source: {k8s:app=curl-client k8s:io.kubernetes.pod.namespace=client-ns}
...
You should clearly see a DENIED verdict for the connection attempt, with the specific policy (deny-ingress-to-netcat) that caused the denial. This level of detail is invaluable for debugging network policy issues. For more advanced Cilium features like WireGuard encryption, check out our guide on Cilium WireGuard Encryption for Pod-to-Pod Traffic.
Production Considerations
Deploying Cilium Hubble in a production environment requires careful planning to ensure scalability, reliability, and security.
- Resource Allocation: Hubble Relay and Hubble UI can consume significant CPU and memory, especially in large clusters with high traffic. Monitor their resource usage and adjust requests/limits accordingly. Consider increasing
hubble-relayreplicas for high availability. - Data Retention: Hubble Relay stores flow data in memory by default. For longer retention, consider integrating with external storage solutions like Prometheus and Grafana or a dedicated flow export mechanism. Cilium can export flows in various formats, including IPFIX.
- Security and Access Control:
- Hubble UI: Access to the Hubble UI should be restricted. Consider placing it behind an Ingress with authentication (e.g., OAuth2 proxy) or using a VPN.
- Hubble CLI: Ensure that users with
hubbleCLI access have appropriate RBAC permissions if they are using a non-adminkubeconfig. ThehubbleCLI typically uses the currentkubeconfigcontext.
- Scalability: In very large clusters, the amount of flow data can be overwhelming. Hubble provides filtering at the agent level to reduce the data sent to the relay. Configure this carefully to avoid dropping essential data while managing load.
- Monitoring and Alerting: Monitor the health of Cilium and Hubble components using standard Kubernetes monitoring tools. Set up alerts for pod failures, high resource usage, or persistent network policy denials.
- Upgrade Strategy: Always follow the official Cilium upgrade guide. Test upgrades in a staging environment before applying them to production.
- Integration with Observability Stacks: Hubble can complement existing observability tools. Integrate flow data with your SIEM for security analytics or with your metrics/logging platforms for comprehensive operational insights.
- Network Performance: While eBPF is highly efficient, enabling deep packet inspection (e.g., L7 parsing) can introduce a slight overhead. Monitor network latency and throughput after enabling advanced Hubble features.
Troubleshooting
-
Hubble CLI Cannot Connect to Relay
Issue: When running
hubble observeorhubble ui, you get an error like “Error: unable to connect to Hubble Relay: dial tcp 127.0.0.1:4245: connect: connection refused”.Solution:
This usually means the
hubble-relaypod is not running, or the CLI cannot establish a connection. First, check thehubble-relaypod status:kubectl get pods -n kube-system -l k8s-app=hubble-relayIf it’s not running, check its logs for errors. If it is running, try re-enabling Hubble to ensure the port-forward or configuration is correctly set up:
cilium hubble disable cilium hubble enableAlso, ensure no other process is using port 4245 on your local machine (the default port for
hubble uiandhubble observeto connect to the local proxy). -
No Flows Visible in Hubble UI/CLI
Issue: All Hubble pods are running, but you don’t see any network flows, even after generating traffic.
Solution:
Verify that Cilium is correctly installed as the CNI and is handling network traffic. Check the Cilium agent logs for any errors:
kubectl logs -n kube-system -l k8s-app=cilium --tail 100Ensure that
hubble.enabled=truewas set during Cilium installation. If not, you might need to upgrade your Cilium installation:helm upgrade cilium cilium/cilium --version 1.15.4 \ --namespace kube-system \ --reuse-values \ --set hubble.enabled=true \ --set hubble.ui.enabled=trueAlso, check the
hubble-relaylogs for any issues receiving flows from the agents. -
Hubble UI Not Loading or Showing Errors
Issue: The Hubble UI page is blank, shows a loading spinner indefinitely, or displays connection errors.
Solution:
First, check the status of the
hubble-uipod:kubectl get pods -n kube-system -l k8s-app=hubble-ui
