Usuario tratando de copiar un docker

En el anterior artículo hemos visto cómo instalar Navidrome y aprendido cómo publicar nuestra instalación en internet. Os recuerdo que Navidrome es un software que te permite crear una web que nos ofrece funcionalidades muy similares a Spotify. Con la instalación que hemos hecho, podemos añadir a Navidrome nuestra música y luego acceder a ella desde cualquier equipo conectado a internet, e incluso compartirla con nuestros amigos y familiares, que…

Calla, calla. No me lo recuerdes. He compartido mi biblioteca musical con mi hermana. Pero claro... a ella sólo le gusta el Regetón. No entiende mis refinados gustos musicales. Y eso que tengo en mi biblioteca musical la discografía completa ni más ni menos que de Sepultura y de Megadeath. Canela fina. 

Ehhhh… a ver… ignorando vuestras preferencias musicales, se me ocurre una solución muy simple para resolver problemas como que tenéis. En informática hay cosas que pueden ser complicadas, como limpiarle el ventilador a un portátil HP, pero eso que me dices se puede solucionar fácilmente haciendo…

¿Qué dices? ¡Si limpiar un ventilador de un portátil es sumamente sencillo!

No creo que el autor de este vídeo opine lo mismo que tú, Usuario Anónimo. mira:

Tío, a veces se te va mucho la olla. Te lo digo con todo el cariño del mundo, pero tienes que hacértelo mirar. 

Veeeenga… centrémonos. Cómo te iba diciendo, puedes resolver ese problema de forma sencilla. ¿Por qué no creas dos instalaciones simultáneas de Navidrome en el mismo servidor (Raspberry)? Una instalación tiene tu música, la otra tiene la música de tu hermana. Cada uno usa su instalación y los dos seréis felices ignorando al otro.

Pues porque si hago dos instalaciones de Navidrome a la vez ninguna de las dos va a funcionar, porque ambas usarían el mismo puerto y la misma base de datos y se matarían entre ellas, listillo. ¡Que te lo tengo que decir todo! Si al final voy a ser yo el que tenga que escribir tu blog. 

Te podría comprar en parte el argumento si lo instalases de forma nativa (sólo en parte, porque también se resolvería de forma sencilla), pero estás olvidándote de una cosa. La instalación la hemos hecho en Docker, y en Docker hacer cosas como estas es sumamente sencillo. Vamos a ver cómo podemos hacer «cosas raras» con los Dockers que tenemos instalados, como hacer un duplicado de un Docker o mover un Docker de un equipo a otro. Aunque no estéis interesados en tener este docker duplicado, os ruego hacer lo que se indica en el tutorial. Si al final no necesitáis tener este docker duplicado os daré instrucciones de cómo dejar sólo un Navidrome instalado. Recordad que el objetivo de este artículo es aprender y resulta muy interesante tener estos conceptos claros.

1- Creando dos dockers usando la misma imagen

Vamos a tomar como base el Navidrome que hemos instalado en el artículo anterior. Sabemos instalarlo, y si hacemos un sudo docker ps debería aparecernos entre otras líneas algo similar a ésto.

sudo docker ps

Vemos que está usando el puerto 4533 y está activo. Si miramos qué imágenes tenemos instaladas veremos también algo similar a ésto.

sudo docker image ls

Es decir, tenemos ahora mismo una imagen de Navidrome y un contenedor de Navidrome ejecutándose. Seguramente recordéis que todo lo relativo a este docker estaba en la ruta /home/<tu_usuario>/docker/navidrome . Ahí pusimos tanto el archivo docker-compose.yml como las carpetas que necesitaba el docker (music y data).

Creamos la estructura de carpetas

Lo que haremos será crear dentro de la carpeta «home» una carpeta a la que por ejemplo vamos a llamar «navipruebas» para desplegar una segunda instalación de Navidrome.

¿Navipruebas? ¿En serio? ¿Y no puedo llamarla "reguetondelmonton" por ejemplo?

Ehhhh… tú puedes llamarle como te plazca, Usuario Anónimo. De hecho el nombre de esa carpeta es lo de menos. Simplemente crea la carpeta con el nombre que quieras dentro del directorio «dockers«. Una vez creada haz ahí casi lo mismo que hemos hecho en el tutorial de Navidrome. Crearíamos las mismas carpetas dentro del directorio «navipruebas».

cd docker
mkdir navipruebas
cd navipruebas
mkdir data && mkdir music

Creamos el archivo de configuración de docker compose.

Ahora, con el comando sudo nano docker-compose.yml crearemos el archivo de configuración de nuestro nuevo docker. Dentro de él pondríamos casi casi el mismo Docker, pero modificando tres cosas: el puerto, y las dos últimas líneas, que ahora apuntan al nuevo directorio en lugar de al directorio «navidrome«. Lo dejaríamos así.

version: "3"
services:
  navidrome:
    image: deluan/navidrome:latest
    user: 1000:1000 # should be owner of volumes
    ports:
      - "4540:4533"
    restart: unless-stopped
    environment:
      # Optional: put your config options customization here. Examples:
      ND_SCANSCHEDULE: 1h
      ND_LOGLEVEL: info
      ND_SESSIONTIMEOUT: 24h
      ND_BASEURL: ""
    volumes:
      - $DOCKERDIR/navipruebas/data:/data
      - $DOCKERDIR/navipruebas/music:/music:ro
Vale... entiendo lo de apuntar a la carpeta nueva, pero tengo una duda. El puerto antiguo era el 4533. Ahora has puesto el 4540. ¿Por qué ese número y no otro? ¿Y por qué no cambias el del otro lado, que sigue siendo el 4533?

Muy buena pregunta. Por un lado, el puerto nuevo me lo estoy inventando. He puesto el 4540 pero podría haber puesto… ¿yo qué sé?… El 4821 por ejemplo. Puedes poner cualquier puerto relativamente cercano y que no esté siendo usado por algún otro docker. En ese sentido no hay problema.

código para ilustrar copia de docker.

Sobre el puerto que aparece al otro lado de la misma línea, ese es el puerto que usa el docker internamente. El docker es… como un ordenador dentro de tu ordenador. No es virtualización, sino contenedorización, pero hay cosas que se recuerdan a máquinas virtuales corriendo sistemas operativos dentro de otros sistemas.

En este caso el docker que estamos creando, internamente usa el puerto 4533 para hacer la web accesible al exterior, pero al docker compose le estamos diciendo que «mapee» ese puerto. Aunque el puerto en el contenedor sea el 4533, en el host realmente el puerto expuesto sea el 4540. El contenedor está configurado para usar ese puerto y no otro. Si cambias el puerto interno por otro puerto posiblemente el despliegue se realice correctamente pero no vas a llegar a la web, porque internamente ese docker está configurado para usar ese puerto.

Recuerda que cada uno de los dockers vive en su propio contenedor, por lo que no hay ningún problema en que cada uno de los contenedores use el puerto 4533 a nivel interno. El problema podría venir si esos dos contenedores usan el mismo puerto en el host. Aquí sí que haríamos una liada. Pero resolverlo ya veis que es muy sencillo. Sólo hay que poner un puerto aleatorio expuesto en el host y distinto para cada contenedor y ya quedaría resuelto el problema.

¿Y no cambias el nombre del servicio?

Me estás preocupando. Otra buena pregunta. Efectivamente, el docker-compose.yml tiene estas dos líneas.

services:
  navidrome:

Podría cambiar el nombre del servicio para que no hubiera confusión de ningún tipo y dejarlo así.

services:
  navipruebas:

Pero en cualquier caso funcionaría igual y no os quiero liar demasiado la cabeza. No tenéis por qué cambiar esa línea si no queréis.

Creamos el archivo de variables de entorno y le damos seguridad

Respecto al tutorial anterior, nos quedaría crear el archivo «.env«. Le pondríamos el mismo contenido que en el artículo anterior (DOCKERDIR=/home/<tu_usuario>/docker). Y por supuesto le daríamos seguridad con el comando sudo chmod 600 .env. con esto el docker quedaría listo para ser desplegado.

Desplegamos el docker

¿cómo lo desplegaríamos? Pues con el comando de siempre, pero indicando el nuevo nombre del contenedor (podéis poner navipruebas, reguetondelmonton o lo que queráis). El comando quedaría algo así.

sudo docker-compose -p "navipruebas" up -d

Ahora ya sabéis por qué me empeño en todos los artículos en ponerle un nombre a los dockers en lugar de dejar que se ponga el nombre por defecto. Así no nos despistamos si hacemos alguna prueba de estas. El despliegue sería mucho más corto que la anterior vez que desplegamos navidrome y quedaría tal que así.

Desplegando el contenedor

Una vez que hayamos hecho esto, podríamos entrar en la siguiente dirección para comprobar que todo funciona bien.

http://<ip.de.la.raspberry>:4540

Podéis comprobar también que si accedéis por la dirección local del antiguo navidrome (http://<ip.de.la.raspberry>:4533) está la web que habías hecho anteriormente y con toda tu música, pero en la web nueva esa música no está (porque aún no has empezado a usar ese docker). Con esto has conseguido hacer otra web idéntica pero vacía de contenido. Métele música distinta a la carpeta Music y ya estarías usando ese segundo contenedor con un contenido distinto al primero.

Pero yo al primer navidrome accedo a través de internet con una dirección de mi dominio.

Pues eso también puedes hacerlo en la nueva web. Simplemente crea otro proxy diferente con otra dirección de dominio para acceder a ella desde internet (algo como mihermana.midominio.com) y así tendrás un navidrome tuyo y otro que puedes dárselo a tu hermana.

Revisión de lo que acabamos de hacer.

En este momento ambos contenedores ya están disponibles para usar a través de internet de forma simultánea.

Déjame coger un poco de aire, que estoy intentando asimilar todo esto. Me parece alucinante que se pueda hacer lo que acabamos de ver. ¿Cómo es posible que todo siga funcionando como si nada? ¿ahora voy a tener dos imágenes? ¿tal vez un único contenedor? ¿Seguro que esto no es magia o algo así?

Ehmmm… sí, es magia, por supuesto. Mira: si después de hacer lo que hemos hecho, ejecutamos el comando «sudo docker ps«, obtendremos (entre otras líneas) estas dos:

Comprobación contenedores

Y sí… sé que se ve muy pequeño todo, pero os explico lo que aparece. Podemos ver que entre los contenedores que tenemos activos, hay dos contenedores diferentes con estos nombres.

Nombres de contenedores

Así que sí. Efectivamente tenemos dos contenedores independientes, no uno. Cada uno de ellos consume recursos de la Raspberry (Pero ya os digo que lo que consume cada uno es algo ínfimo). Es importante saber eso: que tenemos dos contenedores diferentes y que cada uno de ellos tiene volúmenes configurados en sitios diferentes (diferente base de datos y diferentes archivos de música). Por tanto son dos instalaciones completamente independientes.

En «STATUS» podemos ver que los dos contenedores están corriendo sin problemas (healthy). Pero fijaos en los puertos.

Puertos

Uno de ellos expone al host el puerto 4540 y el otro el 4533, aunque internamente, dentro del contenedor, ambos usen el 4533 (pero por separado, como si fueran dos ordenadores independientes que no tienen nada que ver el uno con el otro).

Si vemos las imágenes que tenemos en el sistema mediante el comando «sudo docker image ls«, obtendremos esto otro.

sudo docker image ls

Si os fijáis, hay una única imagen del navidrome. Esa imagen (que es la que ha realizado el desarrollador) la hemos usado para desplegar por separado cada uno de los dos contenedores. Así que si tengo 30 contenedores diferentes de Navidrome, los 30 usarán la misma imagen para su despliegue.

Borrado de este docker de pruebas

Como prueba nos ha bastado, así que vamos a eliminar la web de navipruebas (Salvo que la queramos para tener una segunda biblioteca). Para eliminar este docker sólo tenemos que hacer un sudo docker ps y ver qué «Container ID» tiene el contenedor que queremos borrar (en el ejemplo de arriba sería el 8666ad784219). Para desmantelar por completo ese contenedor primero lo debemos parar con el comando…

sudo docker stop 8666ad784219

… y luego lo debemos eliminar con este otro comando:

sudo docker rm 8666ad784219

Tened en cuenta que ese identificador es diferente para cada instalación. Poned el identificador que os indique el comando sudo docker ps.

El contenedor habrá desaparecido cuando hagamos otro sudo docker ps y además habremos recuperado todos los recursos que ese docker estaba consumiendo. Sin embargo el contenedor original de Navidrome seguirá ejecutándose en el equipo y la imagen también la tendremos instalada. Salvo por la carpeta «navipruebas«, que sigue dentro del directorio «dockers«, el docker habrá pasado a mejor vida. Y por supuesto la carpeta navipruebas la podríamos eliminar sin ningún problema, claro. La eliminaríamos con estos comandos:

cd ..
sudo rm -R navipruebas

El equipo lo habríamos dejado como antes de hacer este experimiento.

2- Haciendo una copia exacta de un docker.

Ya que estamos en el ajo, podría interesarnos en algún momento mover una instalación de docker a otro equipo, hacerle una copia de seguridad, o incluso duplicar un docker en funcionamiento (con todos sus datos). En este ejemplo sería coger el Navidrome del Usuario anónimo con toda su música y hacer otro docker exactamente igual, con su misma música, usuarios, etc… Dicho de otra forma, duplicar tanto el contenedor como los volúmenes del docker. Pensad primero si se os ocurre alguna forma de hacerlo, y luego seguid leyendo.

Paramos el contenedor para que no haya ninguna inconsistencia de datos.

Antes de hacer nada, lo suyo sería detener el contenedor que vamos a copiar. Si no lo detenemos, correremos el riesgo de estar copiando datos que están a medio escribir y montar un follón de dimensiones épicas. Así que seamos seguros.

Para detener el contenedor, podemos ir a la carpeta del docker que deseamos parar. En este caso, iremos a la carpeta de Navidrome…

cd /home/<tu_usuario>/docker/navidrome

… y desde esa carpeta teclearemos el comando sudo docker-compose down.

sudo docker compose down

Esto parará este contenedor en concreto y mantendrá el resto de contenedores funcionando con normalidad.

Copiamos la estructura de carpetas del Docker.

Vamos a ir una carpeta hacia atrás en el árbol de directorios y situarnos en la carpeta docker (en la ruta /home/<tu_usuario>/docker). Lo podéis hacer con el comando cd .. (Sí… seguro que ya sabéis cómo moveros por las carpetas, pero me gusta ser muy preciso). Si habéis hecho todo bien, deberíais tener ahí varias carpetas con los dockers que hemos desplegado. Algo así.

ls -l -h -a

Vamos a hacer un duplicado de la carpeta navidrome, conservando los permisos de los archivos y carpetas originales que hay dentro. Lo haremos con el siguiente comando:

cp -r navidrome navidrome2

Levantamos el docker que habíamos detenido.

No nos podemos olvidar de volver a levantar el contenedor que paramos hace un momento. Volvemos a meternos en la carpeta de ese contenedor y lo volvemos a poner en marcha. El comando sería el mismo que el que usamos para desplegar el docker, o sea, éste.

sudo docker-compose -p "navidrome" up -d

con lo que la secuencia de comandos sería algo como ésto.

cd navidrome

Después de esto, salimos de nuevo a la carpeta de dockers con el comando cd .. .

Modificamos el docker copiado.

Ahora debo entrar en el archivo de configuración de nuestro futuro nuevo contenedor. Lo hacemos con estos comandos

cd navidrome2
sudo nano docker-compose.yml

Y ahí vamos de nuevo a cambiar el puerto y las rutas de los volúmenes. Quedaría algo así.

version: "3"
services:
  navidrome:
    image: deluan/navidrome:latest
    user: 1000:1000 # should be owner of volumes
    ports:
      - "4548:4533"
    restart: unless-stopped
    environment:
      # Optional: put your config options customization here. Examples:
      ND_SCANSCHEDULE: 1h
      ND_LOGLEVEL: info
      ND_SESSIONTIMEOUT: 24h
      ND_BASEURL: ""
    volumes:
      - $DOCKERDIR/navidrome2/data:/data
      - $DOCKERDIR/navidrome2/music:/music:ro

El resto de pasos no tenemos por qué hacerlos porque al copiar la carpeta ya tenemos todo hecho (archivo .env, permisos, etc…)

Levantamos el nuevo contenedor.

Lo próximo que deberíamos hacer es desplegar el docker, porque ya tenemos todo lo relativo a las variables de entorno configurado (las hemos copiado junto con las carpetas de datos).

Para levantar nuestro nuevo contenedor, debemos ejecutar este comando.

sudo docker-compose -p "navidrome2" up -d

Y listo: ahora tendremos una web independiente pero idéntica a la primera en la dirección…

http://<ip.de.la.raspberry>:4548

A partir de ahora podemos hacer modificaciones en una de estas webs y la otra seguirá sin modificar. Funcionarán de forma completamente independiente.

Eliminamos esta prueba

Si esto para vosotros ha sido sólo una prueba, debéis primero parar el contenedor, luego eliminarlo y por último borrar la carpeta «navidrome2«. Para saber el id de contenedor tecleamos éste comando.

sudo docker ps

Y cuando sepamos el id del contenedor «navidrome2» usaremos estos comandos para detenerlo, borrarlo y eliminar la carpeta.

Parando contenedores

3- Creando copias de seguridad de un Docker.

Me da que después de todo lo que acabamos de aprender, ya estáis suponiendo cómo voy a plantearos las copias de seguridad de un Docker. Con el esquema de instalaciones que hemos seguido hasta ahora, hacer una copia de seguridad de todos nuestros dockers resulta tan sencillo como parar los contenedores y copiar toda la carpeta «docker» a una ubicación distinta (Lo ideal sería montar una unidad USB o una carpeta de red). Voy a suponer que tenemos montada una unidad USB en la ruta /media/usb.

Detenemos todos los contenedores.

Habíamos visto cómo detener un contenedor. Pero ¿y si quiero detener todos? Tengo un truco para hacerlo. Este comando nos va a dar una simple lista de los id’s de todos los contenedores que están ejecutándose en nuestra máquina.

sudo docker ps -q
sudo docker ps -q

Podemos hacer una combinación de dos comandos. Podemos hacer un comando que coja cada una de esas líneas y detenga los contenedores que correspondan con cada línea. El comando nos quedaría algo así.

sudo docker stop $(sudo docker ps -q)

Si después de hacerlo se nos ocurre hacer un sudo docker ps, no deberíamos ver ningún contenedor activo.

parando contenedores

Copiamos todos los dockers a una ubicación diferente.

Ahora copiaremos absolutamente todo el contenido de la carpeta docker a la nueva ubicación.

rsync -av /home/<tu_usuario>/docker /media/usb

Levantamos de nuevo los dockers que habíamos apagado

Vamos a hacer un comando similar al que usamos cuando apagamos los dockers. En este caso, podríamos saber los id’s de los contenedores que están detenidos con este comando.

sudo docker ps -a -q
sudo docker ps -a -q

Con esa información, podemos encender los contenedores con el siguiente comando.

sudo docker start $(sudo docker ps -a -q)
Iniciando contenedores

Y con esto quedarían todos los dockers copiados. Podríamos llevarlos a una nueva ubicación (a otro servidor) y levantarlos de nuevo.

Estoy impresionado. ¿Y esto se puede automatizar de alguna forma?

Es tan sencillo como hacer un script siguiendo un a uno todos los pasos que hemos visto. Pero como estamos aquí para aprender, os voy a poner un script «made-in-micasa» un poco diferente y algo más complicado (para darle otro enfoque diferente. Siempre es bueno ver diferentes formas de resolver el mismo problema). Este script recorre uno por uno los directorios de los dockers y los va apagando. Luego hace la copia de seguridad y luego vuelve a ir directorio por directorio encendiendo los dockers. Sólo tendríais que poner el usuario de vuestro equipo y el directorio de destino en el script para que funcionase correctamente (para que llegue a la ruta correcta).

#!/bin/bash
#  Cambia esta línea y pon aquí el usuario de tu raspberry
USUARIO="marcos"
# Cambia esta línea y pon aquí el directorio de destino
DESTINO="/media/usb"
# Voy a detener todos los servicios
for dir in /home/$USUARIO/docker/*/; do
  (cd "$dir" && docker-compose down)
done
# Ahora voy a realizar la copia de seguridad
rsync -av /home/$USUARIO/docker "$DESTINO"
# Por último vuelvo a iniciar todos los servicios
for dir in /home/$USUARIO/docker/*/; do
  (cd "$dir" && docker-compose up -d)
done

Y bueno… lo de siempre: lo editamos con nano backup.sh, Control + O para guardar, Control + X para salir, sudo chmod +x backup.sh para hacerlo ejecutable, sudo crontab -e para entrar en el cron y ahí ponéis una automatización de algún tipo. Por ejemplo, para programarlo para que se realice la copia el lunes de cada semana a las 8 de la mañana, la programación en el crontab sería así.

0 8 * * 1 /home/<tu_usuario>/backup.sh

Y con esto ya estaría todo. Espero que con este artículo tengáis más claro el funcionamiento interno de docker. ¿Os ha parecido complicado? Estaré atento a vuestros comentarios.

Share