目的
オンラインコンパイラを作る時に標準出力がストリームでヌルヌル出てくれるといいなって思った
https://editor.ugwis.net/
コンテナの標準入出力
docker attach
と同じような挙動をするエンドポイントがDocker Engineの/containers/<id or name>/attach
に提供されています
ContainerCreate
まず、コンテナの標準入出力を扱うにはContainerCreateのフラグを変える必要があります
resp, err := cli.ContainerCreate(ctx, &container.Config{
Image: lang.Language[query.Language].DockerImage,
WorkingDir: "/workspace",
Cmd: lang.Language[query.Language].RunCmd,
NetworkDisabled: true,
AttachStdin: true,
AttachStdout: true,
AttachStderr: true,
OpenStdin: true,
StdinOnce: true,
Tty: false,
}, &container.HostConfig{
Mounts: []mount.Mount{
mount.Mount{
Type: mount.TypeBind,
Source: "/tmp/compiler/" + runningHash,
Target: "/workspace",
},
},
AutoRemove: true,
}, nil, "")
標準入力に関するフラグのみデフォルトでfalse
で指定されています。
Ttyがtrueの時は、ContainerAttachのReaderから標準入力の内容も出力されます。用途としてはターミナルのようなものを作る時に標準入力をキーボード、標準出力をコンソール画面に使う場合などが考えられます。
ContainerAttach
標準出力を受け取るにはContainerAttachを呼び出し、io.Readerから標準出力を受け取ります。
送られてくるストリームにはheaderが付いており、
header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4}
のような形式になっています。
これは標準出力と標準エラーが同様に流れてくるため、呼び出し側が判別するためにヘッダーが用いられています。
この形式はcontainerLogsメソッドも同じですが公式ドキュメントには書かれていなかった為、意味不明なnull文字に悩まされました
解消するには自分でパースしても良いですが、公式が提供するcopyメソッドを使うとストリーム同士をパイプでうまく繋いでくれます
github.com/docker/docker/pkg/stdcopy.StdCopy
// Attach container
stdin, err := cli.ContainerAttach(ctx, resp.ID, types.ContainerAttachOptions{
Stream: true,
Stdin: true,
})
defer stdin.Close()
if err != nil {
fmt.Println(err.Error())
c.String(http.StatusInternalServerError, err.Error())
return
}
stdout, err := cli.ContainerAttach(ctx, resp.ID, types.ContainerAttachOptions{
Stream: true,
Stdout: true,
})
defer stdout.Close()
if err != nil {
fmt.Println(err.Error())
c.String(http.StatusInternalServerError, err.Error())
return
}
ContainerAttach(WebSocket)
また、clientのライブラリには実装されていませんがdocker Engine側にWebSocketのエンドポイントが提供されており、ブラウザと接続する際はこれに繋ぐことで入出力が楽になると思います