Kubernetes – Almacenamiento con Linstor.

Kubernetes – Almacenamiento con Linstor.

Hace ya un tiempo que escribí sobre k8s, otros proyectos han tomado tiempo y recursos, hace unas semanas retome el clúster que tengo en casa para terminar las configuraciones necesarias y mover lo más que pueda de algunas máquinas virtuales que tengo en vSphere y que entiendo desempeñarían mejor estando en Kubernetes.

Inicialmente las pruebas fueron realizadas con longhorn, algunos problemas los cuales no inspiran mucha confianza me hicieron no escribir sobre esa implementación, claramente Rancher dice que es un proyecto aun en beta.

Hace unas semanas leí sobre Linstor [ http://vitobotta.com/2019/08/07/linstor-storage-with-kubernetes/ ], la instalación fue realizada en Ubuntu que es la misma distribución que estoy usando. He decidido usar esta implementación porque usa LVM como backend en los equipos que aportaran almacenamiento para el clúster, además de esto los nodos que están aportando almacenamiento no necesariamente deben tener k8s, al menos eso es lo que entiendo y algo que debo validar.

Instalando Linstor.

Prepararemos nuestros equipos con los siguientes pasos:

apt-get install linux-headers-$(uname -r)
add-apt-repository ppa:linbit/linbit-drbd9-stack
apt-get update
apt install drbd-utils drbd-dkms lvm2
modprobe drbd
lsmod | grep -i drbd
echo drbd > /etc/modules-load.d/drbd.conf

 

En este punto ya los equipos están listos para instalar los componentes de Linstor. Para estar seguro de que todo estaba bien procedí a reiniciar el nodo.

Seleccionamos un equipo para que sea controlador del cluster de Linstor, además este mismo equipo puede aportar almacenamiento así que también instalaremos el componente de satellite.

apt install linstor-controller linstor-satellite linstor-client

Ahora habilitamos e inicializamos los componentes.

systemctl enable --now linstor-controller
systemctl start linstor-controller

En los demás equipos instalamos el componente de satélite.

apt install linstor-satellite linstor-client

Habilitamos el servicio y lo iniciamos.

systemctl enable --now linstor-satellite
systemctl start linstor-satellite

Desde el controller agregamos los equipos satélites.

linstor node create kube1 172.22.35.25
linstor node create kube2 172.22.35.26
linstor node create kube3 172.22.35.27

Esperamos unos minutos y ejecutamos:

linstor node list

Este comando nos mostrará la lista de nodos disponibles, será algo así:

Ya contamos con un clúster de Linstor pero aun no estamos presentando almacenamiento a este, procederemos a crear almacenamiento administrado por LVM, Linstor soporta ZFS, esta es una herramienta que nunca he usado en el pasado de manera seria así que prefiero LVM que tiene años disponible en las distribuciones Linux.

En cada máquina virtual para Kubernetes agregue dos discos, uno para el sistema operativo y otro para almacenamiento que ahora será usado en su totalidad con Linstor.

Lo primero es preparar el disco físico para ser usado en LVM.

pvcreate /dev/sdb

Luego creamos un volumen group (vg).

vgcreate kube /dev/sdb

He seleccionado kube para que sea el nombre del Volume Group.

Procederemos a crear un thin pool, esto nos permitirá crear volúmenes más grandes que el espacio físico del cual disponemos.

lvcreate -l 100%FREE --thinpool kube/lvmthinpool

Una vez realizados estos pasas en cada nodo que aportara almacenamiento al cluster, volvemos al controller para crear un storage-pool.

linstor storage-pool create lvmthin kube1 linstor-pool kube/lvmthinpool
linstor storage-pool create lvmthin kube2 linstor-pool kube/lvmthinpool
linstor storage-pool create lvmthin kube3 linstor-pool kube/lvmthinpool

validamos que nuestro storage-pool fue creado.

linstor storage-pool list

Debemos recibir lo siguiente:

Listo, ya contamos con una plataforma para provisionar almacenamiento a Kubernetes, lo interesante es que también podría provisionar almacenamiento a otras plataformas tales como Proxmox.

Kubernetes – que necesitamos?

Para habilitar que Kubernetes pueda usar almacenamiento proveniente de Linstor, debemos contar con el plugin CSI y crear StorageClass, recomiendo tener una versión de Kubernetes mínimo 1.13 para que todo funcione bien ya que ahí plugins que no están habilitados por defecto antes de esta versión. Al momento de crear los CRD estos fallaban con un mensaje que especificaba la falta de CSINodeInfo, actualizando RKE a 1.14.6 se resolvió.

Instalaremos todo lo necesario para usar Linstor usando esta línea la cual descargar la versión 0.7.2 del CSI y usando sed estamos cambian un valor de ejemplo por el controlador de Linstor que en mi caso es 172.22.35.25. Esto debe ser ejecutado en la misma maquina de donde administramos k8s.

curl https://raw.githubusercontent.com/LINBIT/linstor-csi/v0.7.0/examples/k8s/deploy/linstor-csi-1.14.yaml | sed "s/linstor-controller.example.com/172.22.35.25/g" | kubectl apply -f -

Podemos ver el avance del de la instalación con:

watch kubectl -n kube-system get all

Procederemos a crear un Storage Class usando este yaml

El número de réplicas debe ser el número de nodos de Kubernetes.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: linstor
provisioner: linstor.csi.linbit.com
parameters:
  autoPlace: "3"
  storagePool: "linstor-pool"

Para este punto ya podemos hacer solicitudes de volúmenes usando un PresistentVolumeClaim

En mi caso ya tenia varios yaml con requerimientos de pvc, aquí uno.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  namespace: services
  name: adguard-config
spec:
  storageClassName: linstor
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
       storage: 2Gi

Podemos ver que el STATUS es Bound, esto quiere decir que satisfactoriamente Linstor asigno espacio a Kubernetes para este volumen.

Desde el punto de vista de Linstor usando

linstor volume list

Podemos ver lo siguiente:

Estos son todos los volúmenes que están siendo usados por contenedores en Kubernetes.

Listo!

Ya puedo usar el cluster de Kubernetes, bueno por la cantidad de volúmenes que se ven en la imagen se puede deducir que lo estoy usando. He movido varios servicios hacia el clúster y hasta ahora muy feliz con Kubernetes.

El próximo paso es los respaldos, para esto estaré usando Velero de Heptio/VMware.

 

Kubernetes en premisa con MetalLB en modo BGP.

Kubernetes en premisa con MetalLB en modo BGP.

Una de las desventajas de tener un cluster de k8s en premisa es la falta de LoadBalancer. Gracias a MetalLB esto está resuelto de una manera fácil y elegante.

Cuando queremos publicar servicios en k8s, lo hacemos usando un Ingress Controller (nginx o Traefik por nombrar algunos). Este servicio se apoya de las direcciones IP de los hosts si no tenemos LoadBalancer. El tema se complica cuando queremos publicar servicios que no son HTTP o HTTPS, sé que nginx puede publicar otros protocolos. La ventaja de un IP via LoadBalancer es que podemos usarlos en varios servicios (pods) y publicar cualquier puerto en TCP o UDP.

MetalLB

Los requerimientos son los siguientes:

  • Un cluster de k8s en la versión 1.9.0 o más reciente y que no tenga un tipo de LoadBalancer en funcionamiento, esto quiere decir que tendríamos problemas usando esta solución en GKE por ejemplo.
  • Una configuración de cluster que pueda coexistir con MetalLB https://metallb.universe.tf/installation/network-addons/
  • Direcciones IPv4 para asignar usando este servicio.
  • Dependiendo del modo operativo, podríamos necesitar un router que soporte BGP.
  • https://metallb.universe.tf/#requirements

En mis primeras pruebas, MetalLB fue configurado usando Layer2 (capa 2), es la forma más rápida de probar esta solución, después de varios días de desplegar aplicaciones que hacían uso de LoadBalancer me di cuenta de que algunas direcciones IP de momento no respondían a las peticiones ARP que son necesarias para alcanzar dicha dirección IP. Por esta razón ahora desplegare MetalLB usando BGP.

En modo BGP, la configuración es más extensa y cuanta con campos que tendrán sentido si se ha usado BGP anteriormente.

En mi caso, ya tengo algo de experiencia usando este protocolo y por eso decidí cambiar de Layer2 a BGP en lugar de buscar una solución al problema descrito anteriormente. Además, mi router de core soporte BGP.

Configuración en Mikrotik RouterOS.

Debemos preparar el router (o en su defecto un Switch con L3 & BGP) para aceptar sesiones BGP desde los nodos de k8s, MetalLB ejecutara un agente en todos los nodos de k8s y estos iniciaran una sesión BGP con nuestro router.

Configuración básica en RouterOS – CLI:

/routing bgp instance
set default as=64635 redistribute-connected=yes redistribute-static=yes router-id=10.45.254.2
/routing bgp peer
add multihop=no name=kube1 remote-address=172.22.35.25 remote-as=64636 ttl=default
add multihop=no name=kube2 remote-address=172.22.35.26 remote-as=64636 ttl=default
add multihop=no name=kube3 remote-address=172.22.35.27 remote-as=64636 ttl=default

 

Instalando MetalLB.

kubectl apply -f https://raw.githubusercontent.com/google/metallb/v0.7.3/manifests/metallb.yaml

También se puede instalar usando Helm, para más información: https://metallb.universe.tf/installation/

Revisamos nuestro entorno k8s para validar que tenemos los componentes de MetalLB en un correcto estado.

¡Excelente! Tenemos tres speaker, uno en cada nodo.

Configurando MetalLB.

Necesitamos un kind tipo ConfigMap para aplicar la configuración deseada a MetalLB.


apiVersion: v1

kind: ConfigMap

metadata:

namespace: metallb-system

name: config

data: config: |

peers:

- peer-address: 172.22.35.1

peer-asn: 64635

my-asn: 64636

address-pools:

- name: default

protocol: bgp

addresses:

- 172.22.35.64/26

bgp-advertisements:

- aggregation-length: 32

localpref: 100

communities:

- name: public

protocol: bgp

addresses:

- 200.1.154.64/26

auto-assign: false

bgp-advertisements:

- aggregation-length: 32

localpref: 100

communities:

Aplicamos el configMap:

Si todos nuestros parámetros son correctos, revisamos en el core router y debemos tener las sesiones BGP establecidas.

¿Qué tenemos?

En este punto deberemos contar con la opción de seleccionar una dirección IP, en mi caso podría ser del pool llamado default o del pool llamado public, se puede diferenciar que las IP del pool llamado public son ruteables y existen en la tabla de Internet (200.1.154.0/24).

Vamos a inicializar un pod que haga uso de una dirección del pool default. Para esto usare un contenedor con un servicio de SMTP el cual no necesita almacenamiento.

Deployment + Service

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
labels:
app: smtp
name: smtp
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: smtp
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
app: smtp
spec:
containers:
- env:
- name: RELAY_NETWORKS
value: 172.22.35.0/24:10.45.0.0/16:200.1.154.0/24
image: namshi/smtp
imagePullPolicy: IfNotPresent
name: smtp
ports:
- containerPort: 25
name: smtp-port
---
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
app: smtp
name: smtp
annotations:
metallb.universe.tf/allow-shared-ip: ekvm
metallb.universe.tf/address-pool: default
spec:
externalTrafficPolicy: Local
ports:
- name: smtp
nodePort: 25
port: 25
protocol: TCP
targetport: smtp-port
selector:
app: smtp
loadBalancerIP: 172.22.35.70
type: LoadBalancer</pre>

¡Excelente!

¡Nuestro servicio de SMTP responde en el IP asignado por MetalLB!

El próximo paso es el almacenamiento.

Kubernetes – montándonos un entorno de pruebas y producción – homeLAB – Parte2

Kubernetes – montándonos un entorno de pruebas y producción – homeLAB – Parte2

Hace unos meses escribí como iniciaba un entorno k8s en casa para hacer pruebas y entender las ventajas de esta nueva plataforma que está revolucionando la forma en que manejamos contenedores.

En mi primer intento me auxilie de varios Raspberry Pi los cuales son excelentes para pequeños procesos y pruebas, lamentablemente en mi caso no me gustó mucho el resultado. En un segundo intento, use la distribución de Rancher junto a su plataforma de administración que permite provisionar un cluster ya sea en premisa o en la nube, en todos mis intentos lo hice en premisa ya que no cuento con un presupuesto para pagar horas en Amazon o Google.

En esta “segunda parte” he llegado mucho más lejos y con mejores resultados, digamos que ahora me he tomado más tiempo para entender cómo se conectan las partes y he decidido administrar k8s sin ningún front-end. Esta vez no he usado RPi, todo se ha instalado en Ubuntu 16.04 con la distribución de Rancher llamada RKE (Rancher Kubernetes Engine). https://github.com/rancher/rke

Rancher Kubernetes Engine, an extremely simple, lightning fast Kubernetes installer that works everywhere.

Esta es la descripción del Proyecto en Github, se puede decir que es una de las maneras más rápidas de tener un cluster de k8s, al menos que yo he probado hasta el momento.

BOM:

  • 3 Ubuntu Servers 16.04 – en mi caso, estos son tres máquinas virtuales.
  • 1 Management server – Otra VM con Ubuntu, no necesariamente debe ser la misma versión que los nodos del cluster.
  • Un rango de direcciones IP para MetalLB.

Instalando RKE:

Primero debemos revisar cumplimos con el Node Requriments. Rancher tiene una tabla con las recomendaciones de OS, hardware y Networking.

https://rancher.com/docs/rancher/v2.x/en/installation/requirements/

Luego procederemos a preparar el equipo que usaremos para levantar el cluster de RKE.

https://rancher.com/docs/rke/v0.1.x/en/installation/

En mi caso fue descargar el binario para Linux y colocarlo en mi PATH, crear un cluster configuration file, este archivo puede tener cualquier nombre, para simplificar los pasos le pondremos cluster.yml igual que la documentación de Rancher.

En este archivo [cluster.yml] colocaremos la información de los nodos que usaremos para formar el cluster, antes de poder lanzar este proceso debemos cumplir con algunos pasos.

  1. En la maquina usada para administración necesitamos contar con SSH keys, desplegar este key en los nodos del cluster, RKE no envía password para hacer login a estos nodos, por esta razón debemos tener ssh-passwordless.
  2. Docker ya instalado y el usuario que cuenta con los keys de SSH en el grupo de Docker.

La forma más rápida de cumplir con este paso es usando un script ya preparado para realizar esta tarea, este script es oficialmente soportado por Rancher.

https://rancher.com/docs/rke/v0.1.x/en/os/

curl https://releases.rancher.com/install-docker/17.03.sh | sh
  1. Tener las herramientas necesarias en la máquina de administración, una de ellas es kubelet. En el caso de Ubuntu se instala como cualquier otro paquete usando APT.

En la siguiente imagen se puede apreciar el mensaje final del script para instalar Docker versión 17.03.

Ya podemos continuar con la preparación del cluster.yaml. Yo he usado uno bastante sencillo:

Ejecutando RKE para inicializar el cluster.

rke up –config rancher-cluster.yml

Al finalizar este proceso, tendremos un mensaje como:

Para probar que nuestros nodos responden y que tenemos un cluster de k8s, copiamos el archivo resultante de la operación anterior [kube_config_rancher-cluster.yml] a la ubicación por defecto usada por kubectl.

cp kube_config_rancher-cluster.yml ~./kube/config

Una alternativa cargarlo en la variable KUBECONFIG:

export KUBECONFIG=$(pwd)/kube_config_rancher-cluster.yml

Hacemos un kubectl get nodes:

… Houston, we have a cluster.

Alguien preguntara: ¿Ya témenos un cluster, ahora que hacemos con él?

En el estado actual, se pueden desplegar aplicaciones que no hagan uso de almacenamiento persistente (no me gusta el hostPath) y que hagan uso del host IP ya que no tenemos un LoadBalacer instalado. Para el próximo articulo estaré desplegando dos soluciones para que nuestro cluster se parezca más a un GKE o un EKS.

LibreNMS – Exportando data a Influxdb para crear gráficos en Grafana.

LibreNMS – Exportando data a Influxdb para crear gráficos en Grafana.

Grafana es un software para crear gráficos con distintas formas y colores, lo interesante es que la data a desplegar se puede manipular de muchas maneras y puede originarse desde diferentes softwares/plataformas.

Esto no es nada nuevo, personalmente vi Grafana hace más de 4 años en un subreddit, quede impresionado e inmediatamente busque como hacer los mismo, el primero intento fue exportando data desde un pfSense para crear un heatmap con los IPs bloqueados por el firewall. Luego de un tiempo se hizo más difícil mantener los equipos enviando información a InfluxDB para graficar en Grafana y decidí detener el proyecto.

LibreNMS soporta exportar a influxDB.

Para mi sorpresa encontré que LibreNMS soportaría exportar data desde su DB en MySQL a una InfluxDB, todo era experimental y en el primero intento nada funciono.

¡En el segundo intento todo funciono a la primera, impresionante!!

Se instaló InfluxDB en el mismo equipo que tiene LibreNMS, también se instaló Grafana. Una vez ambos softwares corriendo correctamente, la configuración de LibreNMS es muy sencilla. Pero primero crearemos la base de datos para LibreNMS en InfluxDB.

CREATE DATABASE librenms
CREATE USER admin WITH PASSWORD ‘admin’ WITH ALL PRIVILEGES
CREATE USER librenms WITH PASSWORD 'admin
GRANT ALL ON librenms TO admin
GRANT READ ON librenms_db TO grafana
exit

Configuración en config.php

$config['influxdb']['enable'] = true;
$config['influxdb']['transport'] = 'http'; # Default, other options: https, udp
$config['influxdb']['host'] = '127.0.0.1';
$config['influxdb']['port'] = '8086';
$config['influxdb']['db'] = 'librenms';
$config['influxdb']['username'] = 'admin';
$config['influxdb']['password'] = 'admin';
$config['influxdb']['timeout'] = 0; # Optional
$config['influxdb']['verifySSL'] = false; # Optional

Si revisamos el documento oficial de como conectar LibreNMS con InfluxDB ( https://docs.librenms.org/#Extensions/metrics/InfluxDB/ ), se puede ver que no se ha cambiado absolutamente nada, ya sé que debería de haber cambiado por lo menos el password, eso será para otra ocasión.

Como validamos que se está exportando data a InfluxDB ¿?

Podemos conectarnos a la InfkuxDB desde la línea de comando y ejecutar un query contra la base de datos de librenms que creamos anteriormente.

$ influx
> use librenms
> show measurements

En pantalla tendremos los nombres de nuestros equipos en LibreNMS con información relacionada a sus NIC, CPU, Espacio en Disco y mucho más, básicamente todo lo que tiene LibreNMS en los gráficos, será exportado a InfluxDB.

¿Pero, si ya tengo graficos en LibreNMS, para que quiero Grafana?

En mi caso, quería crear gráficos personalizados los cuales LibreNMS aún no soporta de una manera tan sencilla como Grafana.

Un ejemplo es relacionado al “app” de ATS-Watts, usando Grafana con la data recibida desde LibreNMS puede crear gráficos que muestren el consumo diario y lo calculen para saber el precio de cada KWh consumido.

En LibreNMS tengo esto:

En Grafana tengo esto:

Otro caso es tener todas las interfaces de equipos de bordes en una misma pantalla, en LibreNMS es muy sencillo hacerlo, pero si decidimos usar Grafana, al momento de compartir esta información es mucho más fácil que con LibreNMS.

El potencial de Grafana es grande, también tenemos la posibilidad de recibir datos desde equipos que no estén en LibreNMS y mezclar esta información en gráficos de la manera que más nos guste.

LibreNMS – Como crear una aplicación para monitorear componentes no soportados.

LibreNMS – Como crear una aplicación para monitorear componentes no soportados.

LibreNMS soporta muchos equipos y dentro de esos equipos existen componentes que también tienen soporte en la plataforma, pero que pasa cuando uno de estos componentes no existe o simplemente el grafico que necesitamos no ha sido creado.

En mi caso todo comenzó cuando agregue un ATS APC el cual tiene soporte SNMP, pero no reporta el consumo en Watts, en realidad tampoco lo reporta en el portal de administración del mismo. Pero si reporta los Amperes consumidos por la regleta en cualquiera de sus líneas de consumo.

Mi primer intento fue usando Collectd, cree un script en Bash que buscaba el OID con el valor de amperes consumidos en el árbol de SNMP y lo calculaba para obtener su equivalente en Watts. Inicialmente todo estaba de maravillas, pero luego el grafico se corrompió y dejo de reportar.

Últimamente eso me ha pasado con varios gráficos que obtenían su información de Collectd, en fin, decidí cambiar de Collectd a otra forma y al parecer la manera oficial en LibreNMS es “desarrollar” una aplicación.

Dentro de LibreNMS existen varias ubicaciones (paths) donde deben existir los componentes de la aplicación, por ejemplo, si se hace un find al nombre de una aplicación existente, se pueden ver cuáles son esas ubicaciones.

En la imagen, realizo una búsqueda del App llamado pi-hole, el autor de esta app describió lo fácil que es crear una aplicación con simplemente copiar pi-hole como base, eso fue lo que hice para resolver el monitoreo de consumo de mi ATS APC.

Nota: en la imagen se puede ver un archivo .rrd, en este caso es el resultado de los valores tomados del host resolver.aanetworks.org que cuenta con pi-Hole y tiene el App habilitado.

ATS-Watts:

Este es el nombre que le he asignado a la nueva “app” que resultó de copiar los archivos usados en la aplicación de pi-hole.

Luego de remover los valores que no necesitaba, en pi-hole se generan alrededor de 12 gráficos. En el caso de ATS-Watts, solo estaré generando uno que es el resultado de un script.

La funcionalidad de este script es colectar la información en SNMP del ATS, este ATS no cuenta con un OID donde muestre el valor en Watts, cuenta con dos valores OID donde la unión con un (.) de estos es el total de Amperes consumidos, luego los multiplica por 110 para tener los Watts.

ats-watts.inc.php:

Este es el primero componente de la aplicación, se puede ver como tomo el OID generado por el script y se asigna a las variables para ser graficadas por rrd.

Para seguir entendiendo como funciona ATS-Watts, se puede ver el resto del código en el repositorio Git que me he montado para jugar un poco y tener mejor control de algunos proyectos que tengo en mente, hello Kubernetes yaml!.

https://git.aanetworks.org/ariel/ATS-Watts

Habilitando la aplicación a un host de LibreNMS:

Una vez terminado de editar los archivos correspondientes, debemos tener la habilita de encender la nueva aplicación en el host que designemos o donde se haya habilitado el script, en mi caso el script de ats-watts.sh se habilito en el mismo servidor de LibreNMS.