--- title: "Cómo instalar Knative en Kubernetes con Kourier" description: "Instala Knative Serving y Eventing en Kubernetes con Kourier, despliega un servicio que escala a cero, reparte tráfico entre revisiones y enruta eventos." category: "Serverless en VPS" language: es source: https://cubepath.com/es/docs/serverless-en-vps/instalacion-de-knative-en-kubernetes updated: 2026-09-25 --- # Cómo instalar Knative en Kubernetes con Kourier Knative añade a Kubernetes un modelo serverless: con **Knative Serving** despliegas un contenedor y obtienes URL, autoescalado por peticiones (incluido el escalado a cero) y revisiones inmutables entre las que repartir tráfico; con **Knative Eventing** conectas productores y consumidores de eventos en formato CloudEvents. En esta guía instalarás ambos componentes con Kourier como capa de red, sobre un clúster k3s en Ubuntu 24.04 o sobre cualquier clúster existente, y los probarás con un despliegue canary y un broker de eventos. ## Requisitos previos - Un clúster de Kubernetes con soporte para servicios `LoadBalancer` y `kubectl` configurado con permisos de administrador. Si no tienes uno, el paso 1 crea un clúster k3s de un nodo en un servidor con Ubuntu 24.04, por ejemplo un VPS de CubePath con al menos 4 GB de RAM y 2 vCPU. - Un usuario no root con privilegios `sudo` en ese servidor. - Opcional: un dominio en el que puedas crear un registro DNS comodín. Para pruebas se usa sslip.io, que no requiere configurar DNS. Knative publica cada versión con una lista de versiones de Kubernetes soportadas. Esta guía usa Knative 1.18; comprueba en las notas de la [última versión](https://github.com/knative/serving/releases) si hay una más reciente y qué versión mínima de Kubernetes necesita. ## Paso 1: Crear un clúster k3s (opcional) Si ya tienes un clúster, salta al paso 2. k3s es una distribución ligera de Kubernetes que se instala con un script. Se desactiva Traefik, el ingress que incluye por defecto, porque Kourier necesita los puertos 80 y 443 del nodo. Configura el cortafuegos. Además de SSH, HTTP y HTTPS, k3s necesita que UFW permita el tráfico de las redes internas de pods (`10.42.0.0/16`) y de servicios (`10.43.0.0/16`); sin estas reglas los pods no pueden comunicarse entre sí. La API de Kubernetes (6443) queda cerrada: gestiona el clúster desde el propio servidor o por un túnel SSH. ```bash sudo ufw allow OpenSSH sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw allow from 10.42.0.0/16 to any sudo ufw allow from 10.43.0.0/16 to any sudo ufw enable ``` Descarga el script de instalación, revísalo y ejecútalo: ```bash curl -sfL https://get.k3s.io -o install-k3s.sh less install-k3s.sh sh install-k3s.sh --disable traefik ``` Configura `kubectl` para tu usuario (k3s instala el binario en `/usr/local/bin/kubectl`): ```bash mkdir -p ~/.kube sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config sudo chown "$USER": ~/.kube/config chmod 600 ~/.kube/config kubectl get nodes ``` ```output NAME STATUS ROLES AGE VERSION knative Ready control-plane,master 40s v1.33.4+k3s1 ``` ## Paso 2: Instalar Knative Serving Define la versión en una variable para no repetirla en cada URL: ```bash export KNATIVE_VERSION=knative-v1.18.0 ``` Instala primero las definiciones de recursos (CRD) y después los componentes principales: ```bash kubectl apply -f https://github.com/knative/serving/releases/download/${KNATIVE_VERSION}/serving-crds.yaml kubectl apply -f https://github.com/knative/serving/releases/download/${KNATIVE_VERSION}/serving-core.yaml ``` Espera a que todos los componentes estén listos: ```bash kubectl wait --for=condition=Available deployment --all -n knative-serving --timeout=300s kubectl get pods -n knative-serving ``` ```output NAME READY STATUS RESTARTS AGE activator-6cf5b8d7c9-7lq2x 1/1 Running 0 60s autoscaler-58b8c6d8f7-t2k4m 1/1 Running 0 60s controller-7d9f6c7b8d-xz9vn 1/1 Running 0 60s webhook-5f8b9c6d4b-q8wzl 1/1 Running 0 60s ``` ## Paso 3: Instalar Kourier como capa de red Knative necesita una capa de red que reciba las peticiones y las enrute a la revisión correcta. Kourier es la opción más ligera y la recomendada si no usas ya Istio o Contour. Instálala: ```bash kubectl apply -f https://github.com/knative/net-kourier/releases/download/${KNATIVE_VERSION}/kourier.yaml ``` Indica a Knative Serving que use Kourier: ```bash kubectl patch configmap/config-network \ --namespace knative-serving \ --type merge \ --patch '{"data":{"ingress-class":"kourier.ingress.networking.knative.dev"}}' ``` Comprueba que el servicio de Kourier tiene una IP externa asignada: ```bash kubectl get service kourier -n kourier-system ``` ```output NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kourier LoadBalancer 10.43.112.57 203.0.113.10 80:31080/TCP,443:31443/TCP 30s ``` Si `EXTERNAL-IP` se queda en ``, tu clúster no tiene un balanceador que atienda servicios `LoadBalancer`. En k3s lo proporciona ServiceLB; en otros clústeres necesitarás MetalLB o el balanceador de tu proveedor. ## Paso 4: Configurar el dominio Cada servicio de Knative recibe una URL con el formato `..`. Elige una de estas dos opciones. **Para pruebas, con sslip.io.** Knative incluye un Job que configura el dominio `.sslip.io`, un DNS público que resuelve cualquier nombre que contenga una IP a esa IP: ```bash kubectl apply -f https://github.com/knative/serving/releases/download/${KNATIVE_VERSION}/serving-default-domain.yaml ``` **Para producción, con tu dominio.** Crea en tu proveedor DNS un registro A comodín `*.your_domain` que apunte a la `EXTERNAL-IP` de Kourier y configura el dominio en Knative: ```bash kubectl patch configmap/config-domain \ --namespace knative-serving \ --type merge \ --patch '{"data":{"your_domain":""}}' ``` Con esta configuración, un servicio `hola` en el namespace `default` tendrá la URL `http://hola.default.your_domain`. ## Paso 5: Instalar la CLI kn `kn` es la CLI de Knative. No es imprescindible (todo se puede hacer con manifiestos y `kubectl`), pero simplifica mucho consultar revisiones y tráfico. Descárgala para tu arquitectura (usa `kn-linux-arm64` en ARM): ```bash curl -fsSLo kn https://github.com/knative/client/releases/download/${KNATIVE_VERSION}/kn-linux-amd64 sudo install -m 755 kn /usr/local/bin/kn rm kn kn version ``` ## Paso 6: Desplegar el primer servicio Un `Service` de Knative (abreviado `ksvc`) describe el contenedor a ejecutar; Knative crea a partir de él la revisión, el deployment, el autoescalado y la ruta. Crea el manifiesto: ```bash nano hola-v1.yaml ``` ```yaml apiVersion: serving.knative.dev/v1 kind: Service metadata: name: hola namespace: default spec: template: metadata: name: hola-v1 annotations: autoscaling.knative.dev/min-scale: "0" autoscaling.knative.dev/max-scale: "10" autoscaling.knative.dev/target: "50" spec: containers: - image: ghcr.io/knative/helloworld-go:latest ports: - containerPort: 8080 env: - name: TARGET value: "v1" resources: requests: cpu: 100m memory: 64Mi limits: memory: 256Mi ``` Las anotaciones controlan el autoescalador de Knative (KPA): - `min-scale: "0"` permite escalar a cero cuando no hay tráfico. - `max-scale: "10"` pone un techo de réplicas. - `target: "50"` es la concurrencia objetivo: el autoescalador añade réplicas cuando cada una atiende de media más de 50 peticiones simultáneas. El nombre de la revisión (`hola-v1`) es opcional, pero ponerlo facilita el reparto de tráfico del paso 8. Debe empezar por el nombre del servicio seguido de un guion. Aplica el manifiesto y espera a que el servicio esté listo: ```bash kubectl apply -f hola-v1.yaml kubectl wait --for=condition=Ready ksvc/hola --timeout=120s kn service list ``` ```output NAME URL LATEST AGE CONDITIONS READY REASON hola http://hola.default.203.0.113.10.sslip.io hola-v1 25s 3 OK / 3 True ``` Haz una petición: ```bash curl "$(kubectl get ksvc hola -o jsonpath='{.status.url}')" ``` ```output Hello v1! ``` ## Paso 7: Comprobar el escalado a cero Observa los pods del servicio en una terminal: ```bash kubectl get pods -l serving.knative.dev/service=hola -w ``` Tras aproximadamente un minuto sin peticiones, el pod pasa a `Terminating` y desaparece. Haz otra petición con `curl` desde otra terminal: verás cómo se crea un pod nuevo. Esa primera petición tarda algo más (arranque en frío) porque el activator de Knative la retiene hasta que el pod está listo; las siguientes son inmediatas. El tiempo de espera antes de escalar a cero se configura de forma global en el ConfigMap `config-autoscaler`. Por ejemplo, para esperar dos minutos: ```bash kubectl patch configmap/config-autoscaler \ --namespace knative-serving \ --type merge \ --patch '{"data":{"scale-to-zero-grace-period":"120s"}}' ``` Para un servicio concreto que no deba sufrir arranques en frío, pon `autoscaling.knative.dev/min-scale: "1"` en su plantilla. ## Paso 8: Dividir tráfico entre revisiones Cada cambio en `spec.template` crea una revisión nueva e inmutable. Por defecto, todo el tráfico va a la última, pero puedes repartirlo para hacer un despliegue canary. Crea la versión 2 enviándole solo el 20 % del tráfico: ```bash nano hola-v2.yaml ``` ```yaml apiVersion: serving.knative.dev/v1 kind: Service metadata: name: hola namespace: default spec: template: metadata: name: hola-v2 annotations: autoscaling.knative.dev/min-scale: "0" autoscaling.knative.dev/max-scale: "10" autoscaling.knative.dev/target: "50" spec: containers: - image: ghcr.io/knative/helloworld-go:latest ports: - containerPort: 8080 env: - name: TARGET value: "v2" traffic: - revisionName: hola-v1 percent: 80 tag: estable - revisionName: hola-v2 percent: 20 tag: canary ``` Aplica el cambio y lanza varias peticiones: ```bash kubectl apply -f hola-v2.yaml kubectl wait --for=condition=Ready ksvc/hola --timeout=120s URL=$(kubectl get ksvc hola -o jsonpath='{.status.url}') for i in $(seq 1 10); do curl -s "$URL"; done ``` Aproximadamente dos de cada diez respuestas serán `Hello v2!`. Las etiquetas (`tag`) crean además una URL fija para cada revisión, útil para probar la versión canary directamente: ```bash kn service describe hola ``` ```output ... Revisions: 20% @latest (hola-v2) #canary [2] (30s) ... 80% hola-v1 #estable [1] (2m) ... ``` La revisión canary responde siempre en `http://canary-hola.default.203.0.113.10.sslip.io`. Cuando la valides, pasa todo el tráfico a la nueva revisión: ```bash kn service update hola --traffic hola-v2=100 ``` Si algo va mal, volver atrás es igual de rápido: `kn service update hola --traffic hola-v1=100`. La revisión antigua sigue existiendo, así que no hay que reconstruir ni volver a desplegar nada. ## Paso 9: Instalar Knative Eventing Knative Eventing permite que los servicios reaccionen a eventos sin conocer quién los produce. Instala los CRD, el núcleo, un canal en memoria y el broker basado en canales: ```bash kubectl apply -f https://github.com/knative/eventing/releases/download/${KNATIVE_VERSION}/eventing-crds.yaml kubectl apply -f https://github.com/knative/eventing/releases/download/${KNATIVE_VERSION}/eventing-core.yaml kubectl apply -f https://github.com/knative/eventing/releases/download/${KNATIVE_VERSION}/in-memory-channel.yaml kubectl apply -f https://github.com/knative/eventing/releases/download/${KNATIVE_VERSION}/mt-channel-broker.yaml kubectl wait --for=condition=Available deployment --all -n knative-eventing --timeout=300s ``` > Advertencia: el canal en memoria (InMemoryChannel) pierde los eventos pendientes si se reinicia su pod. Sirve para desarrollo y pruebas; en producción usa un broker respaldado por Kafka o RabbitMQ, que Knative ofrece como componentes aparte. ## Paso 10: Enrutar eventos con un broker y un trigger El flujo es: los productores envían eventos al **broker**, y cada **trigger** filtra por atributos (por ejemplo, el tipo de evento) y los entrega a un suscriptor. Crea los tres recursos en un solo manifiesto: ```bash nano eventos.yaml ``` ```yaml apiVersion: eventing.knative.dev/v1 kind: Broker metadata: name: default namespace: default --- apiVersion: serving.knative.dev/v1 kind: Service metadata: name: event-display namespace: default spec: template: metadata: annotations: autoscaling.knative.dev/min-scale: "1" spec: containers: - image: gcr.io/knative-releases/knative.dev/eventing/cmd/event_display --- apiVersion: eventing.knative.dev/v1 kind: Trigger metadata: name: pedidos-creados namespace: default spec: broker: default filter: attributes: type: com.ejemplo.pedido.creado subscriber: ref: apiVersion: serving.knative.dev/v1 kind: Service name: event-display ``` `event-display` es una imagen de ejemplo de Knative que imprime en su log cada evento recibido. Aplica el manifiesto y comprueba que el broker y el trigger están listos: ```bash kubectl apply -f eventos.yaml kubectl wait --for=condition=Ready broker/default trigger/pedidos-creados --timeout=120s kubectl get broker default ``` ```output NAME URL AGE READY REASON default http://broker-ingress.knative-eventing.svc.cluster.local/default/default 40s True ``` El broker solo es accesible desde dentro del clúster. Envía un evento con un pod temporal de curl, usando las cabeceras `Ce-*` del formato binario de CloudEvents: ```bash kubectl run curl --image=curlimages/curl --rm -it --restart=Never -- \ -s -o /dev/null -w "%{http_code}\n" -X POST \ http://broker-ingress.knative-eventing.svc.cluster.local/default/default \ -H "Ce-Id: 1" \ -H "Ce-Specversion: 1.0" \ -H "Ce-Type: com.ejemplo.pedido.creado" \ -H "Ce-Source: prueba-manual" \ -H "Content-Type: application/json" \ -d '{"pedidoId": "42"}' ``` El broker responde `202` al aceptar el evento. Revisa el log del suscriptor: ```bash kubectl logs -l serving.knative.dev/service=event-display -c user-container --tail=15 ``` ```output Context Attributes, specversion: 1.0 type: com.ejemplo.pedido.creado source: prueba-manual id: 1 ... Data, { "pedidoId": "42" } ``` Si envías un evento con otro `Ce-Type`, el broker lo acepta igualmente pero el trigger no lo entrega, porque no coincide con su filtro. ## Actualizar servicios desde CI En un pipeline de despliegue continuo basta con construir la imagen, subirla al registro y actualizar el servicio. Knative crea una revisión nueva y mueve el tráfico cuando está lista: ```bash kn service update hola --image registry.your_domain/hola:1.4.0 ``` Si gestionas los manifiestos con Git (Argo CD, Flux), cambia la imagen en el YAML del `Service` y deja que la herramienta lo aplique; el resultado es el mismo. ## Solución de problemas **El `ksvc` se queda con `READY` en `Unknown` o `False`.** Revisa el motivo con `kubectl describe ksvc hola` y el estado de la revisión con `kubectl get revisions`. Los errores más habituales son una imagen que no existe o que no se puede descargar, y un contenedor que no escucha en el puerto declarado. **`curl` a la URL del servicio no responde.** Comprueba que Kourier tiene `EXTERNAL-IP` (`kubectl get svc -n kourier-system`), que los puertos 80 y 443 están abiertos en el cortafuegos y que el dominio configurado en `config-domain` resuelve a esa IP. **El servicio nunca escala a cero.** Algo sigue enviando tráfico (una sonda externa de monitorización, por ejemplo) o la anotación `min-scale` es mayor que 0. Los logs del autoescalador ayudan a verlo: `kubectl logs -n knative-serving deployment/autoscaler --tail=50`. **El trigger no entrega eventos.** Comprueba que el `type` del evento coincide exactamente con el filtro, que el trigger está `READY` con `kubectl get triggers` y los logs del broker con `kubectl logs -n knative-eventing deployment/mt-broker-ingress --tail=50`. ## Conclusión Has instalado Knative Serving con Kourier, desplegado un servicio que escala a cero, repartido tráfico entre dos revisiones con un despliegue canary y enrutado eventos con un broker y un trigger. Como siguientes pasos, activa HTTPS automático para tus servicios con la integración de cert-manager de Knative, sustituye el canal en memoria por un broker de Kafka o RabbitMQ para producción y conecta fuentes de eventos reales como `PingSource` o `ApiServerSource`.