
Docker Compose는 여러 개의 Docker 컨테이너들을 하나의 서비스로 정의하고 구성하여, 복잡한 애플리케이션을 하나의 설정 파일로 관리할 수 있도록 도와주는 도구입니다.
예를 들어, 웹 애플리케이션을 구성할 때, 웹 서버, 데이터베이스, 캐시 등 여러 서비스가 필요할 수 있습니다. 이때 각 서비스를 별도의 컨테이너로 실행하고 관리하려면 여러 명령어를 각각 입력해야 하고, 설정이 복잡해질 수 있습니다. Docker Compose를 사용하면, 각 컨테이너의 설정과 네트워크, 볼륨, 환경 변수 등을 하나의 설정 파일(docker-compose.yml)에 작성하여 한 번에 구성하고 실행할 수 있습니다.
Docker Compose는 여러 개의 컨테이너로 구성된 복잡한 애플리케이션을 쉽게 관리할 수 있도록 도와줍니다. 각각의 컨테이너를 일일이 관리할 필요 없이, 한 곳에 정의된 설정 파일을 통해 모든 컨테이너를 일괄적으로 실행할 수 있습니다. 예를 들어, 웹 서버, 데이터베이스, 캐시 서버 등 여러 서비스가 필요한 경우, Docker Compose로 각 서비스를 하나의 환경에서 간편하게 구성하고 관리할 수 있습니다.
기존에는 MySQL 같은 이미지를 실행할 때 다음과 같은 긴 명령어를 입력해야 했습니다:
$ docker run -e MYSQL_ROOT_PASSWORD=password123 -p 3306:3306 -v /Users/admin/Documents/docker-mysql/mysql_data:/var/lib/mysql -d mysql
이와 같은 긴 명령어를 기억하고 입력하는 것은 번거롭고, 오타가 날 확률도 높아집니다. 하지만 Docker Compose에서는 설정 파일에 필요한 옵션을 미리 정의해 두기 때문에, 매번 복잡한 명령어를 입력할 필요 없이, 단순히 다음과 같이 명령어 한 줄만 입력하면 됩니다:
$ docker-compose up
이 명령어 하나로 모든 컨테이너가 설정된 환경에 맞춰 자동으로 실행됩니다. 복잡한 설정을 한 번에 정의하고, 단순한 명령어로 실행할 수 있어, 배포 및 관리를 더욱 효율적으로 할 수 있습니다.
현재 경로 확인
ls: 현재 디렉터리에 docker-compose.yml 파일이 있는지 경로와 파일을 확인합니다. Docker Compose 명령어는 이 파일이 위치한 경로에서 실행해야 올바르게 작동합니다.
ls
컨테이너 상태 확인
docker compose ps: 현재 Docker Compose 파일로 실행 중인 컨테이너의 상태를 확인합니다. 각 컨테이너의 이름, 상태, 포트 매핑 정보 등을 확인할 수 있습니다.
docker compose ps
docker compose ps -a: 모든 컨테이너(실행 중이거나 종료된 것 포함)의 상태를 표시합니다. 종료된 컨테이너까지 모두 확인하고자 할 때 사용합니다.
docker compose ps -a
컨테이너 로그 확인
docker compose logs: 실행 중인 모든 컨테이너의 로그를 출력하여 확인합니다. 이를 통해 컨테이너에서 발생하는 로그를 모니터링할 수 있습니다.
docker compose logs
컨테이너 실행
docker compose up: Docker Compose 파일에 정의된 컨테이너를 실행합니다. 처음 실행 시 이미지가 없으면 자동으로 빌드합니다. -d 옵션 없이 실행하면 터미널에 로그가 출력됩니다.
docker compose up
docker compose up -d: 백그라운드에서 컨테이너를 실행합니다. -d 옵션을 추가하여 터미널이 실행 상태에서 자유롭게 유지되도록 합니다.
docker compose up -d
이미지 새로 빌드 후 실행
docker compose up --build: 기존에 빌드된 이미지가 있더라도 이미지를 새로 빌드한 후 컨테이너를 실행합니다. 코드나 설정이 변경된 후 새로 빌드된 이미지를 적용하고자 할 때 사용합니다.
docker compose up --build
이미지 다운로드
docker compose pull: Docker Compose 파일에 정의된 이미지를 미리 다운로드합니다. 이미지가 최신 상태인지 확인하거나, 실행 전에 필요한 이미지를 준비할 때 유용합니다.
docker compose pull
컨테이너 종료 및 환경 정리
docker compose down: 현재 실행 중인 모든 컨테이너를 종료하고, 네트워크와 볼륨도 함께 삭제하여 환경을 정리합니다. 이후 재실행할 준비를 할 수 있습니다.
docker compose down
Docker Compose 파일을 하나 생성한 후 다음과 같이 내용을 작성합니다.
services: # 여러 서비스를 정의하는 시작 부분
my-web-server: # 서비스 이름. 사용자 지정 이름으로, 여러 서비스가 있을 때 구분하는 데 사용
container_name: web-server # 컨테이너 이름을 'web-server'로 지정 (이름을 직접 설정하여 관리 용이)
image: nginx # 'nginx' 이미지를 기반으로 컨테이너 생성 (nginx 웹 서버를 사용)
ports:
- 80:80 # 호스트의 80번 포트를 컨테이너의 80번 포트와 연결 (외부에서 접근 가능하게 함)
Docker Compose는 YAML(yml) 파일 형식을 사용합니다. YAML(yml) 파일에서는 들여쓰기를 통해 계층 구조를 나타냅니다. 각 들여쓰기는 더 깊은 수준의 정보를 의미하며, 이를 통해 데이터 간의 종속 관계나 구조를 표현할 수 있습니다.
YAML에서 들여쓰기가 중요한 이유는 중괄호나 괄호 없이 들여쓰기를 기준으로만 데이터를 구분하기 때문입니다. 잘못된 들여쓰기는 파일 구문을 인식하지 못하게 하고 오류를 발생시킵니다.
이러한 점을 고려하여 Docker Compose 파일을 작성할 때는 들여쓰기에 주의하도록 합니다.


Docker Compose 파일을 하나 생성한 후 다음과 같이 내용을 작성합니다.
services: # Docker Compose의 서비스 정의를 시작
my-db: # MySQL 데이터베이스 서비스 이름 정의
image: mysql # MySQL 공식 이미지를 사용
environment: # 환경 변수 설정
MYSQL_ROOT_PASSWORD: pwd1234 # MySQL 루트 사용자 비밀번호 설정
volumes: # 호스트와 컨테이너 간의 데이터 공유를 위해 볼륨 설정
- ./mysql_data:/var/lib/mysql # 호스트의 ./mysql_data 디렉토리를 MySQL 데이터베이스 파일이 저장되는 경로에 마운트
ports: # 포트 매핑 설정
- 3306:3306 # 호스트의 3306 포트를 컨테이너의 3306 포트에 매핑하여 외부에서 MySQL에 접근 가능하도록 설정
docker compose up명령어는 도커 컴포즈 파일이 있는 디렉터리에서 실행해야 합니다.
이유는 Docker Compose가 현재 경로에 있는docker-compose.yml파일을 찾아서 그 내용을 기준으로 컨테이너를 설정하고 실행하기 때문입니다. 따라서,ls명령어로 파일이 있는지 확인한 후 해당 경로에서docker compose up을 실행하면 원하는 설정으로 컨테이너를 시작할 수 있습니다.

스프링부트 프로젝트를 생성한 후 도커 파일과 도커 컴포즈 파일을 모두 생성하여 작성합니다.
도커 파일의 내용은 다음과 같이 작성합니다.
FROM openjdk:17-jdk
COPY build/libs/*SNAPSHOT.jar /app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]
도커 컴포즈 파일의 내용은 다음과 같이 작성합니다.
services: # Docker Compose 서비스 정의
my-server: # Spring Boot 애플리케이션 서버 정의
build: . # 현재 디렉터리에 있는 Dockerfile을 기반으로 이미지를 빌드
ports: # 포트 매핑 설정
- 8080:8080 # 호스트의 8080 포트를 컨테이너의 8080 포트에 매핑하여 외부에서 접근 가능하도록 설정
도커 컴포즈 파일 내용 중
build: .으로 작성하는 이유는 다음과 같습니다.
build: .에서.은 현재 디렉터리를 의미합니다. 이 설정은 Docker Compose가 현재 디렉터리에 있는 Dockerfile을 찾아서 그 파일을 기준으로 이미지를 빌드하라는 뜻입니다.
즉, 현재 디렉터리에 Dockerfile이 위치해 있고, 이 파일을 기반으로my-server서비스를 빌드하여 실행하기 위해build: .를 사용한 것입니다.
터미널에서 도커 컴포즈 빌드 명령어는 다음과 같이 작성합니다.
docker compose up -d --build
터미널 실행 명령어에서
--build부분을 추가한 이유는 다음과 같습니다.
docker compose up -d --build에서--build옵션은 컨테이너를 실행하기 전에 이미지를 새로 빌드하라는 뜻입니다.
이 옵션을 추가하는 이유는, Dockerfile이나 프로젝트 파일에 변경 사항이 있을 때 최신 상태로 이미지를 다시 빌드하고 컨테이너를 실행하기 위함입니다.
따라서,--build옵션을 사용하면 항상 최신 코드와 설정이 반영된 상태에서 컨테이너가 실행됩니다.
결과를 확인해보면, 컨테이너가 잘 실행되고 접속도 잘 되는 것을 확인할 수 있습니다.


Docker CLI 명령어와 compose.yml 파일은 서로 변환이 가능합니다. 이를 통해 CLI로 작성된 명령어를 compose.yml 파일로, 또는 compose.yml 파일 내용을 CLI로 쉽게 변환할 수 있습니다. 이를 편리하게 변환해주는 웹사이트는 다음과 같습니다:
Docker CLI → compose.yml로 변환하기
docker-compose.yml 형식으로 자동 변환해 줍니다.compose.yml → Docker CLI로 변환하기
docker-compose.yml 파일의 내용을 CLI 명령어로 변환하여 Docker 명령어 형태로 나타내 줍니다.이러한 도구를 활용하면 Docker 환경 설정을 더 쉽게 관리할 수 있습니다.
services:
my-db: # MySQL 컨테이너 이름 정의
image: mysql # 사용할 이미지 설정 - MySQL 공식 이미지
environment: # 환경 변수 설정
MYSQL_ROOT_PASSWORD: pwd1234 # MySQL 루트 사용자 비밀번호 설정
volumes: # 호스트와 컨테이너 간의 데이터 영구 저장소 설정
- ./mysql_data:/var/lib/mysql # 호스트의 mysql_data 폴더를 컨테이너의 /var/lib/mysql에 마운트
ports: # 호스트와 컨테이너 간의 포트 매핑
- 3306:3306 # 호스트의 3306 포트를 컨테이너의 3306 포트에 연결
my-cache-server: # Redis 컨테이너 이름 정의
image: redis # 사용할 이미지 설정 - Redis 공식 이미지
ports: # 호스트와 컨테이너 간의 포트 매핑
- 6379:6379 # 호스트의 6379 포트를 컨테이너의 6379 포트에 연결

FROM openjdk:17-jdk
COPY build/libs/*SNAPSHOT.jar /app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]
services:
my-server: # Spring Boot 애플리케이션 컨테이너 정의
build: . # 현재 디렉토리에서 Dockerfile을 사용해 빌드
ports:
- 8080:8080 # 호스트의 8080 포트를 컨테이너의 8080 포트에 연결
depends_on:
my-db: # my-db 컨테이너가 먼저 실행되어야 함
condition: service_healthy # my-db가 'healthy' 상태가 된 후에 my-server 시작
my-db: # MySQL 데이터베이스 컨테이너 정의
image: mysql # MySQL 공식 이미지 사용
environment:
MYSQL_ROOT_PASSWORD: pwd1234 # MySQL 루트 계정 비밀번호 설정
MYSQL_DATABASE: mydb # 자동 생성할 데이터베이스 이름 설정
volumes:
- ./mysql_data:/var/lib/mysql # 호스트의 mysql_data 폴더를 컨테이너의 MySQL 데이터 폴더와 연결
ports:
- 3306:3306 # 호스트의 3306 포트를 컨테이너의 3306 포트에 연결
healthcheck: # 컨테이너 상태 점검 설정
test: ["CMD", "mysqladmin", "ping"] # MySQL 서버가 실행 중인지 확인
interval: 5s # 5초마다 상태 확인
retries: 10 # 최대 10회 재시도
❗️Docker Compose에서
depends_on과healthcheck명령어를 사용한 이유
depends_on과healthcheck명령어는 Docker Compose에서 컨테이너 간의 시작 순서와 연결 상태를 관리하기 위해 사용됩니다. 이를 통해 Spring Boot 애플리케이션이 MySQL 데이터베이스가 준비된 상태일 때 실행되도록 보장할 수 있습니다.
이 설정을 통해 Spring Boot 애플리케이션이 MySQL 데이터베이스가 완전히 실행되고 연결 가능한 상태일 때만 시작하게 되어 오류 발생을 방지할 수 있습니다.
- 데이터베이스 연결 우선 요구: Spring Boot 애플리케이션은 시작할 때 데이터베이스와 연결이 필요합니다. 만약 MySQL이 완전히 실행되지 않은 상태라면 연결에 실패해 애플리케이션이 오류를 발생시킬 수 있습니다.
depends_on명령어: 이 명령어는my-server서비스가my-db서비스가 먼저 실행되어야 한다는 의존성을 정의합니다. 하지만 Docker Compose v2에서는 단순히depends_on으로 컨테이너가 준비된 상태임을 보장하지 않기 때문에healthcheck와 함께 사용해 더 안정적인 실행을 보장할 수 있습니다.healthcheck명령어:healthcheck는my-db컨테이너의 상태를 확인해 애플리케이션이 실행 가능한 상태인지 판단하게 합니다. 위의 설정에서는mysqladmin ping명령어를 사용해 MySQL 서버가OK상태인지 확인합니다. 만약my-db서비스가 준비되지 않은 상태라면my-server는 기다렸다가 데이터베이스가 준비된 후에 실행됩니다.
결과를 확인해 보면, MySQL 컨테이너는 정상적으로 실행되었으나, 스프링 부트 애플리케이션 컨테이너는 종료된 것을 확인할 수 있습니다.

docker logs [컨테이너 아이디] 명령어로 에러 내용을 확인해 보니, 스프링 부트 애플리케이션이 MySQL 컨테이너에 접근할 수 없어 연결이 거부된 에러가 발생한 것을 확인할 수 있습니다.


Docker 환경에서 각 컨테이너는 자신만의 네트워크와 IP 주소를 가지고 있습니다. 이를 통해 서로 독립적인 네트워크 공간을 가지며, 동일한 호스트에서 격리된 상태로 동작하게 됩니다.
localhost: 호스트 컴퓨터에서 localhost는 호스트 컴퓨터 자체를 가리킵니다.localhost: Spring Boot가 실행되는 컨테이너 내부에서 localhost는 Spring Boot 컨테이너 자체를 가리킵니다.application.yml 파일에서 MySQL 데이터베이스 URL을 아래와 같이 설정했습니다:
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
이 설정에서 localhost:3306을 사용하고 있는데, 이는 Spring Boot 컨테이너 내부의 localhost 3306번 포트를 가리킵니다. 그러나 Spring Boot 컨테이너 내부의 3306번 포트에는 MySQL이 실행되고 있지 않기 때문에 MySQL 데이터베이스와 연결할 수 없게 됩니다.
Docker Compose 네트워크를 활용해, Spring Boot 애플리케이션이 MySQL 컨테이너에 접근할 수 있도록 MySQL 서비스의 이름을 사용해야 합니다.
Docker Compose에서 각 컨테이너는 같은 네트워크 내에서 compose.yml 파일에 정의된 서비스 이름을 통해 서로를 식별하고 통신할 수 있습니다. 즉, application.yml 파일에서 localhost 대신 compose.yml에서 정의한 MySQL 서비스 이름(my-db)을 사용하면 Spring Boot와 MySQL이 같은 네트워크 내에서 통신할 수 있게 됩니다.
my-db라는 이름으로 MySQL을 참조할 수 있습니다.application.yml 파일에서 localhost 대신 my-db를 사용하면 MySQL과 정상적으로 연결됩니다."Docker 환경에서 각 컨테이너는 자신만의 네트워크와 IP 주소를 가지고 있습니다"라는 설명과 "Docker Compose에서 각 컨테이너는 같은 네트워크 내에서 compose.yml 파일에 정의된 서비스 이름을 통해 서로를 식별하고 통신할 수 있습니다"라는 설명은 논리적으로 맞지 않는 것처럼 보일 수 있습니다. 이를 이해하기 위해 Docker Compse의 네트워크 구조에 대해 이해할 필요가 있습니다.
Docker Compose에서 말하는 "같은 네트워크 내"는 Docker Compose가 자동으로 생성하는 가상 네트워크를 의미합니다.
docker-compose up을 실행하면, 모든 서비스는 기본적으로 같은 네트워크에 연결됩니다. 이 네트워크는 컨테이너 간의 통신을 위한 가상 네트워크로, Docker가 자동으로 생성합니다.localhost는 그 컨테이너 자체를 가리킵니다.compose.yml에 정의된 서비스들끼리 자동으로 같은 가상 네트워크에 연결시킵니다. 이 가상 네트워크에서는 컨테이너 간에 서로의 서비스 이름을 DNS 호스트명으로 사용하여 접근할 수 있습니다.compose.yml에서 두 서비스 my-server와 my-db를 정의하면 Docker Compose는 다음과 같은 작업을 수행합니다:
my-server에서 my-db라는 서비스 이름을 사용해 my-db 컨테이너에 접근할 수 있도록 자동으로 DNS 해석을 제공합니다.이를 통해 각 컨테이너는 자신만의 환경과 IP를 가지면서도, Docker Compose의 네트워크 내에서 지정된 서비스 이름으로 서로 통신할 수 있게 됩니다.
따라서 Docker Compose의 같은 네트워크는 Docker가 제공하는 컨테이너 간 통신을 위한 네트워크 공간을 의미하며, 이 안에서만 서로의 서비스 이름으로 통신이 가능합니다.
application.yml 예시:spring:
datasource:
url: jdbc:mysql://my-db:3306/mydb
username: root
password: pwd1234
driver-class-name: com.mysql.cj.jdbc.Driver
my-db는 Docker Compose에서 설정한 MySQL 컨테이너의 서비스 이름입니다.
DNS(Domain Name System)는 웹사이트 주소(도메인 이름)를 IP 주소로 변환해 주는 시스템입니다. 예를 들어, 우리가 google.com 같은 주소로 웹사이트에 접속할 때, DNS는 google.com을 실제 서버의 IP 주소(예: 142.250.190.46)로 변환해 줍니다.
DNS 해석(DNS resolution)은 입력된 도메인 이름을 해당하는 IP 주소로 변환하는 과정을 뜻합니다. 쉽게 말해, DNS 서버가 이름을 IP 주소로 "해석"하는 작업입니다.
google.com을 브라우저에 입력합니다.google.com이 연결되어 있는 IP 주소를 찾아서 브라우저에 알려줍니다.이 과정을 통해 우리는 도메인 이름으로 쉽게 원하는 웹사이트에 접속할 수 있습니다.
호스트명은 특정 서버나 컴퓨터를 식별하기 위해 붙인 이름입니다. DNS 호스트명은 DNS에서 IP 주소 대신 특정 장치나 서버를 가리킬 때 사용하는 이름입니다.
mail-server라는 이름으로 이 서버에 접근할 수 있게 됩니다.