En las guías y tutoriales en internet siempre funciona todo sin ningún fallo. Sin embargo, lo más habitual es que encontremos problemas en los primeros intentos de poner en marcha una aplicación.

Personalmente, creo que el aprendizaje es un proceso de ensayo y error, por lo que se aprende solucionando los errores que nos encontramos.

Con esa idea en mente, también intento documentar los fallos que cometo. A continuación tienes el registro de las acciones que realicé para solucionar los problemas encontrados en el arranque de SonarQube.

Lanzando el contenedor

Lanzamos el contenedor con SonarQube conectándolo a la base de datos:

$ sudo docker run -d --name sonarqube -p 9000:9000 -p 9092:9092 \
-e SONARQUBE_JDBC_USERNAME=sonar \
-e SONARQUBE_JDBC_PASSWORD=cF68nTVgP8Nq \
-e SONARQUBE_JDBC_URL=jdbc:mysql://localhost/sonar sonarqube:7.1-alpine
9726b2c018d6151745a382ea22cc53f1a96ada557ae21b2a176313b46fb4a010
$

El contenedor se detiene después de ser lanzado.

Troubleshooting

Mientras observo los logs, lo primero que me viene a la cabeza es que no hemos definido una base de datos en MySQL para SonarQube ni en el comando para lanzar SonarQube.

Eliminamos el contenedor actual de MySQL para SonarQube:

$ sudo docker stop mysql-sonarqube && sudo docker rm mysql-sonarqube
mysql-sonarqube
mysql-sonarqube
$

Creando una base de datos para SonarQube en MySQL

Creamos la base de datos siguiendo las instrucciones de la página en Docker Hub para MySQL:

$ sudo docker run -d --name mysql-sonarqube --network backend-sonarqube \
--mount source=data-mysql-sonarqube,target=/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=J8wqnvJicbpv \
-e MYSQL_DATABASE=sonar \
-e MYSQL_USER=sonar -e MYSQL_PASSWORD=cF68nTVgP8Nq \
mysql:5.7
c12b24cbe07b4026ae7deda7e9085775fa3f794ca28778b665d36cfdf1bd9fa4
$

Al lanzar de nuevo el contenedor, seguimos obteniendo el mismo error.

Modificando la cadena de conexión

Revisando docker image of sonarqube is not running with mysql db configuration, observo dos cosas: primero, cómo añadir la propiedad useUnicode=true y segundo, que en la cadena de conexión se hace referencia a localhost y la base de datos no se encuentra en el contenedor de SonarQube.

En los logs aparece la excepción:

$ sudo docker logs sonarqube
Exception in thread "main" org.sonar.process.MessageException: JDBC URL must have the property 'useUnicode=true'
$

Corrijo la cadena de conexión indicando el nombre del contendor con MySQL (SonarQube) y la propiedad indicada en los logs:

$ sudo docker run -d --name sonarqube -p 9000:9000 -p 9092:9092 \
-e SONARQUBE_JDBC_USERNAME=sonar \
-e SONARQUBE_JDBC_PASSWORD=cF68nTVgP8Nq \
-e SONARQUBE_JDBC_URL='jdbc:mysql://mysql-sonarqube:3306/sonar?useUnicode=true&characterEncoding=utf8' \
sonarqube:7.1-alpine
2887a8817afe286966712ad202a5ed50f4b7a67eba2409c185dc70deaec7ceb9
[2]+  Exit 1 sudo docker run -d --name sonarqube -p 9000:9000 -p 9092:9092 -e SONARQUBE_JDBC_USERNAME=sonar -e SONARQUBE_JDBC_PASSWORD=cF68nTVgP8Nq -e SONARQUBE_JDBC_URL=jdbc:mysql://mysql-sonarqube:3306/sonar?useUnicode=true
$

Problemas de conexión

Revisando los logs de nuevo, destaca el mensaje java.lang.IllegalStateException: Can not connect to database. Please check connectivity and settings (see the properties prefixed by 'sonar.jdbc.').

La cadena de conexión en el comando docker run parece correcta, así que el problema debe estar en otro lugar. Analizando el problema veo que el contenedor sonarqube no está conectado a la red backend-sonarqube, por lo que no encuentra la base de datos especificada en SONARQUBE_JDBC_URL='jdbc:mysql://mysql-sonarqube:3306/...

Volvemos a intentarlo con:

$ sudo docker run -d --name sonarqube -p 9000:9000 -p 9092:9092 \
-e SONARQUBE_JDBC_USERNAME=sonar \
-e SONARQUBE_JDBC_PASSWORD=cF68nTVgP8Nq \
-e SONARQUBE_JDBC_URL='jdbc:mysql://mysql-sonarqube:3306/sonar?useUnicode=true&characterEncoding=utf8' \
--network backend-sonarqube \
sonarqube:7.1-alpine
2866c2e4c4540157da2040b03083010ce8563e8f07e3bb1ffdcf953b0ecbf849
$

El contenedor sigue sin arrancar.

Warnings en la conexión

Revisando de nuevo los logs, vemos que tenemos un par de avisos sobre propiedades que deberían usarse en la cadena de conexión y fallos en la conexión usando SSL (que no está configurado):

$ sudo docker logs sonarqube
20:00:15.435 [main] WARN org.sonar.application.config.JdbcSettings - JDBC URL is recommended to have the property 'rewriteBatchedStatements=true'
20:00:15.441 [main] WARN org.sonar.application.config.JdbcSettings - JDBC URL is recommended to have the property 'useConfigs=maxPerformance'
...
2018.04.26 20:00:32 INFO  web[][o.sonar.db.Database] Create JDBC data source for jdbc:mysql://mysql-sonarqube:3306/sonar?useUnicode=true&characterEncoding=utf8
Thu Apr 26 20:00:32 GMT 2018 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
2018.04.26 20:00:33 ERROR web[][o.s.s.p.Platform] Web server startup failed
java.lang.IllegalStateException: Can not connect to database. Please check connectivity and settings (see the properties prefixed by 'sonar.jdbc.').
...

Añadiremos las dos propiedades recomendadas a la cadena de conexión e indicamos useSSL=false para deshacernos del error de verificación del certificado SSL (que no estamos usando):

$ sudo docker run -d --name sonarqube -p 9000:9000 -p 9092:9092 \
-e SONARQUBE_JDBC_USERNAME=sonar \
-e SONARQUBE_JDBC_PASSWORD=cF68nTVgP8Nq \
-e SONARQUBE_JDBC_URL='jdbc:mysql://mysql-sonarqube:3306/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useConfigs=maxPerformance&useSSL=false' \
--network backend-sonarqube \
sonarqube:7.1-alpine

El contenedor sigue sin arrancar.

Acceso denegado

Revisando los logs, seguimos sin poder conectar con la base de datos, pero ahora es un problema de acceso denegado:

$ sudo docker logs sonarqube
...
Caused by: org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (Access denied for user 'sonar'@'172.18.0.3' (using password: YES))
...
Caused by: java.sql.SQLException: Access denied for user 'sonar'@'172.18.0.3' (using password: YES)
...

Es decir, parece que el contenedor con SonarQube sí que establece conexión con el servidor de MySQL-SonarQube, pero el usuario sonar no tiene acceso para conectarse.

Borrado del volumen de datos para MySQL

Al arrancar MySQL pasando las variables MYSQL_USER y MYSQL_PASSWORD, debería haberse asignado permisos sobre la base de datos especificada en MYSQL_DATABASE. Como en nuestro caso habíamos arrancado la base de datos MySQL usando el volumen data-mysql-sonarqube, mi hipótesis es que ya existía una base de datos y por tanto no se han dado permisos al nuevo usuario.

Podríamos haber dado permisos manualmente al usuario sobre la base de datos, pero corríamos el riesgo de que el proceso de creación del schema en la base de datos no se lanzara (porque la base de datos ya exitía). Al no tener datos en la base de datos, lo más sencillo es eliminar el volumen de datos y el contenedor de MySQL.

Para comprobar mi hipótesis, detengo y elimino el contenedor con base de datos mysql-sonarqube, así como el volumen data-mysql-sonarqube:

$ sudo docker stop mysql-sonarqube
mysql-sonarqube
$ sudo docker rm mysql-sonarqube
mysql-sonarqube
$ sudo docker volume rm data-mysql-sonarqube
data-mysql-sonarqube
$

Lo creamos todo de nuevo:

$ sudo docker volume create data-mysql-sonarqube
data-mysql-sonarqube

$ sudo docker run -d --name mysql-sonarqube --network backend-sonarqube \
--mount source=data-mysql-sonarqube,target=/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=J8wqnvJicbpv \
-e MYSQL_DATABASE=sonar \
-e MYSQL_USER=sonar -e MYSQL_PASSWORD=cF68nTVgP8Nq mysql:5.7
6966a54978df35eaafe6bf10dea7f3f4fcfdb3c5340e0e3ea5cddffd9bd02d06

$ sudo docker run -d --name sonarqube -p 9000:9000 -p 9092:9092 \
  -e SONARQUBE_JDBC_USERNAME=sonar \
  -e SONARQUBE_JDBC_PASSWORD=cF68nTVgP8Nq \
  -e SONARQUBE_JDBC_URL='jdbc:mysql://mysql-sonarqube:3306/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useConfigs=maxPerformance&useSSL=false' \
  --network backend-sonarqube \
  sonarqube:7.1-alpine
4fc7eea457db05e7ad9837ca5f4407d5023e227cb94b188c5f021eca65e39332

Después de unos segundos, al revisar los logs, vemos que esta vez sí que se está creando el schema de la base de datos para SonarQube:

$ sudo docker logs sonarqube
...
2018.04.26 20:19:04 INFO  web[][o.s.s.p.d.m.AutoDbMigration] Automatically perform DB migration on fresh install
2018.04.26 20:19:04 INFO  web[][DbMigrations] Executing DB migrations...
2018.04.26 20:19:04 INFO  web[][DbMigrations] #1 'Create initial schema'...
2018.04.26 20:19:14 INFO  web[][DbMigrations] #1 'Create initial schema': success | time=10068ms
2018.04.26 20:19:14 INFO  web[][DbMigrations] #2 'Populate initial schema'...
2018.04.26 20:19:14 INFO  web[][DbMigrations] #2 'Populate initial schema': success | time=430ms
...

Cuando finaliza el proceso:

$ sudo docker logs sonarqube
...
2018.04.26 20:20:37 INFO  ce[][o.s.ce.app.CeServer] Compute Engine is operational
2018.04.26 20:20:37 INFO  app[][o.s.a.SchedulerImpl] Process[ce] is up
2018.04.26 20:20:37 INFO  app[][o.s.a.SchedulerImpl] SonarQube is up
$

Y accediendo desde el navegador a http://$IPHOST:9000/:

SonarQube home

SonarQube home

Volumen data-sonarqube no montado en sonarqube

Con todos los problemas relacionados con la base de datos de SonarQube, no me dado cuenta de que no he montado el volumen data-sonarqube en el contenedor de sonarqube.

Docker ha creado un volumen automáticamente y lo ha montado en el contenedor; podemos ver el contenedor “sin nombre” lanzando:

$ sudo docker volume ls
DRIVER              VOLUME NAME
local               caf392a6189a23dce515817e2e02c5bc2bc05d89cd1b97da4739f0203c59192b
local               data-gogs
local               data-jenkins
local               data-mysql-gogs
local               data-mysql-sonarqube
local               data-nexus
local               data-portainer

Aunque a nivel funcional no supone ninguna diferencia; disponer de nombres identificativos para los volúmenes de datos nos ayuda a simplificar la gestión del entorno.

Vamos a lanzar un contenedor efímero que monte tanto el volumen actual caf392a6189... como el volumen data-sonarqube y copiaremos el contenido de uno a otro. A continuación, lanzaremos de nuevo el contenedor sonarqube y una vez que hayamos comprobado que funciona correctamente, eliminaremos el volumen caf392a6189....

Creamos el volumen de datos:

$ sudo docker volume create data-sonarqube
data-sonarqube

Detenemos el contenedor de SonarQube y lo eliminamos (para reutilizar el nombre):

$ sudo docker stop sonarqube && sudo docker rm sonarqube
sonarqube
sonarqube

Creamos el contenedor efímero y montamos los volúmenes:

$ sudo docker run --rm -it --mount source=caf392a6189a23dce515817e2e02c5bc2bc05d89cd1b97da4739f0203c59192b,target=/in --mount source=data-sonarqube,target=/out sonarqube:7.1-alpine /bin/sh
/opt/sonarqube # ls /in
README.txt  es5         web
/opt/sonarqube # ls /out
/opt/sonarqube # cd /in/
/in # cp -a . /out
/in # ls /out/
README.txt  es5         web
/in #

Una vez copiados los datos, salimos del contenedor usando exit.

En la versión actual de Docker (18.03.1-ce) no hay ningún comando para renombrar un volumen o para copiar su contenido a otro volumen.

Finalmente, lanzamos un nuevo contenedor para SonarQube, montando el volumen data-sonarqube:

$ sudo docker run -d --name sonarqube -p 9000:9000 -p 9092:9092 \
  --mount source=data-sonarqube,target=/opt/sonarqube/data \
  -e SONARQUBE_JDBC_USERNAME=sonar \
  -e SONARQUBE_JDBC_PASSWORD=cF68nTVgP8Nq \
  -e SONARQUBE_JDBC_URL='jdbc:mysql://mysql-sonarqube:3306/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useConfigs=maxPerformance&useSSL=false' \
  --network backend-sonarqube \
  sonarqube:7.1-alpine
443cb80ef5bdc7168c654b0bea0883cfc8e665e465f3d12fa8bb06bdb1856f14
$

Después de verificar que SonarQube arranca con normalidad, podemos eliminar el volumen “sin nombre”:

$ sudo docker volume ls
DRIVER              VOLUME NAME
local               caf392a6189a23dce515817e2e02c5bc2bc05d89cd1b97da4739f0203c59192b
local               data-gogs
local               data-jenkins
local               data-mysql-gogs
local               data-mysql-sonarqube
local               data-nexus
local               data-portainer
local               data-sonarqube
$ sudo docker volume rm caf392a6189a23dce515817e2e02c5bc2bc05d89cd1b97da4739f0203c59192b
caf392a6189a23dce515817e2e02c5bc2bc05d89cd1b97da4739f0203c59192b
$

Volumen para plugins

Examinando el fichero Dockerfile en GitHub podemos ver que SonarQube expone otro volumen para las extensiones (o plugins):

VOLUME ["$SONARQUBE_HOME/data", "$SONARQUBE_HOME/extensions"]

Si es necesario, podemos crear un nuevo volumen data-sonarqube-extensions y montarlo en un nuevo contenedor para SonarQube repitiendo los pasos descritos en esta sección.

Puerto 9092

Revisando la documentación de SonarQube, en el fichero de configuración sonar.properties el puerto 9092 se usa para conectar con la base de datos incrustada (embedded) H2:

...
#----- Embedded Database (default)
# H2 embedded database server listening port, defaults to 9092
#sonar.embeddedDatabase.port=9092
...

Dado que nosotros usamos MySQL, podemos relanzar el contenedor sin publicar el puerto 9092:

$ sudo docker stop sonarqube
sonarqube
$ sudo docker rm sonarqube
sonarqube
$ sudo docker run -d --name sonarqube -p 9000:9000 \
  --mount source=data-sonarqube,target=/opt/sonarqube/data \
  -e SONARQUBE_JDBC_USERNAME=sonar \
  -e SONARQUBE_JDBC_PASSWORD=cF68nTVgP8Nq \
  -e SONARQUBE_JDBC_URL='jdbc:mysql://mysql-sonarqube:3306/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useConfigs=maxPerformance&useSSL=false' \
  --network backend-sonarqube \
  sonarqube:7.1-alpine
$

Resumen

Después de depurar los diferentes errores surgidos en la creación del contenedor SonarQube, la aplicación está en marcha y funcionando.