Namespace

什么是 Namespace ?

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

介绍

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

Namespace 系统调用参数 内核版本 隔离内容
UTS (Unix Time-sharing System) CLONE_NEWUTS Linux 2.4.19 主机名与域名
IPC (Inter-Process Communication) CLONE_NEWIPC Linux 2.6.19 信号量、消息队列和共享内存
PID (Process ID) CLONE_NEWPID Linux 2.6.19 进程编号
Network CLONE_NEWNET Linux 2.6.24 网络设备、网络栈、端口等等
Mount CLONE_NEWNS Linux 2.6.29 挂载点(文件系统)
User CLONE_NEWUSER Linux 3.8 用户和用户组
  1. PID Namespace:

    • 不同用户的进程通过 PID Namespace 进行隔离,并且不同的 Namespace 中可以有相同的进程 ID。在 Docker 中,所有的 LXC(Linux 容器)进程的父进程是 Docker 进程,每个 LXC 进程具有不同的 Namespace。由于支持嵌套 Namespace,因此可以方便地实现 Docker 中的 Docker(Docker in Docker)。
  2. Net Namespace:

    • 有了 PID Namespace,每个 Namespace 中的进程能够相互隔离,但是网络端口仍然共享主机的端口。通过 Net Namespace 实现网络隔离,每个 Net Namespace 具有独立的网络设备、IP 地址、IP 路由表和 /proc/net 目录。这样,每个容器的网络就能够得到隔离。Docker 默认使用 veth(虚拟以太网)方式将容器中的虚拟网卡与主机上的 Docker 桥接器(docker0)连接起来。
  3. IPC Namespace:

    • 容器中的进程仍然使用常见的 Linux 进程间通信(IPC)方法,包括信号量、消息队列和共享内存。然而,与虚拟机不同的是,容器中的进程实际上是在具有相同 PID Namespace 的主机进程之间进行通信,因此在申请 IPC 资源时需要加入 Namespace 信息,每个 IPC 资源都有一个唯一的 32 位 ID。
  4. MNT Namespace:

    • 类似于 chroot,将进程限制在特定的目录下执行。MNT Namespace 允许不同 Namespace 的进程看到不同的文件结构,从而隔离了每个 Namespace 中进程所看到的文件目录。与 chroot 不同的是,每个 Namespace 中的容器在 /proc/mounts 中的信息仅包含所在 Namespace 的挂载点。
  5. UTS Namespace:

    • UTS("UNIX Time-sharing System")Namespace 允许每个容器拥有独立的主机名和域名,使其在网络上可以被视为一个独立的节点,而不仅仅是主机上的一个进程。
  6. 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 层代表容器可写层。

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 iamge

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

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。

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

容器基础-- namespace,Cgoup 和 UnionFS的更多相关文章

  1. C++ 顺序容器基础知识总结

    0.前言 本文简单地总结了STL的顺序容器的知识点.文中并不涉及具体的实现技巧,对于细节的东西也没有提及.一来不同的标准库有着不同的实现,二来关于具体实现<STL源码剖析>已经展示得全面细 ...

  2. docker容器基础

    一.docker容器基础6种名称空间:UTS.MOunt.IPC.PID.User.Net (1) Linux Namespaces:namespace 系统调用参数 隔离内容 内核版本  UTS   ...

  3. 08 | 白话容器基础(四):重新认识Docker容器

    你好,我是张磊.今天我和你分享的主题是:白话容器基础之重新认识Docker容器. 在前面的三次分享中,我分别从Linux Namespace的隔离能力.Linux Cgroups的限制能力,以及基于r ...

  4. 容器基础(二): 使用Namespace进行边界隔离

    Linux Namespace 容器技术可以认为是一种沙盒(sandbox), 为了实现沙盒/容器/应用间的隔离,就需要一种技术来对容器界定边界,从而让容器不至于互相干扰.当前使用的技术就是Names ...

  5. 容器基础(五): 实现一个简单容器sdocker

    在前面几部分的基础上, 我们更新一下代码,实现一个简单容器 sdocker. sdocker目录构成 linux: # tree . ├── Makefile ├── cpu-test.c # 由cp ...

  6. 容器基础(一): Docker介绍

    IaaS IaaS阶段, 用户租借基础设施,但是还是需要像以前管理服务器那样,用脚本或者手工方式在这些机器上部署应用.这个过程中当然难免会碰到云端机器和本地机器环境不一致的问题.想想每一次同步不同机器 ...

  7. Kubernetes 普及系列:容器基础入门

    随着云原生时代的来临,云以及分布式计算已经是时下最受欢迎的技术之一了.其中 Docker 作为最知名的容器平台,到底有着怎样的魅力来让其无人不知无人不晓?废话不多说,让我们开始逐层掀开容器技术的神秘面 ...

  8. 深入剖析Kubernetes学习笔记:容器基础(05-06)

    05 :从进程说起 1.容器本身没有价值,有价值的是"容器编排" 2.什么是进程? 一旦"程序"被执行起来,它就从磁盘上的二进制文件,变成 1.计算机内存中的数 ...

  9. 工作随笔—Java容器基础知识分享(持有对象)

    1. 概述 通常,程序总是运行时才知道的根据某些条件去创建新对象.在此之前,不会知道所需对象的数量,甚至不知道确切的类型,为解决这个普遍的编程问题:需要在任意时刻和任意位置创建任意数量的对象,所以,就 ...

  10. Docker容器基础认知

    ## 一. [docker](http://www.itxdm.me/archives/tag/docker/)概念 [Docker](http://www.itxdm.me/wp-content/p ...

随机推荐

  1. python入门教程之三编码问题

    1编码问题 Python文件中如果未指定编码,在执行过程中会出现报错: !/usr/bin/python print ("你好,世界") 以上程序执行输出结果为: 文件" ...

  2. 五月十一号java基础知识点

    1.通过add()方法向链表list输入1-10十个数for (int i = 1; i <11 ; i++) { list.add(i);//向链表添加1-10的整数 } import jav ...

  3. DG:Oracle查看是否搭建DataGuard

    Oracle查看是否是DataGuard 1.查看归档路径 show parameter log_archive_dest LOG_ARCHIVE_DEST_n, 归档文件的生成路径, LOCATIO ...

  4. docker安装python+nginx

    一个容器安装python和nginx dockerfile FROM centos:7.9.2009 USER root RUN yum install gcc openssl-devel bzip2 ...

  5. C++核心知识回顾(函数&参数、异常、动态分配)

    复习C++的核心知识 函数与参数 传值参数.模板函数.引用参数.常量引用参数 传值参数 int abc(int a,int b,int c) { return a + b * c; } a.b.c是函 ...

  6. ubutn在线服务器python Package安装到离线服务器

    1.在线服务器导出requirement.txt pip freeze > requirement.txt 该文件生成完毕后,需要做些修改,去掉不需要的库,否则下载的时候会出错. 2.下载whl ...

  7. MINIO使用

    1.作用 官网地址:https://docs.min.io/ 文件存储.文件对象的上传.下载和删除! 2.使用依赖 <dependency> <groupId>io.minio ...

  8. C#写一套最全的MySQL帮助类(包括增删改查)

    介绍说明:这个帮助类包含了六个主要的方法:ExecuteNonQuery.ExecuteScalar.ExecuteQuery.ExecuteQuery(泛型).Insert.Update和Delet ...

  9. Django简介 安装下载 app概念 主要目录介绍

    目录 Django简介 前戏 Django是一个开放源代码的Web应用框架,由Python写成.采用了MTV的框架模式,即模型M,视图V和模版T.这套框架是以比利时的吉普赛爵士吉他手Django Re ...

  10. 想打印k8s资源YAML结果搞懂了Client-Side & Server-Side Apply

    前言 由于查看k8s资源YAML时常看到沉长的YAML与手写的格式,相差甚远不利于阅读,经过探索官方文档,才理解什么是Client-Side & Server-Side Apply. 先看一下 ...