docker compose 를 활용한 컨테이너 관리
Docker Copmose란?
- 여러개의 Docker 컨테이너들을 하나의 서비스로 정의하고 구성해 하나의 묶음으로 관리할 수 있게 도와주는 툴
Docker Compose 를 사용하는 이유
- 여러 개의 컨테이너를 관리하는데 용이
- 여러 개의 컨테이너로 이루어진 복잡한 애플리케이션을 한 번에 관리할 수 있게 해준다.
- 여러 컨테이너를 하나의 환경에서 실행하고 관리하는데 도움이 된다.
- 복잡한 명령어로 실행시키던 걸 간소화 시킬 수 있음
- 이전에 MySQL 이미지를 컨테이너로 실행시킬 때 아래와 같은 명령어를 실행시켰다.
$ docker run -e MYSQL_ROOT_PASSWORD=password123 -p 3306:3306 -v /Users/Documents/Develop/docker-mysql/mysql_data:/var/lib/mysql -d mysql
- 하지만 Docker Compose 를 사용하면
- 단순히 compose.yml 을 정의하고 docker compose up 명령어만 실행시키면 된다.
- 예시
- Docker Compose (Nginx 설치 및 실행)
- Docker CLI 로 컨테이너를 실행시킬 때
$ docker run --name webserver -d -p 80:80 nginx
Docker Compose 로 컨테이너를 실행시킬 때
1.compose.yml 파일 작성하기
- compose.yml
services: # 도커 컴포즈에서는 하나의 컨테이너를 서비스라 부른다. 여러개의 서비스를 적기 전에 초반에 적는 명령어
my-web-server: # 내가 원하는 컨테이너에 붙이고 싶은 이름
container_name: web-server
image: nginx
ports:
- 80:80
services:
my-web-server:
container_name: webserver
image: nginx
ports:
- 80:80
- services:
- my-web-server : Docekr Compose에서 하나의 컨테이너를 서비스(service)라고 부른다. 이 옵션은 서비스에 이름을 붙이는 기능이다.
- container_name:
- web-server : 컨테이너를 띄울 때 붙이는 별칭이다. CLI에서 --name web-server 역할과 동일하다.
- image:
- nginx : 컨테이너를 실행시킬 때 어떤 이미지를 사용할 지 정의하는 명령어이다. $ docker run [이미지명]와 동일한 역할이다.
- ports:
- 포트 매핑은 어떻게 할 지를 설정하는 옵션이다. CLI에서-p 80:80 역할과 동일하다.
2. compose 파일 실행시키기
$ docker compose up -d
docker compose 로 실행시 명령어가 간소화 되었다.
- docker run --name webserver -d -p 80:80 nginx -> docker compose up -d
3. compose 실행 현황 보기
$ docker compose ps
$ docker ps
4. compose로 실행된 컨테이너 삭제
$ docker compose down
자주 사용하는 Docker Compose CLI 명령어
compose.yml에서 정의한 컨테이너 실행
$ docker compose up # 포그라운드에서 실행
$ docker compose up -d # 백그라운드에서 실행
Docker Compose로 실행시킨 컨테이너 확인하기
# compose.yml에 정의된 컨테이너 중 실행 중인 컨테이너만 보여준다.
$ docker compose ps
# compose.yml에 정의된 모든 컨테이너를 보여준다.
$ docker compose ps -a
Docker Compose 로그 확인하기
# compose.yml에 정의된 모든 컨테이너의 로그를 모아서 출력한다.
$ docker compose logs
컨테이너를 실행하기 전에 이미지 재빌드하기
$ docker compose up --build # 포그라운드에서 실행
$ docker compose up --build -d # 백그라운드에서 실행
- compose.yml에서 정의한 이미지 파일에서 코드가 변경 됐을 경우, 이미지를 다시 빌드해서 컨테이너를 실행시켜야 코드 변경된 부분이 적용된다. 그러므로 이럴 때에는 --build 옵션을 추가해서 사용해야 한다.
참고 : docker compose up vs docker compose up --build
- docker compose up : 이미지가 없을 때만 빌드해서 컨테이너를 실행시킨다. 이미지가 이미 존재하는 경우 이미지를 빌드하지 않고 컨테이너를 실행시킨다.
- docker compose up --build : 이미지가 있건 없건 무조건 빌드를 다시해서 컨테이너를 실행시킨다.
이미지 다운받기 / 업데이트하기
$ docker compose pull
- 로컬에 있어도 무조건 도커 허브에서 다운받아짐.
- compose.yml에서 정의된 이미지를 다운 받거나 업데이트 한다.
- 로컬 환경에 이미지가 없다면 이미지를 다운 받는다.
- 로컬 환경에 이미 이미지가 있는데, Dockerhub의 이미지와 다른 이미지일 경우 이미지를 업데이트 한다.
Docker Compose에서 이용한 컨테이너 종료하기
$ docker compose down
Docker Compose 로 프론트엔드(HTML, CSS, Nginx) 실행시키기
- compose.yml
services:
my-web-server:
container_name: web-server
image: nginx
ports:
- 80:80
- Dockerfile
- 현재경로에 생성한 파일 컨테이너의 /usr/share/nginx/html 경로로 복사하기
FROM nginx
COPY . /usr/share/nginx/html
도커 컴포즈 없이 빌드시
- docker run --name webserver -d -p 80:80 nginx
웹서버 이름으로 nginx 이미지를 실행시켜라
compose.xml 생성시
services:
my-web-server:
build: . # 현재 도커 파일을 기준으로 빌드한 이미지를 씀
container_name: web-server
ports:
- 80:80
명령어 실행
docker compose up -d --build
- 매번 이미지를 빌드해주라는 의미로 build 옵션 붙여줌
Docker Compose 로 MySQL 실행시키기
Docker CLI 로 컨테이너를 실행시킬 때
$ docker run -e MYSQL_ROOT_PASSWORD=pwd1234 -p 3306:3306 -v /Users/Documents/Develop/docker-mysql/mysql_data:/var/lib/mysql -d mysql
- 주석버전
docker run \
-e MYSQL_ROOT_PASSWORD=pwd1234 \ # MySQL root 계정 비밀번호 설정 (환경 변수)
-p 3306:3306 \ # 호스트의 3306 포트를 컨테이너의 3306 포트에 매핑 (MySQL 기본 포트)
-v /Users/Documents/Develop/docker-mysql/mysql_data:/var/lib/mysql \ # 호스트의 디렉토리를 컨테이너의 데이터 디렉토리에 마운트 (데이터 영속성 보장)
-d \ # 컨테이너를 백그라운드(detached) 모드로 실행
mysql # 사용할 이미지 이름 (기본적으로 Docker Hub에서 mysql:latest 이미지 사용)
위의 CLI 를 compose.yml 파일로 바꿔봅시다
- compose.yml
services:
my-db:
image: mysql
environment:
MYSQL_ROOT_PASSWORD: pwd1234
volumes:
- ./mysql_data:/var/lib/mysql
ports:
- 3306:3306
compose 파일 실행시키기
- docker-compose.yml 파일을 읽어서,
- 정의된 모든 서비스(예: 웹 서버, DB 등)를 한 번에 생성 및 실행한다.
docker compose up -d
docker compose \ # docker-compose.yml 파일을 기준으로 여러 컨테이너를 정의하고 실행
up \ # 컨테이너를 생성하고 실행 (필요한 이미지가 없으면 자동으로 pull)
-d # detached 모드: 터미널을 점유하지 않고 백그라운드에서 실행
compose 실행 현황 보기
$ docker compose ps
$ docker ps
컨테이너 접속
>docker exec -it 26 bash
로그인 되나 확인해보기
mysql -u root -p
볼륨 마운트 확인
볼륨도 잘 마운트된것을 확인할 수 있다.
Docker Compose로 백엔드(Spring Boot) 실행시키기
빌드된 스프링부트 jar 파일을 기반으로 Dockerfile 를 만들어보자.
Spring Boot 프로젝트 빌드하기
$ ./gradlew clean build
Dockerfile 작성하기
Dockerfile
FROM openjdk:21-jdk
COPY build/libs/*SNAPSHOT.jar /app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]
FROM openjdk:21-jdk # Java 21 JDK 기반 이미지 사용
COPY build/libs/*SNAPSHOT.jar /app.jar # 빌드된 JAR 파일을 컨테이너로 복사
EXPOSE 8080 # 애플리케이션이 사용하는 포트 (문서화 목적)
ENTRYPOINT ["java", "-jar", "/app.jar"] # 컨테이너 실행 시 JAR 실행
참고) compose를 작성하지 않고 Docker CLI로 실행시킬 때
$ docker build -t hello-server . # 이미지 생성
$ docker run -d -p 8080:8080 hello-server # 컨테이너 실행 (호스트:컨테이너 포트 연결)
도커파일 생성 후 이미지를 만들기 위해
docker build -t my-server .
명령어를 수행하였다.
하지만 컴포즈를 쓰면 해당 명령어가 필요 없어진다.
Docker Compose를 사용하면 docker build와 docker run을 한 번에 처리할 수 있음.
compose.yml
services:
my-server:
build: . # 현재 디렉토리에서 Dockerfile을 사용하여 이미지 빌드
ports:
- "8080:8080" # 호스트:컨테이너 포트 매핑
build : . : compose.yml 이 존재하는 디렉토리(.) 에 있는 Docker 로 이미지를 생성해 컨테이너를 띄우겠다는 의미이다.
compose 파일 실행시키기
$ docker compose up -d --build
- --build : 서비스 실행 전에 이미지를 다시 빌드함
- -d : 백그라운드(detached) 모드로 실행
핵심 요약
항목 | 설명 |
build: . | 현재 디렉토리의 Dockerfile을 사용해 이미지 생성 |
docker compose up | 이미지 빌드 + 컨테이너 실행 |
EXPOSE vs ports | EXPOSE는 내부 포트 명시 (문서화), ports는 실제 외부 연결 설정 |
Spring Boot, MySQL 컨테이너 동시에 띄워보기
Spring Boot, MySQL 컨테이너 동시에 띄워보기
스프링부트 의존성 추가
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation 'mysql:mysql-connector-java:8.0.33'
implementation 'org.springframework.boot:spring-boot-starter-web'
간단한 코드 작성
AppController
@RestController
public class AppController {
@GetMapping("/")
public String home() {
return "Hello, World!";
}
}
application.yml에 DB 연결을 위한 정보 작성하기
application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: pwd1234
driver-class-name: com.mysql.cj.jdbc.Driver
불필요한 테스트 코드 삭제
Dockerfile 작성하기
Dockerfile
FROM openjdk:21-jdk
COPY build/libs/*SNAPSHOT.jar /app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]
compose.yml 파일 작성하기
compose.yml
services:
my-server:
build: .
ports:
- 8080:8080
depends_on:
my-db:
condition: service_healthy
my-db:
image: mysql
environment:
MYSQL_ROOT_PASSWORD: pwd1234
MYSQL_DATABASE: mydb
volumes:
- ./mysql_data:/var/lib/mysql
ports:
- 3306:3306
healthcheck:
test: ["CMD", "mysqladmin", "ping"]
interval: 5s
retries: 10
compose.yml(주석)
services:
my-server:
build: . # 현재 디렉토리에 있는 Dockerfile로 my-server 이미지를 빌드합니다.
ports:
- 8080:8080 # 호스트의 8080 포트를 컨테이너의 8080 포트에 매핑합니다 (웹 애플리케이션 접근용).
depends_on:
my-db:
condition: service_healthy # my-db 서비스가 healthcheck를 통과할 때까지 기다린 후 my-server를 시작합니다.
my-db:
image: mysql # 공식 MySQL 이미지를 사용합니다 (명시된 버전 없으므로 기본 latest 사용).
environment:
MYSQL_ROOT_PASSWORD: pwd1234 # root 사용자 비밀번호 설정.
MYSQL_DATABASE: mydb # 컨테이너 시작 시 자동으로 생성될 데이터베이스 이름.
volumes:
- ./mysql_data:/var/lib/mysql # 호스트의 ./mysql_data 디렉토리를 MySQL 데이터 저장소에 마운트 (데이터 영속화).
ports:
- 3306:3306 # 호스트의 3306 포트를 컨테이너의 3306 포트에 매핑 (DB 접근용).
healthcheck:
test: ["CMD", "mysqladmin", "ping"] # MySQL 서버가 살아있는지 확인하는 health check 명령.
interval: 5s # health check를 5초마다 수행합니다.
retries: 10 # 최대 10번 health check를 재시도합니다 (모두 실패 시 unhealthy 처리).
Spring Boot 프로젝트 빌드하기
$ ./gradlew clean build
compose 파일 실행시키기
$ docker compose up -d --build
compose 실행 현황 보기
$ docker compose ps
$ docker ps
$ docker logs [Container ID]
Spring Boot 컨테이너의 로그를 열어보면 아래와 같이 에러 메시지가 떠있다. 아래 에러 메시지는 DB와 연결이 제대로 이루어지지 않았을 때 발생하는 에러이다.
mysql 컨테이너는 잘 떴지만 스프링 부트 jar 는 뜨지 않았다.
mysql 이 잘 떳는지 GUI 로 확인해보자!
왜 db 까지 잘 생성되었지만 스프링 웹에서 연결하지 못했을까?
컨테이너로 실행시킨 Spring Boot가 MySQL에 연결이 안 되는 이유
각각의 컨테이너는 자신만의 네트워크망과 IP 주소를 가지고 있다. 호스트 컴퓨터 입장에서 localhost는 호스트 컴퓨터를 가리키지만, Spring Boot 컨테이너 입장에서 localhost는 Spring Boot 컨테이너를 가리킨다.
Spring Boot 의 코드 작성 시 DB 정보를 localhost 로 입력하였따.
Spring Boot 가 실행되는 환경인 컨테이너 입장에서 localhost:3306 라는 주소는, Spring Boot 컨테이너 내부에 있는 3306번 포트에 연결을 시도하게 되었다.
하지만 Spring Boot 가 실행되는 컨테이너 내부의 3306 포트는 아무것도 실행되고 있지 않다.
application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: pwd1234
driver-class-name: com.mysql.cj.jdbc.Driver
그럼 어떻게 Spring Boot의 컨테이너에서 다른 컨테이너에 존재하는 MySQL에 연결을 할 수 있을까?
compose.yml에서 정의한 Service 이름으로 서로 통신할 수 있다. 바로 예시로 알아보자.
Spring Boot의 DB 정보를 아래와 같이 수정한 뒤 시도해보기
application.yml
spring:
datasource:
url: jdbc:mysql://my-db:3306/mydb
username: root
password: pwd1234
driver-class-name: com.mysql.cj.jdbc.Driver
우리가 이전에 작성했던 compose.yml을 보면 각 컨테이너에 service 이름(my-server, my-db)을 작성했었다.
compose.yml
services:
my-server:
build: .
ports:
- 8080:8080
depends_on:
my-db:
condition: service_healthy
my-db:
image: mysql
environment:
MYSQL_ROOT_PASSWORD: pwd1234
MYSQL_DATABASE: mydb
volumes:
- ./mysql_data:/var/lib/mysql
ports:
- 3306:3306
healthcheck:
test: [ "CMD", "mysqladmin", "ping" ]
interval: 5s
retries: 10
이 service 이름이 컨테이너의 주소를 뜻한다. 해당 컨테이너의 IP 주소와 같은 역할을 한다.
위와 같이 코드를 수정한 뒤에 다시 한 번 컨테이너를 실행시켜보자.
$ ./gradlew clean build
$ docker compose down
$ docker compose up --build -d
$ docker ps # 정상적으로 Spring Boot, MySQL이 실행된 걸 확인할 수 있다.
참고자료 : 비전공자도 이해할 수 있는 Docker 입문/실전