DEV Community

martinsaporiti
martinsaporiti

Posted on

Instalando k3d para jugar con k8s.

En este artículo veremos como configurar k3d en una máquina virtual con linux, con el objetivo de poder contar con un mini cluster de kubernetes para apredener, practicar o, inclusive, tener aplicaciones corriendo.

K3D es un wrapper livianito sobre 3ks, que justamente la mínima distribución de kubernetes que nos brinda Rancher Labs. K3D corre sobre Docker, por lo que puede ser instalado sin demasiada complejidad en un host que tenga instalado Docker. Además, K3D nos permite crear "nodos virtuales" que nos sirven para probar algunas de las características de K8s como son NodeAffinity, Taints y Tolerations.

Otra de las intenciones de este artículo es describir las configuraciones necesarias para poder conectarnos al cluster de kubernetes desde una máquina diferente a la que hostea el cluster, crear un ServiceAccount y darle permisos de administrador y crear otro ServiceAccount con capacidades limitadas.

Requerimientos Previos

Alcanza con tener una máquina virtual (yo uso Virtaulbox) con ubuntu instalado. La imagen de Ubuntu se puede descargar de este link. La instalación es muy sencilla y hay que tener en cuenta solamente que la máquina virtual debe tener acceso a internet y debe ser "visible" desde nuestra máquina (el host de la máquina virtual).

Instalando Docker.

Suponiendo que la ip de nuestra máquina virtual es 192.168.0.91, nos logueamos desde el host a la máquina virtual mediante ssh:

ssh osboxes.org@192.168.0.91
# El password es osboxes.org si bajaste la imagen desde el link que te pasé.
Enter fullscreen mode Exit fullscreen mode

luego ejecutamos:

sudo apt install docker.io
Enter fullscreen mode Exit fullscreen mode

configuramos el Docker para que arranque cuando iniciamos la máquina virtual:

sudo systemctl start docker
sudo systemctl enable docker
Enter fullscreen mode Exit fullscreen mode

... finalmente, probamos:

docker --version
Enter fullscreen mode Exit fullscreen mode

si nos muestra la versión, listo... ya tenemos Docker instalado.

podemos ejecutar adicionalmente ;)

docker run -it --rm busybox echo "Hello from Docker!"
Enter fullscreen mode Exit fullscreen mode

Ahora k3d

Es muy simple la instalación para la última versión estable. Primero necesitamos instalar curl:

sudo apt-get install curl
Enter fullscreen mode Exit fullscreen mode

Vamos a necesitar también tener instalado kubectl para poder interactuar con el cluster. Lo instalamos, acá dejo los pasos.

Ya con Kubectl instalado, seguimos...

wget -q -O - https://raw.githubusercontent.com/rancher/k3d/main/install.sh | bash
Enter fullscreen mode Exit fullscreen mode

Listo!. Ahora vamos a crear nuestro primer cluster teniendo en cuenta los sigiuentes requerimientos:

  • Tenemos que poder crear Persistent Volumes -> necesitamos entonces darle un folder a 3kd para poder crear los volúmenes.
  • Tenemos que poder entrar al cluster desde otra máquina (desde el host de la máquina virtual por ejemplo).
  • Necesitamos 3 nodos además del master para poder probar algunas cosas.

Bueno, ejecutamos entonces el siguiente comando:

# Creamos un folder:
mkdir -p /tmp/k3dvol

# Ejecutamos:
k3d cluster create Kluster --api-port 192.168.0.91:6443 --volume /tmp/k3dvol:/tmp/k3dvol -p 8081:80@loadbalancer --agents 3
Enter fullscreen mode Exit fullscreen mode

donde:

  • Kluster es el nombre del cluster
  • La ip 192.168.0.91 es la ip de la máquina virtual.
  • --agents especifica la cantidad de nodos virtuales adicionales al master.
  • -p 8081:80@loadbalancer especifica el puerto (8081) a través del cual podremos ingresar al loadbalancer del cluster.

probamos:

kubectl get nodes

# Deberíamos obtener algo parecido a esto:

NAME                   STATUS   ROLES    AGE   VERSION
k3d-kluster-agent-0    Ready    <none>   12s   v1.18.9+k3s1
k3d-kluster-server-0   Ready    master   10s   v1.18.9+k3s1
k3d-kluster-agent-2    Ready    <none>   11s   v1.18.9+k3s1
k3d-kluster-agent-1    Ready    <none>   10s   v1.18.9+k3s1
Enter fullscreen mode Exit fullscreen mode

Muy bien, tenemos un cluster con un nodo maestro y tres nodos esclavos. Vamos a usarlo!

Accediento al Cluster Desde otra Computadora.

Tanto los sapiens como los no sapiens (pods por ejemplo) pueden comunicarse e interactuar con un cluster de K8s mediante una api. Generalmente los sapiens utilizan Kubectl para hacerlo y el proceso para "conversar" con el cluster implica tres etapas: Authentication, Authorization y Admission Control. En esta sección veremos entonces como crear un Service Account que nos permita esa conversación con el cluster en forma remota (por fuera del mismo cluster). Vamos a crear un usuario (ServiceAccount) y asignarle permisos de administrador.

Paso 1 - Creación del Service Account y Obtención del Secret.

# Seteamos el usuario a crear (sapiens en este caso):
user=sapien

# Creamos el Service Account en el namespace kube-system:
kubectl create sa ${user} -n kube-system

# Obtenemos el secret para el Service Account creado:
secret=$(kubectl get sa ${user} -n kube-system -o json | jq -r .secrets[].name)
echo "secret = ${secret}"
Enter fullscreen mode Exit fullscreen mode

Paso 2 - Creación del Certificado y Obtención del Token

kubectl get secret ${secret} -n kube-system -o json | jq -r '.data["ca.crt"]' | base64 -d > ca.crt
user_token=$(kubectl get secret ${secret} -n kube-system -o json | jq -r '.data["token"]' | base64 -d)
echo "token = ${user_token}"
Enter fullscreen mode Exit fullscreen mode

Paso 3 - Configuración del Contexto

c=`kubectl config current-context`
echo "context = $c"

cluster_name=`kubectl config get-contexts $c | awk '{print $3}' | tail -n 1`
echo "cluster_name= ${cluster_name}"

endpoint=`kubectl config view -o jsonpath="{.clusters[?(@.name == \"${cluster_name}\")].cluster.server}"`
echo "endpoint = ${endpoint}"
Enter fullscreen mode Exit fullscreen mode

Paso 4 - Configuración final del Service Account en el Cluster - Role Binding.

# Se bindea el service account al rol de administrador existente...
kubectl create clusterrolebinding add-on-cluster-admin-${user} --clusterrole=cluster-admin --serviceaccount=kube-system:${user}

# Creamos el archivo de configuación para la conección.
export KUBECONFIG=$HOME/${user}-kube.yml
touch $HOME/${user}-kube.yml

kubectl config set-cluster ${cluster_name} \
    --embed-certs=true \
    --server=${endpoint} \
    --certificate-authority=./ca.crt

kubectl config set-credentials ${user}-${cluster_name#cluster-} --token=${user_token}

kubectl config set-context ${user}-${cluster_name#cluster-} \
    --cluster=${cluster_name} \
    --user=${user}-${cluster_name#cluster-}
Enter fullscreen mode Exit fullscreen mode

Ahora queda copiar el archivo sapiens-kube.yml a la máquina desde la cual nos queremos conectar a cluster (supongamos el host de la máquina virtual) y agregarlo el path a la variable de entorno KUBECONFIG:

export KUBECONFIG=somepath/sapien-kube.yml
Enter fullscreen mode Exit fullscreen mode

Lo probamos:

#Seteamos el contexto (el nombre del contexto dependerá del nombre del SA):
k config set-context sapiens-k3d-Kluster

k get nodes
...
NAME                   STATUS   ROLES    AGE   VERSION
k3d-kluster-agent-1    Ready    <none>   8h    v1.18.9+k3s1
k3d-kluster-agent-0    Ready    <none>   8h    v1.18.9+k3s1
k3d-kluster-agent-2    Ready    <none>   8h    v1.18.9+k3s1
k3d-kluster-server-0   Ready    master   8h    v1.18.9+k3s1
Enter fullscreen mode Exit fullscreen mode

Podemos crear un namespace:

kubectl create namespace testadmin
...
namespace/testadmin created
Enter fullscreen mode Exit fullscreen mode

Bien, accedimos al cluster desde otra máquina a través utilizando un Service Account creado por nostros y bindeado a un rol de adminitrador. Misión cumplida.

Comó crear un usuario con privilegios limitados?

En la sección anterior vimos como crear un Service Account y cómo bindearlo con un rol con permisos de adminitrador. Ese rol cluster-admin ya estaba creado por defecto por el mismo K8s en su instalación y es un cluster role. Atención con esto, K8s nos proporciona dos tipos de roles, los Role y los ClusterRole. Los ClusterRole son roles para operar sobre todo el cluster, son roles cross, mientras que los ROLE sirven para operar dentro de un namespace determinado.

Ahora vamos a ver como crear nosotros un rol con privilegios limitados. Antes, vamos a plantear el siguiente requerimiento: "necesitamos que el usuario amy solamente pueda listar pods en su propio namespace".

Con el requimiento en mente, lo primero que hay que hacer es crear el namespace:

k create namespace amy
Enter fullscreen mode Exit fullscreen mode

El siguiente paso es crear el rol dentro del mismo namespace. Creamos el archivo amy-role.yaml con el siguiente contenido.

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: amy
  name: pod-and-pod-logs-reader
rules:
- apiGroups: [""]
  resources: ["pods", "pods/log"]
  verbs: ["get", "list"]
Enter fullscreen mode Exit fullscreen mode

lo creamos:

k create -f amy-role.yaml
...
role.rbac.authorization.k8s.io/pod-and-pod-logs-reader created
Enter fullscreen mode Exit fullscreen mode

Ahora tenemos que repetir los mismos haciendo algunos cambios:

# Seteamos el usuario a crear (amy en este caso):
user=amy

# Creamos el Service Account en el namespace amy:
kubectl create sa ${user} -namespace amy

secret=$(kubectl get sa ${user} -n amy -o json | jq -r .secrets[].name)
echo "secret = ${secret}"

kubectl get secret ${secret} -n amy -o json | jq -r '.data["ca.crt"]' | base64 -d > ca.crt
user_token=$(kubectl get secret ${secret} -n amy -o json | jq -r '.data["token"]' | base64 -d)
echo "token = ${user_token}"


c=`kubectl config current-context`
echo "context = $c"

cluster_name=`kubectl config get-contexts $c | awk '{print $3}' | tail -n 1`
echo "cluster_name= ${cluster_name}"

endpoint=`kubectl config view -o jsonpath="{.clusters[?(@.name == \"${cluster_name}\")].cluster.server}"`
echo "endpoint = ${endpoint}"


# Acá realizamos el binding al role creado unos pasos antes... al rol con privilegios limitados y en el namespace amy.
# Prestar atención: ahora creamos un rolebinding en lugar de un clusterrolebinding y además lo creamos en el namespace amy,
# el mismo namespace en donde creamos el role.
kubectl create rolebinding add-on-cluster-${user} --role=pod-and-pod-logs-reader --serviceaccount=amy:${user} -namespace amy

# Creamos el archivo de configuación para la conección.
export KUBECONFIG=$HOME/${user}-kube.yml
touch $HOME/${user}-kube.yml

kubectl config set-cluster ${cluster_name} \
    --embed-certs=true \
    --server=${endpoint} \
    --certificate-authority=./ca.crt

kubectl config set-credentials ${user}-${cluster_name#cluster-} --token=${user_token}

kubectl config set-context ${user}-${cluster_name#cluster-} \
    --cluster=${cluster_name} \
    --user=${user}-${cluster_name#cluster-}

Enter fullscreen mode Exit fullscreen mode

Si todo fue bien, deberíamos tener un archivo amy-kube.yml listo para ser utilizado.

Vamos a probarlo

Nuevamente, vamos a realizar la prueba desde una máquina diferente a la que tiene corriendo el cluster.

export KUBECONFIG=somepath/amy-kube.yml
Enter fullscreen mode Exit fullscreen mode

Nos paramos en el contexto:

k config set-context amy-k3d-Kluster --namespace=amy
Enter fullscreen mode Exit fullscreen mode

y ejecutamos:

k get nodes
...
Error from server (Forbidden): nodes is forbidden: User "system:serviceaccount:amy:amy" cannot list resource "nodes" in API group "" at the cluster scope
Enter fullscreen mode Exit fullscreen mode

Perfecto, no nos permitió listar los nodos porque el Service Account amy no tiene permisos. Qué pasa si queremos listar los pods?

k get pod
...
No resources found in amy namespace.
Enter fullscreen mode Exit fullscreen mode

Funciona, nos permite listarlos (aunque no hay ninguno).

Comentarios Finales

Hemos visto como instalar de forma muy simple un cluster K3d, cluster que es muy liviano y útil para aprender, realizar pruebas y hasta tenerlo operativo en máquinas con recursos limitados, K3d se monta sobre Docker y es una alternativa interesante.

Si bien en la actualidad existen herramientas con interfaces amigables como Lens para realizar configuraciones sobre un cluster de kubernetes de forma mucho más simple, entender como funcionan la autenticación y autorización es importante. Este artículo solamente muestra una parte, la parte práctica para crear un Service Account y darle permisos de administrador o permisos limitados sobre un deterinado namespace y permitir operar sobre el cluster 3kd de forma remota. Recomiendo leer más sobre el tema en la página oficial de Kubernetes.

Top comments (0)