본문 바로가기
IT & 데이터 사이언스/Data Engineering

[Docker] Docker 네트워크

by 바른 호랑이 2024. 8. 8.
728x90
반응형

안녕하세요. 바른호랑이입니다.

이번 게시글에서는 Docker Network에 대해서 알아볼 예정입니다.

게시글은 '시작하세요! 도커/쿠버네티스 친절한 설명으로 쉽게 이해하는 컨테이너 관리'를 기반으로 작성하였으니 참고 바랍니다.

Docker 컨테이너는 도커 엔진 호스트가 가진 네트워크 인터페이스가 아닌 별도의 네트워크 인터페이스를 가지고 있으며, 이는 컨테이너 내부에서 ifconfig를 입력해 보면 보다 자세히 확인할 수 있습니다. 아래의 코드를 입력해 확인해 보면 보다 정확하게 알 수 있습니다.

# create container
sudo docker run -i -t --name tc01 ubuntu:24.04

# install packages
apt update
apt install net-tools

# check network details
ifconfig

Docker는 컨테이너에 내부 IP를 순차적으로 할당하며, 컨테이너를 재시작할 경우 컨테이너의 IP주소는 변경될 수 있다는 특징을 가지고 있습니다. 내부 IP의 경우 도커가 설치된 호스트 내부 망에서만 사용할 수 있기에 외부와 연결을 하기 위해서는 별도의 네트워크 인터페이스가 필요하고, 도커는 이와 같은 기능을 구현하기 위해 각 컨테이너에 자동적으로 가상 네트워크 인터페이스를 생성하기에 사용자가 별도로 해당 네트워크 인터페이스를 생성할 필요는 없습니다. Docker 호스트에서 컨테이너를 실행한 이후에 ifconfig 명령을 실행시켜 보면 veth....라는 인터페이스가 자동으로 생성된 것을 확인할 수 있습니다.

# start container
sudo docker start tc_01

# check network details
ifconfig

위의 출력결과에서 ens33(또는 eth0)은 공인 IP 또는 내부 IP가 할당되어 외부와 통신할 수 있는 호스트의 네트워크 인터페이스이며, veth...으로 시작하는 인터페이스는 컨테이너가 시작될 때 자동으로 생성된 네트워크 인터페이스로 각 컨테이너의 eth0와 연결되어 있습니다. docker0는 각 veth... 인터페이스와 바인딩되어 호스트의 ens33(또는 eth0) 인터페이스를 이어주는 역할을 하며, 이를 그림으로 도식화해보면 아래와 같습니다.

아래 명령어를 활용하면 docker0 브리지에 veth가 실제로 바인딩되어 있는지 확인하는 것이 가능합니다.

# install packages
sudo apt install bridge-utils

# check bridge
brctl show docker0

 

컨테이너를 생성하면 기본적으로 docker0 브리지를 통해 외부와 통신할 수 있는 환경이 제공되지만 필요에 따라 여러 네트워크 드라이버를 사용하는 것 또한 가능합니다. Docker가 제공하는 대표적인 네트워크 드라이버로는 bridge, host, none, container, overlay가 있으며, third-party 플러그인으로는 weave, flannel, openvswitch 등이 있습니다.

Docker에서는 네트워크 정보를 확인할 수 있게 하기 위해서 docker network ls 명령어를 제공하며, 해당 명령어를 통해 Docker네트워크 정보들을 확인할 수 있습니다. 보다 세부적인 네트워크 정보가 알고 싶다면 inspect 명령어를 활용하여 조회할 수 있습니다.

# check network list
sudo docker network ls

# check network details
sudo docker network inspect bridge

조회 결과를 보게 되면 서브넷과 게이트웨이가 172.17.0.0/16, 172.17.0.1로 설정되어 있고, 현재 해당 브리지 네트워크를 사용 중인 컨테이너 목록을 Containers 항목에서 확인할 수 있습니다. 만약 별다른 설정 없이 컨테이너를 생성하면 컨테이너는 자동으로 docker0 브리지를 사용합니다.

만약 docker0가 아닌 다른 별도의 브리지를 사용하고 싶으면 새로운 브리지 네트워크를 생성한 후 --net 옵션을 사용하면 되며, 아래의 코드를 통해 신규 브리지 네트워크를 생성하고 컨테이너에 할당해 볼 수 있습니다.

# create new bridge network
sudo docker network create --driver bridge mybridge

# check network info
sudo docker network ls
ifconfig

# create container
sudo docker run -i -t --name mynetwork_container \
--net mybridge \
ubuntu:24.04

# check network in container
apt update
apt install net-tools
ifconfig

위의 예시를 보면 새로운 172.18이라는 새로운 IP 대역이 할당된 것을 알 수 있으며, 새로 생성한 브리지를 사용한 컨테이너의 IP 또한 172.18 대역에 속해있음을 알 수 있습니다. 이와 같은 사용자 정의 네트워크는 docker network disconnect, connect 명령어를 통해 컨테이너에 유동적으로 붙이고 뗄 수 있으며, 이는 아래의 코드를 실행해 보면 보다 명확하게 알 수 있습니다.

# disconnect network mybridge
sudo docker network disconnect mybridge mynetwork_container

# connect network bridge
sudo docker network connect bridge mynetwork_container

# start & attach container
sudo docker start mynetwork_container
sudo docker attach mynetwork_container

# check network info in container
ifconfig

단, disconnect, connect 명령어는 논 네트워크, 호스트 네트워크 등과 같은 특별한 네트워크 모드에는 사용할 수 없으며, 브리지 네트워크 또는 오버레이 네트워크와 같이 특정 IP 대역을 갖는 네트워크 모드에만 사용할 수 있다는 점을 유의해야 합니다. 추가적으로 네트워크의 서브넷, 게이트웨이, IP 할당 범위 등을 임의로 설정하려면 --subnet, --ip-range, --gateway 옵션을 추가하면 되고, 이 경우 --subnet과 --ip-range는 같은 대역이어야 합니다.

# create network
sudo docker network create --driver=bridge \
--subnet=172.72.0.0/16 \
--ip-range=172.72.0.0/24 \
--gateway=172.72.0.1 \
my_custom_network

# check network info
sudo docker network ls
ifconfig

호스트 네트워크는 말 그대로 호스트의 네트워크 환경을 그대로 사용하는 것으로 브리지 드라이버 네트워크와 달리 네트워크를 별도로 생성할 필요 없이 기존의 host라는 이름의 네트워크를 사용하면 됩니다. 호스트 머신에서 설정한 호스트 이름도 컨테이너가 물려받기 때문에 컨테이너의 호스트 이름도 무작위 16진수가 아닌 도커 엔진이 설치된 호스트 이름으로 설정됩니다. 만약 컨테이너 네트워크를 호스트 모드로 설정하면 컨테이너의 내부 애플리케이션을 별도의 포트 포워딩 없이 바로 서비스할 수 있습니다. 

# create container
sudo docker run -i -t --name network_host \
--net host \
ubuntu:24.04

# check network info in container
apt update
apt install net-tools
ifconfig

none은 아무런 네트워크를 쓰지 않는 것으로 사실상 외부와 연결이 단절된다고 볼 수 있습니다. 생성한 이후에 필요에 따라 disconnect, connect 명령어를 통해 네트워크를 연결하거나 연결을 끊을 수 있습니다.

# create container
sudo docker run -i -t --name network_none \
--net none \
ubuntu:24.04

만약 사용하지 않고 있는 네트워크를 한꺼번에 삭제하고 싶다면, sudo docker network prune 명령어를 사용할 수 있으며, 해당 명령어 사용 시에는 특정 컨테이너에 할당되어 있지 않은 네트워크가 전부 삭제되므로 주의가 필요합니다.

--net 옵션에 만약 container를 입력하면 다른 컨테이너의 네트워크 네임스페이스 환경을 공유할 수 있으며, 이때 공유되는 속성은 내부 IP, 네트워크 인터페이스의 MAC 주소 등입니다. 적용을 위해서는 컨테이너 생성 시 --net 옵션의 값으로 container:[다른 컨테이너의 ID]와 같이 입력하면 됩니다. 만약 -i, -t, -d 옵션을 함께 사용하면 컨테이너 내부에서 쉘을 실행하지만 내부로는 들어가지 않으며, 컨테이너가 종료되지도 않아 테스트용으로 유용하게 사용할 수 있습니다.

# create container_1
sudo docker run -i -t -d --name network_container_1 ubuntu:24.04

# create container_2
sudo docker run -i -t -d --name network_container_2 \
--net container:network_container_1 \
ubuntu:24.04

# check network info
sudo docker exec network_container_1 apt update
sudo docker exec network_container_1 apt install net-tools
sudo docker exec network_container_1 ifconfig

sudo docker exec network_container_2 apt update
sudo docker exec network_container_2 apt install net-tools
sudo docker exec network_container_2 ifconfig

단, 컨테이너 네트워크를 사용하는 컨테이너(자식 컨테이너)는 삭제해도 기준이 되는 컨테이너(부모 컨테이너)에는 영향이 없으나 그 반대의 경우 네트워크 정보가 아예 사라져 버리니 유의해야 하며, 이를 그림으로 도식화해 보면 아래와 같습니다.

부모 컨테이너를 삭제해서 자식 컨테이너의 네트워크 정보가 사라진 모습

브리지 타입의 네트워크와 run 명령어의 --net-alias를 같이 사용하면 특정 호스트의 이름으로 컨테이너 여러 개에 접근할 수 있습니다. 이는 아래의 명령어로 컨테이너를 만들어보고, ping명령어를 날려보면 확인할 수 있습니다.

# create network
sudo docker network create --driver bridge mybridge

# create container1
sudo docker run -i -t -d --name network_alias_container1 \
--net mybridge \
--net-alias alice106 \
ubuntu:24.04

# create container2
sudo docker run -i -t -d --name network_alias_container2 \
--net mybridge \
--net-alias alice106 \
ubuntu:24.04

# create container3
sudo docker run -i -t -d --name network_alias_container3 \
--net mybridge \
--net-alias alice106 \
ubuntu:24.04

# create container for sending ping 
sudo docker run -i -t --name network_alias_ping \
--net mybridge \
ubuntu:24.04

# install packages and send ping in container network_alias_ping
apt update
apt install iputils-ping
ping -c 1 alice106

ping 명령어 결과를 확인해 보면 생성한 컨테이너 3개의 IP로 ping이 전송된 것을 확인할 수 있으며, ping을 전송받는 IP가 매번 달라지는 것을 알 수 있습니다. 이는 IP를 결정하는 것이 별도의 알고리즘이 아닌 라운드로빈 방식(하나의 중앙처리장치가 여러 프로세스를 우선순위 없이 돌아가며 할당받아 실행되는 방식)이기 때문입니다. 이와 같은 것이 가능한 이유는 도커 엔진에 내장된 DNS가 alice106이라는 호스트 이름을 --net-alias 옵션으로 alice106을 설정한 컨테이너로 변환하기 때문이며, 이를 도식화해보면 아래 그림과 같습니다.

Docker의 DNS는 호스트 이름으로 유동적인 컨테이너를 찾을 때 주로 사용되며, 가장 대표적인 예가 바로 --link 옵션입니다. 해당 옵션은 컨테이너가 재시작될 때마다 NAT IP가 변경되는 속성으로 인해 발생할 수 있는 오류들을 사전에 방지하기 위해 사용되며, 컨테이너의 IP가 변경되어도 별명으로 컨테이너를 찾을 수 있게 DNS에 의해 자동으로 관리됩니다. 단, --link옵션은 디폴트 브리지 네트워크(Docker0)의 네트워크 컨테이너 DNS라는 점을 유의해야 합니다.

--net-alias 옵션도 --link 옵션과 비슷한 원리로 작동하며, 만약 사용자 정의 브리지 네트워크를 사용 중이라고 하면 기본 브리지 네트워크(Docker0)의 DNS가 아닌 별도의 내장 DNS 서버를 가집니다. 위의 경우에서는 mybridge라는 이름의 네트워크에 속하도록 생성한 컨테이너 3개는 mybridge 브리지 네트워크 내장 DNS 서버에 alice106이라는 호스트 이름으로 등록된 것입니다. 

위에서 언급했듯이 mybridge 네트워크에 속한 컨테이너에서 alice106이라는 호스트 이름으로 접근하면 DNS 서버는 라운드로빈 방식으로 컨테이너의 IP 리스트를 반환하는데, ping 명령어는 그 리스트에서 가장 첫 번째 IP만을 사용하기에 매번 다른 IP로 ping을 전송하게 됩니다. 이는 아래의 코드를 통해 보다 명확하게 확인해 볼 수 있습니다.

# install packages and ip list in container network_alias_ping
apt install dnsutils
dig alice106

MacVLAN 네트워크는 호스트의 네트워크 인터페이스 카드를 가상화하여 물리 네트워크 환경을 컨테이너에게 동일하게 제공하는 방법으로 해당 방식을 사용하면 컨테이너는 물리 네트워크상에서 가상의 MAC 주소를 가지고 해당 네트워크와 연결된 다른 장치와의 통신이 가능해집니다. 아래의 시나리오를 통해 이를 보다 명확하게 알아보겠습니다.

 서버는 VMware Workstation Pro와 우분투 이미지를 활용해 구현하였으며, 서버 구축 후 Docker를 설치하고 IP주소를 확인한 다음 각각의 서버에서 아래의 코드를 사용하여 MacVLAN 네트워크를 생성해 주었습니다.

# subnet 255.255.255.0 이므로 /24(11111111.11111111.11111111.0) -> 1이 연속 24개
# gatway 192.168.91.2

# create MacVLAN in Server1(192.168.91.140)
sudo docker network create -d macvlan --subnet=192.168.91.0/24 \
--ip-range=192.168.91.64/28 --gateway=192.168.91.2 \
-o macvlan_mode=bridge -o parent=ens33 mymacvlan

# create MacVLAN in server2(192.168.91.150)
sudo docker network create -d macvlan --subnet=192.168.91.0/24 \
--ip-range=192.168.91.128/28 --gateway=192.168.91.2 \
-o macvlan_mode=bridge -o parent=ens33 mymacvlan

ㆍ-d: 네트워크 드라이버를 지정하는 옵션으로 위에서는 macvlan을 사용한다는 것을 명시함. --driver와 같은 의미.

ㆍ--subnet: 컨테이너가 사용할 네트워크 정보를 입력하는 옵션

ㆍ--ip-range: MacVLAN을 생성하는 호스트에서 사용할 컨테이너의 IP 범위를 입력하는 옵션으로 만약 IP 범위가 겹쳐서 동일한 IP의 컨테이너가 생성되면 컨테이너 네트워크가 정상적으로 작동하지 않을 수 있으므로 겹치지 않게 설정해야 함.

ㆍ--gateway: 네트워크에 설정된 게이트웨이를 입력하는 옵션

ㆍ-o: 네트워크의 추가적인 옵션을 지정하는 옵션으로 위에서는 MacVLAN을 bridge 모드로, parent를 ens33이라는 값을 지정하여 생성될 컨테이너 네트워크의 인터페이스의 부모 인터페이스를 ens33로 지정함.

 

네트워크를 정상적으로 생성해 준 후에는 해당 MacVLAN 네트워크를 사용하는 컨테이너를 각각의 서버에 생성해 주었습니다.

# create container in server1(192.168.91.140)
sudo docker run -it --name c1 --hostname c1 \
--network mymacvlan ubuntu:24.04

# check ip info in container c1
apt update
apt install net-tools iproute2
ip a

# create container in server2(192.168.91.150)
sudo docker run -it --name c2 --hostname c2 \
--network mymacvlan ubuntu:24.04

# check ip info in container c1
apt update
apt install iputils-ping net-tools iproute2
ip a

컨테이너의 생성을 완료한 다음에는 c1에서 server2와 c2로 ping을 전송하여 정상적으로 작동하는지 확인해 주었습니다. 단, MacVLAN을 사용하면 기본적으로 해당 컨테이너가 생성된 호스트 머신과는 통신이 불가능하니 유의해야 합니다.

# ping to server2
ping 192.168.91.150 -c 1

# ping to c2
ping 192.168.91.128 -c 1

728x90
반응형

댓글