- 1. r.postContainersStart()
- 2. ContainerStart()
- 3. daemon.containerStart()
- 4. daemon.containerd.Create()
- 5.container.start()
- 结语
- 参考
上一篇介绍了daemon端对container create的处理,这一章将详细介绍daemon端对container start的处理,也就是r.postContainersStart函数
源码阅读基于docker version1.17.05.x。
1. r.postContainersStart()
1.1 源码
r.postContainersCreate()的实现位于moby/api/server/router/container/container_routes.go#L133#L172,代码的主要内容是:
func (s *containerRouter) postContainersStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
//获取hostConfig配置信息
var hostConfig *container.HostConfig
checkpoint := r.Form.Get("checkpoint")
checkpointDir := r.Form.Get("checkpoint-dir")
//调用ContainerStart进一步启动容器,2.详细分析
err := s.backend.ContainerStart(vars["name"], hostConfig, checkpoint, checkpointDir)
}
1.2 流程图
下面2.分析ContainerStart()
函数。
2. ContainerStart()
ContainerStart()的实现位于moby/daemon/start.go#L21#L87,代码的主要内容是:
func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.HostConfig, checkpoint string, checkpointDir string) error {
//可以根据全容器ID、容器名、容器ID前缀获取容器对象
container, err := daemon.GetContainer(name)
//调用containerStart进一步启动容器,3.详细分析
daemon.containerStart(container, checkpoint, checkpointDir, true)
}
下面分析daemon.containerStart()
函数。
3. daemon.containerStart()
daemon.containerStart()的实现位于moby/daemon/start.go#L98#L198,代码的主要内容是:
func (daemon *Daemon) containerStart(container *container.Container, checkpoint string, checkpointDir string, resetRestartManager bool) (err error) {
//在创建的时候调用了一次,这次start又mount一次?
err := daemon.conditionalMountOnStart(container)
//3.1对网络进行初始化,后面再详细分析
err := daemon.initializeNetworking(container)
//调用daemon.containerd.Create()进一步启动,后面4.再详细分析
err := daemon.containerd.Create(container.ID, checkpoint, checkpointDir, *spec, container.InitializeStdio, createOptions...)
}
下面3.1分析daemon.initializeNetworking()
函数,4.分析daemon.containerd.Create()
。
3.1 daemon.initializeNetworking()
daemon.initializeNetworking()的实现位于moby/daemon/container_operations.go#L879#L908。initalizeNetworking()
对网络进行初始化,docker网络有三种,bridge模式(默认值)、host模式和contaier 模式,根据config
和hostConfig
中的参数来确定容器的网络模式,然后调libnetwork
包来建立网络。代码的主要内容是:
func (daemon *Daemon) initializeNetworking(container *container.Container) error {
//如果是container模式
if container.HostConfig.NetworkMode.IsContainer() {
//获取所要共享网络的container的hosts文件
nc, err := daemon.getNetworkedContainer(container.ID, container.HostConfig.NetworkMode.ConnectedContainer())
//将所要共享网络的container的HostnamePath、HostsPath和ResolvConfPath赋值给新container
initializeNetworkingPaths(container, nc)
//将所要共享网络的container的Hostname、Domainname赋值给新container
container.Config.Hostname = nc.Config.Hostname
container.Config.Domainname = nc.Config.Domainname
//container模式从这里就返回了,不执行后面的操作
return nil
}
//如果是host模式
if container.HostConfig.NetworkMode.IsHost() {
if container.Config.Hostname == "" {
//将宿主机的hostname赋值给新建container
container.Config.Hostname, err = os.Hostname()
//这里host没有返回,回往下继续执行
}
}
//下面3.1.1再详细分析
err := daemon.allocateNetwork(container)
//生成container的hosts文件
return container.BuildHostnameFile()
}
下面3.1.1分析daemon.allocateNetwork()
3.1.1 daemon.allocateNetwork()到daemon.connectToNetwork()
daemon.allocateNetwork()的实现位于moby/daemon/container_operations.go#L495#L576,其主要实现为:
func (daemon *Daemon) allocateNetwork(container *container.Container) error {
//如果没有使用网络,或者是container网络,则直接返回
if container.Config.NetworkDisabled || container.HostConfig.NetworkMode.IsContainer() {
return nil
}
networks := make(map[string]*network.EndpointSettings)
for n, epConf := range container.NetworkSettings.Networks {
if n == defaultNetName {
continue
}
networks[n] = epConf
}
for netName, epConf := range networks {
cleanOperationalData(epConf)
//下面再详细分析
if err := daemon.connectToNetwork(container, netName, epConf.EndpointSettings, updateSettings); err != nil {
return err
}
}
//将hosts配置信息存到disk中
err := container.WriteHostConfig()
}
接着分析daemon.connectToNetwork()
func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings, updateSettings bool) (err error) {
//查找需要连接的网络,n为libnetwork.Network类型,config为*networktypes.NetworkingConfig
n, config, err := daemon.findAndAttachNetwork(container, idOrName, endpointConfig)
//获取controller
controller := daemon.netController
//获得sandbox
sb := daemon.getNetworkSandbox(container)
//从给定的network,也就是n,创建一个endpoint options
createOptions, err := container.BuildCreateEndpointOptions(n, endpointConfig, sb, daemon.configStore.DNS)
//创建endpoin
ep, err := n.CreateEndpoint(endpointName, createOptions...)
//由network n 创建endpoint Join options
joinOptions, err := container.BuildJoinOptions(n)
//将endpoint接入sandbox
err := ep.Join(sb, joinOptions...)
}
对网络创建的分析先到此,后续文章再分析
- 创建network
- connect到network
这两部分。下面先接着把container的start过程分析完。
4. daemon.containerd.Create()
daemon.containerd.Create()的实现位于moby/libcontainerd/client_unix.go#L45#L99,其主要代码为:
func (clnt *client) Create(containerID string, checkpoint string, checkpointDir string, spec specs.Spec, attachStdio StdioCallback, options ...CreateOption) (err error) {
//生成一个libcontainerd.container对象
container := clnt.newContainer(filepath.Join(dir, containerID), options...)
//创建目录
err := idtools.MkdirAllAs(container.dir, 0700, uid, gid)
//生成文件config.json
f, err := os.Create(filepath.Join(container.dir, configFilename))
//调用container.start()进一步启动container,在5.中继续分析
return container.start(checkpoint, checkpointDir, attachStdio)
}
5.container.start()
container.start()的实现位于moby/libcontainerd/container_unix.go#L93#L175。该函数的主要功能是发送CreateContainerRequest RPC请求给docker-containerd。进程其主要代码为:
func (ctr *container) start(checkpoint string, checkpointDir string, attachStdio StdioCallback) (err error) {
//CreateContainerRequest对象,应该是保存一些容器信息给libcontainer start时使用
r := &containerd.CreateContainerRequest{
Id: ctr.containerID,
BundlePath: ctr.dir,
Stdin: ctr.fifo(syscall.Stdin),
Stdout: ctr.fifo(syscall.Stdout),
Stderr: ctr.fifo(syscall.Stderr),
Checkpoint: checkpoint,
CheckpointDir: checkpointDir,
// check to see if we are running in ramdisk to disable pivot root
NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "",
Runtime: ctr.runtime,
RuntimeArgs: ctr.runtimeArgs,
}
//发送CreateContainerRequest RPC请求给docker-containerd,这部分还不是很了解
resp, err := ctr.client.remote.apiClient.CreateContainer(context.Background(), r)
}
目前为止,算是分析完了daemon部分的start过程,接下来的内容应该是交给docker-containerd来继续start,但是目前对containerd还不了解,需要去了解containerd之后,才能继续后面的源码分析。
结语
本文分析了daemon对container start的处理做了一个简单分析,很多细节部分没有得到很好的分析,比如网络的具体创建、从daemon到containerd的传递,这些内容将在后续的博客中分析。