• Linux Namespace


    Linux Namespace

    Linux Namespace 是kernel的一个功能,用于隔离系统资源。比如说PID,User ID 等。(可以想象一下变成语言变量的命名空间)这样我们就可以将
    进程,网络接口,挂载点以及用户做一个隔离。

    比如说我们在同一台主机上,需要将资源分给多个类似于root的用户,而且要将其隔离开来互不影响。在我们不去使用多台主机的情况下这里Namespace就
    派上了用场。使用Namespace可以做到UID级别的隔离,也就是说我的ubuntu账户id为5.针对我虚拟化出来一个Namespace,在这个Namespace了里我有
    root用户的权限。在这个系统外,我就是一个普通的用户。甚至都不能访问系统外部。

    除了User Namespace,PID也可以被虚拟。从用户角度看,我的第一个进程pid是1。但是从系统的角度看这个1只是一个映射关系。他也许在系统上的pid
    是5或者x。父命名空间可以看得到子命名空间的状态等等,但是反过来确实不行的。这样下来,我们可以虚拟处多个pid是1的进程但是在父命名空间来看他
    们的pid就是a,b,c...等。(这里字母代指数字,)

    Linux一共实现了6中不通类型的Namespace

    Type System call parameters 系统调用参数 Kernel version
    Mount Namespace CLONE_NEWNS 2.4.19
    UTS Namespace CLONE_NEWUTS 2.6.19
    IPC Namespace CLONE_NEWIPC 2.6.19
    PID Namespace CLONE_NEWPID 2.6.24
    Network Namespace CLONE_NEWNET 2.6.29
    User Namespace CLONE_NEWUSER 3.8

    Namespace的API主要使用如下3个系统调用。

    • clone() 创建新进程。根据系统调用参数来判断是哪些类型的Namespace被创建,而且他们的子进程也会被包含到这些Namespace中。
    • unshare() 将进程移出某个Namespace。
    • setns() 将进程加入到某个Namespace。

    UTS Namespace

    UTS Namespace 主要是用来隔离nodename和domainname两个系统标识。在UTSNamespace中每个Namespace允许有自己的hostname

    使用GO来实现

    package main
    import (
    	"os/exec"
    	"syscall"
    	"os"
    	"log"
    	
    )
    func main() {//
        cmd := exec.Command("sh")  //可以理解为进程的名称
        cmd.SysProcAttr = &syscall.SysProcAttr{
        	Cloneflags: syscall.CLONE_NEWUTS,
        }
        cmd.Stdin = os.Stdin
        cmd.Stdout = os.Stdout
        cmd.Stderr = os.Stderr
        if err := cmd.Run(); err !=nil{
        	log.Fatal(err)
        }
    }
    

    exec.Command("sh")用来指定被fork出来的新进程内的初始命令,默认使用sh来执行。

    使用CLONE_NEWUTS这个标识符去创建一个UTS Namespace。GO帮我们封装了对clone方法的调用,在这段代码执行后就会进入sh的环境

    UTS以下是测试

    • 使用root权限去执行该代码
    • 使用pstree -pl 查看进程树

    可以看到我们新产生的进程和它的id

    • 输出当前进程的pid使用 echo $$
    • 可以验证UTS Namespace 使用 readlink/proc//ns/uts
    • 验证修改hostname 使用 hostname -b

    可以看到pid

    可以看到UTS的Namespace

    可以验证修改后的hostname
    综上:UTS Namespace达到的预期的效果,的确可以对hostname进行隔离。

    IPC Namespace

    IPC Namespace用来隔离System V IPC 和 POSIX message queues。每个IPC Namespace都有自己的System V IPC 和 POSIX message queues。

    修改一行代码就可以进行创建。

    package main
    import (
    	"log"
    	"os"
    	"os/exec"
    	"syscall"
    )
    
    func main() {
        cmd := exec.Command("sh")
        cmd.SysProcAttr = &syscall.SysProcAttr{
        	Cloneflags: syscall.CLONE_NEWUTS|syscall.CLONE_NEWIPC,  // 这里新添加了一行代码。
        }
        cmd.Stdin = os.Stdin
        cmd.Stdout = os.Stdout
        cmd.Stderr = os.Stderr
        if err := cmd.Run();err !=nil{
        	log.Fatal(err)
        }
    }
    

    可以看到 仅仅增加了syscall.CLONE_NEWIPC说我们希望同时创建一个新的IPC Namespace这样就可以了。下面进行演示。

    IPC的测试

    我们将在两个窗口(一个宿主窗口sh,一个普通的用户窗口)使用ipcs -q 命令进行查看, 使用ipcmk -Q命令进行创建。对比两个窗口的显示。

    • ipcs -q 命令用于查询 message queue
    • ipcms -Q 用于创建 message queue

    窗口sh

    其他窗口

    综上

    • 先使用whoami 查看用户
    • 使用$$ 查看各自所属的进程
    • 使用ipcs -q 查看message queue
    • 使用ipcmk -Q 创建message queue

    可以看到进行了隔离。

    PID Namespace

    PID Namespace是用来隔离进程ID的。同样一个进程在不同的PID里可以拥有不同的PID。例如使用在docker容器中我们会发现每个容器都有一个进程pid
    是1。但在容器外就不是1了。

    在上面代码中加入 syscall.CLONE_NWEPID, 代表为fork出来的子进程创建自己的PID Namespace。

    package main
    
    import (
    	"os/exec"
    	"os"
    	"log"
    	"syscall"
    )
    func main() {//
        cmd := exec.Command("sh")
        cmd.SysProcAttr = &syscall.SysProcAttr{
        	Cloneflags: 0X8000000|0X4000000|0X20000000, 
        }
        cmd.Stdin = os.Stdin
        cmd.Stdout = os.Stdout
        cmd.Stderr = os.Stderr
        if err := cmd.Run(); err !=nil{
        	log.Fatal(err)
        }
    }
    

    PID的测试

    我们同样打开两个窗口,一个是sh的一个是普通的。然后查看pid就可以很明显的看到区别了。

    sh窗口

    其他窗口

    综上:这里可以看到该操作打印了Namespace的pid其值为1.也就是说30958被映射到了Namespace的1.这里不能使用ps来查看,因为ps和top等命
    令会使用/proc内容。

    Mount Namespace

    Mount Namespace 是用来隔离各个进程的挂载点视图的。对于不同的Namespace的进程中,看到的文件系统是不一样的。在Mount Namespace中使用
    mount和umount仅仅只会影像当前Namespace内的文件系统,而对全局是没有影响的。(第一个加入的Namespace类型)

    chroot,它也是将一个子目录变成根节点。但是Mount Namespace更加的方便灵活和安全。

    package main
    
    import (
    	"os/exec"
    	"os"
    	"log"
    	"syscall"
    )
    func main() {//
        cmd := exec.Command("sh")
        cmd.SysProcAttr = &syscall.SysProcAttr{
        	Cloneflags: 0X8000000|0X4000000|0X20000000|syscall.CLONE_NEWNS, 
        }
        cmd.Stdin = os.Stdin
        cmd.Stdout = os.Stdout
        cmd.Stderr = os.Stderr
        if err := cmd.Run(); err !=nil{
        	log.Fatal(err)
        }
    }
    

    Mount Namespace测试

    • 运行代码
    • 查看/proc 下的内容。(宿主机下的)
    • 挂在到当前的MountNamespace下

    User Namespace

    User Namespace主要是隔离用户的用户组id。也就是说一个进程的User ID 和 Group ID 在User Namespace内外可以是不同的。比如说。在宿主机上
    以一个非root用户运行创建一个User Namespace,然后在User Namespace里面被映射成root用户。从Linux Kernel3.8开始,非root进程也可以创建
    User Namespace,并且此用户在Namespace里可以被映射出root,且在Namespace中有root权限。

    package main
    import (
     "os/exec"
     "os"
     "log"
     "syscall"
    )
    
    func main() {
        cmd := exec.Command("sh")
        cmd.SysProcAttr = &syscall.SysProcAttr{
        	Cloneflags: syscall.CLONE_NEWNS| syscall.CLONE_NEWIPC| syscall.CLONE_NEWUTS|
        		syscall.CLONE_NEWPID|syscall.CLONE_NEWUSER,
        }
       // cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(1), Gid:uint32(1)}
        cmd.Stdin = os.Stdin
        cmd.Stdout = os.Stdout
        cmd.Stderr = os.Stderr
        if err := cmd.Run();err != nil{
        	log.Fatal(err)
        }
        os.Exit(-1)
    }
    

    NWEUSER 测试

    在之前的基础上增加了 syscall.CLONE_NEWUSER。以root来运行这个程序,看看显示结果。
    宿主机的root用户

    sh的用户

    我们可以看到UID是不通的因此说明User Namespace生效了。

    Network Namespace

    Network Namespace 是用来隔离网络设备,ip,port等网络栈的Namespace。 Network Namespace可以让每个容器都有自己独立的网络设备。
    而且应用可以绑定到自己的端口,每个Namespace还不会冲突。在宿主机器上搭建网桥后,就能很方便的实现容器间的通信,而且不通的容器也可以使
    用相同的端口。

    package main
    
    import (
    	"os/exec"
    	"os"
    	"log"
    	"syscall"
    )
    func main() {//
        cmd := exec.Command("sh")
        cmd.SysProcAttr = &syscall.SysProcAttr{
        	Cloneflags: syscall.CLONE_NEWUSER|syscall.CLONE_NEWPID|syscall.CLONE_NEWUTS|
        		syscall.CLONE_NEWIPC|syscall.CLONE_NEWNS|syscall.CLONE_NEWNET,
        }
        cmd.Stdin = os.Stdin
        cmd.Stdout = os.Stdout
        cmd.Stderr = os.Stderr
        if err := cmd.Run(); err !=nil{
        	log.Fatal(err)
        }
    }
    

    Network测试

    • 先检查自己的网络设备 使用ifconfig
    • 在检查一下sh的网络设备 使用ifconfig

    宿主窗口

    sh窗口

    综上:可以看到宿主机器是由网卡等设备的,而sh并没有。两者是隔离的。

  • 相关阅读:
    IOS 使用动态库(dylib)和动态加载framework
    iOS 开发者应该知道的 ARM 结构
    解惑好文:移动端H5页面高清多屏适配方案
    js 单例模式的实现方式----闭包和构造函数内部判断
    解决express video 手机无法播放的问题
    前后端通吃的单元测试---mocha
    swift-ios开发pod的使用(1)
    UI 自动化测试工具BackstopJS简介(1)
    阿里妈妈-RAP项目的实践(3)
    阿里妈妈-RAP项目的实践(2)
  • 原文地址:https://www.cnblogs.com/Leon-The-Professional/p/9948870.html
Copyright © 2020-2023  润新知