mysqlspringdockeraccess-denied

running mysql with spring boot in docker: access denied to database


aplication.properties:

spring.datasource.url=jdbc:mysql://localhost:3306/springapp
spring.datasource.username=www_data
spring.datasource.password=pass

spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.SQL=DEBUG

Dockerfile:

FROM maven:3.8.5-eclipse-temurin-17-alpine
WORKDIR app
COPY src src
COPY pom.xml pom.xml
RUN mvn clean package -DskipTests
EXPOSE 8080
CMD java -jar target/j1-0.0.1-SNAPSHOT.jar

docker-compose.yaml:

version: "3.7"
services:
  springapp:
    build: .
    ports:
      - "8080:8080"
    networks:
      - j0-net
    environment:
      - spring.datasource.url=jdbc:mysql://mysqldb:3306/springapp
    depends_on:
      - mysqldb
    volumes:
      - .m2:/root/.m2
  mysqldb:
    image: "mysql"
    ports:
      - "3306:3306"
    networks:
      - j0-net
    environment:
      MYSQL_DATABASE: springapp
      MYSQL_USER: www_data
      MYSQL_PASSWORD: pass
      MYSQL_ROOT_PASSWORD: pass
networks:
  j0-net:

When running docker compose up for the first time. I am getting

...
#0 48.96 com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
#0 48.96 
#0 48.96 The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
...

after spring boots run. Running it second time I got

j1-springapp-1  | 
j1-springapp-1  |   .   ____          _            __ _ _
j1-springapp-1  |  /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
j1-springapp-1  | ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
j1-springapp-1  |  \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
j1-springapp-1  |   '  |____| .__|_| |_|_| |_\__, | / / / /
j1-springapp-1  |  =========|_|==============|___/=/_/_/_/
j1-springapp-1  |  :: Spring Boot ::                (v3.2.0)
j1-springapp-1  | 
j1-springapp-1  | 2023-11-26T20:42:13.536Z  INFO 1 --- [           main] com.example.j1.J1Application             : Starting J1Application v0.0.1-SNAPSHOT using Java 17.0.3 with PID 1 (/app/target/j1-0.0.1-SNAPSHOT.jar started by root in /app)
j1-springapp-1  | 2023-11-26T20:42:13.542Z  INFO 1 --- [           main] com.example.j1.J1Application             : No active profile set, falling back to 1 default profile: "default"
j1-springapp-1  | 2023-11-26T20:42:15.049Z  INFO 1 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
j1-springapp-1  | 2023-11-26T20:42:15.173Z  INFO 1 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 105 ms. Found 1 JPA repository interface.
j1-springapp-1  | 2023-11-26T20:42:16.221Z  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
j1-springapp-1  | 2023-11-26T20:42:16.237Z  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
j1-springapp-1  | 2023-11-26T20:42:16.237Z  INFO 1 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.16]
j1-springapp-1  | 2023-11-26T20:42:16.338Z  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
j1-springapp-1  | 2023-11-26T20:42:16.341Z  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2619 ms
j1-springapp-1  | 2023-11-26T20:42:16.636Z  INFO 1 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
j1-springapp-1  | 2023-11-26T20:42:16.781Z  INFO 1 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 6.3.1.Final
j1-springapp-1  | 2023-11-26T20:42:16.866Z  INFO 1 --- [           main] o.h.c.internal.RegionFactoryInitiator    : HHH000026: Second-level cache disabled
j1-springapp-1  | 2023-11-26T20:42:17.357Z  INFO 1 --- [           main] o.s.o.j.p.SpringPersistenceUnitInfo      : No LoadTimeWeaver setup: ignoring JPA class transformer
j1-springapp-1  | 2023-11-26T20:42:17.412Z  INFO 1 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
j1-springapp-1  | 2023-11-26T20:42:18.985Z ERROR 1 --- [           main] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Exception during pool initialization.
j1-springapp-1  | 
j1-springapp-1  | java.sql.SQLSyntaxErrorException: Access denied for user 'www_data'@'%' to database 'springapp'
j1-springapp-1  |   at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:121) ~[mysql-connector-j-8.0.33.jar!/:8.0.33]
j1-springapp-1  |   at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) ~[mysql-connector-j-8.0.33.jar!/:8.0.33]
j1-springapp-1  |   at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:825) ~[mysql-connector-j-8.0.33.jar!/:8.0.33]
...

Why? The user www_data with password pass is specified both in application.properties and docker-compose.yaml. So I assume the user is created and thus should have access to springapp database. So whats going on?

Projects source: https://github.com/shepherd123456/springapp


Solution

  • The mysqldb container takes a moment to start up. We have the 'depends_on-section for springappcontainer in thedocker-compose.yaml, but this only means that we start the mysqldbcontainer before thehspringappcontainer, not that themysqlservice in themysqldb` container is actually ready.

    To fix this, we have to do two things:

    1. We need to know when the mysql service within the mysqldb container is ready
    2. We need to delay the start of the springapp container until the mysql service is ready.

    To achieve 1., we can provide a health check (docs.docker.com) for the mysqldb container. Luckily, since the container ships with mysqladmin, implementing the check means adding the following to the docker-compose.yaml:

    version: "3.7"
    services:
      ...
      mysqldb:
        ...
        healthcheck:
          test: [
            "CMD",
            "mysqladmin",
            "-h", "127.0.0.1",
            "--user=$$MYSQL_USER", # double quotes since the variable MYSQL_USER should be evaluated at container runtime
            "--password=$$MYSQL_PASSWORD", # double quotes since the variable MYSQL_PASSWORD should be evaluated at container runtime
            "ping"
          ]
          interval: 10s
          timeout: 1s
          retries: 3
          start_period: 30s
      ...
    

    For 2., we can add a condition to the depends_on (docs.docker.com) section of springapp in docker-compose.yml

    version: "3.7"
    services:
      springapp:
        ...
        depends_on:
          mysqldb:
            condition: service_healthy
        ...