15
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

mobyを使ってgolangからDockerコンテナを操作する

Last updated at Posted at 2017-09-23

はじめに

  • golangから動的にDockerコンテナを動的に扱いたくて方法を調べた。
  • ソースコードから部分的に抜粋してきてるので、もしかしたらそのまま動かないかもしれません(ごめんなさい)

参考リンク集

パッケージのインポートでハマる

  • 2017/09/23時点では、 github.com/moby/moby および github.com/docker/docker がほぼ同一のAPIを提供している
  • 使用する構造体は、それぞれ適切なパッケージからインポートしなければエラーが起きる
  • 現状、下記のようにインポート先を分ければ動くらしい
    • Docker APIへの接続クライアントは github.com/moby/moby からインポートする
    • それ以外は github.com/docker/docker からインポートする
import (
	"log"

	"github.com/moby/moby/client"
)

func main() {
	cl, err := client.NewEnvClient()
	if err != nil {
		log.Fatalf("%v", err)
	}
}

イメージ関係

イメージのプル

import (
	"log"
	"context"

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

func main() {
	opts := types.ImagePullOptions{}
	_, err := cl.ImagePull(context.TODO(), "<image_name>", opts)
	if err != nil {
		log.Fatalf("%v", err)
	}
}

Dockerfileからイメージビルド

  • Dockerfile は tarファイル として固めておく
    • gzip してもしなくてもOK
  • ビルド開始後、処理完了前に抜けると、ビルドが完了しなかった
    • ctx.Done() 待ってみたけど、返ってこなかった
    • こういうときどうするのが正解か分かんなかったので、とりあえずsleep
import (
	"os"
	"log"
	"context"
	"bufio"

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

func main() {

	// tarファイル取得
	tarfile, _ := os.Open("<tarfile_path>")
	defer tarfile.Close()

	// イメージのビルド
	opts := types.ImageBuildOptions{
		Tags:        []string{"<repository_name>"},
		ForceRemove: true,
	}
	resp, err := cl.ImageBuild(context.TODO(), bufio.NewReader(tarfile), opts)
	if err != nil {
		log.Fatalf("%v", err)
	}
	defer resp.Body.Close()

	// ちょっと待たないと先に死んじゃう
	time.Sleep(100 * time.Millisecond)
}

(おまけ) tarファイルの作成

import (
	"os"
	"ioutil"

	"archive/tar"
	"compress/gzip"
)

func main() {

	// tarファイル生成
	tarfile, _ := os.Create("<tarfile_path>")
	defer tarfile.Close()

	// gzip
	gzw := gzip.NewWriter(tarfile)
	defer gzw.Close()

	// tar
	tw := tar.NewWriter(gzw)
	defer tw.Close()

	// contentの取得
	body, _ := ioutil.ReadFile("<contentfile_path>")

	header := &tar.Header{
		Name: "<dockerfile_path_in_tarfile>",
		Size: int64(len(body)),
	}
	if err := tw.WriteHeader(header); err != nil {
		log.Fatalf("%v", err)
	}
	if _, err := tw.Write(body); err != nil {
		log.Fatalf("%v", err)
	}
}

コンテナ関係

コンテナ作成

  • 前述の通り、パッケージのインポート先が github.com/docker/docker になっていることに注意
import (
	"context"
	"log"

	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/network"
	"github.com/docker/go-connections/nat"
)

func main() {
	config := &container.Config{
		Image: "<image_name>",
	}
	hostConfig := &container.HostConfig{}
	netConfig := &network.NetworkingConfig{}
	resp, err := cl.ContainerCreate(context.TODO(), config, hostConfig, netConfig, "<container_name>")
	if err != nil {
		log.Fatalf("%v", err)
	}

	log.Printf("%v", resp.ID) // ContainerID
}
  • ゲストのポートをホストにバインドする
  • Dockerfileで EXPOSE していても、 configExposedPorts は必要らしい(これで小一時間ハマった)
config := &container.Config{
	Image:        "<image_name>",
	ExposedPorts: nat.PortSet{nat.Port("<guest_port>"): struct{}{}},
}

hostConfig := &container.HostConfig{
	PortBindings: nat.PortMap{
		nat.Port("<guest_port>"): []nat.PortBinding{{HostPort: "<host_port>"}},
	},
}
  • 特定のネットワークに所属させる
netConfig := &network.NetworkingConfig{
	EndpointsConfig: map[string]*network.EndpointSettings{
		"<network_name>": {
			IPAMConfig: &network.EndpointIPAMConfig{
				IPv4Address: "<guest_ip_address>",
			},
		},
	},
}
  • AutoRemove と AutoRestart
  • どちらかのみ設定可能(同時に設定すると起動時にエラーする)
hostConfig := &container.HostConfig{
	RestartPolicy: container.RestartPolicy{
		Name: "always",
	},
	AutoRemove: false,
}

コンテナへのファイルコピー

  • コピーするファイルの渡し方でちょっと迷った
  • tarファイル をオープンして、 bufio.NewReader() でラップするとうまくいった
import (
	"os"
	"log"
	"bufio"

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

func main() {

	// archive の オープン
	archive, _ := os.Open("<archive_path>")
	defer archive.Close()

	// コンテナへコピー
	opts := types.CopyToContainerOptions{}
	if err := cl.CopyToContainer(context.TODO(), "<container_id>", "<guest_destination_path>", bufio.NewReader(archive), opts); err != nil {
		log.Fatalf("%v", err)
	}
}

ネットワーク関係

ネットワーク作成

import (
	"context"
	"log"

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

func main() {
	opts := types.NetworkCreate{
		IPAM: &network.IPAM{
			Config: []network.IPAMConfig{
				{Subnet: "<subnet>"},
			},
		},
	}
	if _, err = cl.NetworkCreate(context.TODO(), "<network_name>", opts); err != nil {
		log.Fatalf("%v", err)
	}
}

一覧取得関係

  • フィルタ条件の渡し方でちょっと迷う
    • 基本的には、 map[string][]string をJSON形式になおしてあげるとOK

イメージ一覧の取得

import (
	"context"
	"log"
	"encoding/json"

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

func main() {

	// フィルタ条件
	filtMap := map[string][]string{"reference": {"<repository_name>:<version>"}}
	filtBytes, _ := json.Marshal(filtMap)
	filt, err := filters.FromParam(string(filtBytes))
	if err != nil {
		log.Fatalf("%v", err)
	}

	opts := types.ImageListOptions{Filters: filt}
	images, err := cl.ImageList(context.TODO(), opts)
	if err != nil {
		log.Fatalf("%v", err)
	}
}

コンテナ一覧の取得

  • All にすると、停止中コンテナも含める
  • Quiet にすると、コンテナIDだけ返ってくる
import (
	"context"
	"log"
	"encoding/json"

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

func main() {

	// フィルタ条件
	filtMap := map[string][]string{"name": {"<container_name>"}}
	filtBytes, _ := json.Marshal(filtMap)
	filt, _ := filters.FromParam(string(filtBytes))

	opts := types.ContainerListOptions{
		All:     true,
		Quiet:   true,
		Filters: filt,
	}
	resp, err := cl.ContainerList(context.TODO(), opts)
	if err != nil {
		log.Fatalf("%v", err)
	}
}

ネットワーク一覧取得

import (
	"context"
	"log"
	"encoding/json"

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

func main() {

	// フィルタ条件
	filtMap := map[string][]string{"name": {"<network_name>"}}
	filtBytes, _ := json.Marshal(filtMap)
	filt, err := filters.FromParam(string(filtBytes))
	if err != nil {
		log.Fatalf("%v", err)
	}

	opts := types.NetworkListOptions{Filters: filt}
	nets, err := cl.NetworkList(context.TODO(), opts)
	if err != nil {
		log.Fatalf("%v", err)
	}
}
15
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?