Linux Namespace是内核的一个概念,它可以隔离一系列的系统资源。
每一个命名空间应该有自己的init进程(PID为1),其他进程的PID依次递增,A和B空间都有PID为1的init进程,自子命名空间的进程映射到父命名空间的进程上,父命名空间可以知道每一个子命名空间的运行状态,而子命名空间之间是隔离的。
Linux有6种Namespace,用来隔离不同的资源:
UTS
UTS Namespace主要用来隔离nodename和hostname两个系统标识。
/*隔离hostname和nodename*/func main() {cmd := exec.Command("sh")cmd.SysProcAttr = &syscall.SysProcAttr{Cloneflags: syscall.CLONE_NEWUTS,}cmd.Stdin = os.Stdincmd.Stdout = os.Stdoutcmd.Stderr = os.Stderrif err := cmd.Run(); err != nil {log.Fatal(err)}}
进入新的sh进程后,使用pstree -pl命令打印进程树。上面带编译后的可执行文件是uts。
此时可以在这个进程中使用hostname -b 新名字来修改hostname,宿主机打印hostname不受影响。
IPC
/**隔离System V IPC和POSIX message queue*/func main() {cmd := exec.Command("sh")cmd.SysProcAttr = &syscall.SysProcAttr{Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC,}cmd.Stdin = os.Stdincmd.Stdout = os.Stdoutcmd.Stderr = os.Stderrif err := cmd.Run(); err != nil {log.Fatal(err)}}
在宿主机使用ipcs -q来查看所有的Message Queue,然后ipcmk -Q创建一个新的MQ。在新的sh进程中使用``ipcs -q``没有发现刚才创建的MQ。
PID
/**隔离进程ID同一个进程在不同PID namespace下可以拥有不同的PID*/func main() {cmd := exec.Command("sh")cmd.SysProcAttr = &syscall.SysProcAttr{Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID,}cmd.Stdin = os.Stdincmd.Stdout = os.Stdoutcmd.Stderr = os.Stderrif err := cmd.Run(); err != nil {log.Fatal(err)}}
在新的sh进程中使用echo $$会打印出进程ID,值为1;
而在宿主机上使用pstree -pl会打印出在宿主机上的进程真实ID。
真实ID为3524。
Mount
/**隔离各个进程看到的挂载点视图在不同namespace的进程中,看到的文件系统层次是不同的。在Mount Namespace中调用mount()和umount()仅仅只会影响当前Namespace内的文件系统,而对全局的文件系统是没有影响的。*/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.Stdincmd.Stdout = os.Stdoutcmd.Stderr = os.Stderrif err := cmd.Run(); err != nil {log.Fatal(err)}}
刚进入sh时挂载的文件系统还是宿主机的。mount -t proc proc /proc
-t proc是指挂载的是一个proc类型的文件系统,然后设备名是proc,目录是/proc。
User
/**隔离用户的用户组ID一个进程的User ID和Group ID在User namespace内外可以是不同的。比较常用的是,在宿主机上以一个非root用户运行创建一个User namespace,然后在User namespace中映射为root用户。*/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.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(1),Gid: uint32(1),}cmd.Stdin = os.Stdincmd.Stdout = os.Stdoutcmd.Stderr = os.Stderrif err := cmd.Run(); err != nil {log.Fatal(err)}}
id命令可以查看uid、gid和groups。
在宿主机和新的sh进程内使用id命令返回的结果是不同的。
Network
/**隔离网络设备、IP地址端口等网络栈可以让每个容器拥有自己独立的(虚拟的)网络设备,而且容器内的应用可以绑定到自己的端口。*/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_NEWNET,}cmd.Stdin = os.Stdincmd.Stdout = os.Stdoutcmd.Stderr = os.Stderrif err := cmd.Run(); err != nil {log.Fatal(err)}}
ifconfig命令可以查看网络设备。
