Proyectos IoT con Raspeberry Pi en contenedores Docker

DockerARM.png

Con anterioridad hemos publicado varias entradas dedicadas a proyectos con Raspberry Pi. Dependiendo del proyecto en si y la tecnología que uses, normalmente tienes que instalar ciertas dependencias y/o servicios. Y si por el motivo que sea necesitas tener diferentes versiones de dichas tecnologías, por ejemplo distintas versiones de ruby, python, etc aquello se puede convertir en un pequeño infierno. Para evitar este posible lío y poder fácilmente instalar tus proyectos de forma automática y sencilla, Docker es un perfecto candidato.

También hemos hablado sobre Docker en el pasado y mi afán por familiarizarme con esta tecnología, el uso de contenedores Docker en mi proyectos IoT era algo que tenía pendiente.

En esta entrada voy a describir los pasos que yo personalmente he seguido para hacer uso de contenedores Docker en RPi.

En mi caso he usado un Raspberry Pi 3 y la imagen oficial Raspbian Jessie Lite. Para la instalación de la misma puedes seguir los pasos descritos en la web oficial.

Una vez tienes tu RPi instalado y conectado a internet, ya sea por red inalámbrica o por cable. Lo primero que haremos será actualizar el sistema:

sudo apt-get update sudo apt-get upgrade

Después de haber hecho esto, lo que haremos será instalar Docker. Recuerda que RPi lleva un procesador ARM y la versión de Docker disponible en los repositorios oficiales de Raspbian es bastante antigua, considerando que Docker, actualmente, saca versiones nuevas con bastante frecuencia.

Los chic@s de hypriot.com (fuente excepcional sobre Docker y RPi) mantienen un repositorio en packagecloud.io con paquetes de Docker engine compilados para ARM, recordemos, que es la arquitectura del procesador que llevan los RPis. Lo primero será instalar  una dependencia de Docker, luego añadir el repositorio de hypriot y finalmente la instalación y activación de Docker:

sudo apt-get install -y apt-transport-https wget -q https://packagecloud.io/gpg.key -O - | sudo apt-key add - echo ‘deb https://packagecloud.io/Hypriot/Schatzkiste/debian/ jessie main’ | sudo tee /etc/apt/sources.list.d/hypriot.list sudo apt-get update sudo apt-get install -y docker-engine sudo systemctl enable docker

Esta entrada está basada en Raspbian Jessie, pero si usas la distribución basada en wheezy, necesitarás cambiar la tercera línea:

echo ‘deb https://packagecloud.io/Hypriot/Schatzkiste/debian/ wheezy main’ | sudo tee /etc/apt/sources.list.d/hypriot.list

Este proceso te llevará un rato y si todo ha ido bien, puedes comprobar que Docker está corriendo con el comando docker version:

docker-version.png

Lo siguiente será crear nuestro contenedor. Para ello existe una imagen basada en Raspbian.

Yo he creado mi propia imagen, la cual puedes descargar desde hub.docker.com (opcional):

sudo docker pull tuxotron/rpi-python-gpio

Ésta, como decía está basada en Raspbian y además lleva Python (2.7), WiringPi2 (librerías para interactuar con la interfaz GPIO) y el módulo para python RPi.GPIO.

El fichero Dockerfile tiene está pinta:

Dockerfile.png

En realidad no necesitas descargarte dicha imagen (docker pull tuxotron/rpi-python-gpio) y puedes ejecutar el contenedor directamente desde Docker hub con el siguiente comando (la primera vez tarda un buen rato):

sudo docker run –rm -ti –cap-add SYS_RAWIO –device /dev/mem tuxotron/rpi-python-gpio

Dicho comando, si has usado Docker anteriormente quizás veas un par de parámetros que no son comunes. De todas formas vayamos por partes y veamos cual es la función de cada uno de los parámetros.

run: crea y lanza un contenedor. –rm: este parámetro es perfecto para hacer pruebas. Le indica a Docker que cuando terminemos con el contenedor, lo borre. Si no hacemos esto, los contenedores se quedarán almacenados en disco y por lo tanto ocupando espacio. Siempre los puedes borrar manualmente.

tuxotron/rpi-python-gpio: nombre de la imagen. Docker busca la imagen localmente primero, si no la tienes descargada, la busca por defecto en hub.docker.com

En condiciones normales, estos son los comandos que necesitarías para correr algún servicio en tu RPi dentro de un contenedor. Probablemente añadirías algún puerto y posiblemente algún volumen.

Pero como puedes observar el comando contiene dos parámetros más:

–device /dev/mem y –cap-add SYS_RAWIO

La razón por la que necesitamos estos dos parámetros es por el acceso al interfaz GPIO (si tu contenedor no hace uso del GPIO, puedes ignorar dichos parámetros). Recuerda que un contenedor Docker corre dentro del host de forma aislada y por defecto no tiene acceso a los recursos del éste. El acceso al interfaz GPIO se hace accediendo directamente a la memoria, de ahí que cuando necesitas acceder a este interfaz necesites privilegios de root. Para permitir que el contenedor tenga acceso a la memoria del host, necesitamos especificarlo de forma explícita con:

–device /dev/mem

Además de esto, Docker provee de granularidad fina de permisos, en la que podemos especificar que tipo de operaciones queremos permitir y/o cuales no. En nuestro caso sólo necesitamos SYS_RAWIO (acceso de entrada/salida a los puertos):

–cap-add SYS_RAWIO

También podemos hacer esto con el parámetro –privileged, pero con éste prácticamente damos carta blanca al contenedor para acceder a los recursos del host, así que por motivos de seguridad es mejor sólo asignar los permisos mínimos.

Si todo fue bien durante la ejecución del contenedor, deberías estar en una sesión de bash dentro del mismo. Para comprobar que el acceso al interfaz GPIO funciona propiamente podemos lanzar el comando: gpio readall y deberías ver algo parecido a esto:

gpio_readall.png

Con esto tienes un contenedor con Raspbian Jessie, Python y acceso al interfaz GPIO.

En futuras entradas publicaré algún ejemplo práctico usando como base la información mostrada en esta entrada.

In the meantime happy hacking!