【Linux】Namespace
一、概念
Linux Namespace 是 Linux 内核提供的一种特性,用于对系统资源进行隔离。通过 Namespace,不同的进程组可以拥有独立的系统资源视图,即使它们在同一台物理机器上运行。这种隔离机制使得容器技术成为可能,因为它允许在单个宿主机上运行多个隔离的容器实例,每个容器都拥有自己的网络、进程空间、用户ID等资源,而不会相互干扰。
二、类型
1、UTS namespaces:隔离主机名和域名信息
使用exec创建一个执行sh命令的新进程, 对该进程属性改为CLONE_NEWUTS,然后运行,
其中cmd的Run方法会阻塞父进程知道子进程执行结束。
package main
import (
"os/exec"
"syscall"
"os"
"fmt"
)
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{
fmt.Println("cmd.run err:",err)
}
}
在sh环境内执行以下命令更改hostname,sh环境中的hostname改变而主机host 未改变。
hostname -b bird
2、IPC namespaces:隔离进程间通信资源
在SysProcAttr类的CloneFlags中或上syscall.CLONE_NEWIPC,实现进程间通信资源隔离。
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{
fmt.Println("cmd run err:",err)
}
}
使用以下命令创建消息队列
ipcmk -Q
在sh环境中执行:
ipcs -q
发现没有消息队列,说明实现了进程间通信隔离,IPC已经被隔离。
3、PID namespaces:隔离进程ID
同理,添加syscall.CLONE_NEWPID
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{
fmt.Println("cmd run err:",err)
}
}
在主机上查看tiny_docker的pid为4828
在sh环境中查看pid为1,主机上的pid4828映射到Namespace后pid为1,实现了PID隔离。
但此时执行ps,top等命令还是会显示主机的信息,应为该命令使用/proc文件内容,此内容未被隔离。
4、Mount namespaces:隔离文件系统挂载点
添加syscall.CLONE_NEWNS(namespace缩写)
"mount"(挂载)是一个过程,它将文件系统(如硬盘驱动器、分区、磁盘映像文件、网络共享等)连接到一个已存在的文件系统树中。挂载操作使得文件系统能够被用户和程序访问,就像它们是本地文件系统的一部分一样。
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{
fmt.Println("cmd run err:",err)
}
}
执行ps -elf还是显示主机的进程,在sh中执行mount -t proc proc /proc将proc挂载到自己的namespace中,再执行ps -elf:
让我们分解这个命令:
mount
:这是用来挂载文件系统的命令。-t proc
:这指定了要挂载的文件系统类型,这里是proc
。proc
:这是源文件系统,对于proc
文件系统来说,源文件系统总是proc
。/proc
:这是挂载点,即挂载后文件系统在文件系统中的路径。/proc
是proc
文件系统的标准挂载点。
当你执行 mount -t proc proc /proc
命令时,你会将 proc
文件系统挂载到 /proc
目录。这个操作通常是自动完成的,因为大多数 Linux 发行版在系统启动时会自动挂载 proc
文件系统。这个文件系统对于系统管理员和程序来说非常重要,因为它提供了一个接口来获取和操作内核和进程的信息。
5、User namespaces:隔离用户和组ID
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.Stdin=os.Stdin
cmd.Stdout=os.Stdout
cmd.Stderr=os.Stderr
if err:=cmd.Run();err!=nil{
fmt.Println("cmd run err:",err)
}
}
执行id命令查看当前的用户和用户组
可以看到UID不同,实现了UID的隔离。
6、Network namespaces:隔离网络设备和网络栈
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|syscall.CLONE_NEWNET,
}
cmd.Stdin=os.Stdin
cmd.Stdout=os.Stdout
cmd.Stderr=os.Stderr
if err:=cmd.Run();err!=nil{
fmt.Println("cmd run err:",err)
}
}