12

Basically i need to something like this

docker run -p something:something --name xxxx imagename

in golang sdk (this one https://docs.docker.com/engine/api/sdks/) for docker api, my current code looks like this

exposedPorts, portBindings, _ := nat.ParsePortSpecs([]string{
    "127.0.0.1:8080:2368",
})
// Running the ghost container
createdBody, err := dockerClient.ContainerCreate(context.Background(),
    &container.Config{
        Image:        "ghost:latest",
        ExposedPorts: exposedPorts,// it supposed to be nat.PortSet
    },
    &container.HostConfig{
        PortBindings: portBindings,// it supposed to be nat.PortMap
    },
    &network.NetworkingConfig{},
    containerName)

I'm using this https://github.com/docker/go-connections/blob/master/nat/nat.go#L126 ParsePortSpecs function which return (map[Port]struct{}, map[Port][]PortBinding, error) but fail since the container.Config.ExposedPorts is nat.PortSet (it's actually map[Port]struct{} tho) and containter.HostConfig.PortBindins is nat.PortMap

I'm not sure if i want to use this client https://github.com/fsouza/go-dockerclient since my current version of docker API is 1.25 and it doesn't support API version above 1.23

Adit
  • 183
  • 1
  • 8

2 Answers2

29

The Docker Client Go SDK might have changed a bit since Jan, but I just got this working so I'll document what I did here.

If you need a port exposed, which would look like 4140/tcp under PORTS on docker ps then you can do the following:

config := &container.Config{
    Image: "nginx",
    ExposedPorts: nat.PortSet{
        "4140/tcp": struct{}{},
    },
}

hostConfig := &container.HostConfig{}

ctx := context.Background()
containerResp, err := Docker.ContainerCreate(ctx, config, hostConfig, nil, "")
if err != nil {
    panic(err)
}

if err := Docker.ContainerStart(ctx, containerResp.ID, types.ContainerStartOptions{}); err != nil {
    panic(err)
}

If you'd like to bind that port to the host on 0.0.0.0, which would look like 0.0.0.0:4140->4140/tcp under PORTS on docker ps you need to add in the port bindings to the hostConfig:

config := &container.Config{
    Image: "nginx",
    ExposedPorts: nat.PortSet{
        "4140/tcp": struct{}{},
    },
}

hostConfig := &container.HostConfig{
    PortBindings: nat.PortMap{
        "4140/tcp": []nat.PortBinding{
            {
                HostIP: "0.0.0.0",
                HostPort: "4140",
            },
        },
    },
}

ctx := context.Background()
containerResp, err := Docker.ContainerCreate(ctx, config, hostConfig, nil, "")
if err != nil {
    panic(err)
}

if err := Docker.ContainerStart(ctx, containerResp.ID, types.ContainerStartOptions{}); err != nil {
    panic(err)
}

Hopefully this'll save somebody some time :)

Tom Mertz
  • 626
  • 9
  • 14
  • I'm getting an error trying to construct the config object with the nat.PortSet. This code `config := &container.Config{ Image: "nginx", ExposedPorts: nat.PortSet{ nat.Port("80:tcp"): struct{}{}, }, }` throws the error `cannot use (nat.PortSet literal) (value of type nat.PortSet) as nat.PortSet value in struct literal` – ZacSketches Apr 22 '20 at 11:53
5
containerCfg := &container.Config {
    Image: haproxyImage,
    Tty: true,
    OpenStdin: true,
    AttachStdout: true,
    AttachStderr: true,
    ExposedPorts: nat.PortSet{
        nat.Port("443/tcp"): {},
        nat.Port("10001/tcp"): {},
    },
}

hostConfig := &container.HostConfig{
    Binds: []string{
        "/var/run/docker.sock:/var/run/docker.sock",
    },
    PortBindings: nat.PortMap{
        nat.Port("443/tcp"): []nat.PortBinding{{HostIP: "0.0.0.0", HostPort: "443"}},
        nat.Port("10001/tcp"): []nat.PortBinding{{HostIP: "0.0.0.0", HostPort: "10001"}},
    },
}
Tamil
  • 201
  • 2
  • 2
  • 2
    Tip: If you still prefer/need the randomly-assigned "HostPort"-side feature that docker does, just omit the `HostPort` key and docker will randomly-assign a number. – Michael Galaxy Mar 04 '19 at 18:11