一、前言 – 容器技术发展史
容器技术是现今计算技术的重要组成部分,其发展历程可以追溯到很早的计算机系统提供的进程隔离工具。以下是容器技术的发展历程,其中涵盖了从早期的进程隔离技术到现代云计算和云原生的演变:
① Jail 时代
- 1979 年 - chroot 的发明: 在贝尔实验室开发的第 7 版 Unix 中引入了 chroot 系统调用。它通过将进程的根目录更改为文件系统中的新位置,实现文件系统的隔离,这一技术被称为 Chroot Jail。可以说,这奠定了容器技术的基础。
- 2000 年 - FreeBSD Jail: 为了实现安全性和易于管理,FreeBSD Jail 在原有的 chroot 功能上增加了独立的进程和网络空间,从而为托管环境中的客户服务提供了更好的资源隔离。
- 2001 年 - Linux VServer: 类似于 FreeBSD Jail,Linux VServer 将文件系统、网络地址、内存等资源进行分区,实现了更为复杂的资源隔离。
- 2004 年 - Solaris Containers: 这一技术结合了系统资源控制和区域隔离,并增加了快照和克隆功能,为后来的云计算技术发展铺平了道路。
② 云时代
- 2006 年 - 云计算概念的提出: Google 提出了当数据量达到现在的1000倍甚至10000倍时,该如何处理的问题。云计算的概念引领了新的资源管理和分配需求。
- 2006 年 - Process Containers 和 cgroups: 由 Google 推出,用于限制、统计和隔离一组进程的资源使用。一年后,它被更名为"Control Groups (cgroups)",并最终并入 Linux 内核。
- 2008 年 - LXC 和 GAE 的推出: LXC 是第一个完整的 Linux 容器管理器实现。同时,Google 推出了 Google App Engine,将开发平台作为一种服务提供。
③ 云原生时代
- 2011 年 - Warden 和 Kubernetes 的出现: CloudFoundry 推出 Warden,解决多主机环境下的容器管理问题。
- 2013 年 - Docker 的问世与风靡: Docker 作为一个工具和生态系统,将应用程序及其依赖打包到可以跨平台运行的容器中。Docker 迅速成为业界标准,带动了容器生态的繁荣。
- 2014 年 - Kubernetes 的崛起: Google 发布了 Kubernetes,支持对容器的编排和管理。随着各大公司加入,Kubernetes 成为了云原生架构的重要部分。
- 2015-2016 年 - OCI 和 CNCF 的成立: 为了解决行业标准问题,Docker 和其他公司成立 OCI,以制定容器和镜像的标准。与此同时,CNCF 的成立,推动了云原生计算的发展。
k8s 成为云原生技术事实标志
- 2016 年 - CRI 标准的发布: 为了将 Kubernetes 和特定的容器运行时解耦,Google 和红帽主导发布 CRI 接口标准。
- 2017 年 - containerd 的发展: 随着 containerd 成为标准的 CRI,实现了容器运行时的标准化,各大厂商纷纷采用 containerd 和 Kubernetes 作为云原生解决方案的基础。
时间 | 事件 |
---|---|
2001 | Linux Vserver 分区 |
2008 | LVS – 第一个 完整 的 Linux 容器管理器 |
2013 | Docker – 打包容器 |
2014 | Google K8s – 编排容器 – 云原生架构 |
2016 | Docker – OCI 标准 |
2017 | K8s API标准 – CRI |
容器技术自其诞生以来,通过文件系统和进程隔离逐步发展到今天的云原生生态系统。在这一过程中,技术创新带来了资源的高效利用和管理的便捷性。尤其是随着 Kubernetes 的普及和标准化,容器已成为现代应用开发和部署的核心技术之一,并对云计算产业的发展产生了深远影响。
二、虚拟化 & 容器化
1. 什么是虚拟化、容器化
物理机:实际的服务器或者计算机。
- 相对于虚拟机而言的对实体计算机的称呼。
- 物理机提供给虚拟机以硬件环境,有时也称为“寄主”或“宿主”。
虚拟化:是指通过虚拟化技术将一台计算机虚拟为多台逻辑计算机。
- 在一台计算机上同时运行多个逻辑计算机,每个逻辑计算机可运行不同的操作系统,并且应用程序都可以在相互独立的空间内运行而互不影响,从而显著提高计算机的工作效率。
容器化:容器化是一种虚拟化技术,又称操作系统层虚拟化(Operating system levelvirtualization),这种技术将操作系统内核虚拟化,可以允许用户空间软件实例(instances)被分割成几个独立的单元,在内核中运行,而不是只有一个单一实例运行。
- 这个软件实例,也被称为是一个容器(containers)。对每个实例的拥有者与用户来说,他们使用的服务器程序,看起来就像是自己专用的。
- 容器技术是虚拟化的一种,而 docker是现今容器技术的事实标准
类比理解
- 物理机就像一个庄园,独立占用了一块土地,花园都是自己的,其他人无法共享使用。
- 虚拟机相当于开发商的一个楼盘,一栋楼一套房子一户人家,共享一块宅基地,共享小区的花园,共享小区的游乐设施
- 容器相当于几个人一起合租一套房,共享这套房子的卫生间、共享厨房、共享 WiFì,只有衣服、电脑等私人物品是你自己的。
2. 为什么要虚拟化、容器化?
我们从上面的历史发展来看,虚拟化和容器化的最主要目的就是资源隔离,随着资源隔离的实现逐渐也带来了更大的收益。
- 资源利用率高:将利用率较低的服务器资源进行整合,用更少硬件资源运行更多业务,降低 IT 支出和运维管理成本。
- 比如上图中我们的土地直接复用,使用这块土地的人多了,但是成本还是庄园那块地
- 环境标准化:一次构建,随处执行。实现执行环境的标准化发布,部署和运维。
- 开发过程中一个常见的问题是 环境一致性问题。由于开发环境、测试环境、生产环境不一致,导致有些bug 并未在开发过程中被发现。
- 而 Docker 的镜像提供了除内核外完整的运行时环境确保了应用运行环境一致性,从而不会再出现 「这段代码在我机器上没问题啊」 这类问题。
- 资源弹性收缩:根据业务情况,动态调整计算、存储、网络等硬件及软件资源。
- 比如遇到双 11 了,把服务扩容 100 个,双 11 过去了, 把扩容的 100 个收回去。
- 差异化环境提供:同时提供多套差异化的执行环境,限制环境使用资源。
- 比如我的服务一个以来 Ubuntu 操作系统,一个服务依赖 CentOS 操作系统,但是没有预算购买两个物理机,这个时候容器化就能很好的提供多种不同的环境。
- 沙箱安全:为避免不安全或不稳定软件对系统安全性、稳定性造成影响,可使用虚拟化技术构建虚拟执行环境。
- 比如我在容器里面执行
rm -f/*
不会把整个服务器搞死,也不影响其他人部署的程序使用。
- 比如我在容器里面执行
- 容器对比虚拟机更轻量,启动更快:传统的虚拟机技术启动应用服务往往需要数分钟,而 Docker 容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。
- 大大的节约了开发、测试、部署的时间。docker不需要虚拟内核,所以启动可以更快,相当于 windows 的开机时间省去了。
- 维护和扩展容易:Docker 使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。
- 此外,Docker 团队同各个开源项目团队一起维护了一大批高质量的 官方镜像,既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本。
- 比如 docker hub 提供了很多镜像,各个系统的一个命令就可以拿到了,研发也可以自己定制镜像分享给各个产品。
3. 虚拟化常见类别
虚拟机:存在于硬件层和操作系统层间的虚拟化技术。
- 虚拟机通过“伪造”一个硬件抽象接口将一个操作系统以及操作系统层以上的层嫁接到硬件上,实现和真实物理机几乎一样的功能。
- 比如我们在一台 Windows 系统的电脑上使用 Android 虚拟机,就能够用这台电脑打开 Android 系统上的应用。
容器:存在于操作系统层和函数库层之间的虚拟化技术。
- 容器通过“伪造”操作系统的接口将函数库层以上的功能置于操作系统上。以 Docker 为例,其就是一个基于 Linux 操作系统的 Namespace 和 Cgroup 功能实现的隔离容器,可以模拟操作系统的功能。
- 简单来说,如果虚拟机是把整个操作系统封装隔离,从而实现跨平台应用的话,那么容器则是把一个个应用单独封装隔离,从而实现跨平台应用。
- 所以 容器体积比虚拟机小很多,理论上占用资源更少M。
- 容器化就是应用程序级别的虚拟化技术。
- 容器提供了将应用程序的代码、运行时、系统工具、系统库和配置打包到一个实例中的标准方法。容器共享一个内核(操作系统),它安装在硬件上。
JVM 之类的虚拟机:存在于函数库层和应用程序之间的虚拟化技术。
- Java 虚拟机同样具有跨平台特性,所谓跨平台特性实际上也就是虚拟化的功劳。
- 因为 Java 语言是 调用操作系统函数库 的,JVM 就是在应用层与函数库层之间建立一个 抽象层,对下通过不同的版本适应不同的操作系统函数库,对上提供统一的运行环境交给程序和开发者,使开发者能够调用不同操作系统的函数库。
补充:应用程序执行环境分层
- 硬件层:提供硬件抽象,包括指令集架构、硬件设备及硬件访问接口
- 操作系统层:提供系统调用接口,管理硬件资源
- 程序库层:提供数据结构定义及函数调用接口
三、常见虚拟化实现方式
1. 主机虚拟化(虚拟机)实现
主机虚拟化的原理是通过在物理服务器上安装一个 虚拟化层 来实现。这个虚拟化层可以在物理服务器和客户操作系统之间建立虚拟机,使得它们可以独立运行。
从软件框架的角度上,根据虚拟化层是直接位于硬件之上还是在一个宿主操作系统之上,将虚拟化划分为
Type1
和Type2
- Type1 类的
Hypervisor
:是一种系统软件,它充当计算机硬件和虚拟机之间的中介,负责有效地分配和利用由各个虚拟机使用的硬件资源,这些虚拟机在物理主机上单独工作,因此,Hypervisor 也称为 虚拟机管理器- 特点:直接运行在硬件之上,没有宿主机操作系统,Hypervisor 直接控制硬件资源和客户机
- 典型框架为Xen、VmwareESX
- Type2 类的
Hypervisor
:运行在一个宿主机操作系统之上(Vmware Workstation) 或者 系统里面,Hypervisor 作为宿主机操作系统中的一个应用程序,客户机就是在宿主机操作系统上的一个进程。
2. 容器虚拟化实现
容器虚拟化,有别于主机虚拟化,是操作系统层的虚拟化。
基本原理:通过 namespace
进行各程序的隔离,加上 cgroups
进行资源的控制,以此来进行虚拟化。
2.1 Namespace(命名空间)
定义:namespace 是 Linux 内核用来隔离内核资源的方式。
作用:实现进程、文件系统、用户等资源的隔离。
具体实现:通过将一个或多个进程指定在同一个namespace中,使得这些进程只能看到与自己相关的资源。
注意:Linux namespaces 是对全局系统资源的一种封装隔离,使得处于不同 namespace 的进程拥有独立的全局系统资源,改变一个 namespace 中的系统资源只会影响当前namespace 里的进程,对其他 namespace 中的进程没有影响。
Namespace | 系统调用参数 | 隔离的全局系统资源 | 内核版本 |
---|---|---|---|
UTS | CLONE_NEWUTS | 主机名和域名 | 2.6.19 |
IPC | CLONE_NEWIPC | 信号量、消息队列和共享内存 – 进程间通信 | 2.6.19 |
PID | CLONE_NEWPID | 进程编号 | 2.6.24 |
Network | CLONE_NEWNET | 网络设备、网络栈、端口等 | 2.6.29 |
Mount | CLONE_NEWNS | 文件系统挂载点 | 2.4.19 |
User | CLONE_NEWUSER | 用户和用户组 | 3.8 |
以上命名空间在容器环境下的隔离效果如下:
UTS
:每个容器能看到自己的 hostname,拥有独立的主机名和域名。IPC
:同一个 IPC namespace 的进程之间能互相通讯,不同的IPC namespace 之间不能通信。PID
:每个 PID namespace 中的进程可以有其独立的 PID,每个容器可以有其 PID 为进程。1 的 rootNetwork
:每个容器用有其独立的网络设备,IP地址,IP路由表,/proc/net目录,端口号。Mount
:每个容器能看到不同的文件系统层次结构。User
:每个 container 可以有不同的 user 和 group id.
想想以下如果我们要隔离两个进程需要怎么办?
- 首先容器进程与进程之间需要隔离,所以需要 PID 隔离
- 首先 容器 A 进程 不能读取 容器 B进程 通讯内容需要、隔离信号量等,所以需要 IPC 隔离
- 首先 **容器 A 进程 **不能读取 容器 B进程 的文件,所以需要 Mount 隔离
- 首先 **容器 A 进程 **不能读取 容器 B进程 的 socket,所以需要 网络隔离、主机隔离
- Docker 允许用户在主机和容器间共享文件夹,同时不需要限制容器的访问权限这就容易让容器突破资源限制。需要借助用户空间来完成用户之间的隔离。
命令详解
命令 | 功能 | 用途 |
---|---|---|
dd |
复制文件并转换数据 | 创建镜像、测试性能、备份磁盘 |
mkfs |
创建文件系统 | 格式化新分区 |
df |
查看磁盘空间 | 查看挂载点使用情况 |
mount |
挂载文件系统 | 挂载 ISO、U盘、设备等 |
unshare |
创建隔离命名空间 | 容器底层调试、实验性隔离环境 |
① dd 命令:用于读取、转换并输出数据,可以从标准输入或文件中读取数据,根据指定的格式转换数据,再输出到文件、设备或标准输出
语法:dd OPTION
,参数如下:
参数 | 描述 |
---|---|
if=文件名 |
输入文件名,默认为标准输入。即指定源文件。 |
of=文件名 |
输出文件名,默认为标准输出。即指定目的文件。 |
bs=bytes |
同时设置读入/输出的块大小为 bytes 个字节,如果是ibs 设置单次读入块大小, obs 设置单次输出块大小, cbs 设置转换块大小 |
skip/seek=blocks |
从 输入/输出 文件开头跳过 blocks 个块后再开始复制 |
count=blocks |
仅拷贝 blocks 个块,块大小等于 ibs 指定的字节数。 |
conv=<关键字>
,关键字有如下类型:
conversion
:用指定的参数转换文件ascii
:转换ebcdic
为ascii
ebcdic
:转换asccii
为ebcdic
ibm
:转换ascii
为alternate ebcdic
block/unblock
:把每一行转换为长度为cbs
,不足部分用空格lcase
/ucase
:交换输入的每对字节noerror
:出错时不停止notrunc
:不截断输出文件sync
:将每个输入块填充到 ibs 个字节,不足部分用空(NULL)字符补齐
案例
# 生成镜像文件
dd if=/dev/zero of=test.txt bs=8k count=1014
# 大写转小写
dd if=in.txt of=out.txt conv=ucase
# 测试磁盘写入速度
dd if=/dev/zero of=testfile bs=1G count=1 oflag=direct
② mkfs 命令:在设备上创建指定类型的文件系统,即 “格式化”
语法:mkfs [-V] [-t fstype] [fs-options] filesys [blocks]
,参数如下:
参数 | 描述 |
---|---|
-t fstype |
指定要建立何种文件系统;如 ext3,ext4。 |
filesys |
指定要创建的文件系统对应的设备文件名。 |
blocks |
指定文件系统的磁盘块数。 |
-V |
详细显示模式。 |
fs-options |
传递给具体文件系统的参数。 |
案例
# 格式化分区为 ext4 文件系统
mkfs -t ext4 test.txt
mke2fs 1.46.5 (30-Dec-2021)
Discarding device blocks: done
Creating filesystem with 20480 4k blocks and 20480 inodes
Allocating group tables: done
Writing inode tables: done
Creating journal (1024 blocks): done
Writing superblocks and filesystem accounting information: done
③ df 命令:用于显示目前在 Linux 系统上的文件系统磁盘使用情况统计。
语法:dd OPTION
,参数如下:
参数 | 描述 |
---|---|
-a, --all |
包含所有的具有 0 Blocks 的文件系统。 |
-h, --human-readable |
使用人类可读的格式。 |
-H, --si |
很像 -h ,但是用 1000 为单位而不是用 1024。 |
-t, --type=TYPE |
限制列出文件系统的类型。 |
-T, --print-type |
显示文件系统的形式。 |
案例:
lighthouse@VM-8-10-ubuntu:~$ df -h
Filesystem Size Used Avail Use% Mounted on
tmpfs 340M 1020K 339M 1% /run
/dev/vda2 69G 22G 45G 33% /
tmpfs 1.7G 24K 1.7G 1% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
tmpfs 340M 4.0K 340M 1% /run/user/1002
tmpfs 340M 4.0K 340M 1% /run/user/1001
④ mount 命令:用于加载文件系统到指定的加载点
语法:
mount [-l]
mount [-t vfstype] [-o options] device dir
常见参数如下:
参数 | 描述 |
---|---|
-l |
显示已加载的文件系统列表。 |
-t vfstype |
指定加载文件系统类型,支持常见的 ext3, ext4, iso9660, tmpfs, xfs 等。 |
-o options |
主要用来描述设备或档案的挂接方式。 |
loop |
用来把一个文件当成硬盘分区挂接上系统。 |
ro |
采用只读方式挂接设备。 |
rw |
采用读写方式挂接设备。 |
device |
要挂接(mount)的设备。 |
dir |
挂载点的目录。 |
案例
lighthouse@VM-8-10-ubuntu:test$ sudo mkdir /mymount
lighthouse@VM-8-10-ubuntu:test$ sudo mount test.txt /mymount
lighthouse@VM-8-10-ubuntu:test$ ll /mymount/
total 24
drwxr-xr-x 3 root root 4096 Jul 9 20:51 ./
drwxr-xr-x 21 root root 4096 Jul 9 20:51 ../
drwx------ 2 root root 16384 Jul 9 20:47 lost+found/
lighthouse@VM-8-10-ubuntu:test$ df -t ext4
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/vda2 72127952 22751700 46319384 33% /
/dev/loop4 72652 24 66896 1% /mymount
# 取消挂载
lighthouse@VM-8-10-ubuntu:test$ sudo umount /mymount
lighthouse@VM-8-10-ubuntu:test$ df -t ext4
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/vda2 72127952 22751804 46319280 33% /
⑤ unshare 命令:
语法:dd OPTION
,参数如下:
参数 | 描述 |
---|---|
-i, --ipc |
不共享 IPC 空间。 |
-m, --mount |
不共享 Mount 空间。 |
-n, --net |
不共享 Net 空间。 |
-p, --pid |
不共享 PID 空间。 |
-u, --uts |
不共享 UTS 空间。 |
-U, --user |
不共享用户。 |
-V, --version |
版本查看。 |
--fork |
执行 unshare 的进程 fork 一个新的子进程,在子进程中执行 unshare 传入的参数。 |
--mount-proc |
执行子进程前,将 proc 优先挂载过去。 |
案例:
lighthouse@VM-8-10-ubuntu:test$ sudo unshare -u /bin/bash
root@VM-8-10-ubuntu:/home/lighthouse/code/test# hostname test1
root@VM-8-10-ubuntu:/home/lighthouse/code/test# hostname
test1
# 查看进程
root@VM-8-10-ubuntu:/home/lighthouse/code/test# ps -ajx | grep bash
1877172 1877180 1877180 1877180 pts/2 1886346 Ss 1001 0:00 -bash
1877180 1886346 1886346 1877180 pts/2 1886346 S+ 0 0:00 sudo unshare -u /bin/bash
1886346 1886347 1886347 1886347 pts/1 1886575 Ss 0 0:00 sudo unshare -u /bin/bash
1886347 1886348 1886348 1886347 pts/1 1886575 S 0 0:00 /bin/bash
1886348 1886576 1886575 1886347 pts/1 1886575 S+ 0 0:00 grep --color=auto bash
# exit 退出进程, 发现之前的没有持久设置
root@VM-8-10-ubuntu:/home/lighthouse/code/test# exit
exit
lighthouse@VM-8-10-ubuntu:test$ hostname
VM-8-10-ubuntu
⚠️ 注意:unshare
是一个运行时命令,执行后会启动一个新的 shell 进程,该进程处于独立的命名空间中,但不会持久化 ,退出即失效。
空间隔离实战
① 进程隔离:unshare
命令专门提供了一个参数--mount-proc
,在新的namespace
挂载一个独立的/proc
目录,方便进行进程的监控。
lighthouse@VM-8-10-ubuntu:test$ sudo unshare --fork --pid --mount-proc /bin/bash
root@VM-8-10-ubuntu:/home/lighthouse/code/test# ps -aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 7636 4272 pts/1 S 21:00 0:00 /bin/bash
root 7 0.0 0.1 10336 3752 pts/1 R+ 21:00 0:00 ps -aux
- 可以看到,创建了新的
namespace
后,ps -aux
只能查到两个进程,一个是bash
,一个是grep
。这就将namespace
内部的进程与宿主机的进程隔离开了。
② 文件隔离
2.2 CGroup
- 定义:
cgroups(Control Groups)
是 linux 内核提供的一种机制,这种机制可以根据需求把系列系统任务及其子任务整合(或分隔)到按资源划分等级的不同组内,从而为系统资源管理提供一个统一的框架。 - 作用:限制CPU、内存、网络等所使用的物理资源。
- 具体实现:本质上来说,cgroups 是内核附加在程序上的一系列钩子(hook),通过程序运行时对资源的调度触发相应的钩子以达到资源追踪和限制的目的。
用途如下:
Resource limitation
:限制资源使用,例:内存使用上限/cpu 的使用限制Prioritization
:优先级控制,例:CPU 利用/磁盘 IO 吞吐Accounting
:一些审计或一些统计Control
:挂起进程/恢复执行进程
相关命令
① pidstat:用于检测一个进程的 CPU、内存、IO、线程等资源的占用情况。
sudo apt install sysstat # 安装
pidstat [option] [时间间隔] [次数] # 语法
选项:
-u
:检测 CPU 使用情况(默认)-r
:检测内存使用情况-d
:检测 IO 使用情况-p
:指定进程 PID,如果指定ALL
则监视所有进程-C
:检测通过指定命令启动的进程
示例:
- 默认输出:
pidstat
会输出所有进程的 CPU 占用情况。 - 指定进程:
pidstat -p 1234
检测 PID 为 1234 的进程。 - 指定命令:
pidstat -C bash
检测通过bash
命令启动的进程。 - 检测内存:
pidstat -r
检测内存占用情况。 - 指定检测次数与频率:
pidstat 1 3
每隔一秒检测一次,共检测三次。
简单测试:
lighthouse@VM-8-10-ubuntu:test$ pidstat -C bash
Linux 5.15.0-126-generic (VM-8-10-ubuntu) 07/09/2025 _x86_64_ (2 CPU)
09:19:13 PM UID PID %usr %system %guest %wait %CPU CPU Command
09:19:13 PM 1001 1877180 0.00 0.00 0.00 0.00 0.00 0 bash
lighthouse@VM-8-10-ubuntu:test$ pidstat -r
Linux 5.15.0-126-generic (VM-8-10-ubuntu) 07/09/2025 _x86_64_ (2 CPU)
09:19:20 PM UID PID minflt/s majflt/s VSZ RSS %MEM Command
09:19:20 PM 0 1 0.09 0.00 184012 10948 0.31 systemd
09:19:20 PM 0 366 0.00 0.83 179024 81516 2.34 systemd-journal
09:19:20 PM 0 410 0.00 0.00 289312 27096 0.78 multipathd
09:19:20 PM 0 412 0.00 0.00 12120 5012 0.14 systemd-udevd
09:19:20 PM 101 828 0.00 0.00 16128 1608 0.05 systemd-network
09:19:20 PM 0 843 0.00 0.00 2816 1104 0.03 acpid
09:19:20 PM 103 848 0.00 0.00 8876 4008 0.12 dbus-daemon
09:19:20 PM 0 864 0.00 0.00 234504 4472 0.13 polkitd
② stress:一个压力测试工具,可以对 CPU、内存、IO 等进行压力测试。
sudo apt install stress # 安装
stress [option] # 语法
参数:
-c --cpu N
:产生 N 个进程,每个进程都循环调用sqrt
函数产生 CPU 压力。-m --vm N
:产生 N 个进程,每个进程都循环调用malloc
和free
函数,产生内存压力。
示例:stress -c 1
创建一个进程进行 CPU 压力测试。
- 左侧使用
stress
创建了一个进程进行CPU压力输出 - 右侧检测
stress
产生的压力 - 结果一个进程占满了
100%
的CPU资源
资源控制实战
① 信息查看
cgroups 版本查看
lighthouse@VM-8-10-ubuntu:~$ cat /etc/*release* # 查看当前系统版本 DISTRIB_ID=Ubuntu DISTRIB_RELEASE=22.04 DISTRIB_CODENAME=jammy DISTRIB_DESCRIPTION="Ubuntu 22.04 LTS" PRETTY_NAME="Ubuntu 22.04 LTS" NAME="Ubuntu" VERSION_ID="22.04" VERSION="22.04 (Jammy Jellyfish)" VERSION_CODENAME=jammy ID=ubuntu ID_LIKE=debian HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" UBUNTU_CODENAME=jammy lighthouse@VM-8-10-ubuntu:~$ cat /proc/filesystems |grep cg nodev cgroup nodev cgroup2 # 可以支持两个版本 cgroup
cgroups 子系统查看
lighthouse@VM-8-10-ubuntu:test$ cat /proc/cgroups #subsys_name hierarchy num_cgroups enabled cpuset 0 109 1 cpu 0 109 1 cpuacct 0 109 1 blkio 0 109 1 memory 0 109 1 devices 0 109 1 freezer 0 109 1 net_cls 0 109 1 perf_event 0 109 1 net_prio 0 109 1 hugetlb 0 109 1 pids 0 109 1 rdma 0 109 1 misc 0 109 1
查看挂载信息:这里是每一个资源的控制目录,比如在
/sys/fs/cgroup/cpu
目录下,就是控制CPU资源的配置文件lighthouse@VM-8-10-ubuntu:~$ mount | grep cgroup cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot)
查看一个进程上的 cgroup 限制
lighthouse@VM-8-10-ubuntu:~$ cat /proc/$$/cgroup 0::/user.slice/user-1001.slice/session-83590.scope # 层级编号:挂载的子系统:路径
② 内存控制
创建内存控制组
cd /sys/fs/cgroup/memory mkdir test_memory
设置最大内存
echo "20971520" > test_memory/memory.limit_in_bytes
将进程加入控制组
stress -m 1 --vm-bytes 50m # 每个进程占用 50M pidstat -r echo "15070" > test_memory/tasks
③ CPU 控制
创建 CPU 控制组
cd /sys/fs/cgroup/cpu mkdir test_cpu
设置 CPU 占用率
echo "5000" > test_cpu/cpu.cfs_period_us echo "2000" > test_cpu/cpu.cfs_quota_us
将进程加入控制组
stress -c 1 pidstat -u echo "60769" > test_cpu/tasks