这是个人的学习笔记,学习的提纲以<<自己动手写Docker>>为基础进行学习和拓展。结合docker源码分析来系统的学习和阅读一份开源代码。期望达到的目的有许多,诸如学习一套优秀代码的架构思想,深入了解docker的原理,学习好的代码风格,总结和了解该如何更高效的学习其他类型的开源软件的源代码来快速获取其优秀的设计思想。
但无论如何,认真对待自己花费的时间,付出的精力,花费了1的时间,一定要获取1的回报,在此基础上发散思维,举一反三,获取更多的收获这将是一件令人欢喜和兴奋的趣事。
Docker利用Linux内核的特性,将Namespace、Cgroup、UnionFS等功能组合起来实现了强大的功能。因此在学习DIY Docker之前需要对这些内核特性有所了解。为了能够快速的上手,这里留下一些“占位符”用于后期对知识点进行补充和扩展。
Namespace
Cgroup
UnionFS
这一小节,实现一个简单的docker run 的命令。想要实现一个docker run 的命令,主要需要做下面几项工作。
(一): 使用namespace进行资源的隔离
这段代码做了两件事
- 使用namespace对资源进行隔离
- 在这个main的进程中,再次使用/proc/self/exe 通过init参数调用initCommand函数()
initCommand函数用来在container内将/bin/sh这个进程替换为 id=1的进程
设置namespace
func NewParrentProcess(tty bool,command string) *exec.Cmd{
cmd := exec.Command("/proc/self/exe", args...)
cmd.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS |
syscall.CLONE_NEWNET | syscall.CLONE_NEWIPC,
}
}
调用initCommand函数
#./mydocker run -ti /bin/bash
func NewParrentProcess(tty bool,command string) *exec.Cmd{
/*
* exec.Command("/proc/self/exe","init","/bin/sh")
*/
args :=[]string{"init",command}
cmd := exec.Command("/proc/self/exe", args...)
return nil
}
(二): 将自己变成id=1的进程
完整代码
package main
import (
"github.com/urfave/cli"
"fmt"
"os"
"os/exec"
"github.com/gpmgo/gopm/modules/log"
"syscall"
"github.com/Sirupsen/logrus"
)
func RunContainerInitProcess(command string,args []string) error{
syscall.Sethostname([]byte("newhost"))
syscall.Chroot("/root/go/src/hans/test/busybox")
os.Chdir("/")
defaultMountFlags := syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV
syscall.Mount("proc", "/proc", "proc", uintptr(defaultMountFlags), "")
argv := []string{command}
logrus.Infof("RunContainerInitProcess command=%s argv=%s",command,argv)
if err := syscall.Exec(command, argv, os.Environ()); err != nil {
logrus.Errorf(err.Error())
}
return nil
}
func NewParrentProcess(tty bool,command string) *exec.Cmd{
args :=[]string{"init",command}
fmt.Printf("NewParrentProcess args=%s",args)
cmd := exec.Command("/proc/self/exe", args...)
//cmd := exec.Command(command)
cmd.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS |
syscall.CLONE_NEWNET | syscall.CLONE_NEWIPC,
}
if tty {
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
}
return cmd
}
func Run(tty bool,cmd string){
parrent := NewParrentProcess(tty,cmd)
if err :=parrent.Start(); err != nil {
log.Error("error:%s\n",err)
}
parrent.Wait()
os.Exit(-1)
}
var initCommand=cli.Command{
Name:"init",
Usage:"",
Action:func(context *cli.Context) error{
cmd := context.Args().Get(0)
log.Info("initCommand %s",cmd)
err :=RunContainerInitProcess(cmd,nil)
return err
},
}
var runCommand=cli.Command{
Name:"run",
Usage: `create a container mydocker run -ti`,
Flags:[]cli.Flag{
cli.BoolFlag{
Name:"ti",
Usage:"enable tty",
},
},
Action: func(context *cli.Context) error{
if len(context.Args()) < 1 {
return fmt.Errorf("missing container command")
}
cmd := context.Args().Get(0)
tty := context.Bool("ti")
Run(tty,cmd)
return nil
},
}
func main() {
app :=cli.NewApp()
app.Name = "mydocker"
app.Usage = "mydocker study. mydocker is a simple container runtime implementation"
app.Commands = []cli.Command{
runCommand,
initCommand,
}
fmt.Printf("main func os.Args=%s",os.Args)
app.Run(os.Args)
}
网友评论