간단하게 ubuntu:20.04 컨테이너를 생성한 뒤에, ifconfig를 입력해서 컨테이너의 네트워크 인터페이스를 확인해봅시다.
기본으로 제공되는 ubuntu 20.04 이미지에서는 ifconfig 명령어를 사용할 수 없으며, apt package 업데이트와 net-tools를 설치해주어야 ifconfig 명령어를 사용할 수 있습니다.
제가 미리 ubuntu 20.04에 net-tools를 설치한 이미지를 올려두었으니, 그것을 사용하셔도 됩니다.
docker run -i -t --name network-test changjunlee/ubuntu:net-tools
기본적으로 도커는 172.17.0.x의 내부 IP를 컨테이너에 순차적으로 할당하며, 이 IP는 컨테이너를 재시작할 때마다 변경될 수 있습니다. 그리고, 내부 IP는 도커가 설치된 호스트, 즉 내부 망에서만 사용할 수 있는 IP로써 외부에서 사용하려면 연결이 필요합니다.
외부와 연결되는 과정은 컨테이너를 시작할 때마다 호스트에 veth라는 네트워크 인터페이스를 생성함으로써 이루어집니다.
컨테이너에서 Ctrl + P, Q를 입력해서 호스트로 빠져나와 ifconfig나 ip addr과 같은 커맨드를 입력해보면, 다음과 같이 veth 인터페이스가 생긴 것을 확인할 수 있습니다.
도커는 각 컨테이너에 외부와의 네트워크를 제공하기 위해서 컨테이너마다 가상 네트워크 인터페이스를 호스트에 생성하고, 이 인터페이스의 이름이 바로 veth로 시작합니다.
사용자가 직접 이 가상 네트워크를 생성할 필요는 없으며, 컨테이너가 생성될 때 도커 엔진에서 자동으로 생성됩니다.
veth 인터페이스뿐 아니라 docker0이라는 브리지도 존재하는데, 이것은 각 veth 인터페이스와 바인딩되어서 호스트의 eth0 인터페이스와 이어주는 역할을 합니다.
컨테이너와 호스트의 네트워크는 아래와 같은 구성으로 이루어져 있습니다.
컨테이너의 eth0 인터페이스는 호스트의 veth...라는 인터페이스와 연결되어 있으며, veth 인터페
이스는 docker0 브리지와 바인딩되어서 외부와 통신할 수 있습니다.
컨테이너를 생성하면 기본적으로 docker0 브리지를 통해 외부와 통신할 수 있는 환경을 사용할 수 있지만, 사용자의 선택에 따라서 여러 네트워크 드라이버를 사용할 수도 있습니다.
도커 자체에서 제공하는 드라이버는 브리지(bridge), 호스트(host), 논(none), 컨테이너(container), 오버레이(overlay)가 있습니다.
서드파티(3rd-party) 플러그인 솔루션으로는 weave, flannel, openvswitch 등이 있으며, 이것들은 더 확장된 네트워크 구성을 위해 활용됩니다.
이 글에서는 도커 자체만으로 사용가능한 브리지, 호스트, 논, 컨테이너에 대해서 알아보겠습니다.
먼저 도커에서 기본적으로 사용할 수 있는 네트워크는 docker network ls 명령어를 통해서 확인할 수 있습니다.
(도커 네트워크를 다루는 명령어는 docker network로 시작합니다.)
docker network ls
따로 네트워크를 생성하지 않더라도, 이미 브리지, 호스트, 논 네트워크가 있는 것을 확인할 수 있습니다.
docker network inspect 명령어를 사용하면 네트워크의 자세한 정보를 살펴볼 수 있습니다.
브리지 네트워크는 컨테이너를 생성할 때 자동으로 연결되는 docker0 브리지를 활용하도록 설정되어 있습니다. 이 docker0 브리지는 172.17.0.x IP 대역을 컨테이너에 순차적으로 할당합니다. 위의 inspect 커맨드로 확인해보면, config 항목의 서브넷과 게이트웨이가 각각 172.17.0.0/16, 172.17.0.1로 설정되어 있음을 확인할 수 있습니다.
또한, 이 브리지 네트워크를 사용하는 컨테이너를 확인할 수도 있습니다. 만약 아무런 설정을 하지 않고 컨테이너를 생성하면 자동으로 docker0의 이름을 가진 브리지 네트워크를 사용하게 됩니다.
기본적으로 존재하는 docker0을 사용하는 브리지 네트워크가 아닌 새로운 브리지 타입의 네트워크를 생성할 수도 있습니다.
아래 커맨드로 mybridge라는 새로운 브리지 네트워크를 생성해봅시다.
docker network create --driver bridge mybridge
이렇게 생성된 브리지 네트워크는 docker run 또는 create 명령어에서 --net 옵션의 값을 새로운 브리지 네트워크로 설정하여 사용되도록 할 수 있습니다.
1. docker run -i -t --name mynetwork_container \
2. --net mybridge \
3. changjunlee/ubuntu:net-tools
컨테이너 내부로 들어와서 ifconfig로 컨테이너의 네트워크 인터페이스를 확인해봅시다.
새로운 IP 대역(172.18.0.2)이 할당된 것을 확인할 수 있습니다. 이처럼 브리지 타입의 네트워크를 생성하면 도커는 IP 대역을 차례로 할당하는데, 여기서는 172.18.0.x 대역의 내부 IP가 할당되었습니다.
이렇게 생성된 사용자 정의 네트워크(mybridge)는 docker network disconnect/connect 명령어를 통해 유동적으로 컨테이너에 붙이고 뗄 수 있습니다.
1. docker network disconnect mybridge mynetwork_container
2. docker network connect mybridge mynetwork_container
이 명령어는 호스트나 논 네트워크 등과 같은 특별한 네트워크 모드에서는 사용할 수 없고, 브리지 또는 오버레이 네트워크와 같은 특정 IP 대역을 갖는 네트워크 모드에서만 이 명령어를 사용할 수 있습니다.
만약 네트워크의 서브넷, 게이트웨이, IP 할당 범위 등을 임의로 설정하면 네트워크를 생성할 때 아래 명령어와 같이 --subnet, --ip-range, --gateway 옵션을 추가하면 됩니다. (단, --subnet과 --ip-range는 같은 대역이어야 합니다.)
1. docker network create --driver=bridge \
2. --subnet=172.72.0.0/16 \
3. --ip-range=172.72.0.0/24 \
4. --gateway=172.72.0.1 \
5. my_custom_network
IP에서 슬래쉬(/) 숫자(/16, /24)는 네트워크 마스트의 비트수를 의마합니다.
컨테이너를 생성할 때, 네트워크를 호스트로 설정하면 호스트의 네트워크 환경을 그대로 사용할 수 있습니다.위에서 설명한 브리지 드라이버 네트워크와는 달리 호스트 드라이버의 네트워크는 별도로 생성할 필요없이 기존의 host라는 이름의 네트워크를 사용합니다.
1. docker run -i -t --name network_host \
2. --net host \
3. changjunlee/ubuntu:net-tools
이렇게 생성한 컨테이너 내부에서 네트워크 환경을 확인(ifconfig 명령어)하면 호스트와 동일하다는 것을 알 수 있습니다.
또한, 호스트 머신의 이름도 컨테이너가 물려받기 때문에, 컨테이너의 호스트 이름이 --name 옵션이 없음에도 도커 엔진이 설치된 호스트의 이름으로 설정된 것을 확인할 수 있습니다.
컨테이너의 네트워크를 호스트 모드로 설정하면 컨테이너 내부의 어플리케이션을 별도의 포트 포워딩없이 바로 서비스할 수 있습니다. 이것은 실제 호스트에서 어플리케이션을 외부에 노출하는 것과 동일합니다.예를 들어, 이전에 아파치 웹 서버 구동을 호스트 네트워크를 사용하는 컨테이너에서 구동한다면, 호스트의 IP와 컨테이너의 아파치 웹 서버 포트인 80을 사용해서 바로 접근할 수 있습니다.
논 네트워크는 말 그대로 아무런 네트워크를 사용하지 않는 것을 의미합니다. 아래와 같은 명령어로 컨테이너를 생성하면, 이 컨테이너는 외부와 연결이 단절됩니다.
1. docker run -i -t --name network_none \
2. --net none \
3. changjunlee/ubuntu:net-tools
생성한 컨테이너 내부에서 네트워크 인터페이스를 확인하면, 로컬호스트(lo)만 존재하는 것을 확인할 수 있습니다.
--net 옵션으로 컨테이너를 설정하면 다른 컨테이너의 네트워크 네임스페이스 환경을 공유할 수 있습니다. 공유되는 속성은 내부 IP, 네트워크 인터페이스의 맥(MAC) 주소 등입니다. 어떻게 공유하는지 살펴보겠습니다.
1. docker run -i -t -d --name network_container_1 changjunlee/ubuntu:net-tools
2. docker run -i -t -d --name network_container_2 \
3. --net container:network_container_1 \
4. changjunlee/ubuntu:net-tools
--net container:[다른 컨테이너의 ID]로 입력합니다.
-i -t -d 옵션을 모두 사용하면, 컨테이너 내부에서 shell을 실행하지만 내부로 들어가지 않으며, > 컨테이너도 종료되지 않아 테스트용으로 많이 사용합니다.
위와 같은 커맨드로 network_container_1의 네트워크 환경을 공유받는 network_container_2 컨테이너를 생성할 수 있습니다.
이처럼 다른 컨테이너의 네트워크 환경을 공유하면 내부 IP를 새로 할당받지 않으며, 호스트에 veth로 시작하는 가상 네트워크 인터페이스도 생성되지 않습니다.
즉, network_container_2의 네트워크와 관련된 사항은 모두network_container_1과 같게 설정됩니다.
실제로 그러한지 다음 커맨드로 확인해봅시다.
1. docker exec network_container_1 ifconfig
2. docker exec network_container_2 ifconfig
브리지 타입의 네트워크와 run 커맨드의 --net-alias 옵션을 함께 사용하면, 특정 호스트의이름으로 컨테이너 여러 개에 접근할 수 있습니다.
아래 커맨드를 통해서 위에서 생성한 mybridge 네트워크를 이용해 컨테이너 3개를 생성하는데, --net-alias 값은 junstar로 설정해서 생성해보겠습니다.
1. docker run -i -t -d --name network_alias_container1 \
2. --net mybridge \
3. --net-alias junstar ubuntu:20.04
4. docker run -i -t -d --name network_alias_container2 \
5. --net mybridge \
6. --net-alias junstar ubuntu:20.04
7. docker run -i -t -d --name network_alias_container3 \
8. --net mybridge \
9. --net-alias junstar ubuntu:20.04
--net-alias값을 설정하면 다른 컨테이너에서 junstar라는 호스트 이름으로 3개의 컨테이너에 접근할 수 있게 됩니다.
inspect 명령어로 각 컨테이너의 IP를 확인해보면, 다음과 같습니다.
이제 3개의 컨테이너에 접근할 컨테이너를 새롭게 생성한 뒤에, junstar라는 이름으로 ping 요청을 전송해보겠습니다.
(ping command를 사용하려면 iputils-ping을 설치해주어야 합니다)
1. docker run -i -t --name network_alias_junstar \
2. --net mybridge ubuntu:20.04
4번의 ping 요청을 날렸는데, 컨테이너 3개의 IP로 각각 ping이 전송된 것을 확인할 수 있습니다.
이는 도커 엔진에 내장된 DNS가 junstar라는 호스트 이름을 --net-alias 옵션으로 junstar를 설정한 컨테이너로 변환하기 때문입니다. (매번 달라지는 IP를 결정하는 것은 별도의 알고리즘이 아닌 라운드 로빈 방식으로 결정됩니다.)
다음은 도커 네트워크에서 사용하는 DNS와 --net-alias의 관계를 나타냅니다.
도커의 DNS는 호스트의 이름으로 유동적인 컨테이너를 찾을 때 주로 사용됩니다.
가장 대표적인 예가 --link 옵션인데, 이것은 컨테이너의 IP가 변경되어서 별명으로 컨테이너를 찾을 수 있게 DNS에 의해서 자동으로 관리됩니다.
(단, 이 경우는 디폴트 브리지 네트워크의 컨테이너 DNS라는 점이 다릅니다. 위는 mybridge 네트워크 컨테이너의 DNS)
--net-alias 옵션은 --link와 유사한 원리로 동작합니다. 도커는 기본 브리지 네트워크가 아닌 사용자가 정의한 브리지 네트워크에 사용되는 내장 DNS 서버를 가지며, DNS의 IP는 127.0.0.11 입니다.
mybridge라는 이름의 네트워크에 속한 3개의 컨테이너는 run 명령어로 생성할 때 --net-alias 옵션에서 junstar라는 값을 입력했으며, 이 컨테이너의 IP는 DNS 서버에 junstar라는 호스트 이름으로 등록됩니다.
mybridge 네트워크에 속한 컨테이너에서 junstar라는 호스트 이름으로 접근하면 DNS 서버는 라운드 로빈 방식을 이용해서 컨테이너의 IP 리스트를 반환하게 됩니다.
ping 명령어는 이 IP 리스트에서 첫 번째 IP를 사용하므로 매번 다른 IP로 ping을 전송하게 됩니다.
dig라는 툴을 사용하면 이를 확인할 수 있는데, dig는 DNS로 도메인 이름에 대응하는 IP를 조회할 때 사용하는 툴입니다.
apt install dnsutils를 입력하여 설치가 가능하며, dig로 junstar 호스트 이름이 변환되는 IP를 확인할 수 있습니다.
(이 명령어를 반복적으로 수행하면, IP 리스트의 순서가 계속 변경되는 것을 확인할 수 있습니다.)
MacVLAN은 호스트의 네트워크 인터페이스 카드를 가상화하여 물리 네트워크 환경을 컨테이너에게 동일하게 제공합니다.
따라서, MacVLAN을 사용하면 컨테이너는 물리 네트워크상에서 가상의 맥(MAC) 주소를 가지며, 해당 네트워크에 연결된 다른 장치와의 통신이 가능해집니다.
예제가 장비 2대가 필요하기 때문에.. 예제는 생략하도록 하겠습니다.