0. 들어가며
지금까지 우리는 도커의 기본적인 개념부터 네트워크까지 모두 배웠다. 이제 여러분은 다음과 같은 작업을 할 수 있다.
# 1. 네트워크 생성
docker network create myapp-network
# 2. 볼륨 생성
docker volume create mysql-data
# 3. MySQL 컨테이너 실행
docker run -d \\
--name mysql-db \\
--network myapp-network \\
-v mysql-data:/var/lib/mysql \\
-e MYSQL_ROOT_PASSWORD=root \\
mysql:8.0
# 4. Spring Boot 앱 컨테이너 실행
docker run -d \\
--name spring-app \\
--network myapp-network \\
-p 8080:8080 \\
-e SPRING_DATASOURCE_URL=jdbc:mysql://mysql-db:3306/mydb \\
myapp:latest
# 5. Nginx 컨테이너 실행
docker run -d \\
--name nginx \\
--network myapp-network \\
-p 80:80 \\
-v ./nginx.conf:/etc/nginx/nginx.conf:ro \\
nginx
매번 이렇게 길고 복잡한 명령어를 입력하는 것은 매우 번거롭고 실수하기 쉽다. 게다가 이 모든 과정을 문서로 남기지 않으면, 다른 개발자가 같은 환경을 구성하려면 또 다시 같은 작업을 반복해야 한다.
이런 문제를 해결하기 위해 등장한 것이 Docker Compose다.
1. Docker Compose란?
1.1. 정의
Docker Compose는 여러 컨테이너로 구성된 애플리케이션을 정의하고 실행하기 위한 도구다.
- YAML 파일 (docker-compose.yml)에 컨테이너들의 설정을 작성한다.
- 단 하나의 명령어(docker-compose up)로 모든 컨테이너를 한 번에 실행할 수 있다.
- 네트워크, 볼륨, 의존 관계 등을 자동으로 관리해준다.
1.2. 실생활 비유: 공장 자동화
- 기존 방식: 공장 기계(컨테이너) 하나하나를 수동으로 켜고, 재료를 넣고, 연결하는 것
- Docker Compose: 모든 기계의 설정이 담긴 자동화 레시피를 만들고, 버튼 하나만 누르면 전체 공장이 가동되는 것
2. Docker Compose 설치
2.1. 확인
최신 Docker Desktop (Windows/Mac)에는 Docker Compose가 기본 포함되어 있다.
# 버전 확인
docker-compose --version
# 또는
docker compose version
2.2. Linux에서 별도 설치
# 최신 버전 다운로드
sudo curl -L "<https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-$>(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# 실행 권한 부여
sudo chmod +x /usr/local/bin/docker-compose
# 설치 확인
docker-compose --version
3. docker-compose.yml 파일 구조
3.1. 기본 구조
version: '3.8' # Compose 파일 버전
services: # 실행할 컨테이너들
service-name:
image: 이미지명
container_name: 컨테이너명
ports:
- "호스트포트:컨테이너포트"
environment:
- ENV_VAR=value
volumes:
- 호스트경로:컨테이너경로
networks:
- 네트워크명
depends_on:
- 다른-서비스
networks: # 사용할 네트워크 정의
network-name:
driver: bridge
volumes: # 사용할 볼륨 정의
volume-name:
3.2. 버전 정보
| 버전 | Docker 엔진 | 주요 특징 |
| 3.8 | 19.03.0+ | 최신 기능, 대부분의 프로젝트에 권장 |
| 3.0 | 1.13.0+ | Swarm 모드 지원 |
| 2.4 | 17.12.0+ | 다양한 개선 사항 |
| 2.0 | 1.10.0+ | 기본적인 Compose 기능 |
4. 주요 옵션 설명
4.1. image
사용할 이미지를 지정한다.
services:
web:
image: nginx:latest
db:
image: mysql:8.0
4.2. build
Dockerfile로 직접 빌드하여 사용한다.
services:
app:
build: . # 현재 디렉토리의 Dockerfile 사용
# 또는
build:
context: ./app # 빌드 컨텍스트 경로
dockerfile: Dockerfile.dev # Dockerfile 파일명 지정
args:
- VERSION=1.0 # 빌드 인자
4.3. ports
포트 매핑을 설정한다.
services:
web:
ports:
- "8080:80" # 호스트:컨테이너
- "443:443"
- "3000" # 호스트 포트 랜덤 할당
4.4. environment
환경 변수를 설정한다.
services:
db:
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: mydb
MYSQL_USER: user
MYSQL_PASSWORD: pass
# 또는 배열 형태
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_DATABASE=mydb
4.5. volumes
볼륨을 마운트한다.
services:
db:
volumes:
- mysql-data:/var/lib/mysql # 볼륨 마운트
- ./init.sql:/docker-entrypoint-initdb.d/init.sql # 바인드 마운트
volumes:
mysql-data: # 볼륨 정의
4.6. networks
네트워크를 설정한다.
services:
web:
networks:
- frontend
app:
networks:
- frontend
- backend
db:
networks:
- backend
networks:
frontend:
driver: bridge
backend:
driver: bridge
4.7. depends_on
서비스 간 의존 관계를 설정한다.
services:
db:
image: mysql:8.0
app:
build: .
depends_on:
- db # db가 먼저 실행된 후 app 실행
주의: depends_on은 시작 순서만 보장할 뿐, DB가 완전히 준비될 때까지 기다리지는 않는다. (healthcheck 필요)
4.8. healthcheck
컨테이너의 상태를 확인한다.
services:
db:
image: mysql:8.0
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
5. 실습 1: WordPress + MySQL
가장 대표적인 예제인 WordPress와 MySQL을 Compose로 구성해보자.
5.1. docker-compose.yml 작성
version: '3.8'
services:
# MySQL 데이터베이스
db:
image: mysql:8.0
container_name: wordpress-db
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
volumes:
- db-data:/var/lib/mysql
networks:
- wordpress-network
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
# WordPress 애플리케이션
wordpress:
image: wordpress:latest
container_name: wordpress-app
restart: always
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_NAME: wordpress
volumes:
- wp-data:/var/www/html
networks:
- wordpress-network
depends_on:
db:
condition: service_healthy # db가 healthy 상태일 때 시작
# phpMyAdmin (선택사항)
phpmyadmin:
image: phpmyadmin/phpmyadmin
container_name: wordpress-phpmyadmin
restart: always
ports:
- "8081:80"
environment:
PMA_HOST: db
PMA_USER: wordpress
PMA_PASSWORD: wordpress
networks:
- wordpress-network
depends_on:
- db
networks:
wordpress-network:
driver: bridge
volumes:
db-data:
wp-data:
5.2. 실행
# 백그라운드에서 모든 서비스 실행
docker-compose up -d
# 실행 상태 확인
docker-compose ps
# 로그 확인
docker-compose logs -f
# 특정 서비스 로그만 확인
docker-compose logs -f wordpress
5.3. 확인
- WordPress: http://localhost:8080
- phpMyAdmin: http://localhost:8081
5.4. 종료 및 정리
# 컨테이너 중지 및 삭제
docker-compose down
# 볼륨까지 함께 삭제 (데이터 완전 삭제)
docker-compose down -v
6. 실습 2: Spring Boot + MySQL
6.1. 프로젝트 구조
my-spring-app/
├── docker-compose.yml
├── backend/
│ ├── Dockerfile
│ ├── pom.xml
│ └── src/
└── init.sql
6.2. Spring Boot Dockerfile
# backend/Dockerfile
FROM openjdk:17-jdk-slim AS builder
WORKDIR /build
COPY pom.xml .
RUN apt-get update && apt-get install -y maven
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
FROM openjdk:17-jre-slim
WORKDIR /app
COPY --from=builder /build/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
6.3. 초기화 SQL
-- init.sql
CREATE DATABASE IF NOT EXISTS myapp;
USE myapp;
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL
);
INSERT INTO users (name, email) VALUES
('John Doe', 'john@example.com'),
('Jane Smith', 'jane@example.com');
6.4. docker-compose.yml
version: '3.8'
services:
mysql:
image: mysql:8.0
container_name: spring-mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: myapp
MYSQL_USER: appuser
MYSQL_PASSWORD: apppassword
ports:
- "3307:3306" # 호스트 3307, 컨테이너 3306 (충돌 방지)
volumes:
- mysql-data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql # 초기화 스크립트
networks:
- spring-network
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
spring-app:
build: ./backend
container_name: spring-app
restart: always
ports:
- "8080:8080"
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/myapp?useSSL=false&serverTimezone=UTC
SPRING_DATASOURCE_USERNAME: appuser
SPRING_DATASOURCE_PASSWORD: apppassword
networks:
- spring-network
depends_on:
mysql:
condition: service_healthy
networks:
spring-network:
driver: bridge
volumes:
mysql-data:
6.5. 실행
# 빌드 및 실행
docker-compose up -d
# 로그 확인
docker-compose logs -f spring-app
# 애플리케이션 상태 확인
curl <http://localhost:8080/actuator/health>
7. 실습 3: 개발 환경을 위한 Compose
개발 환경에서는 소스 코드 변경 시 컨테이너도 자동으로 반영되어야 한다.
7.1. Node.js 개발 환경
version: '3.8'
services:
node-app:
build:
context: .
dockerfile: Dockerfile.dev
container_name: node-dev
ports:
- "3000:3000"
- "9229:9229" # 디버그 포트
volumes:
- .:/app # 소스 코드 마운트
- /app/node_modules # node_modules는 컨테이너 내부 유지
environment:
- NODE_ENV=development
- DEBUG=true
command: npm run dev # nodemon 등으로 자동 리로드
7.2. Python Flask 개발 환경
version: '3.8'
services:
flask-app:
build: .
container_name: flask-dev
ports:
- "5000:5000"
volumes:
- .:/app
environment:
- FLASK_ENV=development
- FLASK_APP=app.py
- DEBUG=true
command: flask run --host=0.0.0.0 --reload
8. 유용한 Compose 명령어
8.1. 기본 명령어
| 명령어 | 설명 |
| docker-compose up -d | 백그라운드에서 서비스 실행 |
| docker-compose down | 서비스 중지 및 컨테이너 삭제 |
| docker-compose ps | 실행 중인 서비스 목록 |
| docker-compose logs -f | 모든 서비스 로그 실시간 확인 |
| docker-compose logs -f [서비스명] | 특정 서비스 로그 확인 |
| docker-compose exec [서비스명] [명령어] | 실행 중인 서비스에 명령 실행 |
| docker-compose run [서비스명] [명령어] | 일회성 명령 실행 |
| docker-compose build | 이미지 빌드 (변경사항 반영) |
| docker-compose restart | 모든 서비스 재시작 |
| docker-compose stop | 서비스 중지 (컨테이너 유지) |
| docker-compose start | 중지된 서비스 시작 |
8.2. 고급 명령어
# 특정 서비스만 빌드/실행
docker-compose up -d db
docker-compose up -d --build spring-app
# 스케일 아웃 (같은 서비스 여러 개 실행)
docker-compose up -d --scale app=3
# 설정 확인
docker-compose config
# 환경 변수 파일 사용
docker-compose --env-file .env.prod up -d
9. 환경 변수 관리
9.1. .env 파일 사용
.env 파일
MYSQL_ROOT_PASSWORD=root123
MYSQL_DATABASE=myapp
MYSQL_USER=appuser
MYSQL_PASSWORD=apppass123
APP_VERSION=1.0.0
docker-compose.yml
version: '3.8'
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
app:
build: .
environment:
- DB_URL=jdbc:mysql://db:3306/${MYSQL_DATABASE}
- DB_USER=${MYSQL_USER}
- DB_PASS=${MYSQL_PASSWORD}
9.2. 여러 환경 분리
# 개발 환경
cp .env.example .env.dev
docker-compose --env-file .env.dev up -d
# 운영 환경
cp .env.example .env.prod
# .env.prod 수정
docker-compose --env-file .env.prod up -d
10. Docker Compose vs 수동 명령어 비교
10.1. 수동 명령어 방식
# 네트워크 생성
docker network create my-network
# 볼륨 생성
docker volume create db-data
# MySQL 실행
docker run -d \\
--name mysql \\
--network my-network \\
-v db-data:/var/lib/mysql \\
-e MYSQL_ROOT_PASSWORD=root \\
mysql:8.0
# 앱 실행
docker run -d \\
--name app \\
--network my-network \\
-p 8080:8080 \\
-e DB_HOST=mysql \\
myapp:latest
10.2. Docker Compose 방식
# docker-compose.yml
version: '3.8'
services:
mysql:
image: mysql:8.0
volumes:
- db-data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: root
networks:
- my-network
app:
build: .
ports:
- "8080:8080"
environment:
DB_HOST: mysql
networks:
- my-network
depends_on:
- mysql
networks:
my-network:
volumes:
db-data:
# 단 한 줄로 모든 것 실행!
docker-compose up -d
11. 마치며
이번 포스팅에서는 여러 컨테이너를 효율적으로 관리하는 Docker Compose에 대해 알아보았다.
- Compose는 여러 컨테이너로 구성된 애플리케이션을 YAML 파일로 정의하고 관리한다.
- docker-compose up 한 줄이면 수동으로 입력하던 긴 명령어들을 대체할 수 있다.
- 네트워크, 볼륨, 의존 관계 등을 자동으로 관리해준다.
- 개발 환경에서는 볼륨 마운트로 실시간 코드 반영이 가능하다.
- .env 파일을 사용하여 환경별 설정을 분리할 수 있다.
이제 여러분은 단일 컨테이너부터 여러 컨테이너로 구성된 복잡한 애플리케이션까지 도커로 자유롭게 다룰 수 있다.
'Docker' 카테고리의 다른 글
| [PROJECT #1] N-Tier 애플리케이션 도커로 구성하기 (Web + App + DB) (0) | 2025.06.01 |
|---|---|
| [ADVANCED #2] 도커 이미지 배포와 공유: Docker Hub 활용법 (0) | 2025.06.01 |
| [BASIC #6] 컨테이너 간 통신: Docker Network 이해하기 (0) | 2025.06.01 |
| [BASIC #5] 데이터를 영구적으로 저장하는 법: Docker Volume (0) | 2025.05.31 |
| [BASIC #4] 나만의 이미지 만들기: Dockerfile 완전 정복 (0) | 2025.05.31 |
