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: 静态资源隔离