实现docker的namespace,cgroups,aufs

286 阅读2分钟

namespace

user namespace

映射当前用户用户成子进程的root,用来隔离user namespace

子user namespace 在父 user namespace中是普通用户,在子user namespace中是超级用户(超级用户只相对于子user namespace所拥有的资源,无法访问其他user namespace中需要超级用户才能访问资源)。

通过id查看

package main

import (
	"log"
	"os"
	"os/exec"
	"syscall"
)

func main() {
	cmd := exec.Command("/bin/bash")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	cmd.SysProcAttr = &syscall.SysProcAttr{}
	cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWUSER
	cmd.SysProcAttr.UidMappings = []syscall.SysProcIDMap{
		{ContainerID: 0, HostID: syscall.Getuid(), Size: 1},
	}
	cmd.SysProcAttr.GidMappings = []syscall.SysProcIDMap{
		{ContainerID: 0, HostID: syscall.Getgid(), Size: 1},
	}
	if err := cmd.Run(); err != nil {
		log.Fatal(err)
	}
}

uts namespace

提供主机名和域名的隔离

通过hostname -b xxx 修改hostname来看是否隔离

package main

import (
	"log"
	"os"
	"os/exec"
	"syscall"
)

func main() {
	cmd := exec.Command("/bin/bash")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	cmd.SysProcAttr = &syscall.SysProcAttr{}
	cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWUTS
	if err := cmd.Run(); err != nil {
		log.Fatal(err)
	}
}

ipc namespace

用来隔离system v ipc 和posix message queues

通过ipcmk -Q创建

ipcs -q 查看是否隔离

ipcrm -Q xxx 来删除

package main

import (
	"log"
	"os"
	"os/exec"
	"syscall"
)

func main() {
	cmd := exec.Command("/bin/bash")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	cmd.SysProcAttr = &syscall.SysProcAttr{}
	cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWIPC
	if err := cmd.Run(); err != nil {
		log.Fatal(err)
	}
}

pid namespace

隔离进程id

运行后通过echo $$ 查看当前的pid

package main

import (
	"log"
	"os"
	"os/exec"
	"syscall"
)

func main() {
	cmd := exec.Command("/bin/bash")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	cmd.SysProcAttr = &syscall.SysProcAttr{}
	cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWPID
	if err := cmd.Run(); err != nil {
		log.Fatal(err)
	}
}

mount namespace

隔离各个进程的挂载点视图.

mount namespace中进行的mount,umount不会影响全局

运行后mount -t proc proc /proc

通过ps -ef 确认

package main

import (
	"log"
	"os"
	"os/exec"
	"syscall"
)

func main() {
	cmd := exec.Command("/bin/bash")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	cmd.SysProcAttr = &syscall.SysProcAttr{}
	cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWNS | syscall.CLONE_NEWPID
	if err := cmd.Run(); err != nil {
		log.Fatal(err)
	}
}

network namespace

隔离网络栈

通过ifconfig来确认

package main

import (
	"log"
	"os"
	"os/exec"
	"syscall"
)

func main() {
	cmd := exec.Command("/bin/bash")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	cmd.SysProcAttr = &syscall.SysProcAttr{}
	cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWNET
	if err := cmd.Run(); err != nil {
		log.Fatal(err)
	}
}

cgroups

安装stress来测试资源限制

创建目录 /sys/fs/cgroup/memory/stress

package main

import (
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"strconv"
	"syscall"
)

const root = "/sys/fs/cgroup/memory/stress"

const self = "/proc/self/exe"

func main() {
	if os.Args[0] == self {
		cmd := exec.Command("sh", "-c", "stress --vm-bytes 400m --vm-keep -m 1")
		cmd.SysProcAttr = &syscall.SysProcAttr{}
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		if err := cmd.Run(); err != nil {
			log.Fatal(err)
		}
		os.Exit(0)
	}
	cmd := exec.Command(self)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Start(); err != nil {
		log.Fatal(err)
	} else {
		file := path.Join(root, "cgroup.procs")
		if err := ioutil.WriteFile(file, []byte(strconv.Itoa(cmd.Process.Pid)), 0644); err != nil {
			log.Fatal(err)
		}
		file = path.Join(root, "memory.limit_in_bytes")
		if err := ioutil.WriteFile(file, []byte("400M"), 0644); err != nil {
			log.Fatal(err)
		}
	}
	cmd.Process.Wait()
}

aufs

union file system

把不同文件系统文件和目录形成一个文件系统

aufs

mount -t aufs -o dirs=./dir1:dir2:dir3 none ./mntdir

对./mnt里的写入会反应到./dir1里,不会影响到dir2,dir3