การสร้าง openvswitch network ด้วย GRE tunneling สำหรับ Qemu/KVM virtual machines

ใน blog นี้เราจะศึกษาการสร้าง openvswitch network และสร้าง GRE tunnel อ้างอิงวิธีการตามที่ระบุใน blog [1] ของ scott lowe

1. สร้าง host vm’s ด้วย virtualbox
ในที่นี้เราจะสร้าง virtual box (vbox) virtual machines ขึ้นสองเครื่องชื่อ host1 และ host2 เพื่อรัน virtual machines cirros1 cirros2 cirros3 cirros4 บนเครื่องทั้งสองด้วย qemu-kvm software ดังภาพที่ 1
ovs-net-teach1
ภาพที่ 1
เครื่อง host1 และ host2 มีสเปค(ตัวอย่าง)ดังนี้

CPU: 2 cores, RAM: 2.5GB
Disk: 16GB
Networks: 
- Adapter 1 (eth0): vbox's NAT 
- Adapter 2 (eth1): vbox's Host Only Network (192.168.90.0/24)
                    (created using vbox's menu)
- Adapter 3 (eth2): vbox's Internal network "local_net"
                    Set promiscuous mode to "Allow All"
OS: ubuntu-server-amd64 14.04.5 
login: "user" (you can define login name and password to whatever you want).
password: "ubuntu123" 

จาก Spec เราจะใช้ eth0 เป็น default Network Adapter เพื่อติดต่อกับ internet จากเครื่อง host1/2 และเราใช้ eth1 เพื่อ remote login จากเครื่อง PC หรือ notebook ของคุณที่รัน vbox ด้วย putty หรือ ssh (ของ cygwin) เข้าสู่เครือง host นั้น ซึ่งอาจทำให้การติดตั้งสะดวกขึ้นกว่าใช้ vbox console เพราะคุณสามารถ cut and paste ข้อความหรือคำสั่งจาก PC ของคุณได้

เพื่อการอ้างอิงต่อไป ผมจะสมมุติว่าได้สร้างเครื่อง vm host1 และ host2 เรียบร้อยแล้วและมีค่า configuration parameters ดังนี้

บนเครื่อง host* ทั้งสอง ให้กำหนดค่า /etc/network/interfaces ให้ eth0 ได้รับค่า IP address จาก dhcp และให้ eth1 มีค่า IP address แบบ static เป็นค่า IP ในวงเดียวกันกับ 192.168.90.0/24 ที่ไม่ซำ้กับค่า Gateway IP ที่กำหนดไว้ด่อนหน้าเมื่อสร้าง Host Only Network (กด link เพื่อดูตัวอย่างการสร้างและใช้งาน) นี้ขึ้น (ผมสมมุติให้เป็น 192.168.90.21 สำหรับ host1 และ 192.168.90.22 สำหรับ host2) ส่วน eth2 ให้มี IP address เป็นแบบ static และสมมุติให้เครื่อง host1 และ host2 มีค่าเป็น 172.17.0.21 และ 172.17.0.22 ตามลำดับ

แบบฝึกหัด ขอให้ นศ อ่านคู่มือการใช้งาน ubuntu และดูเองว่าการกำหนดค่าเหล่านี้ต้องทำอย่างไร?

หลังจากนั้นให้ทดสอบความถูกต้องโดยใช้ putty หรือ ssh ทำ remote login จากเครื่อง PC เข้าสู่ host1 และเปิดอีก putty terminal หนึ่งเพื่อ remote login เข้าเครือง host2 และเมื่อ login แล้วให้ ping address 172.17.0.x ของอีกเครื่องหนึ่ง และ ssh ข้ามเครื่องผ่าน network 172.17.0.0/24 เพื่อทดสอบว่า internal network ทำงานถูกต้อง

เพื่อความสะดวกในการใช้คำสั่ง คุณอาจเข้าไปกำหนดค่าใน /etc/sudoers ให้คุณสามารถใช้ sudo ได้โดยไม่ต้องใส่ password ทุกครั้ง และเปลี่ยน ubuntu repository จาก us.archive.ubuntu.com ให้เป็น th.archive หรือ archive ของประเทศใกล้ๆ

แบบฝึกหัด ขอให้ นศ อ่านคู่มือการใช้งาน ubuntu และกำหนดค่าเหล่านี้เอง?

2. ติดตั้ง openvswitch และติดตั้ง switch “br1” บนเครื่องทั้งสอง

สมมุติว่าผมมี putty terminal ที่เปืดขึ้นและ login เข้า host1 และ host2 เรียบร้อยแล้ว 2 terminal และสมมุติว่า login name ของผมบนทั้งสองเครื่องคือ “user” ในอันดับถัดไปผมจะติดตั้ง openvswitch และ virtual switch บนเครื่องทั้งสอง

บนเครื่อง host1 :
ติดตั้ง openvswitch

$ sudo apt-get update
$ sudo apt-get install openvswitch-switch

สร้าง bridge br1 และเชื่อมต่อกับ eth2 (นศ อาจดู blog นี้ เพิ่มเติม)

$ sudo ovs-vsctl show
$ sudo ovs-vsctl add-br br1 
$ sudo ovs-vsctl add-port br1 eth2
$ sudo ifconfig eth2 0

ตรวจสอบด้วยคำสั่ง ovs-vsctl show จะได้รางงานประมาณข้างล่างนี้

$ sudo ovs-vsctl show
481e0e12-83d6-4f42-a589-a45cc7494d3c
    Bridge "br1"
        Port "eth2"
            Interface "eth2"
        Port "br1"
            Interface "br1"
                type: internal
    ovs_version: "2.0.2"
$

ถึงจุดนี้ นศ ต้องเข้าไปกำหนดค่าในไฟล์เพื่อบอก kernel เวลาที่จะ activate หรือ deactivate interface ว่าจะกำหนดค่า IP address ให้ interface นั้นอย่างไร ตัวอย่าง /etc/network/interfaces ไฟล์ของเครื่อง host1

...
auto eth0
iface eth0 inet dhcp

auto eth1
iface eth1 inet static 
address 192.168.90.21
netmask 255.255.255.0

auto eth2
iface eth2 inet manual

auto br1
iface br1 inet manual

หลังจากนั้น ให้ activate interface

$ sudo ifdown eth2
$ sudo ifup eth2
$ sudo ifup br1

เราจะสร้าง interface ขึ้นใหม่เรียกว่า Tunnel EndPoint (tep) ให้ชื่อว่า “tep0” ซึ่งจะเป็น interface ที่มี IP address และเราจะกำหนดให้มันเป็นช่องทางสำหรับสร้าง Tunnel (ในระดับ Internet Layer)
บนเครื่อง host1 ให้เพิ่ม interface “tep0” เป็น interface ใหม่ของ br1 และกำหนดค่า IP address ให้กับ tep0

$ sudo ovs-vsctl add-port br1 tep0 -- set interface tep0 type=internal
$ sudo ifconfig tep0 172.17.0.21 netmask 255.255.255.0
$ sudo ifconfig
...
br1       Link encap:Ethernet  HWaddr 08:00:27:b5:c6:4b
          inet6 addr: fe80::1c31:ecff:fe54:4d9f/64 Scope:Link
          UP BROADCAST RUNNING  MTU:1500  Metric:1
          RX packets:8 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1
          RX bytes:536 (536.0 B)  TX bytes:648 (648.0 B)
...
eth2      Link encap:Ethernet  HWaddr 08:00:27:b5:c6:4b
          inet6 addr: fe80::a00:27ff:feb5:c64b/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:24 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:1944 (1.9 KB)
...
tep0      Link encap:Ethernet  HWaddr c6:4d:4e:ae:43:19
          inet addr:172.17.0.21  Bcast:172.17.0.255  Mask:255.255.255.0
          inet6 addr: fe80::c44d:4eff:feae:4319/64 Scope:Link
          UP BROADCAST RUNNING  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1
          RX bytes:0 (0.0 B)  TX bytes:648 (648.0 B)
...
$ sudo ovs-vsctl show
481e0e12-83d6-4f42-a589-a45cc7494d3c
    Bridge "br1"
        Port "eth2"
            Interface "eth2"
        Port "tep0"
            Interface "tep0"
                type: internal
        Port "br1"
            Interface "br1"
                type: internal
    ovs_version: "2.0.2"
$

ให้เพิ่มรายละเอียดการ config ค่าให้ tep0 ใน /etc/network/interface ไฟล์เพื่อให้ host1 กำหนดค่า tep0 เมื่อมีการ reboot

...
auto tep0
iface tep0 inet static
address 172.17.0.21
netmask 255.255.255.0

ให้ทำเช่นเดียวกันกับ host2 และเราจะได้ภาพ network interfaces ของทั้งสอง host ดังภาพที่ 2
ovs-net-teach2
ภาพที่ 2
ให้ทดสอบโดยการ ping ระหว่าง host1 และ host2 และใช้ ssh ข้ามไปมาผ่าน 172.17.0.0/24 network

3. ติดตั้ง openvswitch และติดตั้ง switch “br2” และสร้าง a GRE tunnel เชื่อมต่อระหว่าง br2 ของ host1 และ host2

ในอันดับถัดไปเราจะสร้าง virtual switch อีกอันคือ br2 ซึ่งเราจะใช้เป็น switch สำหรับรองรับการเชื่อมต่อของ virtual machines ภายใน host* แต่ละเครื่อง โดยที่ switch ของทั้งสองเครื่องจะส่งข้อมูลทาง “gre0” interface เข้าออกผ่าน GRE tunnel ที่ถูกสร้างขึ้นบน

tep0 --> br1 --> eth2 --> local_net internal network --> eth2 --> br1 --> tep0   

จะได้การเชื่อมต่อที่เป็นรายละเอียดดังภาพที่ 3
ovs-net-teach3
ภาพที่ 3
ที่สามารถมองจากการเชื่อมระหว่าง br2 ของทั้งสองเครื่องดังภาพที่ 4
ovs-net-teach4
ภาพที่ 4

เราจะใช้คำสั่งต่อไปนี้ สมมุติว่าอยู่บนเครื่อง host1

$ sudo ovs-vsctl add-br br2
$ sudo ovs-vsctl add-port br2 gre0 -- set interface gre0 type=gre \
  options:remote_ip=172.17.0.22
$ sudo ovs-vsctl show
481e0e12-83d6-4f42-a589-a45cc7494d3c
    Bridge "br2"
        Port "br2"
            Interface "br2"
                type: internal
        Port "gre0"
            Interface "gre0"
                type: gre
                options: {remote_ip="172.17.0.22"}
    Bridge "br1"
        Port "eth2"
            Interface "eth2"
        Port "tep0"
            Interface "tep0"
                type: internal
        Port "br1"
            Interface "br1"
                type: internal
    ovs_version: "2.0.2"
$

บนเครื่อง host2

$ sudo ovs-vsctl add-br br2
$ sudo ovs-vsctl add-port br2 gre0 -- set interface gre0 type=gre \
  options:remote_ip=172.17.0.21

4. ติดตั้งและรัน qemu-kvm บน host1 และ host2 และ ovs-ifup/ovs-ifdown script สำหรับเชื่อมต่อ cirros* vm เข้ากับ br2

ในอันดังถัดไปเราจะติดตั้ง qemu-kvm และรันมัน แต่เนื้องจาก virtual box ไม่ support nested hardware virtualization support ดังนั้น การรัน KVM บน virtual box vm (host1 และ host2) จึงทำไม่ได้ เพราะ virtualbox ทำ hardware virtualization support ได้ชั้นเดียว (ถ้าคุรใช้ KVM รัน host1 และ host2 แทนที่จะเป็น virtual box ก็จะสามารถทำ nested KVM ได้)

สิ่งที่คุณจะได้ตอนนี้คือคุณรัน qemu emulator บน virtualbox vm ซึ่งก็โอเค

การติดตั้ง qemu-kvm และ load cirros image ทำดังนี้

$ sudo apt-get install qemu-kvm
$ wget http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-x86_64-disk.img
$ ls
cirros-0.3.4-x86_64-disk.img
$ cp cirros-0.3.4-x86_64-disk.img cirros1.img
$ cp cirros1.img cirros2.img

ตอนนี้มี image cirros1.img และ cirros2.img บนเครื่อง host1 ให้ทำแบบเดียวกันบนเครื่อง host2 และสร้าง images cirros3.img และ cirros4.img

ในอันดับถัดไปเราจะสร้าง scripts เพื่อให้ qemu-kvm เรียกเพื่อสร้าง interface สำหรับ vm แต่ละเครื่องที่รันและเชื่อมต่อ interface นี้กับ bridge br2 เราจะเขียน script ovs-ifup และ ovs-ifdown ที่จะถูกเรียกใช้เมื่อ qemu-kvm สร้าง vm และเมื่อ qemu-kvm จบการทำงาน

$ sudo vi /etc/openvswitch/ovs-ifup
#!/bin/sh
switch='br2'
/sbin/ifconfig $1 0.0.0.0 up
ovs-vsctl add-port ${switch} $1
$
$ sudo chmod +x /etc/openvswitch/ovs-ifup
$
$ sudo vi /etc/openvswitch/ovs-ifdown
#!/bin/sh
switch='br2'
/sbin/ifconfig $1 0.0.0.0 down
ovs-vsctl del-port ${switch} $1
$ 
$ sudo chmod +x /etc/openvswitch/ovs-ifdown
$

เพื่อให้การรัน qemu-kvm ง่ายขึ้น ผมสร้าง script ชื่อ kvmrun1.sh และ kvmrun2.sh ขึ้นเพื่อรัน cirros1 vm และ cirros2 vm

$ vi kvmrun1.sh
sudo qemu-system-x86_64 -smp 2 -m 512M \
  -vnc :1 \
  -drive file=cirros1.img,if=virtio,cache=writeback,index=0 \
  -net nic,macaddr=12:34:52:CC:CC:11,model=virtio \
  -net tap,script=/etc/openvswitch/ovs-ifup,downscript=/etc/openvswitch/ovs-ifdown &
$
$ chmod +x kvmrun1.sh
$
$ vi kvmrun2.sh
sudo qemu-system-x86_64 -smp 2 -m 512M \
  -vnc :2 \
  -drive file=cirros2.img,if=virtio,cache=writeback,index=0 \
  -net nic,macaddr=12:34:52:CC:CC:12,model=virtio \
  -net tap,script=/etc/openvswitch/ovs-ifup,downscript=/etc/openvswitch/ovs-ifdown &
$
$ chmod +x kvmrun2.sh 
$

ขอให้สังเกตุว่า

  • เราใช้ para-virtualization driver เรียกว่า virtio ที่ทำให้ guest OS คือ cirros ในที่นีสามารถเรียกใช้ driver สำหรับอ่านเขียน disk และสื่อสารด้วย network ของ host OS ของ host1 ได้โดยตรง
  • นอกจากนั้นในส่วนของ macaddress ขอให้สังเกตุด้วยว่าผมกำหนดให้แต่ละ cirros vm มี macaddress ไม่เหมือนกัน คือ cirros1 มี macaddress คือ 12:34:52:CC:CC:11 ในขณะที่ของ cirros2 มี macaddress คือ 12:34:52:CC:CC:12
  • สำหรับ vnc server นั้นในที่นี้ cirros1 จะใช้ port 5901 ในขณะที่ cirros2 ใช้ port 5902

ทำคล้ายๆกันบน host2 โดยให้สร้าง cirros3.img และ cirros4.img และ kvmrun3.sh และ kvmrun4.sh script files ขึ้นที่นั่น

แบบฝึกหัดขอให้ นศ เปลี่ยนข้อมูลรายละเอียดใน script เหล่านั้นให้เหมาะสม

5. รัน kvmrun1.sh และ kvmrun2.sh บน host1 และ kvmrun3.sh และ kvmrun4.sh บน host2

สิ่งที่จะทำในอันดับถักไปจะเป็นดังภาพที่ 5 คือสร้าง cirros1 cirros2 cirros3 cirros4 vm ขึ้นดังภาพ
ovs-net-teach5
ภาพที่ 5

เริ่มต้นด้วยการรัน kvmrun1.sh บนเครื่อง host1

$ sudo kvmrun1.sh
$ 

จะเห็นได้ว่า shell กลับมารอรับคำสั่งถัดไป

แบบฝึกหัดเพราะเหตุใด

หลังจากนั้นให้คุณใช้ vnc client เช่น tight VNC viewer บนเครื่อง PC ของคุณเข้าดู console ของ vm cirros1 ดังภาพที่
kvmrun1vnc0
ภาพที่

หลังจากนั้น นศ จะเห็นจอดังภาพข้างล่าง ที่จะค้างอยู่ประมาณ 2 – 3 นาที
kvmrun1vnc1
ภาพที่

เหตุที่ cirros ไม่ขึ้นหน้าจอมาให้ login ซะที ทั้งนี้เดาว่าเป็นเพราะ cirros พยายามเชื่อมต่อ network ซึ่งเป็นแบบ dhcp แต่ network ที่เชื่อมต่อเข้ากับ cirros vm คือ network ที่ถูกสร้างขึ้นระหว่าง br2 ไม่มี dhcp server แต่อย่างใดดังนั้นเราจึงต้องรอให้ cirros timeout และกลับมารับการ login ดังภาพข้างล่าง
kvmrun1vnc2
ภาพที่

หลังจากนั้น ให้ login เข้าสู่ cirros1 vm และเปลี่ยน network configuration ดังนี้

$ sudo vi /etc/network/interfaces
...
auto eth0
iface eth0 inet static
address 10.10.10.111
netmask 255.255.255.0
$
$ sudo ifdown eth0
$ sudo ifup eth0
$ ifconfig

ให้ทำแบบเดียวกันกับ kvmrun2.sh บนเครื่อง host1 และ kvmrun3.sh และ kvmrun4.sh บนเครื่อง host2 กำหนดให้ cirros2 ใช้ IP 10.10.10.112 และ cirros3 ใช้ 10.10.10.113 และ ciros4 ใช้ 10.10.10.114 บน 10.10.10.0/24 network

ให้ใช้ ovs-vsctl show บน host1 และ host2 เพื่อดู ovs network จะได้
บน host1:

481e0e12-83d6-4f42-a589-a45cc7494d3c
    Bridge "br2"
        Port "br2"
            Interface "br2"
                type: internal
        Port "gre0"
            Interface "gre0"
                type: gre
                options: {remote_ip="172.17.0.22"}
        Port "tap1"
            Interface "tap1"
        Port "tap0"
            Interface "tap0"
    Bridge "br1"
        Port "eth2"
            Interface "eth2"
        Port "tep0"
            Interface "tep0"
                type: internal
        Port "br1"
            Interface "br1"
                type: internal
    ovs_version: "2.0.2"
$

บน host2 ก็น่าจะได้คล้ายๆกันกับ host1 ขอให้ลองรันสองคำสั่งนี้บนเครื่อง host ทั้งสอง

$ ifconfig
$ ip link 

ให้ศึกษาสองคำสั่งนี้เพราะเป็นเครื่องมือที่ดี

จากนั้นใน cirros1 ให้ ping หา cirros2 และ cirros2 กลับหา cirros1 และให้ ssh จาก cirros1 ไปยัง cissor2 และ cirros2 กลับมาที่ cirros1

ในอันดับถัดไปให้ทดสอบ ping จาก cirros1 ไป cirros3 บนเครื่อง host2 และ cirros3 กลับมาที่ cirros1 ทำได้หรือไม่

หลังจากนั้นให้พยายาม ssh ระหว่าง cirros1 กับ cirros3 ทำได้หรือไ่ม่
ข้อสังเกตุ: ถ้าเป็น cirros จะ ssh ข้าม tunnel ได้ แต่เมื่อเป็น Guest OS image อื่นๆเช่น ubuntu อาจมีปัญหา ไม่สามารถสร้าง TCP connection ระหว่างกันได้ เมื่อผมลองรันกับ ubuntu ได้พบปัญหาดังกล่าว คุณชยาวัฒน์ ได้แก้ปัญหาโดยเปลี่ยน MTU ของ Guest OS จาก 1500 เป็น 1450 สองฝั่งก็สามารถแก้ปัญหาได้ สาเหตุคาดว่าเป็นเพราะเราต้องเผื่อที่ MTU ให้กับ GRE header ด้วย
บนเครื่อง cirros1 cirros2 cirros3 และ cirros4 ข้างล่าง

$ sudo ifconfig eth0 mtu 1450

6. สรุป

ขอให้ นศ ลองสร้าง vm เหล่านี้และทดสอบการเชื่อมต่อ หลังจากนั้นขอให้ลองใช้คำสั่ง cirros* vm

$ route -n

แบบฝึกหัด ลองพิจารณาดูว่าจะต้องทำอย่างไร ถ้าจะทำให้ vm สามารถติดต่อออกสู่ internet
แบบฝึกหัด สำหรับผู้สนใจทดลองทำเพิ่มเติมด้วยตนเอง

  • นศ อาจลองสร้าง interface ขึ้นมาอันหหนึ่งบน br2 ของ host1 และให้ IP 10.10.10.1
    และดูว่า cirros* สามารถ ping หา IP นี้ได้หรือไม่ และ ssh ได้หรือไม่

  • ถ้ากำหนดให้ 10.10.10.1 เป็น gateway และ eth0 เป็นช่องทางออก internet จะทำให้ host1 เป็น SNAT gateway ได้หรือไม่ หลังจากนั้นให้เพิ่ม default gateway route ใน cirros* ทุกเครื่อง แล้วดูว่าสามารถ ping หรือ wget ออก internet ได้หรือไม่
  • ในกรณีที่จะให้มี host มากว่า 2 hosts จะกำหนดให้ openvswitch br2 บนแต่ละ host ใช้ stp หรือ rstp protocol ด้วย ovs-vsctl ได้หรือไม่

อ้างอิง

[1] http://blog.scottlowe.org/2013/05/07/using-gre-tunnels-with-open-vswitch/

Leave a Reply