golangからDocker Remote APIを使ってイメージ・コンテナを操作する方法を覚えたので、次はDockerfileからイメージを作成し、実行する方法を学んだ。
Docker Remote APIの基本的な使い方は以下の記事を参照ください。
かなりハマったので、皆さんの助けになればと思う。
環境は以下のとおり。
- Mac OSX
- go 1.8.3
- Docker for Mac 17.6.1-ce
Dockerfileの作成し、tar.gzでアーカイブする
Docker Remote APIでDockerfileを扱う場合はtar.gz形式でアーカイブする。
このことを知らずにローカルにDockerfileを作って参照して
Error response from daemon: Cannot locate specified Dockerfile: Dockerfile
というエラーがでてかなりハマった。ディレクトリ構成は以下のとおり。
.
├── Dockerfile
└── script.sh
Dockerfileを作成する。
FROM alpine
ADD ./ /root
WORKDIR /root
CMD ["sh", "script.sh"]
alpineのイメージを使い、コンテナにscript.shを追加、起動時にshellで実行するようにしている。
シェルスクリプトの中身は、サンプルなので文字列を出力するだけの簡単なもの。
#! /bin/sh
echo "hello world from container"
最後にtarコマンドでアーカイブする。gzipで圧縮するかどうかは任意。(だから正確にはtar.gzではなくtarな気がするけど雰囲気でtar.gzという拡張子にしている)
また今回はDockerfile.tar.gzというファイル名にしたが、任意の名前でも良い。
# Dockerfileがあるディレクトリに移動
$ cd /path/to/Dockerfile
# ディレクトリ配下すべてをアーカイブ化する
$ tar -zcvf Dockerfile.tar.gz ./
$ ls
Dockerfile Dockerfile.tar.gz script.sh
これでDockerfileを使う準備ができた。
あとはDocker Remote APIにDockerfileを渡してイメージを作成する。
Dockerfileからイメージを作成し、コンテナを起動する
package main
import (
"fmt"
"io"
"io/ioutil"
"os"
"time"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/moby/moby/client"
"golang.org/x/net/context"
)
func check(err error) {
if err != nil {
panic(err)
}
}
func main() {
ctx := context.Background()
cli, err := client.NewEnvClient()
check(err)
// Dockerfile.tar.gzを読み込む
cwd, _ := os.Getwd()
file, err := os.Open(cwd + "/Dockerfile.tar.gz")
defer file.Close()
// イメージ名はUnix時間
imageName := fmt.Sprintf("%v", time.Now().Unix())
// Dockerfileからイメージを作成する
res, err := cli.ImageBuild(ctx, file, types.ImageBuildOptions{
Tags: []string{imageName},
ForceRemove: true,
})
check(err)
defer res.Body.Close()
fmt.Printf("OSType: %s\n", res.OSType)
b, err := ioutil.ReadAll(res.Body)
check(err)
fmt.Println(string(b))
fmt.Printf("Build Image. Image's name is %v\n", imageName)
// イメージ名を指定しコンテナを作成する
resp, err := cli.ContainerCreate(ctx, &container.Config{
Image: imageName,
}, nil, nil, "")
check(err)
// コンテナの実行
err = cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})
check(err)
_, err = cli.ContainerWait(ctx, resp.ID)
check(err)
out, err := cli.ContainerLogs(ctx, resp.ID, types.ContainerLogsOptions{
ShowStdout: true,
})
check(err)
// 実行結果の出力
io.Copy(os.Stdout, out)
// 起動したコンテナを削除する
err = cli.ContainerRemove(ctx, resp.ID, types.ContainerRemoveOptions{})
check(err)
}
上記のコードを実行すると、以下のように出力される。
$ ls
Dockerfile.tar.gz Dockerfile main.go script.sh
$ go run main.go
OSType: linux{"stream":"Step 1/4 : FROM alpine\n"}
{"stream":" ---\u003e 7328f6f8b418\n"}
{"stream":"Step 2/4 : ADD ./ /root\n"}
{"stream":" ---\u003e ee82dcc482cb\n"}
{"stream":"Removing intermediate container f9d0fe0cfd2b\n"}
{"stream":"Step 3/4 : WORKDIR /root\n"}
{"stream":" ---\u003e fd8e0cecb48f\n"}
{"stream":"Removing intermediate container fea92bde02dd\n"}
{"stream":"Step 4/4 : CMD sh script.sh\n"}
{"stream":" ---\u003e Running in ee7b6a9e1dcd\n"}
{"stream":" ---\u003e 3f9c3bb0eeb0\n"}
{"stream":"Removing intermediate container ee7b6a9e1dcd\n"}
{"stream":"Successfully built 3f9c3bb0eeb0\n"}
{"stream":"Successfully tagged 1503883258:latest\n"}
Build Image. Image's name is 1503883258
hello world from container
以下、処理の詳細について解説する。
Dockerfile.tar.gzを開く
今回は
Dockerfile.tar.gz
というファイル名にしたが、任意のファイル名で問題ない。os.Getwdでルートディレクトリを取得、os.Openでファイルを開いている。
cwd, _ := os.Getwd()
file, err := os.Open(cwd + "/Dockerfile.tar.gz")
defer file.Close()
Dockerfileからイメージを作成する
イメージ名は重複させたくなかったのでUnix時間を使っている。
imageBuildのbuildContextにDockerfile.tar.gzを渡してイメージを作成している。
imageName := fmt.Sprintf("%v", time.Now().Unix())
res, err := cli.ImageBuild(ctx, file, types.ImageBuildOptions{
Tags: []string{imageName},
ForceRemove: true,
})
ImageBuildOptionsにもDockerfileを指定できるのだが、buildContentを指定せず
Dockerfile: "./Dockerfile"
のように指定したらError response from daemon: Cannot locate specified Dockerfile: Dockerfile
というエラーが出る。絶対パス、相対パス、ファイル名変更しても見つからないと言われ、思いっきりハマってしまった。よくよく調べてみるとここのDockerfileオプションは、buildContext内のDockerfileの名前だった。(Dockerfileを"hoge"のように別名にしたときに指定するオプション)
// Bad. [Error response from daemon: Cannot locate specified Dockerfile: Dockerfile]
res, err := cli.ImageBuild(ctx, nil, types.ImagesBuildOptions{
Dockerfile: "./Dockerfile",
Tags: []string{imageName},
ForceRemove: true,
})
参考サイト
以上
written by @bc_rikko
0 件のコメント :
コメントを投稿