Tổng quan
KVM được biết đến là một cơ sở hạ tầng ảo hóa cho nhân Linux. KVM cũng cung cấp các mô hình mạng trong việc ảo hóa network. Các mô hình bao gồm:
- NAT
- Host-only
- Linux-bridge
Trong đó Linux-bridge là một mô hình ảo hóa mạng được hỗ trợ bởi KVM. Linux bridge là một công nghệ cung cấp switch ảo để giải quyết vấn đề ảo hóa Network bên trong các máy vật lý. Ở bài viết này mình sẽ giới thiệu về mô hình Bridge trên KVM.
Chúng ta có thể thấy rằng có một con switch được tạo ra nằm bên trong của máy vật lý. Các VM kết nối đến đây để có thể liên lạc được với nhau. Nếu muốn liên lạc ra bên ngoài ta có thể kết nối con switch này với card mạng trên máy vật lý của ta (giống như ta dùng dây kết nối switch với router). Ta có thể kết nối switch với 1 hoặc nhiều port.
Cấu trúc linux bridge
Trong đó:
- Bridge ở đây là switch ảo
- Tap hay tap interface là giao diện mạng để các VM kết nối với switch do Linux bridge tạo ra(nó hoạt động ở lớp 2 của mô hình OSI)
- fd: Forward data có nhiệm vụ chuyển dữ kiệu từ VM tới switch.
Switch ảo do Linux bridge tạo ra có chức năng tương tự với 1 con switch vật lý.
Ta có thể thấy rõ hơn cách kết nối của VM ra ngoài internet. Khi máy vật lý của ta có card mạng kết nối với internet (không phải card wireless). Trên switch ảo của ta sẽ phải có đường để kết nối ra ngoài internet (cụ thể là kết nối với card mạng của máy vật lý). Ta có thể hình dung card mạng trên máy vật lý sẽ được gắn trực tiếp vào switch ảo nên ta có thể thấy sau khi add switch ảo và card vật lý có cùng địa chỉ MAC. Và trên card vật lý sẽ không còn địa chỉ IP mà nó được gắn cho switch ảo.
$ ifconfig enp6s0
enp6s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
ether 00:30:48:db:6c:5e txqueuelen 1000 (Ethernet)
RX packets 37075255 bytes 30351706205 (30.3 GB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 18350891 bytes 13225504313 (13.2 GB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
device interrupt 16 memory 0xfbce0000-fbd00000
$ ifconfig br0
br0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.13.228 netmask 255.255.254.0 broadcast 192.168.13.255
inet6 fe80::230:48ff:fedb:6c5e prefixlen 64 scopeid 0x20<link>
ether 00:30:48:db:6c:5e txqueuelen 1000 (Ethernet)
RX packets 26611549 bytes 26856341341 (26.8 GB)
RX errors 0 dropped 2 overruns 0 frame 0
TX packets 13609391 bytes 12831819181 (12.8 GB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
$ brctl show
bridge name bridge id STP enabled interfaces
br0 8000.003048db6c5e yes enp6s0
Và bây giờ trên các VM muốn giao tiếp với nhau hoặc ra ngoài internet ta chỉ cần kết nối VM đó với switch ảo. Lúc này card mạng trên VM sẽ được gắn với 1 cổng của switch ảo thông qua tap interface và cổng này có tên là br. Ở đây có tên là br0
Khi ta kết nối vào switch ảo các VM sẽ nhận địa chỉ IP cùng với dải địa chỉ IP của card mà ta add và switch và các địa chỉ IP này sẽ được cấp bởi dịch vụ DHCP trên router.
Địa chỉ của card mạng gắn với switch ảo, với mô hình này gói tin bên trong VM đi ra ngoài mạng sẽ đi từ VM đến thẳng card vật lý gắn với switch ảo và đi ra ngoài mạng.
Chuẩn bị
Một máy chạy hệ điều hành CentOS 7 hoặc Ubuntu cài đặt KVM có một card mạng kết nối với internet và cài đặt một VM bên trong máy đó. Chú ý máy vật lý ở đây là máy cài KVM. Máy này có thể là một máy ảo nhưng ở đây ta coi nó như một server vật lý.
Tạo mô hình Bridge trên KVM
Để list tất cả các network đang có trong server, sử dụng lệnh virsh net-list –all. Bạn sẽ thấy có 1 network default mặc định khi cài KVM sẽ tự tạo ra.
$ virsh net-list --all
Name State Autostart Persistent
-------------------------------------------------------
default inactive no yes
Bây giờ bạn hãy xem thông tin card mạng enp6s0, đây chính là card mạng vật lý đang nối với thiết bị network ở ngoài và nó đang được gán ip là 192.168.13.228.
$ ifconfig enp6s0
enp6s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.13.228 netmask 255.255.254.0 broadcast 192.168.13.255
ether 00:30:48:db:6c:5e txqueuelen 1000 (Ethernet)
RX packets 37098477 bytes 30355784188 (30.3 GB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 18362175 bytes 13228221261 (13.2 GB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
device interrupt 16 memory 0xfbce0000-fbd00000
Bước tiếp theo bạn hãy tạo 1 card mạng bridge có tên là br0 (card này bạn có thể tự đặt tên mà bạn muốn). Card br0 sẽ được đặt ip, gateway, … giống như card vật lý và nó cũng sẽ được gắn với card vật lý enp6s0.
cat > /etc/netplan/00-installer-config.yaml << 'OEF'
network:
version: 2
renderer: networkd
ethernets:
enp6s0:
dhcp4: false
dhcp6: false
bridges:
br0:
interfaces: [ enp6s0 ]
addresses: [ 192.168.13.228/23 ]
gateway4: 192.168.12.5
mtu: 1500
nameservers:
addresses: [ 8.8.8.8, 1.1.1.1 ]
parameters:
stp: true
forward-delay: 4
dhcp4: no
dhcp6: no
OEF
Sau khi chuẩn bị xong file cấu hình, bạn hãy chạy lệnh dưới để áp dụng thay đổi. Máy chủ của bạn sẽ rớt mạng 1 lát để cập nhật cấu hình. Sau khoảng 5-10s nếu mọi cấu hình của bạn đúng, máy chủ của bạn sẽ có kết nối trở lại.
sudo netplan generate
sudo netplan --debug apply
Và đây là kết quả sau khi áp dụng config network mới. Card mạng bridge br0 đã được tạo và được gắn ip 192.168.13.228 như đã khai báo còn card mạng vật lý bây giờ đã không còn được gắn ip.
$ ifconfig enp6s0
enp6s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
ether 00:30:48:db:6c:5e txqueuelen 1000 (Ethernet)
RX packets 37098477 bytes 30355784188 (30.3 GB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 18362175 bytes 13228221261 (13.2 GB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
device interrupt 16 memory 0xfbce0000-fbd00000
$ ifconfig br0
br0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.13.228 netmask 255.255.254.0 broadcast 192.168.13.255
inet6 fe80::230:48ff:fedb:6c5e prefixlen 64 scopeid 0x20<link>
ether 00:30:48:db:6c:5e txqueuelen 1000 (Ethernet)
RX packets 26633879 bytes 26859945910 (26.8 GB)
RX errors 0 dropped 2 overruns 0 frame 0
TX packets 13619670 bytes 12834414371 (12.8 GB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Tiếp theo để thêm network mới, bạn sử dụng file xml với nội dung như dưới, để ý một số trường như sau:
- mode=bridge: sử dụng mode bridge.
- bridge name=’br0′: dùng card mạng bridge bạn đã tạo ở bước trên gắn vào đây.
- bridged-network chính là tên của network bạn sẽ tạo.
cat > ./bridged-network.xml << 'OEF'
<network>
<name>bridged-network</name>
<forward mode="bridge" />
<bridge name="br0" />
</network>
OEF
Sử dụng lệnh virsh net-define <file_network_xml> để define network này.
$ virsh net-define ./bridged-network.xml
Network bridged-network defined from ./bridged-network.xml
Dùng lệnh virsh net-list –all để list network, bạn đã thấy network có tên bridged-network đã xuất hiện nhưng nó đang ở trạng thái inactive. Ở trạng thái này network chưa thể sử dụng được.
$ virsh net-list --all
Name State Autostart Persistent
-------------------------------------------------------
bridged-network inactive no yes
Để enable network này, sử dụng lệnh virsh net-start <file_network_xml> để start nó.
$ virsh net-start bridged-network
Network bridged-network started
Sau khi start network xong, bạn thấy network đã chuyển qua trạng thái active, tức là lúc này network đã có thể sử dụng được nhưng mà tuỳ chọn Autostart đang ở trạng thái no, bạn hãy chuyển tuỳ chọn Autostart sang yes để khi máy chủ KVM khởi động lại, network sẽ tự start luôn.
$ virsh net-list --all
Name State Autostart Persistent
-------------------------------------------------------
bridged-network active no yes
Sử dụng lệnh virsh net-autostart <file_network_xml> để enable tính năng này.
$ virsh net-autostart bridged-network
Network bridged-network marked as autostarted
Và đây là kết quả khi mình đã config xong một network.
$ virsh net-list --all
Name State Autostart Persistent
-------------------------------------------------------
bridged-network active yes yes
Để xem thông tin tóm tắt một network bạn có thể sử dụng lệnh virsh net-info <file_network_xml>.
$ virsh net-info bridged-network
Name: bridged-network
UUID: de6d9dd4-1700-493c-a2c2-5c2838e425d2
Active: yes
Persistent: yes
Autostart: yes
Bridge: br0
Kiểm tra.
Mình sẽ tạo 1 máy ảo có tên test-bridged-network để kiểm tra network này như sau. Đầu tiên tạo file sshkey cho username ubuntu.
sudo mkdir -p /kvm-volumes-hdd/test-bridged-network/ssh-keygen
sudo chmod +x /kvm-volumes-hdd/test-bridged-network
sudo chmod 600 /kvm-volumes-hdd/test-bridged-network/ssh-keygen
cd /kvm-volumes-hdd/test-bridged-network/ssh-keygen
ssh-keygen -t rsa -b 4096 -f id_rsa -C hoanghd -N "" -q
Tạo 1 ổ đĩa chứ hệ điều hành, mình tạo 1 ổ đĩa có dung lượng 10G.
qemu-img create -b /kvm-volumes-hdd/images/bionic-server-ubuntu1804-cloudimg-amd64.img -f qcow2 /kvm-volumes-hdd/test-bridged-network/os-test-bridged-network.qcow2 10G
Xem lại thông tin ổ đĩa vừa tạo.
$ qemu-img info /kvm-volumes-hdd/test-bridged-network/os-test-bridged-network.qcow2
image: /kvm-volumes-hdd/test-bridged-network/os-test-bridged-network.qcow2
file format: qcow2
virtual size: 10 GiB (10737418240 bytes)
disk size: 196 KiB
cluster_size: 65536
backing file: /kvm-volumes-hdd/images/bionic-server-ubuntu1804-cloudimg-amd64.img
Format specific information:
compat: 1.1
lazy refcounts: false
refcount bits: 16
corrupt: false
Tiếp theo bạn tạo file cloud_init.cfg khai báo 1 số thông tin cơ bản cho máy ảo.
cat > /kvm-volumes-hdd/test-bridged-network/cloud_init.cfg << OEF
#cloud-config
hostname: test-bridged-network
fqdn: test-bridged-network.hoanghd.com
manage_etc_hosts: true
users:
- name: ubuntu
sudo: ALL=(ALL) NOPASSWD:ALL
groups: users, admin
home: /home/ubuntu
shell: /bin/bash
lock_passwd: true
ssh-authorized-keys:
- $(cat /kvm-volumes-hdd/test-bridged-network/ssh-keygen/id_rsa.pub)
- name: hoanghd
sudo: ALL=(ALL) NOPASSWD:ALL
groups: users, admin
home: /home/hoanghd
shell: /bin/bash
lock_passwd: false
package_update: true
packages_upgrade: true
packages:
- qemu-guest-agent
- traceroute
# only cert auth via ssh (console access can still login)
ssh_pwauth: false
disable_root: false
chpasswd:
list: |
hoanghd:Hoanghd164
expire: False
runcmd:
- sudo touch /etc/cloud/cloud-init.disabled
OEF
Tạo file cấu hình network.
cat > /kvm-volumes-hdd/test-bridged-network/network_config_static.cfg << 'OEF'
version: 2
ethernets:
enp1s0:
dhcp4: false
addresses: [ 192.168.13.231/23 ]
gateway4: 192.168.12.5
nameservers:
addresses: [ 1.1.1.1, 8.8.8.8 ]
OEF
Giờ mình tạo 1 disk mới có tên test-bridged-network-seed.qcow2 và mình mount 2 file config cloud_init.cfg và network_config_static.cfg vào disk này. Mục đích để lúc khởi động kvm sẽ đọc ổ đĩa này để load các file config.
cloud-localds -v --network-config=/kvm-volumes-hdd/test-bridged-network/network_config_static.cfg /kvm-volumes-hdd/test-bridged-network/test-bridged-network-seed.qcow2 /kvm-volumes-hdd/test-bridged-network/cloud_init.cfg
Bạn có thể xem lại thông tin disk này bằng lệnh dưới.
$ qemu-img info /kvm-volumes-hdd/test-bridged-network/test-bridged-network-seed.qcow2
image: /kvm-volumes-hdd/test-bridged-network/test-bridged-network-seed.qcow2
file format: raw
virtual size: 368 KiB (376832 bytes)
disk size: 368 KiB
Bây giờ hãy run máy ảo bằng lệnh dưới.
virt-install --name test-bridged-network \
--virt-type kvm --memory 4096 --vcpus 4 \
--boot hd,menu=on \
--disk path=/kvm-volumes-hdd/test-bridged-network/test-bridged-network-seed.qcow2,device=cdrom \
--disk path=/kvm-volumes-hdd/test-bridged-network/os-test-bridged-network.qcow2,device=disk \
--graphics vnc \
--os-type Linux --os-variant ubuntu18.04 \
--network network:bridged-network \
--console pty,target_type=serial \
--noautoconsole
Nếu không gặp lỗi, bạn sẽ thấy đầu ra xuất hiện như dưới.
Starting install...
Domain creation completed.
Giờ hãy list VM chúng ta thấy VM vừa tạo đã ở trạng thái running
$ virsh list --all
Id Name State
-----------------------------------------
6 test-bridged-network running
Và đây là kết quả.
$ virsh console test-bridged-network
Connected to domain test-bridged-network
Escape character is ^]
Ubuntu 18.04.6 LTS test-bridged-network ttyS0
test-bridged-network login: hoanghd
Password:
Last login: Sat Dec 10 14:10:53 UTC 2022 on ttyS0
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 4.15.0-197-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Sat Dec 10 14:11:49 UTC 2022
System load: 0.26 Processes: 122
Usage of /: 12.0% of 9.51GB Users logged in: 0
Memory usage: 3% IP address for enp1s0: 192.168.13.231
Swap usage: 0%
0 updates can be applied immediately.
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
hoanghd@test-bridged-network:~$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp1s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 52:54:00:35:ad:8e brd ff:ff:ff:ff:ff:ff
inet 192.168.13.231/23 brd 192.168.13.255 scope global enp1s0
valid_lft forever preferred_lft forever
inet6 fe80::5054:ff:fe35:ad8e/64 scope link
valid_lft forever preferred_lft forever
hoanghd@test-bridged-network:~$ ip route
default via 192.168.12.5 dev enp1s0 proto static
192.168.12.0/23 dev enp1s0 proto kernel scope link src 192.168.13.231
hoanghd@test-bridged-network:~$ ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=57 time=30.5 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=57 time=30.4 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=57 time=31.1 ms
64 bytes from 8.8.8.8: icmp_seq=4 ttl=57 time=30.9 ms
--- 8.8.8.8 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 30.492/30.786/31.175/0.353 ms