Openshift Origin y Jenkins con almacenamiento persistente en GlusterFS

origin_logo

En esta entrada le echaremos un vistazo a Red Hat Openshift, un PaaS basado en Kubernetes orientado a desarrolladores. En concreto, utilizaremos la versión open source, Openshift Origin, para simular un entorno clúster local en una sola máquina, desplegando y configurando una serie de máquinas virtuales con la ayuda de Vagrant y Ansible.

Como primer contacto con la plataforma, nos iremos familiarizando con el CLI y el UI de Openshift al mismo tiempo que desplegamos la aplicación de ejemplo Kubernetes Guestbook. Tras esta introducción, haremos uso de la plantilla Jenkins-Persistent para crear un pod de Jenkins que almacene sus datos en un volumen persistente. Para ello, configuraremos un clúster muy básico de GlusterFS como almacenamiento persistente entre los nodos de cómputo de Kubernetes.

Provisionado del clúster mediante Vagrant y Ansible

Nuestro cluster local de Openshift estará compuesto por tres máquinas virtuales de VirtualBox, una para el master de Openshift (master1) y dos más para los nodos de cómputo o esclavos (node1, node2). A mayores también se utilizará una cuarta VM (admin1), también creada por Vagrant, que será empleará para provisionar las otras tres máquinas mediante Ansible.

Instalación de Vagrant y sus plugins

En primer lugar, necesitaremos instalar VirtualBox, Vagrant y los plugins que requiere por defecto la configuración de provisionamiento. Aquí describo los pasos para el que será el anfitrión de mis VMs, el cual corre la distribución CentOS 6:

Es importante que nos fijemos en que la versión de Vagrant que estamos instalando es la versión 1.8.6, que en el momento de escribir esta guía no es la última estable. La razón es que uno de los plugins que necesitamos, landrush, es incompatible con las versiones 1.9.x.

Provisionado de las máquinas virtuales e instalación de Openshift

En el repositorio de GitHub openshift-ansible-contrib se encuentran todos los ficheros de configuración necesarios para realizar la instalación:

git clone https://github.com/openshift/openshift-ansible-contrib.git
cd openshift-ansible-contrib/vagrant

Por defecto, utilizando el Vagrantfile en ese directorio se crearán una serie de VMs, cada una de ellas limitada a 1 CPU core y 1 GB de RAM, con la siguiente configuración:

Nombre FQDN IP
master1 master1.example.com 192.168.50.20
node1 node1.example.com 192.168.50.21
node2 node2.example.com 192.168.50.22
admin1 admin1.example.com 192.168.50.23

Si no se utiliza un usuario privilegiado, durante la instalación se va a solicitar la contraseña del usuario actual para editar el fichero /etc/hosts y reconfigurar el servicio dnsmasq, así que debemos cerciorarnos de que el usuario está en el fichero sudoers antes de ejecutar el siguiente comando:

vagrant up

Comprobando la instalación del cluster

Para asegurarnos de que el clúster ha sido correctamente provisionado, vamos a acceder al master mediante SSH y ejecutar unos comandos básicos:

Después, volvemos al anfitrión y comprobamos que las siguientes lineas han sido anañidas al fichero /etc/hosts:

$ cat /etc/hosts
...
192.168.50.23 admin1.example.com
192.168.50.23 admin1
192.168.50.22 node2.example.com
192.168.50.22 node2
192.168.50.21 node1.example.com
192.168.50.21 node1
192.168.50.20 master1.example.com
192.168.50.20 master1

En caso de que no se hayan añadido automáticamente esas lineas, se pueden añadir de forma manual. De ser necesario, las rutas generadas se podrán encontrar en el fichero ~/.vagrant.d/tmp/hosts.local

Opcional: Configurar el acceso externo

Debido a que no tenía demasiada confianza en que mi portátil pudiese correr estas cuatro VMs con fluidez, he jugado sobre seguro y he utilizado un servidor externo como anfitrión. Sin embargo, para poder interactuar con el clúster de Openshift desde mi portatil, es necesario configurar un tunel SSH para el cliente por linea de comandos y otro para la interfaz web.

Si tu anfitrión es directamente tu máquina local, puedes saltarte esta sección y proceder con la instalación del cliente de Openshift.

Tunel para el CLI de Openshift

Desde un terminal en tu máquina:

ssh -fL 8443:127.0.0.1:8443 user@myserver.example.com -N
sudo sh -c "echo '127.0.0.1 master1.example.com' >> /etc/hosts"

Comprueba que puedes acceder al API REST de Openshift:

Tunel SOCKSv5 para la interfaz web de Openshift

De nuevo, desde tu máquina local:

ssh -fL 11443:127.0.0.1:11443 user@myserver.example.com -N

Ahora, en las opciones de red del navegador será necesario seleccionar uso de un proxy SOCKSv5 con el host 127.0.0.1 y el puerto 11433. Con esto configurado, ya se podría acceder a la interfaz web de Openshift en la URL https://master1.example.com:8443.

Instalación y configuración del cliente de Openshift

Asegúrate de cuál es la versión de Openshift que se ha instalado (pista: está en la salida de oc version) y descarga la misma versión de openshift-origin-client-tools desde la página de releases de su repositorio.

Una vez hayas configurado correctamente tu $PATH para que encuentre el ejecutable oc, prueba a hacer login en el PaaS utilizando el siguiente username y password configurado por defecto:

$ oc login https://master1.example.com:8443
Authentication required for https://master1.example.com:8443 (openshift)
Username: admin
Password: admin123
Login successful.

Con las mismas credenciales, también puedes hacer login a través de la interfaz web:

origin-login-page

Ejemplo 1: Kubernetes Guestbook

En lugar de correr una aplicación con el clásico “hola mundo”, intentemos hacer las cosas un poco más interesantes desplegando la aplicación Kubernetes Guestbook mediante el cliente por linea de comandos de Openshift.

En Openshift, las aplicaciones suelen ser desplegadas utilizando un formato de plantilla específico, el cual contiene objetos que son ligeramente diferentes con los equivalentes de la especificación de Kubernetes. Por ejemplo, en Openshift tenemos los objetos de primer nivel Route o DeploymentConfig, que son abstracciones que no tienen traducción directa a objetos de Kubernetes, así como variables que pueden ser parametrizadas en el momento de desplegar la plantilla desde las interfaz de Openshift.

En este caso, ya que Kubernetes Guestbook no está disponible en el formato de plantilla de Openshift, directamente vamos a utilizar las especificaciones de los ficheros .yaml que podremos encontrar en su repositorio de Git.

Despliegue de la aplicación Guestbook

Utilizando el usuario admin que configuramos previamente con el CLI oc, creamos un nuevo proyecto y descargamos el fichero .yaml del Guestbook:

oc new-project kubetest
wget https://raw.githubusercontent.com/kubernetes/kubernetes/master/examples/guestbook/all-in-one/guestbook-all-in-one.yaml

En este ejemplo, se configura un despliegue frontend en cuyo pod se lanza un contenedor php-redis (imagen gcr.io/google-samples/gb-frontend) en el que se ejecuta un servidor web escuchando en el puerto 80, utilizando el usuario root dentro del contenedor. Aunque esto en principio funcionaria sin problemas en una configuración típica de Docker o Kubernetes, en Openshift no. Por defecto, no se permite a los contenedores correr con el usuario root (en estos casos, Openshift seleccionará un UID arbitrario en cada despliegue del contenedor), por lo que php-redis fallará en el momento en el que quiera usar el puerto 80 con un usuario no privilegiado.

Ya que tampoco queremos modificar la imagen para lanzar el servidor web en otro puerto, lo que haremos será relajar las restricciones de seguridad para este proyecto. Utilizando el cliente oadm desde el nodo master1:

oadm policy add-scc-to-user anyuid system:serviceaccount:kubetest:default

Con esto añadiremos el security context constraint anyuid al service account kubetest:default, esto es, permitimos que en el proyecto kubetest los contenedores tengan libertad para utilizar cualquier UID que quieran.

Nota: anyuid es solo una de las security context constraints preconfiguradas que podríamos utilizar. Puedes comprobar todas las disponibles mediante oc get scc utilizando la cuenta de sysadmin desde el CLI de master1.

Ahora, desde nuestra máquina local, podremos desplegar la aplicación:

oc create -f guestbook-all-in-one.yaml

El progreso del despliegue se puede monitorizar mediante oc get all, comando que muestra los servicios y pods que acabamos de crear.

Creación de una ruta para la interfaz de web del Guestbook

La interfaz de usuario del Guestbook se disponibiliza a través del servicio frontend, pero aún no está accesible de forma externa al cluster. Para ello, necesitaremos crear una ruta externa que apunte a ese servicio. Podremos crearla de forma muy sencilla a través de la interfaz web de Openshift o con el CLI:

oc expose svc frontend

Si utilizamos el comando tal cual con las opciones por defecto, el servicio se expondrá con el FQDN frontend-kubetest.apps.example.com a través de la dirección de master1. Se puede comprobar mediante oc get routes.

Para finalmente conseguir acceder a la aplicación, debemos actualizar el fichero /etc/hosts del anfitrión de las VMs con el nuevo FQDN y la misma dirección IP que el host master1.example.com. Por ejemplo, en mi fichero aparecen las siguientes lineas (que deberían ser las mismas que tus IPs si no has tocado la configuración del Vagrantfile inicial):

192.168.50.20 master1.example.com
192.168.50.20 frontend-kubetest.apps.example.com

Comprobamos que podemos acceder a esa URL en el navegador o que el comando curl -k sobre la URL devuelve correctamente un HTML. Además, también puedes ver el estado del proyecto mediante el CLI de Openshift:

Ahora que el Guestbook está desplegado, podemos probar a modificar el número de instancias de los despliegues de la aplicación:

Por ejemplo, escala hacia arriba el número de instancias de frontend y comprueba que, si hacemos curl varias veces sobre el servicio, se puede ver en los logs de los pods con oc logs [nombre-del-pod] cómo las peticiones se están balanceando automáticamente entre las distintas instancias mediante round-robin. De la misma forma, si escalamos hacia arriba el despliegue de redis-slaves, podemos comprobar en los logs del redis-master cómo las nuevas instancias se registran automáticamente.

oc scale --replicas=3 deployment redis-slave
oc scale --replicas=2 deployment frontend

Aunque podemos escalar hacia arriba y hacia abajo utilizando el CLI, cuando intentamos hacer lo mismo desde la interfaz web, veremos que esas opciones no están disponibles. Además, si vamos a la sección Applications > Deployments veremos que no registrado ningún despliegue. Esto es debido a que en esta versión de Openshift (Origin v1.3.2), el objeto de primer orden de Kubernentes Deployment no está completamente soportado, ya que debería usarse el objeto DeploymentConfig que es específico de Openshift. De hecho, el objeto Deployment ha sido introducido recientemente en Kubernetes y su diseño se ha inspirado precisamente en el DeploymentConfig de Openshift.

origin-guestbook

Ejemplo 2: Jenkins-Persistent sobre GlusterFS

En este ejemplo, vamos a emplear la plantilla Jenkins-Persistent que ya se encuentra disponible por defecto en nuestra instalación de Openshift. Antes de eso, necesitamos proporcionar un almacenamiento en el que Jenkins pueda persistir los datos. Este almacenamiento será una configuración sencilla de GlusterFS con redundancia entre los dos nodos de cómputo.

Para proporcionar el almacenamiento a Openshift, se utilizará provisionamiento estático mediante volúmenes persistentes, lo cual significa que el sysadmin tiene la responsabilidad de crear objetos PersistentVolume que quedarán a disposición de los desarrolladores que los soliciten via PersistentVolumeClaims. En vez de hacer esto, podríamos haber optado por provisionamiento dinámico de PVs utilizando los objetos StorageClasses, pero implicaría un mayor número de pasos en la configuración. Además, vamos a crear el sistema de ficheros Gluster directamente sobre rutas internas en los nodos de cómputo, aunque otra opción podría haber sido desplegar un almacenamiento Red Hat Gluster en contenedores.

Creación de un volumen GlusterFS

En primer lugar, necesitaremos instalar los paquetes gluster necesarios en node1 y node2, así como crear el directorio donde irá el volumen de Gluster (p.ej. /data/gv0 en la ruta raíz). Además, será necesario abrir una serie de puertos en los cortafuegos de dos VMs (24007-24008 para glusterd y 49152-49251 para que accedan los clientes).

yum install -y centos-release-gluster
yum --enablerepo=centos-gluster* install -y glusterfs-server
systemctl enable glusterd.service
systemctl start glusterd.service
mkdir -p /data/gv0
firewall-cmd --zone=public --add-port=24007-24008/tcp --permanent
firewall-cmd --zone=public --add-port=49152-49251/tcp --permanent
firewall-cmd --reload

Después, desde solamente uno de los dos nodos, creamos e iniciamos el volumen:

gluster volume create gv0 replica 2 node1:/data/gv0 node2:/data/gv0 force
gluster volume start gv0

Desde el nodo master1, ahora podemos comprobar que efectivamente el volumen puede ser montado. Además, para permitir que cualquier usuario de Openshift tenga permisos para crear ficheros en el volumen, necesitamos cambiar los permisos del directorio raíz del volumen. Esta es la forma sencilla, si esto lo queremos hacer de forma más controlada en un clúster en producción, tendremos que lidiar con las POSIX ACLs, etiquetas de SELinux y las restricciones de seguridad de Openshift.

yum install -y glusterfs-fuse
mkdir -p /mnt/gv0
mount -t glusterfs node2:/gv0 /mnt/gv0
chmod -R 777 /mnt/gv0

Debemos tener también en consideración que, para que el volumen pueda ser montado, el paquete gluster-fuse debe estar instalado en todos los nodos donde puedan ejecutarse pods de Openshift.

Registro del volumen de Gluster en Openshift

Con el volumen de Gluster creado, el siguiente paso será ponerlo a disposición de los usuarios de Openshift como un PersistentVolume utilizando el plug-in de Openshift para GlusterFS, específico para clústers de almacenamiento dedicado.

Con esta especificación indicaremos a Kubernetes que cree un volumen persistente de 2GiB con permisos RWO (es decir, que el volumen pueda ser montando como lectura-escritura por un único nodo), que son los permisos que va a requerir la petición de almacenamiento que está en la plantilla de Jenkins-Persistent. Por tanto, usando la cuenta sysadmin en master1:

Creación de un proyecto para Jenkinks y configuración del endpoint de almacenamiento

Con el backend de almacenamiento provisionado y el PersistentVolumecreado, ahora podemos volver a nuestro usuario local de Openshift y crear un nuevo proyecto kubetestpv

oc new-project kubetestpv

Para este proyecto necesitaremos especificar dónde se puede encontrar el cluster de almacenamiento mediante un objeto Endpoints, el cual será accedido por nuestros pods a través de un Service.

Aquí debemos fijarnos en que en el parámetro name el valor glusterfs-cluster es el mismo que hemos especificado como endpoint cuando hemos creado el PersistentVolume.

Así, con la creación de estos objetos en el espacio de nombres kubetestpv, la petición de almacenamiento PersistentVolumeClaim en la plantilla de Jenkins debería tener éxito:

oc create -f gluster-service.yaml
oc create -f gluster-endpoints.yaml

Despliegue de la aplicación de Jenkins

Desde la interfaz web, entramos en el proyecto kubetestpv, hacemos click en Add to Project y seleccionamos la “instant app” jenkins-persistent desde el catálogo de plantillas. Se mostrará una página con todas las opciones configurables:

origin-jenkins-persistent-params

 

Si estás utilizando una versión de Origin <1.4, es importante que deshabilites la autenticación por OAuth, ya que el plugin de Jenkins que viene en esa imagen no funciona con versiones antiguas de Openshift debido a una incompatibilidad que surge cuando intenta acceder al servicio OAuth de Openshift.

Si no sabemos esto y desplegamos la plantilla con OAuth activado, el pod de Jenkins quedará quedará bloqueado en la inicialización en estado unhealthy. Afortunadamente, Openshift nos permite arreglar esto “al vuelo”. Podemos ir en la sección de Deployments al despliegue jenkins-1 y directamente editar los parámetros que hemos introducido al cargar la template haciendo click en Actions > Edit YAML:

origin-jenkins-disable-oauth

Trans cambiar el parámetro OPENSHIFT_ENABLE_OAUTH a false, una vez guardemos el fichero automáticamente se eliminarán los pods que están corriendo y se creará un nuevo despliegue jenkins-2, que debería levantarse y estar corriendo en estado OK en menos de un minuto.

Finalmente, para acceder al UI de Jenkins, necesitamos añadir su ruta al fichero /etc/hosts en el anfitrión de las VMs, tal como hicimos con el ejemplo anterior:

$ cat /etc/hosts
...
192.168.50.20 master1.example.com
...
192.168.50.20 jenkins-kubetestpv.apps.example.com

Tras este último paso, la interfaz web de Jenkins debería estar disponible en la URL https://jenkins-kubetestpv.apps.example.com y, finalmente, se podrán utilizar las credenciales pre-configuradas (user: admin, password: password) para acceder dentro de la aplicación.

Notas finales

Ciertamente, Kubernetes es un sobresaliente sistema operativo a nivel de clúster que nos permite desplegar servicios con facilidad de una forma fiable y escalable. Sin embargo, su manejo puede ser relativamente complejo para usuarios que tengan conocimientos limitados de operaciones. Openshift intenta ocupar ese hueco, ofreciendo una serie de abstracciones que hacen Kubernetes mucho más accesible para los desarrolladores y, a mayores, añade una serie de características por defecto como multi-tenancy y seguridad avanzada que hacen de este PaaS un fuerte candidato para ser una solución completa para empresas.

Sin embargo, Openshift no es la única opción si estamos considerando usar un PaaS basado en Kubernetes. Por tanto, en entradas futuras de este blog echaremos un vistazo a otros PaaS de características similares, con el objetivo de evaluar cuál de ellos sería la solución más apropiada para distintos casos de usos y escenarios.