Linux cgroup原理

参考文章

参考视频教程

Docker 核心技术主要从以下几个方面实现:

命名空间(namespace):通过进程命名空间,将docker进程和宿主进程进行隔离.

网络:docker打通命名空间与外部通讯的方式,类型有:bridge模式网络,host模式网络,container模式网络,none模式网络。

文件隔离(cgroup):通过加载虚拟挂载点,重设 root 目录等等,将文件系统进行隔离物理资源的隔离,通过 CGroups(Control Groups),限制容器在 CPU,内存,磁盘IO、网络上的使用率,以此来隔离容器间的资源分配。

镜像:Dockerfile 中的每条命令会形成一个 diff 层,每个 diff 层都是只读的,只有最上面的层是可写的。这个操作通过 UnionFS 实现。通过AUFS,Btrfs,Device mapper,Overlayfs,ZFS,VFS,可以将未修改的镜像层进行联合挂载,从而提高读写效率。

  • CLONE_NEWPID:

  • CLONE_NEWIPC:

  • PID Namespace和IPC Namespace:

  • CLONE_NEWNS:

  • CLONE_NEWNET:

    当调用clone时,设定了CLONE_NEWNS,就会创建一个新的mount Namespace。每个进程都存在于一个mount Namespace里面,mount Namespace为进程提供了一个文件层次视图。如果不设定这个flag,子进程和父进程将共享一个mount Namespace,其后子进程调用mount或umount将会影响到所有该Namespace内的进程。如果子进程在一个独立的mount Namespace里面,就可以调用mount或umount建立一份新的文件层次视图。该flag配合pivot_root系统调用,可以为进程创建一个独立的目录空间。

  • CLONE_NEWUTS:

    当调用clone时,设定了CLONE_NEWUTS,就会创建一个新的UTS Namespace。一个UTS Namespace就是一组被uname返回的标识符。新的UTS Namespace中的标识符通过复制调用进程所属的Namespace的标识符来初始化。Clone出来的进程可以通过相关系统调用改变这些标识符,比如调用sethostname来改变该Namespace的hostname。**这一改变对该Namespace内的所有进程可见。**CLONE_NEWUTS和CLONE_NEWNET一起使用,可以虚拟出一个有独立主机名和网络空间的环境,就跟网络上一台独立的主机一样。

    以上所有clone flag都可以一起使用,为进程提供了一个独立的运行环境。LXC正是通过在clone时设定这些flag,为进程创建一个有独立PID,IPC,FS,Network,UTS空间的container。一个container就是一个虚拟的运行环境,对container里的进程是透明的,它会以为自己是直接在一个系统上运行的

基于 go的api 实现golang进程创建新进程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package main

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

func main() {

	// fmt.Println("hello world")
	log.Println(os.Args)
	if len(os.Args) < 2 {
		log.Println("error: it is not correct cmd")
		return
	}
	switch os.Args[1] {
	case "run":
		child()
	default:
		panic("what ??")
	}

}
func child() {
	log.Printf("running  %v  as pid %d \n", os.Args[2:], os.Getpid())
	cmd := exec.Command(os.Args[2], os.Args[3:]...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	//syscall
	cmd.Stderr = os.Stderr

	cmd.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID,
	}
	must(cmd.Run())

	//call system cmd

}

func must(err error) {
	if err != nil {
		panic(err)
	}
}

proc/self 目录知识

Linux  内核提供了一种通过 /proc 文件系统,在运行时访问内核内部数据结构、改变内核设置的机制。proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间。它以文件系统的方式为访问系统内核数据的操作提供接口。

容器的实践

1
2
3
4
cd /
cp -a /bin/ /home/rootfs
cp -a /lib/ /home/rootfs
cp -a /lib64/ /home/rootfs

注意 要 /bin/ ,如果是 /bin ,会复制链接过去

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
//go:build dev
// +build dev

package main

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

func main() {

	// fmt.Println("hello world")
	log.Println("进入 main进程 ", os.Args)
	if len(os.Args) < 2 {
		log.Println("error: it is not correct cmd")
		return
	}
	switch os.Args[1] {
	case "run":
		run()
	case "fork":
		fork()
	default:
		panic("what ??")
	}

}
func run() {
	//    1  [  2 ->
	//go run echo "hello world"
	log.Printf("run()   %v  as pid %d \n", os.Args[2:], os.Getpid())
	// var a = append([]string{"fork"}, os.Args[2:]...)	// fork 出子进程,不在本进程中运行
	var cmd = exec.Command("/proc/self/exe", append([]string{"fork"}, os.Args[2:]...)...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: syscall.CLONE_NEWUTS | //修改 hostname 不影响
			syscall.CLONE_NEWPID | // 创建 PID 的命名空间
			syscall.CLONE_NEWNS, //创建新的 namespace 命名空间
	}
	must(cmd.Run())

}
func fork() {
	log.Printf("child  %v  as pid %d \n", os.Args[2:], os.Getpid())
	cmd := exec.Command(os.Args[2], os.Args[3:]...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	//syscall
	cmd.Stderr = os.Stderr
	//作为根目录

	// cl
	must(syscall.Chroot("/home/rootfs"))
	must(os.Chdir("/"))
	//
	must(syscall.Mount("proc", "proc", "proc", 0, ""))
	//mount上去 就能看到命令了

	must(cmd.Run())

	//call system cmd

}

func must(err error) {
	if err != nil {
		panic(err)
	}
}

参考老外给的一个项目

参考学习教程2

重点理解 network ,ipc, mount ,PID, User,UTS

uts用来做 hostname 和 domain 的一个隔离, 资源方面的限制

cgroup 又叫 control groups ,是 linux kernel的功能,限制和监控进程对资源的使用

区别:

namespace: 动态隔离,运行时隔离

control group: 静态资源隔离