Currently viewing the human version
Switch to AI version

Docker Compose가 뭔가?

현재 최신인 v2.39.3 Docker Compose는 컨테이너 여러 개를 한번에 관리하는 도구다. 웹서버, DB, Redis 이런 거 하나하나 띄우면서 명령어 치기 귀찮으니까 YAML 파일 하나에 정리해서 한방에 올리자는 거다.

왜 쓰는가?

docker run 여러 번 치면서 포트 번호 외우고 네트워크 설정 삽질하는 거 진짜 개짜증난다. 특히 신입이 와서 "이거 왜 안 돼요?" 소리 듣기 싫으면 Docker Compose 써라.

services:
  web:
    build: .
    ports:
      - "3000:3000"
    depends_on:
      - db
    
  db:
    image: postgres:15
    environment:
      POSTGRES_DB: myapp
      POSTGRES_PASSWORD: password

이거면 끝이다. docker compose up 치면 웹서버랑 PostgreSQL이 알아서 연결되서 뜬다.

실제로 어떻게 도움되는가?

프로젝트 클론받고 docker compose up 한 번이면 개발 환경 끝이다. "내 컴퓨터에서는 잘 됐는데"라는 개소리 이제 안 들어도 되고, 신입 와서 환경 설정으로 반나절 날려먹는 일도 없다.

DB 버전 바꾸고 싶으면 YAML에서 postgres:15postgres:16으로 바꾸고 다시 띄우면 끝이다. Redis 추가하고 싶으면 서비스 하나 더 추가하면 된다.

문제점도 존나 많다

메모리 개많이 먹는다. 특히 M1/M2 맥에서는 x86 이미지 쓰면 에뮬레이션 때문에 개느리다. ARM 이미지 쓰거나 Rosetta 설정 건드려라.

볼륨 마운트도 느리다. 특히 node_modules 같이 파일 많은 폴더는 정말 개느리다. 코드 수정했는데 반영되는 데 몇 초 걸리는 건 그냥 감수해야 한다. Mutagen 같은 파일 동기화 도구 써봤자 조금 나아질 뿐이다.

실제 사용 팁

depends_on은 거짓말쟁이다. 컨테이너만 시작시키고 서비스 준비까지는 안 기다린다. PostgreSQL 컨테이너는 떴는데 아직 DB 초기화 중이면 웹서버가 연결 못하고 뒤진다. 이럴 때는 healthcheck 쓰거나 연결 재시도 로직 넣어라.

db:
  image: postgres:15
  healthcheck:
    test: ["CMD-SHELL", "pg_isready -U postgres"]
    interval: 5s
    retries: 5

이렇게 하면 DB가 진짜 준비될 때까지 기다린다.

Docker Compose vs 다른 도구들

도구

쓸만한가?

왜 그런가?

언제 쓰나?

Docker Compose

괜찮음

간단하고 문서 많음

개발할 때, 소규모 배포

Kubernetes

개복잡함

기능은 많은데 러닝커브 존나빡셈

회사에서 시키면 어쩔 수 없이

Podman Compose

가벼움

Docker 없이도 되고 메모리 적게 먹음

개인 프로젝트, 보안 중요한 곳

Vagrant

이제 아무도 안 씀

VM이라 무겁고 느림

레거시 프로젝트에서나

실제로 Docker Compose 쓸 때 알아둘 것들

계속 터지는 것들 정리해둠

Docker Compose 쓰다 보면 계속 똑같은 문제들이 터진다. 미리 알고 있으면 삽질 안 해도 된다. 공식 가이드도 보긴 하는데 별로 도움 안 된다.

depends_on은 거짓말쟁이

개발자들이 제일 많이 하는 삽질이다. depends_on은 컨테이너만 시작시키고 서비스 준비까지는 안 기다린다. Stack Overflow에서도 이 문제로 빡치는 사람들 개많다.

services:
  web:
    build: .
    depends_on:
      - db  # 이거면 안 됨
  
  db:
    image: postgres:15

PostgreSQL 컨테이너는 떴는데 아직 DB 초기화 중이면 웹앱이 연결 못하고 뒤진다. 진짜 기다리려면 healthcheck 써야 한다.

services:
  web:
    build: .
    depends_on:
      db:
        condition: service_healthy
  
  db:
    image: postgres:15
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      retries: 5

환경변수 관리

YAML에 비밀번호 하드코딩하지 마라. .env 파일 써라.

## .env 파일
DB_PASSWORD=mysecretpassword
DB_USER=postgres
## docker-compose.yml
services:
  db:
    image: postgres:15
    environment:
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD: ${DB_PASSWORD}

그리고 .env.gitignore에 넣어라. 안 그러면 GitHub에 비밀번호 올라간다. GitLeaks 같은 도구로 미리 체크할 수도 있다.

볼륨 문제

컨테이너 죽으면 데이터 날아가는 게 싫으면 named volume 써라.

services:
  db:
    image: postgres:15
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

바인드 마운트(./data:/var/lib/postgresql/data)는 권한 문제 생길 수 있다. 특히 리눅스에서.

메모리 부족

컨테이너가 계속 죽으면 메모리 한계 확인해봐라. OOM Killer가 컨테이너를 죽였을 수도 있다.

services:
  web:
    build: .
    deploy:
      resources:
        limits:
          memory: 1G

기본값이 너무 작을 수 있다. docker stats로 실제 메모리 사용량 확인해보라. 진짜 Node.js 앱 하나 돌리는데 512M은 개부족하다.

포트 충돌

"port already in use" 에러 나면:

  1. docker compose down 먼저 해보라
  2. 안 되면 lsof -i :5432 로 뭐가 포트 점유하고 있는지 확인
  3. 아니면 포트 번호 바꿔라. 5432 말고 5433 이런 식으로.

Watch 모드 (코드 변경 감지)

v2.22부터 생긴 기능인데 개발할 때 유용하다. 코드 바뀌면 자동으로 재시작된다.

services:
  web:
    build: .
    develop:
      watch:
        - action: sync
          path: ./src
          target: /app/src
        - action: rebuild
          path: ./package.json

하지만 Node.js에서 node_modules 볼륨 마운트하면 개느리다. 이건 어쩔 수 없다. 진짜로 코드 저장했는데 5초 지나서 반영되는 거 보면 짜증난다. nodemon이나 ts-node-dev 같은 도구와 함께 쓰면 더 좋다.

프로덕션에서 쓰면 안 되는 이유

Docker Compose는 개발/테스트용이다. 프로덕션에서 쓰면:

  • 서버 하나 죽으면 전체 서비스 죽음 (single point of failure)
  • 무중단 배포 어려움
  • 모니터링/로깅 부족
  • 자동 복구 기능 없음

진짜 프로덕션은 Kubernetes나 AWS ECS 같은 거 써라. Docker Compose로 운영하다가 서버 뒤져서 새벽 3시에 전화 받기 싫으면 말이다.

실제 에러 해결법

"network not found"

docker compose down 하고 다시 up 해라. 네트워크가 꼬였을 가능성 높다.

"volume in use"

docker compose down -v 로 볼륨까지 지우고 다시 시작해라. (데이터 날아감 주의)

"no space left on device"

docker system prune -a 로 안 쓰는 이미지들 정리해라. 5분 정도 걸리고 몇 기가씩 정리된다.

M1/M2 맥에서 느림

ARM 이미지 써라. postgres:15-alpine 이런 식으로. x86 이미지는 에뮬레이션해서 개느리다. 진짜 PostgreSQL 띄우는데 30초 걸리는 거 보면 맥 던지고 싶어진다.

자주 터지는 문제들

Q

왜 컨테이너가 계속 죽나?

A

보통 두 가지 중 하나다:

  1. 메모리 부족: docker compose logs에서 "OOMKilled" 확인
  2. 포트 충돌: "port already in use" 에러

메모리 부족이면 deploy.resources.limits.memory 늘려라. 포트 충돌이면 docker compose down 하고 다시 시작하거나 포트 번호 바꿔라.

Q

depends_on 썼는데 왜 연결 안 돼?

A

depends_on은 컨테이너만 시작시키고 서비스 준비까지 기다리지 않는다. PostgreSQL 컨테이너는 떴는데 아직 DB 초기화 중이면 연결 못하고 뒤진다.

해결법: healthcheck 써라.

db:
  image: postgres:15
  healthcheck:
    test: ["CMD-SHELL", "pg_isready"]
    interval: 5s
Q

YAML 문법 오류 계속 나는데?

A
  • 탭 쓰지 마라. 스페이스만 써라
  • 들여쓰기 정확히 맞춰라
  • VS Code 쓰면 YAML 확장 설치해서 에러 미리 확인해라
Q

M1/M2 맥에서 개느려

A

x86 이미지 쓰고 있을 거다. ARM 이미지로 바꿔라:

  • postgres:15postgres:15 (이미 ARM 지원)
  • node:18node:18-alpine
  • 안 되면 Dockerfile에서 FROM --platform=linux/arm64/v8 명시
Q

볼륨 마운트가 느려

A

특히 Node.js node_modules 마운트하면 개느리다. 해결법:

  1. node_modules는 마운트 말고 컨테이너 안에 두기
  2. Docker Desktop에서 VirtioFS 활성화
  3. 정 안 되면 Mutagen 쓰기
Q

빌드가 너무 오래 걸려

A

.dockerignore 파일 만들어서 불필요한 파일들 제외해라:

node_modules
.git
*.log
Q

환경변수 어떻게 관리하나?

A

.env 파일 써라. YAML에 하드코딩하지 마라.

## .env
DB_PASSWORD=secret123
## docker-compose.yml
environment:
  DB_PASSWORD: ${DB_PASSWORD}

.env는 꼭 .gitignore에 넣어라.

Q

데이터가 컨테이너 재시작하면 사라져

A

바인드 마운트 대신 named volume 써라:

volumes:
  - postgres_data:/var/lib/postgresql/data  # 이렇게

volumes:
  postgres_data:  # 여기서 정의
Q

네트워크 에러 계속 나는데?

A

docker compose down 하고 다시 up 해봐라. 네트워크가 꼬였을 가능성 높다.

Q

"no space left on device" 에러

A

Docker 이미지들이 디스크 꽉 채웠을 거다.

docker system prune -a  # 안 쓰는 이미지 전부 삭제

주의: 필요한 이미지도 삭제될 수 있다.

Q

로그가 너무 많이 쌓여

A

로그 크기 제한 걸어라:

logging:
  options:
    max-size: "10m"
    max-file: "3"
Q

메모리 사용량이 너무 높아

A

리소스 제한 걸어둬라:

deploy:
  resources:
    limits:
      memory: 512M
      cpus: '0.5'
Q

코드 변경이 반영 안 돼

A

볼륨 마운트 제대로 했는지 확인해라:

volumes:
  - ./src:/app/src  # 현재 폴더의 src를 컨테이너의 /app/src에 마운트

Node.js면 nodemon, Python이면 watchdog 같은 파일 감시 도구도 필요하다.

Q

Hot reload가 안 돼

A

Watch 모드 써봐라 (v2.22+):

develop:
  watch:
    - action: sync
      path: ./src
      target: /app/src
Q

컨테이너 간 통신이 안 돼

A

서비스 이름으로 접근해라. IP 주소 쓰지 마라.

## 이렇게 해라
host="db"  # 서비스 이름

## 이렇게 하지 마라  
host="172.18.0.2"  # IP 주소
Q

"Error: connect ECONNREFUSED"

A

DB가 아직 안 떴거나 잘못된 호스트명 썼을 거다. 서비스 이름 확인하고 healthcheck 써라.

Q

"Exit code 137"

A

메모리 부족으로 killed 당했다. 메모리 제한 늘려라.

Q

"network xxx not found"

A

docker compose down 하고 다시 시작해라.

Q

"volume is in use"

A

다른 컨테이너가 볼륨 쓰고 있을 거다. docker compose down -v로 강제로 지워라 (데이터 날아감).

실제 도움되는 자료들