Docker & Kubernetes

도커 네트워크(Docker Network)의 구조와 기능

beekei 2022. 12. 27. 00:56
반응형

도커 네트워크 구조

도커는 컨테이너에 내부 IP를 순차적으로 할당하는데, 이 IP는 컨테이너를 재시작할 때마다 변경될 수 있다.

이 내부 IP는 도커가 설치된 호스트, 즉 내부 망에서만 쓸 수 있는 IP이므로 외부와 연결되어야 한다.

도커는 각 컨테이너에 외부와의 네트워크를 제공하기 위해 컨테이너마다 가상 네트워크 인터페이스를 호스트에 생성하는데 이름이 veth로 시작한다.

veth 인터페이스는 사용자가 직접 생성할 필요는 없으며 컨테이너가 생설될 때 도커 엔진이 자동으로 생성한다.

 

도커가 설치된 호스트에서 ifconfig나 ip addr과 같은 명령어로 네트워크 인터페이스를 확인하면 실행 중인 컨테이너 수만큼 veth로 시작하는 인터페이스가 생성된 것을 확인할 수 있다.

$ ifconfig

docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
...

eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
...

veth05c082e: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
...

docker0은 각 veth 인터페이스와 바인딩돼 호스트의 eth0 인터페이스와 이어주는 브릿지 역활을 한다.

eth0은 공인 IP 또는 내부 IP가 할당되어 실제로 외부와 통신할 수 있는 호스트의 네트워크 인터페이스다.

출력 결과 컨테이너의 eth0 인터페이스는 호스트의 veth로 시작하는 인터페이스와 연결됐으며 veth 인터페이스는 docker0 브리지와 바인딩돼 외부와 통신할 수 있다.

도커 네트워크의 구조


도커 네트워크 기능

컨테이너를 생성하면 기본적으로 docker0 브리지를 통해 외부와 통신할 수 있는 환경을 사용할 수 있지만 사용자의 선택에 따라 여러 네트워크 드라이버를 쓸 수도 있다.

도커 자체적으로 제공하는 드라이버는 브리지(bridge), 호스트(host), 논(none), 컨테이너(container), 오버레이(overlay)가 있다.

서드파티(third-party) 플러그인 솔루션으로 더 확장된 네트워크 구성을 구축할 수 도 있다.

$ docker network ls
NETWORK ID     NAME             DRIVER    SCOPE
2b8eafae3d91   bridge           bridge    local
64163d0b6165   host             host      local
21cb34f62ad6   none             null      local

네트워크 목록을 출력해보면 이미 브리지, 호스트, 논 네트워크가 존재함을 알 수 있다.

브리지 네트워크는 컨테이너를 생성할때 자동으로 연결되는 docker0 브리지를 활용하도록 설정되있다.

$ docker network inspect bridge
...
"IPAM": {
    "Driver": "default",
    "Options": null,
    "Config": [
        {
            "Subnet": "172.17.0.0/16",
            "Gateway": "172.17.0.1"
        }
    ]
},
...

브리지 네트워크 상세 정보를 확인해보면 Config 항목의 서브넷과 게이트웨이가 172.17.0.0/16과 172.17.0.1로 설정되있다.

Containers 항목에는 현재 브리지 네트워크를 사용하는 컨테이너를 확인할 수 도 있다.

브리지 네트워크

컨테이너는 연결된 브리지를 통해 외부와 통신할 수 있다.

기본적으로 존재하는 docker0을 사용하는 브리지 네트워크가 아닌 새로운 브리지 타입의 네트워크를 생성할 수 있다.

$ docker network create --driver bridge mybridge
5cd58d8f85b3e977c225520ca26fdc04e4f6ca19df718e87a5cf99e93ee8c123
$ docker network inspect mybridge
...
"IPAM": {
    "Driver": "default",
    "Options": {},
    "Config": [
        {
            "Subnet": "172.19.0.0/16",
            "Gateway": "172.19.0.1"
        }
    ]
},
...

도커 컨테이너를 실행할때 --net 옵션의 값을 설정하면 해당 네트워크를 사용할 수 있다.

$ docker run -d --name ubuntu -it --net mybridge ubuntu:20.04
$ docker exec -it ubuntu bash
$ root@cd88e44a93c2:/# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.19.0.2  ....

컨테이너 내부에서 ifconfig 명령어를 입력해보면 172.19 대역의 내부 IP가 할당된것을 확인할 수 있다.

connect, disconnect 명령어로 유동적으로 연결하고 연결을 해제할 수 있다.

$ docker network disconnect mybridge ubuntu
$ docker ps
CONTAINER ID   IMAGE          COMMAND   CREATED         STATUS         PORTS     NAMES
cd88e44a93c2   ubuntu:20.04   "bash"    8 minutes ago   Up 8 minutes             ubuntu

$ docker network connect mybridge ubuntu
$ docker ps
CONTAINER ID   IMAGE          COMMAND   CREATED         STATUS         PORTS                NAMES
cd88e44a93c2   ubuntu:20.04   "bash"    9 minutes ago   Up 9 minutes   0.0.0.0:22->22/tcp   ubuntu

네트워크의 서브넷, 게이트웨이, IP 할당 범위 등을 임의로 설정하려면 --subnet, --gateway, --ip-range 옵션을 사용하면 된다.

단 --subnet과 --ip-range는 같은 대역이여야 한다.

호스트 네트워크

네트워크를 호스트로 설정하면 호스트의 네트워크 환경을 그대로 사용할 수 있다.

호스트 드라이버의 네트워크는 별도로 생성할 필요 없이 기존에 host 네트워크를 사용하면 된다.

$ docker run -d --name ubuntu -it --net host ubuntu:20.04

컨테이너 내부에서 ifconfig 명령어로 확인해보면 실제 호스트와 같은 네트워크가 출력된다.

컨테이너의 네트워크를 호스트 모드로 설정하면 컨테이너 내부의 애플리케이션을 별로의 포트 포워딩 없이 바로 서비스할 수 있다.

실제 호스트에서 애플리케이션을 외부에 노출하는 것과 같다.

논 네트워크

none은 말 그대로 아무런 네트워크를 쓰지 않는 것이다.

$ docker run -d --name ubuntu -it --net none ubuntu:20.04

위와 같이 컨테이너를 생성하면 외부와 연결이 단절된다.

컨테이너 내부에서 ifconfig 명령어로 확인해보면 로컬호스트를 나타내는 lo 외에는 존재하지 않는다.

컨테이너 네트워크

컨테이너 네트워크는 다른 컨테이너의 네트워크 네임스페이스 환경을 공유할 수 있다.

내부IP, 네트워크 인터페이스의 맥(MAC) 주소 등의 속성이 공유된다.

--net 옵션의 값으로 container:<컨테이너명>와 같이 입력하면 된다.

$ docker run -d -it --name bridge_ubuntu --net mybridge ubuntu:20.04
$ docker run -d -it --name container_ubuntu --net container:bridge_ubuntu ubuntu:20.04

$ docker exec -it bridge_ubuntu bash
$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.19.0.2  netmask 255.255.0.0 ...
        
$ docker exec -it container_ubuntu bash
$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.19.0.2  netmask 255.255.0.0 ...

두 컨테이너의 eth0 정보가 완전히 같은 것을 확인할 수 있다.

브리지 네트워크와 --net-alias

브리지 타입의 네트워크와 run 명령어의 --net-alias 옵션을 사용하면 특정 호스트 이름으로 컨테이너 여러 개의 접근할 수 있다.

$ docker run -d -it \
--name ubuntu1 \
--net mybridge \
--net-alias mybridge_alias \ 
ubuntu:20.04

$ docker run -d -it \
--name ubuntu2 \
--net mybridge \
--net-alias mybridge_alias \ 
ubuntu:20.04

$ docker run -d -it \
--name ubuntu3 \
--net mybridge \
--net-alias mybridge_alias \ 
ubuntu:20.04

$ docker inspect ubuntu1 | grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "",
                    "IPAddress": "172.19.0.2",
$ docker inspect ubuntu2 | grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "",
                    "IPAddress": "172.19.0.3",
$ docker inspect ubuntu3 | grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "",
                    "IPAddress": "172.19.0.4",

inspect 명령어로 각 컨테이너의 IP를 확인해보면 172.19 대역의 IP인것을 확인할 수 있다.

$ docker run -d -it --name ubuntu4 --net mybridge ubuntu:20.04
$ docker exec -it ubuntu4 bash

$ root@9c31d48a0152:# ping -c 1 mybridge_alias
PING mybridge_alias (172.19.0.2) 56(84) bytes of data.
...

$ root@9c31d48a0152:/# ping -c 1 mybridge_alias
PING mybridge_alias (172.19.0.3) 56(84) bytes of data.
...

$ root@9c31d48a0152:/# ping -c 1 mybridge_alias
PING mybridge_alias (172.19.0.4) 56(84) bytes of data.
...

또 다른 컨테이너에 접속해 위에서 설정한 alias로 핑을 보내보면 각각 172.19.0.2, 172.19.0.3, 172.19.0.4로 전송된 것을 확인할 수 있다.

 

매번 달라지는 IP를 경정하는 것은 별도의 알고리즘이 아닌 라운드 로빈(round-robin)방식 이다.

이것이 가능한 이유는 도커 엔진에 내장된 DNS가 mybridge_alias라는 호스트 이름을 --net-alias 옵션으로 mybridge_alias을 설정한 컨테이너로 변환하기 때문이다.

--net-alias와 도커 DNS의 작동 구조

dig 명령어를 사용하면 해당 alias가 변환되는 IP를 확인할 수 있다.

$ root@9c31d48a0152:/# apt-get install dnsutils
$ root@9c31d48a0152:/# dig mybridge_alias
...
;; ANSWER SECTION:
mybridge_alias.		600	IN	A	172.19.0.4
mybridge_alias.		600	IN	A	172.19.0.3
mybridge_alias.		600	IN	A	172.19.0.2
...

도커의 DNS는 --link 옵션처럼 호스트 이름으로 유동적인 컨테이너를 찾을 때 주로 사용한다.

반응형