본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 11. 26. · 7 Views
Docker Compose 기초 완벽 가이드
여러 컨테이너를 하나의 파일로 관리하는 Docker Compose의 기초를 배웁니다. 실무에서 바로 활용할 수 있는 서비스 정의, 네트워크, 볼륨 설정까지 초급자도 쉽게 따라할 수 있도록 설명합니다.
목차
1. Docker Compose란?
김개발 씨는 오늘 새로운 프로젝트에 투입되었습니다. 웹 서버, 데이터베이스, 캐시 서버까지 총 세 개의 컨테이너를 띄워야 하는데, 매번 docker run 명령어를 세 번씩 입력하자니 벌써부터 머리가 아파옵니다.
"이걸 매번 이렇게 해야 하나요?" 선배 박시니어 씨가 웃으며 대답합니다. "Docker Compose를 쓰면 한 방에 해결돼요."
Docker Compose는 여러 개의 컨테이너를 하나의 설정 파일로 정의하고, 단일 명령어로 한꺼번에 실행하는 도구입니다. 마치 오케스트라 지휘자가 악보 하나로 모든 악기를 조율하듯, docker-compose.yml 파일 하나로 복잡한 멀티 컨테이너 환경을 손쉽게 관리할 수 있습니다.
개발 환경 구축부터 테스트, 배포까지 일관된 환경을 보장받을 수 있습니다.
다음 코드를 살펴봅시다.
# docker-compose.yml 기본 예제
version: '3.8'
services:
# 웹 애플리케이션 서비스
web:
image: nginx:latest
ports:
- "80:80"
# 데이터베이스 서비스
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: secret
김개발 씨는 입사 첫 주부터 난관에 부딪혔습니다. 프로젝트를 로컬에서 실행하려면 웹 서버, 데이터베이스, 메시지 큐까지 세 가지 서비스를 모두 띄워야 했습니다.
처음에는 터미널 창을 세 개 열고 각각 docker run 명령어를 입력했습니다. 그런데 옵션을 빠뜨리거나 순서를 잘못 맞추면 서비스가 제대로 연결되지 않았습니다.
"매번 이렇게 복잡하게 해야 하나요?" 김개발 씨의 한숨 섞인 질문에 박시니어 씨가 다가왔습니다. "Docker Compose를 써보세요.
한 번 설정해두면 명령어 하나로 전부 띄울 수 있어요." 그렇다면 Docker Compose란 정확히 무엇일까요? 쉽게 비유하자면, Docker Compose는 마치 레스토랑의 세트 메뉴 주문서와 같습니다.
손님이 "A 세트 주세요"라고 한마디만 하면 스테이크, 샐러드, 음료가 한꺼번에 나오는 것처럼, docker-compose up이라는 명령어 하나로 정의된 모든 서비스가 동시에 실행됩니다. 각각의 요리를 따로 주문할 필요가 없는 것입니다.
Docker Compose가 없던 시절에는 어땠을까요? 개발자들은 매번 긴 docker run 명령어를 입력해야 했습니다.
포트 매핑, 볼륨 연결, 환경 변수 설정을 일일이 타이핑하다 보면 오타가 나기 일쑤였습니다. 더 큰 문제는 팀원 간의 환경 차이였습니다.
"제 컴퓨터에서는 잘 되는데요?"라는 말이 회의실에서 자주 들려왔습니다. 바로 이런 문제를 해결하기 위해 Docker Compose가 등장했습니다.
docker-compose.yml 파일에 모든 설정을 코드로 남기면, 누구나 동일한 환경을 재현할 수 있습니다. 신입 개발자가 입사해도 git clone 후 docker-compose up만 실행하면 끝입니다.
환경 설정에 반나절을 쓰던 시대는 지났습니다. 위의 코드를 살펴보겠습니다.
**version: '3.8'**은 Compose 파일 형식의 버전을 명시합니다. services 아래에 실행할 컨테이너들을 정의합니다.
web 서비스는 nginx 이미지를 사용하고 80번 포트를 노출합니다. db 서비스는 PostgreSQL 데이터베이스이며 환경 변수로 비밀번호를 설정합니다.
실제 현업에서는 어떻게 활용할까요? 스타트업에서 새 프로젝트를 시작한다고 가정해봅시다.
프론트엔드, 백엔드 API, 데이터베이스, Redis 캐시 서버가 필요합니다. Docker Compose 파일 하나에 네 가지 서비스를 정의해두면, 신규 팀원 온보딩 시간이 하루에서 30분으로 줄어듭니다.
CI/CD 파이프라인에서도 동일한 파일을 사용해 테스트 환경을 구축합니다. 하지만 주의할 점도 있습니다.
Docker Compose는 기본적으로 단일 호스트 환경을 위한 도구입니다. 여러 서버에 분산 배포가 필요하다면 Kubernetes나 Docker Swarm을 검토해야 합니다.
개발과 테스트 환경에서는 Compose가 최적이지만, 프로덕션 환경에서는 상황에 맞는 선택이 필요합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨가 건네준 docker-compose.yml 파일을 받아든 김개발 씨는 터미널에 docker-compose up을 입력했습니다. 화면에 로그가 쏟아지더니, 몇 초 만에 세 개의 서비스가 모두 실행되었습니다.
"와, 진짜 한 방에 되네요!" Docker Compose를 제대로 이해하면 복잡한 개발 환경 구축이 단순해집니다. 여러분도 오늘부터 docker-compose.yml 파일을 작성해 보세요.
실전 팁
💡 - Docker Compose는 Docker Engine과 별도로 설치해야 할 수 있습니다. docker-compose --version으로 설치 여부를 확인하세요.
- 프로젝트마다 docker-compose.yml 파일을 Git에 함께 커밋하여 환경 설정을 코드로 관리하세요.
2. docker-compose.yml 구조
김개발 씨는 docker-compose.yml 파일을 처음 열어보았습니다. YAML 형식이라는 건 알겠는데, version, services, networks, volumes 같은 키워드가 무엇을 의미하는지 도통 감이 오지 않습니다.
"이 파일 구조가 어떻게 되는 거예요?" 박시니어 씨가 화면을 가리키며 차근차근 설명을 시작합니다.
docker-compose.yml은 YAML 형식으로 작성되며, 크게 네 가지 최상위 키로 구성됩니다. version은 파일 형식 버전, services는 컨테이너 정의, networks는 네트워크 설정, volumes는 데이터 저장소를 의미합니다.
이 구조를 이해하면 어떤 Compose 파일도 쉽게 읽고 작성할 수 있습니다.
다음 코드를 살펴봅시다.
# docker-compose.yml 전체 구조
version: '3.8' # Compose 파일 버전
services: # 컨테이너(서비스) 정의
webapp:
image: node:18
ports:
- "3000:3000"
networks: # 커스텀 네트워크 정의
frontend:
driver: bridge
volumes: # 데이터 볼륨 정의
db-data:
driver: local
김개발 씨는 선배가 작성한 docker-compose.yml 파일을 찬찬히 살펴보았습니다. 파일이 생각보다 길었습니다.
어디서부터 읽어야 할지 막막했습니다. "YAML 파일은 들여쓰기가 중요해요.
계층 구조를 잘 보면 의미가 보일 거예요." 박시니어 씨의 조언에 김개발 씨는 다시 파일을 들여다보았습니다. docker-compose.yml의 구조는 마치 회사의 조직도와 같습니다.
최상위에 회사명(version)이 있고, 그 아래 부서(services, networks, volumes)가 있으며, 각 부서 안에 팀원(개별 설정)이 배치되어 있습니다. 조직도를 읽듯이 위에서 아래로 따라가면 전체 구조가 한눈에 들어옵니다.
version 키부터 살펴봅시다. 이 키는 Compose 파일의 스키마 버전을 지정합니다.
'3.8'이 현재 가장 많이 쓰이는 버전입니다. 버전에 따라 지원되는 기능이 다르므로, 팀에서 사용하는 버전을 확인하고 맞추는 것이 좋습니다.
참고로 최신 Docker Compose V2에서는 version 키가 선택 사항이 되었습니다. services 키는 Compose 파일의 심장입니다.
여기에 실행할 컨테이너들을 정의합니다. webapp, db, redis처럼 서비스 이름을 자유롭게 지정하고, 그 아래에 이미지, 포트, 환경 변수 등을 설정합니다.
하나의 서비스는 하나의 컨테이너에 대응한다고 생각하면 됩니다. networks 키는 컨테이너 간 통신을 담당합니다.
기본적으로 같은 Compose 파일의 서비스들은 자동으로 하나의 네트워크에 연결됩니다. 하지만 프론트엔드와 백엔드를 분리하거나, 특정 서비스만 외부에 노출하고 싶을 때 커스텀 네트워크를 정의합니다.
volumes 키는 데이터를 영속적으로 저장합니다. 컨테이너는 삭제되면 내부 데이터도 함께 사라집니다.
데이터베이스처럼 데이터를 보존해야 하는 서비스는 볼륨을 연결해야 합니다. 볼륨은 컨테이너 생명주기와 독립적으로 데이터를 유지합니다.
위의 코드를 다시 한번 살펴보겠습니다. **version: '3.8'**로 파일 형식을 선언했습니다.
services 아래 webapp이라는 서비스를 정의하고 Node.js 18 이미지를 사용합니다. networks에서 frontend라는 브릿지 네트워크를 만들었습니다.
volumes에서 db-data라는 로컬 볼륨을 생성했습니다. YAML 문법에서 주의할 점이 있습니다.
들여쓰기는 반드시 스페이스를 사용해야 합니다. 탭 문자를 쓰면 오류가 발생합니다.
또한 콜론(:) 뒤에는 반드시 공백이 있어야 합니다. 이런 작은 실수로 몇 시간을 허비하는 경우가 많으니 주의하세요.
실무에서는 이 네 가지 키를 조합해 복잡한 환경을 구성합니다. 예를 들어 마이크로서비스 아키텍처에서는 services에 10개 이상의 서비스가 정의되기도 합니다.
각 서비스가 어떤 네트워크에 속하고, 어떤 볼륨을 공유하는지 명확히 정의해두면 운영이 한결 수월해집니다. 김개발 씨는 고개를 끄덕였습니다.
"아, 이제 구조가 보여요. 일단 services부터 읽으면 어떤 서비스가 있는지 알 수 있고, networks와 volumes에서 연결 관계를 파악하면 되겠네요!" docker-compose.yml 구조를 이해하면 남이 작성한 파일도 쉽게 읽을 수 있습니다.
구조를 먼저 파악하고, 세부 설정을 하나씩 익혀나가세요.
실전 팁
💡 - VS Code의 YAML 확장 프로그램을 설치하면 문법 오류를 자동으로 잡아줍니다.
- docker-compose config 명령어로 파일 문법 검증을 할 수 있습니다.
3. 서비스 정의하기
이제 김개발 씨는 직접 서비스를 정의해볼 차례입니다. 웹 애플리케이션과 데이터베이스 두 개의 서비스를 연결해야 합니다.
이미지 선택부터 포트 매핑, 의존성 설정까지 해야 할 게 많아 보입니다. "서비스 정의할 때 뭐부터 시작해야 해요?" 박시니어 씨가 키보드를 가져오며 말합니다.
"일단 이미지부터 정하죠."
서비스 정의는 docker-compose.yml의 핵심입니다. 각 서비스에는 image(사용할 이미지), ports(포트 매핑), environment(환경 변수), depends_on(의존성) 등을 설정합니다.
이 설정들을 조합하면 복잡한 멀티 컨테이너 애플리케이션도 쉽게 구성할 수 있습니다.
다음 코드를 살펴봅시다.
version: '3.8'
services:
# 웹 애플리케이션 서비스
api:
image: python:3.11-slim
working_dir: /app
ports:
- "8000:8000" # 호스트:컨테이너 포트 매핑
environment:
- DATABASE_URL=postgres://db:5432/myapp
depends_on:
- db # db 서비스가 먼저 시작됨
# 데이터베이스 서비스
db:
image: postgres:15
environment:
POSTGRES_DB: myapp
POSTGRES_PASSWORD: secret
김개발 씨는 빈 docker-compose.yml 파일 앞에서 고민에 빠졌습니다. Python 백엔드 API와 PostgreSQL 데이터베이스를 연결해야 하는데, 어디서부터 시작해야 할지 막막했습니다.
"서비스 정의는 레고 블록 조립이라고 생각해요." 박시니어 씨가 설명을 시작합니다. "필요한 블록을 하나씩 끼워 맞추면 됩니다." 서비스 정의는 마치 아파트 입주 신청서와 같습니다.
입주하려면 어떤 평형(image)을 원하는지, 주소(ports)는 어떻게 할지, 옵션(environment)은 무엇을 선택할지 적어내야 합니다. 서비스 하나를 정의하는 것은 컨테이너 한 채의 입주 조건을 명시하는 것입니다.
가장 먼저 image를 지정합니다. image는 컨테이너가 사용할 베이스 이미지입니다.
Docker Hub에서 공식 이미지를 가져오거나, 직접 만든 이미지를 사용할 수 있습니다. python:3.11-slim처럼 태그를 명시하면 특정 버전을 고정할 수 있어 환경 일관성을 유지할 수 있습니다.
ports는 네트워크 접근 경로를 열어줍니다. "8000:8000" 형식은 호스트의 8000번 포트를 컨테이너의 8000번 포트에 연결한다는 뜻입니다.
왼쪽이 호스트, 오른쪽이 컨테이너입니다. 여러 서비스가 같은 포트를 사용하면 충돌이 발생하므로 호스트 포트는 서로 다르게 지정해야 합니다.
environment는 컨테이너에 환경 변수를 전달합니다. 데이터베이스 접속 정보, API 키, 설정값 등을 환경 변수로 주입합니다.
코드에 민감한 정보를 하드코딩하지 않고, 환경에 따라 다른 값을 사용할 수 있어 유연합니다. depends_on은 서비스 시작 순서를 제어합니다.
api 서비스가 db에 의존한다고 선언하면, docker-compose up 실행 시 db가 먼저 시작됩니다. 단, 이는 시작 순서만 보장할 뿐, 데이터베이스가 완전히 준비될 때까지 기다리지는 않습니다.
실제 연결 가능 여부는 애플리케이션 레벨에서 처리해야 합니다. 위의 코드를 분석해보겠습니다.
api 서비스는 Python 3.11 이미지를 사용하고, /app 디렉토리에서 작업합니다. 8000번 포트를 외부에 노출하고, DATABASE_URL 환경 변수로 db 서비스에 연결합니다.
depends_on으로 db가 먼저 시작되도록 했습니다. db 서비스는 PostgreSQL 15 이미지를 사용하며, 데이터베이스 이름과 비밀번호를 환경 변수로 설정했습니다.
실무에서 자주 쓰이는 추가 옵션도 있습니다. build는 Dockerfile로 이미지를 직접 빌드할 때 사용합니다.
volumes는 호스트와 컨테이너 간 파일을 공유합니다. command는 컨테이너 시작 시 실행할 명령어를 지정합니다.
restart는 컨테이너가 종료될 때 자동 재시작 정책을 설정합니다. 초보자가 자주 하는 실수가 있습니다.
depends_on을 설정했으니 데이터베이스가 완전히 준비된 후 앱이 실행될 거라고 기대하는 것입니다. 하지만 depends_on은 컨테이너 시작 순서만 보장합니다.
데이터베이스가 실제로 연결을 받을 준비가 되었는지는 별도로 확인해야 합니다. 이를 위해 wait-for-it.sh 같은 스크립트를 사용하기도 합니다.
김개발 씨는 한 줄 한 줄 타이핑하며 서비스를 정의했습니다. "depends_on이 순서만 보장한다는 거, 몰랐으면 큰일 날 뻔했네요." 박시니어 씨가 고개를 끄덕입니다.
"그래서 항상 애플리케이션에서 연결 재시도 로직을 넣어두는 게 좋아요." 서비스 정의를 마스터하면 어떤 조합의 컨테이너 환경도 만들 수 있습니다. 하나씩 옵션을 추가하며 필요한 설정을 구성해보세요.
실전 팁
💡 - 이미지 태그는 latest 대신 특정 버전을 명시하여 재현 가능한 환경을 만드세요.
- depends_on 대신 healthcheck와 condition을 조합하면 서비스 준비 상태까지 기다릴 수 있습니다.
4. 네트워크와 볼륨 설정
김개발 씨의 서비스는 잘 실행되었지만, 새로운 문제가 생겼습니다. 컨테이너를 재시작하니 데이터베이스에 저장했던 데이터가 모두 사라진 것입니다.
또한 프론트엔드와 백엔드 네트워크를 분리하고 싶은데 방법을 모르겠습니다. "데이터는 어디로 간 거예요?" 박시니어 씨가 설명합니다.
"볼륨을 설정하지 않았으니 당연한 결과예요."
네트워크는 컨테이너 간 통신 경로를 정의하고, 볼륨은 컨테이너 생명주기와 독립적으로 데이터를 보존합니다. Docker Compose는 기본 네트워크를 자동 생성하지만, 커스텀 네트워크로 서비스를 격리할 수 있습니다.
볼륨 없이는 컨테이너 삭제 시 모든 데이터가 유실됩니다.
다음 코드를 살펴봅시다.
version: '3.8'
services:
api:
image: node:18
networks:
- backend # backend 네트워크에 연결
volumes:
- ./src:/app/src # 바인드 마운트: 호스트 폴더 연결
db:
image: postgres:15
networks:
- backend
volumes:
- db-data:/var/lib/postgresql/data # 네임드 볼륨
networks:
backend: # 커스텀 네트워크 정의
driver: bridge
volumes:
db-data: # 네임드 볼륨 정의
김개발 씨는 당황했습니다. 어제 열심히 입력한 테스트 데이터가 온데간데없이 사라졌습니다.
docker-compose down 후 다시 up 했을 뿐인데 말입니다. "컨테이너는 기본적으로 휘발성이에요." 박시니어 씨가 설명합니다.
"컨테이너가 삭제되면 그 안의 파일 시스템도 함께 사라집니다. 데이터를 보존하려면 볼륨을 써야 해요." 볼륨은 마치 외장 하드 드라이브와 같습니다.
컴퓨터(컨테이너)를 포맷해도 외장 하드에 저장한 파일은 그대로 남아있습니다. 볼륨도 마찬가지입니다.
컨테이너가 삭제되어도 볼륨에 저장된 데이터는 독립적으로 유지됩니다. 볼륨에는 두 가지 종류가 있습니다.
바인드 마운트는 호스트의 특정 경로를 컨테이너에 연결합니다. ./src:/app/src처럼 호스트의 src 폴더를 컨테이너의 /app/src에 마운트합니다.
개발 중 코드 변경 사항이 즉시 컨테이너에 반영되어 편리합니다. 네임드 볼륨은 Docker가 관리하는 저장소입니다.
db-data:/var/lib/postgresql/data처럼 이름을 지정하고 컨테이너 경로에 연결합니다. 호스트 경로를 신경 쓸 필요 없이 Docker가 알아서 관리합니다.
데이터베이스처럼 데이터 영속성이 중요한 서비스에 적합합니다. 이제 네트워크에 대해 알아봅시다.
Docker Compose는 기본적으로 프로젝트별 네트워크를 자동 생성합니다. 같은 Compose 파일의 서비스들은 서비스 이름으로 서로를 찾을 수 있습니다.
api 서비스에서 db라는 호스트명으로 데이터베이스에 접근할 수 있는 이유입니다. 커스텀 네트워크는 언제 필요할까요?
마이크로서비스 환경에서 프론트엔드, 백엔드, 데이터베이스를 논리적으로 분리하고 싶을 때 유용합니다. 프론트엔드는 frontend 네트워크에만, 데이터베이스는 backend 네트워크에만 연결하면 불필요한 접근을 차단할 수 있습니다.
위의 코드를 살펴보겠습니다. api와 db 서비스 모두 backend 네트워크에 연결됩니다.
같은 네트워크에 있으므로 서비스 이름으로 통신할 수 있습니다. api 서비스는 바인드 마운트로 로컬 소스 코드를 연결하고, db 서비스는 네임드 볼륨으로 데이터를 영속화합니다.
초보자가 자주 하는 실수가 있습니다. 바인드 마운트 경로를 절대 경로로 작성하면 다른 환경에서 작동하지 않습니다.
./처럼 상대 경로를 사용하세요. 또한 볼륨을 설정하지 않고 프로덕션에 배포했다가 재배포 시 모든 데이터를 잃는 사고도 흔합니다.
데이터를 완전히 삭제하고 싶다면 어떻게 할까요? docker-compose down -v 명령어를 사용합니다.
-v 옵션이 볼륨까지 함께 삭제합니다. 테스트 환경을 초기화할 때 유용하지만, 실수로 프로덕션 데이터를 날리지 않도록 주의해야 합니다.
김개발 씨는 db-data 볼륨을 추가한 후 다시 테스트했습니다. docker-compose down 후 up을 해도 데이터가 그대로 남아있었습니다.
"이제야 안심이 되네요!" 네트워크와 볼륨을 제대로 이해하면 안전하고 유연한 컨테이너 환경을 구축할 수 있습니다. 특히 데이터베이스에는 반드시 볼륨을 설정하세요.
실전 팁
💡 - 개발 환경에서는 바인드 마운트로 코드를 연결하고, 프로덕션에서는 이미지에 코드를 포함시키세요.
- docker volume ls로 생성된 볼륨 목록을, docker volume inspect로 상세 정보를 확인할 수 있습니다.
5. docker-compose up/down
설정 파일 작성을 마친 김개발 씨는 드디어 서비스를 실행할 차례입니다. 터미널에 어떤 명령어를 입력해야 할까요?
up과 down은 알겠는데, -d, --build, -v 같은 옵션들은 언제 쓰는 건지 헷갈립니다. "그냥 up 하면 되는 거 아니에요?" 박시니어 씨가 웃습니다.
"상황에 따라 옵션이 필요해요."
docker-compose up은 서비스를 생성하고 시작하며, docker-compose down은 서비스를 중지하고 제거합니다. -d 옵션으로 백그라운드 실행, --build로 이미지 재빌드, -v로 볼륨 삭제가 가능합니다.
이 명령어들은 Docker Compose 작업의 90%를 차지합니다.
다음 코드를 살펴봅시다.
# 서비스 시작 (포그라운드 - 로그가 터미널에 출력됨)
docker-compose up
# 서비스 시작 (백그라운드 - 터미널 제어권 유지)
docker-compose up -d
# 이미지 재빌드 후 시작
docker-compose up --build
# 특정 서비스만 시작
docker-compose up -d api
# 서비스 중지 및 컨테이너 제거
docker-compose down
# 볼륨까지 함께 삭제 (주의: 데이터 유실!)
docker-compose down -v
김개발 씨는 터미널 앞에서 잠시 망설였습니다. docker-compose up을 입력하면 될 것 같은데, 선배들이 쓰는 옵션이 다 달랐습니다.
어떤 분은 -d를 붙이고, 어떤 분은 --build를 붙이고. "상황에 따라 다른 옵션을 써야 해요." 박시니어 씨가 설명을 시작합니다.
"각 옵션의 의미를 알면 적재적소에 쓸 수 있습니다." docker-compose up은 마치 자동차 시동 걸기와 같습니다. 키를 돌리면(up) 엔진이 켜지고, 키를 빼면(down) 엔진이 꺼집니다.
시동을 걸 때 에어컨을 켤지(-d), 엔진 오일을 교체할지(--build) 선택할 수 있는 것처럼, up 명령어에도 다양한 옵션이 있습니다. 먼저 기본 up 명령어를 봅시다.
docker-compose up을 옵션 없이 실행하면 포그라운드에서 실행됩니다. 모든 서비스의 로그가 터미널에 실시간으로 출력됩니다.
개발 중 디버깅할 때 유용하지만, 터미널 창을 닫으면 서비스도 함께 종료됩니다. -d 옵션은 백그라운드 실행입니다.
docker-compose up -d를 실행하면 서비스가 백그라운드에서 돌아갑니다. 터미널 제어권을 유지할 수 있어 다른 작업을 계속할 수 있습니다.
로그를 보고 싶으면 docker-compose logs -f 명령어를 사용합니다. --build 옵션은 이미지를 재빌드합니다.
Dockerfile을 수정했거나 소스 코드를 이미지에 포함시키는 경우, 기존 이미지로는 변경 사항이 반영되지 않습니다. --build 옵션을 붙이면 이미지를 새로 빌드한 후 서비스를 시작합니다.
특정 서비스만 실행할 수도 있습니다. docker-compose up -d api처럼 서비스 이름을 지정하면 해당 서비스와 의존 서비스만 실행됩니다.
전체 서비스를 띄울 필요 없이 작업 중인 서비스만 빠르게 테스트할 때 유용합니다. 이제 docker-compose down을 알아봅시다.
down 명령어는 실행 중인 컨테이너를 중지하고 제거합니다. 네트워크도 함께 삭제됩니다.
하지만 볼륨은 기본적으로 유지됩니다. 데이터를 보존하기 위한 안전장치입니다.
-v 옵션은 신중하게 사용해야 합니다. docker-compose down -v는 볼륨까지 삭제합니다.
테스트 데이터를 깨끗이 지우고 처음부터 시작하고 싶을 때 사용합니다. 하지만 프로덕션 환경에서 실수로 이 명령어를 치면 모든 데이터가 사라집니다.
정말 조심해야 합니다. 실무에서 자주 쓰는 조합이 있습니다.
개발 중에는 docker-compose up으로 로그를 보며 작업하고, 코드 수정 후에는 docker-compose up --build로 재빌드합니다. 배포 환경에서는 docker-compose up -d로 백그라운드 실행합니다.
김개발 씨는 각 옵션을 하나씩 테스트해보았습니다. -d를 붙이니 터미널이 깔끔해졌고, --build를 붙이니 수정한 코드가 반영되었습니다.
"down -v는 진짜 조심해야겠네요. 데이터 날리면 큰일이니까요." up과 down 명령어를 마스터하면 Docker Compose의 기본기는 완성됩니다.
상황에 맞는 옵션을 선택하여 효율적으로 작업하세요.
실전 팁
💡 - docker-compose ps로 현재 실행 중인 서비스 상태를 확인할 수 있습니다.
- docker-compose logs -f --tail=100으로 최근 100줄 로그부터 실시간으로 볼 수 있습니다.
- docker-compose restart로 서비스를 재시작할 수 있습니다.
6. 환경 변수와 .env 파일
김개발 씨는 docker-compose.yml을 Git에 커밋하려다 멈칫했습니다. 파일 안에 데이터베이스 비밀번호가 그대로 적혀 있었기 때문입니다.
"이거 이대로 올려도 되나요?" 박시니어 씨가 고개를 젓습니다. "민감한 정보는 .env 파일로 분리해야 해요.
그리고 .env는 절대 Git에 올리면 안 됩니다."
환경 변수는 설정값을 코드와 분리하는 방법입니다. Docker Compose는 .env 파일을 자동으로 읽어 변수를 치환합니다.
비밀번호, API 키 같은 민감한 정보를 docker-compose.yml에 직접 쓰지 않고 안전하게 관리할 수 있습니다. .env 파일은 반드시 .gitignore에 추가해야 합니다.
다음 코드를 살펴봅시다.
# .env 파일 (Git에 커밋하지 않음!)
POSTGRES_USER=admin
POSTGRES_PASSWORD=super_secret_password
POSTGRES_DB=myapp
API_PORT=8000
# docker-compose.yml
version: '3.8'
services:
api:
image: node:18
ports:
- "${API_PORT}:3000" # .env의 API_PORT 사용
environment:
- DB_HOST=db
- DB_USER=${POSTGRES_USER}
db:
image: postgres:15
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
김개발 씨는 식은땀이 났습니다. 하마터면 데이터베이스 비밀번호를 GitHub에 올릴 뻔했습니다.
오픈소스 프로젝트에서 AWS 키가 유출되어 수천만 원이 청구된 사례를 들은 적이 있습니다. "민감한 정보는 코드와 분리하는 게 철칙이에요." 박시니어 씨가 강조합니다.
".env 파일을 사용하면 쉽게 해결됩니다." 환경 변수 분리는 마치 금고와 열쇠를 따로 보관하는 것과 같습니다. 금고(docker-compose.yml)는 공개해도 되지만, 열쇠(.env)는 절대 공개하면 안 됩니다.
열쇠 없이는 금고를 열 수 없듯이, .env 파일 없이는 서비스가 제대로 동작하지 않습니다. Docker Compose는 .env 파일을 자동으로 읽습니다.
docker-compose.yml과 같은 디렉토리에 .env 파일을 두면, Compose가 자동으로 파일을 읽어 변수를 치환합니다. 별도의 설정이 필요 없습니다.
변수는 ${변수명} 형식으로 참조합니다. docker-compose.yml에서 ${POSTGRES_PASSWORD}라고 쓰면 .env 파일의 POSTGRES_PASSWORD 값으로 치환됩니다.
따옴표 안에서도, 밖에서도 사용할 수 있습니다. .env 파일 형식은 간단합니다.
한 줄에 하나의 변수를 KEY=VALUE 형식으로 작성합니다. 등호 앞뒤에 공백을 넣지 않습니다.
값에 공백이 있으면 따옴표로 감쌉니다. 주석은 #으로 시작합니다.
.gitignore 설정은 필수입니다. 프로젝트 루트의 .gitignore 파일에 .env를 반드시 추가하세요.
실수로 커밋되는 것을 방지합니다. 대신 .env.example 파일을 만들어 어떤 변수가 필요한지 템플릿으로 제공하면 팀원들이 참고하기 좋습니다.
위의 코드를 살펴보겠습니다. .env 파일에 POSTGRES_USER, POSTGRES_PASSWORD 등을 정의했습니다.
docker-compose.yml에서는 ${POSTGRES_USER}처럼 변수를 참조합니다. api 서비스의 포트도 ${API_PORT}로 유연하게 설정했습니다.
환경별로 다른 .env 파일을 사용할 수도 있습니다. 개발 환경에는 .env.development, 프로덕션에는 .env.production을 만들고, docker-compose --env-file .env.production up처럼 명시적으로 지정할 수 있습니다.
환경에 따라 다른 설정을 적용할 때 유용합니다. 초보자가 자주 하는 실수가 있습니다.
.env 파일을 만들었는데 변수가 치환되지 않는 경우가 있습니다. 대부분 파일 이름 오타(.env가 아니라 env)거나, 파일 위치가 docker-compose.yml과 다른 디렉토리인 경우입니다.
또한 변수명에 오타가 있어도 에러 없이 빈 값으로 치환되므로 주의해야 합니다. docker-compose config로 검증하세요.
이 명령어는 .env 변수가 치환된 최종 결과를 보여줍니다. 변수가 제대로 적용되었는지 확인할 수 있어 디버깅에 유용합니다.
김개발 씨는 .env 파일을 만들고, .gitignore에 추가했습니다. docker-compose config로 확인하니 비밀번호가 제대로 치환되어 있었습니다.
"이제 안심하고 커밋할 수 있겠네요!" 환경 변수 관리를 제대로 하면 보안과 유연성을 모두 잡을 수 있습니다. .env 파일로 민감한 정보를 분리하고, 반드시 .gitignore에 추가하세요.
실전 팁
💡 - .env.example 파일을 만들어 필요한 환경 변수 목록을 문서화하세요. 값은 비워두거나 예시를 넣습니다.
- docker-compose config 명령어로 변수 치환 결과를 미리 확인하세요.
- CI/CD 환경에서는 .env 파일 대신 시스템 환경 변수나 시크릿 매니저를 사용하는 것이 더 안전합니다.
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
Roundcube 웹메일 인터페이스 완벽 가이드
Docker 컨테이너 기반으로 Roundcube 웹메일을 구축하고, Nginx 리버스 프록시부터 플러그인 관리, 테마 커스터마이징까지 전체 과정을 다룹니다. 초급 개발자도 쉽게 따라할 수 있는 실무 중심 가이드입니다.
ClamAV 바이러스 스캔 완벽 가이드
리눅스 서버에서 ClamAV를 활용한 바이러스 스캔 시스템을 구축하는 방법을 다룹니다. 메일 서버와 연동하여 악성코드를 자동으로 탐지하고 처리하는 실무 노하우를 배워봅니다.
스팸 필터링 완벽 가이드 SpamAssassin과 Rspamd 실전 활용
이메일 서버 운영에서 필수적인 스팸 필터링 기술을 다룹니다. SpamAssassin과 Rspamd의 차이점부터 베이지안 필터, 블랙리스트 연동, 커스텀 룰 작성까지 실무에서 바로 적용할 수 있는 내용을 담았습니다.
LDAP 인증 연동 완벽 가이드
LDAP을 활용한 중앙 집중식 인증 시스템 구축 방법을 알아봅니다. OpenLDAP 설정부터 Postfix, Dovecot 연동, 그리고 Active Directory 통합까지 실무에서 바로 적용할 수 있는 내용을 다룹니다.
SSL/TLS 인증서 설정 완벽 가이드 (Let's Encrypt)
메일 서버 운영에 필수적인 SSL/TLS 인증서 설정 방법을 다룹니다. Let's Encrypt를 활용한 무료 인증서 발급부터 자동 갱신까지, 실무에서 바로 적용할 수 있는 내용을 담았습니다.