Docker:namespace环境隔离 CGroup资源控制

Docker:namespace环境隔离 & CGroup资源控制

    • Docker
      • 虚拟机
      • 容器
    • namespace
      • 相关命令
        • dd
        • mkfs
        • df
        • mount
        • unshare
      • 进程隔离
      • 文件隔离
    • CGroup
      • 相关命令
        • pidstat
        • stress
        • cgroup控制
      • 内存控制
      • CPU控制


Docker

在开发中,经常会遇到环境问题,比如程序依赖某个库,库又要具体的版本,以及某些函数必须在指定平台使用,这就会为开发带来很大的麻烦。

为此,有人提出采用虚拟化技术,为软件虚拟出一个环境。就像是在一个冰天雪地的地方建了一个花房养花,花房内温度湿度都刚刚好,将花房与外部的冰雪隔离开。

这种实现环境隔离的技术,主要有虚拟机和容器两种,Docker属于容器化的隔离技术。

虚拟机

所谓虚拟机,其实就是把一台物理主机虚拟为多台逻辑上的计算机。多台虚拟机共用物理上的同一台计算机,而每个逻辑上的计算机可以运行不同的操作系统,安装不同的库,从而提供不同的环境。

在这里插入图片描述

如图,上图红色部分与蓝色部分是两个不同的虚拟机,虚拟机技术在硬件层之上,在操作系统层就开始进行隔离。虚拟机通过伪造一个硬件的抽象接口,把操作系统嫁接到硬件上。

在这里插入图片描述

在硬件层与操作系统层之间,会存在一个虚拟化层,这其实就是一个软件,该层会负责分配硬件资源。

可以看出,如果想要创建多个虚拟机,就要在一台物理主机上跑多个操作系统,这其实要不小的开销,而容器是一种更加轻量的隔离技术。


容器

容器也是一种虚拟化的实现技术,它在操作系统之上进行环境隔离,每个容器可以有自己的一套工具和库,但是它们共享操作系统的内核

在这里插入图片描述

如图,红色和蓝色区域,是两个不同的容器,它们的网络,文件系统等等都是隔离的,互不影响。

因为使用同样的操作系统内核,所以它们的系统调用接口自然就相同,但是基于相同的系统调用接口,配置了不同的库,不同的文件系统,那么最后两个容器就不同。

在这里插入图片描述

比如说上图,可以通过容积隔离技术在一个centOS操作系统上,运行不同版本的Ubuntu容器。这听起来很异想天开,但其实不然。

每个容器有自己独立的用户空间,这包括文件系统、库和用户级工具。用户操作接口是用户在容器内操作系统时接触到的命令行工具、库和应用。因此可以在上层的容器中,执行Ubuntu的命令,虽然内核是CentOS的。

相比于虚拟机技术,容器化技术非常轻量,容器相当于一个跑在操作系统上的进程,启动一个进程的速度是非常快的,通过容器技术,只需几秒钟就在主机上打开一个新的操作系统。

而容器化技术,目前最流行的实现方案就是Docker

那么容器化技术是如何实现各种资源的隔离的?对Linux来说,它依赖于namespaceCGroup技术,这两个技术是由Linux内核提供的。

  • namespace:实现进程,文件系统,用户等资源隔离
  • CGroup:实现CPU,内存,网络等资源隔离

namespace

namespaceLinux 内核用来隔离内核资源的方式。通过 namespace 可以让进程只能看到与自己相关的一部分资源,不同namespace的进程感觉不到对方的存在。

具体的实现方式是把一个或多个进程的相关资源指定在同一个 namespace 中。namespaces 是对全局系统资源的一种封装隔离,使得处于不同 namespace 的进程拥有独立的全局系统资源,改变一个namespace 中的系统资源只会影响当前namespace 里的进程,对其他namespace 中的进程没有影响。

常见namespace

namespace隔离资源
UTS主机名和域名
IPC进程间通信:信号量、消息队列、共享内存
PID进程
NetWork网络设备,端口等
Mount文件系统
User用户

解释:

  1. UTS:每个UTS namespace都可以有自己独立的主机和域名
  2. IPC:每个IPC namespace内部的进程可以进行进程间通信,但是不能跨越IPC namespace进行通信,在逻辑上这算跨主机通信
  3. PID:每个PID namespace都有自己独立的进程pid系统,不同的PID namespace可以出现相同的pid
  4. NetWork:每个NetWork namespace都有自己独立的网络设备,IP地址,路由表,端口号等
  5. Mount:每个Mount namespace有自己的文件系统,互相不能看到对方的文件
  6. User:每个User namespace都有自己独立的用户和用户组

相关命令

dd

dd可以从指定输入流读取数据,并输出到指定输出流。

参数:

  • if=文件名:从指定文件读取数据,如果不指定,则默认从标准输入读取
  • of=文件名:输出数据到指定文件,如果不指定,则默认输出到标准输出
  • bs=xxx:设定一个块block的大小
  • coun=blocks:仅仅从输入流拷贝blocks个块,结合上一个参数,可以指定要读取的数据个数

创建一个指定大小的空文件:

dd if=/dev/zero of=test.txt bs=1M count=80

以上指令,就是创建了一个80M的空文件,输入流是/dev/zero,这是一个会不断产生空白字符的文件,也就是ASCII中字符编码为0的字符。使用这个文件作为输入流,可以快速初始化一个空文件。

在这里插入图片描述

可以看到,最后创建了一个大小为83886080 byte的文件,其实就是80 M


mkfs

mkfs用于在设备上创建一个文件系统,俗称格式化。

mkfs [option] filesys [blocks]

选项:

  • -t:要创建的文件系统类型,比如ext3ext4

其中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 dir
  • device:被挂载的文件
  • dir:挂载到的位置

选项:

  • -t:挂载文件的类型,比如ext3ext4,但是可以不填,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,流程如下:

  1. unshare 创建一个新的命名空间
  2. 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参数,流程如下:

  1. unshare 创建一个新的命名空间
  2. unshare 在新的命名空间中创建一个新的子进程来执行 program
  3. 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选项,此时还是不能观察到进程隔离。因为topps等进程监控的命令,是依赖于/proc/PID这个目录的。但是命令没有进行文件系统隔离,所以还是会使用宿主机的/proc/PID目录,导致namespace内部可以看到外部的进程。

为此,unshare命令专门提供了一个参数--mount-proc,在新的namespace挂载一个独立的/proc目录,方便进行进程的监控。

进程隔离结果:

在这里插入图片描述

可以看到,创建了新的namespace后,ps -aux只能查到两个进程,一个是bash,一个是grep。这就将namespace内部的进程与宿主机的进程隔离开了。


文件隔离

想要进行文件隔离,创建一个新的namespace,然后在里面创建一个文件系统并挂载。再在外部的宿主机查看是否可以看到这个文件系统。

  1. 创建一个新的文件隔离命名空间
unshare --mount --fork /bin/bash
  1. 创建一个指定大小的空文件
dd if=/dev/zero of=data.img bs=1M count=80
  1. 格式化文件为文件系统
mkfs -t ext4 data.img
  1. 挂载文件系统
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查看stressPID,为1507015069,其中15069是控制进程,15070是真正在产生内存压力的进程。将15070写入tasks文件中,让其加入cgroup

结果左侧的stress退出了,无法产生50m的压力。这是因为一开始就限制了cgroup只能占用最多20 m的内存。一旦进程加入后,就会受到限制,从而崩溃。


CPU控制

创建一个CPU的控制组也一样,跳转到目录/sys/fs/cgroup/cpu,然后创建一个目录:

在这里插入图片描述

此处创建了一个test_cpu目录,也就死和创建了一个test_cpuCPU控制组。这个目录同样被初始化了很多文件。

其中控制CPU占用的是cpu.cfs_period_uscpu.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中基于namespacecgroup实现,namespace完成不同容器之间的环境隔离,而cgroup完成多个容器对资源的占用限制,合理分配资源。这就是容器化技术,以及docker的最基本原理,也是底层依赖。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/455202.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VirtualBox虚拟机桥接模式固定ip详解

VirtualBox虚拟机桥接模式固定ip详解 VirtualBox 桥接设置Ubuntu 24.04使用固定IP问题记录 VirtualBox 桥接设置 为什么设置桥接模式?桥接模式可以实现物理机和虚拟机互相通信,虚拟机也可以访问互联网(推荐万金油),物…

AudioSegment 提高音频音量 - python 实现

一些采集的音频声音音量过小可以通过 AudioSegment 实现音量增强。 按照 python 库: pip install AudioSegment 代码具体实现: #-*-coding:utf-8-*- # date:2024-10 # Author: DataBall - XIAN # Function: 音频增加音量import os from pydub import …

网络安全领域推荐证书介绍及备考指南

在网络安全领域,拥有专业认证不仅可以证明个人的专业能力,还能帮助在实际工作中应用先进的技术和知识。以下是几种热门的网络安全证书介绍及备考指南。 1. OSCP (Offensive Security Certified Professional) 证书简介 OSCP是针对渗透测试领域的入门级…

在示波器里面外触发输入通道(EXT TRIG)什么作用?

在示波器中,外部触发输入通道(EXT TRIG)具有以下作用: 1. 提供外部信号触发 外部触发输入通道允许用户使用来自外部设备的信号作为触发源,而不仅仅依赖于示波器自身的输入通道。比如,可以用一个特定事件或…

Docker 基础入门

Docker 基础入门 前言 在云计算和微服务架构日益盛行的今天,软件开发与部署的效率和灵活性成为了企业竞争力的关键因素之一。Docker,作为一种开源的容器化平台,凭借其轻量级、可移植性和易于管理的特性,迅速成为现代软件开发和运…

qt QWidget详解

一、概述 QWidget是容器组件,继承自QObject类和QPaintDevice类。能够绘制自己和处理用户输入,是QT中所有窗口组件类的父类,是所有窗口组件的抽象,每个窗口组件都是一个QWidget,QWidget类对象常用作父组件或顶级组件使…

小新学习K8s第一天之K8s基础概念

目录 一、Kubernetes(K8s)概述 1.1、什么是K8s 1.2、K8s的作用 1.3、K8s的功能 二、K8s的特性 2.1、弹性伸缩 2.2、自我修复 2.3、服务发现和负载均衡 2.4、自动发布(默认滚动发布模式)和回滚 2.5、集中化配置管理和密钥…

信发软件之电脑版拖动——未来之窗行业应用跨平台架构

一、电脑版拖动 二、电脑版随意移动函数 var _movefalse;//移动标记 var _x,_y;//鼠标离控件左上角的相对位置 $("#"宿主id).click(function(){ }).mousedown(function(e){ _movetrue; _xe.pageX-parseInt($("#"宿主id).css("left")); _ye…

【Python爬虫实战】多进程结合 BeautifulSoup 与 Scrapy 构建爬虫项目

#1024程序员节|征文# 🌈个人主页:易辰君-CSDN博客 🔥 系列专栏:https://blog.csdn.net/2401_86688088/category_12797772.html ​ 前言 在大数据时代,爬虫技术是获取和处理网络数据的利器。面对需要处理大…

报表工具怎么选?山海鲸VS帆软,哪个更适合你?

概述 在国产报表软件市场中,山海鲸报表和帆软这两款工具都占有一席之地,许多企业在选择报表工具时常常在它们之间徘徊。然而,随着企业对数据分析需求的不断增长和复杂化,如何选取一款高效、易用且性价比高的报表工具,…

Linux笔记之文件查找和搜索命令which,find,locate,whereis总结

Linux笔记之文件查找和搜索命令which,find,locate,whereis总结 code review! 文章目录 Linux笔记之文件查找和搜索命令which,find,locate,whereis总结1.对比2.whereis 和 which 命令区别3.locate 和 find 命令区别 1.对比 命令功能说明备注which常用于查找可直接执行的命令。…

【状态机DP】【记忆化搜索1:1翻译递归空间优化】力扣2771. 构造最长非递减子数组

给你两个下标从 0 开始的整数数组 nums1 和 nums2 ,长度均为 n 。 让我们定义另一个下标从 0 开始、长度为 n 的整数数组,nums3 。对于范围 [0, n - 1] 的每个下标 i ,你可以将 nums1[i] 或 nums2[i] 的值赋给 nums3[i] 。 你的任务是使用最…

【MySQL】表的增删改查(CRUD)

目录 1.前言 2.增加(Create) 3.查询(Retrieve) 3.1全列查询 3.2指定列查询 3.3查询字段为表达式 3.4别名 3.5去重:DISTINCT 3.6排序:ORDER BY 3.7条件查询:WHERE 3.8分页查询&#x…

【LLM之Agent】《Tool Learning with Large Language Models: A Survey》论文阅读笔记

概述 背景信息 近年来,基于大型语言模型(LLMs)的工具学习成为增强LLMs应对复杂任务能力的有力范式。尽管这一领域快速发展,现有文献的碎片化以及缺乏系统组织,给新入门者带来了阻碍。因此,本论文旨在对现…

stable-zero123模型构建指南

一、介绍 stabilityai出品,能够对有简单背景的物体进行三维视角图片的生成,简单来说也就是通过调整变换观察的视角生成对应视角的图片。 本项目通过comfyui实现。 二、容器构建说明 1. 部署ComfyUI (1)使用命令克隆ComfyUI g…

【设计模式系列】命令模式

目录 一、什么是命令模式 二、命令模式的角色 三、命令模式的典型应用场景 四、命令模式在Runnable中的应用 一、什么是命令模式 命令模式(Command Pattern)是一种行为设计模式,它将一个请求或简单操作封装为一个对象。这个模式提供了一种…

Axure垂直菜单展开与折叠

亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢! 课程主题:Axure垂直菜单展开与折叠 主要内容:垂直菜单单击实现展开/折叠,点击各菜单项显示选中效果 应用场景:后台菜单设…

Python 应用可观测重磅上线:解决 LLM 应用落地的“最后一公里”问题

作者:彦鸿 背景 随着 LLM(大语言模型)技术的不断成熟和应用场景的不断拓展,越来越多的企业开始将 LLM 技术纳入自己的产品和服务中。LLM 在自然语言处理方面表现出令人印象深刻的能力。然而,其内部机制仍然不明确&am…

Apache Seata 新版本集成了 RocketMQ 事务消息

大家好,我是君哥。 Apache Seata 是一款高性能、简单易用的分布式事务中间件,它包含 AT、TCC、SAGA 和 XA 四种模式。 在最近发布的新版本中,Apache Seata 引入了 RocketMQ 中间件,并且跟 RocketMQ 的事务消息配合使用。今天我们…

界面控件DevExtreme中文教程 - 如何与Amazon S3和Azure Blob存储集成?

DevExtreme拥有高性能的HTML5 / JavaScript小部件集合,使您可以利用现代Web开发堆栈(包括React,Angular,ASP.NET Core,jQuery,Knockout等)构建交互式的Web应用程序。从Angular和Reac&#xff0c…