컨테이너 네트워크
컨테이너 네트워크는 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