Docker compose란?
여러 개의 Docker Container를 정의하고 이들을 하나의 애플리케이션으로 관리할 수 있게 해주는 것
- 여기서는 두 개의 컨테이너를 동일 네트워크에 포함시킬 예정
- 네트워크는 별도로 설정하지 않으면 기본값인 bridge로 설정
- docker compose를 사용하면 자동으로 브릿지 네트워크가 생기고 두 컨테이너는 해당 네트워크에 종속
0. 시작 전 현재 Image와 Container 확인
- application.yml에서 server.port=8081로 포트 번호를 변경해준 modify-port-image 파일 한 개만 존재
docker-compose.yml 생성 및 mysql 설정
1. root 컨텍스트에 docker-compose.yml 파일 생성
2. docker-compose.yml 파일 작성
version: '3.8'
services:
db:
image: mysql:8.0
container_name: mysql-container
environment:
MYSQL_ROOT_PASSWORD : 1234
MYSQL_DATABASE: test
ports:
- "3306:3306"
- version : '3.8'
- Docker compose 파일의 버전
- services
- 서비스를 정의
- db
- 데이터베이스 서비스의 이름
- image : mysql:8.0
- 사용할 Docker Image를 지정, 여기서는 공식 MySQL Docker Image를 사용하여 8.0버전을 가져옴
- container_name : mysql-container
- 컨테이너 이름 지정
- ex) docker run --name <컨테이너 이름>에서 컨테이너 이름을 지정하던 것과 동일, 아래 결과에서 확인
- environment
- MySQL 컨테이너에 전달할 환경 변수 설정
- MySQL 컨테이너는 이 환경 변수를 읽어서 MySQL 서버를 설정
- MYSQL_ROOT_PASSWORD : MySQL root 사용자의 비밀번호 설정
- MYSQL_DATABASE : MySQL 데이터베이스의 이름 설정
- MYSQL_USER : MySQL 사용자의 이름 설정
- MYSQL_PASSWORD : MySQL 사용자의 비밀번호 설정
- ports
- 호스트와 컨테이너 간의 포트 매핑 정의
- - "3306:3306" → -"호스트 포트:컨테이너 포트"
- 단, 로컬 컴퓨터에 MySQL이 설치되어 있으면 3306포트를 사용 중이기 때문에 아래와 같은 에러 발생
- 호스트와 컨테이너 간의 포트 매핑 정의
호스트의 포트를 변경하여 다시 시작
- mysql의 이미지를 다운 받는데 약 30초 정도 소요
- Docker Image에 mysql 이미지 추가
Container에 나의 Root 이름과 같은 'simple-spring-boot'라는 컨테이너 추가
- 클릭 시 docker-compose.yml의 container_name : mysql-container로 설정했기에 동일한 이름의 컨테이너가 존재
3. 스프링(applicaiton.yml)과 mysql 연결
server:
port: 8081
spring:
datasource:
url: jdbc:mysql://localhost:3306/test
username: root
password: 1234
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.MySQLDialect
- docker-compose의 DB 정보를 작성해야 함
- 모든 설정이 끝난 뒤 가장 아래에서 수정하여 다시 시도
spring + mysql docker-compose 생성
1. docker-compose.yml 내용 변경
version: '3.8'
services:
myapp:
image: modify-port-image
container_name: springboot-container
build:
context: .
dockerfile: Dockerfile
depends_on:
db :
condition: service_healthy
ports:
- "8081:8081"
db:
image: mysql:8.0
container_name: mysql-container
environment:
MYSQL_ROOT_PASSWORD : 1234
MYSQL_DATABASE: test
ports:
- "3307:3306"
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -p1234 && sleep 5"]
interval: 5s
retries: 10
- 현재 서비스는 myapp, db 두 개의 서비스가 정의
- myapp
- Spring Boot 어플리케이션 서비스의 이름
- image : modify-port-image
- Spring Boot 어플리케이션에 사용할 Docker 이미지를 지정
- port가 8081로 설정된 modify-port-image로 지정
- build
- context : .
- docker-compose.yml이 있는 현재 디렉토리를 빌드 컨텍스트로 사용하겠다는 의미
- dockerfile : Dockerfile
- context로 지정된 디렉토리에 있는 Dockerfile을 사용하여 이미지를 빌드하겠다는 의미
- context : .
- depends_on : db
- db 서비스를 먼저 실행 시키라는 의미
- condition : service_healthy
- 의존할 서비스(db)의 상태가 건강한 지(정상 작동되는 서버인지) 확인
- healthcheck
- 아래의 내용들로 서버 상태 확인
- test, interval, retries : 5초 간격으로 10번 ping 보내서 상태를 확인
2. IntellJ에서 docker-compose.yml 실행 또는 터미널에서 docker compose up 명령어
- mysql-container가 실행 중이기 때문에 계속하여 health check를 함
- mysql-container의 상태가 Waiting → Healthy가 된 후 springboot-container가 Started로 변경
3. Postman을 이용하여 정상 작동 확인
Database를 연동한 것이기 때문에 제대로 DB에 저장이 되는 지 확인이 필요하기 때문에 간단한 CRUD 생성 필요
JPA와 Lombok 의존성 추가
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.projectlombok:lombok'
다음과 같이 간단하게 Service 없이 Entity, Dto, Controller, Repository만 구성
Docker Container가 켜져 있으면 서버포트를 바꾸지 않는 이상 충돌이 발생하여 반드시 끌 것
BoardController
package com.example.simplespringboottest.board;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/api/board")
@RequiredArgsConstructor
public class BoardController {
private final BoardRepository boardRepository;
@GetMapping("/board")
public ResponseEntity<List<BoardDto>> allList(){
List<Board> entityList = boardRepository.findAll();
List<BoardDto> dtoList = entityList.stream().map(BoardDto::new).collect(Collectors.toList());
System.out.println(dtoList);
if(!dtoList.isEmpty())
return new ResponseEntity<>(dtoList, HttpStatus.OK);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
@PostMapping("/board")
public ResponseEntity<String> addArticle(@RequestBody BoardDto boardDto){
Board board = new Board(boardDto);
Board res = boardRepository.save(board);
if(res != null)
return ResponseEntity.status(HttpStatus.OK).body("게시글 등록 완료");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
BoardRepository
package com.example.simplespringboottest.board;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BoardRepository extends JpaRepository<Board, Long> {
}
Board
package com.example.simplespringboottest.board;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Entity
@Getter
@NoArgsConstructor
public class Board {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String content;
public Board(BoardDto dto){
this.id = dto.getId();
this.title = dto.getTitle();
this.content = dto.getContent();
}
}
BoardDto
package com.example.simplespringboottest.board;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
public class BoardDto {
private Long id;
private String title;
private String content;
public BoardDto(Board board){
this.id = board.getId();
this.title = board.getTitle();
this.content = board.getContent();
}
}
Postman으로 정상 작동 확인
Docker compose로 확인
1. IntelliJ 서버 종료
2. 코드가 변경되었으므로 Docker를 처음부터 다시 진행
3. 빌드
4. docker build -t add-board-service-image . → 새로운 이미지 생성
5. 기존 컨테이너 삭제
- IntelliJ의 docker-compose.yml에서 재시작 또는 docker compose up 명령어
- 아래의 빨간 네모칸의 상태가 정지가 된 것이 있으면, 뭔가 문제가 발생했다는 의미
6. Postman 확인
※ 주의사항
application.yml 또는 application.properties의 url이 localhost로 설정하지 않도록 주의
docker-compose.yml의 enviroment가 도커의 mysql 인증 ID, Password, Database이기 때문에
application.yml을 모놀리식에서 사용하던 것처럼 하면 안 되고 docker-compose.yml에 맞춰주여야 함
단, 아래처럼 하면 build 시 실패가 나오긴 하지만 무시하고 진행
빌드 시 에러
docker-application.yml과 application.yml 설정 다르게하여 오류 발생 테스트
- application.yml 파일
- url, root, password 모두 나의 로컬에 맞춤
server:
port: 8081
spring:
datasource:
url: jdbc:mysql://localhost:3306/test
username: root
password: 1234
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.MySQLDialect
- docker-compose.yml 파일
- environment 정보를 docker 내부의 mysql에 생성할 정보로 생각하고 작성함
version: '3.8'
services:
myapp:
image: yml-mismatch-image
container_name: springboot-container
build:
context: .
dockerfile: Dockerfile
depends_on:
db :
condition: service_healthy
ports:
- "8081:8081"
db:
image: mysql:8.0
container_name: mysql-container
environment:
MYSQL_ROOT_PASSWORD : root
MYSQL_DATABASE: dockerdb
MYSQL_USER : user
MYSQL_PASWOORD : user
ports:
- "3307:3306"
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -p1234 && sleep 5"]
interval: 5s
retries: 10
1. 빌드
2. 설정 파일 변경되었으니 새로운 이미지 생성
- docker build -t yml-mismatch-image .
3. 도커 컨테이너 삭제
4. docker compose up
5. container 내부에서 spring 서비스 에러로 중단된 것 확인
- 대략 Database 연동 시 에러가 발생하였다는 문제
에러는 확인하였으니 application.yml 파일의 DB 연동 정보를 docker-compose.yml에 맞춤
1. 빌드
2. 설정 파일 변경되었으니 새로운 이미지 생성
- docker build -t yml-match-image .
3. 기존 컨테이너 삭제
4. docker compose up
5. 컨테이너 안의 두 서비스 모두 정상 작동 확인
6. Postman 확인
application.yml과 docker.compose.yml 변경 사항
'Docker' 카테고리의 다른 글
[Docker Compose] Spring Boot + MySQL + Redis (0) | 2024.08.21 |
---|---|
Docker 설치 및 환경 설정 (0) | 2024.08.09 |
Docker란? (0) | 2024.08.08 |
컨테이너 vs 가상머신(VM) (0) | 2024.08.08 |