Docker Compose의 역할과 기본 구조
왜 Docker Compose가 필요한가
지금까지는 컨테이너를 하나씩 실행했다. Nginx 하나를 띄울 때는 docker run 명령 하나면 충분했다. 하지만 실제 애플리케이션은 보통 하나의 컨테이너로 끝나지 않는다.
예를 들어 웹 애플리케이션을 실행하려면 다음 요소가 함께 필요할 수 있다.
웹 서버 또는 API 서버
PostgreSQL 같은 데이터베이스
Redis 같은 캐시 서버
데이터를 보존할 볼륨
컨테이너끼리 통신할 네트워크
포트, 환경 변수, 실행 옵션
이것을 모두 docker run으로 관리하면 명령이 길어진다. 또한 팀원이 같은 환경을 만들려면 긴 명령 여러 줄을 정확히 복사해야 한다. 옵션 하나만 빠져도 다른 환경이 된다.
Docker Compose는 이 문제를 해결한다. 여러 컨테이너, 네트워크, 볼륨 설정을 하나의 compose.yaml 파일에 적어 두고, docker compose up 한 번으로 실행한다. Compose 파일은 애플리케이션을 구성하는 서비스, 네트워크, 볼륨 등을 선언하는 표준 형식이다.1
핵심은 간단하다.
docker run명령 여러 개를 하나의 파일로 옮긴다.
AI 생성 이미지
여러 docker run 명령을 Compose 파일로 바꾸기
먼저 Compose 없이 웹 서버와 데이터베이스를 실행한다고 생각해 보자.
docker network create app-net
docker volume create pgdata
docker run --name db \
--network app-net \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=appdb \
-v pgdata:/var/lib/postgresql/data \
-d postgres:16
docker run --name web \
--network app-net \
-p 8080:80 \
-d nginx:stable이 명령들은 각각 의미가 있다.
app-net네트워크를 만든다.pgdata볼륨을 만든다.db컨테이너를 PostgreSQL 이미지로 실행한다.데이터베이스 설정을 환경 변수로 전달한다.
PostgreSQL 데이터 디렉터리에 볼륨을 연결한다.
web컨테이너를 Nginx 이미지로 실행한다.호스트의
8080포트를 컨테이너의80포트에 연결한다.
이 내용을 Compose 파일로 옮기면 다음처럼 된다.
services:
web:
image: nginx:stable
ports:
- "8080:80"
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: secret
POSTGRES_DB: appdb
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:훨씬 읽기 쉽다. 어떤 컨테이너가 있고, 어떤 이미지로 실행되며, 어떤 포트와 볼륨을 쓰는지 파일 안에서 한눈에 볼 수 있다. 포트 매핑은 숫자처럼 보이지만 실제로는 호스트 포트:컨테이너 포트 형식의 문자열이므로, 예제처럼 따옴표로 감싸 두면 YAML 해석 문제를 피하는 데 도움이 된다.
또 하나 달라진 점이 있다. 직접 docker network create를 하지 않았다. Compose는 기본 네트워크를 자동으로 만든다. 같은 Compose 파일 안의 서비스들은 이 기본 네트워크에 함께 연결된다.
compose.yaml의 기본 구조
Compose 파일은 YAML 형식을 사용한다. YAML은 들여쓰기로 구조를 표현한다. 따라서 공백이 중요하다. 탭 대신 스페이스 두 칸을 사용하는 습관을 들이는 것이 좋다.
가장 중요한 최상위 항목은 services다.
services:
web:
image: nginx:stableservices 아래에는 실행할 컨테이너 단위를 적는다. 여기서는 web이 서비스 이름이다. Compose에서 서비스는 보통 하나의 역할을 뜻한다. 웹 서버는 web, 데이터베이스는 db, 캐시는 redis 또는 cache처럼 이름을 붙인다.
Compose의 주요 항목을 하나씩 살펴보자.
services
services는 애플리케이션을 구성하는 컨테이너 목록이다.
services:
web:
image: nginx:stable
db:
image: postgres:16위 파일은 web 서비스와 db 서비스를 정의한다. Compose를 실행하면 두 서비스를 기준으로 컨테이너가 만들어진다. 특별히 복제 수를 늘리지 않는 한, 보통 서비스 하나에 컨테이너 하나가 실행된다고 보면 된다.
image
image는 컨테이너를 어떤 이미지로 실행할지 정한다.
services:
web:
image: nginx:stable이는 다음 명령의 핵심 부분과 같다.
docker run nginx:stable이미지가 로컬에 없으면 Docker가 레지스트리에서 내려받는다.
build
직접 만든 Dockerfile로 이미지를 빌드해야 할 때는 build를 사용한다.
services:
api:
build: .
ports:
- "3000:3000"build: .는 현재 디렉터리를 빌드 컨텍스트로 사용하고, 그 안의 Dockerfile을 기준으로 이미지를 만들겠다는 뜻이다. 즉, 다음 흐름을 Compose가 대신 처리한다.
docker build -t some-api .
docker run -p 3000:3000 some-apiimage는 이미 만들어진 이미지를 사용할 때, build는 소스 코드에서 이미지를 직접 만들 때 쓴다고 이해하면 된다. 둘을 함께 적어 빌드한 이미지에 이름을 붙일 수도 있지만, 처음에는 하나씩 구분해서 익히는 편이 좋다.
ports
ports는 컨테이너 포트를 호스트 포트로 공개한다.
services:
web:
image: nginx:stable
ports:
- "8080:80"8080:80에서 왼쪽 8080은 호스트 포트이고, 오른쪽 80은 컨테이너 포트다. 브라우저에서 http://localhost:8080으로 접속하면 Docker가 web 컨테이너의 80 포트로 요청을 전달한다.
모든 서비스에 ports가 필요한 것은 아니다. 데이터베이스처럼 다른 컨테이너만 접근하면 되는 서비스는 외부에 포트를 공개하지 않는 편이 안전하다.
volumes
volumes는 컨테이너 밖의 저장 공간을 컨테이너 안으로 연결한다.
services:
db:
image: postgres:16
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:여기서 pgdata는 이름 있는 볼륨이다. PostgreSQL이 데이터를 저장하는 /var/lib/postgresql/data 경로에 연결된다. 컨테이너를 지웠다가 다시 만들어도 볼륨이 남아 있으면 데이터도 유지된다.
아래쪽의 최상위 volumes 항목은 Compose에게 pgdata라는 이름 있는 볼륨을 사용하겠다고 알려 주는 부분이다. 직접 docker volume create pgdata를 실행하지 않아도 Compose가 필요한 볼륨을 준비해 준다.
개발 중인 소스 코드를 컨테이너에 바로 연결할 때는 바인드 마운트를 사용할 수도 있다.
services:
app:
build: .
volumes:
- ./src:/app/src왼쪽 ./src는 내 컴퓨터의 디렉터리이고, 오른쪽 /app/src는 컨테이너 내부 경로다.
environment
environment는 컨테이너 안에서 사용할 환경 변수를 지정한다.
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: secret
POSTGRES_DB: appdb이는 docker run -e POSTGRES_PASSWORD=secret -e POSTGRES_DB=appdb와 같은 역할을 한다. Compose의 environment는 위처럼 키와 값을 나누어 쓰는 방식과 목록 방식 모두 사용할 수 있다. 단, true, false, yes, no처럼 YAML이 특별하게 해석할 수 있는 값은 문자열로 다루고 싶을 때 따옴표로 감싸는 것이 안전하다.2
실무에서는 비밀번호를 Compose 파일에 그대로 적지 않는 편이 좋다. 지금은 학습을 위해 단순하게 적었지만, 운영 환경에서는 .env, secret 관리 도구, 클라우드 비밀 값 저장소 등을 함께 고려해야 한다.
실습: Compose로 웹 서버와 데이터베이스 실행하기
새 디렉터리를 만들고 이동한다.
mkdir compose-basic
cd compose-basiccompose.yaml 파일을 만든다. Docker Compose는 일반적으로 현재 디렉터리의 compose.yaml 파일을 읽는다.
services:
web:
image: nginx:stable
ports:
- "8080:80"
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: secret
POSTGRES_DB: appdb
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:이제 실행한다.
docker compose up -dup은 Compose 파일에 정의된 서비스를 만들고 실행한다. -d는 백그라운드 실행을 뜻한다. 실행 상태를 확인한다.
docker compose ps브라우저에서 다음 주소에 접속한다.
http://localhost:8080Nginx 기본 화면이 보이면 web 서비스가 정상적으로 실행된 것이다.
로그는 다음 명령으로 확인한다.
docker compose logs특정 서비스의 로그만 보고 싶으면 서비스 이름을 붙인다.
docker compose logs web계속 따라가며 보고 싶을 때는 -f를 사용한다.
docker compose logs -f web컨테이너 안에서 명령을 실행할 때는 exec를 사용한다.
docker compose exec db psql -U postgres -d appdbPostgreSQL 프롬프트가 열리면 데이터베이스 컨테이너에 정상적으로 들어간 것이다. 빠져나올 때는 다음을 입력한다.
q서비스 이름으로 컨테이너 연결하기
Compose는 기본 네트워크를 만들고, 같은 Compose 프로젝트 안의 서비스 이름을 DNS 이름처럼 사용할 수 있게 해 준다. 즉 web 컨테이너는 db 컨테이너를 IP 주소로 찾을 필요가 없다. 서비스 이름인 db로 접근하면 된다.
확인해 보자.
docker compose exec web sh -c 'getent hosts db'IP 주소와 함께 db가 출력되면 이름 해석이 되는 것이다. 이 덕분에 애플리케이션 설정에서는 데이터베이스 주소를 보통 이렇게 적는다.
DB_HOST=db
DB_PORT=5432중요한 점이 있다. 컨테이너 안에서 localhost는 내 컴퓨터가 아니라 그 컨테이너 자신을 뜻한다. 웹 애플리케이션 컨테이너가 PostgreSQL에 접속할 때 localhost:5432를 사용하면 자기 자신 안에서 PostgreSQL을 찾는다. PostgreSQL은 db 서비스에 있으므로 db:5432로 접속해야 한다.
이 규칙은 Redis에도 똑같이 적용된다.
REDIS_HOST=cache
REDIS_PORT=6379서비스 이름을 잘 정해 두면 IP가 바뀌어도 설정을 바꿀 필요가 없다. Compose가 내부 네트워크에서 이름을 해결해 주기 때문이다. 실제 컨테이너 이름은 프로젝트 이름이 붙어 길어질 수 있으므로, 애플리케이션 설정에서는 컨테이너 이름보다 서비스 이름을 기준으로 생각하는 편이 좋다.
자주 쓰는 Compose 명령 흐름
Compose를 사용할 때 가장 자주 쓰는 명령은 다음 다섯 가지다.
docker compose up -d서비스를 만들고 실행한다. image로 지정한 이미지가 로컬에 없으면 내려받고, build로 지정한 서비스는 필요한 경우 이미지를 빌드한 뒤 컨테이너를 시작한다. 이미 빌드된 이미지가 있고 Dockerfile이나 소스 코드 변경을 확실히 반영하고 싶다면 뒤에서 볼 --build 옵션을 함께 사용한다.
docker compose ps현재 Compose 프로젝트의 컨테이너 상태를 확인한다.
docker compose logs -f서비스 로그를 확인한다. 문제가 생겼을 때 가장 먼저 보는 명령이다.
docker compose exec web sh실행 중인 web 서비스 컨테이너 안에서 셸을 연다. 설정 파일 확인, 네트워크 확인, 임시 명령 실행에 유용하다.
docker compose downCompose로 만든 컨테이너와 기본 네트워크를 정리한다. 단, 이름 있는 볼륨은 기본적으로 삭제하지 않는다. 데이터까지 지우고 싶을 때는 다음처럼 -v를 붙인다.
docker compose down -v이 명령은 데이터베이스 볼륨까지 삭제할 수 있으므로 조심해서 사용해야 한다.
Dockerfile이나 애플리케이션 의존성이 바뀌어 이미지를 다시 빌드해야 한다면 다음 명령을 사용한다.
docker compose up -d --buildCompose 파일을 읽는 순서
Compose 파일을 처음 보면 아래 순서로 읽으면 쉽다.
services아래에 어떤 서비스가 있는가?각 서비스는
image를 쓰는가,build를 쓰는가?외부에서 접속해야 하는 서비스에만
ports가 있는가?데이터가 필요한 서비스에
volumes가 연결되어 있는가?설정값은
environment에 잘 들어 있는가?서비스끼리 접속할 때
localhost가 아니라 서비스 이름을 쓰고 있는가?
Compose는 Docker의 새로운 개념이라기보다, 이미 배운 컨테이너 실행 옵션을 파일로 정리하는 도구에 가깝다. 이전에 배운 이미지, 컨테이너, 볼륨, 네트워크 지식이 그대로 사용된다. 차이는 실행 방식이다. 이제는 긴 docker run 명령을 반복하는 대신, 애플리케이션 구조를 compose.yaml에 남기고 한 번의 명령으로 같은 환경을 다시 만들 수 있다.
참고
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.