Instalación de software en contenedores
Los recursos que haréis correr en la máquina virtual son:
- Node-RED, un gestor de comunicaciones con la planta y otras aplicaciones en nube, que permite además ejecutar programas (en la primera parte del proyecto, para comprobar su funcionamiento, NodeRed contendrá un generador de señales sinusoidales a cierta frecuencia y amplitud que emulará la señal capturada).
- Grafana, un interfaz gráfico de baja complejidad que permite presentar datos en una web en forma de gráficos, mapas, etc. Es fácilmente configurable para trabajar con InfluxDB.
- InfluxDB, un servidor de base de datos en el que almacenaremos data.
En la solución tecnológica que vais a implementar instalaréis cada software en un contenedor aislado (container), como podéis ver en la siguiente captura:

Docker
Para crear los contenedores mencionados anteriormente utilizaremos la tecnologia de Docker. Un contenedor Docker es un formato que empaqueta todo el código y las dependencias de una aplicación en un formato estándar que permite su ejecución rápida y fiable en entornos informáticos. Este es ejecutable, independiente y ligero que integra todo lo necesario para ejecutar una aplicación, incluidas bibliotecas, herramientas del sistema, código y tiempo de ejecución. Docker es también una plataforma de software que permite a los desarrolladores crear, probar e implementar aplicaciones en contenedores de forma rápida. Definiremos algunos términos de docker: - Docker Registry, lugar para almacenar y descargar imágenes. Registry es una aplicación de servidor escalable y sin estado que almacena y distribuye imágenes de Docker. Docker Hub es un Docker Registry alojado y administrado por Docker. Este tiene más de 100 000 imágenes de contenedores de proveedores de software, proyectos de código abierto y de la comunidad. Además contiene software y aplicaciones de repositorios oficiales. A continuación se adjunta el link para buscar imágenes de Docker: https://hub.docker.com/search?q=

- Contenedor, a diferencia de una máquina virtual que proporciona virtualización de hardware, un contenedor proporciona virtualización ligera a nivel de sistema operativo mediante la abstracción del "espacio del usuario". Los contenedores comparten el núcleo del sistema host con otros contenedores. Un contenedor, que se ejecuta en el sistema operativo host, es una unidad de software estándar que empaqueta código y todas sus dependencias, para que las aplicaciones se puedan ejecutar de forma rápida y fiable de un entorno a otro. Los contenedores no son persistentes y se activan desde imágenes.

- Imágenes de Docker, colección de software que se ejecutará como un contenedor que incluye un conjunto de instrucciones para crear un contenedor que se pueda ejecutar en la plataforma Docker. Las imágenes no son modificables, de modo que para realizar cambios en una imagen es preciso crear otra nueva.
Dicho esto podemos concluir que una imagen de Docker contiene todo lo que necesitas para ejecutar tu software y un contenedor de Docker es una instancia en ejecución de una imagen de Docker.
Usar Docker tiene dos grandes ventajas que cambian la forma en que creamos, enviamos y ejecutamos software:
- Mejora el proceso para obtener aplicaciones de manera fiable desde el desarrollo hasta la producción.
- Proporciona un formato de imagen estándar para pasar del entorno local a la nube.
Instalación de Docker
Con estos conceptos básicos y una vez conectados a vuestra máquina virtual mediante SSH, deberéis ejecutar los comandos que se presentan en esta sección para descargar e instalar Docker. Los comandos que se encuentran a continuación son los mismos que se aportan en la documentación oficial de Docker, que podéis encontrar en el siguiente enlace:
https://docs.docker.com/engine/install/ubuntu/
NOTA: si disponéis de un procesador AMR consultad la documentación oficial, https://docs.docker.com/engine/install/ubuntu/, ya que el último comando será diferente en vuestro caso)
sudo apt-get udpate
sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg \
lsb-release
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
(lsb\_release -cs) stable" | sudo tee
/etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
Podéis verificar que Docker se ha instalado correctamente ejecutando el siguiente comando, para descargar y ejecutar una imagen en un contenedor, imprimiendo al hacerlo un mensaje informativo por pantalla.
sudo docker run hello-world
El resultado es el siguiente:
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
d1725b59e92d: Pull complete
Digest: sha256:0add3ace90ecb4adbf7777e9aacf18357296e799f81cabc9fde470971e499788
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
En la línea 1 estamos ejecutando el cliente de Docker, y estamos indicando que queremos ejecutar un contenedor a partir de la imagen hello-world del registro público de Docker.
Si es la primera vez que hemos ejecutado esa imagen, nos aparecerá la línea 2, que indica que la imagen no puede ser encontrada y va a proceder a buscarla, por defecto, en el registro público. Si tenemos conexión a Internet se descargará la imagen (línea 6) y automáticamente creará un contenedor.
Tanto si se ha descargado la imagen o ya estaba descargada, el contenedor se ejecutará, obteniendo el texto de bienvenida que se ve en el cuadro anterior.
Comandos útiles
- sudo docker pull (nombre imagen), para descargar imágenes del repositorio oficial.
- sudo docker run (nombre imagen), para descargar imágenes y crear e inicializar los contenedores.
- sudo docker exec, para ejecutar un comando dentro de un contenedor que ya está corriendo.
- sudo docker images, para ver el listado de imágenes que estamos usando.
- sudo docker rmi (nombre imagen), para eliminar imágenes.
- sudo docker ps, para ver el listado de contenedores del sistema.
- sudo docker container start (nombre o id contenedor) o sudo docker start (nombre o id contenedor), para iniciar un contenedor parado.
- sudo docker container stop (nombre o id contenedor) o sudo docker stop (nombre o id contenedor), para detener un contenedor iniciado.
- sudo docker container rm (nombre o id contenedor) o *sudo docker rmi (nombre o id contenedor), para eliminar contenedores.
- sudo docker logs (nombre o id contenedor), para ver las salidas de dicho contenedor. Muy útil para detectar errores.
- sudo docker volume create (nombre del volumen), para crear una base de datos.
- sudo docker volume ls, para ver todos los volúmenes creados.
- sudo docker volume inspect (nombre del volumen), para ver los metadatos de un volumen.
- sudo docker volumen rm (nombre del volumen), para eliminar volúmenes.
- sudo docker --restart always, para que los contenedores se reinicien siempre en caso se de que cierren.
Descargar imágenes con diferentes versiones
Como ya hemos visto anteriormente, podemos descargar una imagen con el comando docker run (nombre de la imagen) y por defecto se descargará la última versión (latest). Si queremos descargar la versión de una imagen en concreto (por ejemplo por el uso de distintos sistemas operativos, arquitecturas...) debemos especificar su tag de la siguiente manera: docker run (nombre de la imagen:tag). Algunos ejemplos:
docker run hello-world:latest
docker run hello-world:linux
docker run postgres:latest
docker run postgres:9.6.24
docker run postgres:alpine
docker run postgres:alpine3.17
De esta manera podemos tener dos versiones distintas de una misma imagen corriendo en el mismo servidor ya que cada uno de estos contenedores son 100% independientes los unos de los otros.
Caso práctico
Para practicar más podemos crear otro contenedor que ejecute una aplicación a partir de una imagen ya creada. Imaginemos que queremos crear una web con WordPress. Si buscamos en el registro encontraremos una imagen llamada wordpress, con la etiqueta oficial. La recomendación es que siempre busquemos imágenes oficiales, están mantenidas y bien documentadas.
En la página encontraremos las diferentes opciones que tiene esta imagen para configurarla, aunque las veremos con más detalle más adelante.

Por ahora iniciemos la imagen como se indica:
docker run -p 8080:80 wordpress
Y comprobaremos como se inicia el contenedor:
$ docker run -p 8080:80 wordpress
Unable to find image 'wordpress:latest' locally
latest: Pulling from library/wordpress
802b00ed6f79: Pull complete
59f5a5a895f8: Pull complete
6898b2dbcfeb: Pull complete
8e0903aaa47e: Pull complete
2961af1e196a: Pull complete
71f7016f79a0: Pull complete
5e1a48e5719c: Pull complete
7ae5291984f3: Pull complete
725b65166f31: Pull complete
e90b121f9520: Pull complete
b5a272809bbd: Pull complete
f045f3ae0e2b: Pull complete
7f51c9ea2d8e: Pull complete
5aa9d0ed164a: Pull complete
8eea44e2bfc7: Pull complete
48918885026e: Pull complete
8ac3e8ada01a: Pull complete
d3da911b920f: Pull complete
94c7e0af5b20: Pull complete
e1f39ac90dec: Pull complete
Digest: sha256:7121cdf8e9f01816653a3b2d2f4fc7bfe1dab956f00db5c7e7689e5f1454029a
Status: Downloaded newer image for wordpress:latest
WordPress not found in /var/www/html - copying now...
Complete! WordPress has been successfully copied to /var/www/html
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 10.17.0.1. Set the 'ServerName' directive globally to suppress this message
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 10.17.0.1. Set the 'ServerName' directive globally to suppress this message
[DDD mmm dd hh:mm:ss.iiiiii yyyy] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.25 (Debian) PHP/7.2.10 configured -- resuming normal operations
[DDD mmm dd hh:mm:ss.iiiiii yyyy] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND'
Vemos en la línea nueva un nuevo parámetro: -p 8080:80. Por defecto, un contenedor está totalmente aislado. Pero si estamos montando un blog con WordPress vamos a necesitar acceder a él desde el navegador.
Con el parámetro -p, versión corta de --publish, podemos indicar que estamos enlazando un puerto de la máquina anfitrión con el contenedor. En este caso estamos enlazando el puerto 8080 de la máquina anfitrión con el 80 del contenedor.
Vamos a abrir la siguiente página web en nuestro navegador:
http://localhost:8080
La cual nos mostrará el asistente de instalación de WordPress, el cual no vamos a instalar porque necesitamos una base de datos que aún no tenemos. Si queremos almacenar datos (una web, una base de datos, etc.) dentro de un contenedor necesitamos una manera de almacenarlos sin perderlos.
Docker ofrece tres maneras:
- A través de volúmenes, que son objetos de Docker como las imágenes y los contenedores.
- Montando un directorio de la máquina anfitrión dentro del contenedor.
- Almacenándolo en la memoria del sistema (aunque también se perderían al reiniciar el servidor).
Lo normal es usar volúmenes, pero habrá ocasiones en que es preferible montar directamente un directorio de nuestro espacio de trabajo. Por ejemplo, para guardar los datos de una base de datos usaremos volúmenes, pero para guardar el código de una aplicación o de una página web montaremos el directorio. Crearemos un vólumen donde guardar la base de datos de la aplicación Wordpress. Para ello ejecutaremos el siguiente comando:
sudo docker volume create wordpress-db
wordpress-db
Tambien necesitamos tener una base de datos dónde almacenar las entradas, crearemos un contenedor a partir de la imagen de MariaDB -base de datos relacional de código abierto de alto rendimiento-. Vamos a crear nuestra base de datos usando este volumen.
sudo docker run -d --name wordpress-db \
--mount source=wordpress-db,target=/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=secret \
-e MYSQL_DATABASE=wordpress \
-e MYSQL_USER=manager \
-e MYSQL_PASSWORD=secret mariadb:10.3.9
El parámetro -d indica que debe ejecutarse como un proceso en segundo plano. Así no podremos pararlo por accidente con Control+C.
La imagen se descargará, si no lo estaba ya, y se iniciará nuestro contenedor de MariaDB:
Unable to find image 'mariadb:10.3.9' locally
10.3.9: Pulling from library/mariadb
124c757242f8: Pull complete
9d866f8bde2a: Pull complete
fa3f2f277e67: Pull complete
398d32b153e8: Pull complete
afde35469481: Pull complete
31f2ae82b3e3: Pull complete
3eeaf7e45ea6: Pull complete
716982328e17: Pull complete
34ce605c9036: Pull complete
4502ed9073c0: Pull complete
2afafbdf5a96: Pull complete
43d52b11dd31: Pull complete
30c7b70556f3: Pull complete
8b1b39f2f89a: Pull complete
41480b9319d7: Pull complete
Digest: sha256:b7894bd08e5752acdd41fea654cb89467c99e67b8293975bb5d787b27e66ce1a
Status: Downloaded newer image for mariadb:10.3.9
30634831d17108aa553a5774e27f398760bdbdf32debc3179843e73aa5957956
Ahora podemos comprobar que se ha creado el contenedor:
sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
30634831d171 mariadb:10.3.9 "docker-entrypoint.s…" 20 seconds ago Up 16 seconds 3306/tcp wordpress-db
Vamos a crear otra vez nuestro contenedor de WordPress, pero esta vez vamos a conectarlo con nuestra base de datos. Necesitaremos montar el directorio del contenedor donde está instalado WordPress con nuestra cuenta de usuario en la máquina anfitrión. Vamos a crear el espacio de trabajo:
mkdir -p ~/Sites/wordpress/target && cd ~/Sites/wordpress
Y dentro de este directorio arrancamos el contenedor:
sudo docker run -d --name wordpress \
--link wordpress-db:mysql \
--mount type=bind,source="$(pwd)"/target,target=/var/www/html \
-e WORDPRESS_DB_USER=manager \
-e WORDPRESS_DB_PASSWORD=secret \
-p 8080:80 \
wordpress:4.9.8
Cuando termine la ejecución, si accedemos a la dirección http://localhost:8080/, ahora sí podremos acabar el proceso de instalación de nuestro WordPress.
Instalación de Docker Compose
Docker Compose es una herramienta para definir y ejecutar soluciones multi-contenedor, que utiliza archivos YAML para configurar las distintas aplicaciones y lleva a cabo el proceso de creación y arranque de los contenedores con solo un comando (docker-compose up).
Características archivos YAML
- Sección version: (número versión), para definir la versión de la sintaxis del archivo en cuestión.
- Sección services:, los diferentes contenedores que se van a crear junto con una red que los conecta entre ellos. - image:, imagen que queremos que corra. - ports:, puerto del contenedor conectado con el puerto de la máquina virtual. - environment:, variables de entorno que nos pueden ser utiles, por ejemplo: usuario y contraseña para acceder al servicio. - volumes:, crear un directorio para hacer que los datos permanezcan.
- Sección volumes:, para hacer permanecer los datos de los contenedores creados.
A continuación, deberéis descargar e instalar Docker-compose con los siguientes comandos:
sudo curl -L
"https://github.com/docker/compose/releases/download/1.28.5/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
Podéis verificar que se ha instalado correctamente ejecutando el siguiente comando:
sudo docker-compose –version
Comandos útiles
- sudo docker-compose up, creación y arranque de los contenedores.
- sudo docker-compose ps, muestra información de los servicios que se definen en el archivo docker-compose.yaml.
- sudo docker-compose stop, para detener servicios.
- sudo docker-compose down, para borrar servicios. Esto borra los contenedores pero no los volúmenes.
- sudo docker-compose down -v, para borrar los contenedores y los volúmenes.
Caso práctico
Haremos lo mismo que el ejercicio anterior de crear una aplicación con wordpress desde docker, pero ahora directamente desde docker-compose. En el mismo directorio donde estábamos en el paso anterior (~/Sites/wordpress), vamos a crear un fichero (como si fuera un fichero de texto) llamado docker-compose.yaml con el siguiente contenido:
version: '3'
services:
db:
image: mariadb:10.3.9
volumes:
- data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=secret
- MYSQL_DATABASE=wordpress
- MYSQL_USER=manager
- MYSQL_PASSWORD=secret
web:
image: wordpress:4.9.8
depends_on:
- db
volumes:
- ./target:/var/www/html
environment:
- WORDPRESS_DB_USER=manager
- WORDPRESS_DB_PASSWORD=secret
- WORDPRESS_DB_HOST=db
ports:
- 8080:80
volumes:
data:
Ahora si ejecutamos la aplicación con el comando:
sudo docker-compose up -d
Cuando arrancamos la aplicación, Compose nos informa de los servicios que ha ido levantando:
Creating network "wordpress_default" with the default driver
Creating volume "wordpress_data" with local driver
Creating wordpress_db_1 ...
Creating wordpress_db_1 ... done
Creating wordpress_web_1 ...
Creating wordpress_web_1 ... done
Podemos ver los contenedores activos:
sudo docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a07b5d4d3982 wordpress:4.9.8 "docker.s…" 10 seconds ago Up 8 seconds 0.0.0.0:8080->80/tcp wordpress_web_1
d9204884cec5 mariadb:10.3.9 "docker.s…" 11 seconds ago Up 10 seconds 3306/tcp wordpress_db_1
Y tambien podemos ver los contenedores con Compose:
sudo docker-compose ps
Name Command State Ports
-------------------------------------------------------------------------------
wordpress_db_1 docker-entrypoint.sh mysqld Up 3306/tcp
wordpress_web_1 docker-entrypoint.sh apach ... Up 0.0.0.0:8080->80/tcp
Si accedemos a la dirección http://localhost:8080/, veremos de nuevo la instalación de WordPress.
Transferencia de archivos a la máquina virtual
Ahora deberéis descargar el directorio IIACPS_base_folder de Atenea y enviarlo a la máquina virtual que habéis creado. Esta transferencia de archivos se llevará a cabo mediante el protocolo SCP (Security Copy Protocol) que está basado en SSH.
Una vez abierto cmd o Windows Terminal en vuestro ordenador, introducir el siguiente comando:
scp -r -i <rutaclaveprivada> <rutadeorigen> usuarioVM@ipVM:
<ruta dedestino>
NOTA: La -r en el comando indica que se está enviando un directorio completo, si necesitáis enviar solo un archivo podéis utilizar el mismo comando, eliminando la -r.
En el terminal deberíais observar que se indica el progreso de la transferencia de archivos, alcanzando el 100% cuando se haya completado. Se recomienda que verifiquéis, estando conectados por SSH a la VM, que podéis localizar fácilmente el directorio IIACPS_base_folder.

Levantado de contenedores
El siguiente comando toma la información recogida en el docker-compose para generar los contenedores. En el caso que nos ocupa, el archivo docker-compose que estáis utilizando ha sido escrito para que levante un contenedor para cada software, tres en total.
sudo docker-compose up
La sesión de terminal que estáis utilizando se quedará bloqueada devolviendo información acerca de los contenedores en forma de logs, esto puede ser útil a la hora de determinar que sucede si algo no funciona como esperabais. Deberéis abrir una nueva para continuar trabajando.
Alternativamente es posible levantar los contenedores sin que la sesión se quede bloqueada y por tanto no se reciban los logs. Para ello, debéis añadir -d al comando anterior, quedando como sigue:
sudo docker-compose up -d
En cualquiera de los dos casos, para verificar que los contenedores están activos utilizad el siguiente comando, que devuelve la lista de contenedores:
sudo docker ps
En la consola deberíais visualizar una lista como la siguiente. Tened en cuenta que es posible que la primera vez que ejecutáis el comando no se hayan generado los tres contenedores. Volved a introducir el comando hasta verificar que los tres contenedores están funcionando.

En este punto, si lo deseáis, ya podéis modificar las prestaciones de la máquina virtual debido a que ya se ha instalado todo el software necesario. La opción más económica es B1ls, ver la siguiente captura.

A continuació se explica un ejemplo de como se crearía una imagen modificando el archivo .yaml.
Ejemplo creación de imagen custom de Node-RED
Para montar la imagen, deberéis situaros dentro de la carpeta IIACPS_base_folder y ejecutar el siguiente comando (NOTA: El punto después de tag está incluido en el comando ya que este indica que apunta al directorio al que estamos):
sudo docker build -t nodered:<tag> .
Observaréis al pulsar “Enter” que se inicia una secuencia de 4 pasos.

Cuando finalice la secuencia, podéis verificar que se ha creado correctamente con el siguiente comando:
sudo docker image ls

Una vez terminada, deberéis editar el archivo “docker-compose.yml” el tag de la imagen que acabáis de montar. Podéis salir del directorio de Nodered con el siguiente comando:
cd ..
Una vez en la carpeta donde se encuentra “docker-compose.yml”, podéis editar el archivo con el siguiente comando:
sudo nano docker-compose.yml
Tened cuidado de no añadir espacios o tabulaciones. Solo debéis editar la línea que se muestra en la siguiente captura modificando el tag que habéis elegido para vuestra imagen. Podréis navegar por el archivo con las flechas direccionales del teclado.

Cuando hayáis realizado la modificación, recordar que podéis guardar los cambios mediante “CTRL+O”, os pedirá que nombréis el archivo y no debéis modificar el nombre, así que será suficiente con pulsar “Enter”. Finalmente, para salir del editor de archivos nano, usad “CTRL+X”. El proceso de crear la imagen custom para Node-RED solo deberéis realizarlo una vez.