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将容器主进程的执行权交给用户程序