banner
biuaxia

biuaxia

"万物皆有裂痕,那是光进来的地方。"
github
bilibili
tg_channel

【轉載】如何使用 Golang 優雅地操作 Docker

title: '【轉載】如何使用 Golang 優雅地操作 Docker'
date: 2021-08-09 10:33:00
comment: false
toc: true
category:

  • Golang
    tags:
  • 轉載
  • Go
  • golang
  • Docker

本文轉載自:如何使用 Golang 優雅地操作 Docker - 知乎


Docker 是目前最流行的 Linux 容器解決方案,它解決了軟體開發中環境配置的問題,因此在日常開發中得到了廣泛的應用。 通常情況下,我們都會按照官網上的步驟下載 Docker 並安裝,然後在終端執行 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 是典型的 C-S 架構,執行 docker run 的終端相當於是客戶端,而 Docker 的容器實際由 Docker Daemon 控制。這裡的 Docker Daemon 運行在在 DOCKER_HOST 這個環境變數指定的機器上。 當我們執行 docker run hello-world 時, DOCKER_HOST 的預設值是 unix:///var/run/docker.sock ,此時客戶端就會通過這個 socket 找到本地的 Docker Daemon,並向他發送 run hello-world 這個指令,然後由 Docker Daemon 去拉取鏡像和啟動容器。參考下圖:

image

從圖中可知,Docker 客戶端和 Docker Daemon 並不一定得在同一台機器上,通過在運行時指定 DOCKER_HOST 環境變數,我們還能將指令發送給其他機器上的 Docker Daemon,例如要讓 192.168.64.1 上的 Docker Daemon 運行 run 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)

連接需要身份驗證的伺服器#

上面連接遠程 Docker Daemon 的方法的前提條件是目標機器開放了 2375 端口。然而在大多數情況下,出於安全考量,伺服器對端口開放有嚴格的限制,開發者通常需要使用 ssh 登錄到伺服器後才能操作伺服器上的 Docker。對於這種情況,我們可以利用 ```github.com/docker/cli/cli/connhelper` 這個庫幫我們完成 ssh 登錄驗證的工作。

創建連接客戶端#

這一步與使用 Go 發送 HTTP 請求時需要創建的客戶端很像,但是需要把 Transport 的 DialContext 換成 connhelper 提供的 Dialer

helper, _ := connhelper.GetConnectionHelper("ssh://[email protected]: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://[email protected]: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 的需求

參考#

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。