强隔离容器的那些事

为什么需要强隔离容器

我们在生产环境中运行容器已久,第一次对强隔离容器诉求是java类应用引起的,如果不配置jvm参数,java虚拟机会根据系统资源信息进行内存gc线程数等配置,在不给容器配额的情况下问题不大,一旦配额了。。。

普通的容器在容器中看到的资源还是宿主机的资源,那么假设宿主机128G而你给容器配额2G,此时堆内存按照128G去分,可想而知后果,同理还有gc线程数等

给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

事情还没结束,进入虚拟机会发现网卡没有地址:

[[email protected] ~]# 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

原文地址:https://www.cnblogs.com/sealyun/p/11277183.html

时间: 2024-10-03 15:14:13

强隔离容器的那些事的相关文章

NSArray是强引用容器

经常比较疑惑NSArray.NSDictionary.NSSet这几个对象容器管理对象所采用的方式是“强引用”还是“弱引用”. 通过简单的命令行程序得到的结论是“NSArray.NSDictionary.NSSet这几个容器都是强引用容器”. 打开Xcode,新建project,选择“OS X”-“Application”-“Command Line Tool”,完成project的创建,默认情况下,project会使用ARC,这里不需要使用ARC,所以还需要在project配置文件中将ARC取

理解Docker(3):Docker 使用 Linux namespace 隔离容器的运行环境

1. 基础知识:Linux namespace 的概念 Linux 内核从版本 2.4.19 开始陆续引入了 namespace 的概念.其目的是将某个特定的全局系统资源(global system resource)通过抽象方法使得namespace 中的进程看起来拥有它们自己的隔离的全局系统资源实例(The purpose of each namespace is to wrap a particular global system resource in an abstraction th

docker容器创建openvpn事遇到的问题

创建容器搭建openvpn(搭建过程不做介绍) 最开始报错/dev/net/tun not found,所以我手动创建了/dev/net/tun mkdir /dev/net -pv mknod /dev/net/tun c 10 200 chmod 666 /dev/net/tun 这个问题解决过后继续报错 ERROR: Cannot ioctl TUNSETIFF tun: Operation not permitted (errno=1) 经过一番google找到解决方法 创建容器时加入-

9 年云原生实践全景揭秘|《阿里巴巴云原生实践 15 讲》正式开放下载

以容器.服务网格.微服务.Serverless 为代表的云原生技术,带来一种全新的方式来构建应用.同时,云原生也在拓展云计算的边界,一方面是多云.混合云推动无边界云计算,一方面云边端的协同.在云的趋势下,越来越多的企业开始将业务与技术向“云原生”演进. 在这个演进过程中,企业都或多或少都面对一些困惑与挑战,其中如何将应用和软件向 Kubernetes 体系进行迁移.交付和持续发布是一个普遍的难题. 阿里巴巴从 2011 年开始通过容器实践云原生技术体系,在整个业界都还没有任何范例可供参考的大背境

隔离 docker 容器中的用户

笔者在前文<理解 docker 容器中的 uid 和 gid>介绍了 docker 容器中的用户与宿主机上用户的关系,得出的结论是:docker 默认没有隔离宿主机用户和容器中的用户.如果你已经了解了 Linux 的 user namespace 技术(参考<Linux Namespace : User>),那么自然会问:docker 为什么不利用 Linux user namespace 实现用户的隔离呢?事实上,docker 已经实现了相关的功能,只是默认没有启用而已.笔者将在

虚拟机已死,容器才是未来?

导读 我也曾经是容器技术尤其是 Docker 粉丝,但用了一年后觉得事情也没那么美好,而颇有一些同学以及一些公司依然认为容器就是银弹,虚拟机已经是昨儿黄花必须打倒,大家赶紧一切皆容器.这里我对这种观点吐吐槽.仅代表作者个人看法 首先要明确的是,软件开发和运维活动中,可维护性.正确性.性能的优先级是依次降低的,不要跟我抬杠少数极端情况.关于可维护性和正确性的先后,著名的 "worse is better" 文章就是很好的无奈的解释,如果你犹豫这两者,这还情有可原,毕竟真善美和糙快猛的斗争

阿里云在LC3大会上透露未来要做的两件事

摘要: 阿里云研究员褚霸在LC3大会上同多位业界资深大咖同台交流表示,阿里云发展到今天,把过去应对淘宝.天猫大规模计算以及双11的计算能力转换成普惠的能力放在云上,这是一个非常大的挑战,也是其他厂商没有经历过的.这些挑战不断帮助阿里云积累经验,提升技术能力. 6月25日,由LFAsia, LLC主办的全球开源顶级盛会LinuxCon + ContainerCon + CloudOpen(LC3)在北京国家会议中心召开.来自国内外的开发人员.架构师.系统管理员.DevOps专家.商业领袖等数千名专

在Linux和Windows的Docker容器中运行ASP.NET Core

(此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 译者序:其实过去这周我都在研究这方面的内容,结果周末有事没有来得及总结为文章,Scott Hanselman就捷足先登了.那么我就来翻译一下这篇文章,让更多的中文读者看到.当然Scott遇到的坑我也遇到了. 不过首先,对于不熟悉的朋友我还是来解释一下Linux容器和Windows容器的概念. 由于容器成为虚拟化和应用托管的一种不可避免的选项,Windows也开始为公众提供容器功能(其实微软具备和使用

Docker容器中运行ASP.NET Core

在Linux和Windows的Docker容器中运行ASP.NET Core 译者序:其实过去这周我都在研究这方面的内容,结果周末有事没有来得及总结为文章,Scott Hanselman就捷足先登了.那么我就来翻译一下这篇文章,让更多的中文读者看到.当然Scott遇到的坑我也遇到了. 不过首先,对于不熟悉的朋友我还是来解释一下Linux容器和Windows容器的概念. 由于容器成为虚拟化和应用托管的一种不可避免的选项,Windows也开始为公众提供容器功能(其实微软具备和使用容器技术很久了).这