--- title: "Cómo instalar Cilium CNI en Kubernetes con k3s y Hubble" description: "Instala Cilium como CNI en un clúster k3s sobre Ubuntu 24.04, sustituye kube-proxy con eBPF, aplica políticas de red L7 y observa el tráfico con Hubble." category: "Ecosistema Kubernetes" language: es source: https://cubepath.com/es/docs/ecosistema-kubernetes/instalacion-y-configuracion-de-cilium-cni updated: 2026-09-25 --- # Cómo instalar Cilium CNI en Kubernetes con k3s y Hubble Cilium es un plugin de red (CNI) para Kubernetes que usa eBPF en el kernel de Linux para enrutar el tráfico de los pods, balancear servicios y aplicar políticas de red, incluidas reglas a nivel HTTP. En este tutorial crearás un clúster k3s en Ubuntu 24.04 sin su CNI ni kube-proxy de serie, instalarás Cilium en modo de sustitución de kube-proxy, aplicarás una política L7 y usarás Hubble para ver qué tráfico se permite y cuál se descarta. ## Requisitos previos Para seguir esta guía necesitas: - Un servidor con Ubuntu 24.04 LTS (kernel 6.8, más que suficiente para Cilium), por ejemplo un VPS de CubePath, con al menos 2 vCPU y 4 GB de RAM. - Un usuario no root con privilegios `sudo`. - La IP del servidor, que en la guía se indica como `your_server_ip`. Si más adelante añades nodos, entre ellos deben estar abiertos 6443/TCP (API de Kubernetes), 10250/TCP (kubelet), 8472/UDP (túnel VXLAN de Cilium), 4240/TCP (health checks de Cilium) y 4244/TCP (Hubble). > Nota: si ya tienes un clúster con otro CNI (Flannel, Calico...), no instales Cilium encima. Cambiar de CNI en un clúster en uso requiere un procedimiento de migración específico; esta guía parte de un clúster nuevo. ## Paso 1: Instalar k3s sin CNI ni kube-proxy k3s trae Flannel y kube-proxy por defecto. Los desactivarás para que Cilium se encargue de la red de pods, de las políticas y del balanceo de servicios. Descarga el script de instalación oficial y revísalo antes de ejecutarlo: ```bash curl -sfL https://get.k3s.io -o k3s-install.sh less k3s-install.sh ``` Instala k3s con las opciones necesarias: ```bash sudo INSTALL_K3S_EXEC="--flannel-backend=none --disable-network-policy --disable-kube-proxy" sh k3s-install.sh ``` - `--flannel-backend=none`: no instala Flannel. - `--disable-network-policy`: desactiva el controlador de NetworkPolicy integrado, ya que Cilium las aplica. - `--disable-kube-proxy`: no ejecuta kube-proxy; Cilium lo sustituye. Copia el `kubeconfig` a tu usuario para usar `kubectl` sin `sudo`: ```bash mkdir -p ~/.kube sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config sudo chown "$USER": ~/.kube/config chmod 600 ~/.kube/config ``` Comprueba el nodo. Es normal que aparezca `NotReady` y que los pods de sistema estén en `Pending`, porque aún no hay CNI: ```bash kubectl get nodes ``` ```output NAME STATUS ROLES AGE VERSION cilium1 NotReady control-plane,master 40s v1.3x.x+k3s1 ``` ## Paso 2: Instalar la CLI de Cilium La CLI `cilium` instala Cilium en el clúster, muestra su estado y ejecuta pruebas de conectividad. Descarga la última versión estable junto con su suma de comprobación y verifícala: ```bash CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt) CLI_ARCH=amd64 if [ "$(uname -m)" = "aarch64" ]; then CLI_ARCH=arm64; fi curl -L --fail --remote-name-all "https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-${CLI_ARCH}.tar.gz"{,.sha256sum} sha256sum --check "cilium-linux-${CLI_ARCH}.tar.gz.sha256sum" ``` ```output cilium-linux-amd64.tar.gz: OK ``` Instala el binario y limpia los archivos descargados: ```bash sudo tar xzvfC "cilium-linux-${CLI_ARCH}.tar.gz" /usr/local/bin rm "cilium-linux-${CLI_ARCH}.tar.gz"{,.sha256sum} cilium version --client ``` ## Paso 3: Instalar Cilium en el clúster Como no hay kube-proxy, los agentes de Cilium no pueden llegar a la API de Kubernetes a través del servicio `kubernetes` del clúster: hay que indicarles la IP y el puerto reales del API server. Además, k3s asigna a los pods el rango `10.42.0.0/16`, así que lo configurarás como pool de IPs de Cilium: ```bash cilium install \ --set kubeProxyReplacement=true \ --set k8sServiceHost=your_server_ip \ --set k8sServicePort=6443 \ --set ipam.operator.clusterPoolIPv4PodCIDRList=10.42.0.0/16 ``` La CLI instala la versión de Cilium que corresponde a su versión estable. Espera a que todos los componentes estén listos: ```bash cilium status --wait ``` ```output Cilium: OK Operator: OK Envoy DaemonSet: OK Hubble Relay: disabled ClusterMesh: disabled DaemonSet cilium Desired: 1, Ready: 1/1, Available: 1/1 Deployment cilium-operator Desired: 1, Ready: 1/1, Available: 1/1 ``` El nodo pasa ahora a `Ready` y los pods de sistema de k3s (CoreDNS, Traefik, metrics-server) arrancan: ```bash kubectl get nodes kubectl get pods -n kube-system ``` Comprueba que Cilium está sustituyendo a kube-proxy. Dentro del pod del agente, la herramienta de diagnóstico se llama `cilium-dbg`: ```bash kubectl -n kube-system exec ds/cilium -- cilium-dbg status | grep KubeProxyReplacement ``` ```output KubeProxyReplacement: True [eth0 your_server_ip fe80::... (Direct Routing)] ``` ## Paso 4: Validar la red con la prueba de conectividad `cilium connectivity test` despliega pods de prueba y comprueba decenas de casos: tráfico entre pods, servicios, DNS, salida a Internet y políticas. Tarda varios minutos: ```bash cilium connectivity test ``` ```output [cilium-test-1] All 70 tests (600 actions) successful, 10 tests skipped, 0 scenarios skipped. ``` Algunas pruebas se omiten porque requieren varios nodos o funciones no activadas; lo importante es que no haya fallos. Al terminar, elimina el namespace de pruebas: ```bash kubectl get ns | grep cilium-test kubectl delete ns cilium-test-1 ``` ## Paso 5: Activar Hubble Hubble es la capa de observabilidad de Cilium: registra cada flujo de red con su origen, destino, protocolo y veredicto (permitido o descartado). Activa Hubble Relay y la interfaz web: ```bash cilium hubble enable --ui cilium status --wait ``` Instala la CLI de Hubble con el mismo procedimiento que la de Cilium: ```bash HUBBLE_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/hubble/master/stable.txt) HUBBLE_ARCH=amd64 if [ "$(uname -m)" = "aarch64" ]; then HUBBLE_ARCH=arm64; fi curl -L --fail --remote-name-all "https://github.com/cilium/hubble/releases/download/${HUBBLE_VERSION}/hubble-linux-${HUBBLE_ARCH}.tar.gz"{,.sha256sum} sha256sum --check "hubble-linux-${HUBBLE_ARCH}.tar.gz.sha256sum" sudo tar xzvfC "hubble-linux-${HUBBLE_ARCH}.tar.gz" /usr/local/bin rm "hubble-linux-${HUBBLE_ARCH}.tar.gz"{,.sha256sum} ``` Abre un port-forward hacia Hubble Relay en segundo plano y comprueba que responde: ```bash cilium hubble port-forward & hubble status ``` ```output Healthcheck (via localhost:4245): Ok Current/Max Flows: 4,095/4,095 (100.00%) Flows/s: 12.34 Connected Nodes: 1/1 ``` ## Paso 6: Aplicar una política de red L7 Despliega un servidor web y dos clientes: uno con la etiqueta `app=client`, que tendrá permiso, y otro sin ella: ```bash kubectl create deployment web --image=nginx kubectl expose deployment web --port=80 kubectl run client --image=curlimages/curl --labels=app=client --command -- sleep infinity kubectl run intruder --image=curlimages/curl --command -- sleep infinity kubectl wait --for=condition=Ready pod -l app=web --timeout=120s kubectl wait --for=condition=Ready pod/client pod/intruder --timeout=120s ``` Crea una `CiliumNetworkPolicy` que solo permita a los pods `app=client` hacer peticiones `GET` al puerto 80 de `web`. Al seleccionar los pods de `web`, todo lo demás que les llegue queda bloqueado: ```bash nano web-policy.yaml ``` ```yaml apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy metadata: name: web-allow-get-from-client spec: endpointSelector: matchLabels: app: web ingress: - fromEndpoints: - matchLabels: app: client toPorts: - ports: - port: "80" protocol: TCP rules: http: - method: GET path: "/.*" ``` Aplica la política: ```bash kubectl apply -f web-policy.yaml ``` Comprueba los tres casos. Una petición `GET` desde `client` funciona: ```bash kubectl exec client -- curl -s -o /dev/null -w '%{http_code}\n' http://web ``` ```output 200 ``` Una petición `POST` desde el mismo pod la rechaza el proxy L7 de Cilium con un 403: ```bash kubectl exec client -- curl -s -o /dev/null -w '%{http_code}\n' -X POST http://web ``` ```output 403 ``` Y desde `intruder` la conexión ni siquiera se establece, así que `curl` agota el tiempo de espera: ```bash kubectl exec intruder -- curl -s --max-time 5 http://web ``` ```output command terminated with exit code 28 ``` ## Paso 7: Observar el tráfico con Hubble Consulta los flujos descartados de los últimos minutos. Verás los paquetes de `intruder` bloqueados por la política: ```bash hubble observe --verdict DROPPED --last 20 ``` ```output Sep 25 12:10:03.114: default/intruder:40512 (ID:12345) <> default/web-5d8f9c7b6-x2k4p:80 (ID:23456) policy-verdict:none INGRESS DENIED (TCP Flags: SYN) Sep 25 12:10:03.114: default/intruder:40512 (ID:12345) <> default/web-5d8f9c7b6-x2k4p:80 (ID:23456) Policy denied DROPPED (TCP Flags: SYN) ``` Y las peticiones HTTP que ha inspeccionado el proxy L7, incluido el `POST` denegado: ```bash hubble observe --protocol http --last 20 ``` Para la interfaz gráfica, abre un port-forward hacia Hubble UI; la CLI te indica la URL local (por defecto `http://localhost:12000`): ```bash cilium hubble ui ``` Si el clúster está en un servidor remoto, crea antes un túnel SSH desde tu equipo con `ssh -L 12000:localhost:12000 your_user@your_server_ip` y abre esa URL en tu navegador. ## Solución de problemas **Los pods de Cilium están en `CrashLoopBackOff` y los logs hablan de la API de Kubernetes.** Casi siempre es un `k8sServiceHost` incorrecto. Revisa con `kubectl -n kube-system logs ds/cilium` y reinstala con la IP correcta: `cilium uninstall` y de nuevo `cilium install`. **Los pods reciben IPs fuera de `10.42.0.0/16` o hay problemas de enrutado.** Comprueba el valor de `ipam.operator.clusterPoolIPv4PodCIDRList` con `cilium config view | grep cluster-pool`. **Una política bloquea tráfico que esperabas permitido.** Usa `hubble observe --verdict DROPPED --namespace ` para ver qué origen y destino se están descartando, y recuerda que en cuanto una política selecciona un pod, todo lo que no se permita explícitamente en esa dirección queda denegado, incluido DNS si es una política de salida. ## Conclusión Has creado un clúster k3s sin Flannel ni kube-proxy, instalado Cilium con sustitución de kube-proxy por eBPF, aplicado una política que filtra por método HTTP y verificado con Hubble qué tráfico se permite y cuál se descarta. Como siguientes pasos puedes añadir nodos worker al clúster, activar el cifrado transparente entre nodos con WireGuard (`encryption.enabled=true` y `encryption.type=wireguard`) y exportar las métricas de Cilium y Hubble a tu sistema de monitorización.