Qué es docker y porqué deberías empezar a usarlo en tus proyectos

Docker es una herramienta que facilita las tareas de desarrollo e implementación a los programadores y la subida a producción de los devops. A continuación te cuento el porqué
Qué es docker y porqué deberías empezar a usarlo en tus proyectos

Seguro que si trabajas en el mundo del desarrollo alguna vez has oído el término docker, container o imagen de docker. Seguramente se lo habrás escuchado a algún desarrollador o programador, al devops de tu empresa o a la gente de sistemas. En este artículo te quiero contar qué es eso de docker además de contar sus ventajas, cómo surge y porqué deberías empezar a usarlo si es que no has empezado ya. Y todo esto usando un lenguaje para todos los públicos.

Qué es docker en pocas palabras

En muy resumidas palabras, docker es una herramienta que permite crear contenedores ligeros y portables que contienen aplicaciones de software y pueden ser ejecutados en cualquier otra máquina con docker instalado, independientemente del sistema operativo.

Hace muchos años, el uso de Java fue revolucionario por un concepto muy sencillo: cualquier programa hecho en Java funcionaba en cualquier ordenador que tuviese la máquina virtual de Java instalado. Pues algo así viene a ser docker: una plataforma que te permite desarrollar un código que, de forma garantizada, funcionará en cualquier otra máquina con docker instalado.

Cuando creamos un contenedor de docker, ponemos en él nuestro código fuente y todas las dependencias que este necesita para funcionar y un docker en cualquier otra máquina se encargará de ejecutarlo de igual forma que lo hace el de tu máquina.

La industria ha aceptado docker como formato de contenedor estándar, pero han pasado muchas cosas hasta llegar a este momento.

Un poco de historia. Dependencias y máquinas virtuales

Al principio no existía docker, ni git ni gestores de dependencias como lo conocemos ahora. El ciclo de software era bastante diferente (e incómodo en mi opinión).

Tú escribías un código fuente, que seguramente necesitase librerías externas para funcionar. Una parte de estas librerías se descargaban en formato zip desde la página del autor, se extraían en una carpeta de tu proyecto y se conectaban con tu código. Otras librerías se instalaban a nivel de sistema operativo, con suerte con algún gestor de linux como aptitude. Todo funcionaba correctamente y el resultado es el que se esperaba. El problema venía cuando tenías que compartir ese código con algún compañero o subirlo a producción.

O bien hacías un fichero zip con el código y librerías o bien lo subías a Subversion. Pero, un momento... ¿y las librerías del sistema operativo? ¿Cómo las compartimos?. O si era un proyecto en algún lenguaje interpretado como PHP y habíamos habilitado una extensión, ¿cómo le indicamos a nuestros compañeros o equipo de sistemas que necesitamos activarla también en producción?. Respuesta corta: con un posit o un fichero "importante.txt". ¯_(ツ)_/¯

A parte, es posible que necesitase una dependencia concreta que tuviese que ser instalada en formato binario; es decir, mediante un fichero exe o .rpm. En Ubuntu se instalaba sin problemas; Redhat tenía una versión minor diferente; la gente con Mac no podía instalarla y en Windows funcionaba solo los días impares.

Ante este caos de versionado de dependencias y disparidad de sistemas operativos y versiones, la industria dio un paso importante para que todos los desarrolladores hiciesen aplicaciones compatibles con el sistema operativo que existía en los ordenadores de producción (los que usaban los clientes finales o los visitantes de la web): todos a una con las máquinas virtuales. Si los sistemas operativos de producción eran un Debian 4, todos los desarrolladores con Debian 4. Los que no usaran este sistema operativo concretamente deberían instalar una máquina virtual, así podrían seguir trabajando en Windows, Mac u otra distribución de Linux, pero probaban el código en dicha máquina, garantizando que funcionase en los ordenadores de producción.

De esta forma, es cierto que el problema con las dependencias dejaba de existir porque de una forma u otra el código siempre se ejecutaba con el mismo sistema operativo, pero aparecían otros problemas derivados: el rendimiento y ¿qué hacemos cuando en el servidor haya que instalar Debian 5?.

Una máquina virtual es más lenta que tu propio equipo: eso es un hecho. Así que si el rendimiento del equipo bajaba. Una tarea que tardaba una hora en hacerse pasaría a ocupar dos horas. Al final son dos sistemas operativos (uno sobre otro). Y por otro lado, cuando tuviésemos que saltar a Debian 5 (o cualquier otro), ¿qué hacemos con el código? Pues sencillo: ¡ actualizar en tu local y a buscar fallos !

En próximos artículos me gustaría hablar sobre la virtualización. Ahí te contaré porqué un sistema virtualizado (como una máquina virtual) siempre es más lento que un sistema real.

Docker al rescate.

Durante años, las máquinas virtuales fueron el estándar de la programación, sobre todo cuando había servidores involucrados. Hasta que un equipo de desarrolladores supo como unificar herramientas que existían desde hace años en el kernel de Linux y hacer que el proceso fuese muy sencillo y rápido: docker.

Docker permitía crear un "ente" que contenía el código fuente y el solito se encargaba de instalar las librerías, hasta las del propio sistema operativo. Y sin modificar el sistema anfitrión, tu ordenador. Esas librerías solo existían en ese "ente".

De esta forma, da igual el sistema operativo, versión o librerías que este tuviese instaladas de serie: docker crea un espacio único para todo el equipo y para los servidores de producción. Tú puedes trabajar en un Ubuntu 18, tu compañero en un Ubuntu 21, otro compañero en Mac, y un tercero en Windows. Y cuando tocase actualizar el sistema operativo de producción no hay contratiempos: docker hace que funcione exactamente igual que en tu máquina. Y sin gestión de máquinas virtuales.

Esto es definitivamente genial: gratis, rápido y sin efectos colaterales. Pero... ¿cómo funciona Docker de forma que yo lo pueda entender?

Como funciona Docker explicado de forma sencilla

Docker, en versión de forma que sea comprensible por todo el mundo, funciona con dos palabras: contenedores e imágenes. El contenedor de docker es esa caja o contenedor donde se ejecuta el código y se instalan las librerías y dependencias y, por otro lado, la imagen de docker es el modelo a partir del cual hay que crear ese contenedor.

En primer lugar, creamos un fichero específico en el proyecto en el que estamos trabajando: un fichero llamado Dockerfile. Este fichero, contiene los comandos para crear la imagen de docker, pero de forma muy generalista, contiene algo así como "necesito crear un contenedor basado en Ubuntu 20, meterle un Apache e instalar Python 3.7 con esta, esta y esta dependencia o librería. Este es mi código fuente de Python y necesito que lo pongas en la carpeta /var/www/python. Despúes, conecta Apache con ese Python para que atienda peticiones web".

Con estas instrucciones, docker crea una imagen: un modelo, plantilla y molde de cómo debe ser el contenedor. Docker va creando una fotografía, capa a capa, de cómo debería quedar el sistema de esa "especie de máquina virtual".Una vez que ha terminado, la imagen está lista para ser compartida con el resto de usuarios y máquinas. Al ser la imagen una plantilla para crear contenedores, docker puede crear todos los contenedores que se necesiten a partir de ese molde.

Una imagen de docker es fija y nunca puede cambiar. Cada vez que hagas un cambio en las instrucciones de montaje, deberemos crear otra imagen. O bien desde cero o bien añadiendo información extra a la versión anterior de la imagen. Una imagen funciona de forma parecida a git: cada cambio que hagamos añade información al repositorio de forma que se puede reconstruir siguiendo las instrucciones pero nunca podemos (o deberíamos) eliminar un commit intermedio.

Cuando compartimos esa imagen con alguien mediante un registry (como un repositorio de git pero en vez de ficheros, controla imágenes ), otra persona puede crear un contenedor a partir de esa imagen. Docker extrae de esa imagen fija y estática un nuevo contenedor vivo sobre el que podemos trabajar. Así creamos una "especie de máquina virtual" viva y operativa a partir de una fotografía (imagen).

Así, cuando hagas un cambio en el código o en la versión de Python, tus compañeros no tienen que desinstalar la versión antigua de Python e instalar la nueva en sus sistemas operativos. Nosotros, como autores, solo tendremos que crear una nueva imagen y compartirla con nuestros compañeros. Ellos se la bajarán y docker creará una nueva caja o contenedor con las nuevas versiones dentro.

Docker no utiliza la virtualización, al menos en Linux. Por lo tanto, un contenedor de docker no puede ser considerado una máquina virtual. Solo he hecho el símil para que te quede más claro que los cambios que hagas dentro del contenedor no afectan a tu ordenador.

Docker ha cambiado la forma de trabajo

Gracias a docker (y a la integración y despliegue continuo), el proceso de subir a producción ha mejorado muchísimo y ha incrementado su productividad. Más o menos, el flujo de trabajo cuando se desarrolla con docker funciona de la siguiente manera de forma general:

  1. Hago un cambio en el código fuente o en las librerías o dependencias que se usan en el proyecto. Gracias a git, hago un commit de estos ficheros cambiados y un push al repositorio central.

  2. Un sistema de integración continua captura ese push y comienza de forma automática las pruebas de código. Si son correctas y no presentan bugs, leerá el fichero Dockerfile y creará una nueva imagen que compartirá en un registry.

  3. Avisará (de alguna forma) a los servidores de producción de que hay una nueva imagen. Estos servidores, se bajarán la última versión de la imagen. Eliminarán los contenedores antiguos y crearán nuevos contenedores basados en esta última imagen. Y ya hemos actualizado los servidores de producción.

En ningún momento hemos tenido que avisar al equipo de sistemas para que se descarguen una dependencia, ni actualicen una versión, ni nada. Docker contiene todo lo que necesita tu código para funcionar.

Las ventajas de docker

Si hasta este momento no te he convencido de porqué deberías usar docker tanto si trabajas en solitario como si trabajas en equipo, a continuación te enumero las ventajas de usar docker.

  • Las librerías no se almacenan en tu ordenador, sino que están en el contenedor. Cada vez que quieras cambiarlas, no tendrás que tocar tu sistema operativo, tan solo delegar en docker para que genere un nuevo contenedor.

  • Da igual que entorno tenga un individuo del equipo (sistema o versión); el código siempre se ejecutará en un entorno idéntico controlado. Igual para producción.

  • Un contenedor de docker es mucho más rápido que una máquina virtual. Tarda menos en arrancar, en recomponerse, en realizar operaciones y en ser detenido cuando haya que cambiarlo. Si sigues usando Vagrant, este es un punto importante.

  • Al estar tu código dentro de un contenedor de docker, tu ordenador o servidor está más protegido. Si alguien consiguiese acceso de forma remota, dañaría el contenedor; no la máquina física.

  • Aunque no lo he mencionado en este artículo, puedes configurar docker para que si detecta algún fallo en el código (como que se quede bloqueado o no responda), pueda parar ese contenedor y volverlo a crear. Esto es más fácil que reiniciar toda la máquina. Este punto lo matizaré más cuando escriba sobre Docker y el Cloud.

  • En referencia al punto anterior, es más fácil delegar en docker (con algún orquestador) administrar y actualizar 400 contenedores de forma automática que 20 máquinas físicas.

Consideraciones

Este artículo pretende ser una introducción sencilla para el público general, sobre todo aquel que está empezando en docker. Por ello, muchos conceptos se han dejado de lado o se han explicado de forma demasiado simple para poder centrarse en el core de docker. En próximos artículos se explicarán nuevos conceptos de docker y se matizarán algunos de los aquí explicados. En definitiva: contenido de divulgación.

En cualquier caso, entender para qué sirve docker (al menos de forma superficial), es garantía de éxito cuando estás empezando en el mundo del desarrollo o devops. No puedes entender conceptos más ámplios como microservicios, distribuido, orquestadores o Kubernetes sin entender los contenedores de docker.

Espero haber llamado tu atención y que te intereses por docker, la piedra angular en el mundo del desarrollo.