Golang docker library - mounting host directory volumes

时间:2019-04-17 01:22:38

标签: docker go

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.

Update 1

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.

Update 2

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

1 个答案:

答案 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 是否实际上是绑定安装。