본문 바로가기

Back-End/Docker

Spring Boot, MySQL 컨테이너 동시에 띄워보기(feat. 오류)

[Spring Boot, MySQL 컨테이너 동시에 띄어보기]

1. Spring Boot 프로젝트 세팅

2. 간단한 코드 작성 또는 이미 만들어진 프로젝트

3. application.yml에 DB를 연결을 위한 정보 작성하기

# applicatiom.yml

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: root
    password: pwd1234
    driver-class-name: com.mysql.cj.jdbc.Driver

4. Dockerfile 작성하기

# Dockerfile

FROM openjdk:17-jdk

COPY build/libs/*SNAPSHOT.jar /app.jar

ENTRYPOINT ["java", "-jar", "/app.jar"]
  • FROM opnejdk:17-jdk
    • 의미 : 이 컨테이너의 기반 이미지를 지정하는 것. 여기서는 openjdk:17-jdk 라는 이미지를 사용하고 있다.
    • 설명 : openjdk:17-jdk는 Java 17 JDK가 설치된 이미지이다. 이 이미지를 기반으로 나의 애플리케이션을 실행할 컨테이너를 만들게 된다.
  • COPY build/libs/*SNAPSHOT.jar /app.jar
    • 의미 : 호스트 컴퓨터에 있는 파일을 컨테이너 안으로 복사하라는 명령어
    • 설명 : build/libs/*SNAPSHOT.jar나의 Java 프로젝트에서 빌드된 Jar 파일이다. 이 파일을 컨테이너 안의 /app.jar 라는 경로로 복사를 하는 것이다. 이렇게 하면 컨테이너 안에서 이 Jar 파일을 실행할 수 있게 된다.
  • ENTRY ["java", "-jar", "/app.jar"]
    • 의미 : 컨테이너가 실행될 때 실행할 명령어를 지정한다
    • 설명 : java -jar /app.jar는 Jar 파일을 실행하는 명령어다. 컨테이너가 시작되면 이 명령어가 실행되어 나의 Java 어플리케이션이 실행되게 된다.

5. compose.yml 파일 작성하기

# compose.yml

services:
  my-server:
    build: .
    ports:
      - 8080:8080
    # my-db의 컨테이너가 생성되고 healthy 하다고 판단 될 때, 해당 컨테이너를 생성한다. 
    depends_on:
      my-db:
        condition: service_healthy
  my-db:
    image: mysql
    environment:
      MYSQL_ROOT_PASSWORD: pwd1234
      MYSQL_DATABASE: mydb # MySQL 최초 실행 시 mydb라는 데이터베이스를 생성해준다.
    volumes:
      - ./mysql_data:/var/lib/mysql
    ports:
      - 3306:3306
    healthcheck:
      test: [ "CMD", "mysqladmin", "ping" ] # MySQL이 healthy 한 지 판단할 수 있는 명령어
      interval: 5s # 5초 간격으로 체크
      retries: 10 # 10번까지 재시도

6. Spring Boot 프로젝트 빌드하기

$ ./gradlew clean build

7. compose 파일 실행시키기

$ docker compose up -d --build

8. compose 실행 현황 보기

$ docker compose ps
$ docker ps
$ docker logs [컨테이너 ID]

 

근데 확인해보니 띠용? 스프링부트가 정상적으로 띄어지지 않았다.. 로그를 확인해보니

Failed to initialize JPA ENtityMAnagerFactory: Unable to ...

이런 DB와 연결이 제대로 이루어지지 않았을 때 발생하는 에러가 로그에 찍혀있다. 뭐지 잘 연결 된 것 같은데 왜 안될까..?


컨테이너로 실행시킨 Spring Boot가 MySQL에 연결이 안 되는 이유

각각의 컨테이너는 자신만의 네트워크 망과 IP 주소를 가지고 있다. 호스트 컴퓨터 입장에서 localhost는 호스트 컴퓨터를 가리키지만 Spring boot 컨테이너 입장에서는 localhost는 Spring Boot 컨테이너를 가리킨다.

근데 SpringBoot의 코드를 작성할 때 DB 정보를 아래와 같이 입력했다. SpringBoot가 실행되는 환경인 컨테이너 입장에서 localhost:3306라는 주소는 Spring Boot 컨테이너 내부에 있는 3306번 포트와 연결을 시도하게 된다. 하지만 Spring Boot가 실행되는 컨테이너 내부의 3306 포트에는 아무것도 실행되고 있지 않다. 이러한 구조상의 문제 때문에 Spring Boot가 My SQL에 연결이 안되고 있었던 것이다.

그럼 어떻게 Spring Boot의 컨테이너에서 다른 컨테이너에 존재하는 MySQL에 연결을 할 수 있을까?
compose.yml에서 정의한 Service 이름으로 서로 통신할 수
있다.

[Spring Boot의 DB 정보를 아래와 같이 수정한 뒤 시도해보기]

# application.yml

spring:
  datasource:
    url: jdbc:mysql://my-db:3306/mydb
    username: root
    password: pwd1234
    driver-class-name: com.mysql.cj.jdbc.Driver

위 코드에서 //localhost:3306 대신 //my-db:3306으로 바꿔주었다.

우리가 이전에 작성했던 compose.yml을 보면 각 컨테이너에 service 이름(my-server, my-db)을 작성했었다.

services:
  my-server:
    build: .
    ports:
      - 8080:8080
    depends_on:
      my-db:
        condition: service_healthy
  my-db:
    image: mysql
    environment:
      MYSQL_ROOT_PASSWORD: pwd1234
      MYSQL_DATABASE: mydb
    volumes:
      - ./mysql_data:/var/lib/mysql
    ports:
      - 3306:3306
    healthcheck:
      test: [ "CMD", "mysqladmin", "ping" ]
      interval: 5s
      retries: 10

Service 이름이 컨테이너의 주소를 뜻한다. 해당 컨테이너의 IP 주소와 같은 역할을 한다.

$ ./gradlew clean build
$ docker compose down
$ docker compose up --build -d

$ docker ps # 정상적으로 Spring Boot, MySQL이 실행된 걸 확인할 수 있다.

위와 같이 코드를 수정한 뒤에 다시 한 번 컨테이너를 실행시켜 보면 잘 실행되는 것을 볼 수 있다.