docker run流程
创建容器
- 通过用户指定的镜像名和tag,在TagStore中查找image_id,获取image对象(本地的image json文件里有镜像信息)
- 检查镜像layer数(不超过127),镜像层数太多会造成性能问题
- 将runconfig.Config和image.Config合并
- 创建{Container.ID}-init和Container.ID镜像的目录,在init layer创建.dockerinit、/etc/hosts、/etc/hostname等, Container.ID镜像是read-write layer
- 将Container对象持久化到path.Join(Container.ID, “config.json”)
- 在Daemon.idIndex中注册Container.ID,在Daemon.contStore中记录Container.ID和Container对象的映射关系
启动容器
- 通过Container.ID从Daemon.contStore中获取Container对象
- 配置容器的dns地址/etc/resolv.conf
- 将所有祖先镜像挂载到path.Join(graphdriver.root, “mnt”, Container.ID)
- 初始化容器的hosts文件/etc/hosts
- 检查主机是否开启cgroup内存限制、swap内存限制、ipv4转发
- 将容器内挂在目录和宿主机目录的映射关系存入container.Volume
- 将容器的link信息存入graphdb中,基于sqllite的图模型的数据库
- 通过iptables开启link容器间的通信,并env化
- 获取容器运行的当前目录,获取用户指定的环境变量
- 构建execdriver的Command对象,包括进程命令、Network、Mount、Resources、WorkingDir,用于容器的启动和配置
- 将Command对象持久化到path.Join(config.root, “execdriver/native/container.json”)
- 通过execdriver.Run启动Command对象
启动dockerinit
dockerinit是daemon启动容器运行的第一个进程,类似linux的init进程
初始化容器的Network资源、Mount资源、设置用户、设置环境变量等在daemon进程中做不到事情
Daemon执行流程
- 创建syncPipe用于跨namespace的通信
- execdriver.Run创建一个exec.Cmd对象来执行dockerinit命令
- exec.Cmd.Args代表dockerinit的执行参数,native表示execdriver类型,pipe表示同步管道的文件描述符,root表示容器- 配置文件container.json和state.json所在目录,args表示用户指定的命令
- Exec.Cmd.SysProcAttr携带需要为进程创建新namespace参数Cloneflags,包括NEWNS、NEWUTS、NEWIPC、NEWPID、NEWNET
- 调用exec.Cmd.Start()启动dockerinit进程
- 调用namespaces.SetupCgroups为dockerinit配置cgroup(cpu、iops、memory、freezer)
- 调用namespaces.InitializeNetworking为dockerinit创建网络栈
- 调用syncPipe.SendToChild(networkState)将需要dockerinit完成的剩余网络配置发给dockerinit
- 调用syncPipe.ReadFromChild与dockerinit同步
Daemon执行流程
- 创建syncPipe用于跨namespace的通信
- execdriver.Run创建一个exec.Cmd对象来执行dockerinit命令
- exec.Cmd.Args代表dockerinit的执行参数,native表示execdriver类型,pipe表示同步管道的文件描述符,root表示容器- 配置文件container.json和state.json所在目录,args表示用户指定的命令
- Exec.Cmd.SysProcAttr携带需要为进程创建新namespace参数Cloneflags,包括NEWNS、NEWUTS、NEWIPC、NEWPID、NEWNET
- 调用exec.Cmd.Start()启动dockerinit进程
- 调用namespaces.SetupCgroups为dockerinit配置cgroup(cpu、iops、memory、freezer)
- 调用namespaces.InitializeNetworking为dockerinit创建网络栈
- 调用syncPipe.SendToChild(networkState)将需要dockerinit完成的剩余网络配置发给dockerinit
- 调用syncPipe.ReadFromChild与dockerinit同步
Dockerinit执行流程
- 从参数中读取root配置文件所在目录
- 从path.Join(root, “container.json”)中读取daemon传过来的execdriver.Command对象,获取容器的配置信息
- 调用mount.InitializeMountNamespace完成挂载资源的初始化,包括rootfs根文件系统,挂载点Volume,设备文件
- 调用namespaces.FinalizeNamespace完成剩余的配置,包括关闭除0/1/2以外的文件句柄,为容器创建新的用户ID、组ID,切换到工作目录
- 调用syscall.Exec将容器主进程的执行权交给用户程序