Componentes principales de un clúster Kubernetes

Kubernetes es una plataforma de código abierto para gestionar cargas de trabajo y servicios en contenedores que facilita la configuración declarativa y la automatización. El nombre de Kubernetes tiene su origen en el griego, que significa timonel o piloto. Es portátil y extensible, y cuenta con un ecosistema de rápido crecimiento. Los servicios y herramientas de Kubernetes están ampliamente disponibles.

En este artículo, recorreremos una visión a 10.000 pies de los principales componentes de Kubernetes, desde lo que compone cada contenedor, hasta cómo se despliega y programa un contenedor en un pod en cada uno de los trabajadores. Es crucial comprender todas las particularidades del clúster Kubernetes para poder desplegar y diseñar una solución basada en Kubernetes como orquestador de aplicaciones en contenedores.

He aquí un resumen de las cosas que vamos a cubrir en este artículo:

  • Componentes del panel de control
  • Los componentes del trabajador de Kubernetes
  • Los pods como bloques de construcción básicos
  • Los servicios de Kubernetes, los equilibradores de carga y los controladores Ingress
  • Despliegues de Kubernetes y conjuntos de demonios
  • El almacenamiento persistente en Kubernetes

El plano de control de Kubernetes

Los nodos maestros de Kubernetes son donde viven los servicios del plano de control principal; no todos los servicios tienen que residir en el mismo nodo; sin embargo, por razones de centralización y practicidad, a menudo se despliegan de esta manera. Evidentemente, esto plantea problemas de disponibilidad de los servicios; sin embargo, pueden superarse fácilmente teniendo varios nodos y proporcionando peticiones de equilibrio de carga para conseguir un conjunto denodos maestros de alta disponibilidad.

Los nodos maestros se componen de cuatro servicios básicos:

  • El kube-apiserver
  • El kube-scheduler
  • El kube-controller-manager
  • La base de datos etcd

Los nodos maestros pueden ejecutarse en servidores de metal desnudo, en máquinas virtuales o en una nube privada o pública, pero no se recomienda ejecutar cargas de trabajo de contenedores en ellos. Veremos más sobre esto más adelante.

El siguiente diagrama muestra los componentes de los nodos maestros de Kubernetes:

Componentes de los nodos maestros de Kubernetes

El kube-apiserver

El servidor de la API es lo que une todo. Es la API REST del clúster que recibe manifiestos para crear, actualizar y eliminar objetos de la API como servicios, pods, Ingress y otros.

El kube-apiserver es el único servicio con el que debemos hablar; también es el único que escribe y habla con la base de datosetcdpara registrar el estado del clúster. Con el comandokubectl, enviaremos comandos para interactuar con él. Esta será nuestra navaja suiza cuando se trate de Kubernetes.

El kube-controller-manager

El demonio kube-controller-manager, en pocas palabras, es un conjunto de bucles de control infinitos que se envían por simplicidad en un único binario. Vigila el estado deseado definido del clúster y se asegura de que se cumpla y satisfaga moviendo todos los bits y piezas necesarios para lograrlo. El kube-controller-manager no es sólo un controlador; contiene varios bucles diferentes que vigilan distintos componentes del clúster. Algunos de ellos son el controlador de servicios, el controlador de espacios de nombres, el controlador de cuentas de servicios y muchos otros. Puedes encontrar cada controlador y su definición en el repositorio GitHub de Kubernetes: https://github.com/kubernetes/kubernetes/tree/master/pkg/controller.

El programador kube

El programador de kube programa tus pods recién creados en nodos con espacio suficiente para satisfacer las necesidades de recursos de los pods. Básicamente, escucha al kube-apiserver y al kube-controller-manager para los pods recién creados, que se ponen en cola y luego son programados en un nodo disponible por el planificador. La definición de kube-scheduler se puede encontrar aquí: https://github.com/kubernetes/kubernetes/blob/master/pkg/scheduler.

Además de los recursos informáticos, el programador kube también lee las reglas de afinidad y antiafinidad de los nodos para saber si un nodo puede o no puede ejecutar ese pod.

La base de datos etcd

La base de datos etcd es un almacén de valores clave consistente y muy fiable que se utiliza para almacenar el estado del clúster Kubernetes. Contiene el estado actual de los pods en los que se está ejecutando el nodo, cuántos nodos tiene actualmente el clúster, cuál es el estado de esos nodos, cuántas réplicas de despliegue se están ejecutando, los nombres de los servicios y otros.

Como hemos dicho antes, sólo el kube-apiserver habla con la base de datos etcd. Si el kube-controller-manager necesita comprobar el estado del clúster, pasará por el servidor API para obtener el estado de la base de datosetcd, en lugar de consultar directamente el almacénetcd. Lo mismo ocurre con el kube-scheduler si el programador necesita hacer saber que un pod ha sido detenido o asignado a otro nodo; informará al servidor de la API, y éste almacenará el estado actual en la base de datos de etcd.

Con etcd, hemos cubierto todos los componentes principales de nuestros nodos maestros de Kubernetes, de modo que estamos preparados para gestionar nuestro clúster. Pero un clúster no sólo se compone de maestros; aún necesitamos los nodos que realizarán el trabajo pesado ejecutando nuestras aplicaciones.

Nodos trabajadores de Kubernetes

Los nodos trabajadores que realizan esta tarea en Kubernetes se llaman simplemente nodos. Anteriormente, en torno a 2014, se les llamaba minions, pero este término se sustituyó posteriormente por el de nodos a secas, ya que el nombre se confundía con la terminología de Salt y hacía pensar a la gente que Salt desempeñaba un papel importante en Kubernetes.

Estos nodos son el único lugar en el que ejecutarás cargas de trabajo, ya que no se recomienda tener contenedores o cargas en los nodos maestros, ya que deben estar disponibles para gestionar todo el clúster. Los nodos son muy sencillos en cuanto a componentes; sólo necesitan tres servicios para cumplir su cometido:

  • Kubelet
  • Kube-proxy
  • Tiempo de ejecución del contenedor

Vamos a explorar estos tres componentes con un poco más de profundidad.

El kubelet

El kubelet es un componente de bajo nivel de Kubernetes y uno de los más importantes después del kube-apiserver; ambos componentes son esenciales para el aprovisionamiento de pods/contenedores en el clúster. El kubelet es un servicio que se ejecuta en los nodos de Kubernetes y escucha al servidor API para la creación de pods. El kubelet sólo se encarga de iniciar/detener y asegurarse de que los contenedores de los pods están sanos; el kubelet no podrá gestionar ningún contenedor que no haya sido creado por él.

El kubelet logra los objetivos hablando con el tiempo de ejecución del contenedor a través de la interfaz de tiempo de ejecución del contenedor (CRI). La IRC proporciona enchufabilidad al kubelet a través de un cliente gRPC, que es capaz de hablar con diferentes tiempos de ejecución de contenedores. Como hemos mencionado antes, Kubernetes admite múltiples tiempos de ejecución de contenedores para desplegarlos, y así es como consigue una compatibilidad tan diversa con diferentes motores.

Puedes consultar el código fuente de kubelet en https://github.com/kubernetes/kubernetes/tree/master/pkg/kubelet.

El kube-proxy

El kube-proxy es un servicio que reside en cada nodo del clúster y es el que hace posible la comunicación entre pods, contenedores y nodos. Este servicio vigila el kube-apiserver en busca de cambios en los servicios definidos (el servicio es una especie de equilibrador de carga lógico en Kubernetes; profundizaremos en los servicios más adelante en este artículo) y mantiene la red actualizada mediante reglasiptablesque reenvían el tráfico a los puntos finales correctos. Kube-proxy también establece reglas eniptablesque realizan un equilibrio de carga aleatorio entre los pods que están detrás de un servicio.

Aquí tienes un ejemplo de una reglaiptablesrealizada por el kube-proxy:

-A KUBE-SERVICES -d 10.0.162.61/32 -p tcp -m comment –comment «default/example: has no endpoints» -m tcp –dport 80 -j REJECT –reject-with icmp-port-unreachable

Ten en cuenta que se trata de un servicio sin puntos finales (no hay pods detrás de él).

Tiempo de ejecución del contenedor

Para poder poner en marcha los contenedores, necesitamos un motor de ejecución decontenedores. Este es el motor base que creará los contenedores en el núcleo de los nodos para que nuestros pods se ejecuten. El kubelet hablará con este tiempo de ejecución y hará girar o detener nuestros contenedores bajo demanda.

Actualmente, Kubernetes admite cualquier tiempo de ejecución de contenedores compatible con OCI, como Docker,rkt,runc,runsc, etc.

Puedes consultar este https://github.com/opencontainers/runtime-spec para saber más sobre todas las especificaciones de la página Git-Hub de la OCI.

Ahora que hemos explorado todos los componentes básicos que forman un clúster, veamos qué se puede hacer con ellos y cómo Kubernetes nos va a ayudar a orquestar y gestionar nuestras aplicaciones en contenedores.

Objetos de Kubernetes

Los objetos de Kubernetes son exactamente eso: son objetos lógicos persistentes o abstracciones que representarán el estado de tu clúster. Tú eres el encargado de decirle a Kubernetes cuál es tu estado deseado para ese objeto, de modo que pueda trabajar para mantenerlo y asegurarse de que el objeto existe.

Para crear un objeto, hay dos cosas que debe tener: un estado y su especificación. El estado lo proporciona Kubernetes, y es el estado actual del objeto. Kubernetes gestionará y actualizará ese estado según sea necesario para que esté de acuerdo con tu estado deseado. El campo deespecificación, por otro lado, es lo que tú proporcionas a Kubernetes, y es lo que le dices para describir el objeto que deseas. Por ejemplo, la imagen que quieres que ejecute el contenedor, el número de contenedores de esa imagen que quieres ejecutar, etc.

Cada objeto tiene campos deespecificaciónespecíficos para el tipo de tarea que realizan, y tú proporcionarás estas especificaciones en un archivo YAML que se envía al kube-apiserver conkubectl, que lo transforma en JSON y lo envía como una solicitud de API. Profundizaremos en cada objeto y sus campos de especificación más adelante en este artículo.

He aquí un ejemplo de un YAML enviado akubectl:

cat << EOF | kubectl create -f -kind: ServiceapiVersion: v1metadata: Nombre: frontend-servicespec: selector: web: frontend ports: – protocolo: puerto TCP: 80 targetPort: 9256EOF

Los campos básicos de la definición del objeto son los primeros, y éstos no variarán de un objeto a otro y son muy autoexplicativos. Echemos un vistazo rápido a ellos:

  • Tipo: El campotipo indica a Kubernetes qué tipo de objeto estás definiendo: un pod, un servicio, un despliegue, etc.
  • apiVersion: Como Kubernetes admite varias versiones de la API, tenemos que especificar la ruta de la API REST a la que queremos enviar nuestra definición
  • metadatos: Se trata de un campo anidado, lo que significa que tienes varios subcampos más de metadatos, donde escribirás definiciones básicas como el nombre de tu objeto, asignándolo a un espacio de nombres específico, y también le pondrás una etiqueta para relacionar tu objeto con otros objetos de Kubernetes

Así pues, ya hemos repasado los campos más utilizados y su contenido; puedes saber más sobre las convenciones de la API de Kuberntes en https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md

Algunos de los campos del objeto pueden modificarse posteriormente, una vez creado el objeto, pero eso dependerá del objeto y del campo que quieras modificar.

A continuación se ofrece una breve lista de los distintos objetos de Kubernetes que puedes crear:

  • Pod
  • Volumen
  • Servicio
  • Despliegue
  • Entrada
  • Secreto
  • ConfigMap

Y hay muchos más.

Veamos con más detalle cada uno de estos elementos.

Pods – la base de Kubernetes

Los pods son los objetos más básicos de Kubernetes y también los más importantes. Todo gira en torno a ellos; podemos decir que Kubernetes es para los pods. Todos los demás objetos están aquí para servirlos, y todas las tareas que realizan son para que los pods alcancen el estado deseado.

Entonces, ¿qué es un pod y por qué son tan importantes los pods?

Un pod es un objeto lógico que ejecuta uno o más contenedores juntos en el mismo espacio de nombres de red, la mismacomunicación entre procesos (IPC) y, a veces, dependiendo de la versión de Kubernetes, el mismo espacio de nombres deID de proceso (PID). Esto se debe a que son los que van a ejecutar nuestros contenedores y, por tanto, serán el centro de atención. El objetivo de Kubernetes es ser un orquestador de contenedores, y con los pods, hacemos posible la orquestación.

Como hemos mencionado antes, los contenedores de un mismo pod viven en una «burbuja» en la que pueden hablar entre sí a través de localhost, ya que son locales entre sí. Un contenedor de un pod tiene la misma dirección IP que el otro contenedor porque comparten un espacio de nombres de red, pero en la mayoría de los casos, se ejecutará de forma individual, es decir, un solo contenedor por pod. Los contenedores múltiples por pod sólo se utilizan en escenarios muy específicos, como cuando una aplicación requiere un ayudante como un empujador de datos o un proxy que necesita comunicarse de forma rápida y resistente con la aplicación principal.

La forma de definir un pod es la misma que para cualquier otro objeto de Kubernetes: mediante un YAML que contiene todas las especificaciones y definiciones del pod:

tipo: PodapiVersion: v1metadata:name: hello-podlabels: hello: podspec: containers: – name: hello-container image: alpine args: – echo – «Hello World»

Vamos a repasar las definiciones básicas de los pods necesarias en el campo spec para crear nuestro pod:

  • Contenedores: El contenedor es una matriz; por tanto, tenemos un conjunto de varios subcampos bajo él. Básicamente, es lo que define los contenedores que se van a ejecutar en el pod. Podemos especificar un nombre para el contenedor, la imagen de la que va a ser un spin-off, y los argumentos o el comando que necesitamos que ejecute. La diferencia entre argumentos y comandos es la misma que la que existe entre CMDy ENTRYPOINT. Ten en cuenta que todos los campos que acabamos de repasar son para el conjunto de contenedores. No forman parte directamente de la especificación del pod.
  • restartPolicy: Este campo es exactamente eso: le dice a Kubernetes qué hacer con un contenedor, y se aplica a todos los contenedores del pod en el caso de un código de salida cero o distinto de cero. Puedes elegir entre cualquiera de las dos opciones, Nunca, EnFallo o Siempre. Siempre será la opción por defecto en caso de que no se defina una RestartPolicy.

Estas son las especificaciones más básicas que vas a declarar en un pod; otras especificaciones requerirán que tengas un poco más de conocimiento sobre cómo utilizarlas y cómo interactúan con otros objetos de Kubernetes. Las revisaremos más adelante en este artículo; algunas de ellas son las siguientes:

  • Volumen
  • Env
  • Puertos
  • dnsPolicy
  • initContainers
  • nodeSelector
  • Límites de recursos y solicitudes

Para ver los pods que se están ejecutando actualmente en tu clúster, puedes ejecutar kubectl getpods:

[email protected]:~$ kubectl get podsNAME READY STATUS RESTARTS AGEbusybox 1/1 Running 120 5d

También puedes ejecutarkubectl describe pods sin especificar ningún pod. Esto imprimirá una descripción de cada pod que se esté ejecutando en el clúster. En este caso, será sólo el podbusybox, ya que es el único que se está ejecutando actualmente:

[email protected]:~$ kubectl describe podsName: busyboxNamespace:defaultPrioridad:0PriorityClassName: <none>Nodo:aks-agentpool-10515745-2/10.240.0.6Hora de inicio: Wed, 19 Sep 2018 14:23:30 -0600Etiquetas: <none>Anotaciones: <ninguno>Estado: En ejecuciónIP:10.244.1.7Contenedores: busybox:[…] (Salida truncada para facilitar la lectura)Eventos:Tipo Razón Edad Desde Mensaje—- —— —- ——-Normal Extraído 45s (x121 sobre 5d) kubelet, aks-agentpool-10515745-2 Imagen de contenedor «busybox» ya presente en la máquinaNormal Creado 44s (x121 sobre 5d) kubelet, aks-agentpool-10515745-2 Contenedor creadoNormal Iniciado 44s (x121 sobre 5d) kubelet, aks-agentpool-10515745-2 Contenedor iniciado

Los pods son mortales. Una vez que mueren o se eliminan, no se pueden recuperar. Su IP y los contenedores que se ejecutaban en él desaparecerán; son totalmente efímeros. Los datos de los pods que se montan como volumen pueden sobrevivir o no, dependiendo de cómo los hayas configurado. Si nuestros pods mueren y los perdemos, ¿cómo nos aseguramos de que todos nuestros microservicios están funcionando? Pues la respuesta son los despliegues.

Despliegues

Los pods por sí mismos no son muy útiles, ya que no es muy eficiente tener más que una sola instancia de nuestra aplicación ejecutándose en un solo pod. Aprovisionar cientos de copias de nuestra aplicación en diferentes pods sin tener un método para buscarlas todas se nos irá de las manos muy rápidamente.

Aquí es donde entran en juego los despliegues. Con los despliegues, podemos gestionar nuestros pods con un controlador. Esto nos permite no sólo decidir cuántos queremos ejecutar, sino que también podemos gestionar las actualizaciones cambiando la versión de la imagen o la propia imagen que están ejecutando nuestros contenedores. Los despliegues son con los que vas a trabajar la mayor parte del tiempo. Tanto los despliegues como los pods y cualquier otro objeto que hemos mencionado antes, tienen su propia definición dentro de un archivo YAML:

apiVersion: apps/v1kind: Deploymentmetadata: name: nginx-deployment labels: deployment: nginxspec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: – name: nginx image: nginx:1.7.9 puertos: – containerPort: 80

Empecemos a explorar su definición.

Al principio del YAML, tenemos campos más generales, comoapiVersion,kind ymetadata. Pero bajospeces donde encontraremos las opciones específicas para este Objeto API.

Bajo spec, podemos añadir los siguientes campos:

Selector: Con el campo Selector, el despliegue sabrá a qué pods dirigirse cuando se apliquen los cambios. Hay dos campos que utilizarás en el selector:matchLabelsymatchExpressions. ConmatchLabels, el selector utilizará las etiquetas de los pods (pares clave/valor). Es importante tener en cuenta que todas las etiquetas que especifiques aquí seránAND. Esto significa que el pod requerirá que tenga todas las etiquetas que especifiques enmatchLabels.

Réplicas: Esto indicará el número de pods que el despliegue necesita para seguir funcionando a través del controlador de replicación; por ejemplo, si especificas tres réplicas, y uno de los pods muere, el controlador de replicación observará la especificación de las réplicas como el estado deseado e informará al programador para que programe un nuevo pod, ya que el estado actual es ahora 2 desde que el pod murió.

RevisionHistoryLimit: Cada vez que realizas un cambio en el despliegue, este cambio se guarda como una revisión del despliegue, que más tarde puedes revertir a ese estado anterior o mantener un registro de lo que se cambió. Puedes consultar el historial conkubectlrollout history deployment/<nombre del despliegue>. ConrevisionHistoryLimit, puedes establecer un número que indique cuántos registros quieres guardar.

Estrategia: Esto te permitirá decidir cómo quieres manejar cualquier actualización o escala horizontal del pod. Para sobrescribir el valor por defecto, que esrollingUpdate, tienes que escribir la clave detipo, donde puedes elegir entre dos valores:recrearorollingUpdate.

Mientras querecreares una forma rápida de actualizar tu despliegue, borrará todos los pods y los sustituirá por otros nuevos, pero implicará que tendrás que tener en cuenta que habrá un tiempo de inactividad del sistema para este tipo de estrategia. ElrollingUpdate, en cambio, es más suave y lento y es ideal para aplicaciones con estado que pueden reequilibrar sus datos. ElrollingUpdateabre la puerta a dos campos más, que sonmaxSurgeymaxUnavailable.

El primero será cuántos pods por encima de la cantidad total quieres al realizar una actualización; por ejemplo, un despliegue con 100 pods y un 20% demaxSurgecrecerá hasta un máximo de 120 pods mientras se actualiza. La siguiente opción te permitirá seleccionar cuántos pods del porcentaje estás dispuesto a matar para sustituirlos por otros nuevos en un escenario de 100 pods. En los casos en los que haya un 20% demaxDisponibilidad, sólo se matarán 20 vainas y se sustituirán por otras nuevas antes de seguir sustituyendo el resto del despliegue.

Plantilla: Se trata de un campo anidado de especificaciones de pods en el que se incluirán todas las especificaciones y metadatos de los pods que va a gestionar el despliegue.

Hemos visto que, con los despliegues, gestionamos nuestros pods, y nos ayudan a mantenerlos en el estado que deseamos. Todos estos pods siguen en algo llamado red declúster, que es una red cerrada en la que sólo los componentes del clúster Kubernetes pueden hablar entre sí, teniendo incluso su propio conjunto de rangos de IP. ¿Cómo nos comunicamos con nuestros pods desde el exterior? ¿Cómo llegamos a nuestra aplicación? Aquí es donde entran en juego los servicios.

Servicios:

El nombre deservicio no describe completamente lo que los servicios hacen realmente en Kubernetes. Los servicios de Kubernetes son los que dirigen el tráfico a nuestros pods. Podemos decir que los servicios son los que unen los pods.

Imaginemos que tenemos una aplicación típica de tipo frontend/backend en la que tenemos nuestros pods del frontend hablando con los del backend a través de las direcciones IP de los pods. Si un pod del backend muere, perdemos la comunicación con nuestro backend. Esto no sólo se debe a que el nuevo pod no tendrá la misma dirección IP del pod que murió, sino que ahora también tenemos que reconfigurar nuestra aplicación para utilizar la nueva dirección IP. Este problema y otros similares se resuelven con los servicios.

Un servicio es un objeto lógico que indica al kube-proxy que cree reglas iptables en función de los pods que están detrás del servicio. Los servicios configuran sus puntos finales, que es como se llama a los pods que están detrás de un servicio, del mismo modo que los despliegues saben qué pods controlar, el campo selector y las etiquetas de los pods.

Este diagrama muestra cómo los servicios utilizan las etiquetas para gestionar el tráfico:

Gestión del tráfico de Kubernetes

Los servicios no sólo harán que kube-proxy cree reglas para enrutar el tráfico; también activarán algo llamadokube-dns.

Kube-dns es un conjunto de pods con contenedoresSkyDNSque se ejecutan en el clúster y que proporcionan un servidor y un reenviador DNS, que crearán registros para los servicios y, a veces, para los pods para facilitar su uso. Siempre que crees un servicio, se creará un registro DNS que apunte a la dirección IP interna del clúster del servicio con la forma nombre-servicio.espacio-nombre.svc.clúster.local. Puedes obtener más información sobre las especificaciones DNS de Kubernetes aquí:https://github.com/kubernetes/dns/blob/master/docs/specification.md.

Volviendo a nuestro ejemplo, ahora sólo tendremos que configurar nuestra aplicación para que hable con elnombre de dominio completamente cualificado (FQDN) del servicio para hablar con nuestros pods del backend. De este modo, no importará la dirección IP que tengan los pods y los servicios. Si un pod detrás del servicio muere, el servicio se encargará de todo utilizando el registro A, ya que podremos decirle a nuestro frontend que dirija todo el tráfico a mi-svc. La lógica del servicio se encargará de todo lo demás.

Hay varios tipos de servicio que puedes crear cuando declaras el objeto a crear en Kubernetes. Vamos a repasarlos para ver cuál es el más adecuado para el tipo de trabajo que necesitamos:

ClusterIP: Es el servicio por defecto. Siempre que crees un servicio ClusterIP, se creará un servicio con una dirección IP interna del clúster que sólo será enrutable dentro del clúster de Kubernetes. Este tipo es ideal para los pods que sólo necesitan hablar entre sí y no salir del clúster.

NodePort: Cuando creas este tipo de servicio, por defecto se asignará un puerto aleatorio entre30000y32767para reenviar el tráfico a los pods de los extremos del servicio. Puedes anular este comportamiento especificando un puerto de nodo en la matriz depuertos. Una vez definido esto, podrás acceder a tus pods a través de<Nodos-IP>:<Puerto-Nodo>. Esto es útil para acceder a tus pods desde fuera del clúster a través de la dirección IP del Nodo.

LoadBalancer: La mayoría de las veces, ejecutarás Kubernetes en un proveedor de la nube. El tipo LoadBalancer es ideal para estas situaciones, ya que podrás asignar direcciones IP públicas a tu servicio a través de la API de tu proveedor de la nube. Es el servicio ideal para cuando quieras comunicarte con tus pods desde fuera de tu clúster. Con el LoadBalancer, no sólo podrás asignar una dirección IP pública, sino también, utilizando Azure, asignar una dirección IP privada de tu red privada virtual. Así, podrás hablar con tus pods desde Internet o internamente en tu subred privada.

Revisemos la definición de YAML de un servicio:

apiVersion: v1kind: Servicemetadata: name: my-servicespec: selector: app: front-end type: NodePort ports: – name: http port: 80 targetPort: 8080 nodePort: 30024 protocol: TCP

El YAML de un servicio es muy sencillo, y las especificaciones variarán según el tipo de servicio que estés creando. Pero lo más importante que debes tener en cuenta son las definiciones de los puertos. Vamos a verlas:

  • Puerto: Es el puerto del servicio que se expone
  • targetPort: Es el puerto de los pods al que el servicio envía el tráfico
  • nodePort: Es el puerto que se expone

Aunque ahora entendemos cómo podemos comunicarnos con los pods de nuestro clúster, todavía tenemos que entender cómo vamos a gestionar el problema de la pérdida de nuestros datos cada vez que se termina un pod. Aquí es donde entran en juego los VolúmenesPersistentes(PV).

Kubernetes y el almacenamiento persistente

El almacenamiento persistente en el mundo de los contenedores es un problema serio. El único almacenamiento que es persistente a través de las ejecuciones del contenedor son las capas de la imagen, y son de sólo lectura. La capa donde se ejecuta el contenedor es de lectura/escritura, pero todos los datos de esta capa se borran una vez que el contenedor se detiene. Con los pods, esto es igual. Cuando un contenedor muere, los datos escritos en él desaparecen.

Kubernetes tiene un conjunto de objetos para gestionar el almacenamiento en los pods. El primero del que hablaremos es el de los volúmenes.

Volúmenes

Los volúmenes resuelven uno de los mayores problemas cuando se trata de almacenamiento persistente. En primer lugar, los volúmenes no son realmente objetos, sino una definición de las especificaciones de un pod. Cuando creas un pod, puedes definir un volumen en el campo de especificaciones del pod. Los contenedores de este pod podrán montar el volumen en su espacio de nombres de montaje, y el volumen estará disponible a pesar de los reinicios o caídas del contenedor. Sin embargo, los volúmenes están ligados a los pods, y si el pod se elimina, el volumen también desaparecerá. Los datos del volumen son otra historia; la persistencia de los datos dependerá del backend de ese volumen.

Kubernetes admite varios tipos de volúmenes o fuentes de volúmenes y cómo se llaman en las especificaciones de la API, que van desde los montajes del sistema de archivos del nodo local, los discos virtuales de los proveedores de la nube y los volúmenes respaldados por el almacenamiento definido por software. Los montajes del sistema de archivos local son los más comunes que verás cuando se trata de volúmenes regulares. Es importante tener en cuenta que la desventaja de utilizar el sistema de archivos del nodo local es que los datos no estarán disponibles en todos los nodos del clúster, sino sólo en el nodo donde se programó el pod.

Examinemos cómo se define un pod con un volumen en YAML:

apiVersion: v1kind: Podmetadata: name: test-pdspec: containers: – image: k8s.gcr.io/test-webserver name: test-container volumeMounts: – mountPath: /prueba-pd nombre: prueba-volumen volúmenes: – nombre: prueba-volumen hostPath: ruta: /datos tipo: Directorio

Fíjate en que hay un campo llamado volúmenes en spec y luego hay otro llamado volumeMounts.

El primer campo (volúmenes) es donde defines el volumen que quieres crear para ese pod. Este campo siempre requerirá un nombre y luego una fuente de volumen. Dependiendo de la fuente, los requisitos serán diferentes. En este ejemplo, la fuente sería hostPath, que es el sistema de archivos local de un nodo. hostPath admite varios tipos de asignaciones, que van desde directorios, archivos, dispositivos de bloque e incluso sockets Unix.

En el segundo campo, volumeMounts, tenemos mountPath, que es donde defines la ruta dentro del contenedor en la que quieres montar tu volumen. El parámetro nombre es la forma de especificar al pod qué volumen debe utilizar. Esto es importante porque puedes tener varios tipos de volúmenes definidos en volumes, y el nombre será la única forma de que el pod sepa cuál

Puedes obtener más información sobre los diferentes tipos de volúmenes aquí https://kubernetes.io/docs/concepts/storage/volumes/#types-of-volumes y en el documento de referencia de la API de Kubernetes (https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.11/#volume-v1-core).

Que los volúmenes mueran con los pods no es lo ideal. Necesitamos un almacenamiento que persista, y así es como surgió la necesidad de los PV.

Volúmenes persistentes, reclamaciones de volúmenes persistentes y clases de almacenamiento

La principal diferencia entre los volúmenes y los PV es que, a diferencia de los volúmenes, los PV son en realidad objetos de la API de Kubernetes, por lo que puedes gestionarlos individualmente como entidades separadas, y por tanto persisten incluso después de que se elimine un pod.

Puede que te preguntes por qué esta subsección tiene mezclados los PV, las demandas de volumenpersistente(PVC) y las clases de almacenamiento. Esto se debe a que todos ellos dependen unos de otros, y es crucial entender cómo interactúan entre sí para aprovisionar el almacenamiento de nuestros pods.

Empecemos por los PV y los PVC. Al igual que los volúmenes, los PV tienen una fuente de almacenamiento, por lo que se aplica el mismo mecanismo que tienen los volúmenes. Tendrás un clúster de almacenamiento definido por software que proporcione un número de unidad lógica(LUN), un proveedor de la nube que proporcione discos virtuales o incluso un sistema de archivos local al nodo de Kubernetes, pero aquí, en lugar de llamarse fuentes de volumen, se denominan tipos de volumenpersistentes.

Los PV son más o menos como los LUN en una matriz de almacenamiento: los creas, pero sin un mapeo; son sólo un montón de almacenamiento asignado a la espera de ser utilizado. Los PVC son como las asignaciones de LUN: están respaldados o vinculados a un PV y también son lo que realmente defines, relacionas y pones a disposición del pod que luego puede utilizar para sus contenedores.

La forma de utilizar las PVCs en los pods es exactamente la misma que con los volúmenes normales. Tienes dos campos: uno para especificar qué PVC quieres usar, y otro para decirle al pod en qué contenedor debe usar ese PVC.

El YAML para la definición de un objeto de la API de PVC debe tener el siguiente código

apiVersion: v1kind: PersistentVolumeClaimmetadata: name: gluster-pvc spec: accessModes: – ReadWriteMany resources: requests: storage: 1Gi

El YAML del pod debe tener el siguiente código

tipo: PodapiVersion: v1metadata: name: mypodspec: containers: – name: myfrontend image: nginx volumeMounts: – mountPath: «/mnt/gluster» name: volume volumes: – name: volume persistentVolumeClaim: claimName: gluster-pvc

Cuando un administrador de Kubernetes crea un PVC, hay dos formas de satisfacer esta petición

  • Estática: Ya se han creado varios PV, y entonces, cuando un usuario crea un PVC, cualquier PV disponible que pueda satisfacer los requisitos se vinculará a ese PVC.
  • Dinámica: Algunos tipos de PV pueden crear PV en función de las definiciones de PVC. Cuando se crea un PVC, el tipo de PV creará dinámicamente un objeto PV y asignará el almacenamiento en el backend; esto es aprovisionamiento dinámico. La pega del aprovisionamiento dinámico es que requiere un tercer tipo de objeto de almacenamiento de Kubernetes, llamado clase dealmacenamiento.

Las clases de almacenamiento son como una forma dejerarquizar tu almacenamiento. Puedes crear una clase que aprovisione volúmenes de almacenamiento lentos, u otra con unidades SSD hiperrápidas. Sin embargo, las clases de almacenamiento son un poco más complejas que la simple jerarquización. Como hemos mencionado en las dos formas de crear PVC, las clases de almacenamiento son las que hacen posible el aprovisionamiento dinámico. Cuando trabajas en un entorno de nube, no quieres estar creando manualmente cada disco backend para cada PVC. Las clases de almacenamiento configurarán algo llamadoaprovisionador, que invoca el complemento de volumen necesario para hablar con la API de tu proveedor de la nube. Cada aprovisionador tiene su propia configuración para poder hablar con el proveedor de la nube o de almacenamiento especificado.

Puedes aprovisionar clases de almacenamiento de la siguiente manera; éste es un ejemplo de una clase de almacenamiento que utiliza Azure-disk como aprovisionador de disco:

kind: StorageClassapiVersion: storage.k8s.io/v1metadata: name: my-storage-classprovisioner: kubernetes.io/azure-diskparameters: storageaccounttype: Standard_LRS kind: Shared

Cada clase de almacenamiento y tipo de PV tendrá requisitos y parámetros diferentes, así como los volúmenes, y ya hemos tenido una visión general de cómo funcionan y para qué podemos utilizarlos. Conocer las clases de almacenamiento y los tipos de PV específicos dependerá de tu entorno; puedes aprender más sobre cada uno de ellos haciendo clic en los siguientes enlaces:

En este artículo, hemos aprendido qué es Kubernetes, sus componentes y cuáles son las ventajas de utilizar la orquestación. Con esto, identificar cada uno de los objetos de la API de Kubernetes, su propósito y sus casos de uso debería ser fácil. Ahora deberías ser capaz de entender cómo los nodos maestros controlan el clúster y la programación de los contenedores en los nodos trabajadores.

Si te ha resultado útil este artículo, ‘Linux práctico para arquitectosdebería serte útil. Con este libro, abarcarás todo, desde los componentes y funcionalidades de Linux hasta el soporte de hardware y software, lo que te ayudará a implementar y poner a punto soluciones eficaces basadas en Linux. Se te llevará a través de una visión general de la metodología de diseño de Linux y de los conceptos básicos del diseño de una solución. Si eres un administrador de sistemas Linux, un ingeniero de soporte de Linux, un ingeniero de DevOps, un consultor de Linux o cualquier persona que quiera aprender o ampliar sus conocimientos en arquitectura, este libro es para ti.

También te podría gustar...