Docker 네트워크 개요
Docker 컨테이너는 기본적으로 격리된 네트워크 환경에서 실행됩니다. 컨테이너 간 통신, 외부 네트워크 접근, 서비스 디스커버리를 위해 Docker는 다양한 네트워크 드라이버를 제공합니다.
비유하면, 각 컨테이너는 독립된 아파트 호실이고, Docker 네트워크는 아파트 단지 내 통신 시스템입니다. 같은 네트워크(단지)에 속한 컨테이너(호실)끼리만 직접 통신할 수 있습니다.
| 드라이버 | 설명 | 주요 용도 |
|---|---|---|
| bridge | 호스트 내부 가상 브리지 (기본값) | 단일 호스트에서 컨테이너 간 통신 |
| host | 호스트 네트워크 직접 사용 | 네트워크 성능이 중요한 경우 |
| none | 네트워크 비활성화 | 완전 격리가 필요한 경우 |
| overlay | 여러 호스트를 연결하는 가상 네트워크 | Docker Swarm, 멀티 호스트 |
| macvlan | 컨테이너에 물리 MAC 주소 할당 | 물리 네트워크에 직접 연결 |
bridge 네트워크
Docker 설치 시 docker0라는 기본 브리지 네트워크가 생성됩니다. 그러나 기본 브리지는 DNS 기반 서비스 디스커버리를 지원하지 않으므로, 사용자 정의 브리지 네트워크를 만들어 사용하는 것이 권장됩니다.
# 기본 네트워크 목록 확인
docker network ls
# NETWORK ID NAME DRIVER SCOPE
# a1b2c3d4e5f6 bridge bridge local
# f6e5d4c3b2a1 host host local
# 1234567890ab none null local
# 사용자 정의 브리지 네트워크 생성
docker network create \
--driver bridge \
--subnet 172.20.0.0/16 \
--gateway 172.20.0.1 \
my-app-network
# 네트워크 상세 정보 확인
docker network inspect my-app-network
# [{ "Name": "my-app-network",
# "Driver": "bridge",
# "IPAM": { "Config": [{ "Subnet": "172.20.0.0/16", "Gateway": "172.20.0.1" }] }
# }]
사용자 정의 브리지에서는 컨테이너 이름으로 DNS 조회가 가능합니다.
# 같은 네트워크에 두 컨테이너 실행
docker run -d --name web --network my-app-network nginx:alpine
docker run -d --name api --network my-app-network node:22-alpine sleep 3600
# api 컨테이너에서 web 컨테이너로 이름으로 접근
docker exec api 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.089 ms
# DNS 조회 확인
docker exec api nslookup web
# Name: web
# Address 1: 172.20.0.2 web.my-app-network
# 기본 bridge에서는 컨테이너 이름 DNS가 작동하지 않음
docker run -d --name test1 nginx:alpine
docker run -it --rm alpine ping -c 1 test1
# ping: bad address 'test1' ← 실패
기본 bridge와 사용자 정의 bridge의 차이점입니다.
| 기능 | 기본 bridge | 사용자 정의 bridge |
|---|---|---|
| DNS 서비스 디스커버리 | 지원 안 함 | 컨테이너 이름으로 통신 |
| 자동 연결 | 지정 안 하면 자동 연결 | 명시적 연결 필요 |
| 격리 수준 | 모든 컨테이너가 같은 네트워크 | 네트워크 단위 격리 |
| 실행 중 네트워크 변경 | 불가 | docker network connect/disconnect |
host 네트워크
host 네트워크는 컨테이너가 호스트의 네트워크 스택을 직접 사용합니다. 포트 매핑 없이 호스트의 포트를 그대로 사용하므로 네트워크 오버헤드가 없습니다.
# host 네트워크로 실행 (포트 매핑 불필요)
docker run -d --name web --network host nginx:alpine
# 호스트의 80 포트로 직접 접근
curl http://localhost:80
# <!DOCTYPE html>
# <html>
# <head><title>Welcome to nginx!</title></head>
# 컨테이너의 네트워크 인터페이스 확인 (호스트와 동일)
docker exec web ip addr
# 호스트의 eth0, lo 등이 그대로 보임
host 네트워크는 다음 경우에 사용합니다.
- 네트워크 성능이 중요한 경우 (NAT 오버헤드 제거)
- 컨테이너가 많은 포트를 사용하는 경우
- 호스트의 네트워크 설정에 직접 접근해야 하는 경우
단, 호스트의 포트를 직접 점유하므로 포트 충돌에 주의해야 하며, 컨테이너 간 네트워크 격리가 불가능합니다.
Docker Compose 네트워킹
Docker Compose는 프로젝트별로 자동으로 네트워크를 생성합니다. {프로젝트명}_default라는 이름의 브리지 네트워크가 만들어지고, 서비스 이름으로 DNS 통신이 가능합니다.
# docker-compose.yml
# 프론트엔드, 백엔드, DB를 네트워크로 분리
services:
# 프론트엔드: 외부 네트워크 + 백엔드 네트워크
nginx:
image: nginx:alpine
ports:
- "80:80"
networks:
- frontend
- backend
depends_on:
- api
# 백엔드 API: 백엔드 네트워크 + DB 네트워크
api:
build: ./api
environment:
# 서비스 이름 'db'로 접근 (DNS 자동 해석)
DATABASE_URL: "postgres://user:pass@db:5432/myapp"
REDIS_URL: "redis://cache:6379"
networks:
- backend
- database
# 데이터베이스: DB 네트워크만 (외부 접근 차단)
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- database
# Redis 캐시: 백엔드 네트워크만
cache:
image: redis:7-alpine
networks:
- backend
# 네트워크 정의
networks:
frontend:
driver: bridge
backend:
driver: bridge
database:
driver: bridge
# 내부 전용 네트워크 (외부 접근 불가)
internal: true
volumes:
pgdata:
# Compose 실행
docker compose up -d
# 생성된 네트워크 확인
docker network ls
# NETWORK ID NAME DRIVER
# ... myproject_frontend bridge
# ... myproject_backend bridge
# ... myproject_database bridge
# api 컨테이너에서 db 접근 확인
docker compose exec api ping -c 1 db
# PING db (172.22.0.3): 56 data bytes
# 64 bytes from 172.22.0.3: time=0.056 ms
# nginx에서 db 접근 시도 (네트워크가 다르므로 실패)
docker compose exec nginx ping -c 1 db
# ping: bad address 'db' ← 격리 작동
이 구성에서 database 네트워크에 internal: true를 설정하면 외부 인터넷 접근이 차단되어 데이터베이스 보안이 강화됩니다.
overlay 네트워크
overlay 네트워크는 여러 Docker 호스트에 걸쳐 컨테이너를 연결합니다. Docker Swarm 모드에서 주로 사용합니다.
# Swarm 초기화 (매니저 노드)
docker swarm init
# overlay 네트워크 생성
docker network create \
--driver overlay \
--attachable \
--subnet 10.0.10.0/24 \
my-overlay-network
# 서비스 배포 (overlay 네트워크 사용)
docker service create \
--name web \
--network my-overlay-network \
--replicas 3 \
nginx:alpine
# 다른 호스트의 컨테이너와도 이름으로 통신 가능
# 10.0.10.2 (호스트 A의 web.1)
# 10.0.10.3 (호스트 B의 web.2)
# 10.0.10.4 (호스트 C의 web.3)
--attachable 옵션을 추가하면 Swarm 서비스뿐만 아니라 일반 컨테이너(docker run)도 overlay 네트워크에 접속할 수 있습니다.
네트워크 디버깅
네트워크 문제를 진단하는 명령어입니다.
# 컨테이너의 IP 주소 확인
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' web
# 172.20.0.2
# 컨테이너 간 연결 테스트
docker exec api curl -s http://web:80
# <!DOCTYPE html>...
# 컨테이너의 네트워크 인터페이스 확인
docker exec web ip addr show eth0
# inet 172.20.0.2/16 scope global eth0
# 네트워크에 연결된 컨테이너 목록
docker network inspect my-app-network --format '{{range .Containers}}{{.Name}} {{.IPv4Address}}{{"\n"}}{{end}}'
# web 172.20.0.2/16
# api 172.20.0.3/16
# 포트 매핑 확인
docker port web
# 80/tcp -> 0.0.0.0:8080
# DNS 해석 테스트
docker exec api getent hosts web
# 172.20.0.2 web
실전 팁
- 사용자 정의 네트워크 사용: 기본 bridge 네트워크 대신 항상 사용자 정의 네트워크를 생성하세요. DNS 서비스 디스커버리, 더 나은 격리, 실행 중 네트워크 변경이 가능합니다.
- 네트워크 분리로 보안 강화: 위의 Compose 예제처럼 프론트엔드, 백엔드, 데이터베이스 네트워크를 분리하면 DB가 외부에 직접 노출되지 않습니다.
internal: true로 외부 인터넷 접근도 차단하세요. - 포트 바인딩 주의:
-p 3000:3000은 모든 인터페이스(0.0.0.0)에 바인딩합니다. 로컬에서만 접근해야 하면-p 127.0.0.1:3000:3000으로 제한하세요. - 네트워크 정리: 사용하지 않는 네트워크는
docker network prune으로 정리합니다. 특히 테스트 후 남은 네트워크가 쌓이면 IP 대역 충돌이 발생할 수 있습니다. - DNS 캐시: Docker의 내장 DNS 서버(127.0.0.11)는 컨테이너 이름 해석을 담당합니다. 컨테이너가 재시작되어 IP가 바뀌어도 이름으로 접근하면 자동으로 새 IP를 해석합니다. IP 하드코딩을 피하고 항상 서비스 이름을 사용하세요.