linux的6种Namespace介绍

linux一共实现了6种不同类型的Namespace:

Namespace 类型 系统调用参数 内核版本
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() 创建新的进程
  • unshare() 将进程移除某个Namespace
  • sents() 将进程加入到Namespace

UTS Namespace

UTS Namespace 主要用来隔离nodename(hostname) 和domainname 两个系统标识。

在UTS Namespace里面, 每个Namespace 允许有自己的hostname 。

下面将使用Go 来做一个UTS Namespace 的例子。

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 出来的新进程内的初始命令,默认使用s h 来执行。下面就是设置系统调用参数, 像2.1.l 小节中讲到的一样,使用CL ONENE WU TS 这个标识符去创建一个UTS Namespace 。Go 帮我们封装了对clone() 函数的调用,这段代码执行后就会进入到一个s h 运行环境中。

执行
go run main. go 命令,在这个交互式环境里,使用pstree -pl 查看一下系统中进程之间的关系, 如下

sh-4.2# echo $$
23137
sh-4.2# pstree -p| grep 23137
           |-sshd(1291)-+-sshd(21695)---bash(21697)---go(23110)-+-utc(23132)-+-sh(23137)-+-grep(23215)

验证一下父进程和子进程是否不在同一个UTS Namespace 中, 看到确实不在同一个uts namespace中

sh-4.2# readlink /proc/23137/ns/uts 
uts:[4026532440]
sh-4.2# readlink /proc/23132/ns/uts 
uts:[4026531838]

所以在这个环境内修改hostname 应该不影响外部主机, 下面来做一下实验:

sh-4.2# hostname -b test
sh-4.2# hostname
test
开启新的终端查看hostname
[root@DH-PROXY-T01 gofile]# hostname
DH-PROXY-T01

IPC Namespace(消息队列)

IPC Namespace 用来隔离System V IPC 和POSIX message queues 每一个IPC Namespace都有自己的system v IPC 和POSIX message queue

样例:

package main

import (
    "os/exec"
    "syscall"
    "os"
    "log"
)

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)
    }

}

查看队列

[root@DH-PROXY-T01 gofile]# go run ipc.go 
sh-4.2#
sh-4.2# ipcmk -Q
消息队列 id:0
sh-4.2# ipcs -q

--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息      
0xfd247a01 0          root       644        0            0   

切换到另一个shell,查看,没有消息证明ipc隔离

sh-4.2# ipcs -q

--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息

PIO Namespace

PID Namespace 是用来隔离进程ID 的。同样一个进程在不同的PID Namespace 里可以拥有不同的PID 。这样就可以理解, 在docker container 里面, 使用ps -ef 经常会发现, 在容器内, 前台运行的那个进程PID 是1 , 但是在容器外,使用ps -ef 会发现同样的进程却有不同的PID , 这就是PID Namespace 做的事情。

在上一小结代码的基础上, 再修改一下代码, 添加一个syscall.CLONE_ NEWPID ,代表为fork 出来的子进程创建自己的PID Namespace 。

package main

import (
    "os/exec"
    "syscall"
    "os"
    "log"
)

func main() {
    cmd := exec.Command("sh")
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID,
    }
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    if err := cmd.Run(); err != nil {
        log.Fatal(err)
    }

}

要打开两个shell 。首先在宿主机上看一下进程树,找一下进程的真实PID

[root@DH-PROXY-T01 gofile]# go run ipc.go 
sh-4.2# echo $$
1
[root@DH-PROXY-T01 gofile]# pstree -p | grep ipc
           |-sshd(1291)-+-sshd(13183)-+-bash(13185)---go(13409)-+-ipc(13431)-+-sh(13436)

可以看到,该操作打印了当前Namespace 的PID , 其值为1 。也就是说,这个13431 的PID 被映射到Namespace 里后PID 为1。这里还不能使用ps 来查看, 因为ps 和top 等命令会使用/proc 内容,具体内容在下面的Mount Namespace 部分会进行讲解。

Mount Namespace

Mount Namespace 用来隔离各个进程看到的挂载点视图。在不同Names pace 的进程中, 看到的文件系统层次是不一样的。在Mount Namespace 中调用mount()和umount() 仅仅只会影响当前Namespace 内的文件系统,而对全局的文件系统是没有影响的。看到这里, 也许就会想到chroot()。它也是将某一个子目录变成根节点。但是, MountNames pace 不仅能实现这个功能,而且能以更加灵活和安全的方式实现。Mount Namespace 是Linux 第一个实现的Names pace 类型, 因此,它的系统调用参数是NEWNS ( New Namespace 的缩写)。

增加了NEWNS 标识,如下

package main

import (
    "os/exec"
    "syscall"
    "os"
    "log"
)

func main() {
    cmd := exec.Command("sh")
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS,
    }
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    if err := cmd.Run(); err != nil {
        log.Fatal(err)
    }

}

运行,执行命令,此时的/proc 还是宿主机的,执行ps -ef 无法正常运行

sh-4.2# ls /proc/
1      consoles   driver       iomem     key-users   mdstat   net       self      sysrq-trigger  version
acpi       cpuinfo    execdomains  ioports   kmsg    meminfo  pagetypeinfo  slabinfo  sysvipc    vmallocinfo
buddyinfo  crypto     fb       irq       kpagecount  misc     partitions    softirqs  timer_list     vmstat
bus    devices    filesystems  kallsyms  kpageflags  modules  sched_debug   stat      timer_stats    zoneinfo
cgroups    diskstats  fs       kcore     loadavg     mounts   schedstat swaps     tty
cmdline    dma        interrupts   keys      locks   mtrr     scsi      sys   uptime
sh-4.2# ps -ef
Error, do this: mount -t proc proc /proc

挂在后再执行,发现sh的pid为1,因为ps -ef是获取的proc的内容,所以当前的mount namespace和外部的空间是隔离的,proc 是一个文件系统,提供额外的机制,可以通过内核和内核模块将信息发送给进程。

sh-4.2# mount -t proc proc /proc
sh-4.2# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 09:26 pts/0    00:00:00 sh
root         7     1  0 09:30 pts/0    00:00:00 ps -ef

User Namespace

User N amespace 主要是隔离用户的用户组ID 。也就是说, 一个进程的User ID 和GroupID 在User Namespace 内外可以是不同的。比较常用的是,在宿主机上以一个非root 用户运行创建一个User Namespace , 然后在User Namespace 里面却映射成root 用户。这意味着, 这个进程在User Namespace 里面有root 权限,但是在User Namespace 外面却没有root 的权限。从Linux Kernel 3 . 8 开始, 非root 进程也可以创建User Namespace , 并且此用户在N amespace 里面可以被映射成root , 且在Namespace 内有root 权限。下面,继续以一个例子来描述, 代码如下。

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 | syscall.CLONE_NEWPID |
            syscall.CLONE_NEWNS | 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)
}

本例在原来的基础上增加了syscall.CLONE_NEWUSER 。首先,以root 来运行这个程序,运行前在宿主机上看一下当前的用户和用户组, 显示如下:

root@iZ254rt8xflZ :~/gocode/src/book# id
uid=O(root) gid=O(root ) groups=O(root )

可以看到我们是root 用户,接下来运行一下程序。
root@iZ254rt8xflZ : ~/ gocode/src/book# go run main.go

$ id
uid=65534 (nobody ) gid=65534(nogroup) groups=65534(nogroup)

可以看到, 它们的UID 是不同的,因此说明User Namespace 生效了。

Network Namespace

Network Namespace 是用来隔离网络设备、IP 地址端口等网络械的Namespace 。NetworkNamespace 可以让每个容器拥有自己独立的(虚拟的)网络设备,而且容器内的应用可以绑定到自己的端口,每个Namespace 内的端口都不会互相冲突。在宿主机上搭建网桥后,就能很方便地实现容器之间的通信,而且不同容器上的应用可以使用相同的端口。同样,在2.1.6 小节的代码的基础上增加syscall.CLONE_ NEWNET 标识符,如下:

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 | syscall.CLONE_NEWPID |
            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)
    }
    os.Exit(-1)
}

进入程序查看网络

[root@DH-PROXY-T01 gofile]# go run main.go 
sh-4.2# ifconfig 
sh-4.2# mount -t proc proc /proc
sh-4.2# ifconfig 
sh-4.2# 

返回宿主机查看网络

[root@DH-PROXY-T01 gofile]# ifconfig
ens192: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.50.133.98  netmask 255.255.255.0  broadcast 10.50.133.255
        ether 00:50:56:b2:47:aa  txqueuelen 1000  (Ethernet)

我们发现, 在Namespace 里面什么网络设备都没有。这样就能断定Network Namespace 与宿主机之间的网络是处于隔离状态了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355

推荐阅读更多精彩内容