docker-compose로 SpringBoot + MySql DB 서버 구동

devdo·2022년 2월 22일
3

Docker

목록 보기
7/11
post-thumbnail

docker-compose를 사용하는 이유

docker-compose를 통해서 Dockerfile 하나보다 여러 컨테이너들을 묶어서 편하게 관리하기가 아주 좋습니다.

이제 docker-compose를 위한 실습을 해봅시다.

UserEntity를 가지고 CURD를 하는 SpringBoot RESTAPI 프로젝트를 만들어보았습니다. 이 프로젝트는 MySql DB와 연동을 해야 하는데 docker-compose.yml 파일로 쉽게 연동할 수 있음을 실습해보겠습니다.


주의!

Mysql 사용시 로컬에서 port 3306를 중지시키고 docker-compose 실습을 하기 바랍니다. 이것때문에 port를 docker-compose에서 아무리 바꿔도 바뀌지 않습니다!...

심신건강을 위해 3306를 꺼두고 진행해주세요! => 작업관리자에서 꺼주시길!


SpringBoot 프로젝트

build.gradle

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'

    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

    // jpa
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

    // mysql
    runtimeOnly 'com.mysql:mysql-connector-j'

    // test에서 lombok 사용
    testCompileOnly 'org.projectlombok:lombok'
    testAnnotationProcessor 'org.projectlombok:lombok'

    // dotenv
    implementation 'io.github.cdimascio:java-dotenv:5.1.1'

}

  • UserEntity
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "users")
public class UserEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;

    private String password;

}
  • UserDto
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserDto {

    private String username;
    private String password;
}
  • UserResult
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserResult {

    private Long id;
    private String username;
    private String password;

    public UserResult(UserEntity users) {
        this.id = users.getId();
        this.username = users.getUsername();
        this.password = users.getPassword();
    }
}
  • UserRepository
public interface UserRepository extends JpaRepository<UserEntity, Long> {
}
  • UserService
@RequiredArgsConstructor
@Service
public class UserService {

    private final UserRepository userRepository;

    public UserEntity create(UserDto userDto) {
        
        UserEntity userEntity = UserEntity.builder()
                .username(userDto.getUsername())
                .password(userDto.getPassword())
                .build();
        
        return userRepository.save(userEntity);
    }

    public UserEntity readOne(Long id) {
        return userRepository.findById(id)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
    }

}

UserController

@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/api")
public class UserController {

    private final UserService userService;

    @PostMapping
    public ResponseEntity create(@RequestBody UserDto userDto) {
        log.info("create Users");
        UserEntity users = userService.create(userDto);
        return ResponseEntity.ok(new UserResult(users));
    }

    @GetMapping("{id}")
    public ResponseEntity getUser(@PathVariable Long id) {
        log.info("readOne");
        UserEntity users = userService.readOne(id);
        return ResponseEntity.ok(new UserResult(users));
    }


}

DB ORM은 JPA를 사용합니다.

application.yml 파일에서 JPA 연동 설정을 해야합니다.
database 설정은 docker-compose.yml에서 설정해줄 거기 때문에 application.yml에서 뺐습니다.

application.yml

spring:
# 주석을 해도 안해도 상관x, 설사 틀린내용이라도. 우선순위가 docker-compose내용으로 하기 때문
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://${PROD_HOST}:3307/${MYSQL_DATABASE}?useSSL=false&serverTimezone=Asia/Seoul&characterEncoding=UTF-8&allowPublicKeyRetrieval=true
    username: ${MYSQL_USER}
    password: ${MYSQL_PASSWORD}


  jpa:
    open-in-view: false # controller 에서 지연로딩을 사용할 때 false로 설정
    hibernate:
      ddl-auto: update # 테이블 생성 및 업데이트 전략 (create, create-drop, update, validate, none)
    properties:
      hibernate:
        format_sql: true # SQL 포맷팅
        highlight_sql: true # 하이라이트 SQL 출력
        use_sql_comments: true # 실제 JPQL SQL 주석 사용 

build.gradle

jar {
    enabled = false
}

DotEnv 설정

.env

#DB
LOCAL_HOST=localhost
PROD_HOST=host.docker.internal

# Mysql
MYSQL_ROOT_PASSWORD=1234
MYSQL_DATABASE=post
MYSQL_USER=post
MYSQL_PASSWORD=post

DotEnvConfig

@Slf4j
@Configuration
public class DotEnvConfig {

    @Bean
    public Dotenv dotenv() {
        log.info(".env file loading...");
        // .env 파일을 읽어서 환경변수로 사용
        return Dotenv.configure().directory("./")
                .ignoreIfMissing() // .env 파일이 없어도 에러 발생 안함
                .load();
    }
}

Dockerfile과 docker-compse.yml 설정

자 이제 Dockerfile과 docker-compse.yml을 보겠습니다.
이 둘은 꼭 같은 경로에 있어야 합니다!

./gradlew clean build로 jar를 새로 생성하고 실행해야 오류가 안납니다.

Dockerfile

FROM openjdk:17-jdk

ARG JAR_FILE=build/libs/*SNAPSHOT.jar

COPY ${JAR_FILE} app.jar

ENTRYPOINT ["java","-jar","/app.jar"]

gradlew clean build 처리 까지한 Dockerfile

Dockerfile

# 최신 17-jdk-alpine 이미지로부터 시작
FROM openjdk:17-jdk-slim

# 작업 디렉토리를 /app으로 설정
WORKDIR /app

# 현재 디렉토리의 모든 파일을 컨테이너의 /app 디렉토리로 복사
COPY . .

# gradlew에 실행 권한 부여
RUN chmod +x ./gradlew
# 프로젝트 빌드
RUN ./gradlew clean build

#ENV SPRING_PROFILES_ACTIVE=prod
# 빌드된 JAR 파일을 컨테이너로 복사
ARG JAR_FILE=build/libs/*SNAPSHOT.jar
RUN mv ${JAR_FILE} app.jar

# 컨테이너가 실행될 때 실행될 명령어 지정
# 오류남! 왜냐하면, spring.profiles.active=prod를 사용하려면, application.properties에 spring.profiles.active=prod를 추가해야함
ENTRYPOINT ["java", "-jar", "app.jar"]
#ENTRYPOINT ["java", "-Dspring.profiles.active=prod", "-jar","app.jar"]

.env 파일 포함 Dockerfile

# 1단계: Gradle 빌드 스테이지
FROM gradle:8.5-jdk17 AS build

# 작업 디렉토리 설정
WORKDIR /app

# Gradle 캐시를 위해 build.gradle과 settings.gradle 먼저 복사
COPY build.gradle settings.gradle ./
COPY gradle gradle
COPY gradlew ./

# gradlew 실행 권한 부여 (중요!)
RUN chmod +x ./gradlew

# 의존성 다운로드 (캐시 최적화)
RUN ./gradlew dependencies --no-daemon

# 소스 코드 복사
COPY src src

# JAR 파일 빌드
RUN ./gradlew build --no-daemon -x test

# 2단계: 실행 스테이지
FROM openjdk:17-jdk-slim AS runtime

# 작업 디렉토리 설정
WORKDIR /app

# 환경 변수 파일 복사
COPY .env .env

# 빌드 스테이지에서 생성된 JAR 파일 복사
COPY --from=build /app/build/libs/*.jar app.jar

# 포트 노출
EXPOSE 8080

# 애플리케이션 실행
ENTRYPOINT ["java", "-jar", "/app/app.jar"]

docker-compse.yml

version: "3.8"

services:
  deploy-db:
    image: mysql:8
    container_name: deploy-db
    env_file:
      - .env
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} # MySQL 루트 비밀번호
      MYSQL_DATABASE: ${MYSQL_DATABASE} # 기본 생성할 데이터베이스 이름
      MYSQL_USER: ${MYSQL_USER} # 사용자 이름
      MYSQL_PASSWORD: ${MYSQL_PASSWORD} # 사용자 비밀번호
      TZ: Asia/Seoul # 타임존 설정
    ports:
      - "3307:3306" # 호스트 포트 3307 -> 컨테이너 포트 3306
    #    volumes:
    #      - mysql_data:/var/lib/mysql # MySQL 데이터 영구 저장
    command: [
        "--character-set-server=utf8mb4", # 문자 셋 설정
        "--collation-server=utf8mb4_general_ci", # 문자 정렬 설정
      ]
    healthcheck:
      test:
        [
          "CMD",
          "mysqladmin",
          "ping",
          "-h",
          "localhost",
          "-u${MYSQL_USER}",
          "-p${MYSQL_PASSWORD}",
        ]
      timeout: 20s
      retries: 10
      interval: 30s
    networks:
      - app-network

  # Spring Boot 애플리케이션 서비스
  app:
    build:
      context: . # 현재 디렉토리를 빌드 컨텍스트로 사용
      dockerfile: Dockerfile # Dockerfile 위치
    container_name: spring_app
    env_file:
      - .env
    environment:
      # Spring Profile 설정
      - SPRING_PROFILES_ACTIVE=prod

      # host.docker.internal 사용 (Windows/Mac)
      - PROD_HOST=${PROD_HOST}

      # MySQL 데이터베이스 정보를 앱 컨테이너에도 전달
      - MYSQL_DATABASE=${MYSQL_DATABASE}
      - MYSQL_USER=${MYSQL_USER}
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
    ports:
      - "8080:8080" # 호스트 포트 8080 -> 컨테이너 포트 8080
    depends_on:
      deploy-db:
        condition: service_healthy # MySQL이 정상 동작할 때까지 대기
    restart: unless-stopped # 컨테이너 자동 재시작
    # host.docker.internal 사용 시 추가 설정
    extra_hosts:
      - "host.docker.internal:host-gateway"
    networks:
      - app-network

# 네트워크 정의
networks:
  app-network:
    driver: bridge

# MySQL 데이터 영구 저장을 위한 볼륨
volumes:
  mysql_data:

docker-compose 파일 실행

이 상태에서 docker-compose 파일을 실행해보겠습니다.

docker-compose up

⭐ Dockerfile을 새로 build하고 싶다면 빌드 옵션을 사용하면 됩니다.

docker-compose up -d --build

만약 다른 경로에 있거나 다른 파일명을 사용하고 싶을 경우 -f 옵션으로 docker-compose 파일을 지정해주면 됩니다.

docker-compose -f [다른 docker-compose.yml] up

# 예시
docker-compose -f docker-compose.v3.yml up

-d 옵션을 주면 백그라운드로 실행시킬 수 있습니다.

docker-compose -d  up

만약 docker-compose 실행을 닫고 싶다면

docker-compose down

이 명령어를 치면 됩니다.


log 명령어를 통해 올라간 springboot 프로젝트가 제대로 실행되는지 확인할 수 있습니다.

docker-compose logs 

제대로 실행되는지

postman 으로 api를 보내는 테스트를 진행해봅시다.

post : localhost:8080/api
get : localhost:8080/api/{id}

네, 둘다 제대로 진행되는지 확인했습니다.



참고

profile
배운 것을 기록합니다.

0개의 댓글