一、开头

接触过docker的同学多多少少听过这样一句话“docker容器通过linux namespace、cgroup特性实现资源的隔离与限制”。今天我们来尝试学习一下这两个东西。

二、关于namesapce

命名空间将全局系统资源包装在一个抽象中,使命名空间内的进程看起来它们拥有自己独立的全局资源实例。命名空间内对全局资源的改变对其他进程可见,命名空间的成员对其他进程不可见。

目前linux 内核已实现的7种命名空间如下:
  1. Namespace FlagAPI操作类型别名) Isolates(隔离内容)
  2. Cgroup CLONE_NEWCGROUP Cgroup root directory (since Linux 4.6)
  3. IPC CLONE_NEWIPC System V IPC, POSIX message queues (since Linux 2.6.19)
  4. Network CLONE_NEWNET Network devices, stacks, ports, etc. (since Linux 2.6.24)
  5. Mount CLONE_NEWNS Mount points (since Linux 2.4.19)
  6. PID CLONE_NEWPID Process IDs (since Linux 2.6.24)
  7. User CLONE_NEWUSER User and group IDs (started in Linux 2.6.23 and completed in Linux 3.8)
  8. UTS CLONE_NEWUTS Hostname and NIS domain name (since Linux 2.6.19)
查看进程的namespace
  1. [root@i-k9pwet2d ~]# pidof bash
  2. 14208 11123 2053
  3. [root@i-k9pwet2d ~]# ls -l /proc/14208/ns
  4. total 0
  5. lrwxrwxrwx 1 root root 0 Jul 20 09:36 ipc -> ipc:[4026531839]
  6. lrwxrwxrwx 1 root root 0 Jul 20 09:36 mnt -> mnt:[4026531840]
  7. lrwxrwxrwx 1 root root 0 Jul 20 09:36 net -> net:[4026531956]
  8. lrwxrwxrwx 1 root root 0 Jul 20 09:36 pid -> pid:[4026531836]
  9. lrwxrwxrwx 1 root root 0 Jul 20 09:36 user -> user:[4026531837]
  10. lrwxrwxrwx 1 root root 0 Jul 20 09:36 uts -> uts:[4026531838]

每一个进程在/proc/[pid]/ns 都可以看到其所属的namespace信息,这些链接文件指向所属的namespace及inode ID,我们可以通过readlink 来查看两个进程的是否属于同一个命名空间,inode相同则他们所属相同命名空间

  1. [root@i-k9pwet2d ~]# readlink /proc/11123/ns/uts
  2. uts:[4026531838]
  3. [root@i-k9pwet2d ~]# readlink /proc/14208/ns/uts
  4. uts:[4026531838]

如何将你的进程注册到命名空间(API操作)?

clone():创建一个新的命名空间,子进程同属新的命名空间,flags即我们创建的namespace类型,形如CLONE_NEW*

  1. int clone(int (*fn)(void *), void *stack, int flags, void *arg, ...
  2. /* pid_t *parent_tid, void *tls, pid_t *child_tid */ );

setns(): 加入一个命名空间,fd为/proc/[pid]/ns 下的链接文件,nstype即我们的Flag

  1. int setns(int fd, int nstype);

unshare() :退出某个namespace并加入创建的新空间。

  1. int unshare(int flags);

ioctl() : ioctl系统调用可用于查询命名空间的信息

  1. int ioctl(int fd , unsigned long request , ...);

下面我们通过shell 命令 unshare 来看看命名空间7大隔离实现

1.PID Namespace

PID Namespace 的作用是用来隔离进程,利用 PID Namespace 可以实现每个容器的主进程为 1 号进程,而容器内的进程在主机上却拥有不同的PID。

  1. [root@i-k9pwet2d ~]# unshare --fork --pid --mount-proc /bin/bash
  2. [root@i-k9pwet2d ~]# ps -aux
  3. USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
  4. root 1 0.0 0.1 115680 2036 pts/0 S 10:46 0:00 /bin/bash
  5. root 12 0.0 0.1 115684 2048 pts/0 S 10:47 0:00 -bash
  6. root 30 0.0 0.0 155468 1804 pts/0 R+ 10:57 0:00 ps -aux
  7. ls -l /proc/1/ns
  8. total 0
  9. lrwxrwxrwx 1 root root 0 Jul 20 11:05 ipc -> ipc:[4026531839]
  10. lrwxrwxrwx 1 root root 0 Jul 20 11:05 mnt -> mnt:[4026532545]
  11. lrwxrwxrwx 1 root root 0 Jul 20 11:05 net -> net:[4026531956]
  12. lrwxrwxrwx 1 root root 0 Jul 20 11:05 pid -> pid:[4026532546]
  13. lrwxrwxrwx 1 root root 0 Jul 20 11:05 user -> user:[4026531837]
  14. lrwxrwxrwx 1 root root 0 Jul 20 11:05 uts -> uts:[4026531838]

在新的PID Namespace中我们只能看到自身命名空间的进程。并且当前的bash处于起来的mnt、pid命名空间。

2.Mount Namespace

它可以用来隔离不同的进程或进程组看到的挂载点。在容器内的挂载操作不会影响主机的挂载目录。

我们创建一个命名空间

  1. unshare --mount --fork /bin/bash

挂在一个目录

  1. [root@i-k9pwet2d ~]# mkdir /tmp/mnt
  2. [root@i-k9pwet2d ~]# mount -t tmpfs -o size=1m tmpfs /tmp/mnt
  3. [root@i-k9pwet2d ~]# df -h |grep mnt
  4. tmpfs 1M 0 1M 0% /tmp/mnt

在命名空间内的挂载并不影响我们的主机目录,我们在主机上查看不到挂载信息

  1. df -h |grep mnt

3.User Namespace

User Namespace用来隔离用户和用户组。我们来创建一个用户命名空间并修改提示符

  1. [root@i-k9pwet2d ~]# PS1='\u@container#' unshare --user -r /bin/bash
  2. root@container#

再查看ns,用户链接是不同的,已处于不同空间。

  1. [root@i-k9pwet2d ~]# readlink /proc/1835/ns/user
  2. user:[4026532192]
  3. [root@i-k9pwet2d ~]# readlink /proc/$$/ns/user
  4. user:[4026531837]

用户命名空间的最大优势是无需 root 权限即可运行容器,避免应用使用root对主机的影响。

4.UTS Namespace

UTS Namespace 用于隔离主机名的,它允许每个 UTS Namespace 拥有一个独立的主机名。

  1. [root@i-k9pwet2d ~]# unshare --fork --uts /bin/bash

在命名空间中修改主机名,在主机中不受影响

  1. [root@i-k9pwet2d ~]# hostname -b container
  2. [root@i-k9pwet2d ~]# hostname
  3. container

主机中

  1. [root@i-k9pwet2d ~]# hostname
  2. i-k9pwet2d

5.IPC Namespace

IPC 命名空间隔离某些 IPC 资源,即 System V IPC 对象(参见sysvipc(7))和(自 Linux 2.6.30 起)POSIX 消息队列(请参阅mq_overview(7))。容器通过IPC Namespace、PID Namespace实现同一 IPC Namespace 内的进程彼此可以通信,不同 IPC Namespace 的进程却不能通信。

我们使用linux中ipc相关命令来测试

ipcs -q 命令:用来查看系统间通信队列列表。

ipcmk -Q 命令:用来创建系统间通信队列。

我们先创建一个IPC Namespace

  1. [root@i-k9pwet2d ~]# unshare --fork --ipc /bin/bash

创建一个通信队列后查询一下

  1. [root@i-k9pwet2d ~]# ipcmk -Q
  2. Message queue id: 0
  3. [root@i-k9pwet2d ~]# ipcs -q
  4. ------ Message Queues --------
  5. key msqid owner perms used-bytes messages
  6. 0x1de4aef6 0 root 644 0 0

在主机上查询,可以看到通信已经被隔离了

  1. [root@i-k9pwet2d ~]# ipcs -q
  2. ------ Message Queues --------
  3. key msqid owner perms used-bytes messages

6.Net Namespace

Net Namespace 可用于隔离网络设备、IP 地址和端口等信息。Net Namespace 可以让每个进程拥有自己独立的 IP 地址,端口和网卡信息。

我们继续创建一个Net Namespace

  1. [root@i-k9pwet2d ~]# unshare --net --fork /bin/bash

查看网络和端口信息

  1. [root@i-k9pwet2d ~]# ip addr
  2. 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
  3. link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
  4. [root@i-k9pwet2d ~]# netstat -ntlp
  5. Active Internet connections (only servers)
  6. Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name

上面看到了一个回环接口lo,状态处于DOWN,我们将它启动,这样我们的Namespace有了自己的网络地址。

  1. 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
  2. link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
  3. inet 127.0.0.1/8 scope host lo
  4. valid_lft forever preferred_lft forever
  5. inet6 ::1/128 scope host
  6. valid_lft forever preferred_lft forever

主机中

  1. [root@i-k9pwet2d ~]# ip addr
  2. 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
  3. link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
  4. inet 127.0.0.1/8 scope host lo
  5. valid_lft forever preferred_lft forever
  6. inet6 ::1/128 scope host
  7. valid_lft forever preferred_lft forever
  8. 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
  9. link/ether 52:54:96:e1:36:04 brd ff:ff:ff:ff:ff:ff
  10. inet 10.150.25.9/24 brd 10.150.25.255 scope global noprefixroute dynamic eth0
  11. valid_lft 80720sec preferred_lft 80720sec
  12. inet6 fe80::5054:96ff:fee1:3604/64 scope link
  13. valid_lft forever preferred_lft forever
  14. [root@i-k9pwet2d ~]# netstat -ntlp
  15. Active Internet connections (only servers)
  16. Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
  17. tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 757/sshd
  18. tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1112/master
  19. ...

7.Cgroup Namespace

Cgroup是对进程的cgroup视图虚拟化。 每个 cgroup 命名空间都有自己的一组 cgroup 根目录。Linux 4.6开始支持。

cgroup 命名空间提供的虚拟化有多种用途:

  • 防止信息泄漏。否则容器外的cgroup 目录路径对容器中的进程可见。
  • 简化了容器迁移等任务。
  • 允许更好地限制容器化进程。可以挂载容器的 cgroup 文件系统,这样容器无需访问主机 cgroup 目录。

8.Time Namespace

虚拟化两个系统时钟,用于隔离时间。 linux 5.7内核开始支持 参考地址:TIME_NAMESPACES(7)


三、关于Cgroup

从上面我们了解到当我们要运行一个容器时,docker等应用会为该容器创建一组 namespace,对操作系统而言可以理解为一组进程。这下我们完成了“权利”的集中,但是“权利越大,责任也大”,我们不能放任这组“大权“不管,所以又有了CgroupLinux Control Group)这个东西。

Cgroup最主要的作用,就是限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等等。

cgroups 框架提供了以下内容

  • 资源限制: 可以为我们的进程组配置内存限制或cpu个数限制又或者仅限于某个特定外围设备。
  • 优先级: 一个或多个组可以配置为优先占用 CPU 或磁盘 I/O 吞吐量。
  • 资源记录: 监视和测量组的资源使用情况。
  • 控制: 可以冻结或停止和重新启动进程组。

一个 cgroup 可以由一个或多个进程组成,这些进程都绑定到同一组限制。这些组也可以是分层的,即子组可以继承父组管理的限制。

Linux 内核为 cgroup 技术提供了对一系列控制器或子系统的访问。控制器负责将特定类型的系统资源分配给一组一个或多个进程。例如,memory控制器限制内存使用,而cpuacct控制器监控 CPU 使用。

我们通过Mount查看系统中cgroup的子系统

  1. [root@i-k9pwet2d ~]# mount -t cgroup
  2. cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
  3. cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct,cpu)
  4. cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_prio,net_cls)
  5. cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
  6. cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
  7. cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
  8. cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
  9. cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
  10. cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
  11. cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
  12. cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)

可以看到cgroup已通过文件系统方式挂载到/sys/fs/cgroup/

  1. [root@i-k9pwet2d ~]# ls -l /sys/fs/cgroup/
  2. total 0
  3. drwxr-xr-x 2 root root 0 Jul 20 12:23 blkio
  4. lrwxrwxrwx 1 root root 11 Jul 20 12:23 cpu -> cpu,cpuacct
  5. lrwxrwxrwx 1 root root 11 Jul 20 12:23 cpuacct -> cpu,cpuacct
  6. drwxr-xr-x 2 root root 0 Jul 20 12:23 cpu,cpuacct
  7. drwxr-xr-x 2 root root 0 Jul 20 12:23 cpuset
  8. drwxr-xr-x 4 root root 0 Jul 20 12:23 devices
  9. drwxr-xr-x 2 root root 0 Jul 20 12:23 freezer
  10. drwxr-xr-x 2 root root 0 Jul 20 12:23 hugetlb
  11. drwxr-xr-x 2 root root 0 Jul 20 12:23 memory
  12. lrwxrwxrwx 1 root root 16 Jul 20 12:23 net_cls -> net_cls,net_prio
  13. drwxr-xr-x 2 root root 0 Jul 20 12:23 net_cls,net_prio
  14. lrwxrwxrwx 1 root root 16 Jul 20 12:23 net_prio -> net_cls,net_prio
  15. drwxr-xr-x 2 root root 0 Jul 20 12:23 perf_event
  16. drwxr-xr-x 2 root root 0 Jul 20 12:23 pids
  17. drwxr-xr-x 4 root root 0 Jul 20 12:23 systemd
接下来我们通过一个实例看看cgroup是如何限制CPU使用的

我们启动一个循环脚本,这个循环脚本将占用近100%的CPU,我们通过cgroup限制到50%

  1. $ cat loop.sh
  2. #!/bash/sh
  3. while [ 1 ]; do
  4. :
  5. done

将我们的脚本放到后台,获取它的PID为21497

  1. nohup bash loop.sh &

我们需要创建一个cgroup控制组loop

  1. [root@i-k9pwet2d ~]# mkdir /sys/fs/cgroup/cpu/loop

loop组是CPU的子组,上面提到子组可以继承父组管理的限制所以loop将继承对系统整个cpu的访问权限

  1. [root@i-k9pwet2d shell]# ls -l /sys/fs/cgroup/cpu/loop
  2. total 0
  3. -rw-r--r-- 1 root root 0 Jul 20 17:15 cgroup.clone_children
  4. --w--w--w- 1 root root 0 Jul 20 17:15 cgroup.event_control
  5. -rw-r--r-- 1 root root 0 Jul 20 17:15 cgroup.procs
  6. -r--r--r-- 1 root root 0 Jul 20 17:15 cpuacct.stat
  7. -rw-r--r-- 1 root root 0 Jul 20 17:15 cpuacct.usage
  8. -r--r--r-- 1 root root 0 Jul 20 17:15 cpuacct.usage_percpu
  9. -rw-r--r-- 1 root root 0 Jul 20 17:15 cpu.cfs_period_us
  10. -rw-r--r-- 1 root root 0 Jul 20 17:15 cpu.cfs_quota_us
  11. -rw-r--r-- 1 root root 0 Jul 20 17:15 cpu.rt_period_us
  12. -rw-r--r-- 1 root root 0 Jul 20 17:15 cpu.rt_runtime_us
  13. -rw-r--r-- 1 root root 0 Jul 20 17:15 cpu.shares
  14. -r--r--r-- 1 root root 0 Jul 20 17:15 cpu.stat
  15. -rw-r--r-- 1 root root 0 Jul 20 17:15 notify_on_release
  16. -rw-r--r-- 1 root root 0 Jul 20 17:15 tasks

查看继承后的loop组cpu限制,计算周期为100000us,采样时间无限制(-1)

  1. [root@i-k9pwet2d shell]# cat /sys/fs/cgroup/cpu/loop/cpu.cfs_period_us
  2. 100000
  3. [root@i-k9pwet2d shell]# cat /sys/fs/cgroup/cpu/loop/cpu.cfs_quota_us
  4. -1

为了限制进程的的cpu使用率为50%,我们需要更新cpu.cfs_quota_us的值为50000

  1. echo 50000 >/sys/fs/cgroup/cpu/loop/cpu.cfs_quota_us

将脚本PID更新到loop控制组下的tasks

  1. [root@i-k9pwet2d shell]# echo 21497 >/sys/fs/cgroup/cpu/loop/tasks

此时我们的脚本CPU使用率已被限制到50%

  1. PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
  2. 21497 root 20 0 113284 1176 996 R 50.0 0.1 12:17.48 bash

在docker启动容器时做的cpu限制参数--cpu-period--cpu-quota实际上就是调整对应容器控制组的cpu配额

转载自 https://www.cnblogs.com/qsing/p/15036317.html

cgroup和Linux Namespace基础操作的更多相关文章

  1. Linux的基础操作

    1.概念 Linux是基于Unix的开源免费的操作系统,由于系统的稳定性和安全性几乎成为程序代码运行的最佳系统环境. 2.Linux的分类 1.按市场需求分为: 图形化界面版.服务器版 2.按原生程度 ...

  2. Linux - 系统基础操作

    wall # 给其它用户发消息 whereis ls # 查找命令的目录 which # 查看当前要执行的命令所在的路径 clear # 清空整个屏幕 reset # 重新初始化屏幕 cal # 显示 ...

  3. Linux mysql 基础操作

    命令  #查看版本 mysql --version   #进入mysql 命令 mysql -u root -p mysql -u root@localhost  (没有密码的情况)   #创建数据库 ...

  4. Linux命令基础操作--vim 归档 压缩 分区 格式化 挂载 Innode

    1 将用户信息数据库文件和组信息数据库文件纵向合并为一个文件/1.txt(覆盖) 使用 cat命令将查看的文件合并输出到/1.txt 这里的关键:定位到文件,如果后面加上/后被认为是目录 分为两步,先 ...

  5. 假期学习【一】Ubuntu中Linux的基础操作

    题目: Linux 系统的安装和常用命令 姓名: 赵路仓 日期: 2020.1.24 实验环境: Ubuntu 实验内容与完成情况: (1)切换到目录 /usr/bin: (2)查看目录/usr/lo ...

  6. Linux Mysql基础操作

    1). 打开MySQL 使用如下两条命令,打开MySQL服务并使用root用户登录: # 启动 MySQL 服务 sudo service mysql start # 使用 root 用户登录,实验楼 ...

  7. linux之基础操作

  8. Linux一些最基础操作

    最后更新时间: 2015-05-06 这是一篇很早之前写的,整理笔记的时候看到了,Linux 非常基础操作. bin/sbin: 一般是存放可以执行文件 绝对路径 相对路径 mkdir dir ls: ...

  9. 看完这篇Linux基本的操作就会了

    前言 只有光头才能变强 这个学期开了Linux的课程了,授课的老师也是比较负责任的一位.总的来说也算是比较系统地学习了一下Linux了~~~ 本文章主要是总结Linux的基础操作以及一些简单的概念~如 ...

  10. Linux-看完这篇Linux基本的操作就会了(转)

    前言 只有光头才能变强 这个学期开了Linux的课程了,授课的老师也是比较负责任的一位.总的来说也算是比较系统地学习了一下Linux了~~~ 本文章主要是总结Linux的基础操作以及一些简单的概念~如 ...

随机推荐

  1. Spring Boot自动配置原理懂后轻松写一个自己的starter

    目前很多Spring项目的开发都会直接用到Spring Boot.因为Spring原生开发需要加太多的配置,而使用Spring Boot开发很容易上手,只需遵循Spring Boot开发的约定就行了, ...

  2. bat想要写一个卸载软件的脚本,最后宣布失败[未完待续...]

    find 的用法:双引号,搜索内容是英文也要用双引号 C:\Users\clouder\Desktop\yanna>find '小智' products.txt FIND: 参数格式不正确 C: ...

  3. MQ收到无序的消息时如何进行业务处理

    业务背景 跟第三方系统做对接,双方通过ActiveMQ进行通信,消息之间是有内在关联的,也就是消息本来应该是有业务顺序的,但由于一些原因,现在收到消息是乱序的,这种情况下做业务处理就有一点小问题了 方 ...

  4. JZOJ 3167.查税

    \(\text{Solution}\) 记 \(k\) 这个办公室相关属性有 \(t,z,s\) 对于以后的某一天 \(T\),其账户余额为 \((T-t)z+s\) 要最大化这东西,不妨另 \(b= ...

  5. Vulhub 漏洞学习之:DNS

    Vulhub 漏洞学习之:DNS 1 DNS域传送漏洞 DNS协议支持使用axfr类型的记录进行区域传送,用来解决主从同步的问题.如果管理员在配置DNS服务器的时候没有限制允许获取记录的来源,将会导致 ...

  6. 下载、编译AspNetCore 的全过程

    1. clone 源码 下载新的 git clone --recursive https://github.com/dotnet/aspnetcore 如果之前clone过,但是没有使用 --recu ...

  7. redis服务创建失败:Could not create server TCP listening socket 127.0.0.1:6379: bind

    1. redis-cli.exe 2.shutdown 如果出现   (error) NOAUTH Authentication required.,则需要验证之前设置的密码(没出现请忽略第三步) 3 ...

  8. (四)Mysql之索引介绍

    索引数据结构的选择:Hash表.二叉树.平衡二叉树.(红黑树近似于平衡二叉树).B树.B+树1)Hash表:Java的HashMap.TreeMap就是Hash表结构,以键值对存储,时间复杂度是O(1 ...

  9. python生成csv并发送邮件

    python版本3.9.4 1.生成csv的方法 #make_csv.py# import csv def produce_csv(): # 1. 创建文件对象 f = open('temp.csv' ...

  10. 十二、21.提交本地代码到Git仓库并推送到码云

    查看分支 运行git add . 把所有修改过后文件添加到暂存区 git commit 把当前所有的代码提交到rights分支 加-m加一个消息 到此所有的功能模块都已经提交到了rights这个分支里 ...