mmul.kubelab

Kubelab Ansible Role

Este rol se puede usar para desplegar un clúster de Kubernetes con una implementación completamente automatizada e idempotente de varios componentes.

Lint the project Ansible Galaxy

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ón cert-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)

Acerca del proyecto

This role automates the creation of a Kubernetes cluster complete of additional dashboard, users and operators.

Instalar
ansible-galaxy install mmul.kubelab
Licencia
mit
Descargas
139
Propietario