mmul.kubelab
Este rol se puede usar para desplegar un clúster de Kubernetes con una implementación completamente automatizada e idempotente de varios componentes.
Características
Este rol se puede configurar para habilitar todas estas características:
Implementación de clúster de control plano único o múltiple con HAProxy y Keepalived para Alta Disponibilidad.
Complementos de múltiples redes Flannel y Calico.
Dashboard de Kubernetes.
Gestión de usuarios con generación de certificados y actualización del archivo
kubeconfig
.Clase de almacenamiento Ceph-CSI para dispositivos de bloque.
MetalLB como balanceador de carga para entornos baremetal.
Ingress NGINX para la exposición del servicio.
Cert Manager para la gestión automatizada de certificados.
Instalar el clúster con el Playbook de Ansible
La mejor manera de preparar el entorno es usar un VirtualEnv de Python, instalando ansible con pip3
:
user@lab ~ # python3 -m venv ansible
user@lab ~ # source ansible/bin/activate
(ansible) user@lab ~ # pip3 install ansible
Collectando ansible
Usando ansible-7.5.0-py3-none-any.whl (43.6 MB)
...
...
Instalando paquetes recolectados: resolvelib, PyYAML, pycparser, packaging, MarkupSafe, jinja2, cffi, cryptography, ansible-core, ansible
Instalación exitosa de MarkupSafe-2.1.2 PyYAML-6.0 ansible-7.5.0 ansible-core-2.14.5 cffi-1.15.1 cryptography-40.0.2 jinja2-3.1.2 packaging-23.1 pycparser-2.21 resolvelib-0.8.1
Luego necesitarás este rol, y en este caso usar ansible-galaxy
es una buena opción para hacerlo todo automático:
(ansible) user@lab ~ # ansible-galaxy install mmul.kubelab -p ansible/roles/
Iniciando el proceso de instalación del rol galaxy
- descargando rol 'kubelab', propiedad de mmul
- descargando rol desde https://github.com/mmul-it/kubelab/archive/main.tar.gz
- extrayendo mmul.kubelab a /home/rasca/ansible/roles/mmul.kubelab
- mmul.kubelab (main) instalado con éxito
Con el rol en su lugar, puedes completar los requisitos, nuevamente usando pip3
:
(ansible) user@lab ~ # pip3 install -r ansible/roles/mmul.kubelab/requirements.txt
...
...
Instalación exitosa de ansible-vault-2.1.0 cachetools-5.3.0 certifi-2023.5.7 charset-normalizer-3.1.0 google-auth-2.18.0 idna-3.4 kubernetes-26.1.0 oauthlib-3.2.2 pyasn1-0.5.0 pyasn1-modules-0.3.0 python-dateutil-2.8.2 requests-2.30.0 requests-oauthlib-1.3.1 rsa-4.9 six-1.16.0 urllib3-1.26.15 websocket-client-1.5.1
Una vez que los requisitos estén disponibles, generalmente usarás el rol lanzando el playbook tests/kubelab.yml
, así:
(ansible) user@lab ~ # ansible-playbook -i tests/inventory/kubelab tests/kubelab.yml
NOTA: la fecha y hora de los sistemas involucrados son importantes. Tener un desfase horario entre la máquina donde ejecutas los playbooks de Ansible y las máquinas de destino podría causar fallos en la verificación de certificados.
NOTA: puedes elegir en cualquier momento reiniciar todo pasando k8s_reset
como true
. Esto reiniciará tu clúster completo, así que úsalo con precaución:
(ansible) user@lab ~ # ansible-playbook -i tests/inventory/kubelab tests/kubelab.yml -e k8s_reset=true
Interactuar con el clúster después de la instalación
Una vez que el playbook complete su ejecución, la mejor forma de interactuar con el clúster es usando el comando kubectl
que se puede instalar de la siguiente manera:
user@lab ~ # curl -s -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
user@lab ~ # chmod +x kubectl
user@lab ~ # sudo mv kubectl /usr/local/bin
El rol de Kubernetes produce un directorio local que contiene el archivo principal de kubeconfig, llamado admin.conf. La forma más fácil de usarlo es exportando la variable KUBECONFIG, así:
user@lab ~ # export KUBECONFIG=~/kubernetes/admin.conf
Desde ahora, hasta el final de la sesión, cada vez que uses kubectl
, dependerá de las credenciales contenidas en ese archivo:
user@lab ~ # kubectl cluster-info
El plano de control de Kubernetes está funcionando en https://192.168.122.199:8443
CoreDNS está funcionando en https://192.168.122.199:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
Para depurar y diagnosticar problemas del clúster, usa 'kubectl cluster-info dump'.
user@lab ~ # kubectl get nodes
NOMBRE ESTADO ROLES EDAD VERSIÓN
kubernetes-1 Listo control-plane 26h v1.25.3
kubernetes-2 Listo control-plane 26h v1.25.3
kubernetes-3 Listo control-plane 26h v1.25.3
kubernetes-4 Listo <ninguno> 26h v1.25.3
También es posible usar diferentes usuarios para acceder al clúster; consulta la sección Usuarios para más detalles.
Configuración
Inventario
Un inventario típico depende de lo que quieras desplegar. En el ejemplo kubelab
puedes declarar dentro del archivo de hosts (ver tests/inventory/kubelab/hosts) todos los nodos:
# Hosts de Kubernetes
[kubelab]
kubernetes-1 k8s_role=control-plane run_non_infra_pods=true
kubernetes-2 k8s_role=control-plane run_non_infra_pods=true
kubernetes-3 k8s_role=control-plane run_non_infra_pods=true
kubernetes-4 k8s_role=worker
Establecerás qué nodos actuarán como plano de control y también si esos ejecutarán o no pods no de infraestructura (para hacer que el plano de control también sea un trabajador).
Luego puedes definir, dentro del archivo de grupo (es decir, inventory/kubelab/group_vars/kubelab.yml), toda la configuración adicional, dependiendo de qué quieras implementar.
El nombre del grupo de hosts para el host de Kubernetes es por defecto kubelab
, pero se puede sobreescribir declarando la variable k8s_host_group
.
Clúster de Kubernetes
Si deseas implementar un clúster de alta disponibilidad con múltiples planos de control, tendrás que especificar estas variables:
k8s_cluster_name: kubelab
k8s_control_plane_node: kubernetes-1
k8s_control_plane_port: 6443
k8s_control_plane_cert_key: "91bded725a628a081d74888df8745172ed842fe30c7a3898b3c63ca98c7226fd"
k8s_multi_control_plane: true
k8s_balancer_VIP: 192.168.122.199
k8s_balancer_interface: eth0
k8s_balancer_port: 8443
k8s_balancer_password: "d6e284576158b1"
k8s_wait_timeout: 1200
k8s_control_plane_ports:
- 2379-2380/tcp
- 6443/tcp
- 8443/tcp
- 10250/tcp
- 10257/tcp
- 10259/tcp
Esto iniciará un clúster comenzando desde el nodo kubernetes-1
habilitando múltiples planos de control a través de k8s_multi_control_plane
y configurando la dirección VIP y la interfaz.
Nota: querrás cambiar tanto k8s_control_plane_cert_key
como k8s_balancer_password
para mayor seguridad.
Nota: es posible tener una forma más atómica de configurar la red de pods, puertos de trabajadores, rangos de nodeports y gestión de firewall; puedes consultar el archivo de valores predeterminados.
Complemento de red
El rol de Kubernetes soporta los complementos de red Flannel y Calico. La configuración depende del complemento que quieras implementar.
Para Flannel necesitarás algo como:
# Complemento Flannel
k8s_network_addon: flannel
k8s_network_addon_ports:
- 8285/udp
- 8472/udp
Para ver cómo implementar Calico, consulta el archivo de valores predeterminados.
Dashboard
El dashboard de Kubernetes se puede implementar agregando esto a la configuración:
k8s_dashboard_enable: true
Una vez que la instalación se complete, la forma más fácil de acceder al dashboard es usando el kubectl proxy
y luego acceder a la URL relacionada
http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/.
Se presentará un aviso de inicio de sesión, y puedes iniciar sesión pasando un token. Por defecto, el rol de Kubernetes crea un usuario llamado dashboard-user
(puedes sobreescribirlo).
Para recuperar el token necesitarás usar kubectl
, así:
user@lab ~ # kubectl -n kubernetes-dashboard create token dashboard-user
<SU TOKEN>
Copia y pega la salida del comando anterior dentro del aviso y habrás completado el inicio de sesión.
Usuarios
Es posible agregar usuarios a tu clúster, declarando algo como esto:
k8s_users:
- name: pod-viewer
namespace: default
role_name: pod-viewer-role
role_rules_apigroups: '""'
role_rules_resources: '"pods","pods/exec","pods/log"'
role_rules_verbs: '"*"'
rolebinding_name: pod-viewer-rolebinding
cert_expire_days: 3650
update_kube_config: true
Esto creará un directorio local que contiene estos archivos:
user@lab ~ # ls -1 kubernetes/users/
pod-viewer.crt
pod-viewer.csr
pod-viewer.key
users.conf
users-rolebindings.yaml
users-roles.yaml
El archivo users.conf
puede usarse para acceder al clúster con este usuario, así:
user@lab ~ # export KUBECONFIG=~/kubernetes/users/users.conf
rasca@catastrofe [~]> kubectl config get-contexts
ACTUAL NOMBRE CLÚSTER AUTOINFORMACIÓN NAMESPACE
* kubernetes-admin@kubelab kubelab kubernetes-admin
pod-viewer@kubelab kubelab pod-viewer default
user@lab ~ # kubectl config use-context pod-viewer@kubelab
Cambiado al contexto "pod-viewer@kubelab".
user@lab ~ # kubectl config get-contexts
ACTUAL NOMBRE CLÚSTER AUTOINFORMACIÓN NAMESPACE
kubernetes-admin@kubelab kubelab kubernetes-admin
* pod-viewer@kubelab kubelab pod-viewer default
user@lab ~ # kubectl get pods
No se encontraron recursos en el namespace predeterminado.
Ceph CSI
El rol de Kubernetes soporta la implementación de la clase de almacenamiento Ceph CSI. Se puede definir de la siguiente manera:
k8s_ceph_csi_enable: true
k8s_ceph_csi_id: lab-ceph
k8s_ceph_csi_secret_userid: kubernetes
k8s_ceph_csi_secret_userkey: AQAWvU5jjBHSGhAAuAXtHFt0h05B5J/VHERGOA==
k8s_ceph_csi_clusterid: d046bbb0-4ee4-11ed-8f6f-525400f292ff
k8s_ceph_csi_pool: kubepool
k8s_ceph_csi_monitors:
- 192.168.122.11:6789
- 192.168.122.12:6789
- 192.168.122.13:6789
Luego será posible declarar nuevos PVC:
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: rbd-pvc
namespace: rasca
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 1Gi
storageClassName: csi-rbd-sc
Y el Pod relacionado:
---
apiVersion: v1
kind: Pod
metadata:
name: csi-rbd-demo-pod
namespace: rasca
spec:
containers:
- name: web-server
image: nginx
volumeMounts:
- name: mypvc
mountPath: /var/lib/www/html
volumes:
- name: mypvc
persistentVolumeClaim:
claimName: rbd-pvc
readOnly: false
NOTA: actualmente solo se admite la provisión de rbd
.
MetalLB
Para habilitar MetalLB, una implementación de balanceador de carga para clústeres de Kubernetes baremetal, usando protocolos de enrutamiento estándar, basta con declarar:
k8s_metallb_enable: true
k8s_metallb_pools:
- name: 'primer-piscina'
addresses: '192.168.122.100-192.168.122.130'
Luego será posible usar este LoadBalancer para crear IPs dentro del rango de piscina declarado (consulta el siguiente ejemplo de ingress-nginx
para entender cómo).
Ingress NGINX
Para habilitar Ingress NGINX, un controlador de Ingress para Kubernetes usando NGINX como proxy inverso y balanceador de carga, basta con declarar:
k8s_ingress_nginx_enable: true
Esto instalará el controlador Ingress NGINX que se puede usar para diferentes propósitos.
Ingress NGINX en planos de control
Por ejemplo, es posible usar Ingress NGINX exponiendo los puertos 80
y 443
en la IP balanceada gestionada por haproxy, declarando esto:
k8s_ingress_nginx_enable: true
k8s_ingress_nginx_haproxy_conf: true
k8s_ingress_nginx_services:
- name: ingress-nginx-externalip
spec:
externalIPs:
- 192.168.122.199
ports:
- name: port-1
port: 80
protocol: TCP
- name: port-2
port: 443
protocol: TCP
selector:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
Esto expondrá ambos puertos en la IP balanceada (en este caso 192.168.122.199
) y hará que el servicio responda allí.
Para probarlo, simplemente intenta esto:
$ kubectl create deployment demo --image=httpd --port=80
deployment.apps/demo creado
$ kubectl expose deployment demo
service/demo expuesto
$ kubectl create ingress demo --class=nginx \
--rule="demo.192.168.122.199.nip.io/*=demo:80" \
--annotation="nginx.ingress.kubernetes.io/service-upstream=true"
ingress.networking.k8s.io/demo creado
$ curl http://demo.192.168.122.199.nip.io
<html><body><h1>¡Funciona!</h1></body></html>
O para probar TLS:
$ kubectl create deployment demo --image=httpd --port=80
deployment.apps/demo creado
$ kubectl expose deployment demo
service/demo expuesto
$ openssl genrsa -out cert.key 2048
(sin salida)
$ openssl req -new -key cert.key -out cert.csr -subj "/CN=demo.192.168.122.199.nip.io"
(sin salida)
$ openssl x509 -req -days 366 -in cert.csr -signkey cert.key -out cert.crt
La firma de solicitud de certificado fue exitosa
subject=CN = demo.192.168.122.199.nip.io
$ kubectl create secret tls tls-secret --cert=./cert.crt --key=./cert.key
secret/tls-secret creado
$ kubectl create ingress demo --class=nginx \
--rule="demo.192.168.122.199.nip.io/*=demo:80,tls=tls-secret" \
--annotation="nginx.ingress.kubernetes.io/service-upstream=true"
ingress.networking.k8s.io/demo creado
$ curl -k https://demo.192.168.122.199.nip.io
<html><body><h1>¡Funciona!</h1></body></html>
La razón por la cual se necesita --annotation="nginx.ingress.kubernetes.io/service-upstream=true"
se explica en este problema de ingress-nginx.
Ingress NGINX con MetalLB
Otra forma es usarlo en combinación con MetalLB, declarando un servicio LoadBalancer
, de la siguiente manera:
k8s_ingress_nginx_enable: true
k8s_ingress_nginx_services:
- name: ingress-nginx-lb
spec:
type: LoadBalancer
loadBalancerIP: 192.168.122.100
ports:
- name: port-1
port: 80
protocol: TCP
- name: port-2
port: 443
protocol: TCP
Esto instalará todo lo relacionado con el controlador y asignará la loadBalancerIP
que forma parte del rango suministrado por MetalLB, exponiendo ambos puertos 80
y 443
.
Cert Manager
Para habilitar Cert Manager, un controlador para automatizar la gestión de certificados en Kubernetes, basta con declarar:
k8s_cert_manager_enable: true
k8s_cert_manager_issuers:
- name: letsencrypt
cluster: true
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt
solvers:
- http01:
ingress:
class: nginx
Esto instalará todo lo relacionado con el controlador y creará un emisor de clúster que utilizará letsencrypt
con resolución de desafío http01
, a través de la clase de ingreso NGINX.
Una vez que todo esté instalado y desees exponer una aplicación, puedes probar todo usando algo como este yaml:
apiVersion: v1
kind: Namespace
metadata:
name: rasca
---
apiVersion: v1
kind: ConfigMap
metadata:
name: index-html
namespace: rasca
data:
index.html: |
¡Este es mi fabuloso servidor web!
---
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: rasca
labels:
app: nginx
spec:
containers:
- name: web-server
image: nginx
volumeMounts:
- name: docroot
mountPath: /usr/share/nginx/html
volumes:
- name: docroot
configMap:
name: index-html
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
namespace: rasca
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt
name: nginx-ingress
namespace: rasca
spec:
ingressClassName: nginx
rules:
- host: nginx.apps.kubelab.mmul.it
http:
paths:
- backend:
service:
name: nginx-service
port:
number: 80
path: /
pathType: Prefix
tls:
- hosts:
- nginx.apps.kubelab.mmul.it
secretName: nginx.apps.kubelab.mmul.it
Si observas específicamente el último recurso, el Ingress
llamado nginx-ingress
, verás dos secciones importantes:
Bajo
metadata
->annotations
la anotacióncert-manager.io/cluster-issuer: letsencrypt
.Bajo
spec:
->tls
la declaración del host.
Con esto en su lugar, después de un tiempo, tendrás tu certificado servido para el servicio expuesto.
Licencia
MIT
Información del Autor
Raoul Scarazzini (rascasoft)
This role automates the creation of a Kubernetes cluster complete of additional dashboard, users and operators.
ansible-galaxy install mmul.kubelab