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であり、この時クライアントはこのソケットを通じてローカルの 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 のインストール

公式サイト - SDK のインストールの手順に従って、依存関係をダウンロードします:

$ 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("Dockerクライアントの作成に失敗しました")
        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 キーの認証操作を行います。

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("Dockerクライアントの作成に失敗しました")
        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 クライアントが使用するコマンドをラップしており、Docker Engine API 用の Go クライアントでは各コマンドの使用方法が詳しく説明されています。これにより、プログラムと Docker Daemon との連携が十分に満たされます。

参考#

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。