Guía para crear un servidor personal de Kubernetes
Habilitando una arquitectura básica que le va a permitir aprender sobre contenedores y sus componentes

Un servidor personal de Kubernetes es una excelente herramienta de aprendizaje. Nos puede ayudar a entender como funciona esta tecnología, y así poder aplicarla en un ambiente laboral.
Nuestros objetivos para esta guía van a ser los siguientes:
Habilitar una instancia de Kubernetes que utilice pocos recursos pero que no limite el aprendizaje sobre contenedores.
Lograr gestionar la instancia de Kubernetes utilizando una interfaz gráfica.
Poder publicar servicios que se puedan accesar externamente utilizando un número de IP.
Poder almacenar informacion permanentemente, a pesar de la naturaleza efímera de los contenedores.
Entre los objetivos que fueron excluídos para esta guía se encuentran:
Mejores prácticas de seguridad.
Arquitectura e implementación para un ambiente de producción.
Explicación detallada de los componentes de Kubernetes.
Como instalar el Sistema Operativo Ubuntu 24.04 LTS y operaciones básicas de Linux como elevar los privilegios del comando a ser ejecutado (por ejemplo, utilizando el comando su).
Configuración de la red.
Pre-requisitos
Es importante que el lector tenga todo esto listo antes de seguir los pasos descritos más adelante:
Una subred dentro del rango de IPs reservados, con al menos 16 IPs disponibles. Para esta guía, vamos a asumir que la red disponible se encuentra dentro del rango 10.0.0.0/24, pero los servicios que se van a publicar estarán en las IPs que van desde 10.0.0.70 a 10.0.0.99.
Una máquina física o virtual con al menos 16GB de memoria, 10GB de disco duro y 2 procesadores. Nos vamos a referir a este servidor con el nombre de “host”, y vamos a asumir que utiliza el IP 10.0.0.12 (efectivamente, el IP se encuentra fuera del rango de los IPs libres que vamos a necesitar, pero dentro de la misma subred 10.0.0.0/24).
El sistema operativo Ubuntu 24.04 LTS va a estar instalado en el host.
Instalando Kubernetes
Vamos a utilizar K3S para faciilar la instalación de Kubernetes, nos enfocaremos en correr todos los servicios en una sola computadora, lo cual está bien para un servidor personal, pero no para un ambiente de producción. Ejecute el siguiente comando para instalar Kubernetes en el host utilizando K3S:
curl -sfL https://get.k3s.io | INSTALL_K3S_CHANNEL=latest K3S_TOKEN=SECRET sh -s - --write-kubeconfig-mode 644 --disable traefik --disable servicelb
El servicio va a durar un par de minutos habilitándose, mientras tanto nos vamos a enfocar en acciones que nos van a hacer más fácil su gestionamiento. Estos comandos van a requerir de un re-inicio de sesión en Linux.
Edite el archivo ~/.bashrc, y agregue la siguiente línea al final:
alias kubectl="k3s kubectl”
Adicionalmente, edite el archivo /etc/environment y agregue la siguiente linea al final del archivo:
KUBECONFIG="/etc/rancher/k3s/k3s.yaml"
En este punto los servicios de Kubernetes deben estar arriba, y puede verificar que todo está corriendo normalmente utilizando el siguiente comando:
kubectl version
Si todo funciona correctamente, el resultado debería verse así:

Adicionalmente, si necesita desinstalar K3S porque quisiera comenzar desde 0, puede utilizar:
/usr/local/bin/k3s-uninstall.sh
Implementando un load balancer para la red
Un servicio de load balancer nos permite tener un IP que envía las solicitudes que este recibe a alguno de los nodos en nuestro cluster de Kubernetes. En nuestro caso solo tenemos un nodo/servidor, pero entender como funciona un load balancer es básico para poder implementar un cluster de múltiples nodos a futuro.
La implementación del load balancer la vamos a hacer utilizando MetalLB. El primer paso es de hecho instalar el servicio de MetalLB en el cluster de Kubernetes utilizando el siguiente comando:
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.15.2/config/manifests/metallb-native.yaml
Luego, ocupamos indicarle a MetalLB cuales son los IPs que puede utilizar para asignar a los servicios que vamos a instalar en el cluster; como dijimos anteriormente, nuestros IPs van desde 10.0.0.70 a 10.0.0.90. Nota: a Kubernetes le va a tomar un rato levantar todos los contenedores/servicios de MetalLB antes de que este comando funcione.
cat <<EOF | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: default-pool
namespace: metallb-system
spec:
addresses:
- 10.0.0.70-10.0.0.90
EOF
Finalmente, ocupamos que MetalLB anuncie los IPs a la red, esto se logra con el comando:
cat <<EOF | k3s kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: external-pool
namespace: metallb-system
spec:
ipAddressPools:
- default-pool
EOF
Probando el load balancer
Estamos listos para correr nuestro primer ejemplo “Hello World!”, para ello, vamos a publicar un servicio de nginx (webserver), que nos permita usar nuestro navegador para ver el mensaje “Hello World!”.
kubectl create namespace sandbox
kubectl create deploy nginx-test --image nginx --namespace sandbox
kubectl get pods --all-namespaces
kubectl expose deploy nginx-test --namespace sandbox --port 80 --type LoadBalancer
kubectl get svc --all-namespaces
Una vez que corran esos comandos y los servicios se encuentren arriba, va a poder ver la página navegando a: (Note el uso de http y no https)
http://10.0.0.70

Gestionando Kubernetes usando un dashboard
Kubernetes puede ser gestionado mediante la linea de comandos utilizando el comando kubectl, pero vamos a habilitar una interfaz gráfica para facilitar este proceso. Comenzamos instalando el servicio:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml
kubectl patch svc kubernetes-dashboard -n kubernetes-dashboard -p '{"spec": {"ports": [{"port": 443,"targetPort": 8443,"name": "https"}],"type": "LoadBalancer"}}'
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-user
namespace: kubernetes-dashboard
EOF
cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-user
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: admin-user
namespace: kubernetes-dashboard
EOF
Una vez que todos los contenedores/servicios del Kubernetes dashboard estén corriendo, podrá ingresar al dashboard utilizando la dirección: (Note el uso de https)
https://10.0.0.71

Para poder autenticarse en la pantalla de login, va a necesitar generar un token primero, mediante el comando:
kubectl -n kubernetes-dashboard create token admin-user

Copie y pegue ese comando en la pantalla de login y podrá ingresar al dashboard. Este es un ejemplo de la vista de los pods para todos los namespaces:

Habilitando almacenamiento persistente
El almacenamiento persistente nos permite conservar información aún y cuando un contenedor se mate, reinicie o mueva. Por su naturaleza, los contenedores no conservan los cambios que se les hace, a no ser que la información se guarde dentro de un medio de almacenamiento persistente.
Volviendo a nuestro ejemplo, en este punto hemos habilitado una serie de servicios que no necesitan guardar información persistente, pero cuando se hace necesario hacerlo, vamos a tener que habilitar un servicio llamado persistent storage.
Esta vez, vamos a realizar la instalación utilizando Helm, que es un manejador de paquetes para Kubernetes (Helm es a Kubernetes lo que Aptitude/apt es a Ubuntu). Para ello, vamos a ejecutar:
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
A continuación, vamos a instalar Longhorn, que se caracteriza por proveer una arquitectura hyper-converged.
sudo apt install open-iscsi
helm repo add longhorn https://charts.longhorn.io
helm repo update
helm install longhorn longhorn/longhorn --create-namespace --namespace longhorn-system
kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'
Probando el almacenamiento persistente
Vamos a modificar nuestra instancia Hello World en nginx para que se puedan almacenar los archivos del sitio en el sistema de archivos del host. Para ello, empezamos creando un Persistent Volume Claim (PVC):
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nginx-pvc
namespace: sandbox
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 1Gi
EOF
Luego ocupamos modificar la instancia existente de nginx para que haga uso del PVC:
kubectl patch deployment nginx-test -n sandbox --type='json' -p='[{"op": "add", "path": "/spec/template/spec/volumes", "value": [{"name": "nginx-storage", "persistentVolumeClaim": {"claimName": "nginx-pvc"}}]}, {"op": "add", "path": "/spec/template/spec/containers/0/volumeMounts", "value": [{"name": "nginx-storage", "mountPath": "/usr/share/nginx/html"}]}]'
Y finalmente modificamos el contenido del archivo para cambiar el mensaje que muestra el sitio:
kubectl exec $(kubectl get pods -n sandbox -l app=nginx-test -o jsonpath='{.items[0].metadata.name}') -n sandbox -- sh -c 'echo "<h1>Hello from my Longhorn volume!</h1>" > /usr/share/nginx/html/index.html'
En este punto, puede acceder el servicio nuevamente en http://10.0.0.70 y va a notar un nuevo mensaje. No solamente eso, puede matar el pod, y el nuevo pod va a contener el mismo mensaje gracias al almacenamiento persistente.

Conclusión
Logramos construir un cluster de Kubernetes con un load balancer, almacenamiento persistente, dashboard y un sitio de nginx que hace uso de todos esos recursos.
Los ejercicios en esta guía pueden ser utilizados en un futuro dentro de contenedores con múltiples nodos, los cuales son aptos para escenarios que van más allá del simple aprendizaje de la tecnología.




