docker虚拟化,容器化的原理与实现

虚拟化,容器化

是什么

  • 物理机:指的是实际的服务器或者计算机,相对于虚拟计算机而言对实体计算机的称呼,有时候被称为'寄主'或者'宿主'。
  • 虚拟机:通过虚拟化计数将一台计算机虚拟为多台逻辑计算机,在一台计算机上可以运行多个逻辑计算机,每一个逻辑计算机中都可以运行不同的操作系统,并且应用程序都可以在相互独立的空间里运行而不受外界影响。
  • 容器化:容器化是一种虚拟化计数,是在操作系统层面上的虚拟化,这种计数将操作系统内核虚拟化,将用户空间软件分割为几个独立的单元,在内核中运行,而不是只有单一的实例在运行,这个软件实例被称为是一个容器。

原因

  • 资源利用率高:将利用率较低的服务器资源进行整合,用更少的硬件运行更多的业务,降低管理成本。
  • 环境标准化:仅需要一次构建就可以多次执行,实现执行环境的标准化发布,在开发代码的时候,由于开发环境,测试环境和生产环境不一致,会导致很多bug并未在开发的时候发现,docker镜像提供了除内核外完整的运行时环境,确保了应用运行环境的一致性。
  • 资源可伸缩:根据业务情况,可以动态的调整计算,存储,网络等硬件资源,当遇到并发量高的场景,可以扩充大量的资源,在不需要使用的时候再将服务收回去。
  • 差异化环境提供:一个应用只能在centos上运行,另外一个应用只能在Ubuntu上运行,但是没有预算购买两个虚拟机,容器化可以很好的解决这种场景。
  • 沙箱安全:在一个容器里执行的危险操作,不会影响到其他的容器,确保安全。
  • 容器更轻量:容器对比虚拟机来说,更轻量,直接运行于宿主内核,无需启动完整的操作系统,可以做到毫秒级别的启动速度。
  • 维护和扩展容易:docker使用分层存储和镜像技术,使得重复部分的复用更为容易,应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得简单。

分类

虚拟机 :存在于硬件层和操作系统层间的虚拟化技术。虚拟机通过伪造一个硬件抽象接口,将一个操作系统以及操作系统层以上的层嫁接到硬件上,实现和真实物理机几乎一样的功能,比如说在一台Windows操作系统上使用安卓虚拟机,就可以使用安卓上的应用。
容器 :存在于操作系统层和函数库层之间的虚拟化技术。容器通过伪造操作系统的接口,将函数库层以上的功能置于操作系统上,以Docker为例,就是一个基于Linux操作系统的Namespace和Cgroup功能实现的隔离容器,可以模拟操作系统的功能。简单来说,如果虚拟机是把整个操作系统封装隔离,从而实现跨平台应用的话,那么容器则是把一个个应用单独封装隔离,从而实现跨平台应用。所以容器体积比虚拟机小很多,理论上占用资源更少。容器化就是应用程序级别的虚拟化技术。
JVM 之类的虚拟机:存在于函数库层和应用程序之间的虚拟化技术。Java 虚拟机同样具有跨平台特性,所谓跨平台特性实际上也就是虚拟化的功劳。我们知道 Java 语言是调用操作系统函数库的JVM就是在应用层与函数库层之间建立一个抽象层,对下通过不同的版本适应不同的操作系统函数库,比如说在Windows上运行,调用的是Windows上的API,在Linux上运行,调用的就是Linux上的API。

实现

主机虚拟化实现

主机虚拟化的原理是通过在物理服务器上安装一个虚拟化层来实现。这个虚拟化层可以在物理服务器和客户操作系统之间建立虚拟机,使得它们可以独立运行。从软件框架的角度上,根据虚拟化层是直接位于硬件之上还是在一个宿主操作系统之上,将虚拟化划分为Type1和Type2。

  • Type1类的Hypervisor(Hypervisor是一种系统软件,它充当计算机硬件和虚拟机之间的中介,负责有效地分配和利用由各个虚拟机使用的硬件资源,这些虚拟机在物理主机上单独工作。
  • Type2类的Hypervisor运行在一个宿主机操作系统之上或者系统里面,Hypervisor作为宿主机操作系统中的一个应用程序,客户机就是在宿主机操作系统上的一个进程。

容器虚拟化实现

原理

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

也就是说,在启动一个进程的时候,将此进程需要的操作系统文件及进程的文件打包在一起,就是容器的虚拟化,根据配置文件来在主机上配置不同的系统,不同的资源,比如说进程A配置Ubuntu操作系统,规定只能使用2G内存,进程B配置centos操作系统,规定只能使用4G内存,这两个容器底层共用同一个内核。

namespace

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

隔离
命令

dd命令:dd命令用于从文件或者标准输入中读取数据,根据指定的格式转换数据,再将数据输出到文件,设备或者标准输出。

bash 复制代码
dd option

dd命令的参数

比如说我想创建一个8M大小的文件:

mkfs命令:此命令用于在设备上创建Linux文件系统,也就是俗称的格式化。

bash 复制代码
mkfs [-V] [-t fstype] [fs-options] filesys [blocks]

mkfs命令参数

比如说我们想把一个文件格式化为一个磁盘:

df命令:此命令用于显示目前在Linux系统上文件系统磁盘使用情况统计。

bash 复制代码
df [option]... [file]...

df命令参数

mount命令:用于加载文件系统到指定的加载点,比如说我们要往windows电脑上插入一个U盘,文管对话框里就会多出一个磁盘,这就是挂载,实质就是为磁盘添加入口,Linux上无法自动挂载。

bash 复制代码
mount -l
mount [-t vfstype] [-o options] device dir

mount命令参数

unshare命令:主要能力是使用与父程序不共享命名空间运行程序。

bash 复制代码
unshare [programs] option [argument]

-i, --ipc # 隔离 IPC 命名空间
-m, --mount # 隔离挂载命名空间
-n, --net # 隔离网络命名空间
-p, --pid # 隔离 PID 命名空间
-u, --uts # 隔离 UTS 命名空间
-U, --user # 隔离用户命名空间
-f, --fork # 创建子进程执行目标程序
--mount-proc # 在新 PID 命名空间中挂载 /proc 文件系统
-r, --map-root-user # 将当前用户映射为命名空间内的 root 用户

比如说修改一个主机名,子进程的主机名修改后,外部的主机名不会变化。

操作

pid隔离

  • 在主机上执行ps -ef可以看到进程的列表如下。
  • 打开另外一个shell,执行命令创建一个bash进程,并且新建一个namespace。
bash 复制代码
unshare --fork --pid --mount-proc /bin/bash

--fork创建了一个bash进程,因为如果不创建新进程,创建的namespace就会用unshare的pid作为新的空间的父进程,而这个unshare进程并不会在namespace中,就会出现报错。

--pid表示我们隔离的是进程的pid,而其他的命名空间没有隔离。

--mount-proc用于给容器挂载/proc文件系统,因为Linux下每个进程都有一个/proc/PID目录,包含大量当前进程相关的信息,没有挂载这个目录,top/ps等依赖于/proc文件系统命令工作的命令就无法工作。

执行ps -ef可以发现,此时/proc下的进程内容已经变了,启动进程也变为bash进程,和主机上的进程空间隔离开了。

mount隔离

  • 在shell窗口执行df -h,查看主机默认命名空间磁盘挂载情况。
  • 执行mount隔离命令,和隔离pid相同的,这里也需要--fork为新的命名空间分配一个祖先进程。
bash 复制代码
unshare --mount --fork /bin/bash
  • 添加新的磁盘挂载。
bash 复制代码
dd if=/dev/zero of=test.img bs=8k count=1024
mkfs -t ext4 ./test.img
mount ./test.img /data/tmpmount

可以看到在新的namespace里已经挂载上了/data/tmpmount,在执行echo "hello world" > /data/tmpmount/test.txt以后,在两个窗口执行ls指令,看到的文件系统是不一样的,表示mount已经发生了隔离。

cgroup

控制
命令

pidstat:pidstat用于监控全部或者指定进程的CPU,内存,线程,设备IO等系统资源占用的情况。

bash 复制代码
pidstat [选项] [<时间间隔>] [<次数>]

如果需要安装此命令,可以使用以下命令:

bash 复制代码
#卸载
apt remove sysstat -y
#安装
apt install sysstat -y

pidstat语法及参数
stress:是Linux的一个压力测试根据,可以对CPU,内存,IO和磁盘进行压力测试。

bash 复制代码
stress [option [arg]]

stress的语法与参数

操作

查看cgroups信息

  • 查看cgroups版本。
bash 复制代码
cat /proc/filesystems | grep cgroup
  • cgroupv1和cgroupv2的查看
bash 复制代码
#查看cgroupv1
cat /proc/cgroups
#查看cgroupv2
cat /sys/fs/cgroup/cgroup.controllers
  • cgroups挂载信息
bash 复制代码
cd /sys/fs/cgroup
mount | grep cgroup
  • 查看一个进程上的cgroup限制
bash 复制代码
#查看当前进程的cgroup
cat/proc/$$/cgroup
#找到自己cpu的目录,里面有对init进程的详细限制信息
ll /sys/fs/cgroup/user.slice/user-1001.slice/session-25400.scope

使用cgroup对内存进行控制

  • 查看当前控制,确认memory是支持的。
bash 复制代码
cat /sys/fs/cgroup/cgroup.controllers
  • 创建cgroup子目录,目录里会自动分配需要的文件。
  • 配置cgroup的策略为最大使用20M内存。
  • 启动一个消耗内存的进程,分配50M的内存,将此进程的pid移动到cgroup策略。
bash 复制代码
#启动进程
stress -m 1 --vm-bytes 50M
#查看进程id及状态,-r表示监控内存,-C stress表示监控进程名为stress的进程
#-p ALL表示监控的进程范围,1表示时间间隔为1s,10000表示持续10000次
pidstat -r -C stress -p ALL 1 10000
#将进程的id移动到cgroup策略
echo pid >> cgroup.procs

可以看到进程无法申请到足够内存退出

LXC

命令

lxc-checkconfig :用于检查系统环境是否满足容器的使用要求,没有参数。
lxc-create:用于创建lxc容器,-n表示容器的唯一名称,-t表示创建容器的模板名称,例如Ubuntu,debian等,option表示参数,例如模板的专属配置项,例如Ubuntu模板对应的参数可以是--release 22.04。

bash 复制代码
lxc-create -n NAME -t TEMPLATE_NAME [--template-options]

lxc-start:用于启动容器,-n表示名字,-d表示后台启动容器。

bash 复制代码
lxc-start -n NAME -d

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

bash 复制代码
lxc-ls -f

lxc-info:查看容器相关的信息,-n表示容器名字。

bash 复制代码
lxc-info -n NAME

lxc-attach:用于进入对应的容器。

bash 复制代码
lxc-attach -name=NAME[-Command]

lxc-stop:停止容器。

bash 复制代码
lxc-stop -n NAME

lxc-destroy:删除处于停机状态的容器。

bash 复制代码
lxc-destroy -n NAME
安装LXC

Ubuntu下安装:

bash 复制代码
#检查是否安装,清理资源
systemctl status lxc
lxc-stop -n xxx
lxc-destroy -n xxx
#卸载软件
apt-get purge --auto-remove lxc lxc-templates
#安装软件
apt install lxc lxc-templates bridge-utils -y
#检查服务是否正常运行
systemctl status lxc
操作
  • 检查lxc功能支持情况。
bash 复制代码
lxc-checkconfig
  • 查看lxc提供的容器模板
bash 复制代码
ls /usr/share/lxc/templates
  • 创建一个lxc虚拟主机。
bash 复制代码
lxc-create -t ubuntu -name lxcname1 -- -r xenial -a amd64
#启动容器
lxc-start -n lxcname1
  • 查看创建的容器信息。
bash 复制代码
lxc-ls -f
  • 查看容器的详细信息。
bash 复制代码
lxc-info -n lxcname1
  • 查看到容器的ip,通过ssh进入容器,查看对应的ip地址,磁盘挂载信息,目录信息等,和宿主机都不一样。
bash 复制代码
ssh ubuntu@ip
  • 在容器外执行命令。
bash 复制代码
lxc-attach -n lxcname1 --clear-env --echo "hello world"
  • 停止容器
bash 复制代码
lxc-stop -n lxcname1
lxc-ls -f
  • 删除容器
bash 复制代码
lxc-destroy -n lxcname1
lxc-ls -f
相关推荐
艾学习2 小时前
安卓arm7l架构下Termux安装picoclaw
linux·运维·服务器
wanhengidc2 小时前
网页版云手机的功能
大数据·运维·服务器·分布式·科技·智能手机
草莓熊Lotso2 小时前
Linux 进程间通信之 System V 共享内存:IPC 的原理与实战
linux·运维·服务器·c语言·数据库·c++·人工智能
tongxh4232 小时前
Nginx搭建负载均衡
运维·nginx·负载均衡
物联网软硬件开发-轨物科技2 小时前
【技术白皮书】开关柜运维的“代际跨越”:全栈数字化如何重塑一键顺控新范式
运维
艾莉丝努力练剑2 小时前
【Linux:文件 + 进程】进程间通信进阶(1)
linux·运维·服务器·网络·c++·人工智能·进程
菜萝卜子2 小时前
【k8s】K8s网络打通:CalicoBGP模式对接OpenWrt/FRR实战
网络·容器·kubernetes
蓝天星空2 小时前
docker部署 asp.net程序
docker·容器·asp.net
IMPYLH2 小时前
Linux 的 cat 命令
linux·运维·服务器