上一篇介绍了daemon端对container create的处理,这一章将详细介绍daemon端对container start的处理,也就是r.postContainersStart函数
源码阅读基于docker 19.03
1. r.postContainersStart()
1.1 源码
r.postContainersCreate()的实现位于moby/api/server/router/container/container_routes.go,代码的主要内容是:
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()的实现位于,代码的主要内容是:
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,代码的主要内容是:
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(ctx, container.ID, spec, createOptions)
// TODO(mlaventure): we need to specify checkpoint options here
//
为指定的容器id创建并启动一个任务
pid, err := daemon.containerd.Start(context.Background(), container.ID, checkpointDir,
container.StreamConfig.Stdin() != nil || container.Config.Tty,
container.InitializeStdio)
}
下面3.1分析daemon.initializeNetworking()
函数,4.分析daemon.containerd.Create()
。
3.1 daemon.initializeNetworking()
daemon.initializeNetworking()的实现位于moby/daemon/container_operations.go。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,其主要实现为:
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\remote\client.go,其主要代码为:
func (c *client) Create(ctx context.Context, id string, ociSpec *specs.Spec, runtimeOptions interface{}) error {
//我们通过 NewContainer 方法可以使用默认的 OCI runtime spec 直接创建容器对象。当然,也可以通过 Opts 模式的参数修改默认值:
_, err := c.client.NewContainer(ctx, id,
containerd.WithSpec(ociSpec),
containerd.WithRuntime(runtimeName, runtimeOptions),
WithBundle(bdir, ociSpec),
}
目前为止,算是分析完了daemon部分的start过程,接下来的内容应该是交给docker-containerd来继续start,但是目前对containerd还不了解,需要去了解containerd之后,才能继续后面的源码分析。
结语
本文分析了daemon对container start的处理做了一个简单分析,很多细节部分没有得到很好的分析,比如网络的具体创建、从daemon到containerd的传递,这些内容将在后续的博客中分析。