macvtap使用教程

sealyun發表於2019-07-31

kubernetes一鍵安裝

macvtap是虛擬機器網路虛擬化常用的一種技術,當然容器也可以用. MACVTAP 的實現基於傳統的 MACVLAN. 和 TAP 裝置一樣,每一個 MACVTAP 裝置擁有一個對應的 Linux 字元裝置,並擁有和 TAP 裝置一樣的 IOCTL 介面,因此能直接被 KVM/Qemu使用,方便地完成網路資料交換工作. 引入 MACVTAP 裝置的目標是:簡化虛擬化環境中的交換網路,代替傳統的 Linux TAP 裝置加 Bridge 裝置組合,同時支援新的虛擬化網路技術,如 802.1 Qbg.

如kata的虛擬化網路就用了這個技術,以下實踐完就會對kata的網路原理比較清楚了,建議對照教程動手實踐.

實驗環境介紹

macvtap使用教程
此圖非常重要,讀整篇文章最好腦海裡都有

初始化環境

qemu libvirt環境

我已經做好了qemu libvirt的映象,大家可以直接使用:
在容器中有非常多的好處,環境如果亂了可以快速恢復乾淨的環境。
使用裝置對也可減少對宿主機網路的影響。

docker run -d --privileged -v /dev:/dev -v /home/fanux:/root --name qemu-vm fanux/libvirt:latest init

注意:

  1. 網路等操作需要容器有特權模式
  2. tap網路需要掛載/dev目錄
  3. /home/fanux可以作為工作目錄,映象自己編寫的libvirt配置等放在裡面防止刪除容器後丟失
  4. 由於libvirt需要systemd所以我們在容器中啟動init程式

也可自己構建映象,我提供了一個Dockerfile, -j引數根據你機器CPU來設定編譯時的執行緒數:

FROM centos:7.6.1810
RUN yum install -y wget && wget https://download.qemu.org/qemu-4.0.0.tar.xz && \
    tar xvJf qemu-4.0.0.tar.xz  \
    && yum install -y automake gcc-c++ gcc make autoconf libtool gtk2-devel \
    && cd qemu-4.0.0 \
    && ./configure \
    && make -j 72 && make install \
    && yum install -y bridge-utils && yum install -y net-tools tunctl iproute && yum -y install openssh-clients \
    cd .. && rm qemu-4.0.0.tar.xz && rm -rf qemu-4.0.0
RUN yum install -y libvirt virt-manager gustfish openssh-clients

虛擬機器映象

進入容器

[root@compute84 libvirt]# docker exec -it qemu-vm bash
bash-4.2# cd
bash-4.2# ls
CentOS-7-x86_64-GenericCloud.qcow2     centos.qcow2     image    nohup.out  start.sh  vm3.xml
CentOS-7-x86_64-Minimal-1810.iso       cloud-init-start.sh  kernel   qemu   vm.xml
Fedora-Cloud-Base-30-1.2.x86_64.qcow2  destroy.sh       libvirt  run.sh vm2.xml

下載虛擬機器映象:

openstack已經提供很多已經裝過cloud-init的映象,地址:
https://docs.openstack.org/image-guide/obtain-images.html

我用的一個比較新的centos的qcow2格式映象:

wget http://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-1905.qcow2

修改虛擬機器root密碼:

virt-customize -a CentOS-7-x86_64-GenericCloud.qcow2 --root-password password:coolpass

啟動虛擬機器

檢視容器網路資訊:

bash-4.2# systemctl start libvirtd
bash-4.2# ip addr
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: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
    link/ether 52:54:00:c6:59:47 brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
       valid_lft forever preferred_lft forever
3: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast master virbr0 state DOWN group default qlen 1000
    link/ether 52:54:00:c6:59:47 brd ff:ff:ff:ff:ff:ff
1310: eth0@if1311: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:acff:fe11:2/64 scope link 
       valid_lft forever preferred_lft forever

1,2,3是libvirt建立的可以忽略,最主要是eth0

編寫libvirt配置

vm3.xml:

<domain type='kvm'>
  <name>vm3</name>
  <memory unit='MiB'>2048</memory>
  <currentMemory unit='MiB'>2048</currentMemory>
  <os>
    <type arch='x86_64'>hvm</type>
    <boot dev='hd'/>
  </os>
  <clock offset='utc'/>
  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>destroy</on_crash>
  <devices>
    <emulator>/usr/local/bin/qemu-system-x86_64</emulator>
  <disk type='file' device='disk'>
       <driver name='qemu' type='qcow2'/>
       <source file='/root/CentOS-7-x86_64-GenericCloud.qcow2'/>
       <target dev='vda' bus='virtio'/>
  </disk>
  <interface type='direct'> 
    <source dev='eth0' mode='bridge' /> 
    <model type='virtio' />    
    <driver name='vhost' /> 
  </interface>
  <serial type='pty'>
    <target port='0'/>
  </serial>
  <console type='pty'>
    <target type='serial' port='0'/>
  </console>
  </devices>
</domain>

這裡配置正確映象地址,interface的地方是macvtap相關的配置。

啟動虛擬機器

bash-4.2# virsh define vm3.xml 
Domain vm3 defined from vm3.xml
bash-4.2# virsh start vm3     
Domain vm3 started

啟動完後就可以看到macvtap裝置被建立出來了

bash-4.2# ip addr
7: macvtap0@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 500
    link/ether 52:54:00:56:e4:20 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::5054:ff:fe56:e420/64 scope link 
       valid_lft forever preferred_lft forever

進入到虛擬機器:

virsh console vm3

如果卡在這一步:

A start job is running for LSB: Bri... networking
cloud-init[2253]: 2019-06-27 08:37:09,971 - url_helper.py[WARNING]: Calling 'http://192.168.122.1/latest/meta-data/instance-id' failed [87/120s]: request error

等它超時就好,因為macvtap時我們需要進入虛擬機器去配置網路。
然後就可以進入虛擬機器了:

CentOS Linux 7 (Core)
Kernel 3.10.0-957.1.3.el7.x86_64 on an x86_64

localhost login: root
Password: 
Last login: Thu Jun 27 07:19:32 from gateway

密碼是我們上面設定的映象密碼:coolpass

配置虛擬機器IP

[root@localhost ~]# ip addr
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: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 52:54:00:56:e4:20 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::5054:ff:fe56:e420/64 scope link 
       valid_lft forever preferred_lft forever
[root@localhost ~]# ip addr add 172.17.0.2/16 dev eth0
[root@localhost ~]# ip route add default via 172.17.0.1 dev eth0
[root@localhost ~]# ip addr
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: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 52:54:00:56:e4:20 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fe56:e420/64 scope link 
       valid_lft forever preferred_lft forever
[root@localhost ~]# ip route 
default via 172.17.0.1 dev eth0 
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.2 
[root@localhost ~]# ping 172.17.0.1
PING 172.17.0.1 (172.17.0.1) 56(84) bytes of data.
64 bytes from 172.17.0.1: icmp_seq=1 ttl=64 time=0.622 ms
64 bytes from 172.17.0.1: icmp_seq=2 ttl=64 time=0.194 ms

配置完後就可以ping通閘道器了。

修改DNS配置

這個不改可能會導致ssh時非常慢:

[root@localhost ~]# cat /etc/resolv.conf 
; Created by cloud-init on instance boot automatically, do not edit.
;
; generated by /usr/sbin/dhclient-script
nameserver 114.114.114.114

修改sshd配置

修改/etc/ssh/sshd-config檔案,將其中的PermitRootLogin no修改為yes,PubkeyAuthentication yes修改為no,AuthorizedKeysFile .ssh/authorized_keys前面加上#遮蔽掉,PasswordAuthentication no修改為yes就可以了。

啟動ssh客戶端容器

docker run --rm -it fanux/libvirt bash
[root@ee18547e9ed2 /]# ssh root@172.17.0.2
ssh: connect to host 172.17.0.2 port 22: Connection refused

會發現不通, 這是因為容器裡的eth0和虛擬機器裡的eth0都配置了相同的地址導致,只需要把容器裡的eth0地址刪掉即可:

bash-4.2# ip addr del 172.17.0.2/16 dev eth0

再次ssh即可進入虛擬機器:

[root@ee18547e9ed2 /]# ssh root@172.17.0.2
The authenticity of host '172.17.0.2 (172.17.0.2)' can't be established.
ECDSA key fingerprint is SHA256:kTk3yy8588WQHNtwpzS+h6u0W3RELWC8hJQwIwLOkdc.
ECDSA key fingerprint is MD5:0c:f3:b5:69:c6:08:05:14:f8:da:42:2f:85:29:51:d0.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '172.17.0.2' (ECDSA) to the list of known hosts.
root@172.17.0.2's password: 
Last login: Thu Jun 27 08:38:00 2019
[root@localhost ~]# ip addr
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: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 52:54:00:56:e4:20 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::5054:ff:fe56:e420/64 scope link 
       valid_lft forever preferred_lft forever

修改虛擬機器mac地址

[root@localhost ~]# ip link set eth0 address 52:54:00:56:e4:23

會發現就連不上虛擬機器了

改回:

[root@localhost ~]# ip link set eth0 address 52:54:00:56:e4:20

又可正常連線了,為啥?

這是因為虛擬機器的eth0的mac地址是必須與macvtap0的mac地址保持一樣,原理很簡單

  1. ARP時問IP地址是172.17.0.2的機器mac地址是什麼
  2. 虛擬機器回了一個52:54:00:56:e4:20
  3. macvtap0是可以理解成掛在網橋埠上的,這樣就把包發給macvtap0了(因為mac地址一樣,不一樣就不會發給macvtap了)
  4. macvtap0就把包丟給qemu應用程式(最終到虛擬機器eth0)

裸用qemu

以上是通過libvirt進行使用的,這樣遮蔽了很多底層的細節,如果是直接使用qemu命令需要如下操作:

建立macvtap裝置:

ip link add link eth0 name macvtap0 type macvtap mode bridge
ip link set macvtap0 address 1a:46:0b:ca:bc:7b up
bash-4.2# cat /sys/class/net/macvtap0/ifindex  # 對應下面命令的/dev/tap2
2
bash-4.2# cat /sys/class/net/macvtap0/address # 與qemu mac地址配置一致
1a:46:0b:ca:bc:7b

啟動qemu,然後虛擬機器裡面的地址配置同libvirt,可以通過vnc客戶端(vnc viewer)進入虛擬機器配置,不在贅述:

bash-4.2# qemu-system-x86_64 -enable-kvm /root/CentOS-7-x86_64-GenericCloud.qcow2\
-netdev tap,fd=30,id=hostnet0,vhost=on,vhostfd=4 30<>/dev/tap2 4<>/dev/vhost-net \
-device virtio-net-pci,netdev=hostnet0,id=net0,mac=1a:46:0b:ca:bc:7b   \
-monitor telnet:127.0.0.1:5801,server,nowait
VNC server running on ::1:5900

cloud-init介紹

上文提到,通過macvtap技術配置虛擬機器網路卡地址是需要進入虛擬機器配置的, 然而我們實現一個虛擬機器管理系統時顯然不會手動進入去配置,這就需要cloud-init了

它可以幫助我們在虛擬機器啟動時配置虛擬機器,如配置密碼,配置網路,執行命令和寫一些檔案等。
先建立一個user-data檔案,裡面內容如下:

#cloud-config
write_files:
 - content: |
    DEVICE=eth0
    ONBOOT=yes
    TYPE=Ethernet
    USERCTL=no
    IPADDR=172.17.0.2
    NETMASK=255.255.0.0
    GATEWAY=172.17.0.1
    BOOTPROTO=static
    DNS1=172.17.0.1
    ONBOOT=yes
   path: /etc/sysconfig/network-scripts/ifcfg-eth0
runcmd:
 - systemctl start network
groups:
  - centos: [root]
  - cloud-users
ssh_pwauth: yes
chpasswd:
    expire: false
    list: |
        user1:222222
        root:333333

建立cloud-init映象檔案

yum install -y cloud-utils
cloud-localds my-seed.img user-data

libvirt中使用該映象

  <disk type='file' device='disk'>
       <driver name='qemu' type='raw'/>
       <source file='/root/my-seed.img'/>
       <target dev='vdb' bus='virtio'/>
  </disk>

為了防止cloud-init走網路獲取metadata,因為網路卡沒設定好所以會卡住五分鐘,我們直接把網路獲取metadata禁止掉:

vi /etc/cloud/cloud.cfg.d/05_logging.cfg

加上:

network:
  config: disabled

然後啟動虛擬機器即可

常見問題

Inappropriate ioctl for device

qemu-system-x86_64: -net tap,fd=5: TUNGETIFF ioctl() failed: Inappropriate ioctl for device
TUNSETOFFLOAD ioctl() failed: Inappropriate ioctl for device

因為容器沒有掛載/dev目錄

KVM bios被禁

[root@helix105 ~]# docker run busybox uname -a
Could not access KVM kernel module: No such file or directory
qemu-lite-system-x86_64: failed to initialize KVM: No such file or directory

/usr/bin/docker-current: Error response from daemon: oci runtime error: Unable to launch /usr/bin/qemu-lite-system-x86_64: exit status 1.
ERRO[0001] error getting events from daemon: net/http: request canceled 
[root@helix105 ~]# lsmod |grep kvm
kvm                   598016  0 
irqbypass              16384  1 kvm
[root@helix105 ~]# modprobe kvm-intel
modprobe: ERROR: could not insert 'kvm_intel': Operation not supported
You have mail in /var/spool/mail/root
[root@helix105 ~]# dmesg |grep kvm
[    8.239309] kvm: disabled by bios

這個要進bios開啟

KVM: Permission denied

bash-4.2# virsh start vm_name1
error: Failed to start domain vm_name1
error: internal error: qemu unexpectedly closed the monitor: Could not access KVM kernel module: Permission denied
2019-06-20T07:26:33.304320Z qemu-system-x86_64: failed to initialize KVM: Permission denied

解決辦法:

#chown root:kvm /dev/kvm
修改/etc/libvirt/qemu.conf,
#user="root"
user="root"
#group="root"
group="root"
重啟服務
#service libvirtd restart,問題解決了

掃碼關注sealyun
macvtap使用教程
探討可加QQ群:98488045