Docker Linux 平台上的一款轻量级虚拟化容器的管理引擎。在全球范围内, Docker 还是一个开源项目,整个项目基于 Go 语言开发,代码托管于 GitHub 网站上,并遵从 Apache 2.0 协议。目前, Docker 可以帮助用户在容器内部快速自动化部署应用,并利用 Linux 内核特性命名空间( namespaces) 及控制组( cgroups) 等为容器提供隔离的运行环境。Docker 借助操作系统层的虚拟化实现资源的隔离,因此 Docker 容器在运行时与虚拟机 (VM) 的运行有很大的区别, Docker 容器与宿主机共享同一个操作系统,不会有额外的操作系统开销。
查看架构图详解,请参考:https://www.infoq.cn/article/docker-source-code-analysis-part1
了解Docker源码解析Dockerfile的流程,了解docker build以及docker run 在client、daemon和engine端的处理。
Docker Client 是Docker 架构中用户与 Docker Daemon 建立通信的客户端。从main()函数开始,位置点击。
docker client的入口函数main,在main中处理传入的参数,并把参数转化为cobra的command类型,最后通过cobra调用相应的方法。
// AddCommands adds all the commands from cli/command to the root command
func AddCommands(cmd *cobra.Command, dockerCli *command.DockerCli) {
cmd.AddCommand(
// checkpoint
checkpoint.NewCheckpointCommand(dockerCli),
// config
config.NewConfigCommand(dockerCli),
// container
container.NewContainerCommand(dockerCli),
//添加子命令run及其选项
container.NewRunCommand(dockerCli),
// image
image.NewImageCommand(dockerCli),
image.NewBuildCommand(dockerCli),
// registry
registry.NewLoginCommand(dockerCli),
registry.NewLogoutCommand(dockerCli),
registry.NewSearchCommand(dockerCli),
// secret
secret.NewSecretCommand(dockerCli),
// service
service.NewServiceCommand(dockerCli),
// system
system.NewSystemCommand(dockerCli),
system.NewVersionCommand(dockerCli),
……………………
)
}
// NewRunCommand create a new `docker run` command
func NewRunCommand(dockerCli *command.DockerCli) *cobra.Command {
var opts runOptions
var copts *containerOptions
cmd := &cobra.Command{
Use: "run [OPTIONS] IMAGE [COMMAND] [ARG...]",
Short: "Run a command in a new container",
Args: cli.RequiresMinArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
copts.Image = args[0]
if len(args) > 1 {
copts.Args = args[1:]
}
//run对应的客户端方法
return runRun(dockerCli, cmd.Flags(), &opts, copts)
},
}
flags := cmd.Flags()
flags.SetInterspersed(false)
// These are flags not stored in Config/HostConfig
flags.BoolVarP(&opts.detach, "detach", "d", false, "Run container in background and print container ID")
flags.BoolVar(&opts.sigProxy, "sig-proxy", true, "Proxy received signals to the process")
flags.StringVar(&opts.name, "name", "", "Assign a name to the container")
flags.StringVar(&opts.detachKeys, "detach-keys", "", "Override the key sequence for detaching a container")
// Add an explicit help that doesn't have a `-h` to prevent the conflict
// with hostname
flags.Bool("help", false, "Print usage")
command.AddTrustVerificationFlags(flags)
copts = addFlags(flags)
return cmd
}
func runRun(dockerCli *command.DockerCli, flags *pflag.FlagSet, ropts *runOptions, copts *containerOptions) error {
//得到代理配置 // ConfigFile ~/.docker/config.json file info
proxyConfig := dockerCli.ConfigFile().ParseProxyConfig(dockerCli.Client().DaemonHost(), copts.env.GetAll())
newEnv := []string{}
for k, v := range proxyConfig {
if v == nil {
newEnv = append(newEnv, k)
} else {
newEnv = append(newEnv, fmt.Sprintf("%s=%s", k, *v))
}
}
copts.env = *opts.NewListOptsRef(&newEnv, nil)
containerConfig, err := parse(flags, copts)
// just in case the parse does not exit
if err != nil {
reportError(dockerCli.Err(), "run", err.Error(), true)
return cli.StatusError{StatusCode: 125}
}
//调用runContainer
return runContainer(dockerCli, ropts, copts, containerConfig)
}
func runContainer(dockerCli *command.DockerCli, opts *runOptions, copts *containerOptions, containerConfig *containerConfig) error {
config := containerConfig.Config
hostConfig := containerConfig.HostConfig
stdout, stderr := dockerCli.Out(), dockerCli.Err()
//实例化一个客户端,用于发送run命令请求
client := dockerCli.Client()
// TODO: pass this as an argument
cmdPath := "run"
warnOnOomKillDisable(*hostConfig, stderr)
warnOnLocalhostDNS(*hostConfig, stderr)
config.ArgsEscaped = false
if !opts.detach {
if err := dockerCli.In().CheckTty(config.AttachStdin, config.Tty); err != nil {
return err
}
} else {
……省略
ctx, cancelFun := context.WithCancel(context.Background())
//client向engine发送create容器请求
createResponse, err := createContainer(ctx, dockerCli, containerConfig, opts.name)
……省略
//start the container
////client向engine发送start容器请求
if err := client.ContainerStart(ctx, createResponse.ID, types.ContainerStartOptions{}); err != nil {
}
func createContainer(ctx context.Context, dockerCli command.Cli, containerConfig *containerConfig, name string) (*container.ContainerCreateCreatedBody, error) {
config := containerConfig.Config
hostConfig := containerConfig.HostConfig
networkingConfig := containerConfig.NetworkingConfig
stderr := dockerCli.Err()
var (
trustedRef reference.Canonical
namedRef reference.Named
)
containerIDFile, err := newCIDFile(hostConfig.ContainerIDFile)
if err != nil {
return nil, err
}
defer containerIDFile.Close()
ref, err := reference.ParseAnyReference(config.Image)
if err != nil {
return nil, err
}
if named, ok := ref.(reference.Named); ok {
namedRef = reference.TagNameOnly(named)
if taggedRef, ok := namedRef.(reference.NamedTagged); ok && command.IsTrusted() {
var err error
trustedRef, err = image.TrustedReference(ctx, dockerCli, taggedRef, nil)
if err != nil {
return nil, err
}
config.Image = reference.FamiliarString(trustedRef)
}
}
//create the container
response, err := dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, name)
//if image not found try to pull it
if err != nil {
if apiclient.IsErrImageNotFound(err) && namedRef != nil {
fmt.Fprintf(stderr, "Unable to find image '%s' locally\n", reference.FamiliarString(namedRef))
// we don't want to write to stdout anything apart from container.ID
if err := pullImage(ctx, dockerCli, config.Image, stderr); err != nil {
return nil, err
}
if taggedRef, ok := namedRef.(reference.NamedTagged); ok && trustedRef != nil {
if err := image.TagTrusted(ctx, dockerCli, trustedRef, taggedRef); err != nil {
return nil, err
}
}
// Retry
var retryErr error
response, retryErr = dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, name)
if retryErr != nil {
return nil, retryErr
}
} else {
return nil, err
}
}
for _, warning := range response.Warnings {
fmt.Fprintf(stderr, "WARNING: %s\n", warning)
}
err = containerIDFile.Write(response.ID)
return &response, err
}
// ContainerAPIClient defines API client methods for the containers
type ContainerAPIClient interface {
//API create方法
ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (container.ContainerCreateCreatedBody, error)
//API start方法
ContainerStart(ctx context.Context, container string, options types.ContainerStartOptions) error
}
//ContainerCreate基于给定配置创建新容器。 它可以与名称相关联,但不是强制性的。
func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, containerName string) (container.ContainerCreateCreatedBody, error) {
var response container.ContainerCreateCreatedBody
if err := cli.NewVersionError("1.25", "stop timeout"); config != nil && config.StopTimeout != nil && err != nil {
return response, err
}
// When using API 1.24 and under, the client is responsible for removing the container
if hostConfig != nil && versions.LessThan(cli.ClientVersion(), "1.25") {
hostConfig.AutoRemove = false
}
query := url.Values{}
if containerName != "" {
query.Set("name", containerName)
}
body := configWrapper{
Config: config,
HostConfig: hostConfig,
NetworkingConfig: networkingConfig,
}
//发送post请求
serverResp, err := cli.post(ctx, "/containers/create", query, body, nil)
if err != nil {
if serverResp.statusCode == 404 && strings.Contains(err.Error(), "No such image") {
return response, imageNotFoundError{config.Image}
}
return response, err
}
err = json.NewDecoder(serverResp.body).Decode(&response)
ensureReaderClosed(serverResp)
return response, err
}
在第5.2.4节上,startContainer()函数就不介绍了,跟createContainer类似,后面有需要,咱再分析。
下一篇章就分析docker run命令在daemon中的处理流程了。
https://www.infoq.cn/article/docker-source-code-analysis-part1
https://guanjunjian.github.io/2017/09/26/study-1-docker-1-client-excuting-flow-for-run/
https://blog.csdn.net/warrior_0319/article/details/79931987
《docker源码分析》
百度网盘:链接: https://pan.baidu.com/s/1m5AqgfZKvPRu2BcmWz-WxA 提取码: wjrj