Docker Remote API如何在开发中使用
Docker Remote API如何在开发中使用
clearraining 发表于3年前
Docker Remote API如何在开发中使用
  • 发表于 3年前
  • 阅读 103
  • 收藏 2
  • 点赞 0
  • 评论 1

腾讯云 技术升级10大核心产品年终让利>>>   

摘要: 通过DockerClient了解怎么建立连接,并且了解Docker Remote API在开发过程中的使用,开发docker的管理应用。

初学docker,了解docker remote API的时候根本不知道怎么使用在开发中,通过DockerClient这个项目能很好的认识到API在开发过程中的使用,如何建立连接等。也希望像我一样的初学者能通过这个项目了解docker API的使用。

一.   基本配置

访问docker远程API有两种方法,使用Unix套接字和HTTP端口。

Unix套接字:docker监听unix:///var/run/docker.sock这个unix套接字。

HTTP远程访问:需要修改配置使能支持远程访问,在ubuntu环境下找到/etc/init/docker.conf文件,找到 DOCKER_OPTS并修改,接着重启docker后台进程。

DOCKER_OPTS="-H tcp://0.0.0.0:4243 -H unix:///var/run/docker.sock"
$sudo service docker restart
验证
$netstat -ant  |grep 4243
tcp6       0      0 :::4243                 :::*                    LISTEN

然后就可以访问了。

举例如下:

$curl http://127.0.0.1:4243/info
{"Containers":4,"Debug":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Dirs","63"]],"ExecutionDriver":"native-0.2","IPv4Forwarding":1,"Images":55,"IndexServerAddress":"https://index.docker.io/v1/","InitPath":"/usr/bin/docker","InitSha1":"","KernelVersion":"3.13.0-32-generic","MemoryLimit":1,"NEventsListener":0,"NFd":16,"NGoroutines":15,"OperatingSystem":"Ubuntu precise (12.04.5 LTS)","SwapLimit":0}

$curl GET http://127.0.0.1:4243/containers/json
[{"Command":"crate -Des.cluster.name=cratecluster -Des.node.name=crate1 -Des.transport.publish_port=4300 -Des.network.publish_host=115.159.76.38 -Des.multicast.enabled=false -Des.discovery.zen.ping.unicast.hosts=115.159.76.38:4300,115.159.26.242:4300 -Des.discovery.zen.minimum_master_nodes=2","Created":1418829276,"Id":"dfd58a62a7c941218088f6e43d8dc17ecfb8cd0783e6c4e338c321d6d39c4bbd","Image":"docker.cn/docker/crate:0.45","Names":["/node1"],"Ports":[{"IP":"0.0.0.0","PrivatePort":4200,"PublicPort":4200,"Type":"tcp"},{"IP":"0.0.0.0","PrivatePort":4300,"PublicPort":4300,"Type":"tcp"}],"Status":"Up 2 weeks"}]


如果想要了解完整的docker API可以访问官网:https://docs.docker.com/reference/api/docker_remote_api/ 。

二.  DockerClient  

下面开始进入dockerClient这个项目,dockerclient.go中实现了对docker remote API的封装。

dockerClient项目:https://github.com/samalba/dockerclient

1.创建DockerClient

a. NewDockerClient()

func NewDockerClient(daemonUrl string, tlsConfig *tls.Config) (*DockerClient, error) {
	return NewDockerClientTimeout(daemonUrl, tlsConfig, time.Duration(defaultTimeout))
}

使用NewDockerClient方法新建一个dockerclient,需要传入daemonUrl和tlsConfig两个参数,创建成功会返回一个DockerClient。其中两个参数具体含义如下:

daemonUrl: 需要传入一个后台docker进程的Url,比如unix:///var/run/docker.sock。

tlsConfig: tlsConfig是cropto/tls包中的Config结构类型,Config结构类型用于配置TLS客户端或服务端,具体参阅cropto/tls包。

TLS:Transport Layer Security(安全传输层协议)

TLS用于在两个通信应用程序之间提供保密性和数据完整性。

使用举例:

docker, _ := dockerclient.NewDockerClient("unix:///var/run/docker.sock", nil)

在NewDockerClient方法中调用NewDockerClientTimeout方法实现具体的DockerClient的创建。


b.NewDockerClientTimeout()

func NewDockerClientTimeout(daemonUrl string, tlsConfig *tls.Config, timeout time.Duration) (*DockerClient, error) {
	u, err := url.Parse(daemonUrl)
	if err != nil {
		return nil, err
	}
	if u.Scheme == "tcp" {
		if tlsConfig == nil {
			u.Scheme = "http"
		} else {
			u.Scheme = "https"
		}
	}
	httpClient := newHTTPClient(u, tlsConfig, timeout)
	return &DockerClient{u, httpClient, tlsConfig, 0}, nil
}

需要向NewDockerClientTimeout方法传入三个参数,分别是daemonUrl、tlsConfig和timeout。在此方法中会先解析daemonUrl,net/url包中的Parse方法解析daemonUrl,返回一个URL结构类型。

URL结构类型:

type URL struct {    
    Scheme   string
    Opaque   string    // 编码后的不透明数据
    User     *Userinfo // 用户名和密码信息
    Host     string    // host或host:port
    Path     string
    RawQuery string    // 编码后的查询字符串,没有'?'
    Fragment string    // 引用的片段(文档位置),没有'#'}

URL基本格式如下:

scheme://[userinfo@]host/path[?query][#fragment]

timeout:timeout是time包中的Duration类型,代表两个时间点之间经过的时间,以纳秒为单位。timeout默认为30s(defaultTimeout)

解析完daemonUrl后,对返回的URL的scheme进行判断,tcp协议并且有安全传输协议的设置scheme为https,否则设为http。当然scheme也有可能是unix。接着需要新建一个HTTPClient,通过向newHTTPClient方法传入三个参数,包括解析后的URL、tlsConfig和timeout。


c. newHTTPClient()

func newHTTPClient(u *url.URL, tlsConfig *tls.Config, timeout time.Duration) *http.Client {
	httpTransport := &http.Transport{
		TLSClientConfig: tlsConfig,
	}
	switch u.Scheme {
	default:
		httpTransport.Dial = func(proto, addr string) (net.Conn, error) {
			return net.DialTimeout(proto, addr, timeout)
		}
	case "unix":
		socketPath := u.Path
		unixDial := func(proto, addr string) (net.Conn, error) {
			return net.DialTimeout("unix", socketPath, timeout)
		}
		httpTransport.Dial = unixDial
		// Override the main URL object so the HTTP lib won't complain
		u.Scheme = "http"
		u.Host = "unix.sock"
		u.Path = ""
	}
	return &http.Client{Transport: httpTransport}
}

newHTTPClient主要是新建一个HTTPClient(就是建立一个网络连接),在方法中先新建一个Transport结构类型的httpTransport实例。

Transport结构类型:

type Transport struct {    
    Proxy func(*Request) (*url.URL, error)              // Proxy指定一个对给定请求返回代理的函数。  
    Dial func(network, addr string) (net.Conn, error)   // Dial指定创建TCP连接的拨号函数。如果Dial为nil,                                                         // 会使用net.Dial。
    TLSClientConfig *tls.Config                         // TLSClientConfig指定用于tls.Client的TLS配置信息                                                        // 如果该字段为nil,会使用默认的配置信息。  
    TLSHandshakeTimeout time.Duration                    
    DisableKeepAlives bool                              
    DisableCompression bool                             
    MaxIdleConnsPerHost int                             
    ResponseHeaderTimeout time.Duration                 
}

接着对URL的scheme进行判断,如果是unix(也就是访问由docker监听的unix套接字),那么进入case "unix",否则进入默认的default。(能传输到这里的一般是unix、http和https)

Example: unix:///var/run/docker.sock

case "unix": 先新建一个socketPath为URL中Path的内容(如上例子中u.path=/var/run/docker.sock), 接着新建网络连接的拨号方法unixDial,即要赋值给httpTransport实例中的Dial方法。在unixDial方法中调用net包中的DialTimeout方法实现采用超时的网络连接。

func DialTimeout(network, address string, timeout time.Duration) (Conn, error)
即
net.DialTimeout("unix", "/var/run/docker.sock", timeout)
举例
conn, err := net.DialTimeout("tcp", "google.com:80", timeout)

最后返回一个Client结构类型实例表示HTTP客户端。

Client结构类型:

type Client struct {    
    Transport RoundTripper     // Transport指定执行独立、单次HTTP请求的机制。
    CheckRedirect func(req *Request, via []*Request) error
    Jar CookieJar             // Jar指定cookie管理器。
    Timeout time.Duration}

最后返回NewDockerClientTimeout方法中,httpClient建立完成,接着返回一个建立完成的DockerClient结构类型实例。

DockerClient结构类型:

type DockerClient struct {
	URL           *url.URL
	HTTPClient    *http.Client
	TLSConfig     *tls.Config
	monitorEvents int32
}

至此DockerClient创建完成。

2. 发送请求处理

a. doRequest()

func (client *DockerClient) doRequest(method string, path string, body []byte, headers map[string]string) ([]byte, error) {
	b := bytes.NewBuffer(body)
	req, err := http.NewRequest(method, client.URL.String()+path, b)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Content-Type", "application/json")
	if headers != nil {
		for header, value := range headers {
			req.Header.Add(header, value)
		}
	}
	resp, err := client.HTTPClient.Do(req)
	if err != nil {
		if !strings.Contains(err.Error(), "connection refused") && client.TLSConfig == nil {
			return nil, fmt.Errorf("%v. Are you trying to connect to a TLS-enabled daemon without TLS?", err)
		}
		return nil, err
	}
	defer resp.Body.Close()
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode == 404 {
		return nil, ErrNotFound
	}
	if resp.StatusCode >= 400 {
		return nil, Error{StatusCode: resp.StatusCode, Status: resp.Status, msg: string(data)}
	}
	return data, nil
}

doRequest方法完成发送请求的工作,比如你想获得docker的信息,那么可以通过doRequest方法发送“GET”请求。下面介绍doRequest方法的具体实现。

b := bytes.NewBuffer(body)
方法原型:
func NewBuffer(buf []byte) *Buffer

NewBuffer使用buf(即body)作为初始内容创建并初始化一个Buffer,本方法用于创建一个用于读取已存在数据的buffer(即b)。

req, err := http.NewRequest(method, client.URL.String()+path, b)
方法原型:
func NewRequest(method, urlStr string, body io.Reader) (*Request, error)

NewRequest使用指定的方法、网址和可选的主题创建并返回一个新的*Request(即req)。Request类型代表一个服务端接收到的或者客户端发送出去的HTTP请求。比如上面所说的一个例子:

curl GET http://127.0.0.1:4243/containers/json

那么method=GET, urlStr=http://127.0.0.1:4243/containers/json , body=nil。

Request结构类型:

type Request struct {
    Method   string    //Method指定HTTP方法(GET,POST,PUT等)。对客户端,""代表GET。
    URL   *url.URL     //URL在服务器端表示被请求的URL,在客户端表示要访问的URL。
    Proto   string
    ProtoMajor   int
    ProtoMinor   int
    Header   Header      //Header字段表示HTTP请求的头域。
    Body   io.ReadCloser   //Body是请求的主体。
    ContentLength   int64
    TransferEncoding   []string
    Close   bool
    Host   string
    Form   url.Values
    PostForm   url.Values
    MultipartForm   *multipart.Form
    Trailer   Header
    RemoteAddr   string
    RequestURL   string
    TLS   *tls.ConnectionState
}

具体的各个字段详细解释可以参考帮助文档或者访问下面一个网址:

帮助文档翻译版:http://git.oschina.net/liudiwu/pkgdoc(或者:http://mygodoc.oschina.mopaas.com

Go包方法使用:https://github.com/astaxie/gopkg 。

req.Header.Add("Content-Type", "application/json")
if headers != nil {
	for header, value := range headers {
		req.Header.Add(header, value)
	}
}
方法原型
type Header map[string][]string
func (h Header) Add(key, value string)

Add方法添加键值对到req的Header字段,header字段即HTTP请求的头域。

resp, err := client.HTTPClient.Do(req)
if err != nil {
	if !strings.Contains(err.Error(), "connection refused") && client.TLSConfig == nil {
		return nil, fmt.Errorf("%v. Are you trying to connect to a TLS-enabled daemon without TLS?", err)
	}
	return nil, err
}
方法原型
func (c *Client) Do(req *Request) (resp *Response, err error)

Do方法发送请求,返回HTTP回复。

defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
	return nil, err
}
if resp.StatusCode == 404 {
	return nil, ErrNotFound
}
if resp.StatusCode >= 400 {
	return nil, Error{StatusCode: resp.StatusCode, Status: resp.Status, msg: string(data)}
}
return data, nil
方法原型
func ReaderAll(r io.Reader) ([]byte, error)

ReaderAll方法从请求返回的resp的Body中读取数据知道EOF活着遇到error,返回读取的数据和遇到的错误。并对返回的HTTP状态码进行判断,最后返回数据并关闭resp.Body。

Response结构类型:

type Response sctuct {
    Status  string
    StatusCode  int   
    Proto  string
    ProtoMajor  int
    ProtoMinor  int
    Header  Header
    Body  io.ReadCloser
    ContentLength  int64
    TransferEncoding  []string
    Close  bool
    Trailer  Header
    Request  *Request
    TLS  *tls.ConnectionState
}

总结:上面是DockerClient中主要的建立连接的方式,对于我这样的初学者来说这个项目起到基础的帮助作用,对于开发docker管理项目起到帮助。

参考资料:

1.https://docs.docker.com/reference/api/docker_remote_api/

2.https://github.com/samalba/dockerclient

3.http://git.oschina.net/liudiwu/pkgdoc

4.http://mygodoc.oschina.mopaas.com

5.https://github.com/astaxie/gopkg



共有 人打赏支持
粉丝 0
博文 1
码字总数 2196
评论 (1)
×
clearraining
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: