- bridge — 기본. 컨테이너마다 가상 IP. -p 로 호스트 포트에 연결
- host — 호스트 네트워크를 그대로 쓴다. 포트 매핑이 없고 격리도 없다
- none — 네트워크 없음
- 사용자 정의 — 컨테이너 이름으로 서로를 찾을 수 있다 (내장 DNS)
docker network create appnet
docker run -d --name db --network appnet postgres
docker run -d --name api --network appnet myapi # DB 주소는 'db:5432'
기본 bridge 에서는 컨테이너 이름으로 서로를 못 찾는다 사용자 정의 네트워크를 만들면 내장 DNS 가 이름을 풀어 준다
- compose 가 자동으로 하는 일이 이것이다
localhost 오해
컨테이너 안의 localhost 는 '그 컨테이너 자신' 이다 호스트도, 다른 컨테이너도 아니다
- 다른 컨테이너 → 컨테이너 이름 (같은 사용자 정의 네트워크)
- 호스트 → host.docker.internal (Docker Desktop)
- 리눅스는 --add-host=host.docker.internal:host-gateway
"DB 에 연결이 안 된다" 의 절반이 이 오해다
포트 매핑
docker run -p 8080:80 nginx # 모든 인터페이스(0.0.0.0)에 연다
docker run -p 127.0.0.1:8080:80 nginx # 로컬만
-p 8080:80 은 0.0.0.0 에 연다 개발 머신에서 무심코 열면 같은 네트워크의 누구나 접근할 수 있다
- 로컬 전용이면 IP 를 명시한다
방화벽보다 앞선다
-
도커의 포트 게시는 iptables 를 직접 만진다
-
호스트 방화벽(ufw) 규칙보다 앞서 적용된다
-
"막았는데 열려 있는" 일이 생긴다
-
대응
- 바인딩 주소를 제한한다 (-p 127.0.0.1:...)
- 도커의 iptables 관리를 끄고 직접 규칙을 쓴다 (운영 부담이 크다)
- 클라우드라면 보안그룹에서 막는다 (호스트 밖 계층)
host 네트워크의 트레이드오프
--network=host
얻는 것 NAT 를 거치지 않아 지연·처리량이 좋다
포트 매핑이 필요 없다
잃는 것 네트워크 격리가 사라진다
포트가 호스트와 충돌한다 (같은 포트를 두 개 못 띄운다)
고성능 네트워크 워크로드에서만 고려한다
진단
docker port web
docker network inspect appnet
docker exec -it web sh -c 'cat /etc/resolv.conf; nslookup db'
ss -lnt | grep 8080 # 호스트에서 실제로 열렸는지
compose 가 자동으로 해 주는 것
services:
api:
image: myapi
depends_on: [db]
db:
image: postgres
- compose 는 프로젝트마다 사용자 정의 네트워크를 만든다
- 서비스 이름으로 서로를 찾을 수 있다 (api 에서 'db:5432')
depends_on 은 '기동 순서' 만 정한다 — '준비됨' 을 기다리지 않는다
- DB 가 아직 접속을 못 받는 상태에서 api 가 뜬다
- 애플리케이션에 재시도를 넣거나 healthcheck 조건을 건다
depends_on:
db:
condition: service_healthy
네트워크 진단 순서
-
① 같은 네트워크에 있는가 — docker network inspect
-
② 이름이 풀리는가 — nslookup db
-
③ 포트가 열려 있는가 — nc -zv db 5432
-
④ 호스트에서 보이는가 — ss -lnt | grep 5432
-
⑤ 방화벽·보안그룹
-
위에서부터 하나씩 좁힌다. 대개 ①이나 ②에서 끝난다
면접 함정
- ❌ "컨테이너끼리는 이름으로 통신된다" → 기본 bridge에서는 안 된다.
- ❌ "방화벽으로 막으면 안전하다" → 도커가 iptables를 앞서 만진다.