DOCKER源码-启动流程
dockerd启动流程
当用户执行命令
systemctl start docker
后,systemd检查docker配置文件/usr/lib/systemd/system/docker.service
。
因为docker配置文件中定义了
BindsTo=containerd.service
,所以containerd将先启动。
如果containerd启动失败,那么dockerd也将启动失败。
containerd启动成功后,dockerd二进制文件开始启动,开始执行main()函数。
dockerd程序的main函数所在文件:
cmd/dockerd/docker.go
main函数中的调用的
newDaemonCommand()
函数用于构建一个Command对象,该对象定义如下:cmd := &cobra.Command{ Use: "dockerd [OPTIONS]", Short: "A self-sufficient runtime for containers.", SilenceUsage: true, SilenceErrors: true, Args: cli.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { opts.flags = cmd.Flags() return runDaemon(opts) }, DisableFlagsInUseLine: true, Version: fmt.Sprintf("%s, build %s", dockerversion.Version, dockerversion.GitCommit), }
Command对象中的RunE字段就是用来启动dockerd的函数,它最终调用了
DaemonCli.start()
函数,DaemonCli定义:
cmd/dockerd/daemon.go
type DaemonCli struct { *config.Config configFile *string flags *pflag.FlagSet api *apiserver.Server d *daemon.Daemon authzMiddleware *authorization.Middleware }
而
DaemonCli
实例中的start()
函数是dockerd启动的主要逻辑,主要包含以下事项:- 从命令行中加载日志相关参数,并配置logrus日志对象
- 根据命令行参数判断是否打开Debug模式,是否打开实验特性
- 创建
--data-root
目录
- 创建PID文件
- 是否启用无root模式
- 加载API Server相关配置,包括证书
- 创建API Server,但现在不启动
- 创建一个协程与containerd保持心跳,如果连接containerd失败,则dockerd启动失败
- 监听信号量并设置hook函数用于释放DaemonCli对象
- 创建Daemon实例,创建Daemon的过程中创建了几个重要对象(这里只列出部分,后面会细讲):
- containerd:负责容器相关操作,且依赖一个RPC客户端,大部分容器相关操作都通过RPC调用containerd进程来完成
- volumes:负责volume的创建、查找、删除等
- imageService:负责镜像相关操作
- netController:负责网络设备的相关操作
- containerd:负责容器相关操作,且依赖一个RPC客户端,大部分容器相关操作都通过RPC调用containerd进程来完成
- 将Daemon对象放入DaemonCli对象中
- 启动Metrics服务,默认地址:unix:/var/run/docker/metrics.sock
- 如果之前配置过swarm的话,则重新加入集群,并启动集群总的服务
- 构建API路由:用Daemon对象中包含的各核心功能与API Path对应起来
- 启动API Server,默认地址:unix:/var/run/docker.sock
- 阻塞直到API Server协程发生错误或完成退出后,结束各个协程
- 从命令行中加载日志相关参数,并配置logrus日志对象
Daemon结构体
以下是Daemon结构体的定义,在19.03.7版本中包含40个字段,下面我们只分析几个重要字段:
daemon/daemon.go
type Daemon struct {
// ... 省略多行 ...
repository string
containers container.Store
containersReplica container.ViewDB
execCommands *exec.Store
imageService *images.ImageService
netController libnetwork.NetworkController
volumes *volumesservice.VolumesService
discoveryWatcher discovery.Reloader
root string
graphDrivers map[string]string // By operating system
containerdCli *containerd.Client
containerd libcontainerdtypes.Client
// ... 省略多行 ...
}
字段说明:
root:字符串,记录了
--data-root
选项的值
repository:字符串,记录了存储容器的目录:
--data-root选项的值 + /containers
containers:一个容器对象缓存器,方便其他需要频繁查询容器状态的模块使用
containersReplica:目前是一个用基数树算法实现的内存式数据库,用于快速查找容器
volumes:负责volume的创建、查找、删除等,在使用默认驱动local的情况下,创建volume的工作由dockerd进程通过创建本地目录来完成,如果已存在同名的vlume,则直接返回该volume,关键代码:
volume/local/local.go
func (r *Root) Create(name string, opts map[string]string) (volume.Volume, error) { // ... 省略多行 ... v, exists := r.volumes[name] if exists { return v, nil } path := r.DataPath(name) if err := idtools.MkdirAllAndChown(path, 0755, r.rootIdentity); err != nil { return nil, errors.Wrapf(errdefs.System(err), "error while creating volume path '%s'", path) } // ... 省略多行 ...
imageService:负责镜像相关操作,镜像的操作都是由dockerd进程在本地完成
netController:负责网络设备的相关操作,详见另一篇文章:DOCKER源码-创建网络
containerd:负责容器相关操作,且依赖一个RPC客户端,大部分容器相关操作都通过RPC调用containerd进程来完成,详见另一篇文章:DOCKER源码-创建容器