基本介绍 @
根据维基百科介绍
Namespaces are a feature of the Linux kernel that partition kernel resources such that one set of processes sees one set of resources, while another set of processes sees a different set of resources. The feature works by having the same namespace for a set of resources and processes, but those namespaces refer to distinct resources. Resources may exist in multiple namespaces. Examples of such resources are process IDs, host-names, user IDs, file names, some names associated with network access, and inter-process communication.
命名空间是 Linux 内核的一项功能,它将内核资源进行分区,使得一组进程看到一组资源,而另一组进程看到不同的资源。
这项功能的工作原理是,为资源集和进程使用相同的命名空间,但这些命名空间指向不同的资源。
资源可以存在于多个命名空间中。这类资源的例子包括进程 ID、主机名、用户 ID、文件名、与网络访问相关的某些名称以及进程间通信。
简单来说,该能力是用来进行进程间隔离的。
资源隔离 @
Linux namespace 实现了 8 项资源隔离,包括主机名、用户权限、文件系统、网络、进程号、进程间通信。
| namespace | 系统调用参数 | 隔离内容 | 内核版本 |
|---|---|---|---|
| UTS | CLONE_NEWUTS | 主机名和域名 | 2.6.19 |
| IPC | CLONE_NEWIPC | 信号量、消息队列和共享内存 | 2.6.19 |
| Time | CLONE_NEWTIME | 时钟 | 2.6.19 |
| PID | CLONE_NEWPID | 进程编号 | 2.6.24 |
| Cgroup | CLONE_NEWCGROUP | Cgroup | 2.6.24 |
| Network | CLONE_NEWNET | 网络设备、网络栈、端口等 | 2.6.29 |
| Mount | CLONE_NEWNS | 挂载点(文件系统) | 2.4.19 |
| User | CLONE_NEWUSER | 用户和用户组 | 3.8 |
可以通过查看文件/proc/[pid]/ns的方式查看当前进程下有哪些 namespace。
可以通过clone方法创建
// 通过传递flag标志来进行资源隔离
int clone(int (*child_func)(void *), void *child_stack, int flags, void *arg);
// 例如下例代码,用于隔离UTS、IPC、PID、Mount
static char c_stack[1024*1024];
clone(func, c_stack, SIGCHLD | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNS, NULL);
也可以使用unshare命令来创建
# 创建完全隔离的环境(类似容器)
sudo unshare --pid --uts --ipc --net --mount --fork bash
# 创建新的 PID 命名空间并运行进程
sudo unshare --pid --fork bash
# 创建新的网络命名空间
sudo unshare --net bash
# 与用户命名空间结合使用(无需 root)
unshare --user --map-root-user bash
namspace 创建 @
每个 namespace 在创建的时候会自动创建一个回环接口 lo ,默认不启用,可以通过 ip link set lo up 启用
ip netns ls # 查看ns列表
ip netns add {ns} # 添加ns
ip netns del {ns} # 删除ns
ip netns exec {ns} {command} # 在ns中执行命令
- 示例
# 创建两个ns
ip netns add ns_0
ip netns add ns_1
ip netns ls
namespace 网络 @
- 新创建的 namespace 默认不能和主机网络,以及其他 namespace 通信
- 使用 Linux 提供的
veth pair来完成通信
- 创建 veth pair
# 创建一对veth
ip link add <veth name> type veth peer name <peer name>
# 查看veth
ip link ls type veth
# 将veth xx加入到namespace yy中
ip link set <veth name> netns <ns name>
# 给veth pair配上ip地址
ip netns exec <ns name> ip link set <veth name> up
示例 @
# 创建一对veth并移动到两个ns
ip link add veth_0 type veth peer name veth_1
ip link set veth_0 netns ns_0
ip link set veth_1 netns ns_1
# 启用
ip netns exec ns_0 ip link set veth_0 up
ip netns exec ns_1 ip link set veth_1 up
# 查看ip地址
ip netns exec ns_0 ip addr
ip netns exec ns_1 ip addr
# 配置ip地址
ip netns exec ns_0 ip addr add 10.1.1.1/24 dev veth_0
ip netns exec ns_1 ip addr add 10.1.1.2/24 dev veth_1
# 查看路由表
ip netns exec ns_0 ip route
ip netns exec ns_1 ip route
# 测试连接
ip netns exec ns_0 ping 10.1.1.2
使用 bridge 来转接 @
# 新建一个bridge
ip link add br0 type bridge
ip link set dev br0 up
# 创建 3 个 veth pair
ip link add type veth
ip link add type veth
ip link add type veth
# 配置第 1 个 net0
ip link set dev veth1 netns net0
ip netns exec net0 ip link set dev veth1 name eth0
ip netns exec net0 ip addr add 10.0.1.1/24 dev eth0
ip netns exec net0 ip link set dev eth0 up
ip link set dev veth0 master br0
ip link set dev veth0 up
# 配置第 2 个 net1
ip link set dev veth3 netns net1
ip netns exec net1 ip link set dev veth3 name eth0
ip netns exec net1 ip addr add 10.0.1.2/24 dev eth0
ip netns exec net1 ip link set dev eth0 up
ip link set dev veth2 master br0
ip link set dev veth2 up
# 配置第 3 个 net2
ip link set dev veth5 netns net2
ip netns exec net2 ip link set dev veth5 name eth0
ip netns exec net2 ip addr add 10.0.1.3/24 dev eth0
ip netns exec net2 ip link set dev eth0 up
ip link set dev veth4 master br0
ip link set dev veth4 up
docker 为了安全性,将 iptables 里面 filter 表的 FORWARD 链的默认策略设置成了 drop,所有不符合 docker 规则的数据包都不会被 forward,导致 ping 不通
为 br0 添加一条 iptables 规则 @
iptables -A FORWARD -i br0 -j ACCEPT
nsenter命令 @
nsenter 是 “namespace enter” 的缩写,它允许用户进入一个或多个已存在的 Linux 内核命名空间,并在其中执行指定的命令或启动新的 shell。
nsenter 命令的基本语法为 nsenter [options] <program> [<argument>...]
调试容器网络
# 获取容器PID
pid=$(docker inspect -f '{{.State.Pid}}' <container_name>)
# 进入容器的网络命名空间并执行ifconfig
nsenter -t $pid -n ifconfig
# 或者进入网络命名空间后启动一个shell,以便连续执行多个命令
nsenter -t $pid -n /bin/bash
进入容器环境
类似于docker exec的效果
# 进入容器的 mount, UTS, IPC, net 和 PID 命名空间
nsenter --target $pid --mount --uts --ipc --net --pid /bin/bash