Escribiendo plugins para kubectl

Kubectl

*Kubectl Plugin*

Como posiblemente ya sabrás kubectl es la herramienta de línea de comandos oficial para interactuar con Kubernetes. Desde ésta puedes hacer prácticamente cualquier cosa en Kubernetes siempre y cuando, obviamente, tengas permiso para ello. Además de todas las posibilidades que esta herramienta ofrece, también tienes la posibilidad de expandirla a través del sistema de plugins.

Un plugin para kubectl no es más que un fichero que cumpla las siguientes tres características:

  • Que sea ejecutable (binario o script)
  • Que se encuentre en el PATH del sistema
  • Que el nombre del fichero empierce por kubectl- (¡guión incluído!)

Los sistema de plugins en kubectl se implementó en la versión 1.8.0 como versión alpha, y luego hubo una reescritura de esta característica en la versión 1.12.0, cuya versión es la mínima recomendada.

Vamos a escribir nuestro primer plugin. Para ello vamos a escribirlo en bash. Así que creemos un fichero llamada kubectl-hello con el siguiente contenido:

#!/bin/bash

echo "Hello, Hello!"

Prefijando el nombre del fichero con kubectl-, cumplimos con uno de los requisitos. Otro de los requisitos es que el fichero sea ejecutable. En nuestro caso:

chmod +x kubectl-hello

El último requisito es que el fichero se encuentre en el PATH del sistema. Para ello puedes copiar el fichero a cualquier directorio que ya se encuentre en el PATH, o en nuestro caso lo que voy a hacer es añadir el directorio donde tengo creado el fichero al PATH. En mi caso el directorio donde he creado el plugin es ~/tmp/kplugin. Por lo tanto (esta acción sólo sobrevive a la sesión desde donde la ejecutes. Si quieres que dicha acción sea permanente tendrás que añadirla a tu ~/.bashrc, ~/.zshrc, o algún otro fichero):

export PATH=$PATH:~/tmp/kplugin

Una vez tenemos hecho esto, podemos ejecutar nuestro script:

kubectl hello
Hello, Hello!

Este comando lo podemos ejecutar desde cualquier parte del sistema, siempre y cuando kubectl esté instalado correctamente.

Vamos a crear ahora un plugin que sea un poco más útil. En este caso vamos a crear otro script (en bash de nuevo), pero éste va a hacer uso del propio comando kubectl para crear un fichero de configuración que pueda ser usado con el propio kubectl. Algunas veces queremos automatizar alguna tarea desde un servidor, por ejemplo desde un pipeline CI/CD, donde usemos kubectl para hacer algo en un clúster de Kubernetes. Por ejemplo la actualización o despliegue de un servcio. Para que el servidor de automatización sea capaz de poder comunicarse con Kubernetes a través de kubectl, éste necesita ciertos datos (dirección del clúster, credenciales, etc) en un fichero de configuración para dicha comunicación. Este fichero de configuración es el que nuestro plugin va a generar.

El contenido de nuestro fichero es el siguiente:

#!/bin/bash

set -e

usage="
USAGE: 
  kubectl kubeconf-generator -a SERVICE_ACCOUNT -n NAMESPACE
"

while getopts a:n: option
do
case "${option}"
in
a) SA=${OPTARG};;
n) NAMESPACE=${OPTARG};;
esac
done

[[ -z "$SA" ]] && { echo "Service account is required" ; echo "$usage" ; exit 1; }
[[ -z "$NAMESPACE" ]] && { echo "Namespace is required" ; echo "$usage" ; exit 1; }

# Get secret name
SECRET_NAME=($(kubectl get sa $SA -n $NAMESPACE -o jsonpath='{.secrets[0].name}'))
  
# Get secret value
SECRET=$(kubectl get secret $SECRET_NAME -n $NAMESPACE -o jsonpath='{.data.token}' | base64 -D)

# Get cluster server name
SERVER=$(kubectl config view --minify -o json | jq -r '.clusters[].cluster.server')
# Get cluster name
CLUSTER_NAME=$(kubectl config view --minify -o json | jq -r '.clusters[].name')

# Get cluster certs
CERTS=$(kubectl config view --raw --minify -o json | jq -r '.clusters[].cluster."certificate-authority-data"')

cat << EOM
apiVersion: v1
kind: Config
users:
- name: $SA
  user:
    token: $SECRET
clusters:
- cluster:
    certificate-authority-data: $CERTS
    server: $SERVER
  name: $CLUSTER_NAME
contexts:
- context:
    cluster: $CLUSTER_NAME
    user: $SAS
  name: svcs-acct-context
current-context: svcs-acct-context
EOM

Este script usa además de kubectl, la herramienta jp. Si no la tienes instalada tendrás que instalarla.

Cómo se puede observar este plugin espera dos parámetros, una cuenta de servicio y el espacio de nombres de dicha cuenta. El fichero de configuración generado, cuando sea usado, interactuará con el clúster con dicha cuenta de servicio, por lo que estará limitado a los permisos de la misma.

Vamos a crearnos un fichero llamado kubectl-kubeconf_generator con el contenido mostrado anteriormente. Fíjate bien que hemos usado el guión necesario después de kubectl y un carácter de subrayado en la segunda parte del nombre. Hablaremos de este detalle más tarde. Por ahora vamos a ejecutar nuestro plugin sin parámetros:

kubectl kubeconf_generator
Service account is required

USAGE:
  kubectl-kubeconf_generator -a SERVICE_ACCOUNT -n NAMESPACE

Ahora pasemos los parámetros. En este caso necesitas tener acceso a un clúster de Kubernetes. En nuestro ejemplo usaremos minikube (asegúrate que está arrancado):

kubectl kubeconf_generator -a default -n default
apiVersion: v1
kind: Config
users:
- name: default
  user:
    token: REDACTADO
clusters:
- cluster:
    certificate-authority-data: null
    server: https://192.168.99.110:8443
  name: minikube
contexts:
- context:
    cluster: minikube
    user:
  name: svcs-acct-context
current-context: svcs-acct-context

Ese fichero de configuración nos permitirá acceder a nuestro clúster (minikube), con la cuenta de servicio default del espacio de nombres default. Este no es el tema de esta entrada, así que lo dejamos aquí.

Volvamos al tema del nombre del plugin. En nuestro último ejemplo nombramos el fichero kubectl-kubeconf_generator, de esta forma kubectl reconoce el plugin como kubeconf_generator o kubeconf-generator, es decir podemos llamar a nuestro plugin de estas dos formas:

kubectl kubeconf_generator -a default -n default
...

o kubectl kubeconf-generator -a default -n default …

Ambas formas funcionan. Ahora, si renombramos nuestro plugin a kubectl-kubeconf-generator, con guión en la segunda separación, para llamarlo desde kubectl lo haremos con un espacio entre los nombres:

kubectl kubeconf generator -a default -n default
...

Siguiendo este patrón podemos crear subcomandos. Por ejemplo podríamos crear dos plugins, uno que genere la configuración en formato yaml y otro en json, de esta forma los nombraríamos:

kubectl-kubeconf-generator-yaml
kubectl-kubeconf-generator-json

Así podríamos llamarlos desde kubectl:

kubectl kubeconf generator json ...
...
kubectl kubeconf generator yaml ...
...

Esto es simplemente un ejemplo para mostrar esta particularidad de cómo kubectl trata el tema de los nombres. Si quieres tener un plugin que permita crear la salida en distintos formatos, lo suyo sería hacerlo a través de un parámetro, y posiblente, usando el parámetro -o para que sea consistente con kubectl.

Para ir terminando, una limitación que existe, es que no puedes sobreescribir un subcomando de kubectl, por ejemplo, kubectl tiene el subcomando version, por lo que si creas un plugin que se llame kubectl-version, cuando invoques kubectl version, se ejecutará el subcomando de kubectl y no tu plugin. También existen una serie de reglas a la hora de resolver conflictos de nombres entre plugins, en las que no vamos a tratar en esta entrada, pero puedes consultarlas en la documentación a través del enlace que tienes al principio de la entrada.

Por último mencionar que kubectl nos ofrece el subcomando plugin list, el cual nos permite ver los plugins que tenemos en el sistema.

kubectl plugin list
The following compatible plugins are available:

/Users/tuxotron/tmp/kplugin/kubectl-hello
/Users/tuxotron/tmp/kplugin/kubectl-kubeconf-generator

En otra entrada hablaremos de cómo manejar los plugins de una forma más limpia y ordenada.