はじめに
- golangから動的にDockerコンテナを動的に扱いたくて方法を調べた。
- ソースコードから部分的に抜粋してきてるので、もしかしたらそのまま動かないかもしれません(ごめんなさい)
参考リンク集
- golangからDocker Remote APIを使ってイメージ・コンテナを操作する方法まとめ
- GolangでDocker Remote APIを使ってDockerfileからイメージ作成する方法
- GoのコードからDockerコンテナの起動を実装する
パッケージのインポートでハマる
- 2017/09/23時点では、
github.com/moby/moby
およびgithub.com/docker/docker
がほぼ同一のAPIを提供している - 使用する構造体は、それぞれ適切なパッケージからインポートしなければエラーが起きる
- 現状、下記のようにインポート先を分ければ動くらしい
- Docker APIへの接続クライアントは
github.com/moby/moby
からインポートする - それ以外は
github.com/docker/docker
からインポートする
- Docker APIへの接続クライアントは
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
していても、config
にExposedPorts
は必要らしい(これで小一時間ハマった)
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)
}
}