最近博客收到了一封交流的私信,感謝您的關注;如今就我理解的docker創建容器時namespace的創建問題作一個 我的的回答:html
一,從原理角度來說:linux
docker建立container,說白了就是linux系統中的一次fork的調用,在fork調用的時候,會傳入一些flag參數,這些參數能夠控制對linux內核的調用使用新的namespace;具體的作法是docker daemon封裝好一個Command類,在這個Command類中,有關於namespace的配置;接着docker daemon將這個Command類發給execdriver,execdriver從中提取出關於namespace配置,而後最終將這些配置賦給golang 的exec.Cmd類,這個類就是docker 容器跑起來之後的第一個進程;這個進程在創立的時候就會建立新的namespace;git
二,從代碼角度來說:github
http://www.cnblogs.com/yuhan-TB/p/5118122.html 這篇文章講了docker run的啓動過程,其中docker daemon 與execdriver之間的交互是經過Command類,這是個重要的類,類的定義在daemon/execdriver/driver.go中;其中主要有:Network、Ipc、Pid、UTS等幾種命名空間,uid namespace 如今還不支持建立獨立的,也就是docker 容器裏面的root和 宿主機的root應該是同樣的;而這幾個命名空間是怎麼放到Command裏面來的呢,具體代碼在daemon/container_unix.go的populateCommand()函數中,這裏面主要的做用就是經過container的config和hostconfig來生成Command中的關於這幾個namespace的配置。golang
如今對於docker daemon來講,它已經生成好了發給execdriver的Command類,那麼在execdriver中是怎麼使用的呢。docker
在 daemon/execdriver/native/create.go中app
func (d *Driver) createContainer(c *execdriver.Command) (*configs.Config, error)函數
這個函數的做用是接收Command類,根據裏面的配置返回一個libcontainer/configs.Config類, 這個類就是execdriver去實際調用去執行關於生成容器的系統調用的時候的配置;ui
裏面有四個這樣的函數,就是來從execdriver.Command類中來提取出namespace的相關配置;spa
if err := d.createIpc(container, c); err != nil {
return nil, err
}
if err := d.createPid(container, c); err != nil {
return nil, err
}
if err := d.createUTS(container, c); err != nil {
return nil, err
}
if err := d.createNetwork(container, c); err != nil {
return nil, err
}
生成的這個libcontainer/configs.Config類會在vendor/src/github.com/opencontainers/runc/libcontainer/factory_linux.go的函數
func (l *LinuxFactory) Create(id string, config *configs.Config) (Container, error) 中被複制到linuxContainer類裏面:
return &linuxContainer{
id: id,
root: containerRoot,
config: config, //對,就是這個config
initPath: l.InitPath,
initArgs: l.InitArgs,
criuPath: l.CriuPath,
cgroupManager: l.NewCgroupsManager(config.Cgroups, nil),
},nil
接着這個config的配置 在 /vendor/src/github.com/opencontainers/runc/libcontainer/container_linux.go
func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe *os.File) (*initProcess, error) { t := "_LIBCONTAINER_INITTYPE=standard"
cloneFlags := c.config.Namespaces.CloneFlags()
if cloneFlags&syscall.CLONE_NEWUSER != 0 {
if err := c.addUidGidMappings(cmd.SysProcAttr); err != nil {
// user mappings are not supported
return nil, err
}
enableSetgroups(cmd.SysProcAttr)
// Default to root user when user namespaces are enabled.
if cmd.SysProcAttr.Credential == nil {
cmd.SysProcAttr.Credential = &syscall.Credential{}
}
}
cmd.Env = append(cmd.Env, t)
cmd.SysProcAttr.Cloneflags = cloneFlags
return &initProcess{
cmd: cmd,
childPipe: childPipe,
parentPipe: parentPipe,
manager: c.cgroupManager,
config: c.newInitConfig(p),
}, nil
}
在飄紅的這兩個部分,將這些關於namespace的配置交給exec.Cmd類;