Docker: creando un contenedor de TetriNET para la Global Azure Bootcamp 2017

9 min de lectura
Docker: creando un contenedor de TetriNET para la Global Azure Bootcamp 2017

Todos los elementos a los que hace referencia este artículo se pueden encontrar en un nuevo repositorio de Github que he creado donde publicaré todos los temas relacionados con Docker, siéntete totalmente libre de examinarlo en https://github.com/cmilanf/docker

Preparando la Global Azure Bootcamp 2017 de Madrid parece que algunas personas se acordaban de la charla de Alberto Marcos y servidor en la edición de 2016, donde utilizamos el TetriNET para ilustrar un ejemplo de conexión inversa cliente-servidor en una Point to Site construida en Azure mediante el glorioso SoftEther VPN. Algunas personas como Alexandre Vázquez no olvidaron el momento:

Así que... ¿por qué no preparaba un servidor oficial de TetriNET al que todo el mundo pudiera conectarse en el evento... y fuera de él? ¡Manos a la obra!

TetriNET, tecnología MUY legacy... ¿en Azure?

TetriNET fue desarrollado en el año 1997 por St0rmCat. El hecho de que todo el mundo sabe jugar al Tetris, lo fácil que era aprender el modo multijugador y los bajos requerimientos hizo que se popularizara muy rápidamente.

St0rmCat integró un pequeño servidor en el mismo programa que hacía las veces de cliente en Windows, pero pronto muchas personas se lanzaron a desarrollar versiones de servidor dedicado para UNIX y otras plataformas. Entre ellas hay dos a las que el paso de los años no les ha hecho especial daño:

  • Jetrix, con la última versión publicada el 26 de agosto de 2008, múltiples capacidades planificadas se quedaron a medias. Esta desarrollado en JAVA.
  • Tetrinetx, con la última versión publicada el 22 de abril de 2001. Está desarrollado en ANSI C, por lo que es pequeño altamente portable y muy muy rápido, casi podría correrlo mi MSX.

Siempre he sido muy fan de Tetrinetx, que además se puede encontrar aún a día de hoy en los repositorios de las distribuciones de GNU/Linux más comunes como Debian o Ubuntu (no confundir Tetrinetx con tetrinet-server).

Sin embargo encontré que los paquetes precompilados no parecen funcionar adecuadamente y tampoco estan bien adaptados al amigo systemd. Si a eso le agregamos que no calificaría de confiable una máquina que está ejecutando una pieza de software de servidor expuesta a Internet que no se ha actualizado desde 2001...

Solución: contenedor Docker

Un contenedor Docker era la solución ideal para dejar al venerable Tetrinetx preparado para ejecutarse en Azure, bien en una máquina virtual o bien en Azure Container Service. Las razones que hacen de la contenerización una solución ideal para este caso son:

  • Abstraerse de dependencias. Una pieza de software del 2001 puede tener dependencias de elementos que quizás no se encuentran en sistemas operativos actuales o bien deben compilarse específicamente.
  • ¿Seguro que estás seguro? Servidor sin actualizaciones desde 2001 expuesto a Internet. Por muy estable que sea la versión voy a asumir que si quieren hackearlo podrán, en cuyo caso mejor estar aislado en un chroot jail (lo que hacía hasta la fecha), una máquina virtual o... la mejor de todas: un contenedor. Con ello no nos hacemos invulnerables, pero al menos es bastante más complicado saltar a otro proceso del sistema.
  • Reutilización. Es muy fácil reutilizar contenedores, por lo que desplegar el servidor de TetriNET en on-premises, Azure u otro proveedor de cloud se hace trivial.

Un momento Carlos... ¿qué es Docker?

Docker es una plataforma de código abierto que automatiza la implementación de aplicaciones dentro de contenedores software. El concepto de contenedor software es la clave aquí, que no es otra cosa que virtualización a nivel de sistema operativo, donde el kernel permite la existencia de múltiples instancias del espacio de usuario, totalmente aisladas entre sí.

Esto puede sugerir un entorno muy similar a la virtualización mediante hipervisores conocidos como Xen, VMWare o Hyper-V; pero la principal diferencia es que el nivel de abstracción necesario hasta llegar a la ejecución de nuestra aplicación es notablemente inferior en el caso de contenedores. Veamos una comparación gráfica:

El diagrama habla por sí solo. En la virtualización cada máquina virtual debe ejecutar un sistema operativo completo con su propio set de bibliotecas de ejecución. En un contenedor estos elementos se comparten y como podréis imaginar, arrancarlo no suele llevar más de 2 segundos.

Tetrinetx no ocupa más de 100 KB. Me parecía exagerado crear una máquina virtual para él solo. La idea de un contenedor me seduce bastante más.

Construyendo una imagen Docker

Si algo me ha sorprendido de Docker es lo fácil que es entender sus conceptos e iniciarte en la creación de tus propios contenedores. Al igual que ocurre en la programación orientada a objetos con las clases y los objetos en Docker tenemos las imágenes y los contenedores. Una imagen Docker es una aplicación o servicio listo para ser instanciado en tantos contenedores como queramos.

Explicar el proceso paso a paso me daría para una publicación en sí y lo encuentro redundante ya que la documentación oficial de Docker tiene un Get Started que lo explica paso a paso a modo de tutorial. Al final del mismo habremos construido nuestro primera imagen personalizada.

Dockerfile, ¿FROM ubuntu? No, FROM alpine

Si has seguido el tutorial, ya sabrás que el Dockerfile es el archivo de receta maestro para cocinar nuestra imagen Docker que después podremos instanciar en contenedores. Los pasos básicos para conseguir una imagen con Tetrinetx listo para funcionar son:

  1. Instalar o hacer base con una distro GNU/Linux. Cuanto más pequeña mejor. Esta es una capacidad de Docker: el poder basar nuestra imagen en otra en lugar de construirla de 0.
  2. Instalar compilador de C y herramientas básicas de desarrollo.
  3. Compilar Tetrinetx y sus dependencias.
  4. Compilar Tetristats, un generador de rankings en HTML.
  5. Eliminar los compiladores y herramientas instaladas en el paso 2.
  6. Configurar el servidor de TetriNET.
  7. Instalar un servidor web ligero, NGINX es un buen ejemplo.
  8. Agregar los elementos a publicar en el servidor web.
  9. Declarar los puertos TCP y UDP que va a exponer el contenedor.
  10. Declarar el ENTRYPOINT.

Un contenedor basado en Ubuntu Server se nos iba aproximadamente a los 500 MB. No está mal dado que es todo el sistema operativo, pero un poco ineficiente si tenemos en cuenta que el servidor de Tetrinetx son unos 100 KB.

Y así fue como descubrí Alpine Linux, una distribución altamente optimizada, basada en musl libc y busybox. ¡El sistema base apenas ocupa 5 MB!

Basando todo el proceso anterior en Alpine Linux la imagen de Docker se queda en unos 35 MB. Sigue siendo una diferencia importante, pero que estoy dispuesto a tolerar.

Para todos los temas relacionados con Docker he creado un nuevo repositorio de Github donde puedes examinar y descargar el código fuente de todo lo comentado en este artículo: https://github.com/cmilanf/docker

Dockerfile

Echemos un vistazo al archivo resultante:

FROM alpine:3.5

LABEL title "Tetrinetx + Tetristats Docker Image"
LABEL maintainer "Carlos Milán Figueredo"
LABEL email "cmilanf@hispamsx.org"
LABEL version "1.0"
LABEL contrib1 "tetrinetx - http://tetrinetx.sourceforge.net"
LABEL contrib2 "tetristats - https://github.com/fcambus/tetristats"
LABEL url "https://calnus.com"
LABEL twitter "@cmilanf"
LABEL usage "docker run -d -p 31457:31457 -p 31458:31458 -p 80:80 -h myhostname.domain.com -e OP_PASSWORD=mypassword -e SPEC_PASSWORD=mypassword --name tetrix cmilanf/tetrinetx"
LABEL thanksto "Beatriz Sebastián Peña"

RUN apk update \
 && apk add --no-cache nano nginx busybox nano openssl \
 && mkdir /opt \
 && mkdir -p /run/nginx \
 && adduser -D -u 1000 -g 'www' www \
 && mkdir /www \
 && chown -R www:www /var/lib/nginx \
 && chown -R www:www /www \
 && mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf.orig \
 && cp -f /var/lib/nginx/html/index.html /www

COPY nginx.conf /etc/nginx

RUN wget -O /tmp/tetrinetx-1.13.16+qirc-1.40c.tar.gz http://prdownloads.sourceforge.net/tetrinetx/tetrinetx-1.13.16+qirc-1.40c.tar.gz?download \
 && tar -xzf /tmp/tetrinetx-1.13.16+qirc-1.40c.tar.gz -C /tmp \
 && mkdir /opt/tetrinetx \
 && mv -f /tmp/tetrinetx-1.13.16+qirc-1.40c/* /opt/tetrinetx

WORKDIR /opt
RUN apk update \
 && apk add --no-cache build-base m4 git \
 && git clone git://git.chiark.greenend.org.uk/~ianmdlvl/adns.git \
 && cd adns \
 && ./configure && make && make install \
 && cd /opt/tetrinetx/src \
 && ./compile.linux \
 && cd /opt \
 && git clone https://github.com/fcambus/tetristats.git \
 && cd tetristats && make && cp -f tetristats.css /www/ \
 && echo -ne '#!/bin/sh\n/opt/tetristats/tetristats -tx /opt/tetrinetx/bin/game.winlist /www/tetristats.html \n\n' > /etc/periodic/15min/tetristats \
 && chmod +x /etc/periodic/15min/tetristats \
 && apk del build-base m4 git

WORKDIR /opt/tetrinetx/bin
COPY game.motd /opt/tetrinetx/bin/
COPY game.winlist /opt/tetrinetx/bin/
COPY channels.conf /tmp
RUN cat /tmp/channels.conf >> /opt/tetrinetx/bin/game.conf \
 && /opt/tetristats/tetristats -tx /opt/tetrinetx/bin/game.winlist /www/tetristats.html

RUN sed -i "s|maxchannels=8|maxchannels=16 |g" /opt/tetrinetx/bin/game.conf \
 && sed -i "s|sd_timeout=0|sd_timeout=600 |g" /opt/tetrinetx/bin/game.conf

ADD tetriweb.tar.gz /www

COPY entrypoint.sh /opt
RUN chmod +x /opt/entrypoint.sh

HEALTHCHECK --interval=5m --timeout=3s \
  CMD pidof tetrix.linux

EXPOSE 31457 31458 80

ENTRYPOINT /opt/entrypoint.sh

Dividiéndolo por secciones podemos encontrar lo siguiente:

  1. FROM. Es la imagen Docker en la que nos vamos a basar. En este caso de alpine.
  2. LABEL. Etiquetas de metadatos.
  3. RUN apk update y subsiguientes. Vamos a instalar los componentes mínimos de ejecución.
  4. COPY nginx.conf /etc/nginx. Copiamos a la imagen la configuración de NGINX que tenemos preparada.
  5. RUN wget -O /tmp/tetrinetx-1.13.16+qirc-1.40c.tar.gz. Descargamos el código fuente de Tetrinetx y lo desempaquetamos.
  6. WORKDIR /opt. En este bloque preparamos todo el entorno para compilar Tetrinetx, adns (dependencia de Tetrinetx) y Tetristats. Fíjate el detalle de que al final del bloque eliminamos las herramientas de compilación para que no ocupen espacio en la imagen final.
  7. WORKDIR /opt/tetrinetx/bin. En este bloque se copian más archivos de configuración preparados a la imagen. Hacemos una ejecución de Tetristats para que cuando el contenedor arranque ya tenga una página por defecto.
  8. RUN sed -i "s|maxchannels=8|maxchannels=16 |g". Ligeritos cambios en la configuración de Tetrinetx.
  9. ADD tetriweb.tar.gz /www. Junto con el contenedor, he preparado una web base que se sirve por HTTP con instrucciones sobre el juego y descarga de los clientes.
  10. COPY entrypoint.sh /opt. Copia y preparación del ENTRYPOINT. El ENTRYPOINT no es ni más ni menos que el elemento que vamos a ejecutar al iniciarse el contenedor. Una peculiaridad de Docker es que -por diseño- un contenedor sólo se mantiene en ejecución cuando la aplicación de primer plano existe como proceso del sistema. Esto quiere decir que si la aplicación que vamos a ejecutar estar preparada para ejecutarse en background -como ocurre con Tetrinetx- el contenedor finalizará su ejecución inmediatamente. Para solventarlo, hacemos un script que realice varias comprobaciones en el arranque del contenedor y se mantenga en bucle infinito. Si tienes curiosidad por ver ese archivo, puedes examinarlo en su entrada de Github.
  11. HEALTHCHECK --interval=5m --timeout=3s. Docker lleva integrado un sencillo sistema para que los contenedores puedan informar no sólo de si se ejecutan correctamente, sino también del estado de salud de las aplicaciones. Aquí vamos a considerar que mientras exista el proceso tetrix.linux nuestro contenedor esta sano.
  12. EXPOSE 31457 31458 80 puertos TCP que vamos a exponer al host para que se mapeen como el administrador estime conveniente.
  13. ENTRYPOINT /opt/entrypoint.sh Ahora sí, el ENTRYPOINT, ya explicado en 10.

Con nuestro Dockerfile y los archivos de apoyo que están en Github podemos construir nuestra imagen Docker con un comando tan sencillo como:

~# docker build -t tetrinetx .

¡Buen momento para el café!

Ejecución del contenedor

docker run -p 31457:31457 -p 31458:31458 -p 80:80 -d -h myhostname.com -e OP_PASSWORD=pass4word -e SPEC_PASSWORD=pass4word --name tetrix cmilanf/tetrinetx
  • -p 31457:31457 -p 31458:31458 -p 80:80. Exposición de puertos. Por defecto seguirá lo que hayamos marcado en EXPOSE, pero podemos poner nuestra propia configuración si así lo deseamos.
  • -d. Daemonize, ejecutar el contenedor como servicio del sistema.
  • -h myhostname.com. El FQDN del contenedor.
  • -e OP_PASSWORD=pass4word. Contraseña del administrador del servidor.
  • -e SPEC_PASSWORD=pass4word. Contraseña para el servidor de espectadores.
  • --name tetrix. El nombre del contenedor. Puede ser el que queramos.
  • cmilanf/tetrinetx. Repositorio y nombre de la imagen que estamos desplegando.

Docker Hub

Docker Hub es el repositorio oficial de imágenes Docker, en el cual encontramos una gran variedad de software preparado para usar como contenedor. En muchas ocasiones, las empresas o grupos de desarrollo tienen su repositorio aquí y publican versión Docker de sus sistemas; mientras que otras veces es la comunidad quien realiza una adaptación a contenedor de un software determinado -teniendo en cuenta que esté licenciado a ello, algo que no es problema con el código abierto-

La imagen de Tetrinetx aquí explicada está publicada en Docker Hub y puede consultarse desde https://hub.docker.com/r/cmilanf/tetrinetx/.

¡Sigue las instrucciones de la web para instalarla!

tetriweb.tar.gz... un feeling especial...

Como he explicado, la imagen Docker lleva integrado un servidor web NGINX con una web HTML pre-cocinada que muestra instrucciones de juego así como descarga del cliente gratuito. Sin embargo quiero hacer un mención especial porque el aspecto de la web es el siguiente.

Efectivamente, tiene el look de una aplicación DOS con colores EGA de principios de los 90. Seguro que os recuerda al MS-DOS EDIT.

Está hecha con un theme de Bootstrap llamado Bootstrap/386. ¡Es sencillo y trivial hacer una web con este feeling usando este theme.

¿Quieres verlo en directo? Dirígete al servidor oficial de TetriNET de la Global Azure Bootcamp - Madrid

Espero que hayáis disfrutado mucho del artículo. ¡Deja un comentario si tienes alguna pregunta o sugerencia!

Happy Docker!