Introducción
En anteriores posts repasamos cómo montar Kubernetes y Rancher usando la herramienta rke. Aunque este método, con cambios menores, sigue siendo válido para adaptarlo a las nuevas versiones de las aplicaciones; la realidad es que este procedimiento es engorroso para laboratorios de desarrollo o pruebas rápidas de versiones. En estos casos es necesaria la creación y aprovisionamiento de máquinas virtuales, así como un entorno de red específico y herramientas externas como Loadbalancers que complican algo más el proceso.
Laboratorios rápidos de Kubernetes gracias a k3s y k3d
Para solucionar estos problemas se han ido desarrollando múltiples herramientas de utilidad, que repasamos brevemente a continuación:
- Vagrant: Es una herramienta del fabricante Hashicorp que permite, con ayuda de un hipervisor tipo VirtualBox/VMware Workstation, desplegar rápidamente máquinas virtuales o Boxes (como se denominan en el argot de Vagrant) y utilizarlas para un desarrollo o laboratorio de forma mucho más ágil. La configuración de Vagrant se realiza mediante Vagrantfiles, que definen de forma declarativa qué sistema operativo, configuración de red y qué provisionamiento tendrá nuestra infraestructura.
- Multipass VM: Es una herramienta de consola del fabricante Canonical (Ubuntu), que utiliza como backend de virtualización KVM en Linux, pero que bajo Windows/MacOS puede usar Virtualbox. Nativamente corre con Hyper-V bajo Windows e Hyperkit en MacOS. Multipass es la mejor forma de crear máquinas virtuales basadas en Ubuntu dentro de un host Ubuntu. La cli es bastante fácil de manejar y nos permite desplegar instancias rápidamente y probar nuestras aplicaciones de forma ágil.
Dejando de lado herramientas más genéricas para entornos de desarrollo y centrándonos en Kubernetes, han surgido varios proyectos para poder desplegarlo de forma rápida, entre ellos Kind (Kubernetes in Docker) y k3s (Lightweight Kubernetes by Rancher).
Ambos proyectos nos permiten testear e incluso llevar a producción nuestros desarrollos sobre Kubernetes y contenedores utilizando Docker como base.
Hoy nos centraremos en k3s y en k3d , su wrapper para despliegue rápido.
k3s y k3d
El proyecto k3s es una distribución production ready ligera y certificada de Kubernetes. Está especialmente diseñada para usarse en entornos Edge o con recursos limitados. Esta distribución tiene un consumo de memoria muy reducido y su binario no supera los 100MB. Está especialmente optimizada para entornos ARM64 y ARMv7, aunque puede instalarse también en tu portátil (que es lo que haremos hoy mediante k3d ). Este proyecto, desarrollado inicialmente por Rancher en 2019, ha sido adoptado recientemente por la Cloud Native Computing Foundation.
El proyecto k3d es un wrapper o manejador que nos permitirá desplegar rápidamente en nuestro portátil clusters de Kubernetes basados en k3s , contando únicamente con Docker instalado.
Os dejamos las webs de k3d y también de k3s para que las tengáis como referencia.
Cómo instalar k3s
La instalación de k3s es bastante fácil. En esta ocasión utilizaremos un portátil con Docker-Desktop instalado y WSL2 bajo Windows, y en la consola correremos el siguiente comando:
# Instalamos k3d wget -q -O - https://raw.githubusercontent.com/rancher/k3d/main/install.sh | bash # Comprobamos versión instalada de k3d y k3s ❯ k3d version k3d version v3.4.0 k3s version v1.19.4-k3s1 (default)
Es tan simple como se aprecia, aunque hay distintos métodos de instalación que puedes consultar aquí.
Creando nuestra primera demo con k3s y k3d
Ha llegado el momento de instalar nuestro primer cluster Kubernetes basado en k3s y usando k3d :
# Creando el cluster mediante k3d ❯ k3d cluster create MiPrimerCluster INFO Created network 'k3d-MiPrimerCluster' INFO Created volume 'k3d-MiPrimerCluster-images' INFO Creating node 'k3d-MiPrimerCluster-server-0' INFO Pulling image 'docker.io/rancher/k3s:v1.19.4-k3s1' INFO Creating LoadBalancer 'k3d-MiPrimerCluster-serverlb' INFO Pulling image 'docker.io/rancher/k3d-proxy:v3.4.0' INFO (Optional) Trying to get IP of the docker host and inject it into the cluster as 'host.k3d.internal' for easy access INFO Successfully added host record to /etc/hosts in 2/2 nodes and to the CoreDNS ConfigMap INFO Cluster 'MiPrimerCluster' created successfully! INFO You can now use it like this: kubectl cluster-info # Vamos a ver qué información de cluster tenemos ❯ kubectl cluster-info Kubernetes master is running at https://0.0.0.0:41671 CoreDNS is running at https://0.0.0.0:41671/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy Metrics-server is running at https://0.0.0.0:41671/api/v1/namespaces/kube-system/services/https:metrics-server:/proxy To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'. # Listamos los pods dentro del cluster ❯ kubectl get pods --all-namespaces NAMESPACE NAME READY STATUS RESTARTS AGE kube-system coredns-66c464876b-9x82l 1/1 Running 0 48s kube-system metrics-server-7b4f8b595-g2l5b 1/1 Running 0 48s kube-system local-path-provisioner-7ff9579c6-mqd7r 1/1 Running 0 48s kube-system helm-install-traefik-44fpj 0/1 Completed 0 48s kube-system svclb-traefik-z6n4v 2/2 Running 0 19s kube-system traefik-5dd496474-c5cjb 0/1 Running 0 19s # Listamos los nodos del cluster ❯ kubectl get nodes NAME STATUS ROLES AGE VERSION k3d-miprimercluster-server-0 Ready master 89s v1.19.4+k3s1 # Y por último revisamos qué contenedores está corriendo nuestro sistema ❯ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8664aee7d160 rancher/k3d-proxy:v3.4.0 "/bin/sh -c nginx-pr…" 2 minutes ago Up 2 minutes 80/tcp, 0.0.0.0:41671->6443/tcp k3d-MiPrimerCluster-serverlb eeda6795a095 rancher/k3s:v1.19.4-k3s1 "/bin/k3s server --t…" 2 minutes ago Up 2 minutes k3d-MiPrimerCluster-server-0
Como podéis ver, k3d ha instalado un cluster basado en la versión 1.19.4 de Kubernetes con k3s como distribución con un único nodo master ; y para ello ha levantado únicamente dos contenedores, el binario de k3s y un balanceador de acceso a la API. Sí, k3s es una distribución que nos permite tener alta disponibilidad.
Aunque este cluster, tal y como está desplegado, no tiene nodos worker. Vamos a desplegarlo de nuevo:
# Creamos un nuevo cluster llamado 'MySegundoCluster' con dos workers nodes ❯ k3d cluster create MySegundoCluster --agents 2 INFO Created network 'k3d-MySegundoCluster' INFO Created volume 'k3d-MySegundoCluster-images' INFO Creating node 'k3d-MySegundoCluster-server-0' INFO Creating node 'k3d-MySegundoCluster-agent-0' INFO Creating node 'k3d-MySegundoCluster-agent-1' INFO Creating LoadBalancer 'k3d-MySegundoCluster-serverlb' INFO (Optional) Trying to get IP of the docker host and inject it into the cluster as 'host.k3d.internal' for easy access INFO Successfully added host record to /etc/hosts in 4/4 nodes and to the CoreDNS ConfigMap INFO Cluster 'MySegundoCluster' created successfully! INFO You can now use it like this: kubectl cluster-info # Listado de nodos vía k3d ❯ k3d node list NAME ROLE CLUSTER STATUS k3d-MySegundoCluster-agent-0 agent MySegundoCluster running k3d-MySegundoCluster-agent-1 agent MySegundoCluster running k3d-MySegundoCluster-server-0 server MySegundoCluster running k3d-MySegundoCluster-serverlb loadbalancer MySegundoCluster running # Listamos los nodos de nuestro recién creado cluster. To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'. ❯ kubectl get nodes NAME STATUS ROLES AGE VERSION k3d-mysegundocluster-server-0 Ready master 14s v1.19.4+k3s1 k3d-mysegundocluster-agent-1 Ready <none> 8s v1.19.4+k3s1 k3d-mysegundocluster-agent-0 Ready <none> 8s v1.19.4+k3s1 # Listamos pods en todos los namespaces ❯ kubectl get pods --all-namespaces NAMESPACE NAME READY STATUS RESTARTS AGE kube-system local-path-provisioner-7ff9579c6-clrqf 1/1 Running 0 3m13s kube-system coredns-66c464876b-wn4g4 1/1 Running 0 3m13s kube-system metrics-server-7b4f8b595-hdxt4 1/1 Running 0 3m13s kube-system helm-install-traefik-mgtjx 0/1 Completed 0 3m13s kube-system svclb-traefik-jkl6q 2/2 Running 0 2m50s kube-system svclb-traefik-rb22p 2/2 Running 0 2m50s kube-system svclb-traefik-9kbkb 2/2 Running 0 2m50s kube-system traefik-5dd496474-8lnzz 1/1 Running 0 2m50s # Listamos contenedores desplegados en el sistema ❯ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a2f24f35340f rancher/k3d-proxy:v3.4.0 "/bin/sh -c nginx-pr…" 3 minutes ago Up 3 minutes 80/tcp, 0.0.0.0:35253->6443/tcp k3d-MySegundoCluster-serverlb dc1c3a3e249a rancher/k3s:v1.19.4-k3s1 "/bin/k3s agent" 3 minutes ago Up 3 minutes k3d-MySegundoCluster-agent-1 7ab17db2bd9b rancher/k3s:v1.19.4-k3s1 "/bin/k3s agent" 3 minutes ago Up 3 minutes k3d-MySegundoCluster-agent-0 5ef980fb5744 rancher/k3s:v1.19.4-k3s1 "/bin/k3s server --t…" 3 minutes ago Up 3 minutes k3d-MySegundoCluster-server-0
Como vemos, nuestro segundo cluster tiene 2 nodos worker y un nodo master recién desplegados; para docker hemos levantado 4 contenedores distintos, uno con k3s actuando de master y dos como agente (worker), más el balanceador de la API.
¡Y todo esto en apenas 3 minutos!
Ahora vamos a hacer un pequeño despliegue de un servidor nginx para comprobar el funcionamiento de nuestro cluster:
# Creamos un deploy de nginx con la imagen oficial ❯ kubectl create deployment nginx --image nginx # Comprobamos los pods ❯ kubectl get pods NAME READY STATUS RESTARTS AGE nginx-6799fc88d8-qljt4 1/1 Running 0 16s # Vemos `[ está nuestro deployment ❯ kubectl describe pods nginx Name: nginx-6799fc88d8-qljt4 Namespace: default Priority: 0 Node: k3d-mysegundocluster-agent-1/172.19.0.4 Start Time: Tue, 29 Dec 2020 13:53:01 +0100 Labels: app=nginx pod-template-hash=6799fc88d8 Annotations: <none> Status: Running IP: 10.42.2.4 IPs: IP: 10.42.2.4 Controlled By: ReplicaSet/nginx-6799fc88d8 Containers: nginx: Container ID: containerd://df89dd1a62bca942f7a14921d11ed69ffb2cdb99b8617d20884479ae5d748d21 Image: nginx Image ID: docker.io/library/nginx@sha256:4cf620a5c81390ee209398ecc18e5fb9dd0f5155cd82adcbae532fec94006fb9 Port: <none> Host Port: <none> State: Running Started: Tue, 29 Dec 2020 13:53:14 +0100 Ready: True Restart Count: 0 Environment: <none> Mounts: /var/run/secrets/kubernetes.io/serviceaccount from default-token-bwnfd (ro) Conditions: Type Status Initialized True Ready True ContainersReady True PodScheduled True Volumes: default-token-bwnfd: Type: Secret (a volume populated by a Secret) SecretName: default-token-bwnfd Optional: false QoS Class: BestEffort Node-Selectors: <none> Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s node.kubernetes.io/unreachable:NoExecute op=Exists for 300s Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 29s default-scheduler Successfully assigned default/nginx-6799fc88d8-qljt4 to k3d-mysegundocluster-agent-1 Normal Pulling 29s kubelet Pulling image "nginx" Normal Pulled 17s kubelet Successfully pulled image "nginx" in 11.7347774s Normal Created 17s kubelet Created container nginx Normal Started 17s kubelet Started container nginx # Listamos los deployments, pods y servicios de nuestro cluster en su namespace default ❯ kubectl get all NAME READY STATUS RESTARTS AGE pod/nginx-6799fc88d8-qljt4 1/1 Running 0 47s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 16m NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/nginx 1/1 1 1 47s NAME DESIRED CURRENT READY AGE replicaset.apps/nginx-6799fc88d8 1 1 1 47s # Ahora escalaremos nuestro deployment a dos réplicas ❯ kubectl scale --replicas=2 deployment/nginx deployment.apps/nginx scaled # Aquí vemos `[ está creando una nueva réplica de nuestro despliegue ❯ kubectl get pods NAME READY STATUS RESTARTS AGE nginx-6799fc88d8-qljt4 1/1 Running 0 3m51s nginx-6799fc88d8-f96tx 0/1 ContainerCreating 0 3s # Y listo, ya está desplegado ❯ kubectl get pods NAME READY STATUS RESTARTS AGE nginx-6799fc88d8-qljt4 1/1 Running 0 4m10s nginx-6799fc88d8-f96tx 1/1 Running 0 22s
¡Listo! Ya podemos desplegar aplicaciones, testar funcionalidades o simplemente jugar con nuestro nuevo cluster de Kubernetes basado en k3s y desplegado mediante k3d en nuestro equipo local.