infra
Platform

모듈 맵

[Docker] Docker 네트워크 드라이브 구조와 DNS 기반 컨테이너 연결

0 / 27 완료

펼치기
0 / 27 완료0%

Docker · 10 / 27

[Docker] Docker 네트워크 드라이브 구조와 DNS 기반 컨테이너 연결

Bridge/Host/None 네트워크 모드와 사용자 정의 네트워크로 컨테이너 간 통신을 구성합니다

🚨INCIDENT ALERT
HIGH

웹 컨테이너에서 curl http://db:5432를 실행했는데 이름을 찾지 못한다는 에러가 났습니다. 두 컨테이너는 같은 서버에 있었지만 기본 bridge 네트워크에서는 컨테이너 이름을 DNS로 해석해주지 않았습니다.

IP를 직접 넣어 임시로 해결했더니 재시작 후 IP가 바뀌어 다시 깨졌습니다. 컨테이너 간 통신은 네트워크를 어떻게 만들고 연결하느냐에 따라 안정성이 완전히 달라집니다.

컨테이너 간 통신과 Docker 네트워크

컨테이너는 기본적으로 서로 격리된 환경에서 동작합니다. 웹 서버가 데이터베이스에 접근하고, 마이크로서비스들이 서로 API를 호출하려면 네트워크 설정이 필요합니다. Docker 네트워크를 이해하면 컨테이너 간 통신을 안전하고 효율적으로 구성할 수 있습니다.


이번 챕터에서 배울 것

컨테이너들이 어떻게 서로를 찾고 통신하는지, 네트워크를 어떻게 격리해야 보안이 강화되는지 직접 실습으로 이해합니다.

  • 1Docker 네트워크 드라이버 4종 — bridge / host / none / overlay 비교
  • 2기본 bridge 네트워크의 한계와 사용자 정의 bridge 네트워크의 장점
  • 3docker network create로 격리된 네트워크 구성 및 컨테이너 연결
  • 4사용자 정의 네트워크에서 컨테이너 이름으로 DNS 해석되는 원리
  • 5컨테이너 간 통신 허용/차단 설계 — 네트워크 격리 전략
실습 환경 준비

컨테이너 간 통신 실습을 위해 curl 도구가 필요합니다. 호스트에 없더라도 curlimages/curl 컨테이너로 대체할 수 있습니다.

Docker 실행 중인지 확인
docker ps
curl 설치 (네트워크 통신 테스트용)
which curl || sudo apt-get install -y curl
현재 Docker 네트워크 목록 확인
docker network ls
실습용 이미지 pull
docker pull nginx:alpine && docker pull curlimages/curl
💡개념

Docker 네트워크의 세 가지 기본 드라이버

웹 서버 컨테이너에서 curl http://db:5432를 실행했는데 연결이 안 됩니다. 같은 호스트에서 두 컨테이너를 실행했는데도 서로 찾지 못하는 상황입니다. 컨테이너는 기본적으로 격리된 네트워크 네임스페이스를 가지기 때문에, 어떤 네트워크 드라이버를 통해 연결하느냐에 따라 통신 방식이 완전히 달라집니다. 기본 브릿지(docker0)는 IP 주소로만 통신 가능하고, 사용자 정의 네트워크는 이름으로 통신 가능합니다. Host 모드는 격리를 포기하고 최고 성능을 택하며, None은 네트워크를 완전히 차단합니다. 이 ConceptBlock에서는 Docker가 제공하는 세 가지 기본 네트워크 드라이버와 각각의 동작 방식을 다룹니다.

Docker는 컨테이너 간 통신 방식에 따라 세 가지 핵심 네트워크 드라이버를 제공합니다.

Docker 네트워크 드라이버 비교

브릿지(Bridge) 네트워크

브릿지 네트워크는 Docker의 기본 네트워크 모드입니다. Docker를 설치하면 docker0라는 가상 브릿지 인터페이스가 자동 생성되고, 별도 설정 없이 실행한 모든 컨테이너는 이 브릿지에 연결됩니다. 호스트의 물리 네트워크 위에 Docker가 만든 가상 스위치로 이해하면 됩니다. 아래 구조를 보면 컨테이너들이 호스트와 분리된 사설 IP 대역에서 동작한다는 것이 명확해집니다.

호스트 머신
├── eth0 (물리 네트워크: 192.168.1.100)
└── docker0 (가상 브릿지: 172.17.0.1)
        ├── container-A (172.17.0.2)
        └── container-B (172.17.0.3)

컨테이너들은 172.17.0.x 대역의 사설 IP를 받아서 서로 통신할 수 있습니다. 단, 기본 브릿지(docker0)에는 내장 DNS가 없습니다. 이것이 핵심 제약사항입니다.

Host 네트워크

--network host 옵션을 사용하면 컨테이너가 호스트의 네트워크 네임스페이스를 그대로 사용합니다. 가상 네트워크 인터페이스가 없으므로 포트 매핑(-p)도 불필요합니다.

로컬 터미널
# 실습 디렉토리 준비
mkdir -p /tmp/docker/part3/exam_10 && cd /tmp/docker/part3/exam_10

# Host 모드로 nginx 실행 — 호스트의 80포트를 직접 사용
docker run --network host nginx

장점: NAT 오버헤드 없이 최고의 네트워크 성능 단점: 포트 충돌 위험, 컨테이너 격리 약화, macOS/Windows에서 미지원

None 네트워크

--network none은 루프백(127.0.0.1) 외 모든 네트워크 인터페이스를 비활성화합니다. 완전한 네트워크 격리가 필요한 보안 작업이나 배치 처리에 사용합니다.

Docker
# 네트워크 없는 완전 격리 컨테이너
docker run --network none alpine sh -c "echo '외부 접근 불가'"

현재 네트워크 목록 확인

로컬 터미널
docker network ls
# NETWORK ID     NAME      DRIVER    SCOPE
# abc123def456   bridge    bridge    local   ← 기본 docker0
# xyz789uvw012   host      host      local
# 111aaa222bbb   none      null      local
위험 명령어컨테이너에 연결되지 않은 사용자 정의 네트워크가 삭제되어 실습/개발 연결 구성이 초기화될 수 있습니다.

미사용 Docker 네트워크 삭제

안전한 실행 조건: 삭제 대상이 실습 또는 임시 네트워크뿐임을 확인한 경우에만 실행합니다.

실행 전 반드시 확인

  • docker network ls로 네트워크 목록을 확인했다
  • 운영 Compose/Swarm 네트워크가 아님을 확인했다
  • 연결된 컨테이너가 없는 네트워크만 삭제 대상임을 이해했다
docker network prune

위 항목을 모두 확인한 후 복사할 수 있습니다


💡개념

사용자 정의 네트워크와 내장 DNS

기본 브릿지(docker0)의 가장 큰 단점은 컨테이너 이름으로 통신이 불가능하다는 점입니다. IP는 재시작마다 바뀔 수 있기 때문에, 안정적인 서비스 간 통신을 위해서는 사용자 정의 네트워크를 만들어야 합니다.

사용자 정의 네트워크의 핵심 기능: 내장 DNS

사용자 정의 네트워크에는 Docker가 자동으로 **내장 DNS 서버(127.0.0.11)**를 제공합니다. 이 DNS 서버는 컨테이너 이름을 자동으로 해당 컨테이너의 IP로 변환해줍니다.

사용자 정의 네트워크와 내장 DNS

사용자 정의 네트워크(my_network)
├── 내장 DNS 서버: 127.0.0.11
├── web 컨테이너 (172.20.0.2) → "web" 이름으로 접근 가능
└── db 컨테이너 (172.20.0.3)  → "db" 이름으로 접근 가능

web 컨테이너 내부에서:
$ ping db  → 172.20.0.3으로 자동 해석 ✓
$ curl http://db:5432  → 동작 ✓

기본 브릿지 vs 사용자 정의 네트워크 비교

두 네트워크 타입은 겉으로는 비슷해 보이지만 실무에서 차이가 확연합니다. 기본 브릿지는 테스트용으로는 충분하지만, Docker Compose로 여러 서비스를 연결하는 순간 사용자 정의 네트워크가 필수가 됩니다.

기능기본 브릿지(docker0)사용자 정의 네트워크
컨테이너 이름 DNS불가가능
자동 서비스 디스커버리불가가능
네트워크 격리모든 컨테이너 공유네트워크별 격리
--link 의존성필요(deprecated)불필요
컨테이너 별칭 설정불가--network-alias 가능

네트워크 생성과 관리 명령어

로컬 터미널
# 사용자 정의 브릿지 네트워크 생성
docker network create my_net

# 서브넷과 게이트웨이를 명시적으로 지정
docker network create \
  --driver bridge \
  --subnet 172.20.0.0/16 \
  --gateway 172.20.0.1 \
  my_net

# 네트워크 상세 정보 확인
docker network inspect my_net

# 네트워크에 연결된 컨테이너 목록 확인
docker network inspect my_net --format '{{range .Containers}}{{.Name}} {{end}}'

# 사용하지 않는 네트워크 정리

기본 브릿지 네트워크 제약사항 확인

먼저 기본 브릿지에서 컨테이너 이름으로 통신이 안 된다는 것을 직접 확인해봅니다.

실습 전 네트워크 실습용 디렉토리를 준비합니다.

로컬 터미널
# 실습 디렉토리 준비
mkdir -p /tmp/docker/part2/network-demo && cd /tmp/docker/part2/network-demo

# 멀티 서비스 네트워크 구성 예제 파일 생성
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
  frontend:
    image: nginx:alpine
    ports:
      - "8081:80"
    networks:
      - frontend-net
  backend:
    image: python:3.11-slim
    command: python -m http.server 8080
    networks:
      - frontend-net
      - backend-net
  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_PASSWORD: secret
    networks:
      - backend-net
networks:
  frontend-net:
  backend-net:
EOF

1단계: 기본 네트워크에서 두 컨테이너 실행

Docker
# 터미널 1: 첫 번째 컨테이너 실행
docker run -d --name container-a alpine sleep 3600

# 터미널 2: 두 번째 컨테이너 실행
docker run -d --name container-b alpine sleep 3600

2단계: 각 컨테이너의 IP 확인

로컬 터미널
# container-a의 IP 확인
docker inspect container-a --format '{{.NetworkSettings.IPAddress}}'
# 예시 출력: 172.17.0.2

# container-b의 IP 확인
docker inspect container-b --format '{{.NetworkSettings.IPAddress}}'
# 예시 출력: 172.17.0.3

3단계: 이름으로 ping 시도 (실패 예상)

Docker
docker exec container-a ping -c 2 container-b
# ping: bad address 'container-b'  ← 이름 해석 불가!

# IP로는 통신 가능
docker exec container-a ping -c 2 172.17.0.3
# PING 172.17.0.3: 56 data bytes — 성공

4단계: 정리

위험 명령어지정한 컨테이너가 실행 중이어도 즉시 종료 후 삭제됩니다.

네트워크 실습 컨테이너 강제 삭제

안전한 실행 조건: container-a와 container-b가 이 모듈에서 만든 실습용 컨테이너인 경우에만 실행합니다.

실행 전 반드시 확인

  • docker ps -a에서 두 컨테이너 이름을 확인했다
  • 컨테이너 내부에 보존할 데이터가 없음을 확인했다
  • 삭제 후 네트워크 실습을 다시 만들 수 있다
docker rm -f container-a container-b

위 항목을 모두 확인한 후 복사할 수 있습니다

기본 브릿지에서는 IP가 아닌 이름으로 통신이 불가능함을 확인했습니다.

🔍실행 후 확인할 것
  • container-a와 container-b의 IP가 서로 다른 172.17.x.x 주소로 표시되는가?
  • container-a에서 container-b 이름으로 ping할 때 bad address가 발생하는가?
  • IP 주소로 ping하면 통신이 되는가?
  • 기본 bridge 네트워크에서는 이름 기반 DNS가 없다는 결론을 설명할 수 있는가?

사용자 정의 네트워크로 이름 기반 통신 구현

사용자 정의 네트워크를 만들어 컨테이너 이름으로 통신하는 방법을 실습합니다.

1단계: 사용자 정의 네트워크 생성

로컬 터미널
docker network create my_net
docker network ls
# my_net이 bridge 드라이버로 생성된 것 확인

2단계: 동일 네트워크에 컨테이너 연결

Docker
# --network my_net 으로 사용자 정의 네트워크에 연결
docker run -d --name web --network my_net nginx:alpine
docker run -d --name db  --network my_net alpine sleep 3600

3단계: 컨테이너 이름으로 ping 통신

Docker
# db 컨테이너에서 web 컨테이너로 이름 기반 ping
docker exec db ping -c 3 web
# PING web (172.20.0.2): 56 data bytes
# 64 bytes from 172.20.0.2: seq=0 ttl=64 time=0.123 ms ← 성공!

# web 컨테이너에서 db로도 통신 가능
docker exec web ping -c 3 db
# PING db (172.20.0.3): 56 data bytes — 성공

4단계: DNS 서버 확인

Docker
# 컨테이너 내부 DNS 설정 확인
docker exec db cat /etc/resolv.conf
# nameserver 127.0.0.11  ← Docker 내장 DNS
# options ndots:0

5단계: 네트워크 검사

로컬 터미널
docker network inspect my_net
# "Containers" 섹션에서 web, db 컨테이너 확인

6단계: 정리

로컬 터미널
docker network rm my_net

실행 중인 컨테이너를 네트워크에 동적으로 연결/해제

컨테이너를 다시 시작하지 않고 네트워크에 연결하거나 해제할 수 있습니다.

1단계: 컨테이너와 두 개의 네트워크 생성

로컬 터미널
docker network create net-frontend
docker network create net-backend

docker run -d --name api-server --network net-frontend alpine sleep 3600
docker run -d --name cache     --network net-backend  alpine sleep 3600

2단계: api-server를 backend 네트워크에도 연결

로컬 터미널
# 실행 중인 컨테이너에 네트워크 추가 연결
docker network connect net-backend api-server

# api-server는 이제 두 네트워크 모두에 연결됨
docker inspect api-server --format '{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}'
# net-frontend net-backend

3단계: 통신 확인

Docker
# api-server에서 cache(net-backend에만 있음)로 통신
docker exec api-server ping -c 2 cache
# 성공 — api-server가 net-backend에 연결되어 있으므로

4단계: 네트워크 연결 해제

로컬 터미널
# api-server를 net-backend에서 분리
docker network disconnect net-backend api-server

# 이제 cache로의 통신 불가
docker exec api-server ping -c 2 cache
# ping: bad address 'cache'

5단계: 정리

로컬 터미널
docker network rm net-frontend net-backend

증상

두 컨테이너를 실행하고 서로 통신을 시도했는데, 컨테이너 이름으로 ping이 실패하거나 connection refused가 발생합니다.

Docker
docker exec app ping -c 2 database
# ping: bad address 'database'

원인 진단

1. 같은 네트워크에 있는지 확인

로컬 터미널
# 각 컨테이너가 어떤 네트워크에 연결되어 있는지 확인
docker inspect app      --format '{{range $k,$v := .NetworkSettings.Networks}}{{$k}}{{end}}'
docker inspect database --format '{{range $k,$v := .NetworkSettings.Networks}}{{$k}}{{end}}'

두 컨테이너가 서로 다른 네트워크에 있으면 통신 불가.

2. 기본 브릿지(docker0) 사용 여부 확인

출력이 bridge(기본값)라면 사용자 정의 네트워크가 아닌 것입니다. 기본 브릿지에는 DNS가 없으므로 이름으로 통신 불가.

해결 방법

방법 1: 사용자 정의 네트워크로 재생성

로컬 터미널
docker network create my_app_net
docker run -d --name app      --network my_app_net my_app_image
docker run -d --name database --network my_app_net postgres:14

방법 2: 실행 중인 컨테이너에 네트워크 연결

로컬 터미널
# 기존 컨테이너를 중단 없이 네트워크에 추가 연결
docker network connect my_app_net app
docker network connect my_app_net database

검증

Docker
docker exec app ping -c 2 database
# PING database: 성공

증상

--network host 옵션으로 컨테이너를 실행했는데, 호스트 IP로 접근이 안 됩니다.

Docker
docker run --network host nginx
# 브라우저에서 localhost:80 접근 시도 → 연결 거부

원인

Host 네트워크 모드는 리눅스 전용 기능입니다. macOS와 Windows에서 Docker Desktop은 리눅스 VM 위에서 동작하기 때문에, --network host를 사용해도 컨테이너가 공유하는 것은 VM의 네트워크이지 실제 macOS/Windows 호스트 네트워크가 아닙니다.

Docker
# macOS에서 확인
docker run --network host alpine ip addr
# 172.x.x.x 대역 출력 — macOS의 실제 네트워크가 아님

해결 방법

macOS/Windows 환경에서는 Host 모드 대신 포트 매핑을 사용합니다.

Docker
# Host 모드 대신 포트 매핑으로 대체
docker run -p 80:80 nginx

리눅스 서버(프로덕션)에서만 Host 모드의 성능 이점을 활용하고, 로컬 개발 환경(macOS/Windows)에서는 포트 매핑 방식을 사용하는 것이 권장됩니다.

환경 감지 스크립트 예시

로컬 터미널
OS_TYPE=$(uname -s)
if [ "$OS_TYPE" = "Linux" ]; then
  NETWORK_FLAG="--network host"
else
  NETWORK_FLAG="-p 8080:8080"
fi
docker run $NETWORK_FLAG my_service

💼
실무 맥락
현업 패턴

실무에서는 서비스 유형에 따라 네트워크를 분리해 보안을 강화합니다. 외부에 노출해야 하는 서비스와 내부 서비스를 다른 네트워크에 배치하는 패턴이 일반적입니다.

실무 네트워크 구성 패턴

[인터넷]
    ↓
[frontend 네트워크]
    ├── nginx (리버스 프록시)
    └── web-app (React 빌드 파일 서빙)
            ↓ (api-gateway만 두 네트워크에 연결)
[backend 네트워크]
    ├── api-gateway
    ├── auth-service
    ├── product-service
    └── order-service
            ↓
[data 네트워크]
    ├── postgres
    └── redis

실제 적용 예시

로컬 터미널
# 네트워크 생성
docker network create net-frontend
docker network create net-backend
docker network create net-data

# 프론트엔드 레이어
docker run -d --name nginx    --network net-frontend -p 80:80 nginx
docker run -d --name web-app  --network net-frontend my_frontend:latest

# API 게이트웨이 — 두 네트워크에 모두 연결 (브릿지 역할)
docker run -d --name api-gateway --network net-frontend my_gateway:latest
docker network connect net-backend api-gateway

# 백엔드 서비스
docker run -d --name auth-service    --network net-backend my_auth:latest
docker run -d --name product-service --network net-backend my_product:latest

# 데이터 레이어
docker run -d --name postgres --network net-data postgres:14
# product-service를 data 네트워크에도 연결
docker network connect net-data product-service

이 패턴의 보안상 이점

  • nginx는 인터넷에 노출되어 있지만, 직접 DB에 접근 불가
  • postgres는 data 네트워크에만 있어서 외부에서 직접 접근 불가
  • 침해사고 발생 시 피해 범위가 해당 네트워크로 제한됨
  • Docker Compose의 networks 섹션으로 이 구성을 코드로 관리 가능

현업 DevOps 엔지니어는 이런 네트워크 분리를 Docker Compose 파일이나 Kubernetes NetworkPolicy로 선언적으로 관리합니다.


증상

두 컨테이너가 동일한 사용자 정의 네트워크에 있고 ping도 성공하는데, 특정 포트로 연결이 되지 않습니다.

Docker
docker exec web curl http://api:8080/health
# curl: (7) Failed to connect to api port 8080: Connection refused

원인 진단

1. 컨테이너가 실제로 해당 포트를 열고 있는지 확인

Docker
# api 컨테이너에서 실제 리스닝 포트 확인
docker exec api netstat -tlnp 2>/dev/null || docker exec api ss -tlnp
# tcp  0  0  0.0.0.0:3000  0.0.0.0:*  LISTEN  (포트 번호 확인)

포트가 8080이 아닌 3000으로 열려 있다면 잘못된 포트를 지정한 것입니다.

2. 애플리케이션이 루프백에만 바인딩되어 있는지 확인

Docker
docker exec api ss -tlnp
# tcp  0  0  127.0.0.1:8080  0.0.0.0:*  LISTEN
#           ↑ 루프백(127.0.0.1)에만 바인딩 → 외부 접근 불가!

애플리케이션이 127.0.0.1:8080에 바인딩되어 있으면 같은 컨테이너 내부에서만 접근 가능합니다. 다른 컨테이너에서 접근하려면 0.0.0.0:8080으로 바인딩해야 합니다.

해결 방법

애플리케이션 설정 변경 (루프백 → 모든 인터페이스)

로컬 터미널
# Node.js 예시 — 0.0.0.0으로 바인딩 변경
app.listen(8080, '0.0.0.0', () => {
  console.log('서버 시작: 0.0.0.0:8080');
});

# Python FastAPI 예시
uvicorn main:app --host 0.0.0.0 --port 8080

확인 방법

Docker
# 변경 후 바인딩 재확인
docker exec api ss -tlnp
# tcp  0  0  0.0.0.0:8080  0.0.0.0:*  LISTEN ← 올바름

# 연결 테스트
docker exec web curl http://api:8080/health
# {"status": "ok"} ← 성공!

포트 번호 불일치인 경우

로컬 터미널
# EXPOSE는 문서화 목적일 뿐, 실제 포트와 다를 수 있음
docker inspect api --format '{{.Config.ExposedPorts}}'
# map[8080/tcp:{}]  — Dockerfile의 EXPOSE 값

# 실제 리스닝 포트는 별도 확인
docker exec api ss -tlnp | grep LISTEN

💡개념

네트워크 디버깅 — 컨테이너 통신이 안 될 때 진단하는 법

두 컨테이너가 같은 Compose 파일에 있는데 서로 통신이 안 됩니다. 에러 로그에는 "Connection refused" 또는 "No such host"가 뜹니다. 네트워크 문제는 원인이 여러 개일 수 있어서 무작정 설정을 바꾸면 오히려 혼란이 커집니다. 컨테이너가 같은 네트워크에 속해 있는지, DNS가 이름을 올바르게 해석하는지, 포트가 실제로 열려 있는지 순서대로 좁혀 나가야 빠르게 찾을 수 있습니다. 이 ConceptBlock에서는 컨테이너 간 통신 문제를 5단계로 진단하는 방법을 다룹니다.

컨테이너 통신 문제 진단 흐름

단계별 진단 절차

컨테이너 간 통신 문제는 대부분 이 5단계 절차로 원인을 찾을 수 있습니다.

Step 1: 두 컨테이너가 같은 네트워크에 있는지 확인

로컬 터미널
# 네트워크 상세 정보 확인 — Containers 섹션에 두 컨테이너 모두 있어야 함
docker network inspect my_net

# 출력 예시
{
  "Name": "my_net",
  "Containers": {
    "abc123": { "Name": "web", "IPv4Address": "172.20.0.2/16" },
    "def456": { "Name": "db",  "IPv4Address": "172.20.0.3/16" }
  }
}

# 특정 컨테이너가 속한 네트워크 목록 확인
docker inspect web --format '{{json .NetworkSettings.Networks}}' | python3 -m json.tool

Step 2: 컨테이너 내부에서 DNS 해석 테스트

Docker
# nslookup으로 이름 → IP 변환 테스트
docker exec web nslookup db
# Server:    127.0.0.11    ← Docker 내장 DNS
# Address:   127.0.0.11#53
# Name:      db
# Address:   172.20.0.3    ← 정상 해석됨

# ping으로 연결 확인
docker exec web ping -c 3 db

# Alpine 기반 이미지는 nslookup 대신 nslookup 패키지 설치 필요
# docker exec -it web sh
# / # nslookup db || apk add --no-cache bind-tools

Step 3: 포트 연결 테스트

Docker
# nc(netcat)으로 TCP 포트 열림 확인
docker exec web nc -zv db 5432
# Connection to db 5432 port [tcp/postgresql] succeeded!

# wget으로 HTTP 응답 확인
docker exec web wget -qO- http://api:8080/health

# curl 사용 시
docker exec web curl -s http://api:8080/health

Step 4: 컨테이너 네트워크 인터페이스 확인

Docker
# 컨테이너 내부 네트워크 인터페이스
docker exec web ip addr show
# eth0: 172.20.0.2/16  ← 컨테이너 IP

# 라우팅 테이블 확인
docker exec web ip route
# default via 172.20.0.1 dev eth0  ← 게이트웨이

자주 발생하는 네트워크 문제

증상원인해결
ping db 실패다른 네트워크에 있음같은 network에 두 컨테이너 연결
DNS 해석 안 됨기본 bridge(docker0) 사용 중사용자 정의 network로 변경
ping 성공, curl :80 실패앱이 127.0.0.1만 리스닝앱 바인딩 주소를 0.0.0.0으로 변경
컨테이너 재시작 후 IP 변경기본 bridge 사용사용자 정의 network + 이름으로 통신
Compose 서비스 간 통신 안 됨다른 Compose 프로젝트external network 공유 설정

기본 bridge vs 사용자 정의 network 한 눈에 비교

Docker
# 기본 bridge (docker0): 이름으로 통신 불가
docker run -d --name app1 nginx
docker run -d --name app2 nginx
docker exec app1 ping app2   # ❌ 실패! IP 직접 입력해야 함

# 사용자 정의 network: 이름으로 통신 가능
docker network create mynet
docker run -d --name app1 --network mynet nginx
docker run -d --name app2 --network mynet nginx
docker exec app1 ping app2   # ✅ 성공! 이름으로 통신됨

💡개념

Docker Compose와 네트워크 — 자동 생성 네트워크 이해

docker-compose.yml에 networks를 한 번도 선언하지 않았는데 서비스 이름으로 통신이 됩니다. 왜 그럴까요? Compose는 프로젝트마다 자동으로 기본 네트워크를 만들고 모든 서비스를 거기에 붙입니다. 이 동작을 모르면 "Compose에서는 됐는데 왜 docker run으로 따로 뜬 컨테이너와는 통신이 안 되지?"라는 혼동이 생깁니다. 자동 생성 네트워크의 이름 규칙과 서비스 간 연결 방식을 이해하면, 다중 Compose 프로젝트를 연결하거나 외부 컨테이너를 편입하는 고급 패턴도 자연스럽게 납득됩니다. 이 ConceptBlock에서는 Compose가 네트워크를 자동으로 구성하는 원리를 다룹니다.

Docker Compose 자동 생성 네트워크

Compose가 네트워크를 자동으로 만드는 이유

docker-compose.yml에 networks를 명시하지 않아도 서비스 간 통신이 되는 이유가 있습니다. Compose는 프로젝트마다 프로젝트명_default 네트워크를 자동으로 만들고 모든 서비스를 여기에 연결합니다.

로컬 터미널
# myapp 디렉토리에서 docker compose up 실행
cd ~/myapp && docker compose up -d

# 자동 생성된 네트워크 확인
docker network ls
# NETWORK ID     NAME            DRIVER
# abc123def456   myapp_default   bridge   ← 자동 생성됨
#                ↑ 디렉토리명이 프로젝트명이 됨

이 네트워크 안에서 서비스 이름이 곧 DNS 이름이 됩니다.

YAML
# 네트워크를 명시하지 않아도 서비스 이름으로 통신 가능
services:
  web:
    image: myapp
    environment:
      - DB_HOST=db   # 'db' = 서비스 이름 = DNS 이름

  db:
    image: postgres:16-alpine

여러 Compose 스택 간 네트워크 공유

서로 다른 docker-compose.yml 파일로 실행한 서비스들이 통신해야 할 때, external network를 사용합니다.

로컬 터미널
# 1. 공유 네트워크 먼저 생성
docker network create shared-net
YAML
# stack-a/compose.yml
networks:
  shared-net:
    external: true   # 외부에서 이미 만든 네트워크 사용

services:
  api:
    image: my-api
    networks:
      - shared-net
      - default        # stack-a 내부 통신용
YAML
# stack-b/compose.yml
networks:
  shared-net:
    external: true

services:
  frontend:
    image: my-frontend
    networks:
      - shared-net   # api 서비스에 접근 가능
Docker
docker compose -f stack-a/compose.yml up -d
docker compose -f stack-b/compose.yml up -d

# frontend에서 api로 통신 가능
docker exec stack-b-frontend-1 curl http://api:8080/health

네트워크 격리 전략

YAML
services:
  web:
    networks:
      - frontend   # 외부와 통신
      - backend    # DB와 통신

  db:
    networks:
      - backend    # backend에만 존재 → 외부 직접 접근 불가
      # frontend 네트워크 없음 → web을 통해서만 접근 가능

  redis:
    networks:
      - backend

networks:
  frontend:    # 외부 트래픽 용
  backend:     # 내부 서비스 전용 (DB, 캐시 등)

이렇게 네트워크를 분리하면 DB와 캐시는 web 서비스를 통해서만 접근 가능하여 보안이 강화됩니다.


정리

Docker 네트워크의 핵심 원칙을 기억하세요.

상황권장 선택
단일 호스트 컨테이너 간 통신사용자 정의 브릿지 네트워크
최고 성능이 필요한 리눅스 서버Host 네트워크
네트워크 완전 격리 필요None 네트워크
이름 기반 서비스 디스커버리사용자 정의 네트워크(내장 DNS)

기본 브릿지(docker0)는 테스트 용도로만 사용하고, 실제 서비스는 항상 docker network create로 만든 사용자 정의 네트워크를 사용하세요. 이름 기반 통신이 가능해져 설정 관리가 훨씬 쉬워집니다.

자주 사용하는 네트워크 관련 명령어

로컬 터미널
# 네트워크 목록 확인
docker network ls

# 네트워크 생성
docker network create my_net

# 네트워크 상세 정보 (연결된 컨테이너, IP 대역 확인)
docker network inspect my_net

# 실행 중인 컨테이너를 네트워크에 연결
docker network connect my_net my_container

# 컨테이너를 네트워크에서 분리
docker network disconnect my_net my_container

# 사용하지 않는 네트워크 일괄 삭제

# 컨테이너가 연결된 네트워크 확인
docker inspect my_container --format '{{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}'
위험 명령어미사용 사용자 정의 네트워크가 삭제되어 컨테이너 간 연결 실습 환경이 초기화됩니다.

핵심 요약의 네트워크 일괄 정리

안전한 실행 조건: 삭제 대상이 컨테이너에 연결되지 않은 임시 네트워크뿐인 경우에만 실행합니다.

실행 전 반드시 확인

  • docker network ls와 docker network inspect로 삭제 대상을 확인했다
  • Compose 프로젝트가 사용하는 네트워크가 아님을 확인했다
  • 다시 생성할 네트워크 이름과 설정을 알고 있다
docker network prune

위 항목을 모두 확인한 후 복사할 수 있습니다

네트워크 설계 시 보안과 격리를 위해 서비스 유형(프론트엔드/백엔드/데이터)별로 별도 네트워크를 만들고, 필요한 컨테이너만 각 네트워크에 연결하는 최소 권한 원칙을 적용하세요.

다음 모듈에서는 여러 컨테이너, 네트워크, 볼륨 구성을 Docker Compose 파일 하나로 선언하고 한 번에 실행하는 방법을 다룹니다.

지식 확인

퀴즈 — 5문제

Q1

Docker의 기본 브릿지 네트워크(docker0)에서 컨테이너 이름으로 통신이 안 되는 이유는 무엇인가요?

Q2

`docker network create my_net` 명령으로 생성된 네트워크의 기본 드라이버는 무엇인가요?

Q3

Host 네트워크 모드로 컨테이너를 실행하면 어떤 특징이 있나요?

Q4

사용자 정의 네트워크에 연결된 두 컨테이너 A(이름: web)와 B(이름: db)가 있을 때, web 컨테이너에서 db에 접근하는 올바른 방법은?

Q5

None 네트워크 모드를 사용하는 적합한 시나리오는 무엇인가요?

0 / 5 답변

🧪 실습으로 확인하기

Docker Compose 멀티 서비스 구성

초급

docker-compose.yml로 nginx + 앱 컨테이너를 함께 정의하고, 서비스 간 통신과 볼륨 마운트를 구성한다.

35📋 4단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요

docker중급 · 60
[Docker] 팀 전체가 클릭 한 번으로 똑같은 환경을 구성하는 Docker 워크플로우
Docker 트랙 계속
networking입문 · 45
[Network] OSI 7계층과 TCP/IP 4계층 모델 실무적 관점 분석
Networking 트랙 시작점