Linux Namespace是内核的一个概念,它可以隔离一系列的系统资源。
每一个命名空间应该有自己的init进程(PID为1),其他进程的PID依次递增,A和B空间都有PID为1的init进程,自子命名空间的进程映射到父命名空间的进程上,父命名空间可以知道每一个子命名空间的运行状态,而子命名空间之间是隔离的。
image.png
Linux有6种Namespace,用来隔离不同的资源:
image.png

UTS

UTS Namespace主要用来隔离nodename和hostname两个系统标识。

  1. /*
  2. 隔离hostname和nodename
  3. */
  4. func main() {
  5. cmd := exec.Command("sh")
  6. cmd.SysProcAttr = &syscall.SysProcAttr{
  7. Cloneflags: syscall.CLONE_NEWUTS,
  8. }
  9. cmd.Stdin = os.Stdin
  10. cmd.Stdout = os.Stdout
  11. cmd.Stderr = os.Stderr
  12. if err := cmd.Run(); err != nil {
  13. log.Fatal(err)
  14. }
  15. }

进入新的sh进程后,使用pstree -pl命令打印进程树。上面带编译后的可执行文件是uts。
image.png
此时可以在这个进程中使用hostname -b 新名字来修改hostname,宿主机打印hostname不受影响。

IPC

  1. /**
  2. 隔离System V IPC和POSIX message queue
  3. */
  4. func main() {
  5. cmd := exec.Command("sh")
  6. cmd.SysProcAttr = &syscall.SysProcAttr{
  7. Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC,
  8. }
  9. cmd.Stdin = os.Stdin
  10. cmd.Stdout = os.Stdout
  11. cmd.Stderr = os.Stderr
  12. if err := cmd.Run(); err != nil {
  13. log.Fatal(err)
  14. }
  15. }

在宿主机使用ipcs -q来查看所有的Message Queue,然后ipcmk -Q创建一个新的MQ。在新的sh进程中使用``ipcs -q``没有发现刚才创建的MQ。

PID

  1. /**
  2. 隔离进程ID
  3. 同一个进程在不同PID namespace下可以拥有不同的PID
  4. */
  5. func main() {
  6. cmd := exec.Command("sh")
  7. cmd.SysProcAttr = &syscall.SysProcAttr{
  8. Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID,
  9. }
  10. cmd.Stdin = os.Stdin
  11. cmd.Stdout = os.Stdout
  12. cmd.Stderr = os.Stderr
  13. if err := cmd.Run(); err != nil {
  14. log.Fatal(err)
  15. }
  16. }

在新的sh进程中使用echo $$会打印出进程ID,值为1;
而在宿主机上使用pstree -pl会打印出在宿主机上的进程真实ID。
image.png
真实ID为3524。

Mount

  1. /**
  2. 隔离各个进程看到的挂载点视图
  3. 在不同namespace的进程中,看到的文件系统层次是不同的。在Mount Namespace中调用mount()和umount()仅仅只会
  4. 影响当前Namespace内的文件系统,而对全局的文件系统是没有影响的。
  5. */
  6. func main() {
  7. cmd := exec.Command("sh")
  8. cmd.SysProcAttr = &syscall.SysProcAttr{
  9. Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS,
  10. }
  11. cmd.Stdin = os.Stdin
  12. cmd.Stdout = os.Stdout
  13. cmd.Stderr = os.Stderr
  14. if err := cmd.Run(); err != nil {
  15. log.Fatal(err)
  16. }
  17. }

刚进入sh时挂载的文件系统还是宿主机的。
mount -t proc proc /proc
-t proc是指挂载的是一个proc类型的文件系统,然后设备名是proc,目录是/proc。

image.png

User

  1. /**
  2. 隔离用户的用户组ID
  3. 一个进程的User ID和Group ID在User namespace内外可以是不同的。
  4. 比较常用的是,在宿主机上以一个非root用户运行创建一个User namespace,然后在User namespace中映射为root用户。
  5. */
  6. func main() {
  7. cmd := exec.Command("sh")
  8. cmd.SysProcAttr = &syscall.SysProcAttr{
  9. Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS | syscall.CLONE_NEWUSER,
  10. }
  11. cmd.SysProcAttr.Credential = &syscall.Credential{
  12. Uid: uint32(1),
  13. Gid: uint32(1),
  14. }
  15. cmd.Stdin = os.Stdin
  16. cmd.Stdout = os.Stdout
  17. cmd.Stderr = os.Stderr
  18. if err := cmd.Run(); err != nil {
  19. log.Fatal(err)
  20. }
  21. }

id命令可以查看uid、gid和groups。
在宿主机和新的sh进程内使用id命令返回的结果是不同的。

Network

  1. /**
  2. 隔离网络设备、IP地址端口等网络栈
  3. 可以让每个容器拥有自己独立的(虚拟的)网络设备,而且容器内的应用可以绑定到自己的端口。
  4. */
  5. func main() {
  6. cmd := exec.Command("sh")
  7. cmd.SysProcAttr = &syscall.SysProcAttr{
  8. Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS | syscall.CLONE_NEWNET,
  9. }
  10. cmd.Stdin = os.Stdin
  11. cmd.Stdout = os.Stdout
  12. cmd.Stderr = os.Stderr
  13. if err := cmd.Run(); err != nil {
  14. log.Fatal(err)
  15. }
  16. }

ifconfig命令可以查看网络设备。
image.png