Docker 시리즈 ④ Network — 컨테이너는 어떻게 통신하는가
Docker의 네트워크 모드(bridge, host, none), 컨테이너 간 통신, 포트 매핑 동작 원리를 초보자 눈높이로 정리했다.
Docker 시리즈 ④ Network — 컨테이너는 어떻게 통신하는가
시리즈 순서: Host → Image → Container → Network → Volume → Dockerfile → Compose → Security → Swarm
들어가며
docker run -p 3000:3000 my-app을 입력하면 브라우저에서 localhost:3000으로 접속이 된다. 그런데 컨테이너는 격리되어 있다고 했는데, 어떻게 외부에서 접속이 가능한 걸까?
또 컨테이너끼리 서로 부르려면 IP를 알아야 할까? 아니면 이름으로 부를 수 있을까?
이 글에서 그 마법의 정체를 풀어본다.
Docker가 자동으로 만드는 가상 네트워크
Docker를 설치하면 호스트에 `docker0`라는 가상 네트워크 브리지가 자동으로 생긴다.
[호스트 머신]
│
├─ eth0 (192.168.0.10) ← 실제 네트워크 카드
│
└─ docker0 (172.17.0.1) ← 가상 브리지
│
├─ container A (172.17.0.2)
├─ container B (172.17.0.3)
└─ container C (172.17.0.4)- 컨테이너는 모두
172.17.x.x같은 가상 IP를 받는다 docker0가 가상 라우터/스위치 역할을 한다- 컨테이너 → 외부 인터넷은 NAT(주소 변환)로 통신
이 구조 덕에 컨테이너끼리는 같은 네트워크처럼 통신하고, 외부에서 컨테이너에 직접 접근은 안 된다.
네트워크 모드 3가지
docker run --network <모드>로 설정한다.
1. bridge (기본값) — 가장 많이 씀
위에서 설명한 그대로다. 컨테이너가 가상 네트워크 안에 격리되어 있다.
docker run -d --name web nginx # bridge 모드 (기본)2. host — 호스트와 네트워크 공유
컨테이너가 호스트 머신의 네트워크를 그대로 사용한다. 격리 없음.
docker run -d --network host nginx장점:
- 빠름 (NAT 거치지 않음)
- 포트 매핑(
-p) 불필요
단점:
- 포트 충돌 위험 (호스트의 80번 포트를 컨테이너가 쓰면 호스트는 못 씀)
- macOS/Windows에선 사실상 동작 안 함 (VM 위에서 도니까)
3. none — 네트워크 없음
완전히 격리. 외부와 단절된 컨테이너가 필요할 때.
docker run --network none my-task배치 작업, 보안 민감한 작업에 가끔 쓴다.
포트 매핑(-p)의 진짜 동작
docker run -d -p 8080:80 nginx이 명령이 의미하는 것:
[외부 사용자]
│ localhost:8080 접속
▼
[호스트 머신: 8080 포트]
│ Docker가 트래픽을 받아서 NAT
▼
[nginx 컨테이너: 80 포트]호스트의 8080 포트로 들어온 모든 트래픽이 컨테이너의 80번 포트로 전달된다. 이걸 포트 포워딩 또는 포트 퍼블리싱이라고 한다.
-p 옵션 형식
-p 8080:80 # 호스트 8080 → 컨테이너 80 (모든 인터페이스)
-p 127.0.0.1:8080:80 # 로컬에서만 접근 가능
-p 8080:80/udp # UDP 프로토콜
-p 8080:80 -p 8443:443 # 여러 포트 동시 매핑
-P # 컨테이너의 EXPOSE된 포트 전부 자동 매핑흔한 실수
# ❌ 순서 헷갈림
docker run -p 80:8080 my-app # 호스트 80 → 컨테이너 8080
# 컨테이너가 8080에서 듣고 있어야 함
# 순서: 호스트:컨테이너외우는 법: "바깥(호스트):안(컨테이너)" — 외부에서 들어오는 입구가 먼저.
컨테이너끼리 통신하기
방법 1: IP로 통신 (비추천)
$ docker inspect web | grep IPAddress
"IPAddress": "172.17.0.2"
# 다른 컨테이너에서
$ curl 172.17.0.2문제: 컨테이너를 재시작하면 IP가 바뀐다. 운영에선 못 쓴다.
방법 2: 사용자 정의 네트워크 + 컨테이너 이름 (권장)
기본 bridge 네트워크는 컨테이너 이름으로 못 부른다. 사용자 정의 네트워크를 만들면 가능하다.
# 1. 네트워크 생성
docker network create my-net
# 2. 같은 네트워크에 컨테이너 띄우기
docker run -d --name db --network my-net postgres
docker run -d --name app --network my-net my-app
# 3. app 컨테이너에서 db 컨테이너로 이름으로 통신 가능
# app 안에서: postgresql://db:5432 ← "db"가 호스트명이게 가능한 이유는 Docker가 사용자 정의 네트워크에 자동으로 DNS 서버를 붙여주기 때문이다. 컨테이너 이름이 곧 호스트명이 된다.
실무 팁: docker-compose를 쓰면 자동으로 사용자 정의 네트워크가 만들어지고, 서비스 이름으로 통신 가능. 별도 설정 거의 필요 없음 (Compose 글에서 자세히)자주 쓰는 네트워크 명령어
# 네트워크 목록
docker network ls
# 상세 정보
docker network inspect bridge
docker network inspect my-net
# 생성
docker network create my-net
docker network create --driver bridge --subnet 10.0.0.0/24 my-net
# 컨테이너를 네트워크에 추가/제거
docker network connect my-net my-container
docker network disconnect my-net my-container
# 사용하지 않는 네트워크 정리
docker network prune외부 → 컨테이너 vs 컨테이너 → 외부
외부 → 컨테이너 (Inbound)
기본은 막혀 있다. -p 옵션으로 명시적으로 열어줘야 한다.
docker run -d nginx # 외부에서 접근 불가
docker run -d -p 80:80 nginx # 외부에서 :80 접근 가능컨테이너 → 외부 (Outbound)
기본은 열려 있다. 컨테이너 안에서 인터넷에 자유롭게 접근 가능하다.
$ docker exec my-app curl https://google.com # 정상 동작이게 가능한 이유는 Docker가 자동으로 NAT(iptables 규칙)를 설정해주기 때문이다.
헷갈리는 포인트 정리
❓ "localhost로 컨테이너끼리 통신이 안 돼요"
컨테이너 안의 `localhost`는 그 컨테이너 자기 자신이다. 다른 컨테이너에 접근하려면 컨테이너 이름이나 IP를 써야 한다.
// ❌ 컨테이너 A 안에서
fetch('http://localhost:5432') // A 자기 자신을 부름
// ✅ 컨테이너 이름 사용 (사용자 정의 네트워크에서)
fetch('http://db:5432')❓ "macOS에서 host.docker.internal이 뭐예요?"
컨테이너 안에서 호스트 머신(macOS 자체)에 접근할 때 쓰는 특수 호스트명이다.
# 컨테이너 안에서 호스트의 3000번 포트 접근
curl http://host.docker.internal:3000호스트에서 돌고 있는 DB나 서비스에 컨테이너에서 접근할 때 유용하다. (Linux에선 기본 지원 안 함, 별도 설정 필요)
❓ "포트 매핑했는데 접속이 안 돼요"
체크 순서:
- 컨테이너 안의 앱이
0.0.0.0이 아니라127.0.0.1로 바인딩하면 외부에서 접속 불가 docker ps에서 PORTS 컬럼에 매핑이 표시되는지 확인- 호스트의 방화벽이 막고 있는지 확인
// ❌ Express 예시
app.listen(3000, '127.0.0.1') // 컨테이너 안에서만 접근 가능
// ✅
app.listen(3000, '0.0.0.0') // 모든 인터페이스에서 접근 가능❓ "기본 bridge랑 사용자 정의 bridge 차이가 뭐예요?"
기본 bridge (docker0) | 사용자 정의 bridge | |
|---|---|---|
| DNS (이름으로 통신) | ❌ 불가 | ✅ 가능 |
| 격리성 | 모든 컨테이너 공유 | 네트워크별로 격리 |
| 권장 여부 | 비추천 | 권장 |
운영 환경에선 항상 사용자 정의 네트워크를 만들어 쓰는 게 좋다.
정리
- Docker는 가상 네트워크 브리지(
docker0)로 컨테이너를 연결 - 모드: bridge(기본, 격리), host(공유), none(없음)
- 포트 매핑은
-p 호스트:컨테이너순서 - 컨테이너끼리 이름으로 부르려면 사용자 정의 네트워크 필수
- 컨테이너 안의
localhost는 그 컨테이너 자신이다 (헷갈리지 말 것)
다음 글에선 컨테이너가 죽어도 데이터를 남기는 방법 — 볼륨(Volume)을 다룬다.
이전 글: Docker 시리즈 ③ Container 다음 글: Docker 시리즈 ⑤ Volume — 데이터를 어떻게 보존하는가
Related Posts
같이 읽으면 좋은 글
Docker 시리즈 ⑨ Swarm — 여러 호스트로 확장하기, 그리고 Kubernetes로
Docker Swarm의 핵심 개념(Service, Task, Stack)을 정리하고, 왜 결국 Kubernetes로 넘어가게 되는지 실제 프로젝트 경험을 바탕으로 정리했다.
Docker 시리즈 ⑧ Security — 컨테이너 보안의 기본
컨테이너 보안의 기초 — non-root 실행, 시크릿 관리, .dockerignore, 이미지 스캔, 리소스 제한까지 초보자도 챙겨야 할 핵심을 정리했다.
Docker 시리즈 ⑦ Compose — 여러 컨테이너 한꺼번에 다루기
여러 컨테이너를 단일 명령으로 관리하는 docker compose의 구조, 핵심 옵션, 그리고 실무 활용 패턴을 초보자 눈높이로 정리했다.