概要
わりとサンプルが Web 上に少ないので試したのを紹介します
今回試したのは
docker run alpine echo hello
docker run -d -p 8080:80 nginx
になります
また使用したライブラリは docker が公開している公式のライブラリになります
https://github.com/docker/docker/tree/master/client
このライブラリの使い方にも結構苦労したのでその辺も紹介します
環境
- Mac OS X 10.12.3
- golang 1.7.5
- docker client 946787e85e7c747c78354c5ca8d7a512b8766969
docker client ライブラリのインストール
go get でインストールします
が、vendor ディレクトリの関係で少し作業が必要になります
- export GOPATH=”/Users/hawksnowlog/go”
- go get github.com/docker/docker/client
- cd /Users/hawksnowlog/go/src
- mv /Users/hawksnowlog/go/src/github.com/docker/docker/vendor .
という vendor ディレクトリの移動が必要になります
これをやらないで go build を実行すると
(type "vendor/github.com/docker/go-connections/nat".PortSet) as type "github.com/docker/docker/vendor/github.com/docker/go-connections/nat".PortSet in field value
というエラーとなるビルドに失敗します
執筆時に以下の issue があったのでこれが解決されればやる必要はなくなるかもしれません
https://github.com/docker/docker/issues/28269
alpine のコード
ではコーディングしていきます
- mkdir -p /Users/hawksnowlog/go/src/github.com/hawksnowlog/docker-golang-test
- cd /Users/hawksnowlog/go/src/github.com/hawksnowlog/docker-golang-test
- touch main.go
package main
import (
"fmt"
"io"
"os"
"time"
"context"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
)
func main() {
ctx := context.Background()
cli, err := client.NewEnvClient()
if err != nil {
panic(err)
}
imageName := "alpine:latest"
_, err = cli.ImagePull(ctx, imageName, types.ImagePullOptions{})
if err != nil {
panic(err)
}
count := 0
max := 30
downloaded := false
for {
images, err := cli.ImageList(ctx, types.ImageListOptions{})
if err != nil {
panic(err)
}
for _, image := range images {
for _, tag := range image.RepoTags {
fmt.Println(tag)
if tag == imageName {
downloaded = true
}
}
}
if downloaded == true {
break
}
if count >= max {
break
}
count++
time.Sleep(5 * time.Second)
}
resp, err := cli.ContainerCreate(ctx,
&container.Config{
Image: imageName,
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)
}
- go fmt github.com/hawksnowlog/docker-golang-test
- go build github.com/hawksnowlog/docker-golang-test
- go install github.com/hawksnowlog/docker-golang-test
- ./docker-golang-test
で動作します
成功すれば hello world と標準出力に表示されると思います
ポイントは ImagePull 後に ImageList してイメージがダウンロードされるのを待っている部分です
これがないと次の ContainerCreate を行ったときに「そんなイメージありません」と言われてエラーとなってしまいます
公式のチュートリアルを見るとこの処理が完全に抜け落ちているため、手動で docker pull をしないと必ずエラーになってしまいます
あと docker 公式の golang ライブラリには docker run が実装されていません
なので、create -> start と順番に実行する必要があります
nginx のコード
次に nginx を動作させるコードです
作業ディレクトリやファイルは先ほど同じです
- vim main.go
package main
import (
"fmt"
"time"
"context"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
"github.com/docker/go-connections/nat"
)
func Nginx() {
ctx := context.Background()
cli, err := client.NewEnvClient()
if err != nil {
panic(err)
}
imageName := "nginx:latest"
_, err = cli.ImagePull(ctx, imageName, types.ImagePullOptions{})
if err != nil {
panic(err)
}
count := 0
max := 30
downloaded := false
for {
images, err := cli.ImageList(ctx, types.ImageListOptions{})
if err != nil {
panic(err)
}
for _, image := range images {
for _, tag := range image.RepoTags {
fmt.Println(tag)
if tag == imageName {
downloaded = true
}
}
}
if downloaded == true {
break
}
if count >= max {
break
}
count++
time.Sleep(5 * time.Second)
}
p1, _ := nat.NewPort("tcp", "80")
p2, _ := nat.NewPort("tcp", "80")
ports1 := make(nat.PortSet)
ports2 := make(nat.PortMap)
ports1[p1] = struct{}{}
ports2[p2] = []nat.PortBinding{nat.PortBinding{"0.0.0.0", "8080"}}
resp, err := cli.ContainerCreate(ctx,
&container.Config{
Image: imageName,
ExposedPorts: ports1,
},
&container.HostConfig{
PortBindings: ports2,
}, nil, "")
if err != nil {
panic(err)
}
if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
panic(err)
}
}
こっちのポイントは ContainerCreate 時にコンテナとホストでポートバインディングするパラメータが追加になっている点です
コマンドで言うところの -p
オプションです
また、nginx イメージは Cmd オプションが不要なので削除しています
(ここで nat を参照しているのですが、この参照が vendor ディレクトリを移動しないとできないようです)
あとは処理の最後で ContainerWait しないようにしています
これがあると実行したときにコンテナが stop するまでプロンプトが返ってこなくなります
最後に
alpine と nginx イメージをコマンドではなく golang 製のライブラリから API をコールして run してみました
正直コード的には長すぎるしかなり面倒でした
他の言語のライブラリを見ると run が実装されているものが多いので素直にそれを使ったほうが良いかもしれません
ただ、docker 公式なのでバージョン追従やサポートは手厚いと思います
0 件のコメント:
コメントを投稿