공부해봅시당
[Project] mysql, spring-boot, docker-compose 실행 본문
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의 내용을 첨부한다.
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 이름
- jdbc(Java Database Connectivity)
- 파라미터
- useSSL=false
- SSL 연결 비활성화
- autoReconnect=true
- 서버와의 연결이 끊어졌을 때 자동으로 재연결
- allowPublicKeyRetrieval=true
- 공개키 검색 허용
- useUnicode=true
- 유니코드 사용 활성화
- characterEncoding=UTF-8
- 문자 인코딩은 UTF-8 사용
- serverTimezone=UTC
- 서버 시간대는 UTC 사용
- useSSL=false
- jdbc:mysql://localhost:3306/userdb
- driver-class-name
- com.mysql.jdbc.Driver와 com.mysql.cj.jdbc.Driver 두가지가 있음
- 전자는 Deprecated이므로 com.mysql.cj.jdbc.Driver를 사용하도록 해야 함
- url
- jpa(Java Persistence API)
- database-platform: Hibernate를 위해 데이터베이스가 사용할 SQL Dialect
- MySQL의 SQL Dialect 참고
- 여기에서는 org.hibernate.dialect.MySQL8Dialect를 사용
- hibernate.ddl-auto: 스키마 생성 전략
- create로 설정 시, 애플리케이션이 시작될 때마다 데이터베이스 스키마를 새롭게 다시 생성
- update로 설정 시, 기존 데이터를 유지하면서 데이터베이스 스키마를 변경하거나 업데이트
- database-platform: Hibernate를 위해 데이터베이스가 사용할 SQL Dialect
해결 완료
위 설정대로 테스트를 완료한 후, 기존 프로젝트로 돌아가 테스트 설정 방법대로 모두 수정하니 성공적으로 서비스가 작동하였다.
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 해야하기 때문에 권장하지 않는다고 한다.
'STUDY > 개발 고민' 카테고리의 다른 글
[성능 테스트] nGrinder + Springboot 부하 테스트 준비 - groovy 파일로 스크립트 실행해보기 (0) | 2023.11.07 |
---|---|
[성능 테스트] nGrinder + Springboot 부하 테스트 준비 - nGrinder 설치 및 agent 실행 (0) | 2023.10.17 |
[성능 테스트] 성능테스트를 위한 준비 - MYSQL에 Mockaroo로 더미 데이터 넣기2 (0) | 2023.10.17 |
[성능 테스트] 성능테스트를 위한 준비 - MYSQL에 프로시저로 더미 데이터 넣기 (1) | 2023.10.17 |
[Spring Boot] Security 7.0 버전업에 의한 Deprecated 문제 (1) | 2023.07.09 |