Ya he hablado de k3d otras veces en el blog; del mismo modo que kind permite desplegar Kubernetes en Docker: cada nodo del clúster se ejecuta en un contenedor.
k3d hace lo mismo pero en vez de Kubernetes vanilla, usa la distribución ligera de Kubernetes, k3s.
Este fin de semana he actualizado la documentación del repositorio onthedock/k8s-devops en lo relativo a k3d y he aprovechado para explorar un par de cosas nuevas: deplegar el clúster de forma declarativa (como código) y el uso del registry interno en k3s.
En este artículo me centro en la primera parte: el despliegue del clúster como código.
En el momento de escribir este artículo, la versión actual de k3d es la v5.4.9.
Desde la versión 4.0.0 de k3d existe la posibilidad de especificar la configuración del clúster como código. Sin embargo, si estás acostumbrado a desplegar clústers de k3d desde la línea de comando, debes tener en cuenta que las opciones en el fichero de configuración no coinciden al 100% con los parámetros de la CLI.
El fichero de configuración sólo tiene dos campos obligatorios: kind
y apiVersion
:
apiVersion: k3d.io/v1alpha4
Esta versión va cambiando, por lo que debes estar atento a la documentación oficial.kind: Simple
El resto de opciones del fichero de configuración son opcionales.
Otro punto a tener en cuenta es que cualquier opción especificada en el fichero de configuración puede ser sobrescrita desde el parámetro equivalente de la CLI, que tiene preferencia. Esto permite tener usar un fichero de configuración como una plantilla y modificar algunos valores desde la CLI al desplegar (por ejemplo, el nombre del clúster).
Número de servidores y agentes
Rancher denomina server
a los nodos del control plane (anteriormente, masters), y agent
a los nodos worker.
En el fichero de configuración, especificamos cuántos nodos de cada tipo integrarán nuestro clúster:
apiVersion: k3d.io/v1alpha4 # this will change in the future as we make everything more stable
kind: Simple # internally, we also have a Cluster config, which is not yet available externally
# metadata:
# name: default # name that you want to give to your cluster (will still be prefixed with `k3d-`)
servers: 1 # same as `--servers 1`
agents: 2 # same as `--agents 2`
Con esta configuración, k3d genera un clúster con un control plane de un solo nodo, y dos nodos adicionales como workers.
Configuración del endpoint de la API del clúster
Mediante el siguiente bloque se puede configurar el endpoint de la API de Kubernetes del clúster:
kubeAPI: # same as `--api-port myhost.my.domain:6445` (where the name would resolve to 127.0.0.1)
host: "myhost.my.domain" # important for the `server` setting in the kubeconfig
hostIP: "127.0.0.1" # where the Kubernetes API will be listening on
hostPort: "6445" # where the Kubernetes API listening port will be mapped to on your host system
Como el nombre especificado en host
debe resolver a 127.0.0.1 no suelo tomarme la molestia de especificarlo, y accedo usando localhost
directamente. Sin embargo, si usas k3d de forma local durante el desarrollo, puedes especificar el FQDN del clúster “real” y resolverlo a 127.0.0.1 en /etc/hosts
, por ejemplo.
Puerto para el balanceador
Otro bloque que no suelo usar para los clústers locales es el de configuración del balanceador del clúster, pero que es interesante tener en cuenta en algunos casos:
ports:
- port: 8080:80 # same as `--port '8080:80@loadbalancer'`
nodeFilters:
- loadbalancer
Para el resto de opciones, consulta la documentación oficial en Using Config Files.
Despliegue de un registry
La configuración completa del registry se puede especificar mediante el bloque:
registries: # define how registries should be created or used
create: # creates a default registry to be used with the cluster; same as `--registry-create registry.localhost`
name: registry.localhost
host: "0.0.0.0"
hostPort: "5000"
proxy: # omit this to have a "normal" registry, set this to create a registry proxy (pull-through cache)
remoteURL: https://registry-1.docker.io # mirror the DockerHub registry
username: "" # unauthenticated
password: "" # unauthenticated
volumes:
- /some/path:/var/lib/registry # persist registry data locally
use:
- k3d-myotherregistry:5000 # some other k3d-managed registry; same as `--registry-use 'k3d-myotherregistry:5000'`
config: | # define contents of the `registries.yaml` file (or reference a file); same as `--registry-config /path/to/config.yaml`
mirrors:
"my.company.registry":
endpoint:
- http://my.company.registry:5000
Habitualmente, usaremos el registry de manera “normal”, es decir, para almacenar imágenes en el clúster. El registry ofrece la posibilidad de usarse como proxy para mirrorear imágenes de un registry externo… De nuevo, es interesante en algunos escenarios pero no es el caso de uso más habitual.
Configuración definitiva
Para un clúster de desarrollo personal, una configuración equilibrada es:
apiVersion: k3d.io/v1alpha4 # this will change in the future as we make everything more stable
kind: Simple # internally, we also have a Cluster config, which is not yet available externally
# metadata:
# name: demo # name that you want to give to your cluster (will still be prefixed with `k3d-`)
servers: 1 # same as `--servers 1`
agents: 2 # same as `--agents 2`
registries: # define how registries should be created or used
create: # creates a default registry to be used with the cluster; same as `--registry-create registry.localhost`
name: registry.localhost
host: 0.0.0.0
hostPort: "5000"
config: |
mirrors:
"registry.localhost:5000":
endpoint:
- "http://registry.localhost:5000"
Manos a la obra
Creamos el fichero de configuración k3d-cluster-1s-2a+registry.yaml
.
El fichero tiene el campo name
comentado porque especificaremos un nombre para el clúster desde la CLI:
$ k3d cluster create onthedock --config k3d-cluster-1s-2a+registry.yaml
INFO[0000] Using config file k3d-cluster-1s-2a+registry.yaml (k3d.io/v1alpha4#simple)
INFO[0000] Prep: Network
INFO[0000] Created network 'k3d-onthedock'
INFO[0000] Created image volume k3d-onthedock-images
INFO[0000] Creating node 'registry.localhost'
INFO[0000] Successfully created registry 'registry.localhost'
INFO[0000] Starting new tools node...
INFO[0000] Starting Node 'k3d-onthedock-tools'
INFO[0001] Creating node 'k3d-onthedock-server-0'
INFO[0001] Creating node 'k3d-onthedock-agent-0'
INFO[0001] Creating node 'k3d-onthedock-agent-1'
INFO[0001] Creating LoadBalancer 'k3d-onthedock-serverlb'
INFO[0001] Using the k3d-tools node to gather environment information
INFO[0002] HostIP: using network gateway 172.18.0.1 address
INFO[0002] Starting cluster 'onthedock'
INFO[0002] Starting servers...
INFO[0002] Starting Node 'k3d-onthedock-server-0'
INFO[0013] Starting agents...
INFO[0015] Starting Node 'k3d-onthedock-agent-0'
INFO[0015] Starting Node 'k3d-onthedock-agent-1'
INFO[0029] Starting helpers...
INFO[0029] Starting Node 'registry.localhost'
INFO[0031] Starting Node 'k3d-onthedock-serverlb'
INFO[0039] Injecting records for hostAliases (incl. host.k3d.internal) and for 5 network members into CoreDNS configmap...
INFO[0053] Cluster 'onthedock' created successfully!
INFO[0054] You can now use it like this:
kubectl cluster-info
Como no hemos configurado el endpoint de la API del clúster, es recomendable ejecutar kubectl cluster-info
y averiguar en qué puerto se encuentra expuesta, aunque el fichero .kube/config
se ha actualizado para incluir la información de autenticación en un nuevo contexto en el fichero existente.
$ kubectl cluster-info
Kubernetes master is running at https://0.0.0.0:39643
CoreDNS is running at https://0.0.0.0:39643/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
Metrics-server is running at https://0.0.0.0:39643/api/v1/namespaces/kube-system/services/https:metrics-server:https/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
Como k3d despliega los nodos del clúster de Kubernetes en contenedores, podemos ver los “nodos” del clúster mediante docker ps
:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7ff706a7eeaf ghcr.io/k3d-io/k3d-proxy:5.4.9 "/bin/sh -c nginx-pr…" 6 minutes ago Up 6 minutes 80/tcp, 0.0.0.0:39643->6443/tcp k3d-onthedock-serverlb
9642acef4de4 rancher/k3s:v1.25.7-k3s1 "/bin/k3d-entrypoint…" 6 minutes ago Up 6 minutes k3d-onthedock-agent-1
fa352ab7c521 rancher/k3s:v1.25.7-k3s1 "/bin/k3d-entrypoint…" 6 minutes ago Up 6 minutes k3d-onthedock-agent-0
fac4d8517805 rancher/k3s:v1.25.7-k3s1 "/bin/k3d-entrypoint…" 6 minutes ago Up 6 minutes k3d-onthedock-server-0
e5725219073b registry:2 "/entrypoint.sh /etc…" 6 minutes ago Up 6 minutes 0.0.0.0:5000->5000/tcp registry.localhost
Vemos que tenemos los tres nodos del clúster, un nodo k3d-onthedock-server-0
para el control plane y dos workers k3d-onthedock-agent-0
y k3d-onthedock-agent-1
.
Además, tenemos un balanceador frente a la API del clúster, k3d-onthedock-serverlb
y un nodo adicional para el registry registry.localhost
.
k3d permite gestionar los diferentes clúster desplegados de manera sencilla (aunque en este ejemplo sólo tengo 1):
$ k3d cluster list
NAME SERVERS AGENTS LOADBALANCER
onthedock 1/1 2/2 true
Crear un clúster adicional es tan sencillo como:
$ k3d cluster create test -s 1 -a 1
INFO[0000] Prep: Network
INFO[0000] Created network 'k3d-test'
INFO[0000] Created image volume k3d-test-images
INFO[0000] Starting new tools node...
INFO[0000] Starting Node 'k3d-test-tools'
INFO[0001] Creating node 'k3d-test-server-0'
INFO[0001] Creating node 'k3d-test-agent-0'
INFO[0001] Creating LoadBalancer 'k3d-test-serverlb'
INFO[0001] Using the k3d-tools node to gather environment information
INFO[0002] HostIP: using network gateway 172.19.0.1 address
INFO[0002] Starting cluster 'test'
INFO[0002] Starting servers...
INFO[0002] Starting Node 'k3d-test-server-0'
INFO[0012] Starting agents...
INFO[0013] Starting Node 'k3d-test-agent-0'
INFO[0024] Starting helpers...
INFO[0024] Starting Node 'k3d-test-serverlb'
INFO[0032] Injecting records for hostAliases (incl. host.k3d.internal) and for 3 network members into CoreDNS configmap...
INFO[0035] Cluster 'test' created successfully!
INFO[0035] You can now use it like this:
kubectl cluster-info
Y ahora:
$ k3d cluster list
NAME SERVERS AGENTS LOADBALANCER
onthedock 1/1 2/2 true
test 1/1 1/1 true
Añadimos un nodo agent
adicional (en el clúster test
):
$ k3d node create test-agent-1 -c test
INFO[0000] Adding 1 node(s) to the runtime local cluster 'test'...
INFO[0000] Using the k3d-tools node to gather environment information
INFO[0000] Starting new tools node...
INFO[0000] Starting Node 'k3d-test-tools'
INFO[0001] HostIP: using network gateway 172.19.0.1 address
INFO[0002] Starting Node 'k3d-test-agent-1-0'
INFO[0007] Successfully created 1 node(s)!
De momento, no hay opción de listar los nodos de un solo clúster:
$ k3d node list | grep -i test
k3d-test-agent-0 agent test running
k3d-test-agent-1-0 agent test running
k3d-test-server-0 server test running
k3d-test-serverlb loadbalancer test running
Tan fácil como es crear un clúster, podemos destruirlo:
$ k3d cluster delete test
INFO[0001] Deleting cluster 'test'
INFO[0004] Deleting cluster network 'k3d-test'
INFO[0004] Deleting 1 attached volumes...
INFO[0004] Removing cluster details from default kubeconfig...
INFO[0004] Removing standalone kubeconfig file (if there is one)...
INFO[0004] Successfully deleted cluster test!
Conclusión
k3d mejora la velocidad a la que puedes desplegar un clúster para realizar pruebas; el proceso de descarga de la imagen de cada uno de los tipos de nodos es lo que consume mayor tiempo; pero una vez que tenemos copias locales de las imágenes, levantar un clúster de Kubernetes es cuestión de segundos.
A diferencia de otras plataformas -como MiniKube-, k3d (y k3s) fueron desarrollados para entornos productivos, en el edge. Gracias a su velocidad (debida en parte a componentes más ligeros), permite generar clústers (y destruirlos) de forma rápida para realizar todo tipo de pruebas durante el desarrollo.
Como hemos visto, k3d incluye un balanceador y, opcionalmente, un registry, por lo que simula a la perfección la arquitectura de entornos productivos desde la comodidad de tu equipo de desarrollo 😉.