프로젝트를 진행하며 사용해왔던 Docker Compose를 왜 사용하는지에 대해 알아보자
일단 Docker Compose는 여러개의 docker 컨테이너들을 하나의 서비스로 정의하고 묶음으로 관리할 수 있게하는 도구이다.
많이 사용했던 docker compose up
이 이 명령어이다.(포그라운드에서 실행)
# compose.yml
services: # docker compose에서는 하나의 컨테이너를 services라고 명한다.
my-web-server: # 내가 붙이고싶은 컨테이너의(서비스) 이름
container_name: webserver # 아래 설정된 이미지로 컨테이너를 생성할 때의 컨테이너 이름
image: nginx # 이미지
ports: # 매핑될 포트
- 80:80
이제 여기에 백그라운드에서 실행시키고 싶다면
docker compose up -d
Docker CLI(터미널 명령어)를 compose.yml 파일 코드로 수정해주는 사이트
compose.yml 파일 코드를 Docker CLI로 수정해주는 사이트
compose.yml
파일에서 정의한 설정들을 기반으로 컨테이너들을 실행docker compose up # 포그라운드
docker compose up -d # 백그라운드
docker compose 중지 및 삭제
docker compose down
compose.yml
로 설정된 컨테이너들 중 실행 중인 컨테이너를 확인
docker compose ps
compose.yml
로 설정된 컨테이너들 모두를 확인
docker compose ps -a
docker compose의 로그를 확인
`docker compose logs
이미지를 다시 빌드하고 실행시켜야 할 때(ex:수정 사항이 있는 프로젝트 빌드 시)
docker compose up --build
DockerHub에서 최신 이미지를 가져와 실행시키고 싶을 때
docker compose pull
# 컨테이너에 필요한 이미지를 찾을 때 원래는 로컬 환경에서 먼저 찾아보고, 없다면 DockerHub에서 가져오게되는데
# 만약 DockerHub에 최신 이미지 버전이 올라와서 그걸 이용해서 실행시키고 싶을 경우
원래 MySQL을 docker로 실행시켰을 때 명령어
docker run -e MYSQL_ROOT_PASSWORD=[비밀번호] -p 3306:3306 -v [경로]:[경로] -d mysql
이거를 docker compose로 실행해보자
#compose.yml
services:
my-db:
image: mysql
environment:
MYSQL_ROOT_PASSWORD: [비밀번호]
volumes:
- ./mysql_data:/var/lib/mysql # docker compose로는 상대경로 설정 가능
ports:
- "3306:3306"
docker compose up -d
제대로 실행됐는지 확인
이제 docker compose down
을 하더라도 볼륨 설정을 해놨기 때문에 데이터는 유지된다.
보통 프로젝트에서 스프링부터와 데이터베이스를 같이 사용하는데 이 때 Docker Compose를 이용한다.
의존성을 추가한 스프링 프로젝트를 만들고 실행한다.
// AppController
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AppController {
@GetMapping("/")
public String home() {
return "Hello World!";
}
}
# application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: password123
driver-class-name: com.mysql.cj.jdbc.Driver
# Dockerfile
FROM openjdk:17-jdk
COPY build/libs/*SNAPSHOT.jar /app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]
# compose.yml(수정 전)
services:
my-server:
build: .
ports:
- "8080:8080"
my-db:
image: mysql
environment:
MYSQL_ROOT_PASSWORD: password123
MYSQL_DATABASE: mydb
volumes:
- ./mysql_data:/var/lib/mysql
ports:
- "3306:3306"
여기서 스프링부트가 실행될 때 application.yml에 mysql이 켜져있지 않으면 에러가 발생하게 된다.
그러므로 compose.yml에 추가 설정을 해줘야한다.
my-db가 제대로 실행되고있는지 확인한 뒤에 my-server를 실행시킨다.
# compose.yml(수정 후)
services:
my-server:
build: .
ports:
- "8080:8080"
depends_on:
- my-db
my-db:
image: mysql
environment:
MYSQL_ROOT_PASSWORD: password123
MYSQL_DATABASE: mydb
volumes:
- ./mysql_data:/var/lib/mysql
ports:
- "3306:3306"
healthcheck:
test: ["CMD", "mysqladmin", "ping"]
interval: 5s
retries: 10
이제 docker compose ps
로 확인해보면
이상하게 my-db는 실행되고있지만 my-server는 보이지 않는다.
my-server 컨테이너의 로그를 확인해보면
Failed to initialize JPA EntityManagerFactory
라면서 DB가 실행되어있지 않아서 에러가 발생했다.
각각의 컨테이너는 고유한 네트워크망과 IP주소를 가지고 있다.
그 말은 스프링부트 서버 컨테이너의 입장에서
# application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: password123
driver-class-name: com.mysql.cj.jdbc.Driver
application.yml 파일에 localhost:3306로 설정해놨으므로 다른 컨테이너의 포트를 찾을 수 없는 것이다.
# application.yml
# ...
url: jdbc:mysql://my-db:3306/mydb
# ...
application.yml 파일에 localhost 대신
compose.yml에 설정한 서비스 이름을 찾아서 접근이 가능하다.
파일들을 수정하고 다시 빌드단계부터 compose up을 해보면
에러 없이 정상적으로 작동한다.