10年资深架构师谈Linux上容器背后的虚拟化解决方案

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
简介:

写在前面

 

目前主流的虚拟化技术有类似Intel VT这样的纯粹底层硬件虚拟化技术,也有类似Xen这样的半虚拟化技术,它并不是一个真正的虚拟机,而是相当于自己运行了一个内核的实例,可以自由的加载内核模块,虚拟的内存和IO,稳定而且可预测。

 

也有kvm这样的完全虚拟化技术,所有的kvm类型的虚拟技术都可以装各种linux的发行版和各 种win的发行版,当然,还有最近比较流行的容器类技术,例如lxc,docker都是基于操作系统之上的进程级别的隔离技术,本次分享主要围绕上层的虚拟化技术来剖析Linux上的解决方案。

 

1cpu vt技术

 

为解决纯软件虚拟化解决方案在可靠性、安全性和性能上的不足很多cpu都引入了硬件虚拟化的技术,比如Intel在它的硬件产品上引入了Intel VT(Virtualization Technology,虚拟化技术)。

 

Intel VT可以让一个CPU工作起来像多个CPU在并行运行,从而使得在一部电脑内同时运行多个操作系统成为可能。

 

20160720055054307.jpg

 

Intel提供了:

  • Cpu虚拟化

  • 内存虚拟化

  • I/O虚拟化

  • 图形卡虚拟化

  • 网络虚拟化

 

2chroot分析

 

chroot是内核提供的一个系统调用,从安全性的角度考虑,来限定用户使用的根目录,ch在经过 chroot 之后,系统读取到的目录和文件将不在是旧系统根下的而是新根下(即被指定的新的位置)的目录结构和文件,它的作用如下:

 

  • 增加了系统的安全性,限制了用户的权力

  • 建立一个与原系统隔离的系统目录结构,方便用户的开发

  • 切换系统的根目录位置,引导 Linux 系统启动以及急救系统等

 

所以,很多容器技术都使用了chroot。

 

3chroot实现

 

下面我们来看下chroot系统调用的实现:

 

20160720055104236.jpg

20160720055111492.jpg

 

从上面的源码可以发现chroot的逻辑其实非常简单,先是通过user_path_at函数搜索路径获取该filename的path,然后通过set_fs_root把当前进程的文件系统的root设置为path。

 

4cgroup分析

 

介绍

cgroups(Control Groups)最初叫Process Container,由Google工程师(Paul Menage和Rohit Seth)于2006年提出,后来因为Container有多重含义容易引起误解,就在2007年更名为Control Groups,并被整合进Linux内核。可以理解为把进程放到一个组里面统一加以控制。

 

cgroups可以限制、记录、隔离进程组所使用的物理资源(包括:CPU、memory、IO等)。

 

使用方法

20160720055120348.jpg

 

当我们创建文件夹后,自动生成的文件如下:

 

20160720055127160.jpg

 

设置task允许使用的cpu为0-1:

echo 0-1 > cpuset.cpus

 

添加task到cgroup:

echo [pid] >> tasks

 

这样就可以对指定pid的进程限制使用的cpu仅为0和1。

 

体系结构

接下来我们介绍一下cgroup的整体架构:

 

20160720055135323.jpg

 

由上图,我们来简单介绍一下cgroup的主要概念:

 

  • cgroupfs_root:可以理解为我们mount操作指定的dir目录。

 

  • css_set:提供了与进程相关的cgroups信息.其中cg_links 指向一个由 struct_cg_cgroup_link 连成的链表。Subsys 是一个指针数组,存储一组指向 cgroup_subsys_state 的指针。一个 cgroup_subsys_state 就是进程与一个特定子系统相关的信息。通过这个指针数组,进程就可以获得相应的 cgroups 控制信息了。

     

  • css_set_table:css_set_table保存了所有的css_set,hash函数及key为css_set_hash(css_set->subsys)

     

  • hierarchy:层级,cgroup可以组织成 hierarchical 的形式,既一颗cgroup树。group树上的子节点cgroup是父节点cgroup的孩子,继承父cgroup的特定的属性。

 

  • cgroup:可以理解为在mount目录下mkdir新建的目录。cgroups 中的资源控制都以cgroup为单位实现。cgroup表示按某种资源控制标准划分而成的任务组,包含一个或多个子系统。一个任务可以加入某个cgroup,也可以从某个cgroup迁移到另外一个cgroup。

 

  • cg_group_link:由于cgroup和css_cet之间是多对多的关系,cg_group_link是用来关联这两者的。一个css_set为什么会有多个cgroup?——其根本的原因在于一个task会被attach到多个hierarchy,并且在每个hierarchy下的必有一个cgroup管理着该task(一个task只有一个css_set),所以有几个hierarchy该css_set就有几个cgroup。一个cgroup为什么会有多个css_set?——因为一个cgroup可以attach多个进程,而这些进程它们的css_set可能不一样,这个由该task所属的cgroups决定,只有所属的cgroups完全一样,它们才会共享一个css_set,否则的话,它们的css_set就不一样。

     

  • cgroup_subsys:代表cgroup的某个子系统,它代表了某个子系统如何注册到cgroup或者销毁的过程:

 

20160720055146715.jpg

 

上面的代码罗列了cgroup_subsys接口的钩子函数。

 

  • cgroup_subsys_state:代表每个子系统真正的控制结构,图中的cup,cupset,blkio_group等都是它的实现,也就是说,每个子系统对系统资源的限制就是通过cgroup_subsys_state的实现来完成的。

 

task和cgroup的关系:

css_set->tasks是所有引用该css_set的tasks的list的head,task之间用task->cg_list进行链接,一个cgroupfs_root的所有cgroup_subsys由cgroupfs_root->subsys_list组织;所有的cgroupfs_root通过它的root_list连接到roots这个全局变量头里。

 

从某种角度上来讲,cgroup的这个结构,有点像业务系统中的权限系统。

 

5cgroup文件系统

 

cgroup是通过标准的vfs接口方式提供给上层交互的,每当我们mount或者mkdir时候,目录下面的文件就是通过cgroup自己创建的,这些文件定义方式如下:

 

全局cgroup.c中:

20160720055158669.jpg

20160720055205963.jpg

 

子系统也维护了各自的files[]文件,比如cpuset:

 

20160720055214851.jpg

20160720055222597.jpg

20160720055230806.jpg

 

以cgroup.procs文件的写操作为例:

 

20160720055238283.jpg

20160720055247861.jpg

 

这个写入的过程最终通过cgroup_attach_task把cgroup下的subsys attach到该task中。

 

  • 创建cgroup

创建一个新的cgroup也是通过标准的vfs操作来执行的,在cgroup中定义了cgroup文件系统的一些操作:

 

20160720055259602.jpg

 

创建cgroup是通过mkdir来创建的:

 

20160720055306194.jpg

20160720055314103.jpg

20160720055323617.jpg

20160720055330715.jpg

20160720055339483.jpg

 

以上过程先为该cgroup所属的每个subsys创建一个cgroup_subsys_state,并初始化。

 

通过该cgroup->subsys[]可以获得该cgroup的所有cgroup_subsys_state,同样通过cgroup_subsys_state->cgroup可以知道该cgroup_subsys_state所属的cgroup。以后cgroup与subsys的group控制体的转换都是通过该结构来完成。   

 

这里并没有建立css_set与该cgroup的关系,因为mkdir时该cgroup还没有attach任何进程,所以也不会有css_set与它有关系。

 

  • attach task

attach task的过程是由cgroup_attach_task函数来完成的:

 

20160720055349228.jpg

 

它的执行过程总共分为3步:

 

1、 cgroup_migrate_add_src从task的css_set中找到dst_cgrp相关的hierarchy,它通过调用cset_cgroup_from_root来找到src_cset指定dst_cgrpc层级下的cgroup:

 

20160720055357555.jpg

 

2、cgroup_migrate_prepare_dst,通过find_css_set查找一个已经存在的css_set或者创建一个新的,首先通过find_existing_css_set查找是否有一个可用的css_set,否则,创建一个新的css_set,并且建立新的css_set与该task之前旧的css_set的所有关联的cgroup的关系,再把该新的css_set加入到哈希表hlist:

 

20160720055405828.jpg

20160720055413629.jpg

 

3、cgroup_migrate,进行真正的attach工作:

 

20160720055420487.jpg

 

cgroup_taskset_migrate,先判断该task是否可以在所有subsys上can_attach,然后进行attach:

 

20160720055427204.jpg

20160720055436306.jpg

20160720055444651.jpg

 

  • cpuset子系统

如果是多核心的cpu,这个子系统会为cgroup任务分配单独的cpu和内存。

 

这里我们简单分析对cpuset.cpus文件的操作:

 

20160720055453323.jpg

 

在files数组中定义了write操作的函数是cpuset_write_resmask

20160720055503339.jpg

20160720055511606.jpg

 

针对FILE_CPULIST的场景,由update_cpumask更新cpu对应的mask:

 

20160720055520643.jpg

20160720055528321.jpg

 

最终的目的就是在于更新该cgroup下的每个进程的cpus_allowed.

然后在下次task被唤醒的时候,select_task_rq_fair选择cpu_allowed里的某一个cpu,可能是load最低的)来确定它应该被置于哪个CPU的运行队列及运行,一个进程在某一时刻只能存在于一个CPU的运行队列里。

 

6Namespace分析

 

Cgroup提供了资源限制的功能,namespace则提供了资源隔离的功能,它提供六种namespace隔离功能:

 

20160720055536846.jpg

隔离后每个namespace看上去就像一个单独的Linux系统。

 

20160720055542952.jpg

 

一个进程可以属于多个namesapce,在task_struct 结构中有一个指向namespace结构体的指针nsproxy:

 

20160720055549866.jpg

 

假如不指定ns,那么默认所有的进程创建的时候,都会指定一个默认的ns:

20160720055558390.jpg

 

如果想要手动指定自己的ns,那么可以通过clone函数传入参数来实现。

 

Clone函数执行的过程中会调用do_fork->copy_process->copy_namespaces->create_new_namespaces:

20160720055610922.jpg

20160720055618202.jpg

 

在创建完独立的ns后,以分配pid为例:

 

20160720055625294.jpg

 

pid将会在其独立的ns中进行分配。

 

7lxc介绍

 

当我们了解完chroot,cgroup,namespace这些概念后,lxc其实是基于这些东西的封装,提供了一套软件包,来帮我们创建容器,我们可以粗略的认为LXC = Cgroup+ namespace + Chroot + veth +用户态控制脚本。

 

  • docker和虚拟化分析

docker在0.9.0之前的版本,是直接通过LXC来创建虚拟化容器的。目前docker自己维护了libcontainer来进行容器的管理。

 

下面摘录部分片段:

 

创建networknamespace:

 

20160720055633124.jpg

 

cgroup和相关限制的资源定义:

 

20160720055640871.jpg

20160720055649291.jpg

20160720055657468.jpg

20160720055705992.jpg

 

8其他

 

除了以上提到的进程资源隔离和限制的轻量级虚拟化解决方案外,Linux还提供了像KVM这样的全虚拟化解决方案:

 

20160720055037159.jpg

KVM是Kernel-based Virtual Machine的缩写,这是一个针对X86上的Linux完全虚拟化硬件解决方案,它可以基于Intel VT AMD-V两种不同厂商的解决方案来实现。

 

您可以很容易地运行多个虚拟机运行Linux或windows镜像,每个虚拟机都有自己的单独的虚拟私人硬件;它包含一个磁盘,一个网卡,一个图形适配器等等。KVM实际上是一个开源的软件解决方案。

 

Q&A

 

Q1:虚拟化应该会带来一些系统性能损耗,在硬件虚拟化,半虚拟化,kvm完全虚拟化,docker等,老师有没有一些数据可供参考?

A1:理论上肯定是硬件虚拟化性能最好的。目前kvm已经在很多公司生产环境上跑了,我也在河狸家的生产环境上跑着kvm,目前支撑生产环境问题不大。docker只是一个进程级别的隔离,理论上对性能是不会有大的影响的,不过docker的主要问题还是网络上面,比如跨容器之间的通信,使用类似vxlan这样的技术,我测试了下性能至少损失30%。还有一种方法是建多个bridge,然后每个容器的veth和bridge挂钩。

 

Q2:虚拟化鼻祖开源的qemu用的是什么虚拟化技术?

A2:qemu是模拟器,底层还是调用了kvm,可以参考我最后发的那张kvm的架构图。

 

Q3:最近看Openstack,KVM是标准配置?

A3:openstack适合大集群的管理,像什么创业公司机器不多的话自己在libvert上涌python包一层管理都行,或者用apache下的cloud stack来管理。

 

作者介绍  陈科

  • 河狸家技术总监,超十年互联网从业经验,曾就职于阿里、华为等企业架构部门


本文来自云栖社区合作伙伴"DBAplus",原文发布时间:2016-07-20

目录
相关文章
|
8天前
|
Cloud Native 云计算 Docker
云原生之旅:从容器化到微服务架构
【9月更文挑战第27天】本文将引领读者进入云原生的世界,探索如何通过容器化技术实现应用的快速部署与扩展,并深入理解微服务架构的设计哲学。我们将一起见证代码如何转化为可在云端无缝运行的服务,同时讨论云原生生态中的最佳实践和面临的挑战。
|
2月前
|
运维 监控 Cloud Native
自动化运维的魔法书云原生之旅:从容器化到微服务架构的演变
【8月更文挑战第29天】本文将带你领略自动化运维的魅力,从脚本编写到工具应用,我们将一起探索如何通过技术提升效率和稳定性。你将学会如何让服务器自主完成更新、监控和故障修复,仿佛拥有了一本能够自动翻页的魔法书。
|
1月前
|
NoSQL 关系型数据库 Redis
mall在linux环境下的部署(基于Docker容器),Docker安装mysql、redis、nginx、rabbitmq、elasticsearch、logstash、kibana、mongo
mall在linux环境下的部署(基于Docker容器),docker安装mysql、redis、nginx、rabbitmq、elasticsearch、logstash、kibana、mongodb、minio详细教程,拉取镜像、运行容器
mall在linux环境下的部署(基于Docker容器),Docker安装mysql、redis、nginx、rabbitmq、elasticsearch、logstash、kibana、mongo
|
11天前
|
Linux 编译器 开发工具
快速在linux上配置python3.x的环境以及可能报错的解决方案(python其它版本可同样方式安装)
这篇文章介绍了在Linux系统上配置Python 3.x环境的步骤,包括安装系统依赖、下载和解压Python源码、编译安装、修改环境变量,以及常见安装错误的解决方案。
20 1
|
17天前
|
运维 Kubernetes Cloud Native
探索云原生技术:容器化与微服务架构的融合之道
【9月更文挑战第18天】在数字化转型的浪潮中,云原生技术以其灵活性、可扩展性成为企业创新的强大引擎。本文将深入探讨云原生技术的核心概念,特别是容器化和微服务架构如何相辅相成,共同推动现代应用的开发与部署。通过实际代码示例,我们将揭示这些技术如何简化运维,加速产品上市时间,并提高系统的可靠性和弹性。无论你是开发人员、架构师还是IT决策者,这篇文章都将为你提供宝贵的洞见和实践指导。
20 2
|
19天前
|
运维 Cloud Native 持续交付
云原生之旅:从容器化到微服务架构的探索
【9月更文挑战第16天】在数字化转型的浪潮中,云原生技术成为推动企业创新和效率提升的关键力量。本文将带你深入了解云原生的核心理念,从容器化技术的入门应用到微服务架构的设计实践,揭示如何利用这些先进技术构建更灵活、更可靠的系统。我们将通过具体案例,探讨云原生技术如何帮助企业实现快速迭代与持续交付,以及在这一过程中可能遇到的挑战和解决方案。
|
17天前
|
Kubernetes Cloud Native Java
云原生技术之旅:从容器化到微服务架构
【9月更文挑战第18天】云原生技术正改变着我们构建、部署和管理应用的方式。本文将通过一次虚拟的旅行,带领读者探索云原生的核心概念,如容器化、微服务、持续集成与交付等。我们将以一个实际案例为线索,逐步展开对Kubernetes集群管理、Docker容器创建和Spring Boot微服务开发的讨论。就像在旅途中不断发现新风景一样,您将了解到这些技术如何协同工作,提升开发效率和应用性能。准备好了吗?让我们启航!
|
28天前
|
运维 Cloud Native 云计算
云原生之旅:从容器化到微服务架构的演进之路
在数字化浪潮中,云原生技术如同星辰大海中的灯塔,为航船指引方向。本文将带你穿梭于云计算的世界,探索从容器化技术到微服务架构的变革旅程。我们将一窥云原生如何助力企业灵活应对快速变化的市场需求,以及在这一过程中,开发者和运维人员是如何成为时代变革的弄潮儿。让我们一同启航,驶向云原生的广阔天地。
|
23天前
|
运维 Cloud Native Devops
云原生架构的崛起与实践云原生架构是一种通过容器化、微服务和DevOps等技术手段,帮助应用系统实现敏捷部署、弹性扩展和高效运维的技术理念。本文将探讨云原生的概念、核心技术以及其在企业中的应用实践,揭示云原生如何成为现代软件开发和运营的主流方式。##
云原生架构是现代IT领域的一场革命,它依托于容器化、微服务和DevOps等核心技术,旨在解决传统架构在应对复杂业务需求时的不足。通过采用云原生方法,企业可以实现敏捷部署、弹性扩展和高效运维,从而大幅提升开发效率和系统可靠性。本文详细阐述了云原生的核心概念、主要技术和实际应用案例,并探讨了企业在实施云原生过程中的挑战与解决方案。无论是正在转型的传统企业,还是寻求创新的互联网企业,云原生都提供了一条实现高效能、高灵活性和高可靠性的技术路径。 ##
29 3
|
25天前
|
运维 Kubernetes Cloud Native
云原生之旅:容器化与微服务架构的融合之道
在数字化转型的浪潮中,云原生技术以其高效、灵活的特性成为企业IT架构升级的重要选择。本文将探讨云原生的核心概念——容器化和微服务架构,并阐述它们如何相互融合,共同推动现代应用的开发与部署。通过深入浅出的解释,我们将揭示云原生如何助力企业快速适应市场变化,实现业务的持续创新和价值最大化。
下一篇
无影云桌面