sábado, 15 de septiembre de 2018

Ejecución de múltiples servicios en contenedores

Hola de nuevo, hoy vamos a estudiar un punto importante a la hora de crear nuestras imágenes relacionado con el control de los procesos que corren en nuestros contenedores.

En general la recomendación a la hora de diseñar servicios complejos basados en Docker, es que cada contenedor sea responsable de un único servicio, estableciendo la comunicación entre ellos mediante las definiciones de red y volúmenes que ya hemos visto.

El proceso principal de un contenedor será el ENTRYPOINT o CMD que habremos definido en nuestro dockerfile el cual en ocasiones, puede lanzar múltiples procesos que correrán dentro de nuestro contenedor.

En esos casos, o en casos en los que necesariamente tengamos que ejecutar más de un servicio dentro del mismo contenedor, es muy recomendable utilizar un gestor de procesos que controle el estado de los mismos, asegurándonos así que se gestionan las señales correctamente, parando los proceos de forma correcta cuando sea necesario y que se controlan procesos que se puedan quedar en estado zombie o que no respondan adecuadamente.

Una forma de lograr esto sería incluir un gestor de procesos, como supervisord, en nuestra imagen base para luego utilizarlo en todas y cada una de las imágenes siguientes. El problema de esta aproximación es que hará nuestras imágenes más pesadas debido a que tendremos que incluir supervisord y todas sus dependencias.

Una opción mucho más ligera es usar el flag --init en el momento de construir nuestro contenedor, de este modo Docker inserta un pequeño gestor de procesos como proceso principal, el cual podrá realizar todas las acciones descritas de control de procesos dentro de nuestro contenedor sin necesidad de cambiar nuestras imágenes. En concreto, al usar el flag --init, docker inserta en nuestra imagen el gestor de procesos tini, del cual podéis obtener más información en https://github.com/krallin/tini.

Al construir un contenedor con la opción --init usando una de nuestras imágenes, el resultado es el siguiente:

Contenedor creado con opción --init a partir de imagen ldap.
Como podemos ver en la salida anterior, ahora el proceso con PID 1 del contenedor ya no es el correspondiente a nuestro servicio LDAP, sino el init insertado por Docker en el momento de la creación del contenedor y el proceso slapd es un proceso hijo de este nuevo proceso init.

Como está claro no hemos tenido que modificar la imagen de ninguna manera con lo que, de una forma increiblemente sencilla, podemos construir contenedores en los que se ejecuten más de un servicio o estar seguros de que, en caso de problemas con los procesos de nuestros contenedores, podremos usar un gestor de procesos que evitará problemas como procesos huerfanos o procesos zombies dentro de los mismos.

jueves, 13 de septiembre de 2018

Configurando el acceso a la API de Docker

El demonio dockerd que se ejecuta en nuestros hosts implementa una API RESTful a la que podemos acceder mediante un cliente HTTP. De esta manera podemos integrar los servicios proporcionados por Docker con otros servicios de nuestra infraestructura.

La configuración por defecto de Docker utiliza un socket Unix, con lo que no hay acceso desde la red a la API del motor de Docker. Como para determinados casos es necesario poder comunicarnos con el servicio Docker desde la red, vamos a ver como configurarlo para que sea accesible mediante conexiones TCP de forma segura. 

En principio, todo lo que tenemos que hacer es configurar el servicio Docker para que escuche en el puerto y direcciones IP del host que queramos y especificar las opciones de configuración tlsverify y tlscacert. De este modo, cuando el demonio dockerd actúa en modo servidor, solo aceptará conexiones de clientes autenticados con un certificado firmado por la CA en la que se confía y, en modo cliente, solo se conectará a servidores con un certificado firmado por la misma CA.

Por tanto, como es necesario disponer de una CA, crearemos una entidad certificadora de pruebas generando un certificado autofirmado usando openssl. Una vez creada nuestra CA, crearemos y firmaremos el resto de certificados que necesitemos. Como es lógico en sistemas en producción, deberíamos tener implementada una CA para todas estas tareas.

Con nuestros certificados ya listos, solo tenemos que configurar el servicio Docker de nuestra máquina usando el comando systemctl del siguiente modo:

Modificación del servicio docker. Añadimos socket de escucha y opciones TLS.
De este modo el servicio docker escuchará en el puerto TCP y dirección IP especificadas verificando las conexiones TLS de cualquier cliente y, adicionalmente creará el socket local que nos permitirá seguir usando los comandos localmente en el host. 

Si intentamos conectar ahora a nuestro host docker por el puerto especificado usando https, recibiremos un mensaje de error que nos indica que ha intentado validar nuestro certificado. Esto viene dado por la opción tlsverify y al no tener un certificado firmado por la CA configurada mediante la opción tlscacert, genera la denegación de nuestro acceso. Creando un certificado para nuestra máquina cliente con nuestra CA de pruebas podremos acceder sin problemas:

Acceso al servicio Docker remoto mediante HTTPS y verificación de certificado cliente.
Con esta configuración ya establecida, veremos más adelante como usarla para crear arquitecturas y servicios más complejos.

sábado, 8 de septiembre de 2018

Usando contenedores como build agents de Jenkins.

Siguiendo con Jenkins y su integración con Docker, vamos a estudiar como podemos usar contenedores para realizar builds de nuestros desarrollos, de tal modo que lancemos contenedores efímeros que solo se encargarán de realizar las builds y las pruebas que configuremos para cada caso.

Para poder integrar un servicio Jenkins con un host Docker es necesario configurar el demonio dockerd de nuestro dockerhost para que pueda recibir peticiones por red. Para esto, suponiendo que el servicio Jenkins es un contenedor dentro del propio host Docker, tenemos que editar la configuración del demonio dockerd y añadir una definición de URL para el arranque del demonio. Esto podemos hacerlo de manera simple con el comando systemctl edit docker.service y solo tenemos que añadir las siguientes líneas de configuración:

Modificación de configuración del servicio Docker para añadir un socket de escucha de red y verificación TLS.
Una vez realizado este cambio es necesario reiniciar el servicio Docker para que los nuevos cambios tengan efecto, para esto solo es necesario ejecutar el comando systemctl con la opción restart y si todo está correcto, el demonio Docker estará escuchando en el puerto definido en nuestra nueva configuración y requerirá validación TLS. Adicionalmente es muy posible que haya que añadir una regla en los cortafuegos implicados para permitir las conexiones a dicho puerto.

Tras realizar la configuración anterior, pasamos a configurar nuestro servicio Jenkins, con lo que desde el apartado Nube de la configuración del sistema, añadimos un proveedor específico de nube en Jenkins que será de tipo Docker. Para esto, al añadir Docker como proveedor de nube, tendremos que rellenar los campos siguientes:

Configuración de nube Docker en Jenkins.
Es muy importante crear unas credenciales basadas en un certificado para la validación del servicio Jenkins con el demonio dockerd del host remoto. Para esto tendremos que generar un certificado firmado por la misma CA que hemos utilizado para el certificado usado por el demonio dockerd y que hemos especificado en la configuración del servicio Docker del host remoto. 

Podemos comprobar que la configuración y el acceso al host Docker es correcto con solo utilizar el botón Test Connection, si todo es correcto la versión del host Docker aparecerá como puede verse en la imagen anterior. 

Ahora que hemos realizado la configuración correctamente, podemos ver que nuestro host Docker aparece en el apartado Docker dentro de la configuración de Jenkins:

Nuestro host Docker registrado en Jenkins.
El siguiente paso es configurar las imágenes que se usarán para realizar las builds, así como los parámetros que se usarán al lanzarlas. Para esto es importante tener en cuenta que la imagen a utilizar debe tener instalado java, además de las herramientas para realizar la build y pruebas necesarias. Para esto lo recomendable sería crear imágenes a medida, a partir de una imagen base que podemos crear como nodo slave básico de Jenkins. Luego podremos modificar dicha imagen base con las herramientas necesarias para cada uno de los casos que necesitemos.

Para esto lo mejor será, a partir de nuestra imagen base Linux, crear una nueva imagen en la que incluyamos java y git para, posteriormente, crear imágenes en las que incluyamos las herramientas que necesitemos en función de las herramientas de desarrollo que vayamos a utilizar.

Por tanto, para comenzar, creamos una nueva imagen cuyo dockerfile será el siguiente:

Dockerfile de nuestra imagen base para un nodo slave de Jenkins.
En este dockerfile hay unos cuantos puntos importantes a tener en cuenta:
  • Las instrucciones COPY y ADD nos permiten añadir archivos a nuestra imagen como ya sabemos, pero lo importante es que siempre los añadirán como root, aunque el fichero o directorio origen sean de otro usuario.
  • Debido al punto anterior, la segunda instrucción ejecuta el comando /usr/bin/chown dentro del contenedor temporal generado para crear la imagen. De este modo podemos cambiar la propiedad del directorio /JENKINS de la imagen. Esta instrucción RUN se ha ejecutado como root.
  • Especificamos que el directorio /JENKINS de la imagen es un punto de montaje de volúmenes. De este modo podremos mantener cualquier resultado generado en una build en caso de ser necesario.
  • Hasta este punto, todo se ha ejecutado como el usuario root en cualquiera de los contenedores temporales que se han ido generando para crear nuestra imagen. La siguiente instrucción define que el usuario a utilizar tiene el UID 10000, que es el usuario jenkins dentro de nuestra imagen, hará que cualquier otra instrucción RUN, CMD o ENTRYPOINT siguiente se ejecuten con dicho usuario. Por este motivo, la instrucción RUN que ha cambiado el propietario del directorio /JENKINS debe ir antes que la instrucción USER.
  • A continuación definimos variables de entorno que serán necesarias en los contenedores generados para las diferentes builds.
Con este dockerfile contruimos la imagen y comprobamos que, efectivamente, los nuevos programas añadidos están disponibles en la nueva imagen y que funcionan correctamente.

Con nuestra imagen ya creada y tras comprobar que el software que hemos incluido en la misma funciona adecuadamente, modificamos el dockerfile de la imagen del siguiente modo:

Dockerfile de nuestra imagen base para jenkins.
En este dockerfile solo hay una diferencia respecto al anterior, la definición de ENTRYPOINT es distinta y apunta a un script. Este script es muy simple, solo registra una entrada en el log del contenedor y luego lanza un comando sleep. La razón para esto es que, cuando Jenkins lanza un job en el que usa contenedores remotos como build agents, necesita un tiempo para poder engancharse con el contenedor y por tanto, si este se parase muy rápido, el build fallaría.

Ahora que tenemos nuestra imagen básica creada, la cual solo contiene de momento git y java, vamos a añadir la plantilla en la configuración de nuestro servidor Jenkins y crear nuestro job.

La configuración de la plantilla la realizaremos como podemos ver en la siguiente imagen:

Configuración de nuestra plantilla base en Jenkins.
En la configuración anterior, uno de los puntos más importantes, es la etiqueta que le asignemos a esta plantilla ya que será la que luego usaremos en la definición de nuestro job. También debemos asegurarnos de especificar el nombre correcto de nuestra imagen Docker a utilizar, en este caso jenkins_slave_base.

Adicionalmente, si no especificamos en el campo Pull strategy la opción Never pull, Jenkins siempre intentará obtener la imagen para construir el contenedor de un repositorio remoto, en vez del repositorio local de nuestro dockerhost.

Con esta configuración de plantilla, nuestro job podemos configurarlo del siguiente modo:

Creación del job en Jenkins - Especificamos el uso de un contenedor docker específico.
En el punto anterior vemos que, al especificar la misma etiqueta que hemos definido en el momento de crear la plantilla de docker, Jenkins indica que esta proporcionado por un proveedor de nube.

Realizamos cualquier configuración adicional que necesitemos para nuestro job, como obtener nuestra fuente de un repositorio git y lo lanzamos para comprobar que funciona correctamente:

Resultado del build usando un contenedor.
Además del resultado anterior indicado por Jenkins, podemos comprobar en la salida por consola del job como se han realizado todos los pasos adecuadamente.

Adicionalmente, mientras se está ejecutando el job, si comprobamos en nuestro dockerhost la lista de contenedores corriendo vemos que aparece un contenedor adicional que, al finalizar el job desaparece ya que se crea como un contenedor efímero:

Contenedor lanzado como build agent por Jenkins.
Ahora, si añadimos un compilador como por ejemplo gcc y creamos una nueva imagen a partir de esta, podremos configurar una nueva template en Jenkins que usaremos para compilar nuestro código C creando los jobs correspondientes.

Para crear una nueva template, tendremos que repetir la configuración de template anterior pero cambiando el nombre de la etiqueta, así como el nombre de la imagen a utilizar para construir los contenedores. Por ejemplo, para el caso de un slave agent para realizar builds de código C, podríamos hacer algo como lo siguiente:

Definimos una nueva template para usar la imagen que contiene el gcc.
Ahora definimos un nuevo job que haga uso de esta template y que realice la compilación del código fuente que obtiene de nuestro repositorio git, con lo que el job quedaría más o menos con esta configuración:

Configuración del job - Definición de la template Docker a utilizar.
Configuración del job - Definición de tareas simples de ejecución.
Con esta configuración de job, la salida por consola del job que obtenemos es la siguiente:

Resultado de la ejecución de nuestro job.
Por tanto, como podemos ver, es posible integrar Jenkin con nuestra infraestructura Docker para poder usar de una manera mucho más óptima nuestros recuursos. De este modo Jenkins es capaz de lanzar contenedores efímeros como agentes para nuestros entornos CI/CD permitiedo mucha más flexibilidad y aprovechamiento de los recursos.

miércoles, 29 de agosto de 2018

Conceptos avanzados de imágenes Docker - Parte III

Hola de nuevo, hoy seguimos trabajando con la construcción de imágenes para Docker. En este caso veremos como añadir opciones que nos permitan configurar los servicios que despliegan, según nuestras necesidades.

Siguiendo con nuestra imagen de un servicio de directorio basado en OpenLDAP,  vamos a ver como podemos especificar las opciones de configuración del mismo.

Con la imagen realizada hasta ahora se incluye el fichero de configuracion de OpenLDAP, que se encuentra en la ruta /ldap/etc/openldap/slapd.conf y que tiene una configuración por defecto.

Como es lógico, esta configuración no nos será de gran utilidad y necesitaremos cambiarla en función de nuestro servicio.

Para poder configurar adecuadamente el servidor OpenLDAP de nuestra imagen tenemos dos opciones, aunque en general podemos optar por estas dos aproximaciones para cualquier software contenido en una imagen cuya configuración dependa de un fichero o ficheros de texto.

La primera es, sabiendo donde está el fichero de configuración dentro de la imagen, montar un volumen que contenga un fichero slapd.conf que establezca la configuración deseada en el momento de crear un contenedor, por ejemplo:

Establecemos la configuración del servidor OpenLDAP con un volumen que contiene la configuración.

La otra opción consiste en complicar un poco nuestra imagen, usando un script de arranque que utilice los valores de variables de entorno que pasaremos a nuestro contenedor en el momento de su creación. Aunque puede sonar un poco complicado, vamos a ver que es muy sencillo.

Vamos a empezar con un ejemplo muy simple, para ello vamos a crear un contenedor en el que vamos a inyectar unas variables de entorno y vamos a comprobar como estas variables pasan a estar disponibles dentro del contenedor. Para inyectar variables en un contenedor solo tenemos que usar la opción --env en el momento de su creación es decir, solo tenemos que hacer algo como lo siguiente:

Inyección de variables de entorno en un contenedor.
Como es lógico podemos pasar tantas variables de entorno como necesitemos y, como vemos, conservarán el nombre y valor asignado. Por tanto, teniendo esto en cuenta, podemos hacer un script que recoja dichas variables de entorno y sustituya los valores de las mismas en el fichero de configuración de nuestro servicio, que en nuestro ejemplo es un servidor OpenLDAP.

Como el punto importante en este caso es el script que debemos crear, vamos a empezar por hacer una prueba sencilla con una imagen diferente. Este script debe comprobar si existen las variables de entorno, usarlas para cambiar el contenido del fichero de configuración slapd.conf y arrancar el demonio slapd. Adicionalmente debería mostrar un mensaje en stderr si falta alguna de las variables de configuración necesarias.

Como ejemplo de lo que podemos hacer en función de nuestras necesidades, esta sería la salida de una imagen de test generada a partir de la imagen ldap original. En ella hemos cambiado el ENTRYPOINT por nuestro nuevo script el cual, de momento, solo comprueba las variables pasadas y las registra en STDOUT y STDERR:

Creación del contenedor sin las variables requeridas.
En el caso de arrancar un contenedor con las variables requeridas por el servicio, la salida mostrada será la siguiente:

Creación del contenedor con todas las variables necesarias para la configuración del servicio OpenLDAP.
Por tanto, ya solo nos queda modificar el script para incluir una linea que arranque el servidor OpenLDAP, para lo cual lo mejor es incluir una llamada como la siguiente:

Arranque del servicio en nuestro script de inicio de la imagen ldap.
De este modo nos aseguramos que las señales enviadas al contenedor llegan de manera correcta al demonio slapd y que este se cierra ordenadamente cuando lo paremos.

Ahora podemos o bien crear una nueva imagen, cambiando el ENTRYPOINT para que use nuestro nuevo script o bien generar una imagen a partir de la original. En mi opinión, siempre dependiendo del servicio y teniendo bien documentadas las diferencias entre nuestras imágenes, creo que lo mejor es mantener ambas imágenes y usar una u otra en función de las necesidades que tengamos en cada momento.

El script creado para este caso está disponible via git en https://github.com/locurastecnicas/DOCKER_scripts. Este script es muy mejorable ya que, al menos, debería generar la password de forma aleatoria en caso de no proporcionarla mediante una variable.

martes, 28 de agosto de 2018

Trabajando con Jenkins en contenedores

Supongamos que tenemos un contenedor generado a partir de una imagen de Jenkins con el que queremos trabajar en nuestro entorno de CI/CD. Si creamos el contenedor de manera que sea efímero, al pararlo perderemos toda nuestra configuración salvo que seamos capaces de mantener el directorio que contiene toda la configuración del servicio Jenkins.

Para poder mantener dicha configuración, primero acudimos a la documentación de Jenkins la cual, en la URL https://wiki.jenkins.io/display/JENKINS/Administering+Jenkins, nos indica que toda la información de configuración utilizada por Jenkins se almacena en la ruta dada por la variable JENKINS_HOME. Si inspeccionamos la imagen podremos ver lo siguiente:

Variables de entorno de la imagen de Jenkins.
Adicionalmente veremos una referencia a un volumen en la definición de dicha imagen:

Declaración de volumen en la imagen de Jenkins.
Por tanto queda claro que necesitamos mantener el contenido de dicha ruta, si queremos mantener la configuración de nuestro servicio jenkins.

Ahora bien, ya tenemos un contenedor corriendo y hemos generado cierta configuración que no queremos perder, con lo que necesitamos saber donde está almacenada localmente dicha información para poder copiarla. Si inspeccionamos el contendor que está corriendo en nuestro sistema veremos lo siguiente:

Definición de montaje del volumen local para JENKINS_HOME.

Y al inspeccionar esta ruta tenemos lo siguiente, que veremos que coincide con el directorio de configuración de Jenkins:

El directorio de configuración del contenedor de Jenkins en nuestro disco local.
Por tanto copiamos todos esos ficheros a otra ruta local, que será la que utilizaremos como volumen para nuestro servicio Jenkins y volvemos a crear un nuevo contenedor efímero, pero esta vez mapeamos un volumen que apunte a esta nueva ruta que acabamos de crear:

Creación de nuevo contenedor con mapeo de volumen a la configuración de Jenkins.
Y como podemos comprobar, el servicio Jenkins vuelve a estar disponible con la configuración que hubiésemos realizado hasta ese momento:

Servicio Jenkins disponible con configuración existente.
A partir de este punto podemos seguir configurando y utilizando el servicio sin problemas. Siguiente parada, ejecución de compiladores mediante contenedores específicos para cada job.

sábado, 28 de julio de 2018

Cluster ONTAP - Mapeo de usuarios

Como ya comenté en la entrada sobre acceso multiprotocolo, hay un concepto asociado a los accesos CIFS que es fundamental y que es muy importante comprender.

Siempre que un sistema cliente Windows accede a una carpeta compartida por CIFS, independientemente de si es un sistema NetApp 7-mode o C-mode, se realiza un mapeo de usuarios de Windows a Unix.

Esto se explica teniendo en cuenta los orígenes de los sistemas NetApp como servidores NFS y que están basados en FreeBSD.

Puede que no sea muy intuitivo pero, aunque no estemos hablando de acceso multiprotocolo este mapeo de usuarios siempre es necesario y en caso de que no se realice correctamente, se denegará el acceso al share.

Vamos a ver esto resumido con unos cuantos ejemplos empezando con una SVM que solo proporcione acceso CIFS. Este caso, que es el más simple, nos permite ver las opciones existentes para mapear usuarios.

Cuando creamos una SVM con protocolo CIFS, la unimos a un dominio y creamos un share CIFS, cuando nos conectamos y consultamos las sesiones CIFS veremos lo siguiente:

Sesiones CIFS existentes en una SVM.
 En la salida anterior vemos que nuestro usuario del dominio, se ha mapeado al usuario Unix pcuser. La pregunta evidente es de donde sale ese usuario y como se ha establecido dicho mapeo.

Si analizamos la configuración del servidor CIFS de nuestra SVM, usando el comando cifs options show, veremos un montón de opciones de configuración y entre ellas, la siguiente:

Opciones de configuración del servidor CIFS de una SVM.


Esa opción denominada Default Unix User, es la que establece que usuario se usará por defecto para mapear cualquier usuario Windows que se conecte a este servidor CIFS, sino hay otro método establecido de mapeo. En ONTAP 7-mode el equivalente es usar el comando options con la opción de configuración wafl.default_unix_user la cual, por defecto, también es pcuser.

Lo que nos queda es saber de donde sale ese usurio, para lo cual, solo tenemos que revisar los usuarios de la SVM desde la sección SVM Settings:

Usuarios Unix locales de una SVM.

Por tanto, como podemos ver, una SVM tendrá un conjunto de usuarios Unix por defecto, entre ellos el usuario pcuser que se usará por defecto para mapear a cualqueir usuario Windows que se conecte a una carpeta compartida mediante CIFS.

Como vemos hay un apartado Name Mapping desde el cual podemos crear nuestras propias reglas de mapeo en caso de ser necesario. Hagamos una prueba y creemos una regla de mapeo según la cual para determinados usuarios del dominio vamos a usar un usuario Unix local de la SVM diferente. Para esto creamos un par de usuarios Unix nuevos en la SVM, que llamaremos proyecto1 y proyecto2:

Creamos dos nuevos usuarios locales Unix en la SVM.
Y ahora establecemos reglas de mapeo específicas para los usuarios del dominio usrprj1 y usrprj2, para esto solo necesitamos crear las reglas en el apartado Name Mapping desde SVM Settings, las cuales serán:

Regla de mapeo para un usuario de Windows a Unix.
Reglas de mapeo de Win a Unix establecidas para dos usuarios del dominio.
Con estas dos reglas creadas, al conectarnos de nuevo a la carpeta compartida por CIFS tendremos el siguiente resultado:

Sesión CIFS establecida para un usuario con mapeo específico.
Si nos conectamos a esa misma carpeta compartida con un usuario que no tenga ninguna regla de mapeo específica, lo que tendremos será lo siguiente:

Conexión de un usuario con mapeo por defecto.
Con lo que, evidentemente se utiliza el uusario pcuser por defecto para realizar el mapeo del usuario Windows que realiza la conexión.

El hecho de que se especifique un usuario Unix para el mapeo es muy importante en el caso de acceso multiprotocolo como vimos en la entrada anterior, ya que los permisos del sistema de archivos en una SVM multiprotocolo deberían ser Unix, mientras que en una SVM CIFS como la de nuestro ejemplo, el sistema de archivos que debemos usar en nuestra SVM debe ser NTFS.

Es decir, lo recomendado cuando tengamos que crear una SVM a la que solo accederán clientes Windos mediante protocolo CIFS, es que la seguridad del sistema de archivos sea NTFS y que dejemos las opciones de mapeo de usuarios por defecto. De este modo no tendremos ningún problema de acceso ni permisos ya que, como podemos ver, el sistema de archivos de la SVM establece perfectamente los permisos según los usuarios del dominio e independientemente de los mapeos de los usuarios:

Permisos NTFS en el share de la SVM.
Al pasar a un entorno multiprotocolo, como el explicado en el post anterior, la cosa se complica un poco más ya que, como vimos, clientes CIFS y NFS accederán a la misma información, en ocasiones simultáneamente. Como un acceso CIFS desde un cliente Windows siempre requiere un mapeo correcto de usuario, el mapeo debe realizarse a un usuario Unix válido conocido por los sistemas cliente Unix o Linux que accederán a la misma información mediante el protocolo NFS.

Esto nos lleva a que la SVM debe usar como servicio de nombres el mismo que usen nuestros clientes NFS, ya que dicho servicio debe contener todos los usuarios válidos y al mismo tiempo, nos debe permitir realizar el mapeo entre clientes Windows y Unix.
 
Para estos entornos, lo recomendado es disponer de un dominio Windows, al que uniremos nuestra SVM y que además usaremos de servicio de nombres para nuestras máquinas cliente Unix o Linux así como para nuestra SVM. Hay más opciones disponibles, como usar un servidor OpenLDAP como servicio de nombres implementando el esquema LDAP correspondiente a la RFC 2307 para nuestros sistemas Unix y AD para los sistemas Windows, pero creo que lo más sencillo es disponer de una sola fuente de nombres basada en AD.

Entre los muchos atributos de las entradas de grupos y cuentas de usuario de AD, hay una serie de atributos correspondientes a la RFC 2307 que, mediante la correcta configuración del servicio de nombres de los clientes Unix y Linux y de nuestras SVMs, podremos usar para obtener información de usuarios y grupos, además de poder realizar el mapeo de usuarios del que estamos hablando.

Veamos algunos de esos atributos directamente en las cuentas de usuario de nuestro laboratorio de pruebas:

Editor de atributos de una cuenta de usuario.
Como vemos, hay una serie de atributos que contienen valores que definen una cuenta de usuario de un sistema Unix o Linux, como homeDirectory, loginShell o gidNumber, así como la definición de la clase de objeto posixAccount. En concreto la lista de atributos y clases de objeto está definida en la RFC 2307 y es recomendable su consulta para implementar un servidor LDAP como servicio de nombres para máquinas Unix o Linux.

En el caso de nuestras SVMs, debemos configurarlas para que usen como servicio de nombres de grupos y cuentas de usuario un servidor LDAP, para lo cual solo tenemos que utilizar los siguientes comandos:

Configuración del cliente LDAP de una SVM.

Configuración servicio de nombres de una SVM para que use LDAP para usuarios y grupos.
De este modo estamos configurando el cliente LDAP de la SVM para que busque la información de grupos y nombres en un servidor LDAP, indicando el DN para realizar las búsquedas, así como el dominio de AD existente, las bases de búsuqeda para cuentas de usuario y de grupo así como el ámbito de las búsquedas.

Con esta configuración establecida, al conectar mediante el protocolo CIFS a nuestra SVM multiprotocolo ya vimos que el mapeo se realizaba correctamente y que al escribir o crear datos, los permisos eran los del usuario mapeado:

Mapeo correcto de usuario Windows a usuario Unix.
Creación de una carpeta en el share.

Comprobamos que la carpeta se ha creado con el usuario Unix mapeado correcto.
Por tanto, si queremos usar SVMs en entornos multiprotocolo, lo recomendado es configurar nuestro servicio de nombres para que use nuestros DCs como fuente de nombres, tanto para los clientes Unix y Linux como para las SVMs. De este modo solo tendremos un servicio de nombres y, el principal problema será la modificación de forma correcta de los atributos correspondientes en AD, pero para esto solo hace falta un poco de Perl o Python y una pequeña web que nos deje modificarlos de forma simple o bien implementarlo en el proceso de alta de usuarios con scripts PowerShell.


Usando Jenkins como gestor de eventos

Hay ocasiones en las que ciertas herramientas pueden usarse para resolver problemas de administración, aunque sus funcionalidades estén enfocadas a otros ámbitos.

Imaginemos que al terminar la instalación de un sistema, es necesario realizar ciertas tareas adicionales que requieren acceso a una red aislada, pero el equipo en cuestión no tiene permisos de acceso a dicha red. Por ejemplo pensemos en la instalación y configuración de un equipo de usuario que, en su paso final debe acceder a la red de administración para lanzar un script que realizará unas tareas de configuración adicionales.

Para casos similares a este y muchos otros más, podemos usar Jenkins configurando un job parametrizable que podremos lanzar mediante una simple petición HTTP POST.

La idea es usar Jenkins como gestor de eventos, siendo el evento generado por nosotros mediante la petición HTTP la cual, al corresponder a un job configurado, ejecutará un script con los parámetros que pasemos en la petición.

Lo primero es crear un usuario que será el que utilicemos para todas las tareas de gestión de eventos y darle los permisos necesarios que, si estamos usando el modo de seguridad basado en proyecto, deben ser al menos de lectura global, de lectura y ejecución de jobs y de update de ejecuciones. Para diferenciar este usuario del resto lo mejor es darle un nombre distintivo, en nuestro caso creamos el usuario Event Manager (eventmgr).

Ahora, para definir el job solo tenemos que asegurarnos de marcar la opción Esta ejecución debe parametrizarse y Lanzar ejecuciones remotas. En resumen, la forma de configurarlo es la siguiente:

Creación inicial del job y asignación de seguridad.

Configuración de parametrización y especificación de los parámetros.
Marcamos el job como ejecutable remotamente y generamos un token de seguridad.
Especificamos que se ejecutará, en este caso de ejemplo un simple echo.
Con el job ya configurado, lo único que es necesario hacer ya es lanzar una petición HTTP a nuestro servidor Jenkins con los parámetros necesario, en nuestro ejemplo solo nos va a generar un fichero con los valores de ambos parámetros. La URL a la que debemos realizar la petición HTTP será:

Acceso a la URL especificando los parámetros para nuestro job.
Y podemos comprobar que el job se ha ejecutado correctamente y ha generado el fichero con los valores especificados en los parámetros pasados al job:

Resultado de la ejecución del job.
Por tanto con Jenkins podremos realizar acciones basándonos en eventos que nosotros mismos generemos, lo cual nos facilitará bastante ciertas tareas administrativas.