// containerd-shim is a small shim that sits in front of a runtime implementation that allows it to be reparented to init and handle reattach from the caller.
// the cwd of the shim should be the path to the state directory where the shim can locate fifos and other information\
// Arg0: id of the container
// Arg1: bundle path
// Arg2: runtime binary
在啟動容器時,containerd-shim的命令行格式如下所示:
docker-containerd-shim 9decb150527a3b64a86b85cfc6faeb09f786dcb2f4e668611418280c75755539
/var/run/docker/libcontainerd/9decb150527a3b64a86b85cfc6faeb09f786dcb2f4e668611418280c75755539 docker-runc
1、containerd/containerd-shim/main.go
func main()
(1)、創建log文件,f, err := os.OpenFile(filepath.Join(cwd, "shim-log.json") ...)
(2)、調用err := start(f)函數,若err不為nil,當err為errRuntime的時候,直接關閉f並返回,否則將錯誤記錄到shim-log.json中
2、containerd/containerd-shim/main.go
// start handling signals as soon as possible so that things are properly reaped
// or if runtime exits before we hit the handler
func start(log *os.File) error
(1)、set the shim as the subreaper for all orphaned processes created by the container,err := osutils.SetSubreaper(1)
(2)、打開exit pipe和control pipe
(3)、調用p, err := newProcess(flag.Arg(0), flag.Arg(1), flag.Arg(2)),加載process實例,再調用p.create()
(4)、msgC := make(chan controlMesage, 32),創建一個goroutine,從control pipe中不斷讀取controlMessage
(5)、最后,一個無限for循環,對來自signal的信號和controlMessage進行處理
(6)、當從signal中獲得的信號為SIGCHLD時,當退出的進程為runtime時,退出shim
(7)、對來此control pipe的controlMessage進行處理,當msg的Type為0時,關閉stdin,當Type為1時,且p.console不為nil,則調整tty的窗口大小
3、containerd/containerd-shim/process.go
func newProcess(id, bundle, runtimeName string)
(1)、p := &process{id: id, bundle: bundle, runtime: runtimeName}
(2)、調用s, err := loadProcess(),從process.json文件中加載state,p.state = s
(3)、調用p.openIO(),最后,返回p
4、containerd/containerd-shim/process.go
// openIO opens the pre-created fifo's for use with the container in RDWR so that they remain open if the other side stops listening
func (p *process) openIO() error
(1)、先創建一個goroutine,打開p.state.Stdin, p.stdinCloser = os.openFile(p.state.Stdin, syscall.O_WRONLY)
// NewConsole returns an initialized console that can be used within a container by copying bytes from the master side to the slave that is attached as the tty for the container's init process
(2)、如果p.state.Terminal為true,調用master, console, err := newConsole(uid, gid),p.console = master, p.consolePath = console,再打開p.state.Stdin和p.state.Stdout,最后調用io.Copy將stdin/stdout與master相連
(3)、對於非tty的情況,調用i, err := p.initializeIO(uid),p.shimIO = i,再打開p.state.Stdout和p.state.Stderr(方式為可讀寫)分別與i.Stdou和i.Stderr相連。接着打開p.state.Stdin為只讀模式,再將i.Stdin和p.state.Stdin相連
5、containerd/containerd-shim/process.go
func (p *process) initializeIO(rootuid int)
該函數生成三個os.Pipe()並分別將一端賦給i = &IO{},再將另一端賦給p.stdio
6、containerd/containerd-shim/process.go
func (p *process) create() error
(1)、獲取當前目錄,創建logPath := filepath.Join(cwd, "log.json"),再擴展args := append([]string{"--log", logPath, "--log-format", "json"}, p.state.RuntimeArgs...)
(2)、對exec,checkpoint,create進行不同的處理,這里只討論create的情況,繼續擴展args = append(args, "create", "--bundle", p.bundle, "--console", p.consolepath)
(3)、擴展參數--pid-file,cmd := exec.Command(p.runtime, args...),並且將cmd的stdio設置為p.stdio
(4)、調用cmd.Start(),再調用p.stdio.stdout.Close()和p.stdio.stderr.Close()(why????)
(5)、cmd.Wait(),從pid文件中讀出pid,並且將p.containerPid 設置為pid