虚拟化和容器化
**物理机:**实际的服务器或者计算机。相对于虚拟机而言的对实体计算机的称呼。物理 机提供给虚拟机以硬件环境,有时也称为"寄主"或"宿主"。
虚拟化:是指通过虚拟化技术将一台计算机虚拟为多台逻辑计算机。在一台计算机上 同时运行多个逻辑计算机,每个逻辑计算机可****运行不同的操作系统,并且应用程序都 可以****在相互独立的空间内运行而互不影响,从而显著提高计算机的工作效率。
容器化:容器化是一种虚拟化技术,又称操作系统层虚拟化,这种技术将操作系统内核虚拟化,可以允许用户空间软件实例 被分割成几个独立的单元,在内核中运行,而不是只有一个单一实例运 行。这个软件实例,也被称为是一个容器。对每个实例的拥有者与用户 来说,他们使用的服务器程序,看起来就像是自己专用的。容器技术是虚拟化的一种。 docker 是现今容器技术的事实标准。
虚拟化类型

虚拟机
存在于硬件层和操作系统层间的虚拟化技术。虚拟机通过**"伪造"**一个硬件抽象接口, 将一个操作系统以及操作系统层以上的层嫁接到硬件上,实现和真实物理机几乎一样 的功能。
容器
存在于操作系统层和函数库层之间的虚拟化技术。容器通过**"伪造"**操作系统的接口, 将函数库层以上的功能置于操作系统上。
JVM
存在于函数库层和应用程序之间的虚拟化技术。Java 虚拟机同样具有跨平台特性,所 谓跨平台特性实际上也就是虚拟化的功劳。我们知道 Java 语言是调用操作系统函数库 的,JVM 就是在应用层与函数库层之间建立一个抽象层,对下通过不同的版本适应不 同的操作系统函数库,对上提供统一的运行环境交给程序和开发者,使开发者能够调 用不同操作系统的函数库。
空间隔离 - namespace
namespace是 Linux 内核用来隔离内核资源的方式。通过 namespace 可以让一些进 程只能看到与自己相关的一部分资源,而另外一些进程也只能看到与它们自己相关的 资源,这两拨进程根本就感觉不到对方的存在。具体的实现方式是把一个或多个进程 的相关资源指定在同一个 namespace 中。
Linux namespaces 是对全局系统资源的一种封装隔离,使得处于不同 namespace 的 进程拥有独立的全局系统资源,改变一个 namespace 中的系统资源只会影响当前 namespace 里的进程,对其他 namespace 中的进程没有影响。
基础命令
dd命令
作用:用于读取、转换并输出数据。
语法:dd OPTION
参数:
▪ if=文件名:输入文件名,默认为标准输入。即指定源文件。
▪ of=文件名:输出文件名,默认为标准输出。即指定目的文件。
▪ ibs=bytes:一次读入 bytes 个字节,即指定一个块大小为 bytes 个字节。
obs=bytes:一次输出 bytes 个字节,即指定一个块大小为 bytes 个字节。
bs=bytes:同时设置读入/输出的块大小为 bytes 个字节。
▪ cbs=bytes:一次转换 bytes 个字节,即指定转换缓冲区大小。
▪ skip=blocks:从输入文件开头跳过 blocks 个块后再开始复制。
▪ seek=blocks:从输出文件开头跳过 blocks 个块后再开始复制。
▪ count=blocks:仅拷贝 blocks 个块,块大小等于 ibs 指定的字节数。
▪ conv=<关键字>,关键字可以有以下 11 种:
conversion:用指定的参数转换文件。
ascii:转换 ebcdic 为 ascii
ebcdic:转换 ascii 为 ebcdic
ibm:转换 ascii 为 alternate ebcdic
block:把每一行转换为长度为 cbs,不足部分用空格填充
unblock:使每一行的长度都为 cbs,不足部分用空格填充
lcase:把大写字符转换为小写字符
ucase:把小写字符转换为大写字符
swap:交换输入的每对字节
noerror:出错时不停止
notrunc:不截短输出文件
sync:将每个输入块填充到 ibs 个字节,不足部分用空(NUL)字符补齐。
▪ --help:显示帮助信息
▪ --version:显示版本信息
bash
# 生成 1 个镜像文件
dd if=/dev/zero of=fdimage.img bs=8k count=10240
#将 testfile 文件中的所有英文字母转换为大写,然后转成为 testfile_1 文件
dd if=testfile_2 of=testfile_1 conv=ucase
mkfs命令
作用:用于在设备上创建 Linux 文件系统,俗称格式化。
语法:mkfs [-V] [-t fstype] [fs-options] filesys [blocks]
参数:
-t fstype:指定要建立何种文件系统;如 ext3,ext4
filesys :指定要创建的文件系统对应的设备文件名;
blocks:指定文件系统的磁盘块数。
-V : 详细显示模式
fs-options:传递给具体的文件系统的参数
bash
#将 sda6 分区格式化为 ext4 格式
mkfs -t ext4 /dev/sda6
#格式化镜像文件为 ext4
mkfs -t ext4 ./fdimage.img
df命令
作用:用于显示目前在 Linux 系统上的文件系统磁盘使 用情况统计。
语法:df [OPTION]... [FILE]...
参数:
○ -a, --all 包含所有的具有 0 Blocks 的文件系统
○ -h, --human-readable 使用人类可读的格式(预设值是不加这个选项的...)
○ -H, --si 很像 -h, 但是用 1000 为单位而不是用 1024
○ -t, --type=TYPE 限制列出文件系统的 TYPE
○ -T, --print-type 显示文件系统的形式
bash
#查看磁盘使用情况
df -h
#查看磁盘的系统类型
df -Th
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: 挂载点的目录
bash
#将 /dev/hda1 挂在 /mnt 之下。
mount /dev/hda1 /mnt
#将镜像挂载到/mnt/testext4 下面,需要确保挂载点也就是目录存在
mkdir -p /mnt/testext4
mount ./fdimage.img /mnt/testext4
unshare命令
作用:创建一个新的命名空间(namespace),并将当前进程或指定的程序运行在这个新的命名空间中。使用与父程序不共享的名称空间运行程序。
语法:unshare [options] program [arguments]
参数:
-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 优先挂载过去
隔离演示
pid隔离
执行命令
bash
#创建隔离pid的命名空间
sudo unshare --pid --fork --mount-proc /bin/bash
解释如下:
--pid 是隔离pid空间。
--fork是执行unshare的进程fork一个新的子进程,在子进程里执行unshare传入的参数。
如果不加--fork,则会报错Can't allocate memory,因为执行 /bin/bash 需要在新的命名空间中执行,需要交由init进程管理。但是unshare不会自动进行fork。
--mount-proc是因为Linux 下的每个进程都有一个对应的 /proc/PID 目录,该目录包含 了大量的有关当前进程的信息。执行子进程前,将 proc 优先挂载过去。
在新的命名空间中使用ps -ef能看到,都是与当前命名空间进程相关的进程,且执行的命令为1号进程。

但是在外部空间中则存在大量进程信息。且1号进程并不是在命名空间中执行的命令。

mount隔离
执行命令
bash
#常见隔离mount的命名空间
unshare --mount --fork /bin/bash
#创建后面挂载的文件的位置
mkdir /data
#添加磁盘挂载
#创建文件
dd if=/dev/zero of=fdimage.img bs=8k count=8
#对文件格式化
mkfs -t ext4 ./fdimage.img
#将格式化文件 挂载到data下面
mount ./fdimage.img /data
在当前命名空间中能看见挂载的磁盘

复制ssh回话,但是外层却看不见这个挂载的磁盘。便实现了mount隔离

资源控制 - cgroups
cgroups(Control Groups) 是 linux 内核提供的一种机制,**这种机制可以根据需求把一系列系统任务及其子任务整合(或分隔)到按资源划分等级的不同组内,从而为系统资源管理提供一个统一的框架。**简单说,cgroups 可以限制、记录任务组所使用的物理资 源。本质上来说,cgroups 是内核附加在程序上的一系列钩子(hook),通过程序运行时 对资源的调度触发相应的钩子以达到资源追踪和限制的目的。
基础了解
pidstat
pidstat 是 sysstat 的一个命令,用于监控全部或指定进程的 CPU、内存、线程、设备 IO 等系统资源的占用情况。Pidstat 第一次采样显示自系统启动开始的各项统计信息, 后续采样将显示自上次运行命令后的统计信息。用户可以通过指定统计的次数和时间 来获得所需的统计信息。
语法:
pidstat [ 选项 ] [ <时间间隔> ] [ <次数> ]
参数:
-u:默认参数,显示各进程的 CPU 使用统计
-r:显示各进程的内存使用统计
-d:显示各进程的 IO 使用情况
-p:指定进程号,ALL 表示所有进程
-C:指定命令
-l:显示命令名和所有参数
安装和卸载
Ubuntu:
bash#卸载 apt remove sysstat -y #安装 apt install sysstat -yCentos:
bash#卸载 yum remove sysstat -y #安装 yum install sysstat -y
stress
stress 是 Linux 的一个压力测试工具,可以对 CPU、Memory、IO、磁盘进行压力测 试。
语法: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-keep:一直占用内存,区别于不断的释放和重新分配(默认是不断释放并重新 分配内存) -d, --hdd N:产生 N 个不断执行 write 和 unlink 函数的进程(创建文件,写入内容,删 除文件)
--hdd-bytes B:指定文件大小
-t, --timeout N:在 N 秒后结束程序
-q, --quiet:程序在运行的过程中不输出信息
安装和卸载
Ubuntu
bash#卸载 apt remove stress -y #安装 apt install stress -yCentos
bash#卸载 yum remove stress -y #安装 yum install stress -y
cgroups信息查看
cgroups版本查看
执行命令
bash
cat /proc/filesystems | grep cgroup
显示信息如下,表示当前内核已启用cgroup v1的"cgroup"文件系统类型 ,但只支持作为虚拟文件系统挂载,并不关联任何块设备。
如果内核同时编译了cgroup2,则会看到单独的一行:nodev cgroup2

cgroups子系统查看
执行命令
cpp
cat /proc/cgroups
显示如下:

cgroups挂载信息查看
执行命令
cpp
mount | grep cgroup
显示如下

查看一个进程上的cgroups限制
执行命令
bash
cat /proc/$$/cgroup
其中$$表示当前进程的pid
显示如下

将挂载信息中查看的cup信息和当前进程组内的cpu信息进行组合查看。
执行命令
cpp
ll /sys/fs/cgroup/cpu,cpuacct/user.slice/
路径的前大部分是来自挂载信息,而后面的信息则来自当前进程组内cpu信息。
效果如下:

cgroups对内存进行控制
1、创建一个控制组cgroup
执行mount | grep cgroup,通过挂载信息找到管理内存的位置

执行cd /sys/fs/cgroup/memory
然后创建一个文件夹。mkdir testMemory

创建完成之后,文件夹内部会有很多文件是自动创建好的。其中
memory.limit_in_bytes 是限制内存的上限
tasks是管理进程pid的文件
2、配置控制组cgroup的内存上限为20M
执行echo "20971520" > testMemory/memory.limit_in_bytes
接下来只要进程pid 在tasks中,进程就会收到改控制组的约束了,其上限字节数就为20M了

3、启动一个消耗进程,该进程占用50M内存
复制2个回话,在一个回话中,执行 stress -m 1 --vm-bytes 50M

在另一个会话中,对这个进程进行监控。执行 pidstat -C stress -p ALL -r 2 100000

4、将消耗50M的进程交给刚刚创建的控制组进行管理
执行echo "2950" > testMemory/tasks命令。

然后另外两个 连接就能看见明显的断开。

还有一个

将消耗50M的进程交给这个只允许进程内存上限为20M的控制组管理,则改消耗进程便会收到这个控制组节制。
cgroups对cpu进行控制
1、创建一个控制组cgroup
执行mount | grep cgroup,通过挂载信息找到管理cpu的位置

执行cd /sys/fs/cgroup/memory
然后创建一个文件夹。mkdir testMemory

创建完成之后,文件夹内部会有很多文件是自动创建好的。其中
cfs_period_us表示系统总 CPU 带 宽 ,默认值 100000。
cfs_quota_us表示 Cgroup 可以使用的 cpu 的带宽,默认值为-1,表示使用的 CPU 不受 cgroup 限制。
2、启动一个消耗进程,该进程打满cpu
执行命令 stress -c 1

3、对该消耗进程进行监控
执行命令 pidstat -C stress -u -p ALL 2

4、配置新建控制组的cpu使用率为20%
cpu 使用率 的计算公式 cfs_quota_us/cfs_period_us
1)cfs_period_us:cfs_period_us 表示一个 cpu 带宽,单位为微秒。系统总 CPU 带 宽 ,默认值 100000。
2)cfs_quota_us:cfs_quota_us 表示 Cgroup 可以使用的 cpu 的带宽,单位为微秒。 cfs_quota_us 为-1,表示使用的 CPU 不受 cgroup 限制。cfs_quota_us 的最小值为1ms(1000),最大值为 1s。
所以我们将 cfs_quota_us的值设置为30000**,从理论上讲就可以限制test_cpu控制的进程的cpu利用率最多是30%****。**
执行命令 echo 20000 > cpu.cfs_quota_us

5、将打满cpu的消耗进程移入该控制组
执行命令 echo "20143" > tasks

查看监控,消耗进程的cpu使用逐渐降至20%左右

LXC
LXC(LinuX Containers)Linux 容器,一种操作系统层虚拟化技术,为 Linux 内核容 器功能的一个用户空间接口。它将应用软件系统打包成一个软件容器(Container), 内含应用软件本身的代码,以及所需要的操作系统核心和库。透过统一的名字空间 和 共享 API来分配不同软件容器的可用硬件资源,创造出应用程序的独立沙箱运行环境, 使得 Linux 用户可以容易的创建和管理系统或应用容器。
LXC 是最早一批真正把完整的容器技术用一组简易使用的工具和模板来极大的简化了 容器技术使用的一个方案
基础知识
常用命令如下:
lxc-checkconfig
检查系统环境是否满足容器使用要求;
格式: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-ls -f
lxc-info
查看容器相关的信息;
格式:lxc-info -n NAME
lxc-attach
进入容器执行命令;
格式:lxc-attach --name=NAME [-- COMMAND]
lxc-stop
停止容器;
格式:lxc-stop -n NAME
lxc-destory
删除处于停机状态的容器;
格式:lxc-destory -n NAME
安装LXC
Ubuntu安装
安装前执行检查看下是否需要卸载,如果需要卸载,执行下面的命令完成卸载,不需 要直接到第 2 步
bash
# 一、检查是否安装。清理资源
systemctl status lxc
lxc-stop -n xxx # lxc-ls -f 遍历所有容器,停止运行的容器
lxc-destroy -n xxx # 删除对应的容器
# 二、 卸载软件
apt-get purge --auto-remove lxc lxc-templates
# 三、 检查服务已经没有该服务了
systemctl status lxc
没有安装的话,执行下面的命令完成安装
bash
#一、安装
#lxc 主程序包
#lxc-templates lxc 的配置模板
#bridge-utils 网桥管理工具
apt install lxc lxc-templates bridge-utils -y
#二、检查服务是否正常运行
systemctl status lxc
Centos安装
安装前执行检查看下是否需要卸载,如果需要卸载,执行下面的命令完成卸载,不需 要直接到第 2 步
bash
# 一、检查是否安装。清理资源
systemctl status lxc #检查是否安装
lxc-stop -n xxx # lxc-ls -f 遍历所有容器,停止对应的容器
lxc-destroy -n xxx #删除对应的容器
# 二、 卸载软件
yum remove lxc lxc-templates lxc-libs lxc-extra libvirt
debootstrap
# 三、检查,提示服务不存在
systemctl status lxc
CentOS 安装 LXC,如果已经安装,可以检查下是否需要卸载,如果需要卸载执行 Centos 卸载 LXC
bash
# 一、 配置源
yum -y install epel-release #这个软件包里包含 epel yum 源和GPG 的配置
# 二、 安装程序
# lxc 主程序包
# lxc-templates lxc 的配置模板
# bridge-utils 网桥管理工具 lxc-libs lxc 所需的库文件
# libcgroup cgroup 安装包
# libvirt 管理 Linux 的虚拟化功能所需的服务器端守护程序。 需要针对特定驱
动程序的管理程序。
# debootstrap debootstrap 是 Debian 引导程序,它允许您将 Debian 基本系统
(例如 Debian 或 Ubuntu)安装到当前正在运行的系统的目录中。
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
systemctl status libvirtd
LXC使用
1、检查lxc是否运行
执行命令 systemctl status lxc

2、检查lxc功能支持情况
执行命令 lxc-checkconfig

3、查看lxc提供的容器模版
执行命令 ls /usr/share/lxc/templates/

4、创建容器
执行命令
lxc-create -t ubuntu --name lxchost1 -- -r xenial -a amd64

5、查看容器的状态
执行命令 lxc-ls -f

6、启动容器
执行命令lxc-start -n lxchost1 -d
-d 选项是在后台运行。否在就在前台运行,需要登录等

7、进入容器,查看容器信息和宿主机信息对比
①使用ip地址进入容器。
执行命令 ssh ubuntu@192.168.122.65

接下来就是和宿主机进行对比了。这里就不演示了。因为这里已经进入容器了,和宿主机也就进行了隔离。
②使用命令进入容器。执行命令 lxc-attach -n lxchost1 --clear-env -- /bin/bash

8、停止容器
执行命令 lxc-stop -n lxchost1

9、删除容器
执行命令 lxc-destroy -n lxchost1

Docker
Docker是一个容器的易用工具,容器实现的本质还是Linux中的namespace和cgroups。
但是Docker也可以理解为是容器虚拟化的实现。
Docker 是基于 Go 语言实现的一个开源项目,它的主要目标是"Build,Ship and Run Any APP,Anywhere",即通过对组件的封装、分发、部署、运行等生命周期的 管理,使得用户的应用及其运行环境能够做到**"一次封装,到处运行"**。
早期 Docker 利用 LXC 做容器管理引擎,但是在创建容器时,不再使用模板去安装生 成,而是通过镜像技术(把一个操作系统用户空间所需要使用到的组件事先编排好, 并整体打包成一个文件,image 文件),镜像文件集中放在一个仓库中。当需要创建容 器时,Docker 调用 LXC 的工具 lxc-create,但不再通过 lxc 的模板去安装,而是连接 到镜像服务器上下载匹配的镜像文件,而后基于镜像启动容器。所以,Docker 极大的 简化了容器的使用难度。以后我们创建启动容器,只需要一个命令,docker-run, docker-stop 就可以启动停止一个容器了。
docker的引擎迭代
Docker 早期是基于LXC 容器管理引擎实现,当后来成熟之后,Docker 自建了一个容 器引擎叫 libcontainer,后来 CNCF 的介入,Docker 又研发了一个工业化标准的容器 引擎 runC,目前所使用的新版 Docker,所使用的容器引擎就是 RunC。
docker和虚拟机的区别
|---------|-----------------------------------|-------------------------------|
| | 传统虚拟机 | Docker容器 |
| 磁盘占用 | 几个 GB 到几十个 GB 左右 | 几十 MB 到几百 MB 左右 |
| cpu内存占用 | 虚拟操作系统非常占用 CPU 和内存,需要通 过虚拟层调用占用率高 | Docker 引擎占用资源极低,直接作用于硬件资源 占用少 |
| 启动速度 | (从开机到运行项目) 几分钟 | (从开启容器到运行项目)几秒 |
| 安装管理 | 需要专门的运维技术 | 安装、管理方便 |
| 应用部署 | 手动部署,速度慢 | 体系化部署,可以自动化,速度快 |
| 隔离性 | 系统级别 | 进程级别 |
| 封装程度 | 打包整个操作系统 | 打包项目代码和依赖信息 |

Docker****为什么比虚拟机资源利用率高,启动快?
docker 有比虚拟机更少的抽象层。docker 不需要 Hypervisor 实现硬件资源虚拟化,运 行在 docker 容器上的程序直接使用的是实际物理机的硬件资源。因此在 cpu、内存利 用率上 docker 将会在效率上有明显的优势。docker 利用的是宿主机的内核,而不需要 Guest OS,节省了 Guest OS 占用的资源。
docker 不需要****Guest OS ,创建一个容器时,不需要和虚拟机一样重新加载一个操作 系统内核。从而避免引寻、加载操作系统内核返回时耗时耗资源的过程,当新建一个 虚拟机时,虚拟机软件需要加载 Guest OS,返回新建过程是分钟级别的。而新建一个 docker 容器只需要几秒钟。
Docker和JVM虚拟化的区别
|-------|-----------------------------------------------------|-------------------------------------|
| | JVM | Docker容器 |
| 性能 | Jvm 需要占用一定的的 CPU 和内存 | 基本没有损失 |
| 虚拟层面 | 基于 JVM 虚拟机,更加上 层 | 基于操作系统,更加通用 |
| 代码无关性 | 一个特定代码的执行平台, 它是运行时才存在的,只能 支撑特定代码的执行,并且 必须是在 jvm 进程内 | 模拟了一整个操作系统,它是静态存在的, 可以支撑任何相同平台的应用程序 |
| 主机隔离性 | jvm 不隔离主机 | 通过命名空间实现隔离 |