CKA를 준비해보자 30일차 - Docker Networking

0

CKA

목록 보기
30/43

Docker Networking

dokcer의 network에 대해서 알아보도록 하자.

먼저 docker engine이 설치된 host가 다음과 같이 있다고 하자.

--------Host----------
| docker             |
|                    |
---------eth0---------
    192.168.1.10

다음과 같이 host의 network interface가 eth0으로 '192.168.1.10'을 가지고 있다고 하자.

이제 container를 만들 것인데, container생성 시에 network를 설정해 줄 수 있다. 다음과 같이 container의 network를 'none'으로 생성해줄 수 있다.

docker run --network none nginx

다음과 같이 none network를 가진 nginx container가 하나 생기게 되는 것이다.

--------Host---------
| ---container----  |
| |              |  |
| |              |  |
| ----------------  |
|  x x x x  x x x   |
|                   |
---------eth0--------
    192.168.1.10

none network으로 container를 만들면, 해당 container는 그 어떤 network 특성도 가지지 않기 때문에, docker내의 container들 끼리도 통신이 안되고, 외부로 네트워크도 안될 뿐더라, container내부에서 외부로도 통신이 불가능하다.

다음은 host network이다. container를 host의 network와 연결하였기 때문에 port를 통해서 포워딩이 될 수 있다.

--------Host----------
| ---container----   |
| |              |   |
| |              |   |
| ----------------   |
| 192.168.1.10:80    |
|                    |
---------eth0---------
    192.168.1.10

즉, 같은 host network를 사용하기 때문에 container는 host와 같은 network IP를 공유하며 port를 통해서 포트 포워딩되는 것이다. 따라서, host에서도 접근할 수 있고, container도 host에 접근이 가능하다.

단, 동일한 image의 container를 같은 port로 올릴 수는 없다. 이는 port가 이미 사용 중이기 때문에 충돌이 발생한다.

다음은 bridge network이다. 이 경우는 내부 사설망(internal private network)가 생성된다.

--------------------Host---------------
| ---container1---  ---container2---  | 
| |              |  |              |  |
| |              |  |              |  |
| ----------------  ----------------  |
| 172.17.0.2          172.12.0.3      |
|     |                  |            | 
|     -------bridge-------            |
|     |                  |            |
|     --------------------            |
|            172.17.0.0               |
|                 |                   |
|  docker         |                   |
----------------eth0-------------------
             192.168.1.10

bridge는 host network와는 다른 172.17.0.0과 같은 network를 가지는데, 이 bridge network를 virtual switch로 간주하여 container들의 IP를 172.17.0.x 대역으로 제공해준다. 이 덕분에 container들끼리의 통신이 가능한 것이다. 또한 host와의 통신이 가능한 gateway 역할도 하여, 외부와 container의 통신을 가능하게 해준다.

정리하자면 bridge network는 host안의 private network망이 만들어진 것이다.

bridge network는 docker가 설치되면서 만들어지는데, 다음과 같다.

docker network ls
NETWORK ID     NAME       DRIVER    SCOPE
2692d913c905   bridge     bridge    local
21a2269850b6   host       host      local
4cfe8789ba2e   none       null      local

이렇게 docker는 bridge라는 network를 만들어내는데, 재미난 것은 이 bridge는 하나의 network namespace이기 때문에, virtual network interface를 가져 host와 연결된다. 그래서 ip link를 실행하면 보이게 된다.

ip link
...
3: docker0: <NO-CARRIER,BROADCAST ....
... ff:ff:ff:ff:ff:ff

docker0이 바로 bridge network가 host에 보이는 것이다. 즉, bridge라는 이름으로 docker network로 쓰이면서, host에서는 docker0라는 이름을 가지는 것이다.

따라서, ip addr를 통해서 bridge network의 ip까지도 알아낼 수 있다.

ip addr
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc
...
    inet 172.17.0.1/24 ...

172.17.0.1/24가 바로 bridge가 가지는 IP가 되는 것이다.

이전 시간에 bridge에 서로 다른 network namespace들을 연결한 것처럼, 사실 container를 만들면 서로 다른 network namespace들이 하나씩 만들어지고, bridge로 연결된다.

docker run nginx

이렇게 container를 만들고, ip netns를 실행하면 network namespace가 추가된 것을 볼 수 있을 것이다.

ip netns
b3165c10a92b

b3165c10a92b namespace를 볼 수 있다. 이를 통해서 알 수 있는 것은 container와 network namespace는 사실상 동일하다는 것이다.

그런데, docker는 어떻게 container를 bridge에 연결하는 것일까?? 이는 이전 시간에 배웠던 내용과 아주 동일하다. 각 container마다 network namespace를 만들고, 해당 namespace에 virtual network interface를 만들고, 이를 bridge network interface에 연결하는 것이다.

-------------Host---------------
|       --b3165c10a92b--       |   
|       |              |       |
|       |              |       |
|       ----eth0@if8----       |
|              |               |
|        vethbblc34@1f7        |
|     -------bridge-------     |
|     |     docker0      |     |
|     --------------------     |
|          172.17.0.1          |
|               |              |
|  docker       |              |
--------------eth0--------------
           192.168.1.10

이렇게 연결된 것을 볼 수 있다. 이전 시간에 봤듯이 container network namespace에 해당하는 eth0에 IP를 할당을 해주어야한다. 이미 이 작업도 docker가 해주었다.

ip addr를 통해서 확인해보도록 하자.

ip -n b3165c10a92b addr
7: eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_IP> mtu ...
    link/ether 02:42:ac:11...
    inet 172.17.0.3/16 brd 172.17.255.255 scope ...

172.17.0.3/16을 확인할 수 있다.

-------------Host---------------
|       --b3165c10a92b--       |   
|       |              |       |
|       |              |       |
|       ----eth0@if8----       |
|          172.17.0.3          |
|              |               |
|        vethbblc34@1f7        |
|     -------bridge-------     |
|     |     docker0      |     |
|     --------------------     |
|          172.17.0.1          |
|               |              |
|  docker       |              |
--------------eth0--------------
           192.168.1.10

위와 같은 동작이 매번 container들이 생성될 때마다 실행되는 것이다.

docker bridge는 하나의 private network망으로서 port역시도 bridge내부에서만 의미가 있다. 따라서 host에서는 host IP에 port로 container 내부에 열린 port에 접근할 수가 없다.

가령 nginx server를 container로 배포하고 80 port로 열었다면, hostip:80으로 접근이 안된다는 것이다.

재밌는 것은 bridge network 자체가 host와 연결되어 있기 때문에, bridge network의 IP대역으로 port에 접근하면 성공한다.

curl http://172.17.0.3:80

다음과 같이는 가능하다는 것이다.

단, host외부에서는 해당 IP를 찾지 못하므로 에러가 발생한다. 이는 bridge가 host내에서만 유의미한 IP를 가지는 private network이기 때문이다.

이러한 문제를 해결하기 위한 방법으로 port forwarding를 해주면 된다. 즉, container의 network namespace에 있는 port를 host의 port로 연결하여 포워딩시키는 것이다.

docker run -p 8080:80 nginx

다음은 nginx의 80 port를 host의 8080으로 포워딩하는 것이다.

-------------Host---------------
|       --b3165c10a92b--       |   
|       |              |       |
|       |              80-----8080
|       |              |       |
|       ----eth0@if8----       |
|          172.17.0.3          |
|              |               |
|        vethbblc34@1f7        |
|     -------bridge-------     |
|     |     docker0      |     |
|     --------------------     |
|          172.17.0.1          |
|               |              |
|  docker       |              |
--------------eth0--------------
           192.168.1.10

다음과 같이 nginx container의 port를 host의 port를 통해서 외부로 노출하는 것이다.

가령 외부에서 192.168.1.10:8080으로 접근하면 container에 접근이 가능한 것이다.

curl http://192.168.1.10:8080
Welcome to nginx!

그런데, 어떻게 한 port에서 다른 port로 traffic을 흘려보낼 수 있는 것인가?? docker는 어떻게 이러한 일을 수행할 수 있는 것일까?

바로 NAT table 규칙을 추가하는 것이다. 즉, docker내의 container 주소를 host주소로 변환하되, 해당 8080 port로부터 전달된 packet에 대해서만 바꾼다는 것이다.

iptables -t nat -A DOCKER -j DNAT --dport 8080 --to-destination 172.17.0.3:80

host의 8080 port애 요청이 오면 172.17.0.3:80으로 packet을 변환하여 처리하는 것이다. host는 내부 bridge와 연결되어 있으므로 172.17.0.3:80를 처리할 수 있는 것이다.

다음을 통해서 확인할 수 있다.

iptables -nvL -t nat
Chain   DOCKER  (2 references)
target      prot    opt     source      desintation
DNAT        tcp     --      anywhere    anyware     tcp dpt:8080to:172.17.0.2:80

0개의 댓글