[Docker] Spring 프로젝트 실행

chrkb1569·2023년 9월 10일
0

Docker

목록 보기
4/5
post-thumbnail

지난 시간에는 도커를 활용해서 간단한 자바 파일을 실행해봤는데, 오늘은 도커를 사용해서 간단한 Spring 서버를 띄워보도록 하겠습니다.

프로젝트 설계

일단 프로젝트 의존성은 다음과 같이 설정하겠습니다.

그리고 간단하게 데이터베이스에 정보를 저장하고 조회할 수 있도록 API를 설계해줍니다.

Domain

@Entity
@Getter
@Table(name = "TODO")
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ToDo {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "todo_id")
    private Long id;

    @Column(nullable = false)
    private String content;

    public ToDo(String content) {
        this.content = content;
    }
}

Service

@Service
@RequiredArgsConstructor
public class ToDoService {
    private final ToDoRepository toDoRepository;

    public List<ToDo> getList() {
        return toDoRepository.findAll();
    }

    public void saveList(String content) {
        ToDo toDo = new ToDo(content);
        toDoRepository.save(toDo);
    }
}

Controller

@RestController
@RequiredArgsConstructor
@RequestMapping("/api")
public class ToDoController {
    private final ToDoService toDoService;

    @GetMapping("/todo")
    @ResponseStatus(HttpStatus.OK)
    public List<ToDo> getList() {
        return toDoService.getList();
    }

    @PostMapping("/todo")
    @ResponseStatus(HttpStatus.CREATED)
    public void makeList(@RequestParam(name = "content") String content) {
        toDoService.saveList(content);
    }
}

요청 및 응답

다음처럼 로컬에 띄운 서버에 데이터를 생성 요청을 보낼 수 있으며,

데이터베이스에 저장된 데이터는 다음처럼 조회될 수 있습니다.

Dockerfile 설계

그럼 프로젝트가 어떻게 설계되었는지 대충 알아보았으니, 일단 Dockerfile을 만들어 보도록 하겠습니다.

일단 스프링 프로젝트를 실행하기 위해서는 프로젝트 폴더를 jar 파일로 변경해줄 필요가 있습니다.

다음처럼 프로젝트 폴더 내부로 이동해준 뒤,

$ ./gradlew build

다음 명령어를 통하여 jar 파일을 생성해줍니다.

생성된 jar 파일은 다음처럼 /build/libs 내부에 위치하고 있습니다.

jar파일을 생성했으니, 다음처럼 Dockerfile을 만들어줍니다.

openjdk 이미지를 통해 로컬에서 복사해온 jar 파일을 실행하도록 Dockerfile을 작성하였습니다.

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test
    username: "docker"
    password: "1234567890"
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL5InnoDBDialect!

일단은 이렇게 해서 컨테이너를 실행해보도록 하겠습니다.

아마 저와 같이 설정을 동일하게 하신 분들은 다음처럼 오류가 발생하는 것을 확인할 수 있습니다.

어떻게 보면 오류나는게 당연한건데,

저희 로컬 환경에서 Spring 서버를 구동하고 동작시킬 경우에는 로컬 환경에 데이터베이스가 설치되어 있었기 때문에 다음처럼 데이터베이스와 연동을 할 수 있었지만,

컨테이너의 환경에서는 다음처럼 별도의 데이터베이스가 존재하지 않기 때문에, 다음과 같이 오류가 발생하게 된 것입니다.

오류

그래서 원래 생각은 MySQL 이미지로 컨테이너를 생성한 뒤, 이를 통하여 Spring 서버와 데이터베이스가 데이터를 주고 받으면 되겠구나! 라는 생각을 하였으나...

제가 잘못 생각했던 부분이 있어서 이를 구현하는데에 시간이 좀 걸렸습니다.

일단 가장 큰 오류는 바로 컨테이너가 동일한 환경에서 생성될 것이라고 생각했던 부분이 가장 큰 오류였던 것 같습니다.

따라서, 아무리 localhost로 접근을 해보아도 동일한 네트워크 환경에 데이터베이스가 없기 때문에 이러한 오류가 발생했던 것입니다.

해결책?

제가 생각한 방식대로 컨테이너끼리 통신하는 환경을 구축하기 위해서는 사실 Docker-Compose를 사용하는 방식이 가장 간편하지만, docker network를 사용해서 구현할 수 있는 것 같아, 이 방법을 사용해보기로 결정하였습니다.

네트워크 생성

먼저, MySQL 컨테이너와 Spring 서버가 같이 동작할 수 있는 네트워크를 형성해야합니다.

$ docker network ls

다음의 명령어를 사용할 경우, 다음처럼 현재 도커에서 기본적으로 사용하고 있는 네트워크들의 리스트를 확인할 수 있습니다.

우리는 여기에 새로운 네트워크를 추가해 줄 것입니다.

$ docker network create {생성할 네트워크의 이름}

다음의 명령어를 통하여 새로운 네트워크를 생성할 수 있습니다.

저는 test라는 이름의 네트워크를 만들어보도록 하겠습니다.

MySQL 컨테이너 실행

일단 네트워크도 만들었으니, 이제는 MySQL 이미지를 가져와서 컨테이너를 실행해야하는데, 뭐 기본적으로 설정해야하는 부분들이 있을 수도 있으니 일단 docker hub에 들어가서 확인좀 해보겠습니다.

https://hub.docker.com/_/mysql

들어가서 살펴보다보면,

다음처럼 이미지를 사용하기 위해서는 어떠한 설정들을 해줘야하는지, 이를 명시하는 부분이 있는데, 별도의 환경 변수들을 통해서 MySQL에서 사용할 마스터 비밀번호, 생성할 사용자, 생성할 사용자의 비밀번호 등을 미리 설정한 상태에서 컨테이너를 동작할 수 있습니다.

일단 제가 yml 파일에서 설정했던 바로는
사용자 이름 -> docker
사용자 비밀번호 -> 1234567890
데이터베이스 이름 -> test

이렇게만 설정해주면 좋을 것 같네요.

$ docker run -d -p 3306:3306
--name chrkb1569 --rm
-e MYSQL_ROOT_PASSWORD=1234
-e MYSQL_USER=docker
-e MYSQL_PASSWORD=1234567890
-e MYSQL_DATABASE=test
--network test mysql

다음의 명령어를 사용해서 MySQL 컨테이너를 실행해줍니다.

-d -> 터미널을 백그라운드에서 실행함
-p -> 컨테이너에서 개방할 포트 설정
--name -> 실행할 컨테이너의 이름
--rm -> 컨테이너 종료시, 컨테이너를 바로 삭제함
MYSQL_ROOT_PASSWORD -> MySQL 마스터 비밀번호
MYSQL_USER -> MySQL에서 생성할 사용자 이름
MYSQL_PASSWORD -> MySQL에서 생성하는 사용자의 비밀번호
MYSQL_DATABASE -> 새롭게 생성할 데이터베이스 이름
--network -> 어떠한 네트워크에서 컨테이너를 동작시킬 것인가?

여기서 중요한 점은 --network 옵션을 통해서 MySQL 컨테이너가 실행될 네트워크를 지정할 수 있습니다.

$ docker inspect {컨테이너 이름}

다음의 명령어를 사용할 경우, 해당 컨테이너의 정보들을 확인할 수 있는데,

이는 현재 실행 중인 MySQL 컨테이너 정보이고,

이는 우리가 전에 생성하였던 네트워크에 대한 정보입니다.

자세히 살펴보면 Network ID가 test의 ID인 것을 확인할 수 있습니다.

그럼 MySQL 컨테이너가 잘 실행되었는지 확인해볼까요?

다음의 명령어를 사용할 경우, 다음처럼 컨테이너 내부로 진입할 수 있습니다.

다음처럼 MySQL에 접속을 할 수 있는데, 여기서 root의 비밀번호는 우리가 이전에 환경 변수로 설정해주었던 마스터 비밀번호를 의미합니다.

일단 우리가 원하는대로 잘 설정된 것을 확인할 수 있습니다.

Spring 컨테이너 실행

일단 프로젝트를 이미지로 만들기 전, 한가지 수정할 사항이 있습니다.

바로 yml 파일인데요

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test
    username: "docker"
    password: "1234567890"
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL5InnoDBDialect!

이게 기존의 yml 파일이었는데, 여기서 localhost 부분을 MySQL 컨테이너 이름으로 변경해줍니다.

컨테이너가 서로 동일한 네트워크에 존재한다면, 컨테이너의 이름을 통하여 해당 컨테이너와 통신할 수 있다는 도커의 특징으로 인해 다음과 같이 설정할 수 있습니다.

그리고 이를 이미지로 만든 뒤, 이를 다음의 명령어로 실행하면,

$ docker run -d -p 8080:8080
--name spring_container --rm
--network test springimage

다음처럼 잘 동작하는 것을 확인할 수 있습니다.

그런데 생각해보니 이렇게되면 제가 서버에 요청을 못보내더라구요 ㅋㅋㅋㅋㅋ

이럴거면 왜 API까지 다 구현했나 싶기도하고..

나중에 이 프로젝트는 배포까지 해봐야 할 것 같네요 ㅋㅋㅋ

일단은 데이터베이스랑 잘 연동되었다는 것만 확인할 것 같은데, 아까 MySQL 컨테이너에 접근했던 방식 그대로 사용해보면

다음처럼 별도의 테이블이 생성된 것을 확인할 수 있습니다.

중간에 컨테이너 통신 관련해서 오류가 발생한 덕분인지 이 게시글 하나를 작성하는데에 일주일정도 걸린 것 같네요 ㅠ

뭐 그래도 그덕에 컨테이너 네트워크 환경에 대해서 알 수 있었던 시간이었습니다.

다음에는 오늘 만들었던 프로젝트에 요청해볼 수 있도록 배포를 한 번 해보면 좋을 것 같아서 그 부분에 대해서 글을 작성할 것 같습니다.

감사합니다!

0개의 댓글

관련 채용 정보