2017/09/01

golangからDocker Remote APIを使ってイメージ・コンテナを操作する方法まとめ

photo by Daniel Ramirez

プログラムからDockerのイメージやコンテナの操作をして、必要に応じてコンテナを起動したり停止したりというサービスをつくろうとしていた。
いろいろ調べたので、Go言語(以下、Golangと呼ぶ)からDockerのイメージやコンテナの操作をする方法をまとめる。

Dockerはコンテナ仮想化ツールで、Golangで開発されている。
そのため、Docker Engineのクライアント(moby/client)をそのままGolangで使える。
※ Dockerの詳しい説明は省略する

今回はMobyを使ってイメージの作成・削除、コンテナの起動・停止を行う。


環境は以下のとおり。
  • Mac OSX
  • go 1.8.3
  • Docker for Mac 17.6.1-ce


Dockerをインストールする


Macで開発していたので、Docker for Macを使った。
リンク先からダウンロードして、インストールする。

WindowsならDocker for Windowsを使うと楽だ。

Linux(CentOSなど)で使うなら、以下の記事にDockerインストール用のシェルスクリプトが書いてあるので、参考にしてほしい。

Mobyのインストール


go getでインストールする場合は、以下のコマンドを実行する
$ go get -u github.com/moby/moby

depを使っている場合は、github.com/moby/moby/clientをimportしてからdep ensureを実行する。するとvendorディレクトリにインストールされる。

私の場合はdepを使っているので後者のコマンドを実行する。


Dockerイメージの取得からコンテナ起動まで


開発準備は整ったので、実際にDockerイメージの取得からコンテナ起動までを行う。

クライアントの生成

import (
  "io"
  "os"

  "github.com/docker/docker/api/types"
  "github.com/docker/docker/api/types/container"
  "github.com/moby/moby/client"
  "golang.org/x/net/context"
)

func main() {
  ctx := context.Background()
  cli, err := client.NewEnvClient()
  if err != nil {
    panic(err)
  }
  // 略
}
ここのimportで注意すべき点がある。
執筆時点ではクライアントはmobyなのだが、typesについてはすべてdockerを参照している。

moby配下にもtypesがあり、最初はそちらを参照していたところ以下のようなエラーがでてちょっとハマった。
cannot use "/github.com/moby/moby/api/types".ContainerListOptions literal (type "/github.com/moby/moby/api/types".ContainerListOptions) as type "/github.com/docker/docker/api/types".ContainerListOptions in argument to cli.ContainerList'

イメージの取得

$ docker pull [image]
_, err = cli.ImagePull(ctx, "docker.io/library/alpine", types.ImagePullOptions{})
if err != nil {
  panic(err)
}

aplineのイメージをimagePullで取得する。

コンテナの作成

$ docker build [path | url]
resp, err := cli.ContainerCreate(ctx, &container.Config{
  Image: "alpine",
  Cmd:   []string{"echo", "hello world"},
}, nil, nil, "name")
if err != nil {
  panic(err)
}

先ほどaplineのイメージを取得したので、ContainerCreateでコンテナの作成を行う。
コンテナ、ホスト、ネットワークの設定ができる。
今回はコンテナの設定(container.Config)だけを行う。

aplineのイメージを使い、起動時にはecho "hello world"を実行する。

コンテナの実行

$ docker start [container]
if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
  panic(err)
}

ContainerCreateではContainerCreateCreatedBodyという戻り値があり、コンテナのIDや警告を取得することができる。
ContainerStartでコンテナを実行する。その際に戻り値のコンテナIDを使っている。

コンテナの実行が終わるまで待機

$ docker wait [container]
if _, err = cli.ContainerWait(ctx, resp.ID); err != nil {
  panic(err)
}

ターミナルからDockerコンテナを実行するときはあまり使わないが、ContainerWaitで指定されたコンテナの状態がnot-running, next-exit, removedのいずれかになるまで待機する。

コンテナのログを取得

$ docker logs [image]
out, err := cli.ContainerLogs(ctx, resp.ID, types.ContainerLogsOptions{ShowStdout: true})
if err != nil {
  panic(err)
}

io.Copy(os.Stdout, out)

ContainerWait同様にあまり使わない気がするが、ContainerLogsでコンテナで生成されたログをio.ReadCloserに返してくれるので、io.Copy関数を使い標準出力に表示する。

コードの全体

package main

import (
 "io"
 "os"

 "github.com/docker/docker/api/types"
 "github.com/docker/docker/api/types/container"
 "github.com/moby/moby/client"
 "golang.org/x/net/context"
)

func main() {
 ctx := context.Background()
 cli, err := client.NewEnvClient()
 if err != nil {
  panic(err)
 }

 _, err = cli.ImagePull(ctx, "docker.io/library/alpine", types.ImagePullOptions{})
 if err != nil {
  panic(err)
 }

 resp, err := cli.ContainerCreate(ctx, &container.Config{
  Image: "alpine",
  Cmd:   []string{"echo", "hello world"},
 }, nil, nil, "")
 if err != nil {
  panic(err)
 }

 if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
  panic(err)
 }

 if _, err = cli.ContainerWait(ctx, resp.ID); err != nil {
  panic(err)
 }

 out, err := cli.ContainerLogs(ctx, resp.ID, types.ContainerLogsOptions{ShowStdout: true})
 if err != nil {
  panic(err)
 }

 io.Copy(os.Stdout, out)
}

を実行すると、コンソールにhello worldと表示される。
$ go run main.go
hello world



その他のよく使いそうな関数


Dockerを使っていると、これまでに紹介したもの以外によく使うコマンドがある。

イメージ一覧の取得

$ docker images
list, err := cli.ImageList(ctx, types.ImageListOptions{})
if err != nil {
  panic(err)
}
for _, image := range list {
  fmt.Printf("ID: %s, Labels: %s, Container: %d, Size: %d\n", image.ID, image.Labels, image.Containers, image.Size)
}

ImageListでイメージ一覧を取得でき、ImageSummaryを配列を返す。
私の環境で実際に実行すると以下のようなリストが表示される。
ID: sha256:328..., Labels: map[build-date:20170801 license:GPLv2 name:CentOS Base Image vendor:CentOS], Container: -1, Size: 192510595
ID: sha256:21e..., Labels: map[], Container: -1, Size: 66833621
ID: sha256:732..., Labels: map[], Container: -1, Size: 3965955
ID: sha256:893..., Labels: map[], Container: -1, Size: 714537141
ID: sha256:a85..., Labels: map[], Container: -1, Size: 183754122
ID: sha256:b6c..., Labels: map[], Container: -1, Size: 689015971
ID: sha256:47b..., Labels: map[], Container: -1, Size: 351873541
ID: sha256:c9e..., Labels: map[], Container: -1, Size: 389739875

コンテナ一覧の取得

$ docker ps -a
containers, err := cli.ContainerList(ctx, types.ContainerListOptions{
  All: true,
})
if err != nil {
  panic(err)
}
for _, container := range containers {
  fmt.Printf("ID: %s, Name: %s, Image: %s, Command: %s\n", container.ID, container.Names, container.Image, container.Command)
}

ContainerListでコンテナ一覧が取得できる。ContainerListOptionsAll: trueにすることで、停止中のコンテナも表示できる。
また、ContainerListはContainerの配列を返す。
私の環境で実際に実行すると以下のようなリストが表示される。
ID: 5108..., Name: [/goofy_noether], Image: alpine, Command: echo 'hello world'
ID: 2709..., Name: [/peaceful_goldstine], Image: alpine, Command: echo 'hello world'
ID: 2809..., Name: [/redis], Image: redis, Command: docker-entrypoint.sh redis-server

コンテナの削除

$ docker rm [container]
err = cli.ContainerRemove(ctx, "id", types.ContainerRemoveOptions{})
if err != nil {
  panic(err)
}

ContainerRemoveで指定したIDのコンテナを削除できる。


だいたいこんな感じだろうか。
その他の関数については、mobyクライアントのドキュメントを読めばわかると思う。

ただドキュメントもそこまで親切ではなく、オプションを指定したらどうなるかなど書かれていない。そんなときはDocker EngineのAPIリファレンスを見ることをオススメする。



Dockerfileを用いたイメージ作成は、結構ハマったので別記事として後日書く。

追記: 2017/09/04 08:00



参考サイト





以上

written by @bc_rikko

0 件のコメント :

コメントを投稿