整体框架分析
dockerd 底层运行容器需要依赖多个二级制组件:docker daemon, containerd, container-shim, runC,
代码实现上,containerd包含了container-shim代码。同一份代码,通过Makefile编译控制,编译成两个二级制文件。
组件间通信
概括图
通信流程:
1. docker daemon 模块通过 grpc 和 containerd模块通信:dockerd 由libcontainerd
负责和containerd
模块进行交换, dockerd 和 containerd 通信socket文件:docker-containerd.sock
2. containerd 在dockerd 启动时被启动,启动时,启动grpc请求监听。containerd处理grpc请求,根据请求做相应动作;
3. 若是start或是exec 容器,containerd 拉起一个container-shim , 并通过exit 、control 文件(每个容器独有)通信;
4. container-shim别拉起后,start/exec/create拉起runC进程,通过exit、control文件和containerd通信,通过父子进程关系和SIGCHLD监控容器中进程状态;
5. 若是top等命令,containerd通过runC二级制组件直接和容器交换;
6. 在整个容器生命周期中,containerd通过 epoll 监控容器文件,监控容器的OOM等事件;
NOTE:
containerd,container-shim 组件本质上runC 和dockerd 间的adapter中间件,容器本身有runC单独完成 — 使用runC可以单独完成一个容器部署。
组件通信详细分析
DOCKER DEAMON —> LIBCONTAINERD 组件分析
LIBCONTAINERD 部分主要作用:1,赋值和CONTAINERD进程通信 2,监控CONTAINERD进程状态
LIBCONTAINERD 随DOCKERD启动,启动过程中启动协程监控:1. 和CONTAINERD间的grpc链接情况 2. 监听由CONTAINERD发送过来的消息
启动监控消息协程
//libcontainerd/remote_linux.go
//starEventMonitor() ---> handleEventStream()
func (r *remote) handleEventStream(events containerd.API_EventsClient) {
for {
e, err := events.Recv() // ---> 此为阻塞方法:等待CONTAINERD发送 gRPC消息
...
if err := container.handleEvent(e); err != nil {
logrus.Errorf("libcontainerd: error processing state change for %s: %v", e.Id, err)
}
...
}
上面主要代码主要功能就是接收gRPC返回的事件,并调用各自容器来的handleEvent对事件进行处理
//libcontainerd/container_linux.go
func (ctr *container) handleEvent(e *containerd.Event) error {
ctr.client.lock(ctr.containerID)
defer ctr.client.unlock(ctr.containerID)
switch e.Type {
case StateExit, StatePause, StateResume, StateOOM:
st := StateInfo{
CommonStateInfo: CommonStateInfo{
State: e.Type,
ExitCode: e.Status,
},
OOMKilled: e.Type == StateExit && ctr.oom,
}
if e.Type == StateOOM {
ctr.oom = true
}
if e.Type == StateExit && e.Pid != InitFriendlyName {
st.ProcessID = e.Pid
st.State = StateExitProcess
}
// Remove process from list if we have exited
switch st.State {
case StateExit:
ctr.clean()
ctr.client.deleteContainer(e.Id)
case StateExitProcess:
ctr.cleanProcess(st.ProcessID)
}
ctr.client.q.append(e.Id, func() {
if err := ctr.client.backend