【Docker】容器化和虚拟化基础

Docker发展史

Jail(监狱)时代

1979 年 贝尔实验室发明 chroot

chroot的设计原理是:把一个进程的文件系统隔离起来。

​ chroot 系统调用可以将进程及其子进程的根目录更改为文件系统中的新位置。隔离以后,该进程无法访问到外面的文件,因此这个被隔离出来的新环境像监狱一样,被命名为 Chroot Jail (监狱)。后续测试只需要把测试信息放到 Jail 中就可以完成测试了。这一进步是进程隔离的开始:为每个进程隔离文件访问。所以 chroot 可以认为是容器技术的鼻祖。

2000 年 FreeBSD 4.0 发行 FreeBSD Jail

FrsBSD Jail:以实现其服务与其客户服务之间的明确分离,以实现安全性和易于管理。每个 Jail 都是一个在主机上运行的虚拟环境,有自己的文件、进程、用户和超级用户帐户,能够为每个系统分配一个IP 地址。

**FreeBSD Jail 不仅仅有 chroot 的文件系统隔离,并且扩充了独立的进程和网络空间。 **

2001 年 Linux VServer 发行

与 FreeBSD Jails 一样, Linux VServer 是一种监狱机制,可以对计算机系统上的资源(文件系统、网络地址、内存)进行分区。

云时代

云计算的目的:需要处理海量数据、超高并发、快速扩展等问题,**此时不仅仅需要隔离还需要能够对资源进行控制和调配。 **

image-20230623141539788

2006 年 google 推出 Process Containers

​ Process Containers(由 Google 于 2006 年推出)旨在限制、统计和隔离一组进程的资源使用(CPU、内存、磁盘 I/O、网络)。一年后它更名为“Control Groups(cgroups)”。

2008 年 LXC 推出

​ LXC(Linux 容器)是 Linux 容器管理器的第一个、最完整的实现。它是在 2008 年使用 cgroups 和 Linux 命名空间实现的,它可以在单个 Linux 内核上运行,不需要任何补丁。

​ 同时 Google 在 GAE 中使用了 Borg (Kubernetes 的前身)来对容器进行编排和调度。LXC 和 Borg 其实就相当于最早的 docker 和 k8s 。

2011 年 CloudFoundry 推出 Warden

​ 2011 年启动了 Warden,早期使用 LXC,后来替换为自己的实现,直接对 Cgroups 以及 Linux Namespace 操作。开发了一个客户端-服务器模型来管理跨多个主机的容器集合,并且可以管理 cgroups、命名空间和进程生命周期。

2013 年 Docker 推出到风靡全球

​ Docker 在初期与 Warden 类似,使用的也是 LXC,之后才开始采用自己开发的 libcontainer 来替代 LXC,它是将应用程序及其依赖打包到几乎可以在任何服务器上运行的容器的工具。与其他只做容器的项目不同的是, Docker 引入了一整套管理容器的生态系统,这包括高效、分层的容器镜像模型、全局和本地的容器注册库、清晰的 REST API、命令行等等。

​ Docker 为提供了一整套的解决方案,不仅解决了容器化问题,而且解决了分发问题,很快被各大厂商选择变成了云基础设施,厂商围绕 Docker 也开始了生态建设。

云原生时代

​ 云原生是一种构建和运行应用程序的方法,是一套技术体系和方法论。云原生(CloudNative)是一个组合词,Cloud+Native。Cloud表示应用程序位于云中,而不是传统的数据中心;Native表示应用程序从设计之初即考虑到云的环境,原生为云而设计,在云上以最佳姿势运行,充分利用和发挥云平台的弹性+分布式优势。

k8s

​ 容器只是解决了容器化,分发问题,但是一个软件的网络问题、负载均衡问题、监控、部署、更新、镜像管理、发布等很多问题并没有有效的解决。Google 内部调度系统 Borg 已经拥有 10 多年的使用容器经验,在 2014 年 6 月推出了开源的 K8S,可以支持对容器的编排和管理,完成生态的闭环。

image-20230623142206552

最后发展的结果是:Kubernetes 已成了容器编排领域的绝对标准, Docker 已成容器事实的标准。containerd成为CRI(Container Runtime Interface 容器运行时接口) 实现标准。

容器虚拟化基础

虚拟化和容器化概念

什么是虚拟化和容器化?

物理机:实际的服务器或者计算机。相对于虚拟机而言的对实体计算机的称呼。物理机提供给虚拟机以硬件环境,有时也称为“寄主”或“宿主 。

虚拟化:是指通过虚拟化技术将一台计算机虚拟为多台逻辑计算机。在一台计算机上同时运行多个逻辑计算机,每个逻辑计算机可运行不同的操作系统,并且应用程序都可以在相互独立的空间内运行而互不影响,从而显著提高计算机的工作效率。

容器化:容器化是一种虚拟化技术,又称操作系统层虚拟化(Operating system levelvirtualization),这种技术将操作系统内核虚拟化,可以允许用户空间软件实例(instances)被分割成几个独立的单元,在内核中运行,而不是只有一个单一实例运行。这个软件实例,也被称为是一个容器(containers)。对每个实例的拥有者与用户来说,他们使用的服务器程序,看起来就像是自己专用的。容器技术是虚拟化的一种。docker 是现今容器技术的事实标准。

为什么需要容器化和虚拟化?

资源利用率高:将利用率较低的服务器资源进行整合,用更少硬件资源运行更多业务,降低 IT 支出和运维管理成本。

环境标准化:一次构建,随处执行。实现执行环境的标准化发布,部署和运维。开发过程中一个常见的问题是环境一致性问题。由于开发环境、测试环境、生产环境不一致,导致有些bug 并未在开发过程中被发现。而 Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性。

资源弹性伸缩:根据业务情况,动态调整计算、存储、网络等硬件及软件资源。比如遇到双 11 了,把服务扩容 100 个,双 11 过去了, 把扩容的 100 个收回去。

差异化环境提供:同时提供多套差异化的执行环境,限制环境使用资源 。

image-20230623144904482

容器对比虚拟机更轻量,启动更快: 传统的虚拟机技术启动应用服务往往需要数分钟,而 Docker 容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。

虚拟化

应用程序执行环境分层

  • 硬件层:提供硬件抽象,包括指令集架构、硬件设备及硬件访问接口
  • 操作系统层 :提供系统调用接口,管理硬件资源
  • 程序库层:提供数据结构定义及函数调用接口

image-20230623145543331

虚拟化实现分类

虚拟机:存在于硬件层和操作系统层间的虚拟化技术。 虚拟机通过“伪造”一个硬件抽象接口,将一个操作系统以及操作系统层以上的层嫁接到硬件上,实现和真实物理机几乎一样的功能。比如我们在一台 Windows 系统的电脑上使用 Android 虚拟机,就能够用这台电脑打开 Android 系统上的应用。

容器:存在于操作系统层和函数库层之间的虚拟化技术。 容器通过“伪造”操作系统的接口,将函数库层以上的功能置于操作系统上。

**以 Docker 为例,其就是一个基于 Linux 操作系统的 Namespace 和 Cgroup 功能实现的隔离容器,可以模拟操作系统的功能。**简单来说,如果虚拟机是把整个操作系统封装隔离,从而实现跨平台应用的话,那么容器则是把一个个应用单独封装隔离,从而实现跨平台应用。

**JVM之类的虚拟机 ** :JVM 就是在应用层与函数库层之间建立一个抽象层,对下通过不同的版本适应不同的操作系统函数库,对上提供统一的运行环境交给程序和开发者,使开发者能够调用不同操作系统的函数库。

常见虚拟化的实现

主机虚拟化(虚拟机)实现

主机虚拟化的原理是通过在物理服务器上安装一个虚拟化层来实现。这个虚拟化层可以在物理服务器和客户操作系统之间建立虚拟机,使得它们可以独立运行。

从软件框架的角度上,根据虚拟化层是直接位于硬件之上还是在一个宿主操作系统之上,将虚拟化划分为 Type1 和 Type2。

image-20230623150640996

Type1:Type1 类的 Hypervisor(Hypervisor 是一种系统软件,它充当计算机硬件和虚拟机之间的中介,负责有效地分配和利用由各个虚拟机使用的硬件资源,这些虚拟机在物理主机上单独工作,因此, Hypervisor 也称为虚拟机管理器。 )直接运行在硬件之上,没有宿主机操作系统, Hypervisor 直接控制硬件资源和客户机。典型框架为 Xen、 VmwareESX。

Type2:Type2 类的 Hypervisor 运行在一个宿主机操作系统之上(Vmware Workstation)或者系统里面,Hypervisor 作为宿主机操作系统中的一个应用程序,客户机就是在宿主机操作系统上的一个进程。

容器虚拟化实现

容器虚拟化,有别于主机虚拟化,是操作系统层的虚拟化。**通过 namespace 进行各程序的隔离,加上 cgroups 进行资源的控制,以此来进行虚拟化。 **

Docker三要素

docker的三要素:镜像、容器、仓库。

镜像和容器

​ docker的镜像(image)就是一个只读模板。镜像可以用来创建Docker容器,一个镜像可以创建多个容器。

从面向对象的角度

​ image就相对于一个root文件系统。比如官方镜像centos:7,就包含了完整的一套centos:7最小系统的root文件系统。docker镜像文件就相当于C++中的类模板,而容器实例化(docker run)类似于C++中new出来的实例对象。

image-20230326000245267

从镜像容器角度

​ 可以把容器看做是一个简单的Linux环境(包括root用户权限、进程空间、用户空间和网络空间)和运行在其中的应用程序。

docker run redis1
docker run redis2

image-20230623145204938

仓库

仓库:是集中存放镜像文件的场所。

docker公司提供的官方reqistry被称为Docker Hub,存放各种镜像模板的地方。

仓库分为公开仓库和私有仓库两种形式。最大的公开仓库是Docker Hub(https://hub.docker.com/),存放了数量庞大的镜像供用户下载。国内公开的仓库有阿里云、网易云等。

docker执行流程

image-20230623145347858

docker入门图解

image-20230326004308546

docker运行的原理

​ docker是一个Client-Server结构的系统,Docker守护进程运行在主机上,然后通过Socket连接从客户端访问,守护进程从客户端接受命令并管理运行在主机上的容器。

image-20230326004909959

docker平台架构图解

Docker 是一个 C/S 模式的架构,后端是一个松耦合架构,众多模块各司其职。

docker运行的基本流程为:

image-20230326010427129

image-20230326010450423

namespace

什么是namespace

​ namespace 是 Linux 内核用来隔离内核资源的方式。通过 namespace 可以让一些进程只能看到与自己相关的一部分资源,而另外一些进程也只能看到与它们自己相关的资源,这两拨进程根本就感觉不到对方的存在。具体的实现方式是把一个或多个进程的相关资源指定在同一个 namespace 中。

​ Linux namespaces 是对全局系统资源的一种封装隔离,使得处于不同 namespace 的进程拥有独立的全局系统资源,改变一个 namespace 中的系统资源只会影响当前namespace 里的进程,对其他 namespace 中的进程没有影响。

​ Linux 提供了多个 API 用来操作 namespace,它们是 clone()、 setns() 和 unshare() 函数,为了确定隔离的到底是哪项 namespace,在使用这些 API 时,通常需要指定一些调用参数: CLONE_NEWIPC、 CLONE_NEWNET、 CLONE_NEWNS、CLONE_NEWPID、 CLONE_NEWUSER、 CLONE_NEWUTS 和CLONE_NEWCGROUP。如果要同时隔离多个 namespace,可以使用 | (按位或)组合这些参数。

namesp ace系统调用参数被隔离的全局系统资源引入内 核版本
UTSCLONE_NEWUTS主机名和域名2.6.19
IPCCLONE_NEWIPC信号量、消息队列和共享内存 - - 进程间通信2.6.19
PIDCLONE_NEWPID进程编号2.6.24
NetworkCLONE_NEWNET网络设备、网络栈、端口等2.6.29
MountCLONE_NEWNS文件系统挂载点2.4.19
UserCLONE_NEWUSER用户和用户组3.8

以上命名空间在容器环境下的隔离效果:

  • UTS:每个容器能看到自己的 hostname,拥有独立的主机名和域名。
  • IPC:同一个 IPC namespace 的进程之间能互相通讯,不同的 IPC namespace 之间不能通信。
  • PID:每个 PID namespace 中的进程可以有其独立的 PID,每个容器可以有其 PID 为1 的 root 进程。
  • Network:每个容器用有其独立的网络设备, IP 地址, IP 路由表, /proc/net 目录,端口号。
  • Mount:每个容器能看到不同的文件系统层次结构。
  • User:每个 container 可以有不同的 user 和 groupid

namespace之dd命令

Linux dd命令用于读取、转换并输出数据,主要作用是转换和拷贝文件。

dd可从标准输入或者文件中读取数据,根据指定的格式来抓换数据,再输入到文件、设备或者标准输出。

dd OPTION

image-20230623214933065

字段描述
if代表输入文件。如果不指定if,默认就会从stdin中读取输入。
of代表输出文件。如果不指定 of,默认就会将 stdout 作为默认输出
bs代表字节为单位的块大小
count代表被复制的块数
/dev/zero是一个字符设备,会不断返回 0 值字节(\0)
conv=ucase把字母由小写变为大写
conv=lcase把字母由大写变为小写
conv=noerror出差时不停止处理

参考案例

mkdir tmp
dd if=/dev/zero of=./tmp/dd_test1 bs=8k count=1024

image-20230623220425328

dd命令的作用是:可以快速清空磁盘数据,磁盘备份。

dd if=/dev/zero of=/dev/sbd

拷贝一个文件

dd if=./dd_test2 of=./dd_test3 conv=ucase

image-20230623221937766

/dev/zero介

​ /dev/zero是一个特殊的字符设备文件,当我们使用或读取它的时候,它会提供无限连续不断的空的数据流(特殊的数据格式流)主要使用场景有两个:

  • 可以使用/dev/zero文件覆盖其他文件信息,比如本文中的清空磁盘
  • 产生指定大小的空文件,例如:交换文件、模拟虚拟文件系统等。

namespace之mkfs命令

用于在设备上创建Linux文件系统,俗称格式化,比如我们的U盘就可以进行格式化

语法:

shell 
mkfs [options] [-t <type>] [fs-options] <device> [<size>]

image-20230623222357657
参数说明:

参数说明描述
-t --type文件系统类型;若不指定,将使用 ext2
fs -options实际文件系统构建程序的参数
device要使用的设备路径
size要使用设备上的块数
-V --verbose解释正在进行的操作;多次指定 -V 将导致空运行(dry-run)
-V --version显示版本信息并退出,将 -V 作为 --version 选项时必须是惟一选项
-h --help显示此帮助并退出

案例:指定分区格式化为ext4格式

mkfs -t ext4 ./dd_test1

image-20230623223311040

案例:格式化指定磁盘2G空间

mkfs /dev/sdb 2G

namespace之df命令

Linux df(英文全程:disk free)命令用于显示目前在Linux系统上的系统磁盘使用情况。

语法:

Shell 
df [OPTION]... [FILE]...

image-20230623223744105

参数说明描述
-a–all 包含所有的具有0 Blocks的文件系统。
-h–human-readable 使用人类可读的格式
-H–si 很像-h参数,但是用1000单位而不是1024
-t–type=TYPE 限制列车文件系统的TYPE
-T–print-type 显示文件系统的格式
df

image-20230623231417359

namespace之mount命令

Linux挂载》一节讲到,所有的硬件设备必须挂载之后才能使用,只不过,有些硬件设备(比如硬盘分区)在每次系统启动时会自动挂载,而有些(比如 U 盘、光盘)则需要手动进行挂载。

mount命令用于加载文件系统到指定的加载点。此命令也常用于挂载光盘,使我们可以访问光盘中的数据,因为将光盘插入光驱在,Linux并不会自动挂载,必须使用Linux mount命令手动挂载。

Linux系统下不同目录可以挂载不同分区和磁盘设备,它的目录和磁盘分区是分离的,可以通过挂载进行自由组合。

不同的目录数据可以跨越不同的磁盘分区或者不同的磁盘设备。

挂载的实质是为了磁盘添加入口

mount语法

mount [-l]
mount [-t vfstype] [-o options] device dir

参数:

  • -l:显示已加载的文件系统列表。

  • -t:加载文件系统类型支持常见系统类型ext3、ext4、iso9660、tmpfs、xfs等,大部分清空可以不指定,mount可以自己识别。

  • -o:options 主要用来描述设备或档案的挂接方式。

    • loop:用于把一个文件当成硬盘分区挂接上系统。
    • ro:采用只读方式挂接设备。
    • rw:采用读写方式挂接设备。
  • device:要挂接(mount)的设备。

  • dir:挂载点的目录

mkdir -p ./data/testmymount
mount ./dd_test1 ./data/testmymount
df -h | grep data/testmymount

image-20230624000802261

namespace之unshare命令

unshare主要的能力是使用与父进程不共享的名称空间运行程序。

语法:

shell 
unshare [options] program [arguments]
参数说明描述
-i --ipc不共享IPC空间,信号量、消息队列和共享内存的隔离
-m --mount不共享Monut空间,不同的文件系统层次结构。
-n --net不共享Net空间,具有独立的网络资源。
-p --pid不共享PID空间
-u --uts不共享UTS空间
-U --user不共享用户空间
-V --version版本查看
– fork执行unshare的进程fork创建一个新的在子进程,在子进程里执行unshare传入参数
–mount -proc执行子进程钱,先将proc优先挂载过去

案例–UTS隔离

unshare -u /bin/bash
hostname testunshare1
hostname

image-20230624003326264

通过unshare命令,我们实现了UTS级别的进程隔离。

namespace实战:实现进程隔离

隔离两个进程需要怎么办?

(1)首先容器进程与进程之间需要隔离,所以需要 PID 隔离

(2)首先容器 A 进程不能读取容器 B 进程通讯内容需要隔离信号量等,所以需要 IPC隔离

(3)首先容器 A 进程不能读取容器 B 进程的文件,所以需要 Mount 隔离

(4)首先容器 A 进程不能读取容器 B 进程的 socket,所以需要网络隔离、主机隔离

(5) Docker 允许用户在主机和容器间共享文件夹,同时不需要限制容器的访问权限,

这就容易让容器突破资源限制。需要借助用户空间来完成用户之间的隔离。

PID隔离

使用ps -ef,可以看到进程列表,进程ID为1的进程为:

image-20230624004828415

unshare -p /bin/bash

image-20230624004626840

这里报错的原因:

unshare创建的新的namespace会用unshare的PID作为新的空间的父进程。但是unshare进程不在新的namespace中,所以会出现cannot allocate memory错误。

解决办法:–fork创建一个新的进程。

unshare -p --fork /bin/bash

image-20230624010210825

使用ps -ef查看PID,我们发现并没有实现PID级别的隔离,为什么?

原因:

在 Linux 系统中,/proc 目录是一个位于内存中的伪文件系统。该目录下保存的并不是真正的文件和目录,而是一些【运行时】的信息,如 CPU 信息、负载信息、系统内存信息、磁盘 IO 信息等。Linux下的每一个进程都要一个对应的/proc/PID目录。对于一个PID namespace而言,/proc目录只包含当前namespace和它所有子孙后代namespace里的进程信息。

创建新的PID namespace后,如果想让子进程中的top,ps等依赖/proc文件系统的命令工作,还需要挂载/proc文件系统。【一开始的ps,top等依赖挂载的是原来namespace的/proc目录系统】

unshare -p --fork --mount-proc /bin/bash

image-20230624011258265

实现PID级别隔离成功。

namespace实战:实现mount隔离

mount隔离是通过隔离文件系统挂载点,实现文件系统级别的隔离。

案例:

unshare --mount /bin/bash

因为mount隔离的是文件系统挂载点,所以我们需要挂载一个文件挂载点。

创建一个文件块、格式化并挂载到目录下

mkdir -p data/mymount2
dd if=/dev/zero of=./mount2 bs=8k count=1024
mkfs -t ext4 ./mount2
mount ./mount2 ./data/mymount2

image-20230624013545676

查看挂载点,观察其他进程是否可以看到mymount2挂载点。

结果是其他进程无法看到mymount2挂载点。

image-20230624014411491

实现mount级别隔离成功。

向挂载的目录中创建文件,观察现象:

在该目录下创建文件,并查看文件内容。在该进程中可以看到文件和文件中的内容。

image-20230624014650202

在其他进程中,无法看到文件和文件的内容。通过mount进行文件挂载点的隔离,可以实现文件系统的隔离操作。

image-20230624014926164

cgroups

cgroups基本概念

什么是cgroups?

cgroups(Control Groups) 是 linux 内核提供的一种机制, 这种机制可以根据需求把一系列系统任务及其子任务整合(或分隔)【进程】到按资源划分等级的不同组内,从而为系统资源管理提供一个统一的框架。

​ 简单说, cgroups 可以限制、记录任务组所使用的物理资源。本质上来说, cgroups 是内核附加在程序上的一系列钩子(hook),通过程序运行时对资源的调度触发相应的钩子以达到资源追踪和限制的目的。

为什么需要cgroups?

​ cgroups可以做到对 cpu,内存等资源实现精细化的控制。目前越来越火的轻量级容器Docker 及 k8s 中的 pod 就使用了 cgroups 提供的资源限制能力来完成 cpu,内存等部分的资源控制。

​ 比如在一个既部署了前端 web 服务,也部署了后端计算模块的八核服务器上,可以使用 cgroups 限制 web server 仅可以使用其中的六个核,把剩下的两个核留给后端计算模块。

cgroup的用途:

  • Resource limitation: 限制资源使用,例:内存使用上限/cpu 的使用限制
  • Prioritization: 优先级控制,例: CPU 利用/磁盘 IO 吞吐
  • Accounting: 一些审计或一些统计
  • Control: 挂起进程/恢复执行进程

cgroup可控制的系统:

blkio对块设备的 IO 进行限制。
cpu限制 CPU 时间片的分配
cpuacct生成 cgroup 中的任务占用 CPU 资源的报告,与 cpu 挂载在同 一目录。
cpuset给 cgroup 中的任务分配独立的 CPU(多处理器系统) 和内存节 点。
devices限制设备文件的创建,和对设备文件的读写
freezer暂停/恢复 cgroup 中的任务。
memory对 cgroup 中的任务的可用内存进行限制,并自动生成资源占用 报告。
perf_event允许 perf 观测 cgroup 中的 task
net_clscgroup 中的任务创建的数据报文的类别标识符,这让 Linux 流量 控制器(tc 指令)可以识别来自特定 cgroup 任务的数据包,并 进行网络限制。
hugetlb限制使用的内存页数量。
pids限制任务的数量。
rdma限制 RDMA 资源(Remote Direct Memory Access,远程直接数 据存取)

pidstat监视工具

pidstat概念

pidstat是sysstat的一个命令,用于监控全部或指定进程的CPU、内存、线程、设备、IO等系统资源的占用情况。

Pidstat第一次采样显示自系统启动开始的各项统计信息,后续采样将显示自上次运行命令后的系统信息。用户可以通过指定系统的次数和时间来获得所需的统计信息。

语法

pidstat [选项] [<时间间隔>] [<次数>]

参数:

  • -u:默认参数,显示各进程的CPU使用统计。
  • -r:显示各进程的内存使用统计。
  • -d:显示各进程的IO使用情况。
  • -p:指定进程号,ALL表示所有进程。
  • -C :指定命令。
  • -l:显示命令名和所有参数。

下载

yum install sysstat

image-20230624140900067

案例:实现监控mysql内存的使用情况,每秒监控一次,统计1000次:

pidstat -r -p ALL -C mysql -l 1 1000

image-20230624141559102

stress压测工具

stress概述

stress是Linux的一个压力测试工具,可以对CPU、内存、IO、磁盘进行压力测试。

语法:

shell
stress [OPTION [ARG] ]

-c, --cpu N:产生N个进程,每个进程都循环调用sqrt函数产生CPU压力。
-i, --io N:产生N个进程,每个进程循环调用sync将内存缓冲区内容写到磁盘上,产生IO压力。通过系统调用sync刷新内存缓冲区数据到磁盘中,以确保同步。如果缓冲区内数据较少,写到磁盘中的数据也较少,不会产生IO压力。在SSD磁盘环境中尤为明显,很可能iowait总是0,却因为大量调用系统调用sync,导致系统CPU使用率sys 升高。
-m, --vm N:产生N个进程,每个进程循环调用malloc/free函数分配和释放内存。
–vm-bytes B:指定分配内存的大小
–vm-stride B:不断的给部分内存赋值,让COW(Copy On Write)发生
–vm-hang N :指示每个消耗内存的进程在分配到内存后转入睡眠状态N秒,然后释放内存,一直重复执行这个过程
–vm-keep:一直占用内存,区别于不断的释放和重新分配(默认是不断释放并重新分配内存)
-d, --hdd N:产生N个不断执行write和unlink函数的进程(创建文件,写入内容,删除文件)
–hdd-bytes B:指定文件大小

–hdd-noclean:不要将写入随机ASCII数据的文件Unlink
-t, --timeout N:在N秒后结束程序
–backoff N:等待N微秒后开始运行
-q, --quiet:程序在运行的过程中不输出信息
-n, --dry-run:输出程序会做什么而并不实际执行相关的操作
–version:显示版本号
-v, --verbose:显示详细的信息

CPU压力测试工具

先使用pidstat启动一个监控进程,指定监控stress命令:

pidstat监控与stress命令相关的所有进程,查看CPU的使用情况,每两秒监控一次,共监控1000次

pidstat -C stress -p ALL -u 2 1000

image-20230624145137952

开启两个CPU进程执行sqrt,60秒后结束。

stress --cpu 2 --timeout 60

image-20230624145243835

查看监控结果:

image-20230624145322487

IO测试

开启2个IO进程,执行sync系统调用,刷新内存缓冲区到磁盘。

stress --io 2 --timeout 60

image-20230624150217998

​ 使用stress无法模拟iowait升高,但sys升高。stress -i参数表示通过系统调用sync来模拟IO问题,但sync是刷新内存缓冲区数据到磁盘中,以确保同步。如果内存缓冲区内没多少数据,读写到磁盘中的数据也就不多,没法产生IO压力。使用SSD磁盘的环境中尤为明显,iowait一直为0,但因为大量系统调用,导致系统CPU使用率sys升高。

stress --io 2 --hdd 2 --timeout 60s

–hdd 2:产生2个不断执行write和unlink函数的进程(创建文件,写入内容,删除文件)。这样就避免了iowait一直是0的情况。

image-20230624150703637

Memory测试

pidstat监控与stress命令相关的所有进程,查看CPU和Memory的使用情况,每两秒监控一次,共监控1000次

pidstat -C stress -p ALL -u -r 2 1000

开启2个进程分配内存,每次分配100MB内存,保持100秒后释放,100秒后退出。

stress --vm 2 --vm-bytes 100M --vm-hang 100 --timeout 100s

image-20230624151500857

磁盘IO

开启两个进程,每次向磁盘写入10M的数据。等待20000微秒后运行。

stress --hdd 2 --hdd-bytes 10M --backoff 20000

image-20230624152942411

cgroups的信息查看

cgroups版本查看

cat /proc/filesystems | grep cgroup

image-20230624154055919

如果看到cgroup2,说明支持cgroup v2

cgroups子系统查看

cat /proc/cgroups

image-20230624154238238

上面的内容,表示cgroups支持对哪些资源进行控制。

cgroups挂载信息查看

可以看到默认存储位置为/sys/fs/cgroup

mount | grep cgroup

image-20230624154516466

查看一个进程的cgroup限制

查看当前进程的cgroup限制:$$表示当前进程的pid

cat /proc/$$/cgroup

image-20230624154917015

比如:想要查看当前进程的cgroup的CPU限制信息,只需要在默认存储位下/sys/fs/cgroup,查找对应的限制资源目录即可:

image-20230624155356187

查看该进程的CPU时间片:

cat /sys/fs/cgroup/cpu,cpuacct/user.slice/cpu.cfs_period_us

image-20230624155539777

cgroup实战:控制内存

第一步:向cgroup中添加内存管理组。

mount | grep cgourp
cd /sys/fs/cgroup/memory
mkdir temp_memory

image-20230624161933186

ll ./temp_memory

image-20230624162114217

该cgroup内存组的配置如上,改变上述配置文件即可管理该资源组。比如tasks管理哪些进程在该资源组中,memory_limit_in_bytes限制了进程可使用的最大的内存大小。

image-20230624162440980

第二步:使用stress创建一个进程,不断的开辟40M的内存;并使用pidstat监视进程的内存使用情况。

pidstat -C stress -p ALL -r 2 1000
stress --vm 2 --vm-bytes 40M  --vm-hang 10

image-20230624164430892

第三步:将temp_memory资源组的内存最大限制为20M,并将9714进程加入到资源组中;观察现象。

echo "20971820" > memory.limit_in_bytes
echo "9714" > tasks

image-20230624164622326

进程启动失败,原因是cgroup限制了资源组的最大内存大小。

cgroup实战:控制CPU使用率

第一步:向cgroup中添加cpu管理组。

mount | grep cgroup
cd /sys/fs/cgroup/cpu
mkdir test_cpu

image-20230624165245083

第二步:使用stress创建一个进程,打满cpu使用;并使用pidstat监视进程的cpu使用情况。cfs_period_us表示一个cpu带宽,cfs_quota_us表示cgroup可以使用的cpu的带宽。

stress -c 1
pidstat -C stress -p ALL -u 2 1000

image-20230624165638752

第三步:将test_cpu资源组的cpu使用率最大限制为30%,并将进程加入到资源组中;观察现象。

设置cgroup使用率为30%,cpu使用率的计算公式为:cfs_quota_us/cfs_period_us

  • cfs_period_us:表示一个cpu带宽,单位为微秒。系统总CPU带宽,默认值为100000
  • cfs_quota_us:表示cgroup可以使用的cpu的带宽,单位为微秒。cfs_period_us为-1,表示使用cpu不受crgoup限制。cfs_quota_us的最小值为1ms(100),最大值为1s。

将cfs_quota_us的值修改为30000,就可以限制test_cpu的cpu使用率为30%。

echo "30000" > cpu.cfs_quota_us
echo "15095" > tasks

image-20230624170659902

stress进程的cpu使用率被限制到30%。

LXC

LXC基本概念

什么是LXC?

​ LXC(LinuX Containers) Linux 容器,一种操作系统层虚拟化技术,为 Linux 内核容器功能的一个用户空间接口。它将应用软件系统打包成一个软件容器(Container),内含应用软件本身的代码,以及所需要的操作系统核心和库。

image-20230623145204938

​ 透过统一的名字空间和共享 API 来分配不同软件容器的可用硬件资源,创造出应用程序的独立沙箱运行环境,使得 Linux 用户可以容易的创建和管理系统或应用容器 。

​ LXC 是最早一批真正把完整的容器技术用一组简易使用的工具和模板来极大的简化了容器技术使用的一个方案

LXC的问题?

​ LXC 虽然极大的简化了容器技术的使用,但比起直接通过内核调用来使用容器技术,其复杂程度其实并没有多大降低,因为我们必须要学会 LXC 的一组命令工具,且由于内核的创建都是通过命令来实现的,通过批量命令实现数据迁移并不容易。其隔离性也没有虚拟机那么强大。同时LXC的跨平台跨主机的效果并不是很好。

后来就出现了 docker,所以从一定程度上来说, docker 就是 LXC 的增强版。

image-20230624171742771

LXC的基本命令

lxc-checkconfig:检查系统环境是否满足容器使用要求。

lxc-create:创建lxc容器

lxc-create -n NAME -t TEMPLATE_NAME [--template-options]

lxc-start:启动容器;格式:lxc-start -n NAME -d

lxc-ls:列出所有容器;-f表示打印常用信息

lxc-info:查看容器相关的信息;格式:lxc-info -n NAME

lxc-attach:进入容器执行命令;格式:lxc-attach --name=NAME [ --COMMAND ]

lxc-stop:停止容器;格式:lxc-stop -n NAME

lxc-destroy:删除处于停机状态的容器;lxc-destory -n NAME

LXC的安装

yum -y install lxc lxc-templates bridge-utils lxc-libs libcgroup libvirt lxc-extra debootstrap

# 启动和检查
systemctl start lxc #启动lxc服务
systemctl start libvirtd #启动虚拟机监控服务
systemctl status lxc     #检查lxc是否启动
systemctl status libvirtd # 检查lxc虚拟机监控服务是否启动

image-20230624180901843

可以看到我们的lxc服务已经成功启动

image-20230624181135168

lxc虚拟机监控服务已经成功启动。

LXC容器操作

首先先检查LXC的系统环境是否支持

lxc-checkconfig

image-20230624181425350

查看lxc支持哪些模板:

ll /usr/share/lxc/templates/

image-20230624182010745

第一步:创建一个ubuntu环境,命名容器名为lxchost1,CPU架构为amd64。

lxc-create -t ubuntu --name lxchost2 -- -r xenial -a amd64

image-20230624185150077

默认的user和password都是ubuntu。

第二步:启动容器

lxc-ls -f  #查看有哪些可用的容器。

image-20230624185230742

lxc-start -n lxchost1 -d  #启动容器,-d表示在后台运行
lxc-ls -f  #查看有哪些可用的容器。

image-20230624185919991

第三步:进入容器查看信息

方法一:通过ssh连接ubuntu容器

ssh ubuntu@192.168.122.152

image-20230624190031377

查看两台机器的区别:

ls /

image-20230624190207580

可以看到ubuntu和宿主机已经发生了隔离。

方法二:lxc-attach进入容器

 lxc-attach -n lxchost1 --clear-env -- /bin/bash
 ps -ef

image-20230624190501981

可以看到容器和宿主机发生了隔离效果。

第四步:容器的停止和删除

lxc-ls -f
lxc-stop -n lxchost1

image-20230624190814967

容器已经停止运行。

删除容器时,需要保证容器已经停止运行。

lxc-destroy -n lxchost1

image-20230624191043447

lxchost1容器已经被成功删除。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

影中人lx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值