beadss / docker-study

0 stars 0 forks source link

도커 네트워크 구조 #9

Open joont92 opened 5 years ago

joont92 commented 5 years ago

다들 잘 아시겠지만 개인적으로 조금 알아보고 정리한 내용 공유드립니다


veth interface, NET namespace

도커의 네트워크 구조를 이해하기 위해선 먼저 리눅스의 NET namespaceveth interface에 대해 알아야한다

(자세한 내용은 https://bluese05.tistory.com/28 를 참조한다.. 100배 설명이 더 잘되어 있다)

도커 네트워크 구조

도커는 위에서 언급한 veth interface와 NET namespace 를 사용해 네트워크를 구성한다
그림으로 보면 아래와 같다

docker-network

  1. 생성되는 도커 컨테이너는 namespace 로 격리되고, 그 상태에서 통신을 위한 네트워크 인터페이스(eth0)를 할당받는다
  2. host PC의 veth interface 가 생성되고 도커 컨테이너 내의 eth0 과 연결한다

    컨테이너의 네트워크 격리를 달성하기 위해 선택한 방법인 것 같다

  3. host PC의 veth interface 는 docker0 이라는 다른 veth interface 와 연결된다
  4. 이 과정이 컨테이너가 추가될 때 마다 반복된다

여기서 나오는 docker0은 docker 실행 시 자동으로 생성되는 가상 브릿지 이다
컨테이너가 생성될 때 마다 가상 인터페이스가 생성되고, 이 브릿지에 바인딩 되는 형태라고 보면 된다
즉, 모든 컨테이너는 외부로 통신할 때 이 docker0 브릿지를 무조건 거쳐가야 되는 것이다
이러한 특징 때문에 도커 컨테이너끼리 서로 통신이 가능한 것이다

docker0 브릿지에 할당된 ip 대역에 맞춰 컨테이너들도 ip가 할당된다
e.g. docker0 = 172.17.0.1/16, container1 = 172.17.0.2, container2 = 172.17.0.3 ...

아래는 컨테이너 2개를 띄웠을 떄의 모습이다

$ ip link ls

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 08:00:27:d8:72:d9 brd ff:ff:ff:ff:ff:ff
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 02:42:c0:e5:8c:93 brd ff:ff:ff:ff:ff:ff
4: vethf3bf38b@if34: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default
    link/ether 02:13:50:cf:5c:48 brd ff:ff:ff:ff:ff:ff link-netnsid 1
5: veth1680438@if36: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default
    link/ether f2:b2:f7:eb:a5:55 brd ff:ff:ff:ff:ff:ff link-netnsid 2

그리고 아래는 브릿지에 연결되어 있는 veth interface 를 조회한 모습이다

$ brctl show docker0
bridge name bridge id       STP enabled interfaces
docker0     8000.0242c0e58c93   no      veth1680438
                                        vethf3bf38b

참고로 mac이나 window에서는 이 docker0 브릿지나 veth interface 들을 볼 수 없다
VM 안으로 감쳐줘 있기 때문이다

사실 docker container의 네트워크 모드는 총 4개이다

bridge, host, container, none 으로 총 4개가 존재한다
위에서 살펴본 구조가 bridge 네트워크로 생성한 container의 동작방식이며, default 값이다
kubernetes의 pod을 생성할때는 container 모드를 사용한다
자세한 내용은 https://bluese05.tistory.com/38를 참조한다

docker-compose 로 띄우면 다른 네트워크 대역을 가진다

docker-compose 를 공부할 때 docker-compose 로 띄운 컨테이너들은 모두 같은 네트워크에 속한다는 말이 있었고, 실제로도 그랬다
단독으로 띄운 도커 컨테이너와 docker-compose 를 통해 띄운 도커 컨테이너들을 들어가서 ip를 확인해보면 각자 네트워크 대역대가 다름을 알 수 있다

이는 docker-compose 로 컨테이너를 띄우면 compose 로 묶은 범위에 맞춰 브릿지를 하나 더 생성하기 때문이다

# 이렇게 docker-compose로 컨테이너를 띄우고
$ docker-compose -f docker-compose.yml up
# 네트워크 인터페이스를 확인해보면
$ ip link ls

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 08:00:27:d8:72:d9 brd ff:ff:ff:ff:ff:ff
# docker0 브릿지와 아까 띄워놓은 컨테이너들
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 02:42:c0:e5:8c:93 brd ff:ff:ff:ff:ff:ff
4: vethf3bf38b@if34: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default
    link/ether 02:13:50:cf:5c:48 brd ff:ff:ff:ff:ff:ff link-netnsid 1
5: veth1680438@if36: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default
    link/ether f2:b2:f7:eb:a5:55 brd ff:ff:ff:ff:ff:ff link-netnsid 2
# 여기서부터 docker-compose로 띄운 부분
# bridge가 하나 더 생겼다!
6: br-776e18676383: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 02:42:e0:f3:47:6b brd ff:ff:ff:ff:ff:ff
7: vethc059d77@if39: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-776e18676383 state UP mode DEFAULT group default
    link/ether 0a:f7:37:0c:cd:64 brd ff:ff:ff:ff:ff:ff link-netnsid 3
8: veth0fea37d@if41: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-776e18676383 state UP mode DEFAULT group default
    link/ether b2:95:86:71:c6:43 brd ff:ff:ff:ff:ff:ff link-netnsid 4

이런 상태이므로 물론 docker-compose로 띄운 컨테이너와 일반 컨테이너간의 통신은 불가능하다(서로 경유하는 브릿지가 다르므로)

외부와 통신은 어떻게 할까?

우리는 컨테이너를 띄울 때 아래와 같이 포트포워딩을 설정하여 외부에 컨테이너를 공개할 수 있다(+expose)

# 포트포워딩 설정하여 컨테이너 생성
$ docker container run -p 8080:80 nginx
# 포트 listen 확인
$ netstat -nlp | grep 8080

tcp6       0      0 :::8080                 :::*                    LISTEN      26113/docker-proxy-

근데 보다시피 docker-proxy 라는 프로세스가 해당 포트를 listen 하고 있음을 볼 수 있다
이는 간단히 docker host 로 들어온 요청을 해당하는 컨테이너로 넘기는 역할만을 수행하는 프로세스이다
컨테이너에 포트포워딩이나 expose를 설정했을 경우 같이 생성되는 프로세스이다

그렇지만 중요한것은, 실제로 포트포워딩을 이 docker-proxy가 담당하는 것이 아니라, host PC iptables 에서 관리한다는 점이다

$ iptables -t nat -L -n

Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
MASQUERADE  all  --  172.17.0.0/16        0.0.0.0/0
MASQUERADE  tcp  --  172.17.0.4           172.17.0.4           tcp dpt:80

Chain DOCKER (2 references)
target     prot opt source               destination
RETURN     all  --  0.0.0.0/0            0.0.0.0/0
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8080 to:172.17.0.4:80

보다시피 모든 요청을 DOCKER Chain 으로 넘기고, DOCKER Chain 에서는 DNAT를 통해 포트포워딩을 해주고 있음을 볼 수 있다
(이 iptables 룰은 docker daemon이 자동으로 한다)

docker-proxy 는 iptables가 어떠한 이유로 NAT를 사용하지 못하게 될 경우 사용된다고 한다

참고 :