Lesson  in  Kubernetes 101

Hablar con el clúster

Antes del primer Pod: qué es kubectl en realidad —un cliente HTTP contra el API server—, cómo se consulta el esquema sin memorizar un solo campo, el YAML que hace fallar los manifiestos antes de que Kubernetes los lea, y con qué clúster y qué Namespace estás hablando.

kubectl es un cliente HTTP

Antes de crear nada conviene saber con qué estás hablando y con qué herramienta.

kubectl es la línea de comandos con la que se trabaja con un clúster, y es importante entender qué no es: no es Kubernetes, no es un agente, no tiene ninguna inteligencia propia y no habla con los nodos. Es un binario que traduce lo que escribes a llamadas HTTP contra el API server y que formatea la respuesta para que se pueda leer.

Todo el trabajo se hace desde la pestaña dev-machine, contra un clúster de Kubernetes real.

Note

💡 Puedes abrir la pestaña Explorer en cualquier momento para ver de forma visual lo que ocurre dentro del clúster.

Paso 1: la sintaxis, que es siempre la misma

Empieza por comprobar qué versión tienes delante:

kubectl version

Verás dos: la del cliente que acabas de ejecutar y la del servidor con el que habla. Son piezas distintas y pueden no coincidir.

A partir de ahí, todo comando tiene la misma forma:

kubectl [verbo] [objeto] [opciones]

kubectl get pods, kubectl delete pod web, kubectl scale deployment web. Si te sabes esa forma, el resto del curso es vocabulario.

Paso 2: mira la petición HTTP con tus propios ojos

Que kubectl sea un cliente HTTP no hay que creérselo: se puede ver. Cualquier comando admite --v, con valores de 0 a 9, que muestra los datos de la petición. El nivel de detalle sale por la salida de error, así que para guardarlo hay que redirigir las dos:

kubectl get pods -A --v=7 > ~/traza.txt 2>&1

Ábrelo y busca la línea de la petición:

grep -i "GET" ~/traza.txt -A4
"Request" verb="GET" url="https://127.0.0.1:6443/api/v1/pods?limit=500"
"Response" status="200 OK" milliseconds=4

"Request" verb="GET" url="https://172.16.0.2:6443/api/v1/pods?limit=500" headers=<
        Accept: application/json;as=Table;v=v1;g=meta.k8s.io,application/json;as=Table;v=v1beta1;g=meta.k8s.io,application/json
        User-Agent: kubectl/v1.36.3 (linux/amd64) kubernetes/0f29094
 >
"Response" status="200 OK" milliseconds=52

Una petición GET a una URL, con sus cabeceras y su código de respuesta. Nada más.

De ahí salen tres consecuencias que conviene tener claras desde el principio:

  • Todo lo que hace kubectl lo puede hacer cualquier otro cliente: un script con curl, un programa en Go, Argo CD o el operador que escribirás en el módulo de Extensibilidad. La API es el producto; kubectl es una forma cómoda de usarla, no la única.
  • Cuando algo tarda o falla, --v=8 te dice de qué lado está el problema, porque ves la petición, el código de respuesta y el tiempo.
  • Los permisos no los tiene kubectl: los tienes tú, y se comprueban en el API server. Es de lo que va el módulo de Seguridad.

Paso 3: qué tipos existen, y cómo se abrevian

El clúster sabe decirte qué tipos de objeto entiende, sin buscar en ninguna documentación:

kubectl api-resources

Fíjate en las columnas: el nombre del tipo, sus abreviaturas, si vive dentro de un Namespace o es del clúster entero, y a qué grupo de la API pertenece. Esa lista crece: en el módulo de Extensibilidad aparecerá en ella un tipo que habrás inventado tú.

Las abreviaturas son las que se escriben en el día a día, y ahorran cientos de pulsaciones. Para buscar una concreta:

kubectl api-resources | grep persistentvolumeclaims

Paso 4: no memorices campos

Un manifiesto de Pod admite más de cien campos y nadie se los sabe. No hace falta: la documentación está dentro del clúster, y corresponde exactamente a la versión que tienes delante.

kubectl explain pod.spec

Devuelve la descripción de cada campo de ese nivel. Puedes bajar todo lo que quieras por el árbol:

kubectl explain pod.spec.containers

Y con --recursive lo despliega entero de golpe:

kubectl explain pod.spec --recursive | head -40

Funciona igual con tipos que no venían de fábrica, como el Gateway del módulo de Red o la Promocion del de Extensibilidad. Si en cualquier momento del curso te quedas con la duda de qué admite un campo, esta es la respuesta.

Resumen

  • kubectl traduce comandos a peticiones HTTP contra el API server: --v=7 te lo enseña.
  • La sintaxis es siempre kubectl [verbo] [objeto] [opciones].
  • kubectl api-resources dice qué tipos existen y cómo se abrevian.
  • kubectl explain dice qué campos admite cada uno, en la versión de tu clúster.

En la siguiente unidad, la otra mitad: el fichero YAML que describe lo que quieres, y a qué clúster van a parar tus comandos.

El fichero, y con qué clúster hablas

Con kubectl se puede trabajar de dos maneras, y las dos aparecen en todo el curso:

  • El modo imperativo son comandos que dicen qué hacer: kubectl run web --image=..., kubectl create namespace tienda, kubectl scale deployment web --replicas=5.
  • El modo declarativo son ficheros que describen cómo quieres que estén las cosas, y que se envían al clúster con kubectl apply -f. El fichero se puede leer, revisar, versionar en Git y volver a aplicar.

El criterio para elegir es sencillo: lo imperativo sirve para averiguar qué pasa; lo declarativo, para dejar constancia de lo que quieres. Un arreglo urgente hecho con un comando funciona igual de bien, pero no queda escrito.

Paso 1: casi ningún error de Kubernetes es de Kubernetes

Esos ficheros se escriben en YAML, y merece la pena dedicarle dos minutos, porque casi todos los errores de un principiante con Kubernetes son en realidad errores de YAML.

Solo hay que retener dos reglas. La primera: la indentación decide la estructura, y solo con espacios; un tabulador es un error de sintaxis. La segunda: un guion al principio de la línea crea un elemento de una lista.

apiVersion: v1                    # clave: valor
kind: Pod
metadata:                         # un bloque, con sus claves dentro
  name: web
  labels:
    app: web                      # tres niveles: metadata, labels, app
spec:
  containers:                     # una lista...
    - name: web                   # ...con un elemento, que es otro bloque
      image: ghcr.io/iximiuz/labs/nginx:alpine

Ese guion importa más de lo que parece: cuando llegues a las NetworkPolicy del módulo de Red, un guion de más o de menos cambiará una regla de «hay que cumplir las dos condiciones» a «basta con cumplir una».

Y luego están los tipos. Los valores son texto, números o booleanos, y YAML los adivina. Casi siempre acierta, y las dos veces que no acierta duelen:

  • Un valor como NO, on, off o yes se lee como booleano. El caso clásico es el código de país de Noruega, NO, que acaba convertido en false.
  • Un número de versión se lee como número decimal, y ahí es donde se pierde una tarde entera.

Chócate con ello ahora, que es barato. Escribe este fichero:

cat > ~/tipos.yaml <<'YAML'
apiVersion: v1
kind: ConfigMap
metadata:
  name: tipos
  namespace: tienda
data:
  version: 1.10
YAML

Es un ConfigMap, el objeto que guarda configuración y que verás a fondo en el módulo de Configuración. Aquí solo importa una cosa de él: el bloque data únicamente admite texto. Aplícalo y mira qué pasa:

kubectl apply -f ~/tipos.yaml

No se crea. El clúster se queja de que ahí donde esperaba una cadena ha recibido un número: tú escribiste 1.10, pero YAML no vio una versión, vio un decimal —y en un decimal el cero final no significa nada, así que además ya no es 1.10, es 1.1—. Arréglalo poniéndole comillas al valor en el fichero y vuelve a aplicarlo.

Note

💡 La regla práctica: si el valor es una cadena y hay la menor duda, ponle comillas. Los puertos, las réplicas y los booleanos van sin ellas.

Fíjate en que no has escrito -n en ningún momento: el manifiesto lleva su Namespace dentro, en metadata. Cuando no lo lleva —o cuando el comando es de los que no leen ficheros, como kubectl get— sí hace falta decirlo, y de eso va el último paso de esta lección.

Y antes de aplicar nada, esto valida un fichero sin tocar el clúster:

kubectl apply -f ~/tipos.yaml --dry-run=client

Paso 2: que el comando escriba el fichero por ti

Hay una tercera vía que combina los dos modos: pedirle a un comando imperativo que, en lugar de tocar el clúster, escriba el fichero declarativo que habría aplicado. Se consigue añadiendo -o yaml al --dry-run=client de antes:

kubectl create namespace pruebas --dry-run=client -o yaml > ~/pruebas.yaml

Ábrelo en la pestaña IDE:

cat ~/pruebas.yaml

Tienes el manifiesto, y en el clúster no se ha creado nada:

kubectl get namespace pruebas
Error from server (NotFound): namespaces "pruebas" not found

A partir de ahí editas el fichero y lo aplicas cuando quieras. Es la forma más rápida de empezar un manifiesto que existe, y evita el error clásico de copiar uno de internet sin saber qué hace la mitad de sus líneas. En el módulo Trabajando con el clúster esto se convierte en un método de trabajo completo.

Note

💡 Cambiando client por server, la validación la hace el API server: comprueba también los campos que dependen del clúster y pasa por los controladores de admisión, sin llegar a guardar nada.

Paso 3: con qué clúster estás hablando

Falta responder de dónde saca kubectl la dirección del clúster y quién eres tú.

Lo lee de un fichero llamado kubeconfig, normalmente en ~/.kube/config, que puede contener varios clústeres a la vez. Cada combinación de clúster, usuario y Namespace por defecto se llama contexto, y solo hay uno activo en cada momento. Todo comando que ejecutes va contra el contexto activo.

kubectl config current-context
kubectl config get-contexts

En la columna NAMESPACE verás que ahora mismo está vacía: sin nada puesto, se usa el Namespace default.

Note

⚠️ El error más caro de esta profesión es ejecutar en producción un comando que creías que iba al clúster de pruebas. kubectl config current-context antes de tocar nada cuesta un segundo. Muchos administradores acaban poniendo el contexto activo en el prompt de la terminal por este motivo.

El clúster de este curso ya trae creados los dos Namespaces del libro. Compruébalo:

kubectl get namespaces

Ahí están tienda, donde vive la aplicación, y plataforma, para las piezas que no caben dentro de ella. Todo el curso ocurre en tienda, y como el Namespace forma parte del contexto se puede fijar de una vez y dejar de escribir -n tienda en cada comando:

kubectl config set-context --current --namespace=tienda

Vuelve a mirarlo y compara con lo de antes:

kubectl config get-contexts

En el resto de lecciones del curso este paso viene hecho de fábrica, y verás la bandera -n solo donde importa de verdad: cuando un comando cruza de Namespace o cuando la herramienta lo exige.

Resumen

  • Modo imperativo para averiguar; modo declarativo para dejar constancia.
  • YAML: la indentación es la estructura, el guion es una lista, y sin comillas 1.10 deja de ser una versión.
  • --dry-run=client valida sin tocar nada; con -o yaml además te escribe el manifiesto.
  • El kubeconfig guarda contextos —clúster, usuario y Namespace—, y solo hay uno activo.

Ya sabes hablar con el clúster. En la siguiente lección le pides algo: tu primer Pod.

Next lesson
Tu primer Pod