공부해봅시당

[Project] mysql, spring-boot, docker-compose 실행 본문

STUDY/개발 고민

[Project] mysql, spring-boot, docker-compose 실행

tngus 2023. 7. 4. 02:15

mysql과 spring-boot를 docker-compose.yml을 사용하여 실행하고자 하였을 때 발생했던 문제들에 대해 기술하도록 하겠다.

 

문제 발생

문제는 기존에 postgresql로 잘 돌아가던 서비스를 mysql로 변경하고자 하면서 발생했다.

 

이번에 채팅 서버를 개발하면서 Kafka를 사용해보기로 결정한 후, 관련 래퍼런스를 찾다가 상당히 괜찮은 래퍼런스를 발견했다.

 

아래는 래퍼런스 깃허브 링크이다.

 

https://github.com/ghkdqhrbals/spring-chatting-server

 

GitHub - ghkdqhrbals/spring-chatting-server: Spring-Java 기반 채팅 및 주문 서버입니다. 현재 MSA 아키텍처로

Spring-Java 기반 채팅 및 주문 서버입니다. 현재 MSA 아키텍처로 전환중에 있습니다. - GitHub - ghkdqhrbals/spring-chatting-server: Spring-Java 기반 채팅 및 주문 서버입니다. 현재 MSA 아키텍처로 전환중에 있습

github.com

 

해당 래퍼런스가 좋은 이유는 아래 블로그 링크로 충분히 확인이 가능하다고 생각한다.

https://ghkdqhrbals.github.io/portfolios/docs/project/

 

📌 실시간 채팅서버 프로젝트

Gyumin Hwangbo Porfolio

ghkdqhrbals.github.io

 

해당 프로젝트는 postgresql로 DB를 설정했고, 우리팀은 보다 더 보편적이면서 점유율이 높은 mysql을 사용하기로 결정했다.

 

따라서 위 postgresql을 mysql로 변경해야 했고, 쉽게 변경할 수 있을 것이라 기대했던 것과는 다르게 3일간 수많은 난관에 부딪혔다.

 

발생했던 문제과 해결

본래 코드에서 PostgreSQL을 MySQL로 변경하기에는 Kafka, Zookeeper, SpringBoot, MySQL 등 다수의 컨테이너를 확인해야하는 문제가 있었다.

 

따라서 새롭게 Spring Boot 테스트 프로젝트를 만들었다. 그리고 MySQL과 Spring Boot만을 docker-compose.yml에 올려서 테스트를 진행해보기로 하였다.

 

발생한 문제를 모두 해결한 테스트 프로젝트 코드는 아래 깃허브 링크에서 확인할 수 있다.

https://github.com/vivian0304/mysql-springboot-docker-test/tree/main

 

GitHub - vivian0304/mysql-springboot-docker-test

Contribute to vivian0304/mysql-springboot-docker-test development by creating an account on GitHub.

github.com

 

build.gradle의 MySQL dependencies 수정

가장 처음에 발생했던 문제는 아래와 같다.

could not find mysql:mysql-connector-java

build.gradle의 문제로, mysql의 버전이 올라가면서 표기 방식이 달라졌다고 한다.

 

MySQL 8.0.31 버전부터 groupId와 artifactId가 변경되었다고 한다.

 

참고했던 블로그의 원문을 첨부한다.

https://velog.io/@yiseull/Could-not-find-mysqlmysql-connector-java-%ED%95%B4%EA%B2%B0

 

Could not find mysql:mysql-connector-java 해결

Repository 테스트를 하던 도중 아래와 같은 테스트 실행 실패 문구가 떴습니다.메시지를 보니 mysql-connector-java 를 찾을 수 없다는 것 같은데,,, build.gradle 에도 잘 있고 도대체 뭐가 문제일까...구글

velog.io

 

[기존] MySQL 8.0.31 이전 maven 버전

<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>

 

[변경 후] MySQL 8.0.31 이후 maven 버전

<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>

 

[변경 후] MySQL 8.0.31 이후 gradle 버전

dependencies {
	// MySQL dependencies
	implementation 'com.mysql:mysql-connector-j'
	runtimeOnly 'com.mysql:mysql-connector-j'   
}

 

 

이후에는 정체를 알 수 없는 에러들이 수도 없이 발생하여 모두 기록하며 진행하지 못했다.

따라서 해결 이후의 코드를 중심으로 작성하였다.

 

 

docker-compose.yml 파일의 DB 설정들을 MySQL 설정으로 변경

mysql에 연결하기 위해 ssl을 false로 설정하는 등 여러 설정이 모두 포함되어 있다.

version: "3"
services:
  mysql-db:
    container_name: mysql-db #컨테이너 명
    image: mysql #이 컨테이너를 실행하기 위해 사용하는 Docker 이미지
    environment: #컨테이너에서 사용할 환경 변수
      MYSQL_DATABASE: userdb #MySQL 데이터베이스 이름
      MYSQL_ROOT_HOST: '%' #root 사용자의 접근을 허용하는 호스트, '%'는 모든 호스트 허용
      MYSQL_ROOT_PASSWORD: rootpwd #root 사용자의 비밀번호
    command: #MySQL 서버 시작 시, 추가적으로 전달되는 명령어
      - --default-authentication-plugin=mysql_native_password #인증 플러그인으로 mysql_native_password 사용
      - --character-set-server=utf8mb4 #서버의 문자셋을 UTF-8로 설정
      - --collation-server=utf8mb4_unicode_ci #서버의 정렬 순서를 UTF-8로 설정
      - --skip-character-set-client-handshake #클라이언트가 제안하는 문자셋 설정을 무시하고 서버의 문자셋 설정을 사용
    expose: #컨테이너가 연결할 포트 설정
      - "3303"
    volumes: 
      - ./backups:/home/backups
    ports: #포트 설정
      - "3303:3306" #호스트의 3303 포트를 컨테이너의 3306 포트에 연결
    restart: always #컨테이너가 실패할 경우, 항상 재시작

  application:
    container_name: application
    build: .
    depends_on:
      - mysql-db
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://mysql-db:3306/userdb?useSSL=false&autoReconnect=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: rootpwd
    restart: always

 

  • --default-authentication-plugin=mysql_native_password: 링크 참고

 

 

application.yml 파일의 PostgreSQL 설정을 MySQL 설정으로 변경

application.yml의 설정들이다.

mysql driver 사용을 위해 com.mysql.ci.jdbc.Driver 설정을 해야 한다.

 

여기서는 특히 org.hibernate.dialect.MySQL8Dialect 부분에 유의해야 한다.

stackoverflow의 내용을 첨부한다.

https://stackoverflow.com/questions/43716068/invalid-syntax-error-type-myisam-in-ddl-generated-by-hibernate

 

Invalid syntax error "type= MyISAM" in DDL generated by Hibernate

I am getting this error for my Java code Caused by :`com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException`: You have an error in your SQL syntax; check the manual that corresponds to your

stackoverflow.com

 

application.yml

주석으로 표기하지 못한 내용은 코드 아래 부분에 따로 기술하였다.

profiles:
  default: local
spring:
  datasource:
    url: jdbc:mysql://mysql-db:3306/userdb?useSSL=false&autoReconnect=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root #데이터베이스 접속 계정 이름
    password: rootpwd #데이터베이스 접속 비밀번호
    driver-class-name: com.mysql.cj.jdbc.Driver


  jpa:
    database: mysql #연결할 데이터베이스 이름
    database-platform: org.hibernate.dialect.MySQL8Dialect
    generate-ddl: true #DDL 생성을 활성화
    show-sql: true #SQL 문을 로그로 출력
    properties: #추가 Hibernate 설정
      hibernate: 
        format_sql: true #SQL의 포맷팅을 활성화
        use_sql_comments: true #SQL 주석 사용
        jdbc:
          time_zone: Asia/Seoul #JDBC 연결 시간대
    hibernate:
      ddl-auto: create
  • database
    • url
      • jdbc:mysql://localhost:3306/userdb
        • jdbc(Java Database Connectivity)
          • 자바에서 데이터베이스에 접속할 수 있도록 하는 자바 API
        • mysql: 연결할 jdbc가 mysql이라는 뜻
          • postgresql로 연결하고 싶을 경우, postgresql로 변경하면 됨
        • localhost: IP 주소
        • 3306: mysql의 접속 PORT 번호
          • postgresql로 연결하고 싶을 경우, (기본 포트라면) 5432로 변경하면 됨
        • userdb: 연결하고자 하는 DATABASE 이름
      • 파라미터
        •  useSSL=false
          • SSL 연결 비활성화
        • autoReconnect=true
          • 서버와의 연결이 끊어졌을 때 자동으로 재연결
        • allowPublicKeyRetrieval=true
          • 공개키 검색 허용
        • useUnicode=true
          • 유니코드 사용 활성화
        • characterEncoding=UTF-8
          • 문자 인코딩은 UTF-8 사용
        • serverTimezone=UTC
          • 서버 시간대는 UTC 사용
    • driver-class-name
      • com.mysql.jdbc.Driver와 com.mysql.cj.jdbc.Driver 두가지가 있음
      • 전자는 Deprecated이므로 com.mysql.cj.jdbc.Driver를 사용하도록 해야 함
  • jpa(Java Persistence API)
    • database-platform: Hibernate를 위해 데이터베이스가 사용할 SQL Dialect
    • hibernate.ddl-auto: 스키마 생성 전략
      • create로 설정 시, 애플리케이션이 시작될 때마다 데이터베이스 스키마를 새롭게 다시 생성
      • update로 설정 시, 기존 데이터를 유지하면서 데이터베이스 스키마를 변경하거나 업데이트

 

해결 완료

위 설정대로 테스트를 완료한 후, 기존 프로젝트로 돌아가 테스트 설정 방법대로 모두 수정하니 성공적으로 서비스가 작동하였다.

 

MySQL과 Spring Boot, docker-compose로 인해 고통받는 사람들이 있다면 이 글을 통해 해결이 되었으면 한다.

 

 

아직 남아 있는 의문점 -> 해결

해결 당시, 가장 애를 먹었던 부분은 포트번호였다.

 

docker-compose.yml에서 3303으로 expose 했기 때문에 3303으로 포트번호를 작성했지만, 찾을 수 없는 url이라는 에러 문구만 반복해서 나타날 뿐이었다.

 

기존 코드

docker-compose.yml

version: "3"
services:
  mysql-db:
    container_name: mysql-db
    image: mysql
    environment:
      MYSQL_DATABASE: userdb
      MYSQL_ROOT_HOST: '%'
      MYSQL_ROOT_PASSWORD: rootpwd
    command:
      - --default-authentication-plugin=mysql_native_password
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
      - --skip-character-set-client-handshake
    expose:
      - "3303"
    volumes:
      - ./backups:/home/backups
    ports:
      - "3303:3303" # 이 부분이 3303:3306이 아니라 3303:3303 이었다
    restart: always

  application:
    container_name: application
    build: .
    depends_on:
      - mysql-db
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://mysql-db:3303/userdb?useSSL=false&autoReconnect=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
      	# 따라서 위 SPRING_DATASOURCE_URL 또한 3303으로 포트번호를 설정하였다.
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: rootpwd
    restart: always

application.yml

profiles:
  default: local
spring:
  datasource:
    url: jdbc:mysql://mysql-db:3303/userdb?useSSL=false&autoReconnect=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    	# 위 포트 번호를 3303으로 설정했었다.
    username: root
    password: rootpwd
    driver-class-name: com.mysql.cj.jdbc.Driver


  jpa:
    database: mysql
    database-platform: org.hibernate.dialect.MySQL8Dialect
    generate-ddl: true
    show-sql: true
    properties:
      hibernate:
        format_sql: true
        use_sql_comments: true
        jdbc:
          time_zone: Asia/Seoul
    hibernate:
      ddl-auto: create

 

 

고민 끝에 해결 후 코드처럼 default 포트번호인 url을 3306으로 변경 후 진행하였더니 해결되었다.

 

해결 후 코드

docker-compose.yml

version: "3"
services:
  mysql-db:
    container_name: mysql-db
    image: mysql
    environment:
      MYSQL_DATABASE: userdb
      MYSQL_ROOT_HOST: '%'
      MYSQL_ROOT_PASSWORD: rootpwd
    command:
      - --default-authentication-plugin=mysql_native_password
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
      - --skip-character-set-client-handshake
    expose:
      - "3303"
    volumes:
      - ./backups:/home/backups
    ports:
      - "3303:3306"
    restart: always

  application:
    container_name: application
    build: .
    depends_on:
      - mysql-db
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://mysql-db:3306/userdb?useSSL=false&autoReconnect=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: rootpwd
    restart: always

application.yml

profiles:
  default: local
spring:
  datasource:
    url: jdbc:mysql://mysql-db:3306/userdb?useSSL=false&autoReconnect=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: rootpwd
    driver-class-name: com.mysql.cj.jdbc.Driver


  jpa:
    database: mysql
    database-platform: org.hibernate.dialect.MySQL8Dialect
    generate-ddl: true
    show-sql: true
    properties:
      hibernate:
        format_sql: true
        use_sql_comments: true
        jdbc:
          time_zone: Asia/Seoul
    hibernate:
      ddl-auto: create

 

 

하지만 PostgreSQL로 설정되어 있던 기존의 application.properties파일은 아래와 같이 설정했으며 정상적으로 작동했었다.

참고로 PostgreSQL의 기본 포트 번호는 5432이다.

  chatting-db-2:
    container_name: chatting-db-2
    image: postgres:12-alpine
    environment:
      - POSTGRES_PASSWORD=password
      - POSTGRES_USER=postgres
      - POSTGRES_DB=chat2
    expose:
      - "5434" # Publishes 5433 to other containers but NOT to host machine
    ports:
      - "5434:5434"
    volumes:
      - ./backups:/home/backups
    command: -c wal_level=logical -p 5434
    restart: always

  chatting-server-2:
    container_name: chatting-server-2
    build: ./spring-chatting-backend-server
    ports:
      - "8084:8084"
    environment:
      - SPRING_DATASOURCE_URL=jdbc:postgresql://chatting-db-2:5434/chat2
      - SPRING_DATASOURCE_USERNAME=postgres
      - SPRING_DATASOURCE_PASSWORD=password
      - SPRING_JPA_HIBERNATE_DDL_AUTO=update
      - SERVER_PORT=8084
      - KAFKA_BOOTSTRAP=kafka1:9092,kafka2:9092,kafka3:9092 # 내부포트
    depends_on:
      - kafka1
      - kafka2
      - kafka3
      - chatting-db-2
    restart: always

 

 

이 부분에 대한 의문점을 해결하기 전, 몇 가지 가설을 세워보았다.

 

[가설1] 내부적으로는 3306에서 MySQL이 돌아가기 때문에 3306으로 설정해야 한다.

하지만 이 가설은 docker-compose.yml 파일에서 내부와 외부를 모두 3303으로 설정하고, url의 포트번호를 3303으로 설정했을 때도 통하지 않았기 때문에 설득력이 낮아 보인다.

 

[가설2] MySQL은 docker-compose.yml에서 어떻게 설정하든 3306에서 돌아간다.

사실 이 가설도 가설1과 크게 다르지 않다. 하지만 이게 아니라면 크게 설명되지 않을 것 같아 이렇게 가설을 세워보았다.

 

아직 의문점을 해결하지 못한 상황이며, 의문점을 해결하게 된다면 업데이트 하도록 하겠다.

 

더해서, 해당 의문점에 대한 해결안을 알고 있으시다면 댓글 남겨주시면 감사할 것 같다.

 

 

아래 댓글 내용에 따르면 기존에 세웠던 가설1의 내용이 맞았다.

 

PostgreSQL은 아래 명령어를 통해 container 내부에서 사용될 port를 변경한다.

command: -c wal_level=logical -p 5434

 

하지만 mysql의 경우, 직접 설정파일(my.cnf)에서 포트를 변경한 후, image로 build 해야하기 때문에 권장하지 않는다고 한다.