docker最初版本源碼分析

docker經過3年發展,從代碼量看,已經發展成爲一個相對較大的項目。這裏分析下docker 最初版本0.1.0時的代碼,感受下一個高大上開源項目最初的樣子,同時相對看後期版本代碼,看最初版本代碼更能明白一個項目的核心功能。

docker  v0.1.0 一共才一二十個go文件。

程序入口在docker/docker.go文件的main函數。該版本的docker程序集成了客戶端和服務端,根據main參數來選擇客戶端或者服務端角色。

func main() {

                if docker.SelfPath() =="/sbin/init" {

                                // Running in init mode

                                docker.SysInit()

                                return

                }

                // FIXME: Switch d and D ? (to be more sshd like)

                fl_daemon:= flag.Bool("d",false, "Daemonmode")

                fl_debug:= flag.Bool("D",false, "Debugmode")

                flag.Parse()

                rcli.DEBUG_FLAG= *fl_debug

                if *fl_daemon{

                                if flag.NArg() !=0 {

                                                flag.Usage()

                                                return

                                }

                                //服務端模式

                                if err :=daemon(); err != nil{

                                                log.Fatal(err)

                                }

                }else {

                                //客戶端模式

                                if err :=runCommand(flag.Args()); err != nil {

                                                log.Fatal(err)

                                }

                }

}

這裏主要介紹服務端模式

func daemon() error {

                //實例化docker服務

                service,err := docker.NewServer()

                if err != nil {

                                return err

                }

                //linten端口,準備API調用

                return rcli.ListenAndServe("tcp", "127.0.0.1:4242",service)

}

func NewServer() (*Server, error){

                rand.Seed(time.Now().UTC().UnixNano())

                if runtime.GOARCH !="amd64" {

                                log.Fatalf("The docker runtime currently only supports amd64(not %s). This will change in the future. Aborting.",runtime.GOARCH)

                }

                //準備運行時環境

                runtime,err := NewRuntime()

                if err != nil {

                                return nil,err

                }

                srv:= &Server{

                                runtime:runtime,

                }

                return srv, nil

}

func NewRuntime() (*Runtime, error){

                return NewRuntimeFromDirectory("/var/lib/docker")

}

 

funcNewRuntimeFromDirectory(root string) (*Runtime, error){

                //運行時環境,實際是對/var/lib/docker文件夾和元數據的管理

                runtime_repo:= path.Join(root, "containers")

                if err :=os.MkdirAll(runtime_repo, 0700); err != nil && !os.IsExist(err) {

                                return nil,err

                }

                //實例化鏡像管理,實際就是一堆鏡像文件和元數據的管理

                g,err := NewGraph(path.Join(root, "graph"))

                if err != nil {

                                return nil,err

                }

                //實例化tag管理

                repositories,err := NewTagStore(path.Join(root, "repositories"), g)

                if err != nil {

                                return nil,fmt.Errorf("Couldn't create Tag store:%s", err)

                }

                //實例化網絡管理

                netManager,err := newNetworkManager(networkBridgeIface)

                if err != nil {

                                return nil,err

                }

                authConfig,err := auth.LoadConfig(root)

                if err != nil &&authConfig == nil{

                                // If the auth file does not exist, keep going

                                return nil,err

                }

                runtime:= &Runtime{

                                root:           root,

                                repository:     runtime_repo,

                                containers:     list.New(),

                                networkManager:netManager,

                                graph:          g,

                                repositories:   repositories,

                                authConfig:     authConfig,

                }

 

                if err :=runtime.restore(); err != nil {

                                return nil,err

                }

                return runtime, nil

}

 

funcListenAndServe(proto, addr string, serviceService) error {

                listener,err := net.Listen(proto, addr)

                if err != nil {

                                return err

                }

                log.Printf("Listening for RCLI/%s on %s\n", proto,addr)

                defer listener.Close()

                for {

                                if conn, err :=listener.Accept(); err != nil {

                                                return err

                                }else {

                                                //匿名函數,處理API的handle協程

                                                go func(){

                                                                if DEBUG_FLAG {

                                                                                CLIENT_SOCKET= conn

                                                                }

                                                                if err :=Serve(conn, service); err != nil {

                                                                                log.Printf("Error: " +err.Error() + "\n")

                                                                                fmt.Fprintf(conn,"Error: "+err.Error()+"\n")

                                                                }

                                                                conn.Close()

                                                }()

                                }

                }

                return nil

}

 

func Serve(connio.ReadWriter, service Service) error {

                r:= bufio.NewReader(conn)

                var args []string

                if line, err :=r.ReadString('\n'); err != nil {

                                return err

                }else iferr := json.Unmarshal([]byte(line), &args);err != nil{

                                return err

                }else {

                                return call(service, ioutil.NopCloser(r), conn,args...)

                }

                return nil

}

func call(serviceService, stdin io.ReadCloser, stdout io.Writer, args ...string) error {

                return LocalCall(service, stdin, stdout, args...)

}

 

func LocalCall(serviceService, stdin io.ReadCloser, stdout io.Writer, args ...string) error {

                if len(args) == 0 {

                                args= []string{"help"}

                }

                flags:= flag.NewFlagSet("main",flag.ContinueOnError)

                flags.SetOutput(stdout)

                flags.Usage= func() { stdout.Write([]byte(service.Help())) }

                if err :=flags.Parse(args); err != nil {

                                return err

                }

                cmd:= flags.Arg(0)

                log.Printf("%s\n", strings.Join(append(append([]string{service.Name()}, cmd), flags.Args()[1:]...), " "))

                if cmd == "" {

                                cmd= "help"

                }

//獲取方法

                method:= getMethod(service, cmd)

                if method != nil {

                                return method(stdin, stdout, flags.Args()[1:]...)

                }

                return errors.New("Nosuch command: " + cmd)

}

 

func getMethod(serviceService, name string) Cmd {

                if name == "help" {

                                return func(stdinio.ReadCloser, stdout io.Writer, args ...string) error {

                                                if len(args) == 0 {

                                                                stdout.Write([]byte(service.Help()))

                                                }else {

                                                                if method :=getMethod(service, args[0]); method == nil {

                                                                                return errors.New("Nosuch command: " + args[0])

                                                                }else {

                                                                                method(stdin,stdout, "--help")

                                                                }

                                                }

                                                return nil

                                }

                }

                methodName:= "Cmd"+ strings.ToUpper(name[:1]) +strings.ToLower(name[1:])

                //使用go語言的反射包來尋着相應的hand函數。在這裏就是通過strings來尋找相應函數,比如API 函數是run ,那麼返回的就是CmdRun方法,同時傳遞參數過去。

                method,exists :=reflect.TypeOf(service).MethodByName(methodName)

                if !exists {

                                return nil

                }

                //返回實際handle函數。

                return func(stdinio.ReadCloser, stdout io.Writer, args ...string) error {

                                //同時傳遞參數過去

                                ret:= method.Func.CallSlice([]reflect.Value{

                                                reflect.ValueOf(service),

                                                reflect.ValueOf(stdin),

                                                reflect.ValueOf(stdout),

                                                reflect.ValueOf(args),

                                })[0].Interface()

                                ifret == nil{

                                                return nil

                                }

                                return ret.(error)

                }

}

//這裏以run container 爲例子

func (srv *Server) CmdRun(stdin io.ReadCloser, stdoutio.Writer, args ...string)error {

                config,err := ParseRun(args)

                if err != nil {

                                return err

                }

                if config.Image =="" {

                                return fmt.Errorf("Imagenot specified")

                }

                if len(config.Cmd)== 0 {

                                return fmt.Errorf("Commandnot specified")

                }

                // Create new container

                container,err := srv.runtime.Create(config)

                if err != nil {

                                return errors.New("Errorcreating container: " +err.Error())

                }

                if config.OpenStdin {

                                cmd_stdin,err := container.StdinPipe()

                                if err != nil {

                                                return err

                                }

                                if !config.Detach {

                                                Go(func() error{

                                                                _,err := io.Copy(cmd_stdin, stdin)

                                                                cmd_stdin.Close()

                                                                return err

                                                })

                                }

                }

                // Run the container

                if !config.Detach {

                                cmd_stderr,err := container.StderrPipe()

                                if err != nil {

                                                return err

                                }

                                cmd_stdout,err := container.StdoutPipe()

                                if err != nil {

                                                return err

                                }

                                if err :=container.Start(); err != nil {

                                                return err

                                }

                                sending_stdout:= Go(func()error {

                                                _,err := io.Copy(stdout, cmd_stdout)

                                                return err

                                })

                                sending_stderr:= Go(func()error {

                                                _,err := io.Copy(stdout, cmd_stderr)

                                                return err

                                })

                                err_sending_stdout:= <-sending_stdout

                                err_sending_stderr:= <-sending_stderr

                                if err_sending_stdout !=nil {

                                                return err_sending_stdout

                                }

                                if err_sending_stderr !=nil {

                                                return err_sending_stderr

                                }

                                container.Wait()

                }else {

                                if err :=container.Start(); err != nil {

                                                return err

                                }

                                //正常創建並運行容器的話,就打印容器ID

                                fmt.Fprintln(stdout,container.Id)

                }

                return nil

}

 

func (runtime *Runtime) Create(config *Config)(*Container, error){

                // Lookup image

                img,err :=runtime.repositories.LookupImage(config.Image)

                if err != nil {

                                return nil,err

                }

                container:= &Container{

                                // FIXME: we should generate the ID here instead ofreceiving it as an argument

                                Id:              GenerateId(),

                                Created:         time.Now(),

                                Path:            config.Cmd[0],

                                Args:            config.Cmd[1:],//FIXME: de-duplicate from config

                                Config:          config,

                                Image:           img.Id, //Always use the resolved image id

                                NetworkSettings:&NetworkSettings{},

                                // FIXME: do we need to store this in the container?

                                SysInitPath:sysInitPath,

                }

                container.root= runtime.containerRoot(container.Id)

                // Step 1: create the container directory.

                // This doubles as a barrier to avoid race conditions.

                if err :=os.Mkdir(container.root, 0700); err != nil {

                                return nil,err

                }

                // Step 2: save the container json

                if err :=container.ToDisk(); err != nil {

                                return nil,err

                }

                // Step 3: register the container

                if err :=runtime.Register(container); err != nil {

                                return nil,err

                }

                return container, nil

}

 

func (container *Container) Start() error{

                if err :=container.EnsureMounted(); err != nil {

                                return err

                }

                if err :=container.allocateNetwork(); err != nil {

                                return err

                }

                if err :=container.generateLXCConfig(); err != nil {

                                return err

                }

                params:= []string{

                                "-n", container.Id,

                                "-f", container.lxcConfigPath(),

                                "--",

                                "/sbin/init",

                }

 

                // Networking

                params= append(params, "-g",container.network.Gateway.String())

 

                // User

                if container.Config.User != "" {

                                params= append(params, "-u",container.Config.User)

                }

 

                // Program

                params= append(params, "--",container.Path)

                params= append(params, container.Args...)

                //所有的操作就是爲下面的命令行準備參數和環境

                container.cmd= exec.Command("/usr/bin/lxc-start", params...)

 

                // Setup environment

                container.cmd.Env= append(

                                []string{

                                                "HOME=/",

                                                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",

                                },

                                container.Config.Env...,

                )

 

                var err error

                if container.Config.Tty {

                                err= container.startPty()

                }else {

                                err= container.start()

                }

                if err != nil {

                                return err

                }

                // FIXME: save state on disk *first*, then converge

                // this way disk state is used as a journal, eg. we canrestore after crash etc.

                container.State.setRunning(container.cmd.Process.Pid)

                container.ToDisk()

                go container.monitor()

                return nil

}

 

容器需要提供一系列方法,比如mount 文件作爲容器容器的rootfs。

//加載文件系統

func (container *Container) Mount() error{

                image,err := container.GetImage()

                if err != nil {

                                return err

                }

                return image.Mount(container.RootfsPath(),container.rwPath())

}

func (image *Image) Mount(root, rw string)error {

                if mounted, err :=Mounted(root); err != nil {

                                return err

                }else ifmounted {

                                return fmt.Errorf("%sis already mounted", root)

                }

                //獲取鏡像層列表

                layers,err := image.layers()

                if err != nil {

                                return err

                }

                // Create the target directories if they don't exist

                if err :=os.Mkdir(root, 0755); err != nil && !os.IsExist(err) {

                                return err

                }

                if err :=os.Mkdir(rw, 0755); err != nil && !os.IsExist(err) {

                                return err

                }

                // FIXME: @creack shouldn't we do this after going overchanges?

                //加載AUFS文件系統

                if err :=MountAUFS(layers, rw, root); err != nil {

                                return err

                }

                // FIXME: Create tests for deletion

                // FIXME: move this part to change.go

                // Retrieve the changeset from the parent and apply it tothe container

                //  - Retrieve thechanges

                changes,err := Changes(layers, layers[0])

                if err != nil {

                                return err

                }

                // Iterate on changes

                for _, c := range changes {

                                // If there is a delete

                                if c.Kind ==ChangeDelete {

                                                // Make sure the directory exists

                                                file_path,file_name := path.Dir(c.Path),path.Base(c.Path)

                                                if err :=os.MkdirAll(path.Join(rw, file_path), 0755);err != nil{

                                                                return err

                                                }

                                                // And create the whiteout (we just need to create emptyfile, discard the return)

                                                if _, err :=os.Create(path.Join(path.Join(rw, file_path),

                                                                ".wh."+path.Base(file_name)));err != nil{

                                                                return err

                                                }

                                }

                }

                return nil

}

//獲取鏡像層列表

func (img *Image) layers() ([]string,error) {

                var list []string

                var e error

                if err :=img.WalkHistory(

                                func(img *Image)(err error) {

                                                if layer, err :=img.layer(); err != nil {

                                                                e= err

                                                }else iflayer != ""{

                                                                list= append(list, layer)

                                                }

                                                return err

                                },

                );err != nil{

                                return nil,err

                }else ife != nil{ // Did an error occur inside the handler?

                                return nil,e

                }

                if len(list) == 0 {

                                return nil,fmt.Errorf("No layer found for image%s\n", img.Id)

                }

                return list, nil

}

 

func MountAUFS(ro []string, rw string,target string) error{

                // FIXME: Now mount the layers

                rwBranch:= fmt.Sprintf("%v=rw",rw)

                roBranches:= ""

                for _, layer :=range ro {

                                roBranches+= fmt.Sprintf("%v=ro:",layer)

                }

                branches:= fmt.Sprintf("br:%v:%v",rwBranch, roBranches)

                return mount("none",target, "aufs", 0, branches)

}

func mount(source string, target string,fstype string, flags uintptr, data string) (err error) {

                //最後實質就是調用系統命令mount 類型爲aufs類型文件系統作爲容器的rootfs

                return syscall.Mount(source, target, fstype,flags, data)

}

 

 

關於鏡像的管理,主要見graph.go 和image.go

type Graph struct {

                Rootstring

}

//實例化圖層

func NewGraph(root string) (*Graph,error) {

                abspath,err := filepath.Abs(root)

                if err != nil {

                                return nil,err

                }

                // Create the root directory if it doesn't exists

                if err :=os.Mkdir(root, 0700); err != nil && !os.IsExist(err) {

                                return nil,err

                }

                return &Graph{

                                Root:abspath,

                },nil

}

 

func (graph *Graph) Exists(id string)bool {

                if _, err :=graph.Get(id); err != nil {

                                return false

                }

                return true

}

 

func (graph *Graph) Get(id string)(*Image, error){

                // FIXME: return nil when the image doesn't exist,instead of an error

                img,err := LoadImage(graph.imageRoot(id))

                if err != nil {

                                return nil,err

                }

                if img.Id !=id {

                                return nil,fmt.Errorf("Image stored at '%s' has wrong id'%s'", id, img.Id)

                }

                img.graph= graph

                return img, nil

}

 

func (graph *Graph) Create(layerData Archive, container *Container, comment string)(*Image, error){

                img:= &Image{

                                Id:      GenerateId(),

                                Comment:comment,

                                Created:time.Now(),

                }

                if container !=nil {

                                img.Parent= container.Image

                                img.Container= container.Id

                                img.ContainerConfig= *container.Config

                }

                if err :=graph.Register(layerData, img); err != nil {

                                return nil,err

                }

                return img, nil

}

//註冊鏡像

func (graph *Graph) Register(layerData Archive, img *Image) error {

                if err :=ValidateId(img.Id); err != nil {

                                return err

                }

                // (This is a convenience to save time. Race conditionsare taken care of by os.Rename)

                if graph.Exists(img.Id) {

                                return fmt.Errorf("Image%s already exists", img.Id)

                }

                tmp,err := graph.Mktemp(img.Id)

                defer os.RemoveAll(tmp)

                if err != nil {

                                return fmt.Errorf("Mktempfailed: %s", err)

                }

                if err :=StoreImage(img, layerData, tmp); err != nil {

                                return err

                }

                // Commit

                if err :=os.Rename(tmp, graph.imageRoot(img.Id)); err !=nil {

                                return err

                }

                img.graph= graph

                return nil

}

 

func (graph *Graph) Mktemp(id string)(string, error){

                tmp,err := NewGraph(path.Join(graph.Root, ":tmp:"))

                if err != nil {

                                return "",fmt.Errorf("Couldn't create temp: %s",err)

                }

                if tmp.Exists(id) {

                                return "",fmt.Errorf("Image %d already exists",id)

                }

                return tmp.imageRoot(id), nil

}

 

func (graph *Graph) Garbage() (*Graph,error) {

                return NewGraph(path.Join(graph.Root, ":garbage:"))

}

//刪除不是真正刪除,只是暫時rename

func (graph *Graph) Delete(id string)error {

                garbage,err := graph.Garbage()

                if err != nil {

                                return err

                }

                return os.Rename(graph.imageRoot(id),garbage.imageRoot(id))

}

 

func (graph *Graph) Undelete(id string)error {

                garbage,err := graph.Garbage()

                if err != nil {

                                return err

                }

                return os.Rename(garbage.imageRoot(id),graph.imageRoot(id))

}

 

func (graph *Graph) GarbageCollect() error{

                garbage,err := graph.Garbage()

                if err != nil {

                                return err

                }

                return os.RemoveAll(garbage.Root)

}

 

func (graph *Graph) Map() (map[string]*Image, error) {

                // FIXME: this should replace All()

                all,err := graph.All()

                if err != nil {

                                return nil,err

                }

                images:= make(map[string]*Image, len(all))

                for _, image :=range all {

                                images[image.Id]= image

                }

                return images, nil

}

 

func (graph *Graph) All() ([]*Image,error) {

                var images []*Image

                err:= graph.WalkAll(func(image*Image) {

                                images= append(images, image)

                })

                return images, err

}

 

func (graph *Graph) WalkAll(handler func(*Image)) error {

                files,err := ioutil.ReadDir(graph.Root)

                if err != nil {

                                return err

                }

                for _, st := range files {

                                if img, err :=graph.Get(st.Name()); err != nil {

                                                // Skip image

                                                continue

                                }else ifhandler != nil{

                                                handler(img)

                                }

                }

                return nil

}

 

func (graph *Graph) ByParent() (map[string][]*Image,error) {

                byParent:= make(map[string][]*Image)

                err:= graph.WalkAll(func(image*Image) {

                                image,err := graph.Get(image.Parent)

                                if err != nil {

                                                return

                                }

                                if children, exists :=byParent[image.Parent]; exists {

                                                byParent[image.Parent]= []*Image{image}

                                }else {

                                                byParent[image.Parent]= append(children, image)

                                }

                })

                return byParent, err

}

 

func (graph *Graph) Heads() (map[string]*Image, error) {

                heads:= make(map[string]*Image)

                byParent,err := graph.ByParent()

                if err != nil {

                                return nil,err

                }

                err= graph.WalkAll(func(image *Image) {

                                // If it's not in the byParent lookup table, then

                                // it's not a parent -> so it's a head!

                                if _, exists :=byParent[image.Id]; !exists {

                                                heads[image.Id]= image

                                }

                })

                return heads, err

}

 

func (graph *Graph) imageRoot(id string)string {

                return path.Join(graph.Root, id)

}

 

 

 

網絡處理network.go

//最終調用iptable實現端口映射

//Wrapper around the iptables command

func iptables(args ...string) error {

                if err :=exec.Command("/sbin/iptables",args...).Run(); err !=nil {

                                return fmt.Errorf("iptablesfailed: iptables %v", strings.Join(args, ""))

                }

                return nil

}

func (mapper *PortMapper) setup() error{

                if err :=iptables("-t", "nat", "-N","DOCKER"); err != nil {

                                return errors.New("Unableto setup port networking: Failed to create DOCKER chain")

                }

                if err :=iptables("-t", "nat", "-A","PREROUTING", "-j", "DOCKER");err != nil{

                                return errors.New("Unableto setup port networking: Failed to inject docker in PREROUTING chain")

                }

                if err :=iptables("-t", "nat", "-A","OUTPUT", "-j","DOCKER"); err != nil {

                                return errors.New("Unableto setup port networking: Failed to inject docker in OUTPUT chain")

                }

                return nil

}

 

func (mapper *PortMapper) iptablesForward(rule string, port int,dest net.TCPAddr) error {

                return iptables("-t","nat", rule, "DOCKER", "-p","tcp", "--dport",strconv.Itoa(port),

                                "-j", "DNAT","--to-destination",net.JoinHostPort(dest.IP.String(), strconv.Itoa(dest.Port)))

}

 

func (mapper *PortMapper) Map(port int,dest net.TCPAddr) error {

                if err :=mapper.iptablesForward("-A", port,dest); err != nil{

                                return err

                }

                mapper.mapping[port]= dest

                return nil

}

 

func (mapper *PortMapper) Unmap(port int)error {

                dest,ok := mapper.mapping[port]

                if !ok {

                                return errors.New("Portis not mapped")

                }

                if err :=mapper.iptablesForward("-D", port,dest); err != nil{

                                return err

                }

                delete(mapper.mapping, port)

                return nil

}

 

 

 


發佈了41 篇原創文章 · 獲贊 9 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章