도커 네트워크(Docker Network)는 호스트 OS 위에서 실행되는 컨테이너들이 서로 통신하거나, 외부 네트워크와 통신할 수 있도록 제공되는 소프트웨어 기반의 가상 네트워크 환경이다.
컨테이너들은 물리 서버가 아니기 때문에 물리 네트워크 카드, 스위치를 가질 수 없다.
도커는 이를 해결하기 위해 리눅스 커널의 네트워크 기능을 조합하여 가상 네트워크를 구성한다.
도커 네트워크의 주요 구성 요소는 다음과 같다.
- 브릿지(bridge): 가상 스위치
- 브릿지 네트워크(bridge network) 브릿지 드라이버로 만든 가상 네트워크 단위
- 가상 인터페이스(veth): 컨테이너를 브릿지에 연결하는 가상 랜 케이블
- NAT / 포트 포워딩: 도커 네트워크(컨테이너)와 외부 네트워크가 통신하기 위한 변환 메커니즘
브릿지(Bridge)와 브릿지 네트워크(Bridge Network)
브릿지는 리눅스 커널이 제공하는 가상 스위치(virtual switch) 기능이다.
도커는 이 브릿지를 이용해 여러 개의 컨테이너를 하나의 가상 네트워크에 연결한다.
브릿지 네트워크는 도커의 기본 네트워크 드라이버인 브릿지 드라이버를 사용하여 만든 하나의 가상 네트워크 단위이다.
위에서 말한 브릿지를 이용해 여러 컨테이너들이 연결되는 가상 네트워크 자체를 의미하기도 한다.
도커를 설치하면 기본적으로 아래의 요소들이 생성된다.
- docker0: 기본 브릿지
- bridge: docker0를 사용하는 기본 브릿지 네트워크
컨테이너들은 기본적으로 기본 브릿지 네트워크(bridge)와 기본 브릿지(docker0)를 사용한다.
만약 사용자가 새로운 브릿지 네트워크를 만들게 되면, br-<randomID>형태의 새로운 브릿지가 생성된다.
즉 하나의 브릿지는 하나의 브릿지 네트워크만 관리한다.
# 사용자 정의 브릿지 네트워크 생성
docker network create --driver bridge my_bridge
# 호스트에서 브릿지 장치 확인
ip link show # br-xxxxxxxxxx 형태로 표시됨
가상 인터페이스(veth)
도커 내부에서만의 관점에서 보자면 브릿지 네트워크(가상 네트워크)는 물리적인 네트워크, 컨테이너는 물리적인 호스트와 동일한 의미를 가진다.
물리적인 호스트는 네트워크에 연결되기 위해 물리 NIC(Network Interface Card)를 갖고 있다.
하지만 컨테이너는 물리적인 장치를 가질 수 없기 때문에, 물리 NIC도 갖고 있지 않다.
대신에 도커는 veth(virtual ethernet)를 통해 컨테이너와 브릿지 네트워크를 연결한다.
veth의 주요 개념은 다음과 같다.
- veth는 항상 쌍(pair)으로 생성된다.
- veth의 한쪽은 컨테이너 내부와 연결된다.
- veth의 나머지 한쪽은 브릿지와 연결된다.
- 즉 각 컨테이너는 자기만의 veth 쌍을 갖고, veth를 통해 브릿지 네트워크에 연결된다.
컨테이너의 실제 네트워크 통신
동일한 브릿지 네트워크에 속한 컨테이너 간의 네트워크 통신은 다음과 같다.
# 브릿지가 스위치 역할을 수행한다.
# IP로 통신이 가능하며, 내부 DNS를 제공하기 때문에 컨테이너 이름으로로 통신 가능하다.
# NAT/포트 포워딩이 불필요하다.
컨테이너 A eth0
↓
vethA
↓
브릿지
↓
vethB
↓
컨테이너 B eth0
서로 다른 브릿지 네트워크에 속한 컨테이너 간의 네트워크 통신은 다음과 같다.
# 기본적으로 서로 다른 브릿지 네트워크에 속한 컨테이너는 완전히 격리되어있다.
# 통신하려면 명시적인 설정이 필요하다.
## 한 컨테이너를 다른 네트워크에도 연결
docker network connect my_bridge2 app1
컨테이너와 외부 네트워크는 다음과 같이 통신한다.
# 외부 네트워크 -> 컨테이너
## 외부에서는 컨테이너 IP를 직접 알 수도, 접근할 수도 없음
## 외부 요청은 항상 '호스트 IP + 포트'로만 진입
## 포트 포워딩이 이 요청을 어느 컨테이너로 보낼지 결정
## 포트 포워딩은 호스트로 들어온 특정 포트의 트래픽을, 특정 컨테이너의 특정 포트로 전달하는 규칙
## docker run -p 8080:80 nginx는 호스트 IP:8080 → 컨테이너 IP:80의 의미를 가진다.
[외부 네트워크]
│
│ (호스트 IP:8080)
▼
[호스트 네트워크 인터페이스]
│
│ (포트 포워딩 규칙: 8080 → 컨테이너:80)
▼
[호스트 네트워크 스택]
│
▼
[브릿지 (docker0, br-xxxx, ...)]
│
▼
[veth]
│
▼
[컨테이너 eth0 : 80]
# 컨테이너 -> 외부 네트워크
## 컨테이너 IP는 보통 172.x.x.x, 10.x.x.x 같은 사설 IP
## 사설 IP는 인터넷에서 라우팅 불가능하다. 그래서 외부로 나갈 때는 반드시 호스트의 공인/로컬 IP로 변환해야 한다.
## NAT는 IP 주소를 변환하는 기술이며, 컨테이너가 외부로 나갈 때 NAT가 자동으로 적용된다.
[컨테이너 IP: 172.18.0.2]
│
│ (출발 패킷)
▼
[호스트 네트워크 스택]
│
│ (SNAT: 출발지 IP 변경)
▼
[호스트 IP: 192.168.0.10]
│
▼
[외부 네트워크]