Siguiendo con la serie practica en Docker hoy vamos a darle una vuelta a Mysql sobre Docker. Veremos como nos permite muchísima flexibilidad, por ejemplo convivir varias bases de datos en la misma máquina. Esto es ideal por ejemplo para separar los entornos de PRE y PRO. En el anterior articulo de Docker ya dimos unas pinceladas de la instalación de Docker, asi que continuaremos a partir de ahí.
Para empezar debemos saber que Docker dispone de una plataforma con miles de imagenes disponibles para ser descargadas, llamada Docker hub.
Si entramos en docker hub y hacemos una busqueda nos presentará un listado de imagenes de Mysql, vamos con la primera que es la oficial. Si entramos en la descripción nos indica incluso como ejecutar la imagen. Como ya indicamos anteriormente, si hacemos un docker pull nos bajará la imagen, pero si hacemos el comando que nos propone, un docker run, al no tenerla disponible se la bajará y la pondrá en funcionamiento.
docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
Si repasamos con detenemiento el comando veremos que nos pide un nombre para nuestro contenedor, unas variables de entorno, en este caso el password, esa -d de detach (que veremos despues), el nombre de la imagen y el tag de la versión. Si omitimos ésta se bajara la 'latest' por defecto o podemos pedir una version determinada. Esto es crucial a la hora de sincronizar equipos de desarrollo, es primordial que todos trabajemos con la misma versión, y así nos evitaremos el "pues en mi máquina compila" ;^)
Vamos a lanzar el comando run e iremos completando las diferentes opciones:
daniel@homeserver:~$ docker run --name=test1 mysql
Unable to find image 'mysql:latest' locally
latest: Pulling from library/mysql
a330b6cecb98: Pull complete
9c8f656c32b8: Pull complete
88e473c3f553: Pull complete
062463ea5d2f: Pull complete
daf7e3bdf4b6: Pull complete
1839c0b7aac9: Pull complete
cf0a0cfee6d0: Pull complete
1b42041bb11e: Pull complete
10459d86c7e6: Pull complete
b7199599d5f9: Pull complete
1d6f51e17d45: Pull complete
50e0789bacad: Pull complete
Digest: sha256:99e0989e7e3797cfbdb8d51a19d32c8d286dd8862794d01a547651a896bcf00c
Status: Downloaded newer image for mysql:latest
2021-09-18 12:23:16+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.26-1debian10 started.
2021-09-18 12:23:16+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
2021-09-18 12:23:16+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.26-1debian10 started.
2021-09-18 12:23:16+00:00 [ERROR] [Entrypoint]: Database is uninitialized and password option is not specified
You need to specify one of the following:
- MYSQL_ROOT_PASSWORD
- MYSQL_ALLOW_EMPTY_PASSWORD
- MYSQL_RANDOM_ROOT_PASSWORD
Al no indicar ningun tag, se descargará la version latest, en este caso ha sido la 8.0.26. Primero buscará en mi entorno local para ver si existe alguna imagen, y como no hay, se la baja. Después, efectivamente nos pide especificar como vamos a abordar el tema de las contraseñas, elegiremos la primera.
Ahora, si hacemos un docker ps para ver nuestro flamante contenedor corriendo, veremos que, oh!, no hay nada corriendo.
daniel@homeserver:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Al hacer un docker ps -a para que los presente todos, corriendo y detenidos, ahora sí que encontramos el contenedor, pero wait!, nos figura como exited y lo que nos interesa es que esté funcionando como un servicio.
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b0887a971b09 mysql "docker-entrypoint.s…" 11 seconds ago Exited (1) 11 seconds ago test1
Perfecto lo que vamos a hacer es ponerle la contraseña y ver qué pasa:
daniel@homeserver:~$ docker run --name=test1 --env="MYSQL_ROOT_PASSWORD=my-secret-pw" mysql:latest
docker: Error response from daemon: Conflict. The container name "/test1" is already in use by container "48406a6ea618fcf7b6701ce8287d6c61e657050a9559262fe3e886600afd0e04". You have to remove (or rename) that container to be able to reuse that name.
See 'docker run --help'.
Ok, se ha dado cuenta de que tenemos el contenedor test1 de antes, asi que lo borraremos primero, con docker rm.
daniel@homeserver:~$ docker rm test1
test1
daniel@homeserver:~$ docker run --name=test1 --env="MYSQL_ROOT_PASSWORD=my-secret-pw" mysql:latest
2021-09-18 13:10:00+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.26-1debian10 started.
2021-09-18 13:10:00+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
2021-09-18 13:10:00+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.26-1debian10 started.
2021-09-18 13:10:00+00:00 [Note] [Entrypoint]: Initializing database files
2021-09-18T13:10:00.940384Z 0 [System] [MY-013169] [Server] /usr/sbin/mysqld (mysqld 8.0.26) initializing of server in progress as process 44
2021-09-18T13:10:00.949051Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started.
2021-09-18T13:10:02.043743Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended.
2021-09-18T13:10:04.311436Z 0 [Warning] [MY-013746] [Server] A deprecated TLS version TLSv1 is enabled for channel mysql_main
2021-09-18T13:10:04.311860Z 0 [Warning] [MY-013746] [Server] A deprecated TLS version TLSv1.1 is enabled for channel mysql_main
2021-09-18T13:10:04.363191Z 6 [Warning] [MY-010453] [Server] root@localhost is created with an empty password ! Please consider switching off the --initialize-insecure option.
Parece que ha arrancado, pero ahora tenemos la pega de que estamos "encerrados" en la consola dentro del contenedor, está corriendo en atach mode. De ahí la necesidad del modo --detach, para que corra en modo background, o detached. Ok, lo paramos, docker stop, lo borramos de nuevo y lo creamos en modo detach. Para pararlo lo podemos hacer por su nombre o por su id. Abrimos otra consola y hacemos:
daniel@homeserver:~$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
901a6670d68d mysql:latest "docker-entrypoint.s…" 4 minutes ago Up 4 minutes 3306/tcp, 33060/tcp test1
daniel@homeserver:~$ docker stop 901a6670d68d
901a6670d68d
daniel@homeserver:~$ docker rm 901a6670d68d
901a6670d68d
Ahora volvemos a ejecutar:
daniel@homeserver:~$ docker run --name=test1 --detach --env="MYSQL_ROOT_PASSWORD=my-secret-pw" --publish 6666:3306 mysql:latest
2e2fa63b37a7ce5b708b1c51c427e357169dc4cd10b982ebc5caa851f71de517
daniel@homeserver:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a8da71af3c12 mysql:latest "docker-entrypoint.s…" 6 minutes ago Up 6 minutes 33060/tcp, 0.0.0.0:6666->3306/tcp test1
Como ahora como nuestro contenedor está corriendo en modo background nos hemos perdido la salida, pero se puede consultar facilmente con:
daniel@homeserver:~$ docker logs test1
2021-09-18 13:19:24+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.26-1debian10 started.
2021-09-18 13:19:25+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
2021-09-18 13:19:25+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.26-1debian10 started.
2021-09-18 13:19:25+00:00 [Note] [Entrypoint]: Initializing database files
[...]
Otro detalle es que he añadido una cofiguración adicional, por si quiero exponer un puerto especifico para el contenedor fuera del host, con lo que el puerto externo será el 6666. Perfecto.
Ahora vamos a proceder a conectarnos al contenedor, pero antes debemos averiguar cual es la ip de nuestro contenedor, para ello usaremos docker ispect.
daniel@homeserver:~$ docker inspect test1 | grep IPAddress
"SecondaryIPAddresses": null,
"IPAddress": "172.17.0.2",
"IPAddress": "172.17.0.2",
Excelente, ahí tenemos la IP, la 172.17.0.2. Ya solo nos falta comprobar si tenemos el cliente de mysql, en caso contrario, hacemos un "apt-get install mysql-client" y ejecutamos:
daniel@homeserver:~$ mysql -uroot -pmy-secret-pw -h 172.17.0.2 -P 3306
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 9
Server version: 8.0.26 MySQL Community Server - GPL
Copyright (c) 2000, 2021, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
Ya tenemos el contenedor funcionando!! Ahora vamos a proceder a mapear la configuración con una carpeta local del host. Esto nos puede valer para tener diferentes configuraciones y entornos. Entremos en el contenedor y veamos lo que hay, para ello usaremos docker exec en modo interactivo de la bash del contenedor.
daniel@homeserver:~$ docker exec -it test1 bash
root@a8da71af3c12:/# cd /etc
root@a8da71af3c12:/etc# ls
X11 bindresvport.blacklist deluser.conf group host.conf issue ld.so.conf.d login.defs motd os-release perl rc2.d rcS.d selinux ssl timezone
adduser.conf cron.daily dpkg group- hostname issue.net ldap logrotate.d mtab pam.conf profile rc3.d resolv.conf shadow subgid update-motd.d
alternatives debconf.conf environment gshadow hosts kernel libaudit.conf machine-id mysql pam.d profile.d rc4.d rmt shadow- subuid xattr.conf
apt debian_version fstab gshadow- init.d ld.so.cache localtime mecabrc nsswitch.conf passwd rc0.d rc5.d securetty shells systemd
bash.bashrc default gai.conf gss inputrc ld.so.conf logcheck mke2fs.conf opt passwd- rc1.d rc6.d security skel terminfo
root@a8da71af3c12:/etc# cd mysql/
root@a8da71af3c12:/etc/mysql# ls
conf.d my.cnf my.cnf.fallback
Bien, pues vamos a crear una carpeta local en el host, donde se guardará la configuración. Imaginemos que queremos setear un parametro, como el número máximo de conexiones, a 256.
mkdir -p /root/mysql-container/test1/conf.d/
vim /root/mysql-container/test1/conf.d/mytest1.cnf
y dentro escribimos:
[mysqld]
max_connections=256
Con esto ha quedado modificado la configuración, paramos el contenedor, lo borramos y lo re-arrancamos con un nuevo parámetro volumen, donde mapeamos la carpeta local con la del contenedor:
daniel@homeserver:~$ docker run --name=test1 --detach --env="MYSQL_ROOT_PASSWORD=my-secret-pw" --publish 6666:3306 --volume=/root/mysql-container/test1/conf.d/:/etc/mysql/conf.d mysql:latest
f029fee67584c849c047bb2629162ed2e46c36c3da2d3149eef28f339b85f43f
daniel@homeserver:~$ mysql -uroot -pmy-secret-pw -h 172.17.0.2 -P 3306
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.26 MySQL Community Server - GPL
Copyright (c) 2000, 2021, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respectiveowners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> show global variables like "max_connections";
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| max_connections | 256 |
+-----------------+-------+
1 row in set (0,00 sec)
Perfecto, la query de 'show global' nos indica que efectivamente estamos usando nuestro nuevo archivo de configuración. Ahora gestionemos el almacenamiento del container ubicándolo en el host. Igual que antes, creamos una carpeta y mapeamos al volumen /var/lib/mysql:
root@homeserver:~/mysql-container# mkdir storage
Nuevamente paramos el contenedor, lo eliminamos y lo arrancamos con la nueva configuración:
root@homeserver:~/mysql-container/test1/storage# docker run --name=test1 --detach --env="MYSQL_ROOT_PASSWORD=my-secret-pw" --publish 6666:3306 --volume=/root/mysql-container/test1/conf.d/:/etc/mysql/conf.d --volume=/root/mysql-container/test1/storage/:/var/lib/mysql mysql:latest
Un vistazo a la carpeta que hemos creado con el storage de nuestra instancia de Mysql y vemos que han aparecido los archivos de Mysql:
root@homeserver:~/mysql-container/test1/storage# ls
auto.cnf ca.pem client-key.pem '#ib_16384_1.dblwr' ib_logfile0 ibtmp1 mysql performance_schema public_key.pem server-key.pem undo_001
ca-key.pem client-cert.pem '#ib_16384_0.dblwr' ibdata1 ib_logfile1 '#innodb_temp' mysql.ibd private_key.pem server-cert.pem sys undo_002
Hay que hacer hincapié en que la destrucción o la detención del contenedor no eliminará los volumenes y los datos permanecerán intactos. Por otro lado existen restricciones con el uso de estos carpetas de datos. Más de dos instancias de Mysql NO podrán acceder a los datos, con lo cual el escalado horizontal tendrá que ser resuelto conotras técncias, como replicación.
Espero que os haya sido de ayuda, un saludo, hasta pronto.