[리눅스 네트워크] 1개의 NIC에서 Veth를 이용하여 멀티 IP 구성하기 (Update. 2020.04.30)

blog post

Veth peer 구성부터 활용까지

 

집에서 오픈스택 기반 Private 클라우드를 구축하던 도중 호스트 네트워크 구성 단계에서 문제가 발생했습니다.


가상화가 필요 없는 Cinder, Controller, Network 노드는 어차피 Vmware로 돌릴 계획이므로 가상 NIC를 2개씩 (각각 관리용, 통신용 NIC)구성하였으나, Compute Node가 문제였습니다. 현재 Compute Node의 경우, 가상화를 지원하는 Intel NUC Kit으로 구축하였는데 문제는 물리 NIC가 한 개라는 점입니다.

이럴때 흔히 다음과 같이 멀티 IP를 사용하여 구성하곤 합니다: 
eno0:0 - 192.168.0.2/24 --> gw - 192.168.0.1
eno0:1 - 192.168.0.3/24
(eno0: 현재 리눅스 기반 Compute 노드에 잡힌 NIC)
 
물론 이런 경우에는 ip rule과 route를 사용하여 외부로 나가는 인터페이스가 eth0:0임을 명시적으로 정해줘야 합니다. 안그러면 먹통될 것입니다.

하지만 저는 이 방법을 안쓰고 하나의 물리 인터페이스를 통해 두 네트워크 대역의 IP를 구성하되, 다음과 같이 멀티 IP방식을 쓰지 않고 구성해볼 계획입니다:
eno0 - 192.168.0.2/24 --> gw - 192.168.0.1
         - 10.0.0.2/24
 
물론 이렇게 두 개의 네트워크가 필요한 이유는 오픈스택 Host 네트워크에서 192.168.0.0/24 대역은 관리용 인터페이스로, 10.0.0.0/24 대역은 노드별 서비스간 통신을 위한 용도 입니다. 특히 10.0.0.0/24 대역은 게이트웨이도 필요 없습니다. 외부 통신용이 아니기 때문입니다.
 
구성하는 방식에는 여러가지가 있겠지만 이번 포스팅에서는 veth를 사용하여 구성해 보도록 하겠습니다.
따라서 우선 veth에 대해 알아야 합니다.


vethVirtual Ethernet Devices의 약자로, 한 쌍의 인터페이스가 Peer로 서로 연결된 가상 인터페이스입니다. 가상의 인터페이스이기 때문에 물리 NIC와 달리 임의로 생성 가능하고, 생성시 반드시 하나의 peer 인터페이스를 함께 생성해야 합니다. 생성 방법은 다음과 같습니다.

# 포맷: ip link add [name] type veth peer name [peer-name]  
# 예시: veth0 과 veth1을 생성하고 link 상태를 up.

[root@Compute0 ~]# ip link add veth0 type veth peer name veth1
[root@Compute0 ~]# ip link set up veth0
[root@Compute0 ~]# ip link set up veth1
[root@Compute0 ~]# ifconfig
[root@Compute0 ~]# ifconfig veth1 && ifconfig veth0
veth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
        inet 10.0.0.25  netmask 255.255.255.0  broadcast 0.0.0.0
        inet6 xxxx::xxxx:xxxx:xxxx:xxxx  prefixlen 64  scopeid 0x20
        ether xx:xx:xx:xx:xx:xx  txqueuelen 1000  (Ethernet)
        RX packets 424719  bytes 427581571 (407.7 MiB)
        RX errors 0  dropped 1  overruns 0  frame 0
        TX packets 388158  bytes 150649891 (143.6 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

veth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 xxxx::xxxx:xxxx:xxxx:xxxx  prefixlen 64  scopeid 0x20
        ether xx:xx:xx:xx:xx:xx  txqueuelen 1000  (Ethernet)
        RX packets 388160  bytes 150650023 (143.6 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 424721  bytes 427581719 (407.7 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

 

veth는 특히 리눅스 시스템에서 분리된 Network Stack을 터널링 할 때 유용합니다. 오픈스택이나 도커 등에서 많이 사용되는데, 이에 관해서 다룰 기회가 있으면 추후에 다루도록 하겠습니다.

 

 


이제 방법은 간단합니다. 리눅스 시스템에 브릿지를 하나 생성해주고 기존 물리 포트인 eno0와 veth0를 bridge로 묶어 주기만 하면 됩니다. 이때 주의할 점은 현재 ssh로 eno0 포트에 연결하여 작업중인 경우, 콘솔 작업을 대비해야 합니다. 왜냐하면 bridge인 br0와 eno0가 묶임으로써, ip 할당은 이제 eno0가 아닌 br0에 할당되기 때문에 eno0로의 직접 연결은 끊기기 때문입니다.

# 포맷: ip link add  type veth peer name 
# 예시: veth0 과 veth1을 생성하고 link 상태를 up.

[root@Compute0 ~]# brctl addbr br0
[root@Compute0 ~]# brctl addif br0 eno0
[root@Compute0 ~]# brctl addif br0 veth0
[root@Compute0 ~]# ip link set up br0
[root@Compute0 ~]# brctl show br0
bridge name	bridge id		STP enabled	interfaces
br0		8000.1c697a096271	no		eno1
							veth0
[root@Compute0 ~]# ip addr add 10.0.0.25/24 dev veth1
[root@Compute0 ~]# ip addr add 192.168.0.25/24 dev br0

이제 다시 관리용 인터페이스인 192.168.0.25에 ssh로 접속하여 작업할 수 있습니다. 그리고 veth1을 통해 같은 10.0.0.0/24 대역의 다른 ip와 통신할 수 있습니다.

 
[root@Compute0 ~]# ping 10.0.0.26
PING 10.0.0.26 (10.0.0.26) 56(84) bytes of data.
64 bytes from 10.0.0.26: icmp_seq=1 ttl=64 time=0.627 ms
64 bytes from 10.0.0.26: icmp_seq=2 ttl=64 time=0.753 ms
64 bytes from 10.0.0.26: icmp_seq=3 ttl=64 time=0.847 ms

원리는 간단합니다.

아래 그림과 같이, 우선 현재 veth0는 l2 브릿지로 eno0와 묶였기 때문에 실제 물리 포트를 공유할 수 있는 상태가 되었습니다. 여기에 veth0와 veth1이 peer로 서로 터널링 구성된 상태에서 veth1이 외부에서 ip를 받은 상태이기 때문에, 10.0.0.0/24 대역의 ip는 자연스럽게 포트를 타고 유입되는 구조입니다. 결과적으로 bridge와 veth 피어링 방식을 활용하면 굳이 ip rule, route로 명시적 경로를 지정하지 않아도 1개의 NIC를 활용하여 다수의 네트워크를 구성할 수 있습니다. 

 

 

물론 veth를 활용한 방법만 있는 것은 아니고 macvtap 등 다른 가상 인터페이스를 활용한 구성도 가능합니다.

 

마지막 구성단계가 한 가지 더 남았습니다. ip 명령을 통해 생성한 veth 인터페이스 타입은 기존 물리 nic나 linuxbridge와 같이 /etc/sysconfig/network-script/ (RHEL계열 기준)에서 ifcfg-* 방식으로 스크립팅이 안됩니다. 따라서 재부팅 상황을 대비하여 부팅단계에서 브릿지와 veth 인터페이스를 생성하고 브릿지에 바인딩하기 위한 스크립트를 별도로 만들어줘야 합니다. 저의 경우, 아래와 같이 /var/tmp/ 에 쉘 스크립트를 하나 작성하여 재부팅 할 때도 자동으로 인터페이스를 세팅하도록 구성했습니다.

 

* 수정사항: 기존에는 재부팅 상황에 대비하여 브릿지용 /etc/sysconfig/network-scripts/ifcfg-br0 스크립트를 통해 브릿지 인터페이스(br0)를 사전에 생성하는 것을 전제로 작성하였습니다. 하지만 네트워크 스크립트를 통해 브릿지를 생성하면, 이후 #brctl addif br0 veth0 명령이 먹히지 않음을 확인하였습니다. 따라서 아래 커맨드 리스트와 같이, 브릿지 생성부터 dns 설정까지 create_interface.sh 스크립트에서 전부 실행하도록 개선하였습니다.

 

[root@Compute0 ~]# vim /var/tmp/create_interface.sh
#!/bin/bash
ip link add veth0 type veth peer name veth1
ip link set up veth0
ip link set up veth1
brctl addbr br0
ip link set up br0
ip addr add 192.168.0.25/24 dev br0
route add default gw 192.168.0.1 dev br0
echo "nameserver 8.8.8.8" > /etc/resolv.conf
brctl addif br0 veth0
ip addr add 10.0.0.25/24 dev veth1



 

 

TAGS.

Comments