Theme:

Docker Compose에서 서비스 이름만으로 다른 서비스에 접근할 수 있는 건 마법이 아닙니다. 내부에서 어떤 네트워크와 DNS 메커니즘이 동작하는지 이해하면 더 유연한 구성이 가능합니다.

Compose의 기본 네트워크

네트워크를 따로 선언하지 않으면, Docker Compose는 자동으로 default 네트워크를 생성합니다.

YAML
# compose.yaml
services:
  api:
    image: myapi:latest
  db:
    image: postgres:16-alpine
BASH
docker compose up -d

# 자동 생성된 네트워크 확인
docker network ls
# NETWORK ID     NAME                  DRIVER
# abc123         myproject_default     bridge

# 서비스 이름으로 통신 가능
docker compose exec api ping db
# PING db (172.20.0.3): 56 data bytes...

프로젝트 이름(myproject)은 디렉토리 이름 또는 COMPOSE_PROJECT_NAME에서 결정됩니다.

서비스 DNS 해석

Compose의 각 서비스는 서비스 이름으로 DNS 해석이 됩니다.

YAML
services:
  api:
    image: myapi:latest
    environment:
      # 서비스 이름을 호스트로 사용
      DB_HOST: db
      REDIS_HOST: redis
      CACHE_URL: redis://redis:6379

  db:
    image: postgres:16-alpine

  redis:
    image: redis:7-alpine

애플리케이션 코드에서 db, redis라는 호스트명을 그대로 사용할 수 있습니다. Docker의 내장 DNS 서버(127.0.0.11)가 서비스 이름을 해당 컨테이너의 IP로 해석합니다.

스케일링과 DNS

BASH
# api 서비스를 3개로 스케일링
docker compose up -d --scale api=3
BASH
# 'api'로 DNS 질의 시 모든 인스턴스의 IP 반환
docker compose exec db nslookup api
# Name: api
# Address: 172.20.0.4
# Address: 172.20.0.5
# Address: 172.20.0.6

기본적으로 라운드 로빈 방식으로 IP가 반환되어 간단한 로드 밸런싱이 됩니다. 하지만 클라이언트 측 DNS 캐싱에 주의해야 합니다.

네트워크 별칭

YAML
services:
  postgres-primary:
    image: postgres:16-alpine
    networks:
      backend:
        aliases:
          - db
          - database
          - primary-db

networks:
  backend:

db, database, primary-db 어떤 이름으로든 접근할 수 있습니다.

다중 네트워크 구성

기본 패턴: 프론트엔드/백엔드 분리

YAML
services:
  nginx:
    image: nginx:1.25-alpine
    ports:
      - "80:80"
    networks:
      - frontend

  api:
    image: myapi:latest
    networks:
      - frontend  # nginx에서 접근 가능
      - backend   # db에 접근 가능

  worker:
    image: myworker:latest
    networks:
      - backend   # db에 접근 가능, 외부 접근 불가

  db:
    image: postgres:16-alpine
    networks:
      - backend   # api와 worker만 접근 가능

  redis:
    image: redis:7-alpine
    networks:
      - backend

networks:
  frontend:
  backend:
PLAINTEXT
외부 트래픽


┌─ frontend ─────────────┐
│  nginx ──→ api          │
└───────────┼─────────────┘

┌─ backend ─┼─────────────┐
│  api ──→ db             │
│  worker ──→ db          │
│  api ──→ redis          │
│  worker ──→ redis       │
└─────────────────────────┘

nginx에서 db로 직접 접근은 불가능합니다. 서로 다른 네트워크에 있기 때문입니다.

네트워크 설정 옵션

YAML
networks:
  frontend:
    driver: bridge
    ipam:
      config:
        - subnet: 172.28.0.0/24
          gateway: 172.28.0.1

  backend:
    driver: bridge
    internal: true  # 외부 인터넷 접근 차단

internal: true로 설정하면 해당 네트워크의 컨테이너는 외부 인터넷에 접근할 수 없습니다. 보안이 중요한 백엔드 네트워크에 유용합니다.

다른 Compose 프로젝트와 네트워크 공유

서로 다른 Compose 파일(프로젝트)의 서비스가 통신해야 할 때 external 네트워크를 사용합니다.

프로젝트 A: 공유 인프라

YAML
# infra/compose.yaml
services:
  db:
    image: postgres:16-alpine
    networks:
      - shared

  redis:
    image: redis:7-alpine
    networks:
      - shared

networks:
  shared:
    name: shared-infra  # 명시적 네트워크 이름

프로젝트 B: 애플리케이션

YAML
# app/compose.yaml
services:
  api:
    image: myapi:latest
    environment:
      DB_HOST: db
      REDIS_HOST: redis
    networks:
      - shared-infra

networks:
  shared-infra:
    external: true  # 이미 존재하는 네트워크 사용
BASH
# 순서대로 실행
cd infra && docker compose up -d
cd ../app && docker compose up -d

# app의 api가 infra의 db, redis에 접근 가능

external: true는 Compose가 네트워크를 생성/삭제하지 않고, 이미 존재하는 네트워크를 참조합니다.

볼륨 공유 패턴

서비스 간 볼륨 공유

YAML
services:
  # API가 생성한 파일을 Nginx가 서빙
  api:
    image: myapi:latest
    volumes:
      - static-files:/app/static

  nginx:
    image: nginx:1.25-alpine
    volumes:
      - static-files:/usr/share/nginx/html/static:ro

volumes:
  static-files:

데이터 초기화 패턴

PostgreSQL, MySQL 등의 공식 이미지는 /docker-entrypoint-initdb.d/ 디렉토리의 스크립트를 최초 실행 시 자동으로 실행합니다.

YAML
services:
  db:
    image: postgres:16-alpine
    volumes:
      - pg-data:/var/lib/postgresql/data
      - ./db/init:/docker-entrypoint-initdb.d:ro
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: secret

volumes:
  pg-data:
SQL
-- db/init/01-schema.sql
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(255) UNIQUE NOT NULL
);

-- db/init/02-seed.sql
INSERT INTO users (name, email) VALUES
    ('테스트 사용자', 'test@example.com');

파일은 이름 순으로 실행됩니다. 번호 접두사를 붙여 실행 순서를 보장하세요.

주의: 초기화 스크립트는 볼륨이 비어있을 때(최초 실행)만 실행됩니다. 이미 데이터가 있으면 무시됩니다.

외부 볼륨 사용

YAML
services:
  api:
    volumes:
      - important-data:/data

volumes:
  important-data:
    external: true  # docker volume create important-data로 미리 생성

external: true 볼륨은 docker compose down -v로도 삭제되지 않아 중요한 데이터를 보호합니다.

볼륨 드라이버 설정

YAML
volumes:
  nfs-data:
    driver: local
    driver_opts:
      type: nfs
      o: addr=192.168.1.100,rw
      device: ":/shared/data"

  tmpfs-cache:
    driver: local
    driver_opts:
      type: tmpfs
      device: tmpfs
      o: size=100m

다중 Compose 파일 간 볼륨 공유

YAML
# 프로젝트 A
volumes:
  shared-uploads:
    name: app-uploads  # 명시적 이름

# 프로젝트 B
volumes:
  uploads:
    external: true
    name: app-uploads  # 같은 이름으로 참조

실전: 마이크로서비스 네트워크 구성

YAML
services:
  gateway:
    image: nginx:1.25-alpine
    ports:
      - "80:80"
    networks:
      - public

  user-service:
    image: user-svc:latest
    networks:
      - public
      - user-db-net
      - messaging

  order-service:
    image: order-svc:latest
    networks:
      - public
      - order-db-net
      - messaging

  user-db:
    image: postgres:16-alpine
    volumes:
      - user-db-data:/var/lib/postgresql/data
    networks:
      - user-db-net

  order-db:
    image: postgres:16-alpine
    volumes:
      - order-db-data:/var/lib/postgresql/data
    networks:
      - order-db-net

  rabbitmq:
    image: rabbitmq:3.13-management-alpine
    networks:
      - messaging

networks:
  public:
  user-db-net:
    internal: true
  order-db-net:
    internal: true
  messaging:
    internal: true

volumes:
  user-db-data:
  order-db-data:

각 서비스가 필요한 네트워크에만 연결되어 있어서 보안이 강화됩니다.

트러블슈팅

BASH
# 네트워크 상태 확인
docker network ls
docker network inspect myproject_default

# 서비스 간 DNS 해석 테스트
docker compose exec api nslookup db
docker compose exec api wget -qO- http://other-service:8080/health

# 연결된 네트워크 확인
docker inspect mycontainer --format '{{json .NetworkSettings.Networks}}' | jq

# 네트워크 재생성 (문제 해결 시)
docker compose down
docker compose up -d

정리

  • Docker Compose는 자동으로 default 네트워크를 생성하고, 서비스 이름으로 DNS 해석이 가능합니다.
  • 다중 네트워크로 프론트엔드/백엔드를 분리하여 보안을 강화합니다. internal: true로 외부 접근을 차단할 수 있습니다.
  • 다른 Compose 프로젝트와 통신이 필요하면 external: true 네트워크를 공유합니다.
  • 데이터베이스 초기화는 /docker-entrypoint-initdb.d/에 SQL/쉘 스크립트를 마운트합니다 (최초 실행 시에만 동작).
  • 중요한 데이터의 볼륨은 external: true로 선언하여 docker compose down -v로부터 보호합니다.
댓글 로딩 중...