¿Qué es Docker?

¿Qué es Docker?

Docker es una pequeña maravilla de la ingeniería de sistemas. Lo mejor para entender qué es y para lo qué sirve es comentar un posible caso práctico.

Imagina que necesitas instalar en un servidor una aplicación web que tiene un montón de dependencias para funcionar: una base de datos mongo, un servidor de apps como nginx, una máquina virtual de java, etc. Tras un día de duro trabajo, todo funciona a la perfección pero al cabo de unos días viene el jefe y dice: «el servidor en el que has montado la infraestructura no sirve, hemos encontrado una alternativa más barata». Un día de trabajo a la basura.

Seguro que muchas veces has encontrado el término «contenedor» asociado a Docker y es posible que no te quedara del todo claro cual es el símil en realidad. El símil es que el contenedor es, ni más ni menos, que la aplicación y todo lo necesario para que funcione, preparado para correr en cualquier equipo con Docker instalado. Es un «paquete» con todas las dependencias necesarias y configuradas para ejecutar la aplicación que has desarrollado ejecutando un par de comandos y despreocupándote completamente del nuevo entorno de despliegue.

¿Que tienen que ver las máquinas virtuales en todo esto?

Pues no mucho, por no decir nada. Estamos más acostumbrados a trabajar con máquinas virtuales y por eso tendemos a usarlas como comparativa para definir qué es Docker. La diferencia fundamental es que las máquinas virtuales usan como base un sistema operativo con todos sus procesos al margen del sistema operativo host, mientras que Docker usa al propio host, por lo que es muchísimo más ligero. Las máquinas virtuales virtualizan equipos y Docker virtualiza procesos. Esto conlleva que Docker sea mucho más ligero tanto en espacio de disco duro como RAM. Otra gran diferencia con respecto a las máquinas virtuales es su concepción de vida útil. Podemos crear contenedores como churros y destruirlos sin mayor preocupación (se pueden persistir datos en el host fácilemnte), no así las máquinas virtuales, por razones obvias.

Puedes pensar en Docker como en la MV de Java y en los contenedores como en los .jar o .war con todas las librerias y código necesarios para la ejecución del programa.

Con Docker en definitiva se simplifica todo lo que tiene que ver con la portabilidad de aplicaciones, asumiendo el rol de interfaz común.

Instalar Mysql en Debian 9

Más de una vez. sobre todo al principio de mis andaduras por Linux, me he encontrado con problemas a la hora de instalar mysql en la distribucion Debian 9. Multitud de errores

Recuerdo mis primeras andanzas en Linux con la base de datos MySQL-MariaDB. Era un joven confuso que quería correr los menos comandos posibles mediante sudo, pues mi usuario ya pertenecía al grupo con mayores privilegios del sistema. No entendía por qué no podía correr procesos como mysql usando el usuario root de la base de datos (que sabía que existía).

user@debian:~$ mysql -u root -p
Enter password:
ERROR 1698 (28000): Access denied for user ‘root’@’localhost’

¿Por qué se da este error? La explicación es relativamente sencilla: en MariaDB, la autenticación por defecto para el usuario root se da mediante sockets de UNIX.

Unix authentication mysql root

Esto quiere decir, de forma sencilla, que si la base de datos tiene un usuario que coincide con el usuario de la sesión que quiere iniciar el proceso mysql, permite iniciar este proceso sin contraseña. Por mucho que nosotros digamos que nos vamos a conectar con el usuario root que existe en el DBMS y se supone que tiene máximos privilegios, no va a existir autenticación posible, por dos motivos:

  1. No estamos iniciando el proceso desde una sesión de root (con sudo).
  2. El ususario root del DBMS no tiene contraseña seteada, pues se autentifica mediante sockets de UNIX en función de la sesión que lanza el proceso.

Si no te importa la seguridad porque estás en un ordenador personal y tienes ese tic nervioso que yo tenía al principio usando sistemas Linux, en este enlace puedes encontrar los pasos necesarios para cambiar la autenticación para root de modo socket a modo usuario-contraseña.

Si la pifiamos mucho durante el proceso, y nos cargamos Mysql podéis seguir este otro enlace para reinstalar completamente el gestor de base de datos en vuestra distribución.

De cualquier forma yo recomiendo que no cambiéis el modo de autenticación de root a no ser que sea completamente necesario. Si queréis permisos de administración elevados de la base de datos, cread un nuevo usuario en el gestor con el mismo nombre que vuestro usuario en UNIX y con autenticación mediante sockets para no necesitar setear contraseña ninguna. Mucho más cómodo y seguro.

El patrón Composite

El patrón Composite

Existen ocasiones en las que sabemos que un objeto va a estar compuesto de otros muchos, que un contenedor va a albergar distintos componentes, y que todos esos componentes van a tener un comportamiento o funcionalidad similar. En definitiva, sabemos que nuestra implementación va a tener una estructura jerárquica.

Este patrón define una serie de conceptos fundamentales para su comprensión:

  • Component: implementa un comportamiento común entre las clases mediante una interfaz de manipulacion a sus nodos hijos. Es el nodo principal del que derivan los demás.
  • Leaf: representa los nodos “hoja” (no poseen hijos). Define comportamientos para objetos primitivos.
  • Composite: Son los componentes del patrón. Extienden el Componente principal y pueden ser padres de otros nodos hijos.
  • Cliente: manipula objetos de la jerarquía a través del nodo principal.

Los clientes usan la interfaz de Component para interactuar con objetos en la estructura Composite. Si el nodo con el que interactúa es un nodo primitivo o «leaf» lo hace directamente, pero en caso de interactuar con un Composite, lo hace a través de la interfaz que provee el Component.

El diagrama UML del patrón es de esta forma:

Nota importante: Este diagrama difiere un poco del diagrama «oficial» para evitar el error, a mi juicio, de la implementacion de métodos inútiles como por ejemplo en la propia Wikipedia. Bajo la implementación tradicional, se requiere que las clases «leaf» lancen errores de tipo UnsupportedOperationException. Para mí no tiene mucho sentido cuando puedes crear un nuevo nivel de abstracción que además separe responsabilidades entre la clase que define la operacion (Component) y la que define los métodos jerárquicos (Composite).

Si nos damos cuenta, aquí los nodos «Leaf» no implementan los métodos requeridos para realizar la jerarquía, y solo ejecutan operaciones. Los Composite son los encargados de hacer que los Components (tanto Leafs como Composite) ejecuten una determinada acción.

A continuación expongo un ejemplo de código de una clase Composite y otra Component bajo el anterior diagrama. Vamos a crear un ejemplo con el mapa político de Europa. En nuestro caso, la interfaz común será Territorio, que podrá contener otros territorios, paises y provincias consecutivamente hasta llegar a los nodos hijos finales: las ciudades. La finalidad será contar el número de habitantes.

Este patrón permite la generación de una jerarquía fácilmente, asi como una interfaz de acceso a toda la jerarquía para el cliente, aislándolo de la implementación concreta y por tanto siguiendo con los principios de diseño.

«Agile is dead»: Dave Thomas

«Agile is dead»: Dave Thomas

Tras escribir las entradas de Scrum, el todopoderoso algoritmo de Google no me ha dejado de sugerir nuevos articulos y videos relacionados. El último recurso me ha llamado poderosamente la atención, porque consistía en una conferencia de Dave Thomas bajo el título «Agile is dead». Ni que decir cabe que la curiosidad me pudo y me dispuse a escuchar cómo uno de los fundadores de los valores que hoy conocemos gracias al Manifesto for Agile Software Development, aparentemente cargaba contra aquello que predicaba hasta no hace mucho tiempo.

Evidentemente, Dave Thomas no carga contra los valores fundamentales de su manifiesto durante su conferencia, sino contra la industria creada en torno a esos famosos doce principios. Cómo la necesidad de vender un producto lo ha hecho excesivamente complejo, llegando incluso a pervertir la propia filosofía del desarrollo ágil mediante la imposicion de reglas y procedimientos férreos, con la única finalidad de colocar un sello de autenticidad.

Crear certificaciones, cursos con un valor monetario orientado a empresas… toda esta industria favorece la aparición de procedimientos estandarizados bajo la premisa de «esto es lo único válido para implementar el desarrollo ágil de software». Frente a esto, volver a los orígenes, a los doce principios, y tener la suficiente inteligencia y flexibilidad para implementar el desarrollo agil en tu entorno de trabajo. Si el desarrollo ágil es adaptarse, adaptémonos también al desarrollo ágil.

 

El patrón Iterator

El patrón Iterator

En Java como en cualquier lenguaje de programacion moderno orientado a objetos, existen multitud de tipos de listas y colecciones. Algunas de ellas permiten añadir punteros nulos, otras relacionan una clave a un valor y otras tienen una lógica interna que permiten comportamientos muy particulares y las hacen idóneas para según qué escenarios. Sin embargo todas ellas necesitarán eventualemente recorrer sus elementos, bien sea para ordenarlos o para ejecutar una acción sobre cada uno de ellos.

Evidentemente esto no es un problema en sí mismo, salvo que existan multitud de colecciones de distintos tipos que tenga que manejar un solo objeto. Si hacemos caso omiso a las estrechas dependencias que se crean entre las clases, no supone mayor problema, pero los patrones de diseño nos obligan a pensar en el largo plazo, y sabemos que una clase que depende de muchas implementaciones concretas (en este caso tipo de colecciones) puede ser un quebradero de cabeza. La solución que se nos puede pasar de primeras por la cabeza es la abstracción. En java por ejemplo, todas las colecciones salvo los mapas, heredan de la interfaz Collection, pero eso tampoco nos ayuda demasiado, ya que agrupaciones como los arrays o los mapas no heredan de Collection.

Se podría decir que java nos da una solución de serie, que es la de implementar en una clase la interfaz Iterator, de forma que no tengamos que saber qué tipo de coleccion contiene la clase, sino que es iterable y por tanto podemos navegar por los elementos que contiene.

Sin embargo, no puede ser tan fácil. Existe un principio de diseño que nos dice que hay que encapsular aquel código que depende de un único actor. Dicho de otra forma: debemos aislar el código que sólo tenga un motivo para cambiar. Fíjate que ahora la clase que contiene las colecciones tiene dos razones para cambiar, por ejemplo la forma de manejar los elementos y la forma de recorrerlos. Necesitaremos por tanto desarrollar una clase que cumpla este cometido concreto, implementando la interfaz Iterator.

Imaginemos por ejemplo una tienda de ropa con distintos proveedores. Cada proveedor tiene una forma distinta de organizar su ropa. Un proveedor la almacena en un Array y otro la almacena en un ArrayList. Ambas formas de organizar las prendas impican una forma distinta para iterar sobre ellas.

Como dueños de un comercio necesitamos acceder a la ropa de esos proveedores, pero tenemos el problema de depender de (por el momento) dos tipos de colecciones concretas. Es decir, si obtenermos las prendas mediante getters obtendremos en cada caso un array y una lista respectivamente. No queremos depender de una forma de iterar la ropa concreta para cada proveedor, por lo que creamos un objeto que se encargue de abstraer esta funcionalidad y que las tiendas (sus clientes) puedan usar. Este objeto deberá ser creado por los propios proveedores a traves de un método que devuelva un objeto iterable.

Si vemos detenidamente el diagrama UML anterior, podemos apreciar rápidamente ciertas formas de abstraer al cliente de las implementaciones concretas de los proveedores. Hemos dicho que todos los proveedores tenían que «entregar» un objeto de tipo Iterator al cliente, por lo que somos capaces de generalizar este comportamiento y crear una interfaz para todos los proveedores. De esta forma no dependemos de implementaciones concretas en el cliente, sino de una abstraccion: el Proveedor.
Ahora tenemos un cliente desacoplado tanto de los proveedores como del tipo de colecciones que usan.