본문으로 건너뛰기

컨테이너 네트워크

컨테이너 네트워크는 Docker 환경에서 컨테이너 간 통신을 제어하는 핵심 인프라입니다. 올바른 네트워크 설계는 보안(불필요한 포트 외부 노출 방지), 안정성(서비스 간 격리), 유지 보수성(DNS 기반 서비스 디스커버리)을 동시에 확보합니다. 이 챕터에서는 Docker 네트워크의 종류부터 실전 멀티 네트워크 구성, 디버깅 방법까지 체계적으로 설명합니다.


Docker 네트워크 종류​

Docker는 다양한 네트워크 드라이버를 제공하며, 용도에 따라 적절한 드라이버를 선택해야 합니다.

드라이버사용 환경설명
bridge단일 호스트 (기본)호스트에 가상 브리지(docker0) 생성, 컨테이너 간 격리 통신
host단일 호스트 (고성능)컨테이너가 호스트 네트워크를 직접 공유, 격리 없음
overlay다중 호스트 (Swarm/K8s)여러 Docker 호스트에 걸친 분산 네트워크
none완전 격리네트워크 인터페이스 없음, 외부 통신 불가
macvlan레거시 연동컨테이너에 실제 MAC 주소 할당, 물리 네트워크 직접 연결

bridge 네트워크 (기본)​

단일 Docker 호스트에서 컨테이너 간 통신에 사용하는 가장 일반적인 드라이버입니다.

호스트 OS
┌─────────────────────────────────────────┐
│ │
│ docker0 (브리지: 172.17.0.1) │
│ │ │
│ ├── nginx (172.17.0.2) │
│ ├── app (172.17.0.3) │
│ └── db (172.17.0.4) │
│ │
└─────────────────────────────────────────┘

기본 bridge 네트워크의 특성:

  • 컨테이너는 IP 주소로 서로 통신 가능
  • 기본 bridge(docker0)에서는 DNS 이름 해결 불가(IP 직접 사용해야 함)
  • 사용자 정의 bridge 네트워크에서는 컨테이너 이름 = DNS 자동 해결
# 기본 bridge 네트워크 확인
docker network ls

# 기본 bridge (docker0) 상세 정보
docker network inspect bridge

host 네트워크​

컨테이너가 호스트의 네트워크 스택을 직접 사용합니다. 포트 매핑이 필요 없으며 성능이 가장 좋지만, 격리가 없어 보안에 취약합니다.

services:
app:
image: myapp:latest
network_mode: host # 호스트 네트워크 직접 사용

none 네트워크​

네트워크 인터페이스가 완전히 제거된 상태입니다. 파일 처리, 암호화 등 외부 통신이 전혀 필요 없는 배치 작업에 활용합니다.

docker run --network none myapp:latest

사용자 정의 bridge 네트워크의 핵심 장점​

Docker Compose로 생성되는 네트워크는 모두 사용자 정의 bridge 네트워크 입니다. 기본 docker0 브리지와 달리 중요한 추가 기능을 제공합니다.

DNS 이름 자동 해결​

사용자 정의 bridge 네트워크에서는 컨테이너의 서비스 이름 이 곧 DNS 호스트명이 됩니다.

# 컨테이너 이름이 'db'이면 내부에서 'db'로 접근 가능
# Spring Boot application.properties 예시:
# spring.datasource.url=jdbc:postgresql://db:5432/myapp_db
# ↑↑
# 컨테이너 서비스 이름 = DNS

# Nginx upstream에서:
# upstream spring_app {
# server app:8080; ← 'app' 서비스가 자동으로 IP로 해석됨
# }

네트워크 별 격리​

서로 다른 네트워크에 속한 컨테이너는 통신할 수 없습니다. 하나의 컨테이너가 여러 네트워크에 동시에 참여하는 것도 가능합니다.


Docker Compose 자동 네트워크 생성​

networks 섹션을 별도로 정의하지 않으면, Docker Compose는 프로젝트 이름을 접두사로 하는 기본 네트워크를 자동 생성하고 모든 서비스를 해당 네트워크에 연결합니다.

# networks 섹션 없는 경우 → 자동으로 {project}_default 생성
services:
nginx:
image: nginx:alpine
app:
image: myapp:latest
db:
image: postgres:16-alpine
# 프로젝트 이름이 'myproject'이면:
# myproject_default 네트워크 자동 생성
# 모든 서비스가 동일 네트워크에 연결됨

docker network ls
# NETWORK ID NAME DRIVER SCOPE
# abc123 myproject_default bridge local

자동 생성 네트워크의 단점: 모든 서비스가 같은 네트워크에 있어 DB가 외부에서 접근 가능한 위험이 있습니다. 명시적 멀티 네트워크 구성을 권장합니다.


멀티 네트워크 구성: frontend / backend 분리​

실전 프로덕션 환경에서는 네트워크를 분리하여 불필요한 통신을 원천 차단합니다.

┌──────────────────────────────────────────────────────────┐
│ 호스트 OS │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ frontend network (bridge) │ │
│ │ │ │
│ │ ┌─────────────┐ ┌──────────────────────┐ │ │
│ │ │ Nginx │───────►│ Spring Boot │ │ │
│ │ │ :80, :443 │ │ :8080 │ │ │
│ │ └─────────────┘ └──────────────────────┘ │ │
│ │ ▲ │ │ │
│ └──────────┼──────────────────────────┼───────────────┘ │
│ │ 포트 노출 │ │
│ 외부 트래픽 │ │
│ ┌─────────────▼────────────────┐ │
│ │ backend network (bridge) │ │
│ │ │ │
│ │ ┌──────────────────────┐ │ │
│ │ │ Spring Boot │ │ │
│ │ │ :8080 │ │ │
│ │ └──────────┬───────────┘ │ │
│ │ │ │ │
│ │ ┌──────────▼───────────┐ │ │
│ │ │ PostgreSQL │ │ │
│ │ │ :5432 │ │ │
│ │ └──────────────────────┘ │ │
│ └──────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
version: '3.9'

services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
networks:
- frontend # frontend 네트워크만 참여

app:
build: .
expose:
- "8080"
networks:
- frontend # Nginx와 통신
- backend # DB와 통신

db:
image: postgres:16-alpine
expose:
- "5432"
networks:
- backend # backend 네트워크만 참여 (외부 접근 불가)

networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # 이 네트워크의 컨테이너는 외부 인터넷 접근 불가

핵심 격리 규칙:

  • Nginx는 DB에 직접 접근 불가 (같은 네트워크에 없음)
  • DB는 외부 인터넷 접근 불가 (internal: true)
  • 외부에서 DB 포트(5432)는 절대 접근 불가 (expose만 사용, ports 미사용)

ports vs expose 차이​

두 지시어는 모두 포트를 "열지만" 그 범위가 다릅니다.

지시어범위용도
ports호스트 ↔ 컨테이너외부에서 접근 가능, 호스트 포트 바인딩
expose컨테이너 간 (내부)컨테이너 네트워크 내부에서만 통신
# ports: 호스트의 8080 포트로 외부에서 직접 접근 가능
ports:
- "8080:8080" # 호스트:컨테이너

# expose: 동일 네트워크의 다른 컨테이너에서만 접근 가능
expose:
- "8080" # 외부에서는 접근 불가

실전 원칙:

  • Nginx: ports로 80, 443 외부 노출
  • Spring Boot, DB: expose만 사용, 외부 접근 차단

서비스 디스커버리 (DNS)​

사용자 정의 네트워크에서 Docker는 내장 DNS 서버를 통해 컨테이너 이름을 IP 주소로 자동 변환합니다.

# Spring Boot 컨테이너 내부에서 DNS 확인
docker compose exec app ping db # 'db' 서비스명이 IP로 해석됨
docker compose exec app nslookup db # DNS 조회 결과 확인

# Nginx 컨테이너에서 app 서비스 DNS 확인
docker compose exec nginx ping app
docker compose exec nginx wget -O- http://app:8080/actuator/health

DNS 해석 규칙:

  • 서비스명 → 해당 컨테이너의 내부 IP
  • 스케일 업 시 (--scale app=3) → DNS round-robin으로 자동 로드 밸런싱
  • 컨테이너 재시작으로 IP가 변경돼도 DNS가 자동 갱신됨

overlay 네트워크 (Docker Swarm)​

여러 Docker 호스트에 걸쳐 컨테이너를 배포하는 Docker Swarm 환경에서 사용합니다. 물리적으로 다른 서버의 컨테이너가 마치 같은 네트워크에 있는 것처럼 통신할 수 있습니다.

# Swarm 초기화
docker swarm init --advertise-addr 192.168.1.10

# worker 노드 참가
docker swarm join --token <token> 192.168.1.10:2377

# overlay 네트워크 생성
docker network create \
--driver overlay \
--attachable \
my-overlay-network
# docker-compose.yml (Swarm 배포용)
version: '3.9'

services:
nginx:
image: nginx:alpine
networks:
- overlay-network
deploy:
replicas: 2
placement:
constraints:
- node.role == worker

networks:
overlay-network:
driver: overlay
attachable: true

overlay 네트워크는 VXLAN 터널링을 사용하여 서로 다른 호스트의 컨테이너를 연결합니다. 단일 호스트에서는 bridge 네트워크를 사용하는 것이 더 간단하고 성능도 좋습니다.


네트워크 디버깅​

docker network inspect​

네트워크의 상세 정보(연결된 컨테이너, IP 대역, 드라이버 설정 등)를 확인합니다.

# 모든 네트워크 목록
docker network ls

# 특정 네트워크 상세 정보
docker network inspect myproject_frontend

# 연결된 컨테이너와 IP 확인
docker network inspect myproject_backend --format \
'{{range .Containers}}{{.Name}}: {{.IPv4Address}}{{println}}{{end}}'

docker exec를 통한 연결 테스트​

# 컨테이너 내부에서 다른 컨테이너로 ping
docker compose exec nginx ping app
docker compose exec nginx ping db # frontend만 속한 nginx는 db 접근 불가

# curl로 HTTP 연결 테스트
docker compose exec nginx curl -v http://app:8080/actuator/health

# netstat으로 열린 포트 확인
docker compose exec app netstat -tlnp

# traceroute로 경로 추적
docker compose exec app traceroute db

네트워크 연결 문제 체계적 진단​

# 1. 컨테이너가 실행 중인지 확인
docker compose ps

# 2. 같은 네트워크에 속해 있는지 확인
docker network inspect myproject_backend

# 3. 방화벽 규칙 확인 (호스트)
sudo iptables -L -n | grep DOCKER

# 4. 컨테이너 로그에서 연결 오류 확인
docker compose logs app | grep -i "connection refused\|timeout\|refused"

# 5. DNS 해석 확인
docker compose exec app nslookup db
docker compose exec app cat /etc/resolv.conf

보안 관점: 불필요한 포트 외부 노출 방지​

체크리스트​

# 현재 호스트에서 열린 포트 확인 (외부 노출 현황)
netstat -tlnp | grep docker
# 또는
ss -tlnp | grep docker

잘못된 설정 (보안 위험):

# ❌ DB 포트를 호스트에 직접 노출 - 절대 금지
db:
image: postgres:16-alpine
ports:
- "5432:5432" # 외부에서 DB 직접 접근 가능!

올바른 설정:

# ✅ expose만 사용하여 컨테이너 내부 통신만 허용
db:
image: postgres:16-alpine
expose:
- "5432" # 컨테이너 네트워크 내부에서만 접근 가능
networks:
- backend # backend 네트워크만 참여

추가 보안 강화 방법:

# 개발 환경에서 잠깐 DB에 직접 접속해야 할 때:
# docker-compose.override.yml (개발 전용)
services:
db:
ports:
- "127.0.0.1:5432:5432" # 로컬호스트에서만 접근 (0.0.0.0 사용 금지)

0.0.0.0:5432:5432는 모든 인터페이스에서 접근 가능하므로 반드시 127.0.0.1:5432:5432처럼 로컬호스트로 제한해야 합니다.


고수 팁​

1. 네트워크 이름 명시적 지정

networks:
frontend:
name: myapp-frontend # Docker 네트워크 실제 이름 지정 (프로젝트명 접두사 없음)
backend:
name: myapp-backend
internal: true

2. 기존 외부 네트워크 재사용

여러 Docker Compose 프로젝트가 같은 네트워크를 공유해야 할 때:

networks:
shared-network:
external: true # 이미 존재하는 네트워크 사용
name: myapp-frontend

3. IPv6 지원

networks:
frontend:
driver: bridge
enable_ipv6: true
ipam:
config:
- subnet: "2001:db8::/64"

4. 네트워크 대역 충돌 해결

회사 내부 네트워크와 Docker 대역이 충돌할 때:

networks:
frontend:
driver: bridge
ipam:
config:
- subnet: 172.30.0.0/24 # 커스텀 서브넷 지정
gateway: 172.30.0.1