How do I perform the equivalent of:
docker run -v /host/path:/container/path image:tag
from Go using the official docker client package?
I've tried different Mounts and Volumes options within the HostOption and ConfigOption structs for the client.ContainerCreate() function, but can't quite figure it out.
In particular, the Volumes
member (of type map[string]struct{}
) is particularly hard to figure out how to use, and I can't find any documentation on what values are supposed to be present within the struct.
Code to demonstrate my problem:
package main
import (
"context"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
//"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/client"
"github.com/docker/docker/pkg/stdcopy"
"log"
"os"
"path/filepath"
)
func getThisDir() string {
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err != nil {
panic(err)
}
return dir
}
func main() {
log.Println("Creating client")
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
panic(err)
}
image := "ubuntu:18.04"
hostPath := getThisDir()
containerPath := "/host_files"
log.Printf(" Image: %s\n", image)
log.Printf(" Host Path: %s\n", hostPath)
log.Printf(" Container Path: %s\n", containerPath)
ctx := context.Background()
cli.NegotiateAPIVersion(ctx)
log.Println("Creating container")
var cont container.ContainerCreateCreatedBody
if cont, err = cli.ContainerCreate(
context.Background(),
&container.Config{
Image: image,
Entrypoint: []string{"/bin/bash", "-c", "ls -la " + containerPath},
Volumes: map[string]struct{}{
hostPath: {},
},
},
&container.HostConfig{
Runtime: "runsc",
/*
Mounts: []mount.Mount{
mount.Mount{
Type: mount.TypeVolume,
Source: hostPath,
Target: containerPath,
},
},
*/
},
nil,
"TEST_CONTAINER",
); err != nil {
panic(err)
}
defer func() {
log.Println("Cleaning up")
if err := cli.ContainerRemove(
context.Background(),
cont.ID,
types.ContainerRemoveOptions{
Force: true,
RemoveVolumes: true,
},
); err != nil {
panic(err)
}
}()
log.Println("Starting container")
if err = cli.ContainerStart(
context.Background(),
cont.ID,
types.ContainerStartOptions{},
); err != nil {
panic(err)
}
log.Println("Waiting for container to exit")
waitOk, waitErr := cli.ContainerWait(
ctx,
cont.ID,
container.WaitConditionNotRunning,
)
select {
case <-waitOk:
log.Println("Container exited normally!")
case err = <-waitErr:
log.Println("Error waiting")
panic(err)
}
log.Println("Should be done!")
logOutput, err := cli.ContainerLogs(
ctx,
cont.ID,
types.ContainerLogsOptions{
ShowStdout: true,
ShowStderr: true,
Follow: false,
},
)
if err != nil {
panic(err)
}
log.Println("Container output:")
stdcopy.StdCopy(os.Stdout, os.Stderr, logOutput)
}
Compiling and running this yields the output:
2019/04/16 20:42:21 Creating client
2019/04/16 20:42:21 Image: ubuntu:18.04
2019/04/16 20:42:21 Host Path: /home/user/go/src/test
2019/04/16 20:42:21 Container Path: /host_files
2019/04/16 20:42:21 Creating container
2019/04/16 20:42:22 Starting container
2019/04/16 20:42:22 Waiting for container to exit
2019/04/16 20:42:22 Container exited normally!
2019/04/16 20:42:22 Should be done!
2019/04/16 20:42:22 Container output:
ls: cannot access '/host_files': No such file or directory
2019/04/16 20:42:22 Cleaning up
If you uncomment the mount-related lines, you get this output instead:
2019/04/16 20:23:32 Creating client
2019/04/16 20:23:32 Image: ubuntu:18.04
2019/04/16 20:23:32 Host Path: /home/user/go/src/test
2019/04/16 20:23:32 Container Path: /host_files
2019/04/16 20:23:32 Creating container
panic: Error response from daemon: create /home/user/go/src/test: "/home/user/go/src/test" includes invalid characters for a local volume name, only "[a-zA-Z0-9][a-zA-Z0-9_.-]" are allowed. If you intended to pass a host directory, use absolute path
goroutine 1 [running]:
main.main()
/home/user/go/src/test/container.go:66 +0xb0c
The error message doesn't make sense, since I am using an absolute path. Maybe I should reread the documentation for ContainerCreate
.
The docker engine API documentation contains more details about Volumes - I'm beginning to think that I'm mistaken about how docker -v /host/path:/container/path
works - perhaps that is actually a bind mount and not a volume mount.
Rubber duck debugging FTW I guess. Removing the Volumes
settings, adding the Mounts
back in and changing the Type
to mount.TypeBind
made it work:
2019/04/16 20:53:18 Creating client
2019/04/16 20:53:18 Image: ubuntu:18.04
2019/04/16 20:53:18 Host Path: /home/user/go/src/test
2019/04/16 20:53:18 Container Path: /host_files
2019/04/16 20:53:18 Creating container
2019/04/16 20:53:18 Starting container
2019/04/16 20:53:19 Waiting for container to exit
2019/04/16 20:53:19 Container exited normally!
2019/04/16 20:53:19 Should be done!
2019/04/16 20:53:19 Container output:
total XXXX
drwxr-xr-x 7 1000 1000 4096 Apr 17 03:51 .
drwxr-xr-x 34 root root 4096 Apr 17 03:53 ..
-rw-r--r-- 1 1000 1000 10390 Apr 16 12:16 Gopkg.lock
-rw-r--r-- 1 1000 1000 1021 Apr 16 12:16 Gopkg.toml
-rwxr-xr-x 1 1000 1000 12433827 Apr 17 03:53 container
-rw-r--r-- 1 1000 1000 2421 Apr 17 03:51 container.go
2019/04/16 20:53:19 Cleaning up
答案 0 :(得分:0)
删除Volumes
设置,重新添加Mounts
并将Type
更改为mount.TypeBind
使其生效:
2019/04/16 20:53:18 Creating client
2019/04/16 20:53:18 Image: ubuntu:18.04
2019/04/16 20:53:18 Host Path: /home/user/go/src/test
2019/04/16 20:53:18 Container Path: /host_files
2019/04/16 20:53:18 Creating container
2019/04/16 20:53:18 Starting container
2019/04/16 20:53:19 Waiting for container to exit
2019/04/16 20:53:19 Container exited normally!
2019/04/16 20:53:19 Should be done!
2019/04/16 20:53:19 Container output:
total XXXX
drwxr-xr-x 7 1000 1000 4096 Apr 17 03:51 .
drwxr-xr-x 34 root root 4096 Apr 17 03:53 ..
-rw-r--r-- 1 1000 1000 10390 Apr 16 12:16 Gopkg.lock
-rw-r--r-- 1 1000 1000 1021 Apr 16 12:16 Gopkg.toml
-rwxr-xr-x 1 1000 1000 12433827 Apr 17 03:53 container
-rw-r--r-- 1 1000 1000 2421 Apr 17 03:51 container.go
2019/04/16 20:53:19 Cleaning up
叹气。
我唯一不能百分百确定的是docker -v /host/path:/container/path image:tag
是否实际上是绑定安装。