首页 > 其他分享 >容器基础-- namespace,Cgroup 和 UnionFS

容器基础-- namespace,Cgroup 和 UnionFS

时间:2024-05-28 11:01:11浏览次数:26  
标签:00 cgroup -- Namespace sudo namespace zhy Cgroup 进程

Namespace

什么是 Namespace ?

这里的 “namespace” 指的是 Linux namespace 技术,它是 Linux 内核实现的一种隔离方案。简而言之,Linux 操作系统能够为不同的进程分配不同的 namespace,每个 namespace 都具有独立的资源分配,从而实现了进程间的隔离。如果你的 Linux 安装了 GCC,可以通过运行 man namespaces 命令来查看相关文档,或者你也可以访问在线手册获取更多信息。

介绍

下图为各种 namespace 的参数,支持的起始内核版本,以及隔离内容。

Namespace系统调用参数内核版本隔离内容
UTS (Unix Time-sharing System)CLONE_NEWUTSLinux 2.4.19主机名与域名
IPC (Inter-Process Communication)CLONE_NEWIPCLinux 2.6.19信号量、消息队列和共享内存
PID (Process ID)CLONE_NEWPIDLinux 2.6.19进程编号
NetworkCLONE_NEWNETLinux 2.6.24网络设备、网络栈、端口等等
MountCLONE_NEWNSLinux 2.6.29挂载点(文件系统)
UserCLONE_NEWUSERLinux 3.8用户和用户组
  1. PID Namespace:
  • 不同用户的进程通过 PID Namespace 进行隔离,并且不同的 Namespace 中可以有相同的进程 ID。在 Docker 中,所有的 LXC(Linux 容器)进程的父进程是 Docker 进程,每个 LXC 进程具有不同的 Namespace。由于支持嵌套 Namespace,因此可以方便地实现 Docker 中的 Docker(Docker in Docker)。
  1. Net Namespace:
  • 有了 PID Namespace,每个 Namespace 中的进程能够相互隔离,但是网络端口仍然共享主机的端口。通过 Net Namespace 实现网络隔离,每个 Net Namespace 具有独立的网络设备、IP 地址、IP 路由表和 /proc/net 目录。这样,每个容器的网络就能够得到隔离。Docker 默认使用 veth(虚拟以太网)方式将容器中的虚拟网卡与主机上的 Docker 桥接器(docker0)连接起来。
  1. IPC Namespace:
  • 容器中的进程仍然使用常见的 Linux 进程间通信(IPC)方法,包括信号量、消息队列和共享内存。然而,与虚拟机不同的是,容器中的进程实际上是在具有相同 PID Namespace 的主机进程之间进行通信,因此在申请 IPC 资源时需要加入 Namespace 信息,每个 IPC 资源都有一个唯一的 32 位 ID。
  1. MNT Namespace:
  • 类似于 chroot,将进程限制在特定的目录下执行。MNT Namespace 允许不同 Namespace 的进程看到不同的文件结构,从而隔离了每个 Namespace 中进程所看到的文件目录。与 chroot 不同的是,每个 Namespace 中的容器在 /proc/mounts 中的信息仅包含所在 Namespace 的挂载点。
  1. UTS Namespace:
  • UTS(“UNIX Time-sharing System”)Namespace 允许每个容器拥有独立的主机名和域名,使其在网络上可以被视为一个独立的节点,而不仅仅是主机上的一个进程。
  1. User Namespace:
  • 每个容器可以具有不同的用户和组 ID,这意味着容器内部的程序可以使用容器内部的用户执行,而不是主机上的用户。

涉及到三个系统调用(system call)的 API:

  1. clone():用于创建新进程。与 fork() 创建新进程不同的是,clone() 创建进程时可以传递 CLONE_NEW* 类型的命名空间隔离参数,以控制子进程共享的内容。要了解更多信息,请查阅clone 手册
  2. setns():用于将某个进程与指定的命名空间分离。通过 setns(),进程可以脱离一个特定的命名空间,使其不再与该命名空间中的其他进程共享资源。
  3. unshare():用于将某个进程加入到指定的命名空间中。通过 unshare(),进程可以加入到一个特定的命名空间,与该命名空间中的其他进程共享资源。

namespace 的操作

  • 查看当前系统的 namespace
lsns –t <type>
  • 查看某进程的 namespace
ls -la /proc/<pid>/ns/
  • 进入某 namespace 运行命令
nsenter -t <pid> -n ip addr

Test:

# Linux命令行中,可以使用`unshare`命令结合`clone()`创建一个新的进程,并在其中使用命名空间隔离参数。
# 创建一个新的进程,并在其中使用命名空间隔离参数
unshare --pid --net -- sleep 600

ps -ef|grep sleep
root       37915   34572  0 08:53 pts/1    00:00:00 sudo unshare --pid --net -- sleep 600
root       37916   37915  0 08:53 pts/3    00:00:00 sudo unshare --pid --net -- sleep 600
root       37917   37916  0 08:53 pts/3    00:00:00 sleep 600
zhy        37919   37896  0 08:53 pts/2    00:00:00 grep --color=auto sleep

sudo lsns -t net
[sudo] password for zhy:
        NS TYPE NPROCS   PID USER    NETNSID NSFS                           COMMAND
4026531840 net     277     1 root unassigned                                /sbin/init
4026532656 net       1 37347 root          0 /run/docker/netns/c986b82be683 bash
4026532718 net       1 37917 root unassigned                                sleep 600

sudo nsenter -t 37917 -n ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
  1. docker 启动一个 ubuntu
docker run --rm -it docker.m.daocloud.io/ubuntu:22.10 bash
  1. 用另一个窗口 找到这个进程
ps -ef|grep ubuntu
# zhy        37247   34017  0 08:20 pts/0    00:00:00 docker run --rm -it docker.m.daocloud.io/ubuntu:22.10 bash
  1. 查看这个进程的 namespace
ls -la /proc/37247/ns/
total 0
dr-x--x--x 2 zhy zhy 0 May 27 08:24 .
dr-xr-xr-x 9 zhy zhy 0 May 27 08:23 ..
lrwxrwxrwx 1 zhy zhy 0 May 27 08:24 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 zhy zhy 0 May 27 08:24 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx 1 zhy zhy 0 May 27 08:24 mnt -> 'mnt:[4026531841]'
lrwxrwxrwx 1 zhy zhy 0 May 27 08:24 net -> 'net:[4026531840]'
lrwxrwxrwx 1 zhy zhy 0 May 27 08:24 pid -> 'pid:[4026531836]'
lrwxrwxrwx 1 zhy zhy 0 May 27 08:24 pid_for_children -> 'pid:[4026531836]'
lrwxrwxrwx 1 zhy zhy 0 May 27 08:24 time -> 'time:[4026531834]'
lrwxrwxrwx 1 zhy zhy 0 May 27 08:24 time_for_children -> 'time:[4026531834]'
lrwxrwxrwx 1 zhy zhy 0 May 27 08:24 user -> 'user:[4026531837]'
lrwxrwxrwx 1 zhy zhy 0 May 27 08:24 uts -> 'uts:[4026531838]'
  1. 查看namespace
sudo lsns -t pid
        NS TYPE NPROCS   PID USER COMMAND
4026531836 pid     275     1 root /sbin/init
4026532654 pid       1 37347 root bash

sudo lsns -t net
        NS TYPE NPROCS   PID USER    NETNSID NSFS                           COMMAND
4026531840 net     275     1 root unassigned                                /sbin/init
4026532656 net       1 37347 root          0 /run/docker/netns/c986b82be683 bash

为什么查出来执行 **bash** 的 pid 和 **ps -ef** 的不一样?

一个是docker run的进程 PID

一个是 容器内部 ‘bash’ 进程的 PID  这个进程是由docker run的进程通过进程复制(process cloning)创建的子进程。

  1. 在 ubuntu 中执行 ip addr 在主机执行 nsenter -t <pid> -n ip addr
# 容器内
ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
11: eth0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
       
ps -ef
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 08:23 pts/0    00:00:00 bash
root         360       1  0 09:12 pts/0    00:00:00 ps -ef

# 主机
sudo nsenter -t 37347 -n -- ip addr # -n 进入网络namespace执行
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
11: eth0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
      
sudo nsenter -t 37347 -a -- ps -ef # -a 进入所有namespace执行
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 08:23 pts/0    00:00:00 bash
root         359       0  0 09:12 ?        00:00:00 ps -ef

Cgroup

什么是 Cgroup

Linux cgroups 的全称是 Linux Control Groups,它是 Linux 内核的特性,主要作用是限制、记录和隔离进程组(process groups)使用的物理资源(cpu、memory、IO 等)

为什么要使用Cgroup?

可以做到对 cpu,内存等资源实现精细化的控制,容器技术就使用了 cgroups 提供的资源限制能力来完成cpu,内存等部分的资源控制。

核心概念

  • task:任务,对应于系统中运行的一个实体,一般是指进程
  • subsystem:子系统,具体的资源控制器(resource class 或者 resource controller),控制某个特定的资源使用。比如 CPU 子系统可以控制 CPU 时间,memory 子系统可以控制内存使用量
  • cgroup:控制组,一组任务和子系统的关联关系,表示对这些任务进行怎样的资源管理策略
  • hierarchy:层级有一系列 cgroup 以一个树状结构排列而成,每个层级通过绑定对应的子系统进行资源控制。层级中的 cgroup 节点可以包含零个或多个子节点,子节点继承父节点挂载的子系统。一个操作系统中可以有多个层级。
subsystem

subsystem 是一组资源控制的模块,一般包含有:

  • blkio 设置对块设备 (比如硬盘) 的输入输出的访问控制 (block/io)
  • cpu 设置 cgroup 中的进程的 CPU 被调度的策略
  • cpuacct 可以统计 cgroup 中的进程的 CPU 占用 (cpu account)
  • cpuset 在多核机器上设置 cgroup 中的进程可以使用的 CPU 和内存 (此处内存仅使用于 NUMA 架构)
  • devices 控制 cgroup 中进程对设备的访问
  • freezer 用于挂起 (suspends) 和恢复 (resumes) cgroup 中的进程
  • memory 用于控制 cgroup 中进程的内存占用
  • net_cls 用于将 cgroup 中进程产生的网络包分类 (classify),以便 Linux 的 tc (traffic controller) (net_classify) 可以根据分类 (classid) 区分出来自某个 cgroup 的包并做限流或监控。
  • net_prio 设置 cgroup 中进程产生的网络流量的优先级
  • ns 这个 subsystem 比较特殊,它的作用是 cgroup 中进程在新的 namespace fork 新进程 (NEWNS) 时,创建出一个新的 cgroup,这个 cgroup 包含新的 namespace 中进程。

v2

Cgroup v2手册

是否加载了Cgroup v2内核模块

cat /sys/fs/cgroup/cgroup.controllers
cpuset cpu io memory hugetlb pids rdma misc
test
Cpu

执行一段go代码

package main

func main() {
    go func() { for{} }()
    for {}
}

/*
执行 go run test.go
top
    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
  39268 zhy       20   0  709572    868    584 R 200.0   0.0   2:12.27 test
  
  可以看到使用了2个cpu 因为开个两个goroutine for阻塞
*/

限制cpu

sudo mkdir /sys/fs/cgroup/test
sudo echo "100000 100000" | sudo tee /sys/fs/cgroup/test/cpu.max >/dev/null
sudo echo "39268" | sudo tee /sys/fs/cgroup/test/cgroup.procs >/dev/null

# top
#    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
#  39268 zhy       20   0  709572    868    584 R 100.3   0.0   7:45.04 test
# 马上就只占用一个cpu了
Memory
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#define BLOCK_SIZE (100 * 1024 * 1024)
#define NUM_ALLOCATIONS 10
#define SLEEP_SECONDS 30

char* allocMemory(int size) {
    char* out = (char*)malloc(size);
    memset(out, 'A', size);
    return out;
}

int main() {
    int i;

    for (i = 1; i <= NUM_ALLOCATIONS; i++) {
        char* block = allocMemory(i * BLOCK_SIZE);
        printf("Allocated memory block of size %dMB at address: %p\n", i * 100, block);
        sleep(SLEEP_SECONDS);
    }

    return 0;
}

/*
 ps -p 3243 -o rss=,unit=M,cmd=
      M
308512 session-4.scope                ./test2
*/

限制内存

sudo echo "300000000" |sudo tee /sys/fs/cgroup/test/memory.max >/dev/null
sudo echo "64417" | sudo tee /sys/fs/cgroup/test/cgroup.procs >/dev/null

#cat memory.current
#299839488

UnionFS

联合文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下 (unite several directories into a single virtual filesystem)。

联合文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。

另外,不同 Docker 容器就可以共享一些基础的文件系统层,同时再加上自己独有的改动层,大大提高了存储的效率。

最新版 Docker 使用的是 overlay2。

overlay2

现在主流基本都是 overlayFS

OverlayFS 属于文件级的存储驱动,包含了最初的 Overlay 和更新更稳定的 overlay2。

Overlay 只有两层:upper 层和 lower 层,Lower 层代表镜像层,upper 层代表容器可写层。

image.png

test
mkdir test && cd test
mkdir upper lower merged work
echo "file1 from lower" > lower/file1.txt
echo "file2 from lowerr" > lower/file2.txt
echo "file3 from lower" > lower/file3.txt
echo "file2 from upper" > upper/file2.txt
echo "file4 from upper" > upper/file4.txt

current_dir=$(pwd)
sudo mount -t overlay -o lowerdir="$current_dir/lower",upperdir="$current_dir/upper",workdir="$current_dir/work" overlay "$current_dir/merged"

cat merged/file1.txt
file1 from lower
cat merged/file2.txt
file2 from upper
cat merged/file3.txt
file3 from lower
cat merged/file4.txt
file4 from upper

docker image

image.png

每一条指令是一层, 下层可以共用

Docker 的文件系统

典型的Linux文件系统组成如下:

  • Bootfs(引导文件系统)
    • Bootloader(引导加载程序):负责加载内核。
    • Kernel(内核):一旦内核加载到内存中,就会卸载bootfs。
  • Rootfs(根文件系统)
    • /dev、/proc、/bin、/etc等标准目录和文件。
    • 对于不同的Linux发行版,bootfs基本上是一致的,但rootfs会有所差异。

Docker 启动

Linux

  • 在启动后,首先将 rootfs 设置为 readonly, 进行一系列检查,然后将其切换为 “readwrite” 供用户使用。

Docker 启动

  • 初始化时也是将 rootfs 以 readonly 方式加载并检查,然而接下来利用 union mount 的方式将一个 readwrite 文件系统挂载在 readonly 的 rootfs 之上;
  • 并且允许再次将下层的 FS(file system) 设定为 readonly 并且向上叠加。 这样一组 readonly 和一个 writeable 的结构构成一个 container 的运行时态,每一个 FS 被称作一个 FS 层。

写操作

由于镜像具有共享特性,所以对容器可写层的操作需要依赖存储驱动提供的写时复制和用时分配机制,以此来 支持对容器可写层的修改,进而提高对存储和内存资源的利用率。

  • 写时复制 即 Copy-on-Write。
    • 一个镜像可以被多个容器使用,但是不需要在内存和磁盘上做多个拷贝。
    • 在需要对镜像提供的文件进行修改时,该文件会从镜像的文件系统被复制到容器的可写层的文件系统 进行修改,而镜像里面的文件不会改变。
    • 不同容器对文件的修改都相互独立、互不影响。
  • 用时分配
  • 按需分配空间,而非提前分配,即当一个文件被创建出来后,才会分配空间。

标签:00,cgroup,--,Namespace,sudo,namespace,zhy,Cgroup,进程
From: https://blog.csdn.net/daemon365/article/details/139221062

相关文章

  • 容器启动流程(containerd 和 runc)
    启动流程containerd作为一个api服务,提供了一系列的接口供外部调用,比如创建容器、删除容器、创建镜像、删除镜像等等。使用docker和ctr等工具,都是通过调用containerd的api来实现的。kubelet通过cri调用containerd和这些不一样,后续我会介绍到。containerd......
  • CDC 数据实时同步入湖的技术、架构和方案(截至2024年5月的现状调研)
    博主历时三年精心创作的《大数据平台架构与原型实现:数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行,点击《重磅推荐:建大数据平台太难了!给我发个工程原型吧!》了解图书详情,京东购书链接:https://item.jd.com/12677623.html,扫描左侧二维码进入京东手......
  • 推荐丨网站https访问也能免费实现了
    2022年Verizon数据泄露调查报告指出,当年75%的社会工程攻击涉及网络钓鱼,仅2022年一年就有超过33万个账户被网络钓鱼,网络钓鱼占整体社会工程攻击的41%。但是也不能将所有责任归咎于运维人员,因为薄弱的安全意识建设及宣教是大部分漏洞利用的原因。在进行网络信息交互的......
  • 西门子学习笔记3 - 工业物联网(MQTT协议服务器的搭建)
    这里使用的是公开测试的一个服务器(EMQX)的服务器EMQX是一款全球下载量超千万的开源物联网MQTT服务器,单集群支持1亿物联网设备连接,消息分发时延低于1毫秒,助力企业构建关键业务的IoT平台与应用。1、服务器文件的下载1、官方下载地址:免费下载、试用EMQ产品(emqx.com......
  • APIO 2024
    A-September模拟题。取出\(m\)个人里包含相同元素的段,再判断删掉的是不是都是叶子就好,时间复杂度\(O(nm)\)#include<bits/stdc++.h>usingnamespacestd;intsolve(intn,intm,vector<int>fa,vector<vector<int>>s){ intans=0,cnt=0; vector<int>d(......
  • 推荐:实现泛域名快速部署SSL证书
    目录泛域名的概念:泛域名解析的好处:泛域名的应用:快速处理泛域名的SSL证书部署问题:泛域名证书申请方式:对于相当一部分拥有泛域名的用户而言泛域名的作用很大,一个主域名衍生出的大量子域名,方便、易记、集成管理、且省成本。泛域名的概念:泛域名指在一个根域名之下,所有......
  • 核间通信:Linux中RPMsg和OpenAMP详解
    1.核间通信组件简介 目前针对不同级别的操作系统,存在几种核间通信组件,分别是以Linux内嵌组件RPMsg、支持跨平台移植的OpenAMP,短小精简的RPMsg-Lite,这三个组件在代码细节、收发策略、移植性上各有优劣,用户可根据需要选择。它们起初都来源于Linux的RPMsg,遵循统一的协议标准(交互过......
  • (10-3)文件操作:包io
    10.3 包io在Go语言中,io是一个重要的标准库包,提供了一系列内置接口和方法,用于处理输入、输出流操作。包io中包含的常用接口如表10-1所示。表10-1 包io中包含的常用接口接口名称描述io.Reader代表可以读取数据的对象,定义了一个Read方法io.Writer代表可以写......
  • 为什么中国的开发者都喜欢用 vue?
    Vue.js在中国的开发者中非常受欢迎,这主要有以下几个原因:易于学习:Vue.js的学习曲线相对较平缓,对于初学者来说更加友好。它的API和设计都非常直观,使得开发者可以快速上手并开始开发。灵活性:Vue.js设计为渐进式的,这意味着开发者可以根据项目的需要选择使用部分或全部的Vue.js功能......
  • Python的嵌套字典
    基于字典的基本语法:字典名称={“键”:值}在这里键可以有:整形,浮点型,字符串型,元祖(键的值是不变的),值可以取任意数据类型看准键(Key)值(value)对在这里演示一个嵌套字典的案例:stu_score_dict={"王力宏":{"语文":88,"数学":77,"英语":46......