Docker 源码分析(五):Docker Server 的创建

阅读数:5760 2014 年 12 月 11 日

【编者按】在《深入浅出 Docker》系列文章的基础上,InfoQ 推出了《Docker 源码分析》系列文章。《深入浅出 Docker》系列文章更多的是从使用角度出发,帮助读者了解 Docker 的来龙去脉,而《Docker 源码分析》系列文章通过分析解读 Docker 源码,来让读者了解 Docker 的内部实现,以更好的使用 Docker。总之,我们的目标是促进 Docker 在国内的发展以及传播。另外,欢迎加入 InfoQ Docker 技术交流群,QQ 群号:272489193。

1.Docker Server 简介

Docker 架构中,Docker Server 是 Docker Daemon 的重要组成部分。Docker Server 最主要的功能是:接受用户通过 Docker Client 发送的请求,并按照相应的路由规则实现路由分发。

同时,Docker Server 具备十分优秀的用户友好性,多种通信协议的支持大大降低 Docker 用户使用 Docker 的门槛。除此之外,Docker Server 设计实现了详尽清晰的 API 接口,以供 Docker 用户选择使用。通信安全方面,Docker Server 可以提供安全传输层协议(TLS),保证数据的加密传输。并发处理方面,Docker Daemon 大量使用了 Golang 中的 goroutine,大大提高了服务端的并发处理能力。

本文为《Docker 源码分析》系列的第五篇——Docker Server 的创建。

2. Docker Server 源码分析内容安排

本文将从源码的角度分析 Docker Server 的创建,分析内容的安排主要如下:

(1) “serveapi”这个 job 的创建并执行流程,代表 Docker Server 的创建;

(2) “serveapi”这个 job 的执行流程深入分析;

(3) Docker Server 创建 Listener 并服务 API 的流程分析。

3.Docker Server 创建流程

《Docker 源码分析(三):Docker Daemon 启动》主要分析了 Docker Daemon 的启动,而在 mainDaemon() 运行的最后环节,实现了创建并运行名为”serveapi”的 job。这一环节的作用是:让 Docker Daemon 提供 API 访问服务。实质上,这正是实现了 Docker 架构中 Docker Server 的创建与运行。

从流程的角度来说,Docker Server 的创建并运行,代表了”serveapi”这个 job 的整个生命周期:创建 Job 实例 job,配置 job 环境变量,以及最终执行该 job。本章分三节具体分析这三个不同的阶段。

3.1 创建名为”serveapi”的 job

Job 是 Docker 架构中 Engine 内部最基本的任务执行单位,故创建 Docker Server 这一任务的执行也不例外,需要表示为一个可执行的 Job。换言之,需要创建 Docker Server,则必须创建一个相应的 Job。具体的 Job 创建形式位于./docker/docker/daemon.go,如下:

job := eng.Job("serveapi", flHosts...)

以上代码通过 Engine 实例 eng 创建一个 Job 类型的实例 job,job 名为”serveapi”,同时用 flHost 的值来初始化 job.Args。flHost 的作用是:配置 Docker Server 监听的协议与监听的地址。

需要注意的是,《Docker 源码分析(三):Docker Daemon 启动》mainDaemon() 具体实现过程中,在加载 builtins 环节已经向 eng 对象注册了 key 为”serveapi”的 Handler,而该 Handler 的 value 为 api.ServeApi。因此,在运行名为”serveapi”的 job 时,会执行该 job 的 Handler,即 api.ServeApi。

3.2 配置 job 环境变量

创建完 Job 实例 job 之后,Docker Daemon 为 job 配置环境参数。在 Job 实现过程中,为 Job 配置参数有两种方式:第一,创建 Job 实例时,用指定参数直接初始化 Job 的 Args 属性;第二,创建完 Job 后,给 Job 添加指定的环境变量。以下代码则实现了为创建的 job 配置环境变量:

job.SetenvBool("Logging", true)
job.SetenvBool("EnableCors", *flEnableCors)
job.Setenv("Version", dockerversion.VERSION)
job.Setenv("SocketGroup", *flSocketGroup)

job.SetenvBool("Tls", *flTls)
job.SetenvBool("TlsVerify", *flTlsVerify)
job.Setenv("TlsCa", *flCa)
job.Setenv("TlsCert", *flCert)
job.Setenv("TlsKey", *flKey)
job.SetenvBool("BufferRequests", true)

对于以上配置,环境变量的归纳总结如下表:

环境变量名

flag 参数

默认值

作用值

Logging

 

true

使用日志输出

EnableCors

flEnableCors

false

在远程 API 中提供 CORS 头

Version

   

显示 Docker 版本号

SocketGroup

flSocketGroup

“docker”

在 daemon 模式中 unix domain socket 分配用户组名

Tls

flTls

false

使用 TLS 安全传输协议

TlsVerify

flTlsVerify

false

使用 TLS 并验证远程 Client

TlsCa

flCa

 

指定 CA 文件路径

TlsCert

flCert

 

TLS 证书文件路径

TlsKey

flKey

 

TLS 密钥文件路径

BufferRequest

 

true

缓存 Docker Client 请求

3.3 运行 job

配置完毕 job 的环境变量,随即执行 job 的运行函数,具体实现代码如下:

if err := job.Run(); err != nil {
	log.Fatal(err)
}

在 eng 对象中已经注册过 key 为”serveapi”的 Handler,故在运行 job 的时候,执行这个 Handler 的 value 值,相应 Handler 的 value 为 api.ServeApi。至此,名为”serveapi”的 job 的生命周期已经完备。下文将深入分析 job 的 Handler,api.ServeApi 执行细节的具体实现。

4.ServeApi 运行流程

本章将深入分析 Docker Server 提供 API 服务的部分,从源码的角度剖析 Docker Server 的架构设计与实现。

作为一个监听请求、处理请求的服务端,Docker Server 首先明确自身需要为多少种通信协议提供服务,在 Docker 这个 C/S 模式的架构中,可以使用的协议无外乎三种:TCP 协议,Unix Socket 形式,以及 fd 的形式。随后,Docker Server 根据协议的不同,分别创建不同的服务端实例。最后,在不同的服务端实例中,创建相应的路由模块,监听模块,以及处理请求的 Handler,形成一个完备的 server。

”serveapi”这个 job 在运行时,将执行 api.ServeApi 函数。ServeApi 的功能是:循环检查所有 Docker Daemon 当前支持的通信协议,并对于每一种协议都创建一个 goroutine,在这个 goroutine 内部配置一个服务于 HTTP 请求的 server 端。ServeApi 的代码实现位于./docker/api/server/server.go#L1339

第一,判断 job.Args 的长度是否为 0,由于通过 flHosts 来初始化 job.Args,故 job.Args 的长度若为 0 的话,说明没有 Docker Server 没有监听的协议与地址,参数有误,返回错误信息。代码如下:

if len(job.Args) == 0 {
	return job.Errorf("usage: %s PROTO://ADDR [PROTO://ADDR ...]", job.Name)
}

第二,定义两个变量,protoAddrs 代表 flHosts 的内容;而 chError 定义了和 protoAddrs 长度一致的 error 类型 channel 管道,chError 的作用在下文中会说明。同时还定义了 activationLock,这是一个用来同步”serveapi”和”acceptconnections”这两个 job 执行的 channel。在 serveapi 运行时 ServeFd 和 ListenAndServe 的实现中,由于 activationLock 这个 channel 中没有内容而阻塞,而当运行”acceptionconnections”这个 job 时,会首先通知 init 进程 Docker Daemon 已经启动完毕,并关闭 activationLock,同时也开启了 serveapi 的继续执行。正是由于 activationLock 的存在,保证了”acceptconnections”这个 job 的运行起到通知”serveapi”开启正式服务于 API 的效果。代码如下:

var (
	protoAddrs = job.Args
	chErrors   = make(chan error, len(protoAddrs))
)
activationLock = make(chan struct{})

第三,遍历 protoAddrs,即 job.Args,将其中的每一项都按照字符串“://”进行分割,若分割后 protoAddrParts 的长度不为 2,则说明协议加地址的书写形式有误,返回 job 错误;若不为 2,则分割获得每一项中的协议 protoAddrPart[0] 与地址 protoAddrParts[1]。最后分别创建一个 goroutine 来执行 ListenAndServe 的操作。goroutine 的运行主要依赖于 ListenAndServe(protoAddrParts[0], protoAddrParts[1], job) 的运行结果,若返回 error,则 chErrors 中有 error,当前 goroutine 执行完毕;若没有返回 error,则该 goroutine 持续运行,持续提供服务。其中最为重要的是 ListenAndServe 的实现,该函数具体实现了如何创建 listener、router 以及 server,并协调三者进行工作,最终服务于 API 请求。代码如下:

for _, protoAddr := range protoAddrs {
	protoAddrParts := strings.SplitN(protoAddr, "://", 2)
	if len(protoAddrParts) != 2 {
		return job.Errorf("usage: %s PROTO://ADDR [PROTO://ADDR ...]", job.Name)
	}
	go func() {
		log.Infof("Listening for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1])
		chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], job)
	}()
}

第四,根据 chErrors 的值运行,若 chErrors 这个 channel 中有错误内容,则 ServeApi 该函数返回;若无错误内容,则循环被阻塞。代码如下:

for i := 0; i < len(protoAddrs); i += 1 {
	err := <-chErrors
	if err != nil {
		return job.Error(err)
	}
}

return engine.StatusOK

至此, ServeApi 的运行流程已经详细分析完毕,其中核心部分 ListenAndServe 的实现,下一章开始深入。

5.ListenAndServe 实现

ListenAndServe 的功能是:使 Docker Server 监听某一指定地址,接受该地址上的请求,并对以上请求路由转发至相应的处理函数 Handler 处。从实现的角度来看,ListenAndServe 主要实现了设置一个服务于 HTTP 的 server,该 server 将监听指定地址上的请求,并对请求做特定的协议检查,最终完成请求的路由与分发。代码实现位于./docker/api/server/server.go

ListenAndServe 的实现可以分为以下 4 个部分:

(1) 创建 router 路由实例;

(2) 创建 listener 监听实例;

(3) 创建 http.Server;

(4) 启动 API 服务。

ListenAndServe 的执行流程如下图:

图 5.1 ListenAndServer 执行流程图

下文将按照 ListenAndServe 执行流程图一一深入分析各个部分。

5.1 创建 router 路由实例

首先,ListenAndServe 的实现中通过 createRouter 创建了一个 router 路由实例。代码实现如下:

rr, err := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version"))
if err != nil {
	return err
}

createRouter 的实现位于./docker/api/server/server.go#L1094

创建 router 路由实例是一个重要的环节,路由实例的作用是:负责 Docker Server 对请求进行路由以及分发。实现过程中,主要两个步骤:第一,创建全新的 router 路由实例;第二,为 router 实例添加路由记录。

5.1.1 创建空路由实例

实质上,createRouter 通过包 gorilla/mux 实现了一个功能强大的路由器和分发器。如下:

r := mux.NewRouter()

NewRouter() 函数返回了一个全新的 router 实例 r。在创建 Router 实例时,给 Router 对象的两个属性进行赋值,这两个属性为 nameRoutes 和 KeepContext。其中 namedRoutes 属性为一个 map 类型,其中 key 为 string 类型,value 为 Route 路由记录类型;另外,KeepContext 属性为 false,表示 Docker Server 在处理完请求之后,就清除请求的内容,不对请求做存储操作。代码位于./docker/vendor/src/github.com/gorilla/mux/mux.go#L16,如下:

func NewRouter() *Router {
	return &Router{namedRoutes: make(map[string]*Route), KeepContext: false}
}

可见,以上代码返回的类型为 mux.Router。mux.Router 会通过一系列已经注册过的路由记录,来为接受的请求做匹配,首先通过请求的 URL 或者其他条件,找到相应的路由记录,并调用这条路由记录中的执行 Handler。mux.Router 有以下这些特性:

  • 请求可以基于 URL 的主机名、路径、路径前缀、shemes、请求头和请求值、HTTP 请求方法类型或者使用自定义的匹配规则;
  • URL 主机名和路径可以拥有一个正则表达式来表示;
  • 注册的 URL 可以被直接运用,也可以被保留,这样可以保证维护资源的使用;
  • 路由记录可以被用以子路由器:如果父路由记录匹配,则嵌套记录只会被用来测试。当设计一个组内的路由记录共享相同的匹配条件时,如主机名、路劲前缀或者其他重复的属性,子路由的方式很有帮助;
  • mux.Router 实现了 http.Handler 接口,故和标准的 http.ServeMux 兼容。

5.1.2 添加路由记录

Router 路由实例 r 创建完毕,下一步工作是为 Router 实例 r 添加所需要的路由记录。路由记录存储着用来匹配请求的信息,包括对请求的匹配规则,以及匹配之后的 Handler 执行入口。

回到createRouter 实现代码中,首先判断 Docker Daemon 的启动过程中有没有开启 DEBUG 模式。通过 docker 可执行文件启动 Docker Daemon,解析 flag 参数时,若 flDebug 的值为 false,则说明不需要配置 DEBUG 环境;若 flDebug 的值为 true,则说明需要为 Docker Daemon 添加 DEBUG 功能。具体的代码实现如下:

if os.Getenv("DEBUG") != "" {
	AttachProfiler(r)
}

AttachProiler(r) 的功能是为路由实例 r 添加与 DEBUG 相关的路由记录,具体实现位于./docker/api/server/server.go#L1083,如下:

func AttachProfiler(router *mux.Router) {
	router.HandleFunc("/debug/vars", expvarHandler)
	router.HandleFunc("/debug/pprof/", pprof.Index)
	router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
	router.HandleFunc("/debug/pprof/profile", pprof.Profile)
	router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
	router.HandleFunc("/debug/pprof/heap", pprof.Handler("heap").ServeHTTP)
	router.HandleFunc("/debug/pprof/goroutine", pprof.Handler("goroutine").ServeHTTP)
	router.HandleFunc("/debug/pprof/threadcreate", pprof.Handler("threadcreate").ServeHTTP)
}

分析以上源码,可以发现 Docker Server 使用两个包来完成 DEBUG 相关的工作:expvar 和 pprof。包 expvar 为公有变量提供标准化的接口,使得这些公有变量可以通过 HTTP 的形式在”/debug/vars”这个 URL 下被访问,传输时格式为 JSON。包 pprof 将 Docker Server 运行时的分析数据通过”/debug/pprof/”这个 URL 向外暴露。这些运行时信息包括以下内容:可得的信息列表、正在运行的命令行信息、CPU 信息、程序函数引用信息、ServeHTTP 这个函数三部分信息使用情况(堆使用、goroutine 使用和 thread 使用)。

回到createRouter 函数实现中,完成 DEBUG 功能的所有工作之后,Docker Docker 创建了一个 map 类型的对象 m,用于初始化路由实例 r 的路由记录。简化的 m 对象,代码如下:

m := map[string]map[string]HttpApiFunc{
	"GET": {
		……
		"/images/{name:.*}/get":           getImagesGet,
		……
	},
	"POST": {
		……
		"/containers/{name:.*}/copy":    postContainersCopy,
	},
	"DELETE": {
		"/containers/{name:.*}": deleteContainers,
		"/images/{name:.*}":     deleteImages,
	},
	"OPTIONS": {
		"": optionsHandler,
	},
}

对象 m 的类型为 map,其中 key 为 string 类型,代表 HTTP 的请求类型,如”GET”,”POST”,”DELETE”等,value 为另一个 map 类型,该 map 代表的是 URL 与执行 Handler 的映射。在第二个 map 类型中,key 为 string 类型,代表是的请求 URL,value 为 HttpApiFunc 类型,代表具体的执行 Handler。其中 HttpApiFunc 类型的定义如下:

type HttpApiFunc func(eng *engine.Engine, version version.Version,
 w http.ResponseWriter, r *http.Request, vars map[string]string) error

完成对象 m 的定义,随后 Docker Server 通过该对象 m 来添加路由实例 r 的路由记录。对象 m 的请求方法,请求 URL 和请求处理 Handler 这三样内容可以为对象 r 构建一条路由记录。实现代码。如下:

for method, routes := range m {
	for route, fct := range routes {
		log.Debugf("Registering %s, %s", method, route)
		localRoute := route
		localFct := fct
		localMethod := method

			f := makeHttpHandler(eng, logging, localMethod, 
localRoute, localFct, enableCors, version.Version(dockerVersion))

		if localRoute == "" {
			r.Methods(localMethod).HandlerFunc(f)
		} else {
			r.Path("/v{version:[0-9.]+}" + localRoute).
Methods(localMethod).HandlerFunc(f)
			r.Path(localRoute).Methods(localMethod).HandlerFunc(f)
		}
	}
}

以上代码,在第一层循环中,按 HTTP 请求方法划分,获得请求方法各自的路由记录,第二层循环,按匹配请求的 URL 进行划分,获得与 URL 相对应的执行 Handler。在嵌套循环中,通过 makeHttpHandler 返回一个执行的函数 f。在返回的这个函数中,涉及了 logging 信息,CORS 信息(跨域资源共享协议),以及版本信息。以下举例说明 makeHttpHandler 的实现,从对象 m 可以看到,对于”GET”请求,请求 URL 为”/info”,则请求 Handler 为”getInfo”。执行 makeHttpHandler 的具体代码实现如下:

func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, 
localRoute string, handlerFunc HttpApiFunc, enableCors bool, dockerVersion version.Version) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// log the request
		log.Debugf("Calling %s %s", localMethod, localRoute)

		if logging {
			log.Infof("%s %s", r.Method, r.RequestURI)
		}

		if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
			userAgent := strings.Split(r.Header.Get("User-Agent"), "/")
			if len(userAgent) == 2 && !dockerVersion.Equal(version.Version(userAgent[1])) {
				log.Debugf("Warning: client and server don't have the same version 
(client: %s, server: %s)", userAgent[1], dockerVersion)
			}
		}
		version := version.Version(mux.Vars(r)["version"])
		if version == "" {
			version = api.APIVERSION
		}
		if enableCors {
			writeCorsHeaders(w, r)
		}

		if version.GreaterThan(api.APIVERSION) {
			http.Error(w, fmt.Errorf("client and server don't have same version 
(client : %s, server: %s)", version, api.APIVERSION).Error(), http.StatusNotFound)
			return
		}

		if err := handlerFunc(eng, version, w, r, mux.Vars(r)); err != nil {
			log.Errorf("Handler for %s %s returned error: %s", localMethod, localRoute, err)
			httpError(w, err)
		}
	}
}

可见 makeHttpHandler 的执行直接返回一个函数 func(w http.ResponseWriter, r *http.Request) 。在这个 func 函数的实现中,判断 makeHttpHandler 传入的 logging 参数,若为 true,则将该 Handler 的执行通过日志显示,另外通过 makeHttpHandler 传入的 enableCors 参数判断是否在 HTTP 请求的头文件中添加跨域资源共享信息,若为 true,则通过 writeCorsHeaders 函数向 response 中添加有关 CORS 的 HTTP Header,代码实现位于./docker/api/server/server.go#L1022,如下:

func writeCorsHeaders(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
	w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
}

最为重要的执行部分位于 handlerFunc(eng, version, w, r, mux.Vars(r)),如以下代码:

if err := handlerFunc(eng, version, w, r, mux.Vars(r)); err != nil {
		log.Errorf("Handler for %s %s returned error: %s", localMethod, localRoute, err)
		httpError(w, err)
}

对于”GET”请求类型,”/info”请求 URL 的请求,由于 Handler 名为 getInfo,也就是说 handlerFunc 这个形参的值为 getInfo,故执行部分直接运行 getInfo(eng, version, w, r, mux.Vars(r)),而 getInfo 的具体实现位于./docker/api/server/serve.go#L269,如下:

func getInfo(eng *engine.Engine, version version.Version, w http.ResponseWriter, 
r *http.Request, vars map[string]string) error {
		w.Header().Set("Content-Type", "application/json")
		eng.ServeHTTP(w, r)
		return nil
}

以上 makeHttpHandler 的执行已经完毕,返回 func 函数,作为指定 URL 对应的执行 Handler。

创建完处理函数 Handler,需要向路由实例中添加新的路由记录。如果 URL 信息为空,则直接为该 HTTP 请求方法类型添加路由记录;若 URL 不为空,则为请求 URL 路径添加新的路由记录。需要额外注意的是,在 URL 不为空,为路由实例 r 添加路由记录时,考虑了 API 版本的问题,通过 r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f) 来实现。

至此,mux.Router 实例 r 的两部分工作工作已经全部完成:创建空的路由实例 r,为 r 添加相应的路由记录,最后返回路由实例 r。

现 I 时 er 路由记录。需要额外的利次循环中,都有不同的组合 1083lla/mux/mux.go,

5.2 创建 listener 监听实例

路由模块,完成了请求的路由与分发这一重要部分,属于ListenAndServe 实现中的第一个重要工作。对于请求的监听功能,同样需要模块来完成。而在ListenAndServe 实现中,第二个重要的工作就是创建 Listener。Listener 是一种面向流协议的通用网络监听模块。

在创建 Listener 之前,先判断 Docker Server 允许的协议,若协议为 fd 形式,则直接通过 ServeFd 来服务请求;若协议不为 fd 形式,则继续往下执行。

在程序执行过程中,需要判断”serveapi”这个 job 的环境中”BufferRequests”的值,是否为真,若为真,则通过包 listenbuffer 创建一个 Listener 的实例 l,否则的话直接通过包 net 创建 Listener 实例 l。具体的代码位于./docker/api/server/server.go#L1269,如下:

if job.GetenvBool("BufferRequests") {
	l, err = listenbuffer.NewListenBuffer(proto, addr, activationLock)
} else {
	l, err = net.Listen(proto, addr)
}

由于在 mainDaemon() 中创建”serveapi”这个 job 之后,给 job 添加环境变量时,已经给”BufferRequets”赋值为 true,故使用包 listenbuffer 创建 listener 实例。

Listenbuffer 的作用是:让 Docker Server 可以立即监听指定协议地址上的请求,但是将这些请求暂时先缓存下来,等 Docker Daemon 全部启动完毕之后,才让 Docker Server 开始接受这些请求。这样设计有一个很大的好处,那就是可以保证在 Docker Daemon 还没有完全启动完毕之前,接收并缓存尽可能多的用户请求。

若协议的类型为 TCP,另外 job 中环境变量 Tls 或者 TlsVerify 有一个为真,则说明 Docker Server 需要支持 HTTPS 服务,需要为 Docker Server 配置安全传输层协议(TLS)的支持。为实现 TLS 协议,首先需要建立一个 tls.Config 类型实例 tlsConfig,然后在 tlsConfig 中加载证书,认证信息等,最终通过包 tls 中的 NewListener 函数,创建出适应于接收 HTTPS 协议请求的 Listener 实例 l,代码如下:

l = tls.NewListener(l, tlsConfig)

至此,创建网络监听的 Listener 部分已经全部完成。

5.3 创建 http.Server

Docker Server 同样需要创建一个 Server 对象来运行 HTTP 服务端。在ListenAndServe 实现中第三个重要的工作就是创建 http.Server:

httpSrv := http.Server{Addr: addr, Handler: r}

其中 addr 为需要监听的地址,r 为 mux.Router 路由实例。

5.4 启动 API 服务

创建 http.Server 实例之后,Docker Server 立即启动 API 服务,使 Docker Server 开始在 Listener 监听实例 l 上接受请求,并对于每一个请求都生成一个新的 goroutine 来做专属服务。对于每一个请求,goroutine 会读取请求,查询路由表中的路由记录项,找到匹配的路由记录,最终调用路由记录中的执行 Handler,执行完毕后,goroutine 对请求返回响应信息。代码如下:

return httpSrv.Serve(l)

至此,ListenAndServer 的所有流程已经分析完毕,Docker Server 已经开始针对不同的协议,服务 API 请求。

6. 总结

Docker Server 作为 Docker Daemon 架构中请求的入口,接管了所有 Docker Daemon 对外的通信。通信 API 的规范性,通信过程的安全性,服务请求的并发能力,往往都是 Docker 用户最为关心的内容。本文基于源码,分析了 Docker Server 大部分的细节实现。希望 Docker 用户可以初探 Docker Server 的设计理念,并且可以更好的利用 Docker Server 创造更大的价值。

7. 作者简介

孙宏亮,DaoCloud初创团队成员,软件工程师,浙江大学 VLIS 实验室应届研究生。读研期间活跃在 PaaS 和 Docker 开源社区,对 Cloud Foundry 有深入研究和丰富实践,擅长底层平台代码分析,对分布式平台的架构有一定经验,撰写了大量有深度的技术博客。2014 年末以合伙人身份加入 DaoCloud 团队,致力于传播以 Docker 为主的容器的技术,推动互联网应用的容器化步伐。邮箱:allen.sun@daocloud.io

8. 参考文献

http://guzalexander.com/2013/12/06/golang-channels-tutorial.html

http://www.gorillatoolkit.org/pkg/mux

http://docs.studygolang.com/pkg/expvar/

http://docs.studygolang.com/pkg/net/http/pprof/


感谢郭蕾对本文的策划和审校。

给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ)或者腾讯微博(@InfoQ)关注我们,并与我们的编辑和其他读者朋友交流。

收藏

评论

微博

发表评论

注册/登录 InfoQ 发表评论