containerd的中的各種操作都是通過Task來進行的,因此對於容器的create, start, delete等等操作其實都是一個個的Task而已。
Task的數據結構如下所示:
type Task interface { Errorch() chan error } type baseTask struct { errCh chan error mu sync.Mutex }
container的數據結構如下所示,而container對外暴露的interface是Container,其中包含了對container的各種操作,包括ID(), Delete()等等。
type container struct { // path to store runtime state information root string id string bundle string runtime string runtimeArgs []string shim string processes map[string]*process labels []string oomFds []int noPivotRoot bool timeout time.Duration }
容器的delete操作
DeleteTask的數據結構如下所示:
// DeleteTask holds needed paramaters to remove a container type DeleteTask struct { baseTask ID string Status uint32 PID string NoEvent bool Process runtime.Process }
-2、containerd/supervisor/monitor_linux.go
func (m *Monitor) start()對獲取到的syscall.EpollEvent進行處理,當獲取的event的id指向的是一個runtime.Process並且event的Events類型為syscall.EPOLLHUP時,先對該process從events中刪除,進行epoll相關的清理操作,最后調用m.exits <- t
-1、containerd/supervisor/supervisor.go
func (s *Supervisor) exitHandler()
(1)、在啟動daemon的時候,啟動過一個exitHandler的goroutine,該函數主要的作用就是從s.monitor.exits這個runtime.Process類型的channel中獲取退出的process。
(2)、對於每個退出的process,創建 e := &ExitTask{Process: p,},最后s.SendTask(e),最終經過taskHandler的調度,最終會在exit()函數進行處理
0、containerd/supervisor/exit.go
func (s *Supervisor) exit(t *ExitTask)
(1)、調用`proc := t.Process `, `status, err := proc.ExitStatus()`獲取進程的退出碼
(2)、如果proc.ID()不是runtime.InitProcessID,則說明只是一個exec的進程退出,則創建一個ne := &ExecExitTask{},再調用s.execExit()進行處理
(3)、若為退出的是init進程,則創建一個ne := &DeleteTask{},再調用s.delete(ne)進行處理
1、containerd/supervisor/delete.go
func (s *Supervisor) delete(t *DeleteTask) error
(1)、調用i, ok := s.containers[t.ID]獲取容器實例,再調用s.deleteContainer(i.container)
(2)、當t.Process 不為nil 時,調用t.Process.Wait()
(3)、當t.NoEvent為false時,則調用s.notifySubscribers(Event{Type: StateExit, Timestamp: time.Now(), ID: t.ID, Status: t.status, PID: t.PID,})
(4)、更新supervisor的container信息,調用ContainersCounter.Dec(1)和ContainerDeleteTimer.UpdateSince(start)
2、containerd/supervisor/delete.go
func (s *Supervisor) deleteContainer(container runtime.Container) error
該函數很簡單,先調用delete(s.containers, container.ID()),再調用return container.Delete()
3、containerd/runtime/container.go
首先調用os.RemoveAll(filepath.Join(c.root, c.id)),刪除目錄/var/run/docker/libcontainerd/containerd/container-id,接着利用exec.Command直接調用調用命令行`docker-runc delete contain-id。
容器的kill操作
1、containerd/api/grpc/server/server.go
func (s *apiServer) Signal(ctx context.Context, r *types.SignalRequest) (*types.SignalResponse)
這個函數就是根據r中的內容對supervisor.SignalTask進行填充,最后調用s.sv.SendTask(e),再返回return &types.SignalResponse{}, nil
2、containerd/supervisor/signal.go
func (s *Supervisor) signal(t *SignalTask) error
(1)、首先調用i, ok := s.containers[t.ID]獲取container實例
(2)、再調用processes, err := i.container.Processes()獲取容器中所有的process實例
(3)、最后遍歷processes,找到t.PID對應的process,調用return p.Signal(t.Signal)
3、containerd/runtime/process.go
func (p *process) Signal(s os.Signal) error
該函數只是簡單地調用return syscall.Kill(p.pid, s.(syscall.Signal)),給相應的進程發送信號。