← 전체 글로 돌아가기

Docker

Docker Compose에서 컨테이너 이름에 의존하지 않기

Docker Compose로 여러 컨테이너를 관리할 때, 이름 대신 서비스 이름으로 통신하는 방법을 정리했다.

Docker Compose를 사용할 때, 컨테이너 간에 통신할 때 컨테이너의 실제 이름(예: my-app-db-1)에 의존하면 문제가 생긴다. 컨테이너는 재시작될 때마다 이름이 바뀔 수 있기 때문이다.

대신, docker-compose.yml에서 정의한 서비스 이름(예: db)을 호스트명으로 사용해야 한다.

문제: 컨테이너 이름에 의존

version: '3'
services:
  app:
    image: myapp:latest
    environment:
      DATABASE_URL: "postgresql://user:pass@my-app-db-1:5432/mydb"

  db:
    image: postgres:15
    container_name: my-app-db-1  # 실제 컨테이너 이름

이렇게 하면, 컨테이너를 다시 만들 때마다 이름이 my-app-db-2, my-app-db-3 식으로 변경되고, 환경변수는 구식 이름을 가리키게 된다.

해결: 서비스 이름 사용

version: '3.8'
services:
  app:
    build: .
    environment:
      DATABASE_URL: "postgresql://user:pass@db:5432/mydb"
    depends_on:
      - db

  db:
    image: postgres:15
    environment:
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: mydb

중요한 포인트:

  1. 서비스 이름은 db
  2. app 서비스에서 db로 접속할 때 호스트명은 db 사용
  3. container_name은 지정하지 않음 (또는 지정해도 상관없지만 의존하지 않음)

Docker Compose 네트워크

Docker Compose는 자동으로 서비스들 간 네트워크를 만든다. 같은 Compose 프로젝트의 서비스들은 서비스 이름으로 서로를 찾을 수 있다.

# 컨테이너 내부에서 다른 서비스에 접속
docker-compose exec app ping db
# PING db (172.20.0.2)

Node.js 애플리케이션 예시

// config/database.js
const sequelize = new Sequelize({
  host: process.env.DB_HOST || 'db',  // 'db'가 기본값 (서비스 이름)
  port: 5432,
  username: 'user',
  password: 'pass',
  database: 'mydb',
});

환경변수에서 오버라이드하지 않으면, 자동으로 서비스 이름 db를 사용한다.

depends_on 사용

version: '3.8'
services:
  app:
    build: .
    depends_on:
      db:
        condition: service_healthy

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

depends_ondb 서비스가 시작되고 healthy 상태가 될 때까지 app의 시작을 지연시킨다.

여러 네트워크를 사용하는 경우

version: '3.8'
services:
  web:
    build: .
    networks:
      - frontend
      - backend

  db:
    image: postgres:15
    networks:
      - backend

  cache:
    image: redis:7
    networks:
      - frontend
      - backend

networks:
  frontend:
  backend:

같은 네트워크에 속한 서비스들만 서로 통신할 수 있다. webdb에는 접근 가능하지만, cache로는 webcachedb 경로로 통신한다.

실수하기 쉬운 부분

  1. localhost 사용: DATABASE_URL: localhost:5432 - 이건 컨테이너 내부에서 작동 안 함

    • 올바른 방법: DATABASE_URL: db:5432
  2. 컨테이너 이름 직접 사용: container_name: specific-name으로 고정하면, 확장성이 떨어짐

    • 올바른 방법: container_name 생략하고 서비스 이름 사용
  3. 외부에서 컨테이너 접속: 컨테이너 내부에서는 서비스 이름, 외부에서는 localhost 사용

   # 컨테이너 내부
   docker-compose exec app psql -h db -U user mydb

   # 호스트에서
   psql -h localhost -p 5432 -U user mydb

테스트하기

# 컨테이너가 제대로 통신하는지 확인
docker-compose up -d

# 앱 컨테이너에서 db 서비스에 ping
docker-compose exec app ping db

# 또는 curl로 다른 서비스 접속 테스트
docker-compose exec web curl http://api:3000/health

프로덕션 배포

Docker Compose는 개발 환경에서 주로 사용된다. 프로덕션에서는 Kubernetes나 Docker Swarm 같은 오케스트레이션 도구를 사용하면, 비슷한 서비스 디스커버리 기능을 제공한다.

최종 체크리스트

  1. 환경변수에 컨테이너 실제 이름을 하드코딩했는가? (제거)
  2. 서비스 이름으로 통신하는가?
  3. depends_on으로 서비스 시작 순서를 명시했는가?
  4. 네트워크 설정이 필요한가? (여러 네트워크 사용 시)
  5. 로컬 테스트에서 통신이 제대로 되는가?

Docker Compose의 서비스 디스커버리를 제대로 이해하면, 컨테이너를 재시작해도 안정적으로 통신할 수 있다.