이수의 삽질 기록
article thumbnail

시리즈

- [KlosedStack #1] Intro: https://blog.isu.kim/14

- [KlosedStack #2] Vagrant + Libvirt: https://blog.isu.kim/15

배경

지난 시간에는, KlosedStack에서 사용할 VM을 Vagrant로 Libvirt를 사용해 생성했다. 이제는 각 VM끼리 연결을 해줘야한다. 연결에는 OpenvSwitch를 사용할 예정이다.

토폴로지

살짝 복잡해보이지만 어렵지 않다. 간단하게 생각을 하면 다음의 구조와 같다.

  1. 각 VM의 경우 내부 eth1번에 호스트 머신의 macvtap 인터페이스를 붙인다. 또한 VM 내부의 eth1에 IP를 10.0.35.0/24 대역으로 준다. 호스트 A의 VM은 10.0.35.211, 호스트 B의 VM은 10.0.35.212를 받게 설정한다.
  2. macvtap 인터페이스는 veth를 사용하여 브릿지랑 연결해준다. 브릿지 쌍은 veth1a 그리고 veth1b로 설정했다. veth1amacvtap에, 그리고 veth1b는 브릿지인 lb1에 붙게 설정한다.
  3. 이후 브릿지 lb1을 OVS 브릿지인 br-private에 붙여준다. br-private에는 10.0.35.11 또는 10.0.35.12로 IP를 설정한다. 이후 두개의 브릿지를 VXLAN으로 연결한다. 연결의 경우 각 eth1번으로 연결을 해준다. 이렇게 하는 이유는, eth0의 경우 외부 인터넷 접속이 있지만, 사실 eth1의 경우 그냥 스위치에 물려있고 인터넷 접속이 없는 그냥 인터페이스이기 때문이다.

이렇게 설정하면, 각 VM끼리, 그리고 br-private 에서 VM끼리, 또한 반대로도 모든 통신이 가능하다.

나는 네트워크 전문가가 아니다... 이 셋업을 만들어낼라고 엄청난 삽질을 했다. N 교수님이 이 토폴로지를 보시면 경악을 금치 못하실 수도 있다.

전제 + 주의사항

이 게시글은 OVS를 친절하게 바닥부터 알려주려고 만든 글은 아니다... 그래서 ovs-vsctl와 같은 명령어, ip, ifconfig, firewall-cmd와 같은 명령어는 그냥 알고 있다고 전제하고 시작할 예정이다.

또한 br-privateeth0을 설정하는 방안은 너무 인터넷에 잘 알려져있으니 다루지 않을 예정이다.

뿐만 아니라, 이 게시글은 reboot를 살아남지 못한다. reboot시 대부분의 인터페이스는 날라갈 것이다. 이것에 대한 얘기는 추후 다시 다룰 예정이다.

br-private 설정

br-private를 만들어주고 VXLAN을 연결해주자

sudo ovs-vsctl add-br br-private
sudo ifconfig br-private up

여기까지 하면, br-private 인터페이스가 올라올 것이다. 이후 각 br-private을 VXLAN으로 연결해준다.

호스트 1 (10.0.30.111)

sudo ovs-vsctl add-port br-private vxlan0 -- set interface vxlan0 type=vxlan options:remote_ip=10.0.30.112 options:df_default=true options:egress_pkt_mark=0 options:in_key=flow options:out_key=flow options:dst_port=48317 options:tag=123

호스트 2 (10.0.30.112)

sudo ovs-vsctl add-port br-private vxlan0 -- set interface vxlan0 type=vxlan options:remote_ip=10.0.30.111 options:df_default=true options:egress_pkt_mark=0 options:in_key=flow options:out_key=flow options:dst_port=48317 options:tag=123

여기서 주의할 점이 하나 있다. 끝 없는 삽질로 알아낸 사실인데,

You see unexplained packet drops on VXLAN networks using VXLAN port 4789.

ESXi에서 VXLAN을 4789번 UDP 포트로 사용하면 패킷이 드랍 날 수 있다. 자세한 내용은 다음의 게시글을 참고하면 된다. https://kb.vmware.com/s/article/52936

 

VXLAN packet drops when using nested VXLAN encapsulation (52936)

You see unexplained packet drops on VXLAN networks using VXLAN port 4789.Some packets on a given datapath will successfully reach their destination whereas othe

kb.vmware.com

따라서, 나의 경우, ESXi를 사용중이므로 4789번 포트를 사용하면 안되기 때문에, 48317번 UDP를 사용했다. 이를 위한 방화벽을 내려줘야한다. 또한 10.0.0.0/8을 허용하자. 이후 reload

sudo firewall-cmd --permanent --add-port=4789/udp --add-port=8472/udp --add-port=48317/udp
sudo firewall-cmd --permanent --zone=trusted --add-source=10.0.0.0/8
sudo firewall-cmd --reload

이제 테스트를 위해, 각 br-private에다가 각각 10.0.35.11 그리고 10.0.35.12를 할당하자.

sudo ifconfig br-private 10.0.35.11 up
sudo ifconfig br-private 10.0.35.12 up

이후 라우팅과 ip forward 옵션을 켜주자.

sudo sysctl net.ipv4.ip_forward=1
sudo ip route add 10.0.35.0/24 dev br-private

 

이제 10.0.35.11에서 10.0.35.12를 ping 해보자

잘된다! VXLAN을 통해서 패킷이 잘 날라다니는 것을 확인할 수 있다. 조금 더 궁금하다면, tcpdump로 UDP 48317번이 어떻게 통신하는지를 보면 된다. 10.0.30.112에서 eth1번을 확인하면 된다.

sudo tcpdump -vv -i eth1 udp port 48317

결과로 잘 나오는 것을 확인할 수 있다.

 

veth, 브릿지 설정

사실 이실직고를 하자면, veth 말고 그냥 브릿지를 바로 가져다가 Vagrant에 붙여도 된다. 하지만 나는 지금 Neutron을 참고해가면서 설정을 하고 있다. 

https://docs.openstack.org/ocata/networking-guide/deploy-ovs-selfservice.html

따라서, VM의 eth1번과 호스트의 macvtap 인터페이스가 연결되면, macvtap 인터페이스에 veth를 붙여서 이를 브릿지로 연결하려고 한다. 이는 다음처럼 하면 된다.

sudo ip link add veth1a type veth peer name veth1b
sudo ip link add lb1 type bridge
sudo ip link set veth1b master lb1
sudo ovs-vsctl add-port br-private lb1

간단하게, veth pair를 만들고, 브릿지를 만든 다음 OVS의 br-private 브릿지에 붙여준다. 이후 이 두개의 인터페이스를 up 상태로 만들어주자

sudo ifconfig lb1 up
sudo ifconfig veth1b up
사실 나는 네트워크 지식이 거의 없다. 사실 이전에는 브릿지 자체에다가 IP를 줘야하는줄 알았는데, 그게 아니였다. 혹시라도 "어? 브릿지에 IP를 줘야지 통신이 되지 않을까?" 라는 의문을 가진 사람이 있으면 도움이 되었으면 좋겠다.

이렇게 하면 우선 어느정도 설정이 되었다. 지금 veth1a 쪽은 Vagrant에 연결할 준비가 완료되었다!

 

Vagrant 설정

Vagrant의 Vagrantfile은 다음처럼 작성하면 된다.

Vagrant.configure("2") do |config|
  config.vm.box = "generic/ubuntu2204"  # Ubuntu 22.04
  config.vm.box_check_update = false
  config.vm.network :public_network, :bridge => 'veth1a', :dev => 'veth1a', ip: "10.0.35.211"

  config.vm.provider :libvirt do |libvirt|
    # Disable KVM and use pure QEMU
    libvirt.driver = "qemu"
    libvirt.memory = "2048"  # Set the VM memory to 2GB
    libvirt.cpus = 2  # Set the number of CPUs to 2
  end

  config.vm.provision "shell", inline: <<-SHELL
    # Update the system packages
    sudo apt-get update

    # Additional provisioning steps can be added here
  SHELL
end

이 중, public_network와 private_network의 차이점은 간단하게 생각하면 다음과 같다.

- private_network: Vagrant가 virbr1 또는 virbr0으로 자동으로 할당해주는 192.168.121.0/24 대역의 IP이다. 이는 그냥 자동으로 할당된다. VM의 eth0으로 할당된다.

public_network: 우리의 경우, veth가 여기로 들어가면 된다. VM의 eth1로 할당된다.

 

이렇게 설정하면, 호스트 머신의 virbr0 또는 virbr1인터페이스가 192.168.121.0/24 대역을 eth0으로 NAT 해준다. 즉, VM이 인터넷 접속을 private_network로 할 수 있게 된다. 이름은 private_network 이지만 뭔가 이상할 지 몰라도 인터넷 접속이 가능해진다 ㅋㅋ....

 

그리고 public_network를 통해 10.0.35.211을 eth1로 할당해서 br-private 터널 사이에 존재하는 모든 IP와 통신이 가능해진다. 

 

자세한 사항들은

https://github.com/vagrant-libvirt/vagrant-libvirt/issues/349

 

Call to virDomainCreateWithFlags failed: Unable to get index for interface eth0: No such device · Issue #349 · vagrant-libvirt

Hi guys. Just faced the problem with spinning up new VM. I want to provision new vm with single private net. But the problem is if i specify ip for host it fails. vagrant up mn101 Bringing machine ...

github.com

여기서 

  config.vm.network :public_network, :bridge => 'veth1a', :dev => 'veth1a', ip: "10.0.35.211"

이렇게 쓰는 방법을 알려줄 것이고

https://developer.hashicorp.com/vagrant/docs/networking

 

Networking | Vagrant | HashiCorp Developer

In order to access the Vagrant environment created, Vagrant exposes some high-level networking options for things such as forwarded ports, connecting to a public network, or creating a private network.

developer.hashicorp.com

여기에서는 private_network 그리고 public_network에 대한 자세한 설명이 적혀있으니, 참고하면 좋을 것이다.

 

이후

vagrant up

을 해주면, Vagrant가 알아서 VM을 띄워줄 것이다.

이러면 잘 뜬 것이다.

    default: SSH address: 192.168.121.55:22
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Connection refused. Retrying...
    default: Warning: Connection refused. Retrying...
    default: Warning: Connection refused. Retrying...
    default: Warning: Connection refused. Retrying...
    default: Warning: Connection refused. Retrying...​

중간에 이런 문구가 뜰 수 있을텐데, 이는 무시하면 된다. 사실 정확히는 원인은 못찾았으나, VM이 생성중이라 SSH 키를 넣지 못해서 생겼다고 추측중이다... 아닐수도 있다.

이후 

vagrant ssh

를 통해서 VM 안으로 들어가보자.

- 위의 터미널이 호스트 2번 (10.0.30.112) 의 VM 이고, 얘는 정상적으로 10.0.35.212를 받았다.

- 아래의 터미널이 호스트 1번 (10.0.30.111) 의 VM 이고, 얘도 정상적으로 10.0.35.211을 받았다.

얘도 지금 터널을 통해 10.0.35.212에서 10.0.35.211로 ICMP가 잘 날라감을 확인할 수 있다. 

 

검증

이게 ping만 찍히는게 아닌지, 아니면 정말로 인터넷이 서로 되는지를 확인하고자, 간단하게 TCP 통신으로 확인해보자. 한쪽 VM에서 1000MB짜리 랜덤 파일을 만들고, scp로 넘겨줄 것이다

 

VM 1번에서 (10.0.35.211)

dd if=/dev/urandom of=random_file.txt bs=1M count=1000
scp random_file.bin isu@10.0.35.212:/home/isu

 

 

잘 날라간다, 대략 15.9MB/s 니까, 100mbps 정도는 나오는 것 같다.

 

결론 + 다음 얘기

이번 게시글을 통해, 다른 host (node)에서 동작하는 VM들이 OVS의 VXLAN을 통해 통신할 수 있음을 확인했다. 나는 네트워크 전문가가 아니기에, 이번 게시글에는 잘못된 내용이 있을 수 있다. 따라서 적당히 걸러서 듣는것을 추천한다.

 

현재, Vagrant가 생성하는 VM이 qemu를 통해 동작한다. VNC를 사용할 수 있지만, 지금 이걸 설정을 잘못해놓아서 외부에서 확인할 수 없다. 이를 다루는 얘기를 하려고 한다. 

 

또한 이렇게 생성된 Vagrant의 경우 Block storage가 해당 node에 존재한다. 이렇게 되면, 만약 node에 문제가 생기거나, VM을 다른 node로 스케쥴링 해야할 경우 데이터가 모두 날아간다... 따라서 이를 극복하기 위해 Ceph를 구성하여 block storage에 마운트 하여 Vagrant를 사용할 예정이다.

 

+ 본격적인 구현 관련된 얘기는 좀더 아키텍쳐가 정해지면 해보려고 한다.

'Playground > KlosedStack' 카테고리의 다른 글

[KlosedStack #2] Vagrant + Libvirt  (0) 2023.06.30
[KlosedStack #1] Intro  (0) 2023.06.30
profile

이수의 삽질 기록

@IsuKim

두분의 주인님 밑에서 네트워크, 클라우드, 서버 삽질을 하는 학부생입니다