docker源码快速阅读
阅读前准备
git: https://github.com/moby/moby.git
branch: v18.06.3-ce
ide: goland2020.2
配置goland的build tag
一起见证docker 代码中套路
exec.Command=> cmd.Start() => cmd.Wait()
进程模型
docker
|
V
dockerd -> containerd ---> shim -> runc -> runc init -> process1
|--> shim -> runc -> runc init -> process2
+--> shim -> runc -> runc init -> process3
1.docker && containerd
cmd/dockerd/docker.go
52func main() {
53 if reexec.Init() {
54 return
55 }
56
57 // Set terminal emulation based on platform as required.
58 _, stdout, stderr := term.StdStreams()
59
60 // @jhowardmsft - maybe there is a historic reason why on non-Windows, stderr is used
61 // here. However, on Windows it makes no sense and there is no need.
62 if runtime.GOOS == "windows" {
63 logrus.SetOutput(stdout)
64 } else {
65 logrus.SetOutput(stderr)
66 }
67
68 cmd := newDaemonCommand() // 跳
69 cmd.SetOutput(stdout)
70 if err := cmd.Execute(); err != nil {
71 fmt.Fprintf(stderr, "%s\n", err)
72 os.Exit(1)
73 }
74}
18func newDaemonCommand() *cobra.Command {
19 opts := newDaemonOptions(config.New())
20
21 cmd := &cobra.Command{
22 Use: "dockerd [OPTIONS]",
23 Short: "A self-sufficient runtime for containers.",
24 SilenceUsage: true,
25 SilenceErrors: true,
26 Args: cli.NoArgs,
27 RunE: func(cmd *cobra.Command, args []string) error {
28 opts.flags = cmd.Flags()
29 return runDaemon(opts) // 跳
30 },
31 DisableFlagsInUseLine: true,
32 Version: fmt.Sprintf("%s, build %s", dockerversion.Version, dockerversion.GitCommit),
33 }
34 cli.SetupRootCommand(cmd)
35
36 flags := cmd.Flags()
37 flags.BoolP("version", "v", false, "Print version information and quit")
38 flags.StringVar(&opts.configFile, "config-file", defaultDaemonConfigFile, "Daemon configuration file")
39 opts.InstallFlags(flags)
40 installConfigFlags(opts.daemonConfig, flags)
41 installServiceFlags(flags)
42
43 return cmd
44}
cmd/dockerd/docker_unix.go
5func runDaemon(opts *daemonOptions) error {
6 daemonCli := NewDaemonCli()
7 return daemonCli.start(opts) // 跳
8}
cmd/dockerd/daemon.go
74func (cli *DaemonCli) start(opts *daemonOptions) (err error) {
75 stopc := make(chan bool)
76 defer close(stopc)
77 ...
143 rOpts, err := cli.getRemoteOptions()
144 if err != nil {
145 return fmt.Errorf("Failed to generate containerd options: %v", err)
146 }
147 containerdRemote, err := libcontainerd.New(filepath.Join(cli.Config.Root, "containerd"), filepath.Join(cli.Config.ExecRoot, "containerd"), rOpts...) // 跳
148 if err != nil {
149 return err
150 }
151 signal.Trap(func() {
152 cli.stop()
153 <-stopc // wait for daemonCli.start() to return
154 }, logrus.StandardLogger())
155 ...
libcontainerd/remote_daemon.go
61// New creates a fresh instance of libcontainerd remote.
62func New(rootDir, stateDir string, options ...RemoteOption) (rem Remote, err error) {
63 defer func() {
64 if err != nil {
65 err = errors.Wrap(err, "Failed to connect to containerd")
66 }
67 }()
68 ...
93 ...
94 if r.startDaemon {
95 os.Remove(r.GRPC.Address)
96 if err = r.startContainerd(); err != nil { // 跳
97 return
98 }
99 defer func() {
100 if err != nil {
101 r.Cleanup()
102 }
103 }()
104 }
105 ...
205func (r *remote) startContainerd() error {
206 pid, err := r.getContainerdPid()
207 if err != nil {
208 return err
209 }
210 ...
222 ...
223 args := []string{"--config", configFile}
224 cmd := exec.Command(binaryName, args...) // 注意binaryName
225 // redirect containerd logs to docker logs
226 cmd.Stdout = os.Stdout
227 cmd.Stderr = os.Stderr
228 cmd.SysProcAttr = containerdSysProcAttr()
229 // clear the NOTIFY_SOCKET from the env when starting containerd
230 cmd.Env = nil
231 for _, e := range os.Environ() {
232 if !strings.HasPrefix(e, "NOTIFY_SOCKET") {
233 cmd.Env = append(cmd.Env, e)
234 }
235 }
236 if err := cmd.Start(); err != nil {
237 return err
238 }
239
240 r.daemonWaitCh = make(chan struct{})
241 go func() {
242 // Reap our child when needed
243 if err := cmd.Wait(); err != nil {
244 r.logger.WithError(err).Errorf("containerd did not exit successfully")
245 }
246 close(r.daemonWaitCh)
247 }()
exec.Command() => cmd.Start() => cmd.Wait()
再看一下binaryName的定义
27const (
28 maxConnectionRetryCount = 3
29 healthCheckTimeout = 3 * time.Second
30 shutdownTimeout = 15 * time.Second
31 configFile = "containerd.toml"
32 binaryName = "docker-containerd"
33 pidFile = "docker-containerd.pid"
34)
docker-container-shim
再来看下shim部分
vendor/github.com/containerd/containerd/runtime/linux/runtime.go
vendor/github.com/containerd/containerd/runtime/linux/bundle.go
vendor/github.com/containerd/containerd/runtime/shim/client/client.go
拉起shim 用到了exec.Command() => cmd.Start() => cmd.Wait()
docker-runc
vendor/github.com/containerd/containerd/runtime/shim/service.go
vendor/github.com/containerd/containerd/runtime/linux/proc/init.go
NewRunc
只是返回了一个Runc实例,还要继续看一下p.runtime.Create
vendor/github.com/containerd/go-runc/runc.go
r.Command=>Monitor.Start=>Monitor.Wait
套路也依然是 exec.Command=> cmd.Start() => cmd.Wait()
vendor/github.com/containerd/go-runc/command_linux.go
最后看看exec.CommandContext上面的DefaultCommand的定义
vendor/github.com/containerd/go-runc/runc.go