En el artículo anterior les hablé sobre los aspectos generales de Kubernetes, por qué adoptar los microservicios como estándar de desarrollo y cómo facilita Kubernetes este proceso. En este artículo iremos un poco más allá de lo básico, y abordaremos todo lo que rodea a una aplicación desplegada en Kubernetes. Es decir, como ya definimos cómo funciona el cluster a nivel físico, es necesario entender cuáles son las piezas internas móviles con las que interactúan los nodos.
Pods
El principal y más importante componente del sistema es el pod. Es importante mencionar que Kubernetes no trata directamente con containers - utiliza el concepto de múltiples containers co-located. Un grupo de containers puede agruparse en un pod, aunque por lo general un pod solo corre un container dentro. El pod tiene su propia dirección IP privada y hostname.
Un pod, como ya se explicó, es un grupo de containers íntimamente ligados que siempre corren en el mismo worker node y en el mismo namespace. Cada pod es como una máquina lógica separada con sus propios procesos, IP, hostname, etc., ejecutando una aplicación. Todos los containers en un pod aparecerán corriendo en la misma máquina lógica, mientras que otros containers en otros pods, aun y cuando corran en el mismo worker node, aparecerán corriendo en una diferente.
Para entender mejor la relación entre containers, pods y nodos:
Donde se puede observar que cada pod tiene su dirección IP y aloja uno o más containers. En este ejemplo se muestra un solo nodo, pero los pods pueden estar distribuidos a lo largo de diferentes worker nodes. Por otro lado, para almacenar data no volátil para las aplicaciones, en la figura se pueden observar los volumes, de los cuales hablaremos más adelante.
Exponiendo pods al exterior del cluster
Ya se mencionó que cada pod tiene su dirección IP privada, que no habilita la posibilidad de acceder las aplicaciones desde fuera del cluster. Para hacer que los pods sean accesibles desde el exterior se necesita exponerlos a través de un objeto tipo servicio (Service) que permita lograr esta tarea.
Un servicio en Kubernetes es un recurso que se crea para obtener un único y constante punto de acceso a un grupo de pods que proveen la misma aplicación. Cada servicio tiene una dirección IP y un puerto que no varían mientras este existe. De esta manera, los clientes pueden establecer comunicacion con esa dirección IP y puerto, y esas conexiones son luego direccionadas a los pods que sustentan ese servicio.
Existen varios tipos de servicios:
- Nodeports: Para cada uno de estos tipos de servicio, el cluster abre un mismo socket dedicado en todos los nodos (esto es que asigna un mismo puerto a todas las direcciones IP públicas del cluster) y redirecciona todo el tráfico recibido en esos puertos al servicio subyacente.
- LoadBalancers: Son una extensión de los nodeports - Estos hacen accesibles los servicios subyacentes desde un load balancer dedicado provisionado por la infraestructura cloud. Esencialmente, un load balancer obtiene una IP pública dedicada para su servicio a donde los clientes pueden enviar sus requests. Este tipo de servicio no siempre está habilitado porque depende del proveedor cloud y de la posibilidad de crear direcciones IPs públicas.
- Ingress: Es un mecanismo radicalmente diferente para exponer múltiples servicios a través de una misma dirección IP. Este opera en el nivel HTTP (capa de red) y por ende ofrece más características de lo que servicios de capa 4 pueden. Cuando un cliente envía una request HTTP al ingress, el host y el path en la request son lo que definen a qué aplicación está va dirigida.
Existe otro tipo de servicio llamado ClusterIP que, aunque no expone pods al exterior del cluster, son muy útiles a la hora de exponer grupos de pods a otros mediante una IP interna estática o un hostname fijo que otros pods pueden resolver y apuntar. Es muy útil cuando se implementan bases de datos dentro del cluster.
Deployments
Existen múltiples maneras de agrupar y manejar pods, pero la manera más sencilla y eficiente es a través de recursos de alto nivel llamados deployments. Estos están destinados para el deployment de aplicaciones con sus respectivos con un archivo declarativo donde se especifica todo lo relacionado con ese despliegue. Existen otras maneras de hacer esto, como es el caso de usar ReplicationControllers o ReplicaSets puros, pero al ser estos bastante obsoletos no se abordarán.
Cuando se crea un Deployment, un recurso de tipo ReplicaSet es creado por debajo (los ReplicaSets replican y manejan pods) pero que permite actualizar las definiciones de este despliegue de una mejor manera. Por el momento se debe ver un recurso de tipo Deployment como una manera de desplegar pods generalmente replicados y crear un espacio lógico para que un servicio más elevado pueda accederlos a través de un selector único de aplicación, pero que también sirven de herramienta para incorporar cambios en la aplicación sin necesidad de que exista downtime.
Volumes
Un volume en Kubernetes es la manera de guardar data útil para los containers y aplicaciones. Este es construido cuando un pod es iniciado y destruido cuando ese pod es eliminado - y son componentes de los pods. Estos son accesibles por todos los containers dentro del pod pero deben ser “mounted” para que puedan ser accedidos.
Existen muchísimos tipos de volumes en Kubernetes (puedes encontrar muchos de ellos aquí). En Darien Technology usamos los tipo LongHorn, que básicamente son un sistema de almacenamiento distribuido y altamente disponible (HA) desarrollado por Rancher - y utilizamos este tipo porque son la mejor manera de mantener en storage on-premises de manera muy segura y económica.
En el próximo artículo estaremos hablando de los diferentes tipos de implementación de Kubernetes y cómo preparar un ambiente local que emule un cluster de producción. Nos vemos allá!