A logo showing the text blog.marcnuri.com
English
Inicio»Go»Cómo crear un plugin de kubectl con client-go para eliminar un Namespace

Entradas Recientes

  • Fabric8 Kubernetes Client 7.2.0 está disponible!
  • Conectarse a un servidor MCP con JavaScript y AI SDK
  • Conectarse a un servidor MCP con JavaScript y LangChain.js
  • El Futuro de las Herramientas para Desarrolladores en la era de la IA
  • Conectarse a un servidor Model Context Protocol (MCP) con Java y LangChain4j

Categorías

  • Antiguo
  • Front-end
  • Go
  • Herramientas
  • Industria y negocios
  • Inteligencia Artificial
  • Java
  • JavaScript
  • Operaciones
  • Personal
  • Proyectos personales

Archivos

  • mayo 2025
  • abril 2025
  • marzo 2025
  • febrero 2025
  • enero 2025
  • diciembre 2024
  • noviembre 2024
  • agosto 2024
  • junio 2024
  • mayo 2024
  • abril 2024
  • marzo 2024
  • febrero 2024
  • enero 2024
  • diciembre 2023
  • noviembre 2023
  • octubre 2023
  • septiembre 2023
  • agosto 2023
  • julio 2023
  • junio 2023
  • mayo 2023
  • abril 2023
  • marzo 2023
  • febrero 2023
  • enero 2023
  • diciembre 2022
  • noviembre 2022
  • octubre 2022
  • agosto 2022
  • julio 2022
  • mayo 2022
  • marzo 2022
  • febrero 2022
  • enero 2022
  • diciembre 2021
  • noviembre 2021
  • octubre 2021
  • septiembre 2021
  • agosto 2021
  • julio 2021
  • diciembre 2020
  • octubre 2020
  • agosto 2020
  • junio 2020
  • mayo 2020
  • marzo 2020
  • febrero 2020
  • enero 2020
  • noviembre 2019
  • octubre 2019
  • julio 2019
  • diciembre 2018
  • agosto 2018
  • julio 2018
  • junio 2018
  • mayo 2018
  • marzo 2018
  • febrero 2018
  • noviembre 2017
  • octubre 2017
  • agosto 2017
  • julio 2017
  • enero 2017
  • julio 2016
  • enero 2016
  • diciembre 2015
  • noviembre 2015
  • diciembre 2014
  • marzo 2014
  • febrero 2011
  • junio 2008
  • mayo 2008
  • abril 2008
  • enero 2008
  • junio 2007
  • mayo 2007
  • abril 2007
  • marzo 2007

Cómo crear un plugin de kubectl con client-go para eliminar un Namespace

2023-11-20 en Go etiquetado Go / Kubernetes / kubectl / client-go por Marc Nuri | Última actualización: 2023-11-20
English version

Introducción

A medida que Kubernetes continúa dominando la gestión de infraestructuras modernas, herramientas como kubectl se han vuelto esenciales para interactuar con los clústeres de Kubernetes. Sirviendo como una navaja suiza, kubectl permite a los usuarios ejecutar diversas operaciones en sus clústeres. Además, su extensibilidad permite la creación de plugins personalizados, mejorando así su funcionalidad.

En esta publicación, te mostraré cómo crear un plugin kubectl usando Go y client-go. El plugin te permitirá eliminar un Namespace de Kubernetes que se ha quedado en estado Terminating.

Comencemos por comprender el problema que resolverá este plugin para kubectl.

Namespace bloqueado en estado Terminating

Un Namespace de Kubernetes es un mecanismo para aislar recursos u objetos de Kubernetes dentro de un clúster. Un Namespace normalmente alojará Deployments, Pods, Services, Ingresses y otros objetos de Kubernetes de tipo Namespaced. Cuando eliminas un Namespace de Kubernetes, el servidor API lo marcará como Terminating y comenzará un proceso en segundo plano para eliminar todos los recursos contenidos en el mismo. Eventualmente, el clúster eliminará el Namespace.

Sin embargo, hay ocasiones en las que el Namespace se queda bloqueado en el estado Terminating. Esto puede suceder cuando una extensión de la API de Kubernetes no está disponible y el servidor API no puede eliminar esos recursos. En este caso, el Namespace contendrá un finalizador que impide que el Namespace se elimine. El siguiente fragmento de código contiene un Namespace con finalizadores pendientes:

my-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: my-namespace
spec:
   finalizers:
    - kubernetes
status:
  conditions:
  - lastTransitionTime: "2023-11-16T10:31:38Z"
    message: All content-preserving finalizers finished
    reason: ContentHasNoFinalizers
    status: "False"
    type: NamespaceFinalizersRemaining
  phase: Terminating

Para poder forzar la eliminación del Namespace, necesitamos eliminar los finalizadores primero. Esto se puede lograr usando el subrecurso /finalize de la API de Namespace.

Puedes encontrar información en línea sobre cómo solucionar esto desde la línea de comandos. Mi colega, Jens Reimann, incluso tiene un script muy popular que automatiza los pasos necesarios.

Teniendo en cuenta que este es un problema recurrente, creo que es un buen ejemplo para demostrar cómo crear un plugin kubectl.

Creando el plugin kubectl

Un plugin kubectl es, en esencia, un binario con el nombre kubectl-<nombre-del-plugin> que está disponible en el PATH del sistema. Para crear nuestro plugin, necesitaremos un proyecto Go que construya un binario con el nombre kubectl-kill-ns. De esta forma, podremos invocar el plugin con kubectl kill ns my-stuck-namespace.

Comencemos por crear un nuevo módulo Go para nuestro plugin:

bash
mkdir kubectl-kill-ns
cd kubectl-kill-ns
go mod init github.com/marcnuri-demo/kubectl-kill-ns

Si el proceso se ejecuta sin problemas, deberías tener un nuevo archivo go.mod con el siguiente contenido:

go.mod
module github.com/marcnuri-demo/kubectl-kill-ns

go 1.21.2

Tendremos que añadir la dependencia k8s.io/client-go a nuestro proyecto. Para ello, ejecutaremos el siguiente comando:

bash
go get k8s.io/client-go@v0.28.3

Dado que necesitamos crear un binario con un nombre específico, necesitaremos ejecutar el comando de compilación de Go con el flag -o para especificar el nombre del archivo de salida. Para facilitar esto, añadiremos un Makefile al proyecto con el siguiente contenido:

Makefile
.PHONY: build
build:
	go build -ldflags "-s -w" -o kubectl-kill-ns ./cmd/main.go

Implementando la lógica para eliminar el Namespace

La lógica para eliminar el Namespace la implementaremos en el paquete killns. Creemos un nuevo archivo killns.go en el directorio internal/killns.

El punto de entrada para client-go es una configuración del clúster de Kubernetes. Dado que el plugin está destinado a ser utilizado por un usuario que opera desde fuera del clúster, usaremos el siguiente código para detectar y cargar automáticamente la configuración del usuario .kube/config:

internal/killns/killns.go
package killns

import (
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/homedir"
)

func LoadKubeConfig() (*rest.Config, error) {
	home := homedir.HomeDir()
	kubeConfig := filepath.Join(home, ".kube", "config")
	return clientcmd.BuildConfigFromFlags("", kubeConfig)
}

El fragmento anterior comienza obteniendo el directorio de inicio del usuario para la plataforma del sistema actual. A continuación, construye la ruta al archivo .kube/config del usuario y lo utiliza para cargar la configuración de Kubernetes. La función devuelve la configuración cargada o un error si no se ha podido cargar la configuración.

La siguiente parte del archivo killns.go contiene la lógica para eliminar el Namespace. El siguiente fragmento de código contiene las partes relevantes de la función KillNamespace:

internal/killns/killns.go
func KillNamespace(kubeConfig *rest.Config, namespace string) {
	//... config validations
	client, err := kubernetes.NewForConfig(kubeConfig)
	//... client validation
	if errDelete := client.CoreV1().Namespaces().Delete(context.TODO(), namespace, metav1.DeleteOptions{}); errDelete != nil {
		panic(errDelete)
	}
	ns, errGet := client.CoreV1().Namespaces().Get(context.TODO(), namespace, metav1.GetOptions{})
	//... Namespace and finalizer checks
	ns.Spec.Finalizers = []corev1.FinalizerName{}
	_, errUpdate := client.CoreV1().Namespaces().Finalize(context.TODO(), ns, metav1.UpdateOptions{})
	//...
}

La lógica de la función es bastante sencilla:

  • Creamos un cliente de Kubernetes usando la configuración proporcionada.
  • A continuación, intentamos eliminar el Namespace.
  • Si el Namespace sigue existiendo después de la eliminación, comprobamos si el Namespace contiene finalizers.
  • Por último, si el Namespace contiene finalizers, los eliminamos y los actualizamos usando el subrecurso /finalize.

Implementado el punto de entrada del plugin

El punto de entrada del plugin es el código que se ejecutará cuando se invoque el plugin. Crearemos un nuevo archivo main.go en el directorio cmd con el siguiente contenido:

cmd/main.go
package main

// imports

func main() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("Error:", r)
			os.Exit(1)
		}
	}()
	kubeConfig, err := killns.LoadKubeConfig()
	if err != nil {
		fmt.Println(".kube/config not found", err)
		os.Exit(1)
	}
	flag.Parse()
	killns.KillNamespace(kubeConfig, flag.Arg(0))
}

La función es una aplicación de línea de comandos sencilla:

  • Comienza diferiendo una función para recuperarse de cualquier panic que mostrará el mensaje de error y terminará la aplicación.
  • A continuación, carga la configuración de Kubernetes del archivo .kube/config del usuario.
  • Por último, recupera los argumentos de la línea de comandos e invoca la función KillNamespace con la configuración cargada y el nombre del Namespace proporcionado en el primer argumento.

Construyendo el plugin

Emplearemos el Makefile que creamos anteriormente para construir el plugin. Para ello, ejecutaremos el siguiente comando:

bash
make build

Un nuevo binario kubectl-kill-ns debería crearse en el directorio del proyecto.

Probando el plugin

Ahora que tenemos el plugin, podemos probarlo invocándolo directamente desde la línea de comandos:

bash
./kubectl-kill-ns my-stuck-namespace

Considerando que también es un plugin de kubectl, también podemos probarlo invocándolo mediante kubectl:

bash
PATH=$PATH:$(pwd) kubectl kill ns my-stuck-namespace

Si queremos que el plugin esté disponible permanentemente, podemos copiarlo a un directorio del PATH:

Conclusión

En esta publicación, hemos visto cómo crear un plugin kubectl usando Go y client-go. El plugin permite eliminar un Namespace de Kubernetes que se ha quedado en estado Terminating.

Los plugins de kubectl presentan incontables oportunidades para extender la funcionalidad de Kubernetes de forma sencilla. Empleando las técnicas descritas en esta publicación, puedes crear soluciones personalizadas para diversos desafíos de Kubernetes.

Puedes encontrar el código fuente completo de este plugin en GitHub.

Twitter iconFacebook iconLinkedIn iconPinterest iconEmail icon

Navegador de artículos
Cómo Testear Aplicaciones Kubernetes en Go con EnvTest: Una Guía PrácticaDesarrollo de aplicaciones Java cloud-native en Kubernetes con Okteto Cloud
© 2007 - 2025 Marc Nuri