Chapter 4. Docker Network

안녕하세요, 주말에 인사 드립니다 :)
쉬면서도 할껀 해야죠!
오늘은 도커 컨테이너의 기본적인 네트워크 구성 방식에 대해서 알아보겠습니다.
네트워크 엔지니어 였던 만큼, 개인적으로 인프라에선 네트워크가 가장 중요하다고 생각합니다.
네트워크 구성이 되지 않는다면 최종적인 서비스 구현 자체가 안되기 때문이죠~
01. 기본 네트워크 구성
도커 컨테이너도 마찬가지로, 리눅스 기반 네트워킹을 그대로 가져와서 사용하고 있습니다.
📌 리눅스 브리지 : 하드웨어 네트워크 스위치 처럼 작동하는 커널 모듈.
- 도커도 docker0 라고 하는 브릿지 네트워크를 사용하고 있다.
- OSI 2layer에서 사용하는 MAC Address를 이용한 Bridge Service.
- veth pair라고 하며 가상 인터페이스의 쌍이라고 불린다. (HostOS와 컨테이너간 연결 보장)
- 즉, 컨테이너의 eth0은 호스트의 Network namespace나 docker0과 연결된다.

📌 Network Namespace
- 커널에 격리된 네트워크 스택으로 자체 인터페이스, 라우팅 및 방화벽 규칙을 사용한다.
- 기본적으로 리눅스와 컨테이너를 격리시킨다 (보안적 측면)
- docker bridge를 통해서 격리된 네트워크 스택끼리 통신이 가능하도록 구현됨.
- 일반적으로 CNM(Container Network Model) 드라이버는 각 컨테이너에 대해 별도의 네임스페이스를 구현함.

즉, 요약하자면!!
1. 컨테이너는 호스트와 격리된 자체 네트워크 스택을 사용중인데, docker bridge를 통해 호스트와 통신이 가능함.
2. docker bridge는 리눅스 브리지 기반이며, 'docker0' 라는 인터페이스 라고도 불린다.
3. veth pair라는 기술로 컨테이너 내부의 eth 인터페이스와 docker0가 연결되며 한쌍을 이룬다.
4. 컨테이너 네트워크 모델의 명칭을 CNM, 그 구현체를 Libnetwork 라고 부른다.
✅ docker0와 연결된 veth 확인
- 아래 명령어로 확인이 가능하며, 사전에 'bridge-utils'를 설치해야한다.
# apt install -y bridge-utils
brctl show
좀더 자세하게 bridge와 연결된 네트워크 구성을 확인하고 싶다면 아래와 같이 입력해보자.
docker network inpsect bridge

이런식으로 연결된 컨테이너에 대한 IPAM(IP Address Managment)정보를 알 수 있다.
02. NAT Service
📌 Port Forwarding
docker container를 생성할때 '-p' 옵션을 통해서 포트포워딩을 구현할 수 있다.
예를들어, nginx 서비스를 제공하는 컨테이너를 만들때 아래와 같이 생성했다고 가정하자.
docker run -d --name ssunghwan -p 8081:80 nginx:latest
이렇게 컨테이너를 생성하게 된다면 외부의 8081 요청이 docker NAT를 통해 Container의 80포트와 통신되며
외부에서 nginx 이미지를 띄울 수 있게 된다.
이 내용을 네트워크 현업에서는 포트포워딩 이라고 부르며, 'docker ps -a' 명령을 통해 확인이 가능하다.
📌 ip tables 확인
위에서 생성된 포트포워딩 규칙이, host의 iptables NAT 테이블 규칙으로 구현됩니다.
Host는 외부에서 들어오는 요청을 컨테이너에서 정의된 포트포워딩 규칙 대로 컨테이너에 전달을 해야 하기 때문에
DNAT (Destination NAT) 규칙을 사용하여 패킷의 목적지 IP/Port를 컨테이너의 IP/Port로 변경합니다.
sudo iptables -t nat -L -n

P.S 중간에서 docker-proxy라는 친구가 DNAT 규칙을 보고 컨테이너로 연결을 해주는거에요~!
03. 사용자 정의 네트워크 구성
기본으로 제공되는 docker0 같은 default bridge를 사용해도 되지만,
관리와 유지보수가 용의하게 ip 및 subnet을 입맛에 맞게 커스터마이징 해서 사용할 수도 있다.
✅ 네트워크 생성
docker network create \
--driver bridge \
--subnet 172.30.254.0/24 \
--ip-range 172.30.254.0/24 \
--gateway 172.30.254.1 \
vswitch-net

이렇게 사용자 정의 네트워크를 생성하고, vswitch-net 이라는 bridge를 생성한 것까지 확인해보았다.
✅ 사용자 네트워크로 컨테이너 생성
docker run --net=vswitch-net -itd --name=ssunghwan ubuntu:14.04

이렇게 'vswitch-net' 네트워크 대역의 컨테이너를 생성해보면 설정한 CIDR에서 순차적으로 ip 할당받는 것 확인.
물론, 컨테이너 생성 시에 '--ip' 옵션을 추가하여 원하는 ip 대역을 static하게 설정도 가능하다.
3-1. Docker DNS
docker의 기본 docker0 bridge driver에는 DNS가 포함되어 있지 않으므로
사용자 정의 네트워크를 하나 생성하여 docker 내부 DNS를 확인해보고, 'alias'를 통해 그룹을 만들었을때
어떻게 동작하는지 확인해보자.
(위에서 생성한 'vswitch-net' 이라는 bridge를 사용해보도록 하겠습니다)
✅ 사용자 정의 네트워크를 가지고 컨테이너 3개를 그룹핑 해보기.
docker run -itd --name ssunghwan1 --net=vswitch-net --net-alias=testingGroup ubuntu:14.04
docker run -itd --name ssunghwan2 --net=vswitch-net --net-alias=testingGroup ubuntu:14.04
docker run -itd --name ssunghwan3 --net=vswitch-net --net-alias=testingGroup ubuntu:14.04

위와 같이 컨테이너 세개가 생성되고, ip를 할당받은 것을 확인할 수 있습니다.
- 동일한 네트워크에 있는 컨테이너를 하다 생성후에, 이 3개의 컨테이너에 ICMP를 보내보자.

동일한 네트워크의 컨테이너 내부에서 하기의 명령으로 같은 그룹에 있는 컨테이너에게 ICMP를 보냈을때,
3개의 컨테이너에서 동일하게 응답을 받는것을 확인할 수 있다. "alias 역할"
# 동일한 네트워크 대역을 가진 컨테이너 생성 후 접속
docker run -it --name=icmptesting --network=vswitch-net ubuntu:14.04 bash
# icmp 테스트
ping -c 4 testingGroup
그렇다면, 위와 같은 테스팅이 DNS와 무슨 연관이 있을까?
✅ Docker DNS의 역할
- Docker는 각 사용자 정의 네트워크에 대해 내장 DNS를 운영중임.
- 컨테이너가 네트워크에 연결될때
- 컨테이너 이름과 네트워크 alias가 DNS 레코드로 등록된다.
- 동일 alias를 가진 컨테이너는 A레코드에 여러 IP가 바인딩된다.
- 따라서 testingGroup의 alias는 사실상 멀티 A레코드 DNS 엔트리가 됩니다 ^^
추가적으로, 동일한 alias를 가진 여러 컨테이너가 있으면, Docker의 내장 DNS 서버는 이 이름을 조회할때
모든 컨테이너의 IP를 반환한다. 그래서 위와 같이 라운드 로빈 방식으로 ICMP 요청에 대한 응답을 반환하는 것.
- 실제로 하기와 같이 내장 DNS 서버를 확인해보자.
apt-get update && apt-get install -y dnsutils
nslookup testingGroup
cat /etc/resolv.conf

여기까지 네트워크에 대해서 알아봤습니다
감사합니다. :)
P.S
- 여러 Bridge와 연결된 Network Topology

현재는 단일 Host에서의 Bridge만 2개를 생성하여 테스팅을 해보았는데,
나중에 현업에서는 Multi-Host, 여러 Bridge간 토폴로지가 구성이 되어있을 것이다.
네트워크는 중요하기 때문에 햇갈리지 않게 기본 개념을 확실하게 잡고 스터디하자!!