springboot+mysql+docker 멀티 컨테이너 환경 구성

ran·2023년 6월 1일

Docker

목록 보기
1/3

교내에서 프로젝트를 구성하고, 배포하기 위해서 도커를 이용하던 중 3일이라는 시간을 투자하게 만든 난관에 대해 설명하려고 한다.

1. application.yml

spring:
  jpa:
    hibernate:
      ddl-auto: update
    properties:
      hibernate:
        format_sql: true
    database-platform: org.hibernate.dialect.MySQL57InnoDBDialect
    
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://db-mysql:3306/mydb?useSSL=false
    username: user
    password: xxxx

정말 정말 yml 파일을 잘 설정하자. 필자는 로컬에서 설정한 yml을 그대로 사용한 상태로 도커 빌드하다 보니

  • com.mysql.cj.jdbc.exceptions.communicationsexception: communications link failure
  • java.sql.SQLNonTransientConnectionException: Public Key Retrieval is not allowed
  • Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set

위와 같은 오류 지옥을 만났다... 아래에서 위의 오류에 대해 다시 설명하겠다.
우선 위의 코드에서 중요하게 볼것은 url부분이다.

  • url: jdbc:mysql://db-mysql:3306/mydb?useSSL=false
    기존url과는 다르게 생겼다. localhost:3306이 원래 url인데 위에서는 db-mysql이다.
    도커는 애플리케이션과 데이터베이스를 분리된 컨테이너로 실행하므로, 컨테이너 내부에서 동작하는 데이터베이스에 접근하기 위해서는 다른 호스트명이나 IP 주소를 사용해야 한다.
    쉽게 말해, 데이터베이스 컨테이너의 이름이거나 도커 네트워크에서 설정한 별칭을 이용해야한다.
    db-mysql은 mysql컨테이너 생성시 사용할 컨테이너 명칭이다.

필자는 이 사실을 몰라서 하루종일 도커만 만졌다..

2. 도커 파일 생성

FROM openjdk:11-jdk
EXPOSE 8080
RUN apt-get update

ARG JAR_FILE=build/libs/demo-0.0.1-SNAPSHOT.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

위와 같이 도커 파일을 생성했다. 도커파일은 이미지를 생성할때 필요한 파일로 다양한 명령어를 이용하여 설정할 수 있다.
명령어를 설명하면,

  • FROM: 생성할 이미지의 베이스가 될 이미지를 뜻하는 것으로, Dockerfile 생성시 필수
  • RUN: 이미지를 만들기 위해 컨테이너 내부에서 명령어를 실행합니다.
  • ARG: 도커 이미지 빌드시 넘길 수 있는 인자. 즉 jar file의 path를 변수처럼 사용
  • COPY: JAR FILE 경로의 위치한 파일을 app.jar로 복사
  • ENTRYPOINT: 이미지를 컨테이너로 띄울 때 항상 실행되야 하는 커맨드를 지정할 때 사용합니다.

3. 도커 네트워크 생성

도커는 여러 컨테이너를 동일한 환경에서 연동시키기 위해 네트워크 설정이 필요하다.
현재 mysql과 springboot를 이용하므로 동일한 네트워크가 되도록 설정한다.

네트워크 생성

docker network create springboot-mysql-net

생성된 네트워크 확인

docker network ls

4. 도커환경에서 MySQL 구동

1. 도커 허브에서 mysql 가져오기

일반적인 경우: docker pull mysql:5.7
그러나 위와 같이 m1 mac에서 설치하면 no matching manifest for linux/arm64/v8 in the manifest list entries 가 발생한다.
따라서, docker pull --platform linux/amd64 mysql:5.7 과 같이 입력한다.

5.7 버전을 pull해온 이유는 8버전부터 java.sql.SQLNonTransientConnectionException: Public Key Retrieval is not allowed 에러가 발생한다. 이유를 확인해보니 application.yml에서 url부분을 보면 useSSL=false가 있는데, SSL관련 문제로 키 문제이다. 그래서 해당 에러를 안나게 하려고 5버전을 pull한다.

2. mysql run

docker run --name db-mysql -p 3306:3306 --network springboot-mysql-net -e MYSQL_ROOT_PASSWORD=xxxx -e MYSQL_DATABASE=mydb -e MYSQL_USER=user -e MYSQL_PASSWORD=xxxx -d mysql:5.7

mysql 구동
--name: 도커상 host 이름. application.yml의 이름과 동일해야 링크 오류가 안남.!!!
-p: 포트매핑
--network: 네트워크 설정
-e: 환경변수 설정
-d: 백그라운드로 컨테이너 실행

추가로 위와 같이 실행하면 돌아는 가지만, 한글 인코딩이 깨지는 이슈가 발생한다. 따라서 한글이 안깨지게 character set을 utf-8로 변경하려면 아래의 코드를 위의 코드에 이어서 적어주면 된다.(확인해보니 ubuntu 환경에서 해당 명령어를 붙히면 컨테이너가 자동 종료됨. 우선은 로컬에서만 사용)

--character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_c

그러면 아래와 같이 docker run하면 한글이 안깨지게 mysql 컨테이너를 실행하는 것이다.

docker run --name db-mysql -p 3306:3306 --network springboot-mysql-net -e MYSQL_ROOT_PASSWORD=xxxx -e MYSQL_DATABASE=mydb -e MYSQL_USER=user -e MYSQL_PASSWORD=xxxx -d mysql:5.7 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_c

5. 도커환경에서 springboot application 구동

jar파일 build

두가지 방법이 있다. 인텔리제이를 이용하는 방법, cli를 이용하는 방법.
1-1. 인텔리제이 이용
위와 같이 Gradle 탭에 build-bootjar 하면 쉽게 build 된다.

1-2. CLI 이용
프로젝트에 들어가서, gradlew가 있는 위치에서 아래의 명령어 이용

./gradlew build

build/libs에 jar파일이 생성되면 이제 docker build를 통해 이미지로 만들어준다.

Dockerfile을 이용한 이미지 빌드

docker build -t springboot-mysql:1.0 .

중요한 점은 마지막에 space+. 을 꼭 붙혀줘야 하고, dockerfile이 위치한 디렉토리에서 위의 명령어를 수행해줘야 한다. 아니면 도커파일의 경로를 설정해줘야 한다.

빌드된 이미지로 컨테이너 구동

여기서 지옥이 시작됐다.. 컨테이너가 생성은 되는데 계속해서 springboot application 컨테이너가 계속 종료된다...
실행했던 코드는 아래와 같다.

docker run -p 8080:8080 --name springboot-mysql --network springboot-mysql-net -d springboot-mysql:1.0

일단 해당 코드는 문제가 없었다. 하지만 발생하는 문제는 아래와 같았다.

  • com.mysql.cj.jdbc.exceptions.communicationsexception: communications link failure
  • Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set
    해당 에러들이 발생했다.

첫번째 오류는 스프링부트 프로젝트가 DB에 제대로 연결되어 있지 않기 때문에 발생하는 문제였다.

해당 오류가 발생하게 된 이유는 application.yml이 생성한 db컨테이너와 맞지 않았다.
1. url: jdbc:mysql://db-mysql:3306/mydb?useSSL=false
단순히 localhost로 설정하고 컨테이너를 실행했다. -> 컨테이너 이름으로 변경!

  1. username: user, password: xxxx
    yml에서 위와 같이 datasource를 설정했는데, 이는 mysql을 도커에 컨테이너로 run하는 docker run --name db-mysql -p 3306:3306 --network springboot-mysql-net -e MYSQL_ROOT_PASSWORD=xxxx -e MYSQL_DATABASE=mydb -e MYSQL_USER=user -e MYSQL_PASSWORD=xxxx -d mysql:5.7과 연관이 있다.
    바로 MYSQL_USER=user -e MYSQL_PASSWORD=xxxx 와 동일해야 한다.
    필자는 '무지성'으로 yml과 해당 명령어를 통일시키지 않아서 발생했던 문제였다.

두번째 오류는 hibernate.dialect가 제대로 설정되어 있지 않았다.

기존 yml 파일에서는 database-platform: org.hibernate.dialect.MySQL57InnoDBDialect 해당 부분 없이 작성하였다. 그러나 db를 쓸때 방언을 설정해야 sql이 맞게 변환되기 때문에 해당 부분을 추가해줬다.

따라서 이를 고치고 컨테이너를 run하게 되면 잘 작동하는 것을 확인할 수
있다.

참고

profile
Backend Developer

0개의 댓글