Kubernetes – Despliegues sin corte de servicio

kubernetes intro

El pasado mes de Noviembre finalizamos la migración de todos nuestros servicios propios dentro de Arteco, a una infraestructura en la nube gestionado por el orquestador de servicios virtualizados Kubernetes. Nuestra conclusión de dicha experiencia: un acierto a todos los niveles.

Hacía un tiempo que teníamos echado el ojo a varios orquestadores de servicios virtualizados para simplificar el despliegue de imágenes de docker, balanceo de carga, clústering y sobre todo poder hacer despliegues sin cortes de servicio. El uso de alguno de estos orquestadores es la evolución natural cuando se despliegan imágenes de docker en producción.

Punto de partida 

En Arteco como micro empresa no disponíamos de muchos recursos. A la fecha de la escritura de este post sólo éramos 6 trabajadores,  5 analistas programadores y una estupenda manager de redes sociales. Con un equipo tan pequeño las tareas de administración de sistemas quedan reducidas al mínimo tiempo posible. No nos podíamos permitir un administrador de sistemas ya que todas las manos las teníamos ocupadas con desarrollos y mantenimientos de aplicaciones.

De ahí que la estructura en IT de nuestra empresa se resumiera a unas cuantas máquinas virtuales, alojadas en algún proveedor como Google Cloud o Amazon Web Services, ejecutando servicios detrás de un apache http server a modo de reverse proxy. Estos servicios podían ser de terceros o propios, pero instalados mayoritariamente por el gestor de paquetes de la distribución usada. En nuestro caso ubuntu y apt-get.

Intentábamos que cada máquina tuviera la misma configuración posible, pero tras ir evolucionando con el tiempo algunos servicios se fueron desplegado con docker, las distribuciones se fueron quedando obsoletas, distintas versiones entre máquinas virtuales y despliegues manuales mediante scp, scripts y muchos vim a través de ssh.

Pero aquí entra kubernetes al rescate.

Situación objetivo

Dada la existencia de orquestadores como kubernetes o swarm, conocíamos la posibilidad de poder tener un grupo de máquinas (virtuales), todas ellas exactamente iguales, gestionadas por el orquestador. Es decir, que el orquestador decide dónde y cuándo levantar un determinado servicio,  supervisa los servicios por si dejan de estar operativos y puede auto escalarlo en función de la demanda.

Este nuevo paradigma de funcionamiento requiere todo un engranaje de servicios adaptados y conscientes del estado del grupo de máquinas que gestionan: como los servicios de DNS, persistencia de datos, balanceadores de carga, monitores https, reverse-proxy y un sin fin más.

A nivel de usuario, un servicio para un orquestador es una imagen docker que se ejecuta en alguna de las máquinas, probablemente no siempre en la misma. Este servicio debe considerarse como un proceso efímero, al poder reubicarse en otro nodo del grupo en algún momento por decisión del orquestador. De ahí que las aplicaciones normalmente deben ser sin estado (stateless). Es otras palabras: su funcionamiento no debe de depender de dónde se ejecute. Por ejemplo todo lo que escriba en disco será borrado en algún momento, salvo que se le asocie un volumen persistente.

Tras revisar alternativas, en cuanto a posibles orquestadores, nos decidimos por kubernetes por la fuerte acogida que tiene dentro de las infraestructuras de IT y la gran apuesta que están haciendo los grandes proveedores de cloud computing para que se convierta en el estándar de facto.

Por tanto, nuestro objetivo era poder disponer de un buen orquestador de servicios que nos permitiera:

  • Aislar los servicios entre ellos, para evitar problema de dependencias
  • Reutilización de nodos del cluster para poder servir el mayor número de servicios por cada nodo.
  • Monitorización de servicios para lanzar procesos nuevos en caso de parada del servicio
  • Deseable la posibilidad de auto escalado horizontal
  • Balanceado de carga
  • Y el más importante, despliegues en continuo sin corte de servicio.

Y Kubernetes tiene todas esas funcionalidades.

Proceso de migración

Ya teníamos experiencia en docker, por lo que conocíamos algo acerca de virtualización y generación de imágenes. Pero adoptar kubernetes va más allá. Requiere que los servicios se puedan ejecutar en un nodo u en otro en cualquier momento, ser arrancados o parados y por tanto se unan o salgan del cluster dinámicamente.

Es importan mencionar que dentro de kubernetes se consideran dos tipos de servicios: stateless (sin estado) y por tanto dinámicos en el sentido de arrancar o parar y duplicar y balancear carga. O los statefull (con estado). Ambos servicios pueden ser desplegados desde kubernetes, pero en el caso de un servicio statefull lo más normal es sólo disponer de una instancia del servicio sin balanceo de carga y apuntando a un volumen persistente.

En nuestro caso comenzamos por las aplicaciones statefull. Para nosotros ha sido la parte más fácil, ya que a groso modo sólo hay que indicar la imagen de docker que se desea arrancar, publicar el puerto e indicar que tal directorio debe escribirse en un volumen persistente para que si el sistema cae, todo lo escrito ahí esté disponible para la nueva instancia que arranque kuberentes.

Un ejemplo de aplicación statefull de nuestra infraestructura es gitea, un gestor de repositorios git. El fichero que hemos utilizado para poder desplegar en kubernetes tiene la siguiente pinta (gitea-deployment.yml):

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: gitea
  labels:
    app: gitea
spec:
  replicas: 1
  selector:
    matchLabels:
      app: gitea
  template:
    metadata:
      labels:
        app: gitea
    spec:
      containers:
      - image: gitea/gitea
        name: gitea
        env:
        - name: DB_HOST
          value: ...
        - name: DB_NAME
          value: ...
        - name: DB_USER
          value: ...
        resources:
          requests:
            memory: "256Mi"
            cpu: "100m"
          limits:
            memory: "2Gi"
            cpu: "500m"
        ports:
        - containerPort: 3000
          name: gitea
        volumeMounts:
        - name: gitea-persistent-storage
          mountPath: /data
      volumes:
      - name: gitea-persistent-storage
        persistentVolumeClaim:
          claimName: gitea-persistence

Nota: los ficheros yml pueden ser ejecutados contra el cluster con el comando kubectl apply -f <filename.yml>. El comando kubectl está disponible en los sdk de kubernetes.

Lo primero que se aprecia es que los ficheros de configuración en kubernetes son del tipo Yaml. Estos ficheros de configuración conviene tenerlos bajo control de un sistema de versiones como git.

Siguiendo con el ejemplo la imagen de docker que se ejecuta es gitea/gitea que ofrece su servicio por el puerto 3000 y que requiere un volumen persistente denominado gitea-persistent-storage montado sobre el path del sistema de ficheros /data. Es decir, que todo lo que gitea escriba estará guardado de forma persistente entre reinicios o traslados del servicio en el volumen persistente indicado.

Para definir un volumen persistente se debe crear otro objeto dentro de kubernetes con el nombre solicitado por gitea (gitea-persistence). El fichero de configuración asociado al volumen persistente tiene la siguiente pinta (gitea-persistence.yml):

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: gitea-persistence
  labels:
    app: gitea
spec:
  storageClassName: standard
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi

Esta definición publica un objeto dentro de kubernetes con nombre gitea-persistence accesible por cualquier sevicio, pero sólo uno al mismo tiempo (ReadWriteOnce) del clúster con capacidad de almacenar 20 Gigabytes de datos.

Hasta este punto tenemos medio trabajo hecho. Lo que tenemos hasta ahora es gitea ejecutando en un nodo pero al que nadie puede acceder. Para publicar el servicio al resto de nodos precisamente debe definirse otro objeto denominado servicio (gitea-service.yml):

kind: Service
apiVersion: v1
metadata:
  name: gitea
spec:
  selector:
    app: gitea
  ports:
  - protocol: TCP
    port: 3000
    targetPort: 3000

Lo que se consigue definiendo un servicio es que la aplicación (deployment) con nombre gitea (selector.app) sea accesible desde el puerto 3000 en el cluster. Y ahora sí tenemos el servicio gitea accesible desde cualquier nodo del cluster, pero NO desde Internet.

Para que un servicio de kubernetes esté accesible desde internet se debe crear otro objeto que exponga este desde fuera del cluster denominado ingress. Ingress se encarga de funcionalidades como asignarle una ip fija, un nombre de dominio o incluso un certificado SSL. En nuestro caso tenemos una instalación algo más compleja del que se hablará en otro post mediante el uso de traefik.

Una vez desplegados todos los servicios statefull nos quedaba lo más difícil, las aplicaciones propias stateless. Para sacar el máximo partido a kuberentes hemos aprovechado para refactorizar el código de nuestras aplicaciones y convertirlas en stateless con capacidades dinámicas en cuanto a la subscripción o supresión de nuevas instancias de la aplicación en el cluster, como por ejemplo adaptar las cachés para incorporar sistemas de caché distribuidos con capacidad de alterar el número de miembros dinámicamente, o evitar accesos a disco, etc..

El un siguiente post hablaremos de las modificaciones que se han tenido que hacer en el código fuente de nuestras aplicaciones para poder permitir esta elasticidad en el número de instancias del servicio y cómo hemos convertido las aplicaciones a stateless.

Nuestros desarrollos son mayoritariamente realizados con Java y con mucha frecuencia con Spring Boot. Este framework es muy utilizado y dispone de integraciones con muchos sistemas de cache e incluso con la posibilidad de integrar clientes de kubernetes para actuar ante eventos del clúster. Por lo que la adaptación de las aplicaciones fueron bastante rápidas.

Conclusión

La adopción de kubernetes en Arteco ha sido todo un acierto. Ha simplificado y homogeneizado los despliegues. Nos ha permitido reutilización de nodos para sacarles el máximo partido y por tanto abaratado costes. Juntamente con la refactorización de nuestras aplicaciones, podemos ofrecer nuevas funcionalidades a nuestros clientes sin corte del servicio al más puro estilo facebook o gmail, monitorizados y con auto escalado según demanda.

Dada la adopción que esta tecnología está teniendo estamos casi convencidos que será un estándar de facto y cada vez más lo veremos implantado en muchas infraestructuras de sistemas de la información.

Si en vuestra organización estáis pensando en adoptar kubernetes desde Arteco os guiaremos y asesoraremos en el proceso de migración.

 

Autor: Ramón Arnau

Director de Arteco Consulting sl. Ingeniero Informático. Máster en Administración y Dirección de Empresas. Máster en Tecnologías de la Información. Auditor ISO 27001. ITIL.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *