Dockerizar perl

En este artículo voy a hablar sobre como se pueden montar un microservicio para una aplicación existente escrita de Perl. Mi lenguaje de scripting preferido es Perl, debido a que es lenguaje versatil, muy flexible y rápido en su desarrollo a la hora de escriptar.

Durante años he desarrollando muchos scripts y librerías en Perl que me interesa utilizar como si se tratase de un microservicio.

Montarlo como microservicio, me va a permitir escalar la funcionalidad o que sea accesible desde una aplicación web. Normalmente utilizo sistemas de mensajería con rabbitmq o 0mq para la comunicación entre el cliente y el worker, o bien sistemas mixtos de mensajería y servicios restful. Es importante considerar que a la hora de trabajar con mensajería hay que ser cuidadoso con el uso de librerías asíncronas ya que esta basado en AnyEvent.

En este ejemplo, voy a montar un API restful basado en nodejs con express que puede suponer una situación más común en un entorno web. Se puede utilizar un framework web en Perl como Mojolicious pero personalmente prefiero utilizar Nodejs debido a las posteriores integraciones con Javascript en la parte del cliente.

Los puntos básicos que tiene que cubrir nuestro contentedor son:

  • Instalar los módulos que necesitamos.
  • Instalar el código de nuestra aplicación y/o nuestras librerías.
  • Proveer un acceso Restful que ataque a nuestros scripts

El contenedor

Vamos a desglosar punto a punto el Dockerfile:

FROM node:4.6.2
MAINTAINER César Maeso 

RUN 	apt-get update 							;\
	apt-get install -y 						;\
	apt-get install build-essential -y 				&&\
	apt-get install cpanminus -y                    		&&\
	cpanm JSON Data::Dumper Cache::File YAML::XS File::Spec   	&&\
	mkdir -p /usr/src/app 																	

COPY 	libs/ 	/usr/share/perl5
COPY    bin/  	/usr/local/sbin
COPY 	app/ 	/usr/src/app

WORKDIR /usr/src/app
RUN npm install

CMD [ "npm", "start" ]

Instalar los módulos que necesitamos.

La forma más sencilla de comenzar es utilizar una distribución que incluya Perl o utilizar el Docker oficial de Perl en Dockerhub.

En este caso, partiremos de la imagen oficial de nodejs, cuya instalación actualizamos en las primeras líneas con apt-get.

Para poder compilar los módulos y librerías de perl es importante instalar las herramientas de compilación:

apt-get install build-essential  # Debian
yum groupinstall "Development Tools" # Centos

Para realizar la instalación de paquetes, podemos utilizar el cpanminus que realiza una gestión completa de la instalación de paquetes incluyendo sus dependencias.

Como hacemos aquí, dónde instalamos varios paquetes que se pueden encontrar en cpan:

apt-get install cpanminus -y                    		
cpanm JSON Data::Dumper Cache::File YAML::XS File::Spec  

O bien, se puede utilizar la forma más tradicional utilizando la shell de cpan:

yes| perl -e 'require CPAN; CPAN::Shell->force(qw(install YAML::XS));' 

Instalar el código de nuestra aplicación y/o nuestras librerías.

Realizaremos la instalación de nuestras librerías en Perl copiandolas al directorio de librerías por defecto, /usr/share/perl5, o bien seteando la variable de entorno PERL5LIB añadiendo al Dockerfile:

ENV PERL5LIB /usr/share/loquesea/

Como alternativa, se podrían setear en los scripts directamente el path dónde se encuentren nuestras librerías si lo necesitasemos.

Lo habitual cuando utilizamos rutas de librerías personalizadas o entornos de desarrollo:

#!/usr/bin/perl
BEGIN { unshift @INC,"/la_ruta_que_me_parece"; }

Por sencillez, prefiero usar la ruta por defecto.

Con estos pasos, ya tenemos montado el contenedor que ejecuta nuestros scripts en perl y sus módulos. Ahora lo vamos a comunicar con el exterior con un API Restful. Se puede montar con mojolicious pero me gusta más node como framework web, además que ser más útil de cara a integrarlo en la web si utilizas javascript.

Para ello:

COPY    bin/  	/usr/local/sbin

En la primera línea copio unos pequeños scripts que hacen uso de mis librerías en Perl, las que copie con anterioridad. Es decir, son un punto de entrada, que me permite hacer uso de mis librerías sin realizar modificaciones en las mismas.

El código de ejemplo de uno de estos ejecutables podría ser:

#!/usr/bin/perl
use Mipaquete::Milibreria;
die unless length @ARGV[0];
my $r= eval{
	my $milibreria= Mipaquete::Milibreria->new(parametro => @ARGV[0]);
	$milibreria->mifuncion();
};

Acceso Restful a nuestro script de entry point

Ok. Ya sólo nos falta, enlazarlo con el servicio web. Vamos con Node:

COPY 	app/ 	/usr/src/app
RUN npm install

Lo que hago es copiar todos los ficheros de mi aplicación javascript para luego realizar la instalación de la aplicación y sus componentes. Sin extendernos más con el uso de express o node:

#!/usr/bin/env node --harmony
'use strict';
const
express 	      = require('express'),
app 		      = express(),
spawn                 = require('child_process').spawn;

app.get('/miapi', function(req, res) {
  var child = spawn('/usr/bin/perl', ['/usr/local/sbin/mi_punto_de_entrada.pl']);
  child.stdout.pipe(res);
});

app.listen(8080, function(){
  console.log("Servidor up");
});

Para completar el microservicio podemos implementar nuestro entry point en perl, o bien la api de express para que la respuesta que ofrezcan sea un formato de datos como JSON, de este modo tendríamos una api restful json completa.

Para poner en marcha el contendedor haríamos un build de la imagen, que instalará las librerías tanto de perl como las dependencias que definamos en el fichero package.json para node y, por último arrancaríamos el worker escuchando en el puerto 8080 o con la configuración de red que hayamos planeado para nuestra arquitectura.

César Maeso


Creative Commons License

Esta obra está bajo una licencia de Creative
Commons Reconocimiento-NoComercial-CompartirIgual
4.0 Internacional
.