Docker架构的学习

190 阅读4分钟
  1. Docker的使用 (1)常用命令 以下是通过docker help打印出来的docker命令。

其中常用的命令有: docker pull version. 拉取镜像 docker commit id. 提交镜像 docker run 运行容器 docker images 列出镜像 2. Docker架构 docker-1-2

(1)client

docker基于C/S架构,client是docker架构中用户与docker daemon建立通信的客户端。主要通过tcp://host:port,unix://path_to_socket和fd://socketfd三种协议与daemon进行通信,client发送容器管理请求之后,请求由daemon接受并处理,client在收到daemon的处理结果之后,client一次完整的生命周期即结束。当需要继续发送容器管理请求,用户必须再次通过可执行文件docker创建client并重复以上流程。

(2)daemon

daemon是常驻后台的系统进程,所有的容器即是由该进程进行管理。运行docker指的就是运行daemon进程。daemon由server,engine和job组成。

daemon在运行时,后在后台启动一个server,负责接收client传输的命令并通过路由进行分发调度,找到相应的handler来处理请求。route的定义如下图所示。在Route中可以看到Handler以及Path,Server即是通过这两个参数进行路由的分发。

Engine是Docker架构中的运行引擎,存储着大量的容器信息。举例:当执行一个容器的创建时候,首先调用的是handlers对象,而handlers对象中有:{"Create",deamon.ContainerCreate,}说明当执行create的job时执行的是上述handler。也就是说create这个行为可以抽象成一个job,handler中存储的是job的细节。

job是Engine中最基本的工作执行单元。daemon可以完成的每一个任务都会体现为一个job,在Docker容器内运行一个进程,是一个job;创建一个新的容器也是一个job;在网络上下载一个文档,这也是一个job。job接口中包含运行时参数,有环境变量,标准输入与标准输出,错误以及返回状态。

(3)docker registry

用于存储容器镜像。在搜索镜像、下载以及上传镜像时需要与docker进行通信。

(4)Graph

保管容器镜像。不管是docker下载的镜像还是docker构建的镜像,均有graph统一管理。在graph中将不用的镜像进行分类并称之为repository,如名称为ubuntu的镜像属于同一个repository;在同一个repository下,镜像是通过tag进行区分。

(5)drive是docker架构中的驱动模块,通过driver驱动。docker可以实现对docker容器运行环境的定制,主要包括网络,存储,存储以及容器的执行方式。

drive的存在是为了区分不同的docker操作。因为不是所有的操作都是对容器的管理,同时包含用户对容器信息的获取,憝graph的操作等。因此drive的实现可以分为以下三类驱动:graphdriver、networkdriver以及execdriver。

(6)container

container是docker架构中服务交付的最终体现形式。用户对docker容器的配置有以下四个方面

通过制定容器镜像,使得docker容器可以自定义rootfs等文件系统。 通过制定物理资源的配额,如cpu、内存等使得docker容器使用受限的物理资源 通过配置容器的网络以及其安全策略、使得docker容器拥有独立且安全的网络环境 通过指定容器的运行命令,使容器执行指定的任务 (7)docker运行案例分析

以docker pull为例看一下docker架构中各个模块的运行。docker pull的命令是从docker register中下载指定的容器镜像、并将镜像存储在 本地的graph中,以备后续创建docker容器时使用。

起执行流程如下:

docker-1-10-1

(3)Docker Client的代码阅读 如何读代码?我理解的读别人的代码应该分成以下几个方面(排名不分先后,可根据需要进行优先级排序):

软件设计(结构设计) 代码风格 代码技巧 技术(骚操作,巧妙操作) 功能

// NewClientWithOpts initializes a new API client with default values. It takes functors// to modify values when creating it, like NewClientWithOpts(WithVersion(…))// It also initializes the custom http headers to add to each request.//// It won't send any version information if the version number is empty. It is// highly recommended that you set a version or your client may break if the// server is upgraded.func NewClientWithOpts(ops ...Opt) (*Client, error) { client, err := defaultHTTPClient(DefaultDockerHost) if err != nil { return nil, err } c := &Client{ host: DefaultDockerHost, version: api.DefaultVersion, client: client, proto: defaultProto, addr: defaultAddr, } for _, op := range ops { if err := op(c); err != nil { return nil, err } } if _, ok := c.client.Transport.(http.RoundTripper); !ok { return nil, fmt.Errorf("unable to verify TLS configuration, invalid transport %v", c.client.Transport) } if c.scheme == "" { c.scheme = "http" tlsConfig := resolveTLSConfig(c.client.Transport) if tlsConfig != nil { // TODO(stevvooe): This isn't really the right way to write clients in Go. // NewClient should probably only take an *http.Client and work from there. // Unfortunately, the model of having a host-ish/url-thingy as the connection // string has us confusing protocol and transport layers. We continue doing // this to avoid breaking existing clients but this should be addressed. c.scheme = "https" } } return c, nil}