探索 Linux Namespace:Docker 隔离的神奇背后

news2024/11/19 6:19:13

来自:探索云原生 https://www.lixueduan.com

原文:https://www.lixueduan.com/posts/docker/03-container-core/

namespace-kind

在 深入理解 Docker 核心原理:Namespace、Cgroups 和 Rootfs 一文中我们分析了 Docker 是由三大核心技术实现的。

docker-core

今天就一起分析 Docker 三大核心技术之一的 Linux Namespace。

后续文章会演示如何从零实现一个简易的 Docker,这里先简单了解下 Docker 的核心原理。

更新记录

第一版:写于 2022-02-11

第二版:更新于 2023.11.03,迁移到公众号,重制了一下

当我们通过 docker run -it 启动并进入一个容器之后,会发现不论是进程、网络还是文件系统,好像都被隔离了,就像这样:

[root@docker cpu]# docker run -it busybox
/ # 
/ # ps
PID   USER     TIME  COMMAND
    1 root      0:00 sh
    7 root      0:00 ps
/ # ip a 
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
120: eth0@if121: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever
/ # ls
bin    dev    etc    home   lib    lib64  proc   root   sys    tmp    usr    var
  • ps 命令看不到宿主机上的进程
  • ip 命令也只能看到容器内部的网卡
  • ls 命令看到的文件好像也和宿主机不一样

这就是 Docker 核心之一,借助 Linux Namespace 技术实现了视图隔离。

看起来容器和宿主机隔离开了

接下来就让我们一起探索一下 Linux Namespace 以及 Docker 是如何借助该能力来实现隔离的。

1. 什么是 Linux 命名空间(Namespace)?

Namespace 是 Linux 提供的一种内核级别环境隔离的方法。

可以使得处于不同 namespace 的进程拥有独立的全局系统资源,改变一个 namespace 中的系统资源只会影响当前 namespace 里的进程,对其他 namespace 中的进程没有影响。

简单来说:namespace 就是对资源的逻辑隔离

目前,Linux 内核里面实现了 8 种不同类型的 namespace:

namespace-kind

分类系统调用参数隔离内容相关内核版本
Mount namespacesCLONE_NEWNSMount pointsLinux 2.4.19
UTS namespacesCLONE_NEWUTSHostname and NIS domain nameLinux 2.6.19
IPC namespacesCLONE_NEWIPCSystem V IPC, POSIX message queuesLinux 2.6.19
PID namespacesCLONE_NEWPIDProcess IDsLinux 2.6.24
Network namespacesCLONE_NEWNETNetwork devices, stacks, ports, etc.始于Linux 2.6.24 完成于 Linux 2.6.29
User namespacesCLONE_NEWUSERUser and group IDs始于 Linux 2.6.23 完成于 Linux 3.8)
Cgroup namespaceCLONE_NEWCGROUPCgroup root directoryLinux 4.6
Time namespaceCLONE_NEWTIMEBoot and monotonicLinux 5.6

前面 6 种是比较常见的,后面两种 Cgroup Namespace 以及 Time Namespace 则是比较少见。

相关API

和 namespace 相关的函数只有四个,这里简单的看一下:

namespace-api

  • clone
  • setns
  • unshare
  • ioctl_ns

clone:创建一个新的进程并把他放到新的 namespace 中。

int clone(int (*fn)(void *), void *stack, int flags, void *arg, ...
                 /* pid_t *parent_tid, void *tls, pid_t *child_tid */ );
 /*
 flags:
    指定一个或者多个上面的CLONE_NEW*(当然也可以包含跟namespace无关的flags), 
    这样就会创建一个或多个新的不同类型的namespace, 
    并把新创建的子进程加入新创建的这些namespace中。
 */

setns:将当前进程加入到已有的 namespace 中。

int setns(int fd, int nstype);
/*
fd:
    指向/proc/[pid]/ns/目录里相应namespace对应的文件,
    表示要加入哪个namespace
nstype:
    指定namespace的类型(上面的任意一个CLONE_NEW*):
    1. 如果当前进程不能根据fd得到它的类型,如fd由其他进程创建,
    并通过UNIX domain socket传给当前进程,
    那么就需要通过nstype来指定fd指向的namespace的类型
    2. 如果进程能根据fd得到namespace类型,比如这个fd是由当前进程打开的,
    那么nstype设置为0即可
*/

unshare:使当前进程退出指定类型的 namespace,并加入到新创建的 namespace(相当于创建并加入新的 namespace)。

int unshare(int flags);
/*
flags:
    指定一个或者多个上面的CLONE_NEW*,
    这样当前进程就退出了当前指定类型的namespace并加入到新创建的namespace
*/

ioctl_ns:查询 namespace 信息。

new_fd = ioctl(fd, request);
/*
fd: 指向/proc/[pid]/ns/目录里相应namespace对应的文件
request: 
  NS_GET_USERNS: 返回指向拥有用户的文件描述符namespace fd引用的命名空间
    NS_GET_PARENT: 返回引用父级的文件描述符由fd引用的命名空间的命名空间。
*/

看完之后大致可以这样分类:

  • clone、unshare:加入新 namespace
  • setsns:加入已有 namespace
  • ioctl_ns:主要用于查询

clone 和 unshare 都是加入新 namespace,二者有什么区别呢?

二者的功能都是创建并加入新的 namespace, 区别在于:

  • unshare 是使 当前进程 加入新的 namespace
  • clone 是创建一个新的子进程,然后让 子进程 加入新的 namespace,而当前进程保持不变

查看进程所属的 namespaces

系统中的每个进程都有 /proc/[pid]/ns/ 这样一个目录,里面包含了这个进程所属 namespace 的信息,里面每个文件的描述符都可以用来作为 setns 函数(后面会介绍)的参数。

#查看当前bash进程所属的namespace
 lixd  ~  ls -l /proc/$$/ns
total 0
lrwxrwxrwx 1 lixd lixd 0 Jan  6 19:00 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 lixd lixd 0 Jan  6 19:00 ipc -> 'ipc:[4026532227]'
lrwxrwxrwx 1 lixd lixd 0 Jan  6 19:00 mnt -> 'mnt:[4026532241]'
lrwxrwxrwx 1 lixd lixd 0 Jan  6 19:00 net -> 'net:[4026531992]'
lrwxrwxrwx 1 lixd lixd 0 Jan  6 19:00 pid -> 'pid:[4026532243]'
lrwxrwxrwx 1 lixd lixd 0 Jan  6 19:00 pid_for_children -> 'pid:[4026532243]'
lrwxrwxrwx 1 lixd lixd 0 Jan  6 19:00 user -> 'user:[4026531837]'
lrwxrwxrwx 1 lixd lixd 0 Jan  6 19:00 uts -> 'uts:[4026532242]

ipc:[4026532227]为例,其中 ipc 是 namespace 的类型,4026532227 是 inode number。

如果两个进程的 ipc namespace 的 inode number一样,说明他们属于同一个 namespace。

这条规则对其他类型的 namespace 也同样适用。

namespace 数量限制与回收策略

linux 也限制了 namespace 的数量,不能无限制的创建 namespace,具体限制一般在 /proc/sys/user 目录中。具体如下:

$ tree /proc/sys/user/
/proc/sys/user/
├── max_cgroup_namespaces
├── max_inotify_instances
├── max_inotify_watches
├── max_ipc_namespaces
├── max_mnt_namespaces
├── max_net_namespaces
├── max_pid_namespaces
├── max_user_namespaces
└── max_uts_namespaces

$ cat /proc/sys/user/max_pid_namespaces
6784

以看到,当前系统中 PID namespace 最多可以创建 6784 个。

既然数量有限制,那已经创建的 namespace 什么时候会被销毁回收呢?

规则还是比较好理解的:当一个 namespace 中的所有进程都结束或者移出该 namespace 时,该 namespace 将会被销毁。

这也解释了为什么没有创建 namespace 的 API,因为刚创建的 namespace 没有任何进程,立马就会被回收。

不过也有一些特殊情况,可以再没有进程的时候保留 namespace:

  • 存在打开的 FD,或者对 /proc/[pid]/ns/* 执行了 bind mount
  • 存在子 namespace
  • 它是一个拥有一个或多个非用户 namespace 的 namespace。
  • 它是一个 PID namespace,并且有一个进程通过 /proc/[pid]/ns/pid_for_children 符号链接引用了这个 namespace。
  • 它是一个 Time namespace,并且有一个进程通过 /proc/[pid]/ns/time_for_children 符号链接引用了这个 namespace。
  • 它是一个 IPC namespace,并且有一个 mqueue 文件系统的 mount 引用了该 namespace
  • 它是一个 PIDnamespace,并且有一个 proc 文件系统的 mount 引用了该 namespace

一句话描述:当 namespace 有被使用时就不会被回收,反之则会被回收。

2. 使用 Go 语言操作 Linux 命名空间示例

UTS Namespace

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

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

以下程序展示了如何在 Go 中切换 UTS Namespace。

// 注: 运行时需要 root 权限。
func main() {
	cmd := exec.Command("bash")
	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.Fatalln(err)
	}
}

运行并测试

DESKTOP-9K4GB6E# go run main.go
root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker#

运行后会进入了一个新的 shell 环境。

查看以下是否真的进入了新的 UTS Namespace。

首先使用 pstree查看进程关系:

root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# pstree -pl
init(1)─┬─init(1272)───init(1273)───server(1274)─┬─{server}(1282)
        │                                        ├─{server}(1283)
        │                                        ├─{server}(1284)
        │                                        ├─{server}(1285)
        │                                        ├─{server}(1286)
        │                                        ├─{server}(1287)
        │                                        ├─{server}(1288)
        │                                        └─{server}(1289)
        ├─init(3701)───init(3702)───zsh(3703)───su(7520)───bash(7521)───zsh(7575)───go(8104)─┬─main(8182)─┬─bash(8187)───pstree(8194)
        │                                                                                    │            ├─{main}(8183)
        │                                                                                    │            ├─{main}(8184)
        │                                                                                    │            ├─{main}(8185)
        │                                                                                    │            └─{main}(8186)
        │                                                                                    ├─{go}(8105)
        │                                                                                    ├─{go}(8106)
        │                                                                                    ├─{go}(8107)
        │                                                                                    ├─{go}(8108)
        │                                                                                    ├─{go}(8109)
        │                                                                                    ├─{go}(8110)
        │                                                                                    ├─{go}(8111)
        │                                                                                    ├─{go}(8112)
        │                                                                                    ├─{go}(8117)
        │                                                                                    └─{go}(8143)
        ├─init(3763)───init(3764)───zsh(3765)
        ├─init(5171)───init(5172)───fsnotifier-wsl(5173)
        ├─init(7459)───init(7460)───bash(7461)───su(7476)───bash(7477)
        ├─{init}(5)
        └─{init}(6)

主要关注这条:

├─init(3701)───init(3702)───zsh(3703)───su(7520)───bash(7521)───zsh(7575)───go(8104)─┬─main(8182)─┬─bash(8187)

main 程序 pid 为 8182,后续新创建的 bash pid 为 8187,现在查看二者 uts 是否相同即可:

root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# readlink /proc/8182/ns/uts
uts:[4026532242]
root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# readlink /proc/8187/ns/uts
uts:[4026532386]

可以发现二者确实不在一个 UTS Namespace 中。由于 UTS Namespace hostname 做了隔离 所以在这个环境内修改 hostname 应该不影响外部主机, 下面来做 下实验。

在这个新的 bash 环境中修改 hostname

root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# hostname bash
root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# hostname
bash

新开一个在宿主机上查看 hostname:

 lixd  ~ $ hostname
DESKTOP-9K4GB6E

可以看到外部的 hostname 并没有被修改影响,由此可了解 UTS Namespace 的作用。

IPC Namespace

IPC Namespace 用来隔离 sys V IPC和 POSIX message queues

每个 IPC Namespace 都有自己的 Sys V IPC 和 POSIX message queues。

微调一下程序,只是修改了 Cloneflags,新增了 CLONE_NEWIPC,表示同时创建 IPC Namespace。

// 注: 运行时需要 root 权限。
func main() {
	cmd := exec.Command("bash")
	cmd.SysProcAttr = &syscall.SysProcAttr{
		// Cloneflags: syscall.CLONE_NEWUTS,
		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.Fatalln(err)
	}
}

运行并测试:

# 先查看宿主机上的 ipc message queue
DESKTOP-9K4GB6E# ipcs -q
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages

# 然后创建一个 
DESKTOP-9K4GB6E# ipcmk -Q
Message queue id: 0
# 再次查看,发现有了
DESKTOP-9K4GB6E# ipcs -q
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages
0x70ffd07c 0          root       644        0            0

运行程序进入新的 shell

DESKTOP-9K4GB6E# go run main.go
root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# ipcs -q
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages

可以发现,在新的 Namespace 中已经看不到宿主机上的 message queue 了。说明 IPC Namespace 创建成功,IPC 已经被隔离。

PID Namespace

PID Namespace是用来隔离进程ID的

同样一个进程在不同的PID Namespace里可以拥有不同的PID。

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

再次调整程序,增加 PID flags:

// 注: 运行时需要 root 权限。
func main() {
	cmd := exec.Command("bash")
	cmd.SysProcAttr = &syscall.SysProcAttr{
		// Cloneflags: syscall.CLONE_NEWUTS,
		// Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC,
		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.Fatalln(err)
	}
}

运行并测试:

DESKTOP-9K4GB6E# go run main.go
root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# pstree -pl
init(1)─┬─init(1272)───init(1273)───server(1274)─┬─{server}(1282)
        │                                        ├─{server}(1283)
        │                                        ├─{server}(1284)
        │                                        ├─{server}(1285)
        │                                        ├─{server}(1286)
        │                                        ├─{server}(1287)
        │                                        ├─{server}(1288)
        │                                        └─{server}(1289)
        ├─init(3701)───init(3702)───zsh(3703)───su(7520)───bash(7521)───zsh(7575)───go(9103)─┬─main(9184)─┬─bash(9189)───pstree(9196)
        │                                                                                    │            ├─{main}(9185)
        │                                                                                    │            ├─{main}(9186)
        │                                                                                    │            ├─{main}(9187)
        │                                                                                    │            └─{main}(9188)
        │                                                                                    ├─{go}(9104)
        │                                                                                    ├─{go}(9105)
        │                                                                                    ├─{go}(9106)
        │                                                                                    ├─{go}(9107)
        │                                                                                    ├─{go}(9108)
        │                                                                                    ├─{go}(9109)
        │                                                                                    ├─{go}(9110)
        │                                                                                    ├─{go}(9111)
        │                                                                                    ├─{go}(9112)
        │                                                                                    └─{go}(9120)
        ├─init(3763)───init(3764)───zsh(3765)
        ├─init(5171)───init(5172)───fsnotifier-wsl(5173)
        ├─init(7459)───init(7460)───bash(7461)───su(7476)───bash(7477)
        ├─init(8201)───init(8202)───zsh(8203)
        ├─{init}(5)
        └─{init}(6)

可以看到 main 函数 pid 为 9184,而新开的 bash pid 为 9189。

然后再新开的 bash 中查看自己的 pid:

这里只能使用 echo $$ 命令查看,ps、top 等命令会查看到其他 Namespace 中的信息。

root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# echo $$
1

发现 pid 是1,说明再新开的 PID Namespace 中只有一个 bash 这个进程,而且被伪装成了 1 号进程。

Mount Namespace

Mount Namespace用来隔离各个进程看到的挂载点视图

在不同Namespace的进程中,看到的文件系统层次是不一样的。 在Mount Namespace中调用mount()和umount()仅仅只会影响当前Namespace内的文件系统,而对全局的文件系统是没有影响的。

看到这里,也许就会想到chroot(),它也是将某一个子目录变成根节点。但是,Mount Namespace不仅能实现这个功能,而且能以更加灵活和安全的方式实现。

需要注意的是,Mount Namespace 的 flag 是CLONE_NEWNS,直接是 NEWNS 而不是 NEWMOUNT,因为 Mount Namespace 是 Linux 中实现的第一个 Namespace,当时也没想到后续会有很多类型的 Namespace 加入。

再次修改代码,增加 Mount Namespace 的 flag

	cmd.SysProcAttr = &syscall.SysProcAttr{
		// Cloneflags: syscall.CLONE_NEWUTS,
		// Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC,
		// Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID,
		Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS,
	}

运行并测试:

首先运行程序并在新的 bash 环境中查看 /proc

DESKTOP-9K4GB6E# go run main.go
root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# ls /proc
1     3764  7476  9476       cmdline    driver       kallsyms     loadavg  net           swaps        vmallocinfo
1272  3765  7477  9557       config.gz  execdomains  kcore        locks    pagetypeinfo  sys          vmstat
1273  5171  7520  9562       consoles   filesystems  key-users    mdstat   partitions    sysvipc      zoneinfo
1274  5172  7521  9569       cpuinfo    fs           keys         meminfo  sched_debug   thread-self
3701  5173  7575  acpi       crypto     interrupts   kmsg         misc     schedstat     timer_list
3702  7459  8201  buddyinfo  devices    iomem        kpagecgroup  modules  self          tty
3703  7460  8202  bus        diskstats  ioports      kpagecount   mounts   softirqs      uptime
3763  7461  8203  cgroups    dma        irq          kpageflags   mtrr     stat          version

可以看到,有一大堆文件,这是因为现在查看到的其实是宿主机上的 /proc 目录。

现在把 proc 目录挂载到当前 Namespace 中来:

root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# mount -t proc proc /proc

root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# ls /proc
1          cmdline    diskstats    interrupts  key-users    loadavg  mounts        schedstat  sysvipc      vmallocinfo
10         config.gz  dma          iomem       keys         locks    mtrr          self       thread-self  vmstat
acpi       consoles   driver       ioports     kmsg         mdstat   net           softirqs   timer_list   zoneinfo
buddyinfo  cpuinfo    execdomains  irq         kpagecgroup  meminfo  pagetypeinfo  stat       tty
bus        crypto     filesystems  kallsyms    kpagecount   misc     partitions    swaps      uptime
cgroups    devices    fs           kcore       kpageflags   modules  sched_debug   sys        version

可以看到,少了一些文件,少的主要是数字命名的目录,因为当前 Namespace 下没有这些进程,自然就看不到对应的信息了。

此时就可以通过 ps 命令来查看了:

root@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 13:13 pts/2    00:00:00 bash
root        11     1  0 13:13 pts/2    00:00:00 ps -ef

可以看到,在当前 Namespace 中 bash 为 1 号进程。

这就说明,当前 Mount Namespace 中的 mount 和外部是隔离的,mount 操作并没有影响到外部,Docker volume 也是利用了这个特性。

User Namespace

User Narespace 主要是隔离用户的用户组ID

也就是说,一个进程的 UserID 和GroupID 在不同的 User Namespace 中可以是不同的。

比较常用的是,在宿主机上以一个非 root 用户运行创建一个 User Namespace, 然后在 User Namespace 里面却映射成 root 用户。这意味着,这个进程在 User Namespace 里面有 root 权限,但是在 User Namespace 外面却没有 root 的权限。

再次修改代码,增加 User Namespace 的 flag:

	cmd.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS | syscall.CLONE_NEWUSER,
	}

运行并测试:

首先在宿主机上查看一个 user 和 group:

DESKTOP-9K4GB6E# id
uid=0(root) gid=0(root) groups=0(root)

可以看到,此时是 root 用户。

运行程序,进入新的 bash 环境:

DESKTOP-9K4GB6E# go run main.go
nobody@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker$ id
uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)

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

Network Namespace

Network Namespace 是用来隔离网络设备、IP 地址端口等网络栈的 Namespace

Network Namespace 可以让每个容器拥有自己独立的(虛拟的)网络设备,而且容器内的应用可以绑定到自己的端口,每个 Namespace 内的端口都不会互相冲突。

在宿主机上搭建网桥后,就能很方便地实现容器之间的通信,而且不同容器上的应用可以使用相同的端口。

再次修改代码,增加 Network Namespace 的 flag:

	cmd.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS | 
			syscall.CLONE_NEWUSER | syscall.CLONE_NEWNET,
	}

运行并测试:

首先看一下宿主机上的网络设备:

DESKTOP-9K4GB6E# 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
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: bond0: <BROADCAST,MULTICAST,MASTER> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 22:2f:1f:8e:f7:72 brd ff:ff:ff:ff:ff:ff
3: dummy0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 7e:46:8b:04:23:81 brd ff:ff:ff:ff:ff:ff
4: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
    link/ipip 0.0.0.0 brd 0.0.0.0
5: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
    link/sit 0.0.0.0 brd 0.0.0.0
6: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:15:5d:6e:f2:65 brd ff:ff:ff:ff:ff:ff
    inet 172.18.167.21/20 brd 172.18.175.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::215:5dff:fe6e:f265/64 scope link
       valid_lft forever preferred_lft forever

有 lo、eth0 等6 个设备。

然后运行程序:

DESKTOP-9K4GB6E# go run main.go
nobody@DESKTOP-9K4GB6E:/home/lixd/projects/docker/mydocker$ ip addr
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
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
    link/ipip 0.0.0.0 brd 0.0.0.0
3: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
    link/sit 0.0.0.0 brd 0.0.0.0

可以发现,新的 Namespace 中只有3个设备了,说明 Network Namespace 生效了。

3. 小结

  • 0)Docker 使用 namespace 进行隔离。
  • 1)Namespace 的本质:Linux Namespace 是 Linux 提供的一种内核级别环境隔离的方法,本质就是对全局系统资源的一种封装隔离
  • 2)Namespace 的使用:Namespace API 一共 4个,最常用的就是 clone,而 Go 已经把 clone 调用给封装好了,使用时只需要传入不同参数即可控制创建不同 Namespace。

namespace-kind

namespace-api

4. 参考

  • overview of Linux namespaces
  • Namespaces in operation, part 1: namespaces overview
  • Linux namespaces
  • Linux_namespaces
  • DOCKER基础技术:LINUX NAMESPACE(上)
  • DOCKER基础技术:LINUX NAMESPACE(下)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1283108.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【ARM Trace32(劳特巴赫) 使用介绍 12 -- Trace32 常用命令之 d.dump | data.dump 介绍】

文章目录 Trace32 常用命令之 d.dump | data.dump 介绍1 字节显示 (Byte)4 字节显示&#xff08;word&#xff09;8 字节显示&#xff08;通常long&#xff09;十进制显示显示指定列数显示地址范围内的值 Trace32 常用命令之 d.dump | data.dump 介绍 在 TRACE32 调试环境中&a…

S32K116新建工程Debug可以运行,冷启动无法运行问题分析

S32K116使用IAR建立工程后&#xff0c;软件debug可以运行&#xff0c;断电冷启动无法运行。 这种现象基本上都是RAM未初始化导致&#xff0c;由于Debug时&#xff0c;调试器会自动初始化芯片&#xff0c;很多问题都不会暴露处理。 大家可以开一下Startup的汇编文件&#xff0c;…

羊大师教你如何在冬天运动,然后悄悄惊艳所有人

羊大师教你如何在冬天运动&#xff0c;然后悄悄惊艳所有人 寒冷的冬季&#xff0c;寂静的清晨&#xff0c;你是否也曾感到在冰冷的天气中进行锻炼是一件非常困难的事情&#xff1f;但是&#xff0c;现在请跟随小编羊大师一起来探索冬季秘密运动&#xff0c;让你在春节惊艳众人…

XC4060 40V降5V/3.3V 0.6A小电流高耐压芯片 适用于单片机供电输出、电池供电设备

XC4060器件是高效率&#xff0c;同步降压DC/DC稳压器。具有较宽的输入范围&#xff0c;它们适用于广泛的应用&#xff0c;例如来自非稳压源的功率调节。他们的特点是一个长距离(500mQ/300mQ2型) 内部开关的效率最高 (92%)。Sum od (非A选项)和PWM模式(A选项)&#xff0c;工作频…

Windows server 2019 域环境部署

环境准备 准备3台服务器&#xff0c;配置都是8g2核&#xff0c;50g硬盘&#xff0c;操作系统版本Windows Server 2019 Datacenter 域服务器&#xff1a;adc&#xff0c;192.168.56.120服务器1&#xff1a;server1:&#xff0c;192.168.56.121服务器2&#xff1a;server2&…

如何使用gdb调试fork程序

代码示例 #include<stdio.h> #include<unistd.h> #include<sys/types.h> #include<stdlib.h> #include<sys/wait.h>int main(int argc, const char* argv[]) {pid_t pid -1;int status 0;int ret -1; // 创建子进程。若创建成功&#xff0c;…

MySQL基础『数据类型』

✨个人主页&#xff1a; 北 海 &#x1f389;所属专栏&#xff1a; MySQL 学习 &#x1f383;操作环境&#xff1a; CentOS 7.6 阿里云远程服务器 &#x1f381;软件版本&#xff1a; MySQL 5.7.44 文章目录 1.数据类型一览2.整型2.1.INT2.2.BIT 3.浮点数3.1.FLOAT3.2.DECIMAL3…

【c】序列中整数去重

数组中的元素不好直接删除&#xff0c;我们可以把重复的数做标记&#xff0c;将他赋值为0&#xff0c;然后正常打印数组&#xff0c;为0的跳过 #include<stdio.h> int main() {int n;scanf("%d",&n);int arr[n1];for(int i1;i<n;i){scanf("%d&quo…

LV.12 D22 IIC总线原理 学习笔记

一、IIC总线概述 1.1 IIC总线简介 IIC总线 IIC总线是Philips公司在八十年代初推出的一种串行、半双工总线 主要用于近距离、低速的芯片之间的通信&#xff1b;IIC总线有两根双向的信号线一根数据线SDA用于收发数据&#xff0c;一根时钟线SCL用于通信双方时钟的同步&…

<JavaEE> 单例模式的两种实现:“饿汉模式”和“懒汉模式”

目录 一、单例模式概述 二、“饿汉模式”实现单例模式 三、“懒汉模式”实现单例模式 3.1 单线程下的“懒汉模式” 3.2 多线程下的“懒汉模式” 一、单例模式概述 1&#xff09;什么是单例模式&#xff1f; 单例模式是一种设计模式。 单例模式可以保证某个类在程序中只存…

【C++】STL简介(了解)【STL的概念,STL的历史缘由,STL六大组件、STL的重要性、以及如何学习STL、STL的缺陷的讲解】

这里写自定义目录标题 一、什么是STL二、STL的版本1. 原始版本2. P. J. 版本3. RW版本★ 4. SGI版本 三、STL的六大组件四、STL的重要性五、如何学习STL六、STL的缺陷 一、什么是STL STL ( standard template libaray - 标准模板库 )&#xff1a;是C标准库 的重要组成部分&…

Spring到底是如何解决循环依赖问题的?

Spring作为当前使用最广泛的框架之一&#xff0c;其重要性不言而喻。所以充分理解Spring的底层实现原理对于咱们Java程序员来说至关重要&#xff0c;那么今天笔者就详细说说Spring框架中一个核心技术点&#xff1a;如何解决循环依赖问题&#xff1f; 什么是循环依赖问题&#x…

如何保持操纵机构丝杆的精度?

滚珠丝杆是操纵机构中的重要组成部分&#xff0c;可以传递较高的扭矩&#xff0c;并且具有低摩擦、高效率和快速响应的特性&#xff0c;这使得操纵机构能够实现高速、高精度的运动控制&#xff0c;这对于整个系统的性能和精度具有决定性的影响&#xff0c;保持操纵机构丝杆的精…

U-Shape Transformer for Underwater Image Enhancement(用于水下图像增强的U型Transformer)总结

背景 现有的水下数据集或多或少存在图像数量少、水下场景少、甚至不是真实场景等缺点&#xff0c;限制了数据驱动的水下图像增强方法的性能。此外&#xff0c;水下图像在不同颜色通道和空间区域的衰减不一致也没有统一的框架。 贡献 1&#xff09;提出了一种处理 UIE 任务的…

UI咨询公司-蓝蓝设计:顶级秘籍:提升UI设计吸引力的3大绝招

想要让你的UI设计在海量应用中脱颖而出&#xff0c;吸引用户眼球吗&#xff1f;如果你正在寻找提升UI设计吸引力的绝妙方法&#xff0c;那么你绝对不能错过本文&#xff01;我们将为你揭示顶级UI设计师都不会告诉你的3大绝招&#xff0c;让你轻松掌握提升UI设计吸引力的关键技巧…

Cysteine Protease inhibitor 921625-62-9科研

Cysteine Protease inhibitor 5-氨基-3-苯基-1,2,4-噻二唑 英文名称&#xff1a;Cysteine Protease inhibitor 中文名称&#xff1a;半胱氨酸蛋白酶抑制剂 化学名称&#xff1a;5-氨基-3-苯基-1,2,4-噻二唑 CAS&#xff1a;921625-62-9 外观&#xff1a;固体粉末 分子式&#…

智能联动第三方告警中心,完美实现故障响应全闭环

前言 我们曾讨论完善的告警策略是整个数据监控系统的重要组成部分&#xff08;参见《机智的告警策略&#xff0c;完善监控系统的重要一环》&#xff09;&#xff0c;介绍了如何配置告警通知以及场景示例&#xff0c;帮助用户及时更多潜在的故障和问题&#xff0c;有效地保障系…

值班日历实现不同人显示不同的颜色区别

前端UI用的移动端的vantUI。这里只是我的思路总结&#xff0c;和用什么UI框架关系不大。 先看效果图&#xff1a; <van-calendarref"calendar":poppable"false":show-confirm"false":style"{ height: 580px }":min-date"minD…

11.一维字符数组——求字符串长度, 占内存字节数

文章目录 前言一、题目描述 二、题目分析 三、解题 程序运行代码 前言 本系列为一维字符数组编程题&#xff0c;点滴成长&#xff0c;一起逆袭。 一、题目描述 求字符串长度&#xff0c; 占内存字节数 二、题目分析 求字符串长度 法一&#xff1a; while(str[i]!‘\0’){ i…

webview2 runtime 安装错误 0x800700b7

最简单的一种是删除C:\Program Files (x86)\Microsoft文件夹&#xff0c;一般情况下&#xff0c;这个文件夹是空的&#xff0c;但在系统目录下&#xff0c;不允许删除&#xff0c;可以选中获取下管理员权限&#xff0c;或是用第三方软件&#xff0c;直接强制删除。 删除之后就…