[Springboot + MySQL + Docker] 도커를 이용한 멀티 컨테이너 환경 구성

Kim Dae Hyun·2021년 8월 18일
4

Docker

목록 보기
5/7

🔎 Springboot 프로젝트 설정

도커 환경에서 DB를 사용하는 애플리케이션 구동을 목표로 하므로 최대한 간단하게 프로젝트를 구성해볼께요.

springboot 프로젝트는 사용자에게 글을 입력받으면 DB에 저장하고 목록을 확인할 수 있는 아주 간단한 구조입니다.

Dependency

  • Spring Web
  • Spring Data JPA
  • MySQL Connector
  • Thymeleaf
  • Lombok

Entity - Repository - Controller - View 만을 가지는 구조입니다.

Entity

@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Contents {

    @Id @GeneratedValue
    private Long id;

    private String content;

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

Repository

public interface ContentsRepository extends CrudRepository<Contents, Long> {
}

Controller

@RequiredArgsConstructor
@Controller
public class HomeController {

    private final ContentsRepository contentsRepository;

    @GetMapping("/")
    public String home() {
        return "form";
    }

    @PostMapping("/")
    public String form(@ModelAttribute ContentRequest content) {
        contentsRepository.save(new Contents(content.getContent()));
        return "redirect:/";
    }

    @GetMapping("/list")
    public String list(Model model) {
        List<Contents> contents = new ArrayList<>();
        Iterable<Contents> contentsIterable = contentsRepository.findAll();
        contentsIterable.forEach(contents::add);

        model.addAttribute("contents", contents);
        return "list";
    }

    @Data
    @AllArgsConstructor
    static class ContentRequest{
        private String content;
    }
}

입력폼

[ form.html ]
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/" method="post">
    <input type="text" id="content" name="content">
    <button type="submit">등록</button>
</form>
<a href="/list">목록보기</a>
</body>
</html>

목록보기

[ list.html ]
<!DOCTYPE html>
<html xmlns:th="http://thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
  <table border="1">
    <thead>
      <tr>
        <td>ID</td>
        <td>Content</td>
      </tr>
    </thead>
    <tbody>
      <tr th:each="c : ${contents}">
        <td th:text="${c.id}"></td>
        <td th:text="${c.content}"></td>
      </tr>
    </tbody>
  </table>
</body>
<a href="/">글쓰기</a>
</html>

pom.xml

[ pom.xml ]
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.5.3</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.dhk</groupId>
	<artifactId>deploy</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>deploy</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

🔎 springboot 설정파일 (application.yml)

datasourceurl 부분에서 호스트 부분 설정을 잘 해주셔야 합니다.

현재 호스트가 db-mysql로 되어 있는데 이 호스트명은 앞으로 도커환경에서 mysql이 구동되는 컨테이너의 이름입니다.

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

🔎 도커 이미지 빌드를 위한 Dockerfile 작성

FROM openjdk:8-jdk-alpine
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]
  • FROM: 베이스 이미지 빌드하고자 하는 이미지의 기본 골격이 되는 이미지입니다.
  • ARG: 일종의 변수선언이라고 이해해도 될 것 같습니다. 패키징된 jar 파일을 저장합니다.
  • COPY: 실행가능한 패키징된 파일(jar)을 컨테이너 내부로 복사합니다.
  • ENTRYPOINT: 이미지 빌드시 실행되는 명령어입니다. 로컬에서 jar파일을 구동하는 것과 동일한 명령어를 넣어주면 됩니다.

🔎 springboot 프로젝트 패키징

현재 프로젝트는 maven을 사용하고 있기 때문에 mvn 명령어를 사용합니다.

mvn clean package -DskipTests

-DskipTests
이거 꼭 넣어주세요..
yml에서 도커에서 구동될 mysql의 컨테이너명을 넣어주는데 이 부분 때문에 패키징 시 테스트에 실패하는 것 같습니다..

위 명령어를 실행하면 프로젝트 디렉토리에 target이 생성되고 그 안에 jar 파일이 생성될 것 입니다.

이 때 생성되는 jar 파일을 도커 컨테이너 안으로 복사해줘서 컨테이너가 실행시킬 수 있도록 하는 것 입니다.


🔎 도커 네트워크 생성

도커 환경에서 여러 컨테이너를 동일한 환경에서 연동시키기 위해서 네트워크 설정이 필요합니다.

여기서는 MySQLspringboot app이 동일한 네트워크에서 구동되도록 해주어야 합니다.

일단 네트워크부터 하나 생성해줍니다.
$ docker network create springboot-mysql-net

생성된 네트워크는 아래 명령어로 확인 가능합니다.
$ docker network ls


🔎 도커 환경에서 MySQL 구동

도커 허브에서 mysql 이미지 가져오기
$ docker pull mysql:5.7

mysql 구동
--name: 컨테이너명 설정
-p: 포트매핑
--network: 네트워크 설정
-e: 환경변수 설정
-d: 백그라운드로 컨테이너 실행

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

🔎 도커 환경에서 springboot app 구동

Dockerfile을 이용한 이미지 빌드
$ docker build -t springboot-mysql:1.0

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

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

🌡 테스트

두 개 컨테이너 모두 백그라운드로 잘 실행되고 있습니다.

요청 및 DB 테스트

이렇게 여러 컨테이너를 구동시키기 위해 docker에서는 docker-compose 라는 기능을 제공합니다. docker-compose가 더 편하고 좋아서 저도 처음에는 docker-compose로 컨테이너 구동을 하려 했습니다..

근데 자꾸 이상한 부분에서 막혀서 어떤 구조로 돌아가는지 파악하기 위해 docker-compose를 사용하지 않고 구현해봤습니다..ㅎ
다음에는 동일한 app을 docker-compose로 구동하는 법을 알아볼께요.

감사합니다ㅎ 😄

profile
좀 더 천천히 까먹기 위해 기록합니다. 🧐

1개의 댓글

comment-user-thumbnail
2023년 10월 28일

안녕하세요 글 잘보고 있습니다.
지금 따라하면서 network 생성과 mysql과 프로젝트 이미지 생성까지 잘되고 네트워크 연결추가해서 컨테이너 생성도 이상없이 잘되서 실행중인걸 확인했는데, 프로젝트 컨테이너 로그 확인시 java.net.ConnectException: Connection refused이 발생하는데 혹시 이 상황에서는 무슨 설정이 잘못 되었는지 알수 있을까요?
명령어는 아래처럼 실행했습니다.
1. docker run --name onboarding-springboot -p 8080:8080 --network preonboarding-mysql-network -d onboarding-springboot
2. docker run --name mysql-container --network preonboarding-mysql-network -e MYSQL_ROOT_PASSWORD=XXXX -d -p 3306:3306 mysql:latest

답글 달기