Linux 命名空间

Linux 命名空间

命名空间 描述
Mount(mnt) 隔离挂载点
Process ID(process) 隔离进程ID
Network(net) 隔离网络设备、协议栈、端口等
InterProcess Communication(ipc) 隔离进程间通信
UTS 隔离Hostname和NIS域名
User ID(user) 隔离用户和group ID

网络命名空间

# 创建一个网络空间
ip netns add <NAME>

# 在这个网络空间中启动一个bash,除了网络之外,其他的资源全部共享。
ip netns exec <NAME> bash

# 给这个命名空间分配一个网卡

# 先创建一个veth对
ip link add veth_a type veth peer name veth_b
# 列出来看一下
ip link list | grep veth_a
# 将其中一个veth转移到命名空间中
ip link set veth_a netns <NAME> 
# 再列出来看一下,发现其中一个没有了。
ip link list 
# 到命名空间中看另一个连接
ip netns exec <NAME> ip link list


# 配置这个网络接口
ip netns exec <NAME> bash
    ip addr add 10.1.1.1/24 dev veth_a
    ip link set veth_a up
    ip link set lo up
    ip addr show

# 配置对端的信息
ip addr add 10.1.1.2/24 dev veth_b
ip link set veth_b up

# 现在就能ping通了
ip netns exec <NAME> ping 10.1.1.1

# 在命名空间内配置默认路由
ip netns exec bash
    ip route add default via 10.1.1.1
    ip route show

# 使用IP伪装
iptables -t nat -A POSTROUTING -s 10.1.1.0/255.255.255.0 -o enx00e04c3600b8 -j MASQUERADE

# -->现在在命名空间内就可以访问外网了<--

UTS 命名空间

UTS命名空间的主要目的是独立出主机名和网络信息服务,简而言之就是不同于主机的主机名。

#include <sys/wait.h>
#include <stdio.h>
#include <sched.h>

#define STACK (1024 * 1024) 
static char stack[STACK];

int container_main(void*) { 
    sethostname("container",10);
    printf("Inside the container!\n");
    execv("/bin/bash",NULL); 
    return 0; 
} 

int main() { 
    printf("start a container!"); 
    int container_pid = clone(container_main, stack+STACK,CLONE_NEWUTS | SIGCHLD, NULL); 
    waitpid(container_pid, NULL, 0); 
    printf("Parent - container stopped!\n"); 
    return 0; 
} 

使用GCC编译这段程序:

gcc test.cpp -o test

使用root用户运行编译出来的文件:

sudo ./test

在启动的bash中输入命令:

hostname

将看到代码第九行设定的主机名。

IPC 命名空间

linux下的多个进程间的通信机制叫做IPC(Inter-Process Communication),它是多个进程之间相互沟通的一种方法。在linux下有多种进程间通信的方法:半双工管道命名管道消息队列信号信号量共享内存内存映射文件套接字等。

首先我们在主机中创建一个IPC消息队列

ipcmk -Q
# 消息队列 id:0

然后查看这个消息队列:

ipcs -q

使用GCC编译并运行下列代码(编译命令同上一个例子)

#include <sys/wait.h>
#include <stdio.h>
#include <sched.h>

#define STACK (1024 * 1024) 
static char stack[STACK];

int container_main(void*) { 
    printf("Inside the container!\n");
    execv("/bin/bash",NULL); 
    return 0; 
} 

int main() { 
    printf("start a container!"); 
    int container_pid = clone(container_main, stack+STACK,CLONE_NEWIPC | SIGCHLD, NULL); 
    waitpid(container_pid, NULL, 0); 
    printf("Parent - container stopped!\n"); 
    return 0; 
} 

在启动的bash中输入:

ipcs -q

看不到主机中的消息队列,反之在这个bash中创建的消息队列,主机也无法看到。

最后使用命令删除之前创建的消息队列:

ipcrm msg 0 

PID命名空间

命名空间内的PID是独立分配的,所以命名空间内的虚拟 PID可能会与命名空间外的 PID 相冲突,于是命名空间内的 PID 映射到命名空间外时会使用另外一个 PID

使用第一个案例的命令编译下面的代码:

#include <sys/wait.h>
#include <stdio.h>
#include <sched.h>
#include <stdlib.h>

#define STACK (1024 * 1024) 
static char stack[STACK];

int container_main(void*) { 
    printf("Inside the container! PID: [%d]\n",getpid());
    execv("/bin/bash",NULL); 
    return 0; 
} 

int main() { 
    printf("start a container! PID: [%d]\n", getpid()); 
    int container_pid = clone(container_main, stack+STACK,CLONE_NEWPID | SIGCHLD, NULL); 
    waitpid(container_pid, NULL, 0); 
    printf("Parent - container stopped!\n");
    return 0; 
} 

运行生成的二进制文件并着重关注两个函数开头的printf的输出。

有一个问题是在容器的bash中里输入pstop等命令,还是可以看到所有进程。因为,像ps, top这些命令会去读/proc文件系统,所以,因为文件系统没有被隔离,所以这些命令输出的东西都是相同的。

Mount命名空间

Mount namespaces是第一个被加入linuxnamespace,由于当时没想到还会引入其它的namespace,所以取名为CLONE_NEWNS,而没有叫CLONE_NEWMOUNT

#include <sys/wait.h>
#include <stdio.h>
#include <sched.h>
#include <stdlib.h>
#include <sys/mount.h>

#define STACK (1024 * 1024) 
static char stack[STACK];

int container_main(void*) { 
    printf("Inside the container! PID: [%d]\n",getpid());
    if (mount("proc","/proc","proc",0,NULL)<0){
        printf("fail!");
    }
     //重新mount proc到/proc下
    execv("/bin/bash",NULL);
    return 0;
} 

int main() { 
    printf("start a container! PID: [%d]\n", getpid()); 

    int container_pid = clone(container_main, stack+STACK,CLONE_NEWPID | CLONE_NEWNS | SIGCHLD, NULL); 
    waitpid(container_pid, NULL, 0); 
    printf("Parent - container stopped!\n");
    return 0; 
} 

编译上面的程序并运行,然后使用top命令,就只能看到容器内的进程。

qrcode

创建时间:2022-01-22 05:12:26

最后修改:2022-01-22 05:12:26