docker run hello-world
klew@primary:~$ docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

执行 docker run 时发生了什么

docker runDOCKER_HOSTdocker run hello-worldDOCKER_HOSTunix:///var/run/docker.sockrun hello-world
DOCKER_HOSTrun hello-world
$ DOCKER_HOST=tcp://192.168.64.1:2375 docker run hello-world

需要注意的是,这条命令成功执行的条件是 192.168.64.1 开放了 2375 这个端口。 在终端执行 Docker 命令基本已经够用了,但想要集成到应用程序中还是很不方便,例如我想要用 Golang 做一个 Docker 容器监控的项目,那么如何在 Go 程序中获取容器信息呢? Docker SDK 恰好提供了这样的功能

Docker SDK

Docker SDK 提供了与 Docker daemon 交互的接口,官方支持 Python 和 Go,其他热门语言也有对应的第三方库

环境准备

  1. 配置 Docker 环境

使用 Docker Daemon 要求版本在 18.09 以上,本地的 Docker 客户端也要求在 19.03 以上,不然之后尝试连接时会报错:

[] error during connect: Get "http://docker/v1.24/images/json": command [] has exited with exit status 255, please make sure the URL is valid, and Docker 18.09 or
 later is installed on the remote host: stderr=ssh: connect to host: Connection refused
  1. 安装 Go SDK

按照 官网-Install the SDKs 的步骤,下载依赖即可:

$ go get github.com/docker/docker/client

连接本地 Docker Daemon

初始化客户端对象

这里我们直接连接本地的 Docker Daemon,不需要过多配置,直接用环境变量的参数初始化客户端即可。

// NewEnvClient 直接使用环境变量中的 DOCKER_HOST, DOCKER_TLS_VERIFY, DOCKER_CERT_PATH, DOCKER_API_VERSION 配置
cl, err := client.NewEnvClient()

执行命令

Docker SDK 对拉取镜像、运行容器、查看状态等命令都进行了封装,具体可以参考文档,例如想要查看镜像列表则只需要执行如下命令:

cl.ImageList(context.Background(), types.ImageListOptions{})

完整代码

// main.go
package main

import (
    "context"
    "fmt"

    "github.com/docker/docker/api/types"
    "github.com/docker/docker/client"
)

func main() {
    cl, err := client.NewEnvClient()
    if err != nil {
        fmt.Println("Unable to create docker client")
        panic(err)
    }

    fmt.Println(cl.ImageList(context.Background(), types.ImageListOptions{}))

}

运行结果

$ go run main.go
[{-1 1614986725 sha256:d1165f2212346b2bab48cb01c1e39ee8ad1be46b87873d9ca7a4e434980a7726 map[]  [hello-world@sha256:308866a43596e83578c7dfa15e27a73011bdd402185a84c5cd7f32a88b501a24] [hello-world:latest] -1 13336 13336}] <ni
l>

可以看到这里显示了刚刚的 hello-world 镜像

连接远程 Docker Daemon

连接远程的 Docker Daemon 和本地的类似,只不过需要在初始化客户端对象时指定连接远程的方式。 例如如果要使用 TCP 连接 192.168.64.1:2375 的 Docker Daemon,只需要将初始化客户端对象换成如下语句即可:

cl, err := client.NewClient("tcp://192.168.64.1:2375", "", nil, nil)

连接需要身份验证的服务器

`

创建连接客户端

这一步与使用 Go 发送 HTTP 请求时需要创建的客户端很像,但是需要把 Transport 的 DialContext 换成 connhelper 提供的 Dialer

helper, _ := connhelper.GetConnectionHelper("ssh://klew@192.168.64.2:22")
httpClient := &http.Client{
    Transport: &http.Transport{
        DialContext: helper.Dialer,
    },
}

初始化 Docker 客户端

这里我们不再使用环境变量默认的配置,而是通过配置参数的方式初始化 Docker 客户端,同时指定连接的 HTTP 客户端和上下文等信息。这样当我们尝试连接远程服务器的 Docker Daemon 时,connhelper 就会自动帮我们完成 ssh key 的验证操作

cl, err := client.NewClientWithOpts(
    client.WithHTTPClient(httpClient),
    client.WithHost(helper.Host),
    client.WithDialContext(helper.Dialer),
)

执行命令

依然使用查看镜像列表的方式来验证代码

fmt.Println(cl.ImageList(context.Background(), types.ImageListOptions{}))

完整代码

package main

import (
    "context"
    "fmt"
    "net/http"

    "github.com/docker/cli/cli/connhelper"
    "github.com/docker/docker/api/types"
    "github.com/docker/docker/client"
)

func main() {
    helper, err := connhelper.GetConnectionHelper("ssh://klew@192.168.64.2:22")
    if err != nil {
        return
    }
    httpClient := &http.Client{
        Transport: &http.Transport{
            DialContext: helper.Dialer,
        },
    }
    cl, err := client.NewClientWithOpts(
        client.WithHTTPClient(httpClient),
        client.WithHost(helper.Host),
        client.WithDialContext(helper.Dialer),
    )
    if err != nil {
        fmt.Println("Unable to create docker client")
        panic(err)
    }

    fmt.Println(cl.ImageList(context.Background(), types.ImageListOptions{}))
}

运行结果

$ go run main.go
[{-1 1614986725 sha256:d1165f2212346b2bab48cb01c1e39ee8ad1be46b87873d9ca7a4e434980a7726 map[]  [hello-world@sha256:308866a43596e83578c7dfa15e27a73011bdd402185a84c5cd7f32a88b501a24] [hello-world:latest] -1 13336 13336}] <ni
l>

总结

Docker SDK 封装了 Docker 客户端会用到的指令,Go client for the Docker Engine API 详细介绍了各个指令的使用方法,充分满足我们使用程序与 Docker Daemon 的需求

参考