东莞网站设计排行榜,语言可以做网站吗,京东网上商城购物,天律网站建设Docker#xff1a;namespace环境隔离  CGroup资源控制 Docker虚拟机容器 namespace相关命令ddmkfsdfmountunshare 进程隔离文件隔离 CGroup相关命令pidstatstresscgroup控制 内存控制CPU控制 Docker 
在开发中#xff0c;经常会遇到环境问题#xff0c;比如程序依赖某个… Dockernamespace环境隔离  CGroup资源控制 Docker虚拟机容器 namespace相关命令ddmkfsdfmountunshare 进程隔离文件隔离 CGroup相关命令pidstatstresscgroup控制 内存控制CPU控制   Docker 
在开发中经常会遇到环境问题比如程序依赖某个库库又要具体的版本以及某些函数必须在指定平台使用这就会为开发带来很大的麻烦。 
为此有人提出采用虚拟化技术为软件虚拟出一个环境。就像是在一个冰天雪地的地方建了一个花房养花花房内温度湿度都刚刚好将花房与外部的冰雪隔离开。 
这种实现环境隔离的技术主要有虚拟机和容器两种Docker属于容器化的隔离技术。 
虚拟机 
所谓虚拟机其实就是把一台物理主机虚拟为多台逻辑上的计算机。多台虚拟机共用物理上的同一台计算机而每个逻辑上的计算机可以运行不同的操作系统安装不同的库从而提供不同的环境。 如图上图红色部分与蓝色部分是两个不同的虚拟机虚拟机技术在硬件层之上在操作系统层就开始进行隔离。虚拟机通过伪造一个硬件的抽象接口把操作系统嫁接到硬件上。 在硬件层与操作系统层之间会存在一个虚拟化层这其实就是一个软件该层会负责分配硬件资源。 
可以看出如果想要创建多个虚拟机就要在一台物理主机上跑多个操作系统这其实要不小的开销而容器是一种更加轻量的隔离技术。 容器 
容器也是一种虚拟化的实现技术它在操作系统之上进行环境隔离每个容器可以有自己的一套工具和库但是它们共享操作系统的内核 如图红色和蓝色区域是两个不同的容器它们的网络文件系统等等都是隔离的互不影响。 
因为使用同样的操作系统内核所以它们的系统调用接口自然就相同但是基于相同的系统调用接口配置了不同的库不同的文件系统那么最后两个容器就不同。 比如说上图可以通过容积隔离技术在一个centOS操作系统上运行不同版本的Ubuntu容器。这听起来很异想天开但其实不然。 
每个容器有自己独立的用户空间这包括文件系统、库和用户级工具。用户操作接口是用户在容器内操作系统时接触到的命令行工具、库和应用。因此可以在上层的容器中执行Ubuntu的命令虽然内核是CentOS的。 
相比于虚拟机技术容器化技术非常轻量容器相当于一个跑在操作系统上的进程启动一个进程的速度是非常快的通过容器技术只需几秒钟就在主机上打开一个新的操作系统。 
而容器化技术目前最流行的实现方案就是Docker。 
那么容器化技术是如何实现各种资源的隔离的对Linux来说它依赖于namespace和CGroup技术这两个技术是由Linux内核提供的。 
namespace实现进程文件系统用户等资源隔离CGroup实现CPU内存网络等资源隔离 namespace 
namespace 是 Linux 内核用来隔离内核资源的方式。通过 namespace 可以让进程只能看到与自己相关的一部分资源不同namespace的进程感觉不到对方的存在。 
具体的实现方式是把一个或多个进程的相关资源指定在同一个 namespace 中。namespaces 是对全局系统资源的一种封装隔离使得处于不同 namespace 的进程拥有独立的全局系统资源改变一个namespace 中的系统资源只会影响当前namespace 里的进程对其他namespace 中的进程没有影响。 
常见namespace 
namespace隔离资源UTS主机名和域名IPC进程间通信信号量、消息队列、共享内存PID进程NetWork网络设备端口等Mount文件系统User用户 
解释 
UTS每个UTS namespace都可以有自己独立的主机和域名IPC每个IPC namespace内部的进程可以进行进程间通信但是不能跨越IPC namespace进行通信在逻辑上这算跨主机通信PID每个PID namespace都有自己独立的进程pid系统不同的PID namespace可以出现相同的pidNetWork每个NetWork namespace都有自己独立的网络设备IP地址路由表端口号等Mount每个Mount namespace有自己的文件系统互相不能看到对方的文件User每个User namespace都有自己独立的用户和用户组 相关命令 
dd 
dd可以从指定输入流读取数据并输出到指定输出流。 
参数 
if文件名从指定文件读取数据如果不指定则默认从标准输入读取of文件名输出数据到指定文件如果不指定则默认输出到标准输出bsxxx设定一个块block的大小counblocks仅仅从输入流拷贝blocks个块结合上一个参数可以指定要读取的数据个数 
创建一个指定大小的空文件 
dd if/dev/zero oftest.txt bs1M count80以上指令就是创建了一个80M的空文件输入流是/dev/zero这是一个会不断产生空白字符的文件也就是ASCII中字符编码为0的字符。使用这个文件作为输入流可以快速初始化一个空文件。 可以看到最后创建了一个大小为83886080 byte的文件其实就是80 M。 mkfs 
mkfs用于在设备上创建一个文件系统俗称格式化。 
mkfs [option] filesys [blocks]选项 
-t要创建的文件系统类型比如ext3、ext4 
其中filesys是被格式化的文件而block是文件系统的磁盘块数可以省略。 
把刚才创建的文件进行格式化 
mkfs -t ext4 test.txt这样就把刚才的空文件进行了格式化变成了一个文件系统。 df 
df用于显示Linux中的文件系统磁盘使用情况。 
选项 
-h以更加可视化的形式输出默认情况下数据以字节为单位加上该选项后会自动转化为GBMB等单位-T显示文件系统的类型 查看当前的文件系统可以看到其不包含刚才格式化的test.txt因为他只是一个被格式化的文件还没有被挂载。 mount 
mount用于挂载文件系统相当于给文件系统一个访问入口。 
比如说你在电脑上插入一个U盘它往往会显示为E盘或者其它盘符。这个盘符就是一个访问入口因为U盘本身就是一个文件系统如果想要访问这个文件系统的内容Windows自然要提供一个入口因此它自动分配一个E盘让用户可以通过E盘访问U盘。 
同样的刚才格式化test.txt为一个文件系统现在要将其挂载起来才能访问这个文件系统。 
mount [option] device dirdevice被挂载的文件dir挂载到的位置 
选项 
-t挂载文件的类型比如ext3、ext4但是可以不填mount会自动识别文件系统的类型 
把刚才的test.txt挂载到当前/mymount目录下 首先创建一个空目录/mymount随后把test.txt挂载到这个目录下随后可以看到/mymount出现了新的内容。 
随后通过df -t ext4查看系统的文件系统可以看到/dev/loop0文件系统被挂载到了/mymount下说明成功挂载了一个文件系统。 
如果想要删除这个文件系统可以执行 
umount /mymount以上所有命令是在完成一个文件系统的创建便于后续测试文件系统的隔离性。 
接下来看看Linux提供的创建namespace的命令 
unshare 
unshare用于执行一个进程并且为这个进程提供一个独立的namespace。 
unshare [option] program program要执行的程序 
选项 
-i --ipc不共享IPC空间-m --mount不共享Mount空间-n --net不共享Net空间-p --pid不共享PID空间-u --uts不共享UTS空间-U --user不共享用户空间--fork创建一个子进程执行program--mount-proc挂载一个新的/proc到命名空间内 
此处这个--fork有一点点绕因为ushare这个命令本身也是一个进程是在宿主机环境运行的。 
如果直接执行unshare流程如下 
unshare 创建一个新的命名空间unshare 执行 program但是没有创建新的进程而是直接用 program 替换了 unshare 本身 以上unshare不带有--fork参数执行了/bin/bash进程。进入namespace后可以看到/bin/bash的父进程是-bash。这个-bash就是宿主机的bash进程。因为unshare是在bash中执行的所以unshare的父进程是-bash。最后将/bin/bash替换unshare/bin/bash的父进程就是原先的父进程-bash。 
这种情况下看不到unshare进程因为/bin/bash就是原先的unshare。 
如果加上--fork参数流程如下 
unshare 创建一个新的命名空间unshare 在新的命名空间中创建一个新的子进程来执行 program。unshare本身不退出 加上--fork参数后/bin/bash的父进程就变成了unshareunshare的父进程是-bash。也就是说unshare创建了一个子进程来执行program而不是亲自执行program。 
如果你跟着操作了此时还处于namespace中要通过exit来退出不然会影响后续操作。 进程隔离 
现在尝试进行进程隔离也就是隔离通过--pid选项完成 
unshare --fork --pid --mount-proc /bin/bash以上命令用于创建一个新的PID命名空间并执行bash进程也就是执行一个新的命令行。 
此处要加上--fork选项因为要进行进程隔离而unshare本身是宿主机的进程如果直接让unshare本身去执行program那么program就不在新的namespace中导致错误。 以上示例中因为没有加上--fork报错了。 
如果只加上--fork选项此时还是不能观察到进程隔离。因为top、ps等进程监控的命令是依赖于/proc/PID这个目录的。但是命令没有进行文件系统隔离所以还是会使用宿主机的/proc/PID目录导致namespace内部可以看到外部的进程。 
为此unshare命令专门提供了一个参数--mount-proc在新的namespace挂载一个独立的/proc目录方便进行进程的监控。 
进程隔离结果 可以看到创建了新的namespace后ps -aux只能查到两个进程一个是bash一个是grep。这就将namespace内部的进程与宿主机的进程隔离开了。 文件隔离 
想要进行文件隔离创建一个新的namespace然后在里面创建一个文件系统并挂载。再在外部的宿主机查看是否可以看到这个文件系统。 
创建一个新的文件隔离命名空间 
unshare --mount --fork /bin/bash创建一个指定大小的空文件 
dd if/dev/zero ofdata.img bs1M count80格式化文件为文件系统 
mkfs -t ext4 data.img挂载文件系统 
mkdir /mymount
mount -t ext4 data.img /mymount最后通过df -t ext4可以看到文件系统已经挂载成功了。 
打开一个新的终端执行df -t ext4 此时左右终端看到的文件系统不同左侧的namespace内挂载的新文件系统右侧终端看不到了这就是文件隔离。 
此时在namespace中执行exit就会退出这个bash进而退出namespace在其内部创建的文件挂载的文件系统都会自动销毁。 当exit退出后再次查看文件系统也找不到刚才挂载的文件系统了。这和刚才两个终端的情况不同之前是不同namespace之间的文件隔离而此处是退出namespace后文件系统已经被销毁了。 CGroup 
cgroup的可以把一系列任务也就是进程划分到一个任务组并且限制一个任务组的资源占用。比如可以限制一系列任务最多占用多少CPU占用多少内存等等。 
也就是说namespace完成了不同容器之间环境的隔离而cgroup完成了每个容器资源的访问限制。 
相关命令 
为了方便测试CPU与内存压力此处使用两个工具分别完成产生压力以及压力检测。 
pidstat 
pidstat用于检测一个进程的CPU、内存、IO、线程等等资源的占用情况。 
需要下载 
apt install sysstat语法 
pidstat [option] [时间间隔] [次数]选项 
-u检测CPU使用情况默认就是该选项-r检测内存使用情况-d检测IO使用情况-p指定进程pid如果指定ALL则监视所有进程-C检测通过指定命令启动的进程 
直接执行pidstat 此时会输出所有进程默认输出CPU占用情况也就是%CPU这一栏。 
通过-p指定进程 通过-C指定进程 此处指定bash进程。 
通过-r检测内存 此处%MEM栏就是内存占用情况。 
指定检测次数与频率 此处的1 3表示每隔一秒检测一次一共检测三次。 stress 
stress是一个压力测试工具可以对CPU、内存IO等进行压力测试。 
这个工具也要下载 
apt install stress语法 
stress [option]参数 
-c --cpu N产生N个进程每个进程都循环调用sqrt函数产生CPU压力-m --vm N产生N个进程每个进程都循环调用malloc free函数产生内存压力 
示例 左侧使用stress创建了一个进程进行CPU压力输出右侧检测stress产生的压力结果一个进程占满了100%的CPU资源。 cgroup控制 
接下来看看如何操作cgroup它并没有现成的指令来控制而是需要操控配置文件来完成。 
/proc/cgroups查看cgroup支持的资源控制 在/proc/cgroups文件中包含了cgroup所支持的资源控制的类型比如CPU、内存等。 
查看cgroup挂载信息 
mount | grep cgroup这里就是每一个资源的控制目录比如在/sys/fs/cgroup/cpu目录下就是控制CPU资源的配置文件。 内存控制 
创建一个内存的控制组很简单跳转到目录/sys/fs/cgroup/memory然后创建一个目录 此处创建了一个test_memory目录这就算创建了一个test_memory内存控制组。进入目录后可以看到这个目录被初始化了很多文件其中memory.limit_in_bytes这个文件就是这个cgroup可以使用的最大内存数目以字节为单位。 
想要限制这个cgroup的最大内存数量直接往文件写入数据即可 
echo 20971520  memory.limit_in_bytes此处20971520其实就是20 mb此后这个cgroup的最大内存占用就不会超过20 mb。 
那么要如何把一个进程加入控制组这里有一个task文件只需要把进程的PID写入到这个文件中那么一个进程就算加入了这个cgroup。 如图创建一个进程占用50m的内存 
stress -m 1 --vm-bytes 50m随后在另一个端口通过pidstat查看stress的PID为15070和15069其中15069是控制进程15070是真正在产生内存压力的进程。将15070写入tasks文件中让其加入cgroup。 
结果左侧的stress退出了无法产生50m的压力。这是因为一开始就限制了cgroup只能占用最多20 m的内存。一旦进程加入后就会受到限制从而崩溃。 CPU控制 
创建一个CPU的控制组也一样跳转到目录/sys/fs/cgroup/cpu然后创建一个目录 此处创建了一个test_cpu目录也就死和创建了一个test_cpuCPU控制组。这个目录同样被初始化了很多文件。 
其中控制CPU占用的是cpu.cfs_period_us和cpu.cfs_quota_us这两个文件共同完成CPU资源限制。其中cpu.cfs_period_us作为分母cpu.cfs_quota_us作为分子以百分比的形式限制CPU。 
比如cpu.cfs_period_us内填入5000cpu.cfs_quota_us内填入2000。那么该cgroup最多可以占用2000/5000也就是40%的CPU资源。要注意的是这两个文件的最小值都是1000不允许使用比1000更小的数字进行配置。另外的cpu.cfs_quota_us的默认值为-1表示可以占用100%的CPU。 
另一个就是tasks文件同样的只要把PID写入这个文件就算加入了这个CPU控制组。 
启动一个stress进程 由于stress本身就会尽可能占满CPU右侧输出窗口每隔一秒输出stress的状态其一直保持100%的CPU资源占用。 
左下角窗口先限制了test_cpu这个控制组的CPU最大占用率是2000/10000也就是20%。 
随后把stress的PID20849写入tasks 写入后从右边的窗口可以看出stress的CPU占用率立马下降100%到37%最后稳定在20%。 
可以cgroup成功对进程的CPU进行了限制。 容器化技术在Linux中基于namespace和cgroup实现namespace完成不同容器之间的环境隔离而cgroup完成多个容器对资源的占用限制合理分配资源。这就是容器化技术以及docker的最基本原理也是底层依赖。