docker run源码分析(二)之Docker daemon--container start

上一篇咱们简单的分析了docker run命令在Docker daemon中的create实现,接下来咱们开始start的分析。源码基于Docker-ce17.09

1、daemon端对container start的处理,即 r.postContainersStart() 函数。

1.1、流程:

1.2、源码:postContainerStart()函数

func (s *containerRouter) postContainersStart(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {

	version := httputils.VersionFromContext(ctx)
	//获取hostConfig配置信息
	var hostConfig *container.HostConfig
	// A non-nil json object is at least 7 characters.
	//非nil 的json对象至少为7个字符。一系列检查
	if r.ContentLength > 7 || r.ContentLength == -1 {
		if versions.GreaterThanOrEqualTo(version, "1.24") {
			return bodyOnStartError{}
		}
		if err := httputils.CheckForJSON(r); err != nil {
			return err
		}
		c, err := s.decoder.DecodeHostConfig(r.Body)
		if err != nil {
			return err
		}
		hostConfig = c
	}
	//解析form表单
	if err := httputils.ParseForm(r); err != nil {
		return err
	}
	//获得与键关联的值
	checkpoint := r.Form.Get("checkpoint")
	checkpointDir := r.Form.Get("checkpoint-dir")
	//调用ContainerStart进一步启动容器,下面详细分析
	if err := s.backend.ContainerStart(vars["name"], hostConfig, checkpoint, checkpointDir); err != nil {
		return err
	}

	w.WriteHeader(http.StatusNoContent)
	return nil
}

1.3、源码: backend.ContainerStart()函数 和  ContainerStart()函数

// ContainerStart starts a container.
func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.HostConfig, checkpoint string, checkpointDir string) error {
	if checkpoint != "" && !daemon.HasExperimental() {
		return validationError{errors.New("checkpoint is only supported in experimental mode")}
	}
	//可以根据全容器ID、容器名、容器ID前缀获取容器对象
	container, err := daemon.GetContainer(name)
	if err != nil {
		return err
	}

	//调用containerStart进一步启动容器,下面详细分析
	if err := daemon.containerStart(container, checkpoint, checkpointDir, true); err != nil {
		return err
	}
	return nil
}

下面的containerStart()  没看懂,再补充

// containerStart prepares the container to run by setting up everything the
// container needs, such as storage and networking, as well as links
// between containers. The container is left waiting for a signal to
// begin running.
func (daemon *Daemon) containerStart(container *container.Container, checkpoint string, checkpointDir string, resetRestartManager bool) (err error) {
	start := time.Now()
	container.Lock()
	defer container.Unlock()

	if resetRestartManager && container.Running { // skip this check if already in restarting step and resetRestartManager==false
		return nil
	}

	if container.RemovalInProgress || container.Dead {
		return stateConflictError{errors.New("container is marked for removal and cannot be started")}
	}

	// if we encounter an error during start we need to ensure that any other
	// setup has been cleaned up properly
	defer func() {
		if err != nil {
			container.SetError(err)
			// if no one else has set it, make sure we don't leave it at zero
			if container.ExitCode() == 0 {
				container.SetExitCode(128)
			}
			if err := container.CheckpointTo(daemon.containersReplica); err != nil {
				logrus.Errorf("%s: failed saving state on start failure: %v", container.ID, err)
			}
			container.Reset(false)

			daemon.Cleanup(container)
			// if containers AutoRemove flag is set, remove it after clean up
			if container.HostConfig.AutoRemove {
				container.Unlock()
				if err := daemon.ContainerRm(container.ID, &types.ContainerRmConfig{ForceRemove: true, RemoveVolume: true}); err != nil {
					logrus.Errorf("can't remove container %s: %v", container.ID, err)
				}
				container.Lock()
			}
		}
	}()

	if err := daemon.conditionalMountOnStart(container); err != nil {
		return err
	}

	if err := daemon.initializeNetworking(container); err != nil {
		return err
	}

	spec, err := daemon.createSpec(container)
	if err != nil {
		return systemError{err}
	}

	createOptions, err := daemon.getLibcontainerdCreateOptions(container)
	if err != nil {
		return err
	}

	if resetRestartManager {
		container.ResetRestartManager(true)
	}

	if checkpointDir == "" {
		checkpointDir = container.CheckpointDir()
	}

	if daemon.saveApparmorConfig(container); err != nil {
		return err
	}

	if err := daemon.containerd.Create(container.ID, checkpoint, checkpointDir, *spec, container.InitializeStdio, createOptions...); err != nil {
		return translateContainerdStartErr(container.Path, container.SetExitCode, err)

	}

	containerActions.WithValues("start").UpdateSince(start)

	return nil
}

1.4、daemon.containerd.Create()中的Create是在client 接口里面,client提供对容器功能的访问。下面分析Create()函数 ,位置

func (clnt *client) Create(containerID string, checkpoint string, checkpointDir string, spec specs.Spec, attachStdio StdioCallback, options ...CreateOption) (err error) {
	clnt.lock(containerID)
	defer clnt.unlock(containerID)

	if _, err := clnt.getContainer(containerID); err == nil {
		return fmt.Errorf("Container %s is already active", containerID)
	}

	uid, gid, err := getRootIDs(spec)
	if err != nil {
		return err
	}
	dir, err := clnt.prepareBundleDir(uid, gid)
	if err != nil {
		return err
	}
	//生成一个libcontainerd.container对象
	container := clnt.newContainer(filepath.Join(dir, containerID), options...)
	if err := container.clean(); err != nil {
		return err
	}

	defer func() {
		if err != nil {
			container.clean()
			clnt.deleteContainer(containerID)
		}
	}()
	//创建目录
	if err := idtools.MkdirAllAndChown(container.dir, 0700, idtools.IDPair{uid, gid}); err != nil && !os.IsExist(err) {
		return err
	}
	//生成文件config.json
	f, err := os.Create(filepath.Join(container.dir, configFilename))
	if err != nil {
		return err
	}
	defer f.Close()
	if err := json.NewEncoder(f).Encode(spec); err != nil {
		return err
	}
	//调用container.start()进一步启动container
	return container.start(&spec, checkpoint, checkpointDir, attachStdio)
}

1.5、分析:container.start()

该函数主要功能是发送CreateContainerRequest RPC请求给 docker-containerd。(不明白)

func (ctr *container) start(spec *specs.Spec, checkpoint, checkpointDir string, attachStdio StdioCallback) (err error) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	ready := make(chan struct{})

	fifoCtx, cancel := context.WithCancel(context.Background())
	defer func() {
		if err != nil {
			cancel()
		}
	}()

	iopipe, err := ctr.openFifos(fifoCtx, spec.Process.Terminal)
	if err != nil {
		return err
	}

	var stdinOnce sync.Once

	// we need to delay stdin closure after container start or else "stdin close"
	// event will be rejected by containerd.
	// stdin closure happens in attachStdio
	stdin := iopipe.Stdin
	iopipe.Stdin = ioutils.NewWriteCloserWrapper(stdin, func() error {
		var err error
		stdinOnce.Do(func() { // on error from attach we don't know if stdin was already closed
			err = stdin.Close()
			go func() {
				select {
				case <-ready:
				case <-ctx.Done():
				}
				select {
				case <-ready:
					if err := ctr.sendCloseStdin(); err != nil {
						logrus.Warnf("failed to close stdin: %+v", err)
					}
				default:
				}
			}()
		})
		return err
	})
	//CreateContainerRequest对象,应该是保存一些容器信息给libcontainer start时使用
	r := &containerd.CreateContainerRequest{
		Id:            ctr.containerID,
		BundlePath:    ctr.dir,
		Stdin:         ctr.fifo(unix.Stdin),
		Stdout:        ctr.fifo(unix.Stdout),
		Stderr:        ctr.fifo(unix.Stderr),
		Checkpoint:    checkpoint,
		CheckpointDir: checkpointDir,
		// check to see if we are running in ramdisk to disable pivot root
		NoPivotRoot: os.Getenv("DOCKER_RAMDISK") != "",
		Runtime:     ctr.runtime,
		RuntimeArgs: ctr.runtimeArgs,
	}
	ctr.client.appendContainer(ctr)

	if err := attachStdio(*iopipe); err != nil {
		ctr.closeFifos(iopipe)
		return err
	}
	//发送CreateContainerRequest RPC请求给docker containerd(不懂)
	resp, err := ctr.client.remote.apiClient.CreateContainer(context.Background(), r)
	if err != nil {
		ctr.closeFifos(iopipe)
		return err
	}
	ctr.systemPid = systemPid(resp.Container)
	close(ready)

	return ctr.client.backend.StateChanged(ctr.containerID, StateInfo{
		CommonStateInfo: CommonStateInfo{
			State: StateStart,
			Pid:   ctr.systemPid,
		}})

}

参考:

        https://guanjunjian.github.io/2017/09/28/study-5-docker-5-postContainersStart/

       《Docker源码分析》

        百度网盘:链接: https://pan.baidu.com/s/1m5AqgfZKvPRu2BcmWz-WxA 提取码: wjrj 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
This error message indicates that the Docker daemon is not running on your system, or that the Docker client is unable to connect to it. To resolve this issue, you can try the following steps: 1. Start the Docker daemon: You can do this by running the command `sudo systemctl start docker` on Linux or `docker run hello-world` on Windows or macOS. This will start the Docker daemon and allow the Docker client to connect to it. 2. Check if the Docker daemon is running: You can run the command `sudo systemctl status docker` on Linux or `docker ps` on Windows or macOS to check if the Docker daemon is running. If it is not running, you will need to start it as described in step 1. 3. Check Docker socket permissions: Ensure that the Docker socket file (/var/run/docker.sock) has the correct permissions. You can run the command `ls -l /var/run/docker.sock` to check the permissions. The owner should be root and the group should be docker. If the permissions are incorrect, you can change them by running the command `sudo chown root:docker /var/run/docker.sock`. 4. Check if Docker is installed: If you have recently installed Docker, make sure that it is installed correctly. You can run the command `docker version` to check if Docker is installed and running correctly. 5. Check if Docker is running in a container: If you are running Docker in a container, make sure that the container is running and that the Docker daemon is started within the container. If none of these steps resolve your issue, you may need to refer to the Docker documentation or seek assistance from the Docker community.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值