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
中里输入ps
,top
等命令,还是可以看到所有进程。因为,像ps
, top
这些命令会去读/proc
文件系统,所以,因为文件系统没有被隔离,所以这些命令输出的东西都是相同的。
Mount命名空间
Mount namespaces
是第一个被加入linux
的namespace
,由于当时没想到还会引入其它的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
命令,就只能看到容器内的进程。