強隔離容器的那些事

爲什麼需要強隔離容器

我們在生產環境中運行容器已久,第一次對強隔離容器訴求是java類應用引起的,如果不配置jvm參數,java虛擬機會根據系統資源信息進行內存gc線程數等配置,在不給容器配額的情況下問題不大,一旦配額了。。。

普通的容器在容器中看到的資源還是宿主機的資源,那麼假設宿主機128G而你給容器配額2G,此時堆內存按照128G去分,可想而知後果,同理還有gc線程數等
<!--more-->

給jvm配置參數就行了唄

我們很難改變用戶行爲,讓用戶都去改動參數不太現實。

lxcfs一定程度上解決了這個問題

lxcfs可以讓容器有更好的資源可視性,如內存,cpuset等,原理也非常簡單,就是把proc下的一些文件還在給容器,容器內進程讀取資源信息時系統調用會被lxcfs攔截,然後到cgroup下去查該進程資源配額信息進行計算,大部分場景可以通過這個方式修修補補

然鵝,lxcfs的缺陷

第一,支持lxcfs的運行時甚少
第二,用戶使用時不透明,需要自行掛載很多文件不友好
第三,由於第二點,你就得去開發一些特性去支持它,主流方式有幾種

1.k8s上監聽一些對象的創建,進行修改
2.修改kubelet,在volume裏默認加上,我們就是這樣做的,正在把這個特性PR給社區
3.修改runtime,或者直接選擇支持這個特性的運行時,如pouch

第四,cpushare的方式,我們也正在把這個特性pr給社區,通過計算佔比把計算後的cpu核數上報給進程
第五,很多應用從system下面去讀取資源信息,而非proc,這樣又是一大波定製需求。。。還有remout等等問題

總體來說都是修修補補,不能從徹底上解決問題

這讓我越來越看好輕量級虛擬化技術

kata runv等技術的出現真的是把虛擬機容器的優勢強強結合,容器的調度編排管理生態,鏡像標準,再加上虛擬機的強隔離

下面開始一大波名詞解釋以及他們之間的關係

containerd地位難以撼動,真正管理容器的守護進程,k8s和docker都可以通過unix socket去調用它,然後每起一個容器containerd會去調用runc runv kata等

kata runv qemu firecracker rust-vmm都是啥關係

kata和runv都是可以被containerd調用然後調用qemu命令去啓動虛擬機

qemu 和firecracker是一個級別,真正去啓動虛擬機的,和張磊大佬交流時這裏引用大佬一句話:qemu是在一大坨功能上做減法,firecracker是在非常核心簡單的功能上做加法。

那麼我們到底因該選qemu還是firecracker呢,那肯定是與場景相關了,比如我們希望用重量級虛擬機,有狀態,需要遷移,需要systemd sshd等,那麼肯定還是走qemu libvirt, 如果我們走輕量級虛擬機firecracker是個非常不錯的選擇,而且潛力巨大,畢竟是來跑亞馬遜函數計算的,不是蓋的。看下firecracker api就發現真簡單,再去看qemu文檔。。。。什麼**鳥玩意兒。。。

qemu大神別噴我,我承認其強大,但是很多時候遇到問題有點無從下手,很多使用方法我也是從源碼中摸索出來的,個人還是喜歡更輕量級的東西。不過我依然還是對學習qemu有很大熱情。

順便提一下libvirt,既然重,那不如再重一點,libvirt能讓你更方便的管理qemu虛擬機和qemu開發,細節不贅述了

rust-vmm是個更底層的一系列組件,大佬說是政治產物,自己如果對寫hypervisor有興趣可以抱着學習態度去開發玩,生產中直接firecracker就好了,所以rust的潛力還是巨大的,爲了寫虛擬機爲了寫操作系統,和我一起學rust🤪🤪

鋪墊的差不多了,下面正式開始:

因爲kata能支持firecracker和qemu,所以針對kata這個技術來做個具體點的介紹

進程模型

所以kata runtime替代掉的是runc部分的東西,因爲中間有containerd,所以上層如docker k8s感知不到運行時的變化。

containerd會與kata的shim進程通信,shim與agent通信,agent在虛擬機裏面做一些事情,如配置網卡,啓動容器等。

虛擬化方式

這個圖虛線左邊不用看,本質就是調用qemu命令創建虛擬機,右邊實際上kata是把k8s pod這個殼本來是容器,換成了虛擬機,但是有很多細節:

  1. 網絡任然在一個ns中,下文會講
  2. kata agent依然會在虛擬機中啓動容器
kata網絡

熟悉docker默認網絡模式的親都比較清楚設備對還沒變,設備對的另外一端與虛擬機連接是由kata負責,用的技術叫macvtap,它可以讓一個接口擁有多個mac地址。

創建macvtap設備:

ip link add link eth0 name macvtap0 type macvtap mode bridge
ip link set macvtap0 address 1a:46:0b:ca:bc:7b up
cat /sys/class/net/macvtap0/ifindex
cat /sys/class/net/macvtap0/address
通過qemu啓動:
qemu-system-x86_64 -enable-kvm centos.qcow2 \
 -cdrom CentOS-7-x86_64-Minimal-1810.iso \
 -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

注意網絡參數,這塊很少資料介紹的比較清楚都是啥含義,我也是通過學習kata源碼問了很多大牛才徹底理解的。
/dev/tap2 這個2 是通過上面的 /sys/class/net/macvtap0/ifindex 差得的。
vhost是虛擬機網絡虛擬化的一種模式,性能比較高,我們需要把vhost的fd傳入給qemu

對應kata的代碼,本質就是打開了這兩文件,把fd傳入:

func createMacvtapFds(linkIndex int, queues int) ([]*os.File, error) {
  tapDev := fmt.Sprintf("/dev/tap%d", linkIndex)
  return createFds(tapDev, queues)
}

  fds := make([]*os.File, numFds)
  for i := 0; i < numFds; i++ {
    f, err := os.OpenFile(device, os.O_RDWR, defaultFilePerms)
    if err != nil {
      utils.CleanupFds(fds, i)
      return nil, err
    }
    fds[i] = f
  }
  return fds, nil

事情還沒結束,進入虛擬機會發現網卡沒有地址:

[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:59:ee:01 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::5054:ff:fe59:ee01/64 scope link 
       valid_lft forever preferred_lft forever

因爲虛擬機的eth0的地址是kata-agent去配置的,所以這裏需要自己在虛擬機配置一下,ip一定要與設備對另一端的eth0一樣。

網絡其它的部分就是兼容CNI標準了,本文不做過多介紹了。

文件系統DAX(Direct Access filesystem)
內核DAX功能有效地將一些主機端文件映射到來賓VM空間。特別是Kata Containers使用QEMU NVDIMM功能提供內存映射的虛擬設備,可用於將虛擬機的根文件系統DAX映射到guest內存地址空間。

看rootfs是這樣過去的
QEMU配置了NVDIMM內存設備,內存文件後端在主機端文件中映射到虛擬NVDIMM空間。
guest虛擬機內核命令行安裝此NVDIMM設備並啓用DAX功能,允許直接頁面映射和訪問,從而繞過guest虛擬機頁面緩存。這樣虛擬機的根文件系統就來了。

內核文件
kata kernel 此連接有詳細介紹
  1. kata對內核做了一些patch,如內存熱插拔,9pfs緩存優化,arm架構的更好支持等
  2. patch完了後把編譯好的內核放到kata指定的目錄

make -j $(nproc) ARCH="${arch_target}"

docker鏡像轉化成虛擬機鏡像
osbuilder項目專門去做這個事情,這裏要解釋的一個概念是initrd(或“initramfs”)壓縮cpio(1)歸檔,由rootfs創建,加載到內存中並用作Linux啓動過程的一部分。在啓動期間,內核將其解壓縮到一個特殊的實例中,該實例tmpfs將成爲初始的根文件系統。

使用方法也比較簡單,這裏不再贅述。

firecracker簡介

爲什麼我這麼喜歡firecracker,因爲你們一看它API就知道的,簡單到讓你懷疑人生:
以下是個網絡的例子:

  1. 宿主機上創建tap設備
sudo ip tuntap add tap0 mode tap
sudo ip addr add 172.16.0.1/24 dev tap0
sudo ip link set tap0 up
sudo sh -c "echo 1 > /proc/sys/net/ipv4/ip_forward"
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
sudo iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A FORWARD -i tap0 -o eth0 -j ACCEPT
  1. 調用API創建虛擬機網卡
curl -X PUT \
  --unix-socket /tmp/firecracker.socket \
  http://localhost/network-interfaces/eth0 \
  -H accept:application/json \
  -H content-type:application/json \
  -d '{
      "iface_id": "eth0",
      "guest_mac": "AA:FC:00:00:00:01",
      "host_dev_name": "tap0"
    }'
  1. 配置虛擬機網卡
ip addr add 172.16.0.2/24 dev eth0
ip route add default via 172.16.0.1 dev eth0

清清楚楚,乾乾淨淨

輕量級虛擬機其它
現在輕量級虛擬機還是有些問題沒解決,比如監控,就不能像cadvisor那樣去監控容器了,所以這塊kubelet採集的地方就需要定製。

kubevirt簡介
以上都是輕量級虛擬機,然而對於亡openstack之心不死的人還是希望搞出個能管理重量級虛擬機的東西,kubevirt應運而生。

我們如果去基本kata去管理有狀態的重量級虛擬機其實還是有很多事要去做的:
生命週期管理,k8s可沒有啓動停止容器這些概念,所以想要支持虛擬機的啓動重啓就得自己去定義CRD,然後還不夠,因爲kubelet不會去調用CRI的啓動停止的接口,所以還得修改kubelet...
網絡,一般的CNI是滿足不了IP漂移以及VPC這種需求的,所以你需要ovn CNI之類的東西
虛擬機的系統盤數據盤放本地是不行了,改。。。
兼容openstack那些系統鏡像,改。。。

kubevirt正是因爲這個問題所以採用了這樣的架構:

僅資源調度時走k8s,虛擬機的生命週期管理基本已經與CRI沒關係了,全走自己的agent管理,這樣上面的那些問題都可以在virt-handler virt-laucher上解決,不用再去對k8s組件動刀。

本質就是在容器裏起了個虛擬機,不過啓動方式與kata有所不同,它使用了libvirt,qemu更上層的一個封裝,當然玩重量級虛擬機有這個還是方便很多的,很多時候我們需要調試,或者找錯誤,libvirt給了一系列的工具集,同時也對編程友好。

不過每個虛擬機都會去起一個libvirtd進程的做法我覺得還是有待商榷。

總結
本文雖然扯了很多,但是虛擬機還是遠比容器複雜,本文也只能提個冰山一角,希望大家讀完能有個整體的認識。我是希望能用一個統一的技術棧搞定容器,虛擬機,輕量級虛擬機,這樣能極大的節省企業的成本,尤其是人力維護成本。

掃碼關注sealyun

探討可加QQ羣:98488045

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章