Skip to content

Commit 77fe35b

Browse files
committed
cli: add --mount to docker run
Signed-off-by: Akihiro Suda <[email protected]>
1 parent 8d96619 commit 77fe35b

8 files changed

Lines changed: 258 additions & 0 deletions

File tree

cli/command/container/opts.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"strings"
1212
"time"
1313

14+
"github.com/Sirupsen/logrus"
1415
"github.com/docker/docker/api/types/container"
1516
networktypes "github.com/docker/docker/api/types/network"
1617
"github.com/docker/docker/api/types/strslice"
@@ -31,6 +32,7 @@ type containerOptions struct {
3132
attach opts.ListOpts
3233
volumes opts.ListOpts
3334
tmpfs opts.ListOpts
35+
mounts opts.MountOpt
3436
blkioWeightDevice opts.WeightdeviceOpt
3537
deviceReadBps opts.ThrottledeviceOpt
3638
deviceWriteBps opts.ThrottledeviceOpt
@@ -223,6 +225,7 @@ func addFlags(flags *pflag.FlagSet) *containerOptions {
223225
flags.Var(&copts.tmpfs, "tmpfs", "Mount a tmpfs directory")
224226
flags.Var(&copts.volumesFrom, "volumes-from", "Mount volumes from the specified container(s)")
225227
flags.VarP(&copts.volumes, "volume", "v", "Bind mount a volume")
228+
flags.Var(&copts.mounts, "mount", "Attach a filesystem mount to the container")
226229

227230
// Health-checking
228231
flags.StringVar(&copts.healthCmd, "health-cmd", "", "Command to run to check health")
@@ -321,6 +324,10 @@ func parse(flags *pflag.FlagSet, copts *containerOptions) (*containerConfig, err
321324
return nil, errors.Errorf("invalid value: %d. Valid memory swappiness range is 0-100", swappiness)
322325
}
323326

327+
mounts := copts.mounts.Value()
328+
if len(mounts) > 0 && copts.volumeDriver != "" {
329+
logrus.Warn("`--volume-driver` is ignored for volumes specified via `--mount`. Use `--mount type=volume,volume-driver=...` instead.")
330+
}
324331
var binds []string
325332
volumes := copts.volumes.GetMap()
326333
// add any bind targets to the list of container volumes
@@ -589,6 +596,7 @@ func parse(flags *pflag.FlagSet, copts *containerOptions) (*containerConfig, err
589596
Tmpfs: tmpfs,
590597
Sysctls: copts.sysctls.GetAll(),
591598
Runtime: copts.runtime,
599+
Mounts: mounts,
592600
}
593601

594602
if copts.autoRemove && !hostConfig.RestartPolicy.IsNone() {

contrib/completion/bash/docker

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,6 +1518,7 @@ _docker_container_run_and_create() {
15181518
--memory-swap
15191519
--memory-swappiness
15201520
--memory-reservation
1521+
--mount
15211522
--name
15221523
--network
15231524
--network-alias

contrib/completion/fish/docker.fish

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l link -d 'Add
138138
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s m -l memory -d 'Memory limit (format: <number>[<unit>], where unit = b, k, m or g)'
139139
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l mac-address -d 'Container MAC address (e.g., 92:d0:c6:0a:29:33)'
140140
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l memory-swap -d "Total memory usage (memory + swap), set '-1' to disable swap (format: <number>[<unit>], where unit = b, k, m or g)"
141+
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l mount -d 'Attach a filesystem mount to the container'
141142
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l name -d 'Assign a name to the container'
142143
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l net -d 'Set the Network mode for the container'
143144
complete -c docker -A -f -n '__fish_seen_subcommand_from create' -s P -l publish-all -d 'Publish all exposed ports to random ports on the host interfaces'
@@ -330,6 +331,7 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l link -d 'Add li
330331
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s m -l memory -d 'Memory limit (format: <number>[<unit>], where unit = b, k, m or g)'
331332
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l mac-address -d 'Container MAC address (e.g., 92:d0:c6:0a:29:33)'
332333
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l memory-swap -d "Total memory usage (memory + swap), set '-1' to disable swap (format: <number>[<unit>], where unit = b, k, m or g)"
334+
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l mount -d 'Attach a filesystem mount to the container'
333335
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l name -d 'Assign a name to the container'
334336
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l net -d 'Set the Network mode for the container'
335337
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s P -l publish-all -d 'Publish all exposed ports to random ports on the host interfaces'

contrib/completion/zsh/_docker

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,7 @@ __docker_container_subcommand() {
629629
"($help)--log-driver=[Default driver for container logs]:logging driver:__docker_complete_log_drivers"
630630
"($help)*--log-opt=[Log driver specific options]:log driver options:__docker_complete_log_options"
631631
"($help)--mac-address=[Container MAC address]:MAC address: "
632+
"($help)*--mount=[Attach a filesystem mount to the container]:mount: "
632633
"($help)--name=[Container name]:name: "
633634
"($help)--network=[Connect a container to a network]:network mode:(bridge none container host)"
634635
"($help)*--network-alias=[Add network-scoped alias for the container]:alias: "

docs/reference/commandline/create.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ Options:
8585
--memory-reservation string Memory soft limit
8686
--memory-swap string Swap limit equal to memory plus swap: '-1' to enable unlimited swap
8787
--memory-swappiness int Tune container memory swappiness (0 to 100) (default -1)
88+
--mount value Attach a filesytem mount to the container (default [])
8889
--name string Assign a name to the container
8990
--network-alias value Add network-scoped alias for the container (default [])
9091
--network string Connect a container to a network (default "default")

docs/reference/commandline/run.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ Options:
9595
--memory-reservation string Memory soft limit
9696
--memory-swap string Swap limit equal to memory plus swap: '-1' to enable unlimited swap
9797
--memory-swappiness int Tune container memory swappiness (0 to 100) (default -1)
98+
--mount value Attach a filesystem mount to the container (default [])
9899
--name string Assign a name to the container
99100
--network-alias value Add network-scoped alias for the container (default [])
100101
--network string Connect a container to a network
@@ -316,6 +317,29 @@ docker run -v c:\foo:c:\existing-directory-with-contents ...
316317

317318
For in-depth information about volumes, refer to [manage data in containers](https://docs.docker.com/engine/tutorials/dockervolumes/)
318319

320+
321+
### Add bind-mounts or volumes using the --mount flag
322+
323+
The `--mount` flag allows you to mount volumes, host-directories and `tmpfs`
324+
mounts in a container.
325+
326+
The `--mount` flag supports most options that are supported by the `-v` or the
327+
`--volume` flag, but uses a different syntax. For in-depth information on the
328+
`--mount` flag, and a comparison between `--volume` and `--mount`, refer to
329+
the [service create command reference](service_create.md#add-bind-mounts-or-volumes).
330+
331+
Even though there is no plan to deprecate `--volume`, usage of `--mount` is recommended.
332+
333+
Examples:
334+
335+
```bash
336+
$ docker run --read-only --mount type=volume,target=/icanwrite busybox touch /icanwrite/here
337+
```
338+
339+
```bash
340+
$ docker run -t -i --mount type=bind,src=/data,dst=/data busybox sh
341+
```
342+
319343
### Publish or expose port (-p, --expose)
320344

321345
```bash

integration-cli/docker_cli_run_test.go

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4422,6 +4422,184 @@ func (s *DockerSuite) TestRunMountReadOnlyDevShm(c *check.C) {
44224422
c.Assert(out, checker.Contains, "Read-only file system")
44234423
}
44244424

4425+
func (s *DockerSuite) TestRunMount(c *check.C) {
4426+
testRequires(c, DaemonIsLinux, SameHostDaemon, NotUserNamespace)
4427+
4428+
// mnt1, mnt2, and testCatFooBar are commonly used in multiple test cases
4429+
tmpDir, err := ioutil.TempDir("", "mount")
4430+
if err != nil {
4431+
c.Fatal(err)
4432+
}
4433+
defer os.RemoveAll(tmpDir)
4434+
mnt1, mnt2 := path.Join(tmpDir, "mnt1"), path.Join(tmpDir, "mnt2")
4435+
if err := os.Mkdir(mnt1, 0755); err != nil {
4436+
c.Fatal(err)
4437+
}
4438+
if err := os.Mkdir(mnt2, 0755); err != nil {
4439+
c.Fatal(err)
4440+
}
4441+
if err := ioutil.WriteFile(path.Join(mnt1, "test1"), []byte("test1"), 0644); err != nil {
4442+
c.Fatal(err)
4443+
}
4444+
if err := ioutil.WriteFile(path.Join(mnt2, "test2"), []byte("test2"), 0644); err != nil {
4445+
c.Fatal(err)
4446+
}
4447+
testCatFooBar := func(cName string) error {
4448+
out, _ := dockerCmd(c, "exec", cName, "cat", "/foo/test1")
4449+
if out != "test1" {
4450+
return fmt.Errorf("%s not mounted on /foo", mnt1)
4451+
}
4452+
out, _ = dockerCmd(c, "exec", cName, "cat", "/bar/test2")
4453+
if out != "test2" {
4454+
return fmt.Errorf("%s not mounted on /bar", mnt2)
4455+
}
4456+
return nil
4457+
}
4458+
4459+
type testCase struct {
4460+
equivalents [][]string
4461+
valid bool
4462+
// fn should be nil if valid==false
4463+
fn func(cName string) error
4464+
}
4465+
cases := []testCase{
4466+
{
4467+
equivalents: [][]string{
4468+
{
4469+
"--mount", fmt.Sprintf("type=bind,src=%s,dst=/foo", mnt1),
4470+
"--mount", fmt.Sprintf("type=bind,src=%s,dst=/bar", mnt2),
4471+
},
4472+
{
4473+
"--mount", fmt.Sprintf("type=bind,src=%s,dst=/foo", mnt1),
4474+
"--mount", fmt.Sprintf("type=bind,src=%s,target=/bar", mnt2),
4475+
},
4476+
{
4477+
"--volume", mnt1 + ":/foo",
4478+
"--mount", fmt.Sprintf("type=bind,src=%s,target=/bar", mnt2),
4479+
},
4480+
},
4481+
valid: true,
4482+
fn: testCatFooBar,
4483+
},
4484+
{
4485+
equivalents: [][]string{
4486+
{
4487+
"--mount", fmt.Sprintf("type=volume,src=%s,dst=/foo", mnt1),
4488+
"--mount", fmt.Sprintf("type=volume,src=%s,dst=/bar", mnt2),
4489+
},
4490+
{
4491+
"--mount", fmt.Sprintf("type=volume,src=%s,dst=/foo", mnt1),
4492+
"--mount", fmt.Sprintf("type=volume,src=%s,target=/bar", mnt2),
4493+
},
4494+
},
4495+
valid: false,
4496+
},
4497+
{
4498+
equivalents: [][]string{
4499+
{
4500+
"--mount", fmt.Sprintf("type=bind,src=%s,dst=/foo", mnt1),
4501+
"--mount", fmt.Sprintf("type=volume,src=%s,dst=/bar", mnt2),
4502+
},
4503+
{
4504+
"--volume", mnt1 + ":/foo",
4505+
"--mount", fmt.Sprintf("type=volume,src=%s,target=/bar", mnt2),
4506+
},
4507+
},
4508+
valid: false,
4509+
fn: testCatFooBar,
4510+
},
4511+
{
4512+
equivalents: [][]string{
4513+
{
4514+
"--read-only",
4515+
"--mount", "type=volume,dst=/bar",
4516+
},
4517+
},
4518+
valid: true,
4519+
fn: func(cName string) error {
4520+
_, _, err := dockerCmdWithError("exec", cName, "touch", "/bar/icanwritehere")
4521+
return err
4522+
},
4523+
},
4524+
{
4525+
equivalents: [][]string{
4526+
{
4527+
"--read-only",
4528+
"--mount", fmt.Sprintf("type=bind,src=%s,dst=/foo", mnt1),
4529+
"--mount", "type=volume,dst=/bar",
4530+
},
4531+
{
4532+
"--read-only",
4533+
"--volume", fmt.Sprintf("%s:/foo", mnt1),
4534+
"--mount", "type=volume,dst=/bar",
4535+
},
4536+
},
4537+
valid: true,
4538+
fn: func(cName string) error {
4539+
out, _ := dockerCmd(c, "exec", cName, "cat", "/foo/test1")
4540+
if out != "test1" {
4541+
return fmt.Errorf("%s not mounted on /foo", mnt1)
4542+
}
4543+
_, _, err := dockerCmdWithError("exec", cName, "touch", "/bar/icanwritehere")
4544+
return err
4545+
},
4546+
},
4547+
{
4548+
equivalents: [][]string{
4549+
{
4550+
"--mount", fmt.Sprintf("type=bind,src=%s,dst=/foo", mnt1),
4551+
"--mount", fmt.Sprintf("type=bind,src=%s,dst=/foo", mnt2),
4552+
},
4553+
{
4554+
"--mount", fmt.Sprintf("type=bind,src=%s,dst=/foo", mnt1),
4555+
"--mount", fmt.Sprintf("type=bind,src=%s,target=/foo", mnt2),
4556+
},
4557+
{
4558+
"--volume", fmt.Sprintf("%s:/foo", mnt1),
4559+
"--mount", fmt.Sprintf("type=bind,src=%s,target=/foo", mnt2),
4560+
},
4561+
},
4562+
valid: false,
4563+
},
4564+
{
4565+
equivalents: [][]string{
4566+
{
4567+
"--volume", fmt.Sprintf("%s:/foo", mnt1),
4568+
"--mount", fmt.Sprintf("type=volume,src=%s,target=/foo", mnt2),
4569+
},
4570+
},
4571+
valid: false,
4572+
},
4573+
{
4574+
equivalents: [][]string{
4575+
{
4576+
"--mount", "type=volume,target=/foo",
4577+
"--mount", "type=volume,target=/foo",
4578+
},
4579+
},
4580+
valid: false,
4581+
},
4582+
}
4583+
4584+
for i, testCase := range cases {
4585+
for j, opts := range testCase.equivalents {
4586+
cName := fmt.Sprintf("mount-%d-%d", i, j)
4587+
_, _, err := dockerCmdWithError(append([]string{"run", "-i", "-d", "--name", cName},
4588+
append(opts, []string{"busybox", "top"}...)...)...)
4589+
if testCase.valid {
4590+
c.Assert(err, check.IsNil,
4591+
check.Commentf("got error while creating a container with %v (%s)", opts, cName))
4592+
c.Assert(testCase.fn(cName), check.IsNil,
4593+
check.Commentf("got error while executing test for %v (%s)", opts, cName))
4594+
dockerCmd(c, "rm", "-f", cName)
4595+
} else {
4596+
c.Assert(err, checker.NotNil,
4597+
check.Commentf("got nil while creating a container with %v (%s)", opts, cName))
4598+
}
4599+
}
4600+
}
4601+
}
4602+
44254603
// Test that passing a FQDN as hostname properly sets hostname, and
44264604
// /etc/hostname. Test case for 29100
44274605
func (s *DockerSuite) TestRunHostnameFQDN(c *check.C) {

man/docker-run.1.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ docker-run - Run a command in a new container
6161
[**--memory-reservation**[=*MEMORY-RESERVATION*]]
6262
[**--memory-swap**[=*LIMIT*]]
6363
[**--memory-swappiness**[=*MEMORY-SWAPPINESS*]]
64+
[**--mount**[=*[MOUNT]*]]
6465
[**--name**[=*NAME*]]
6566
[**--network-alias**[=*[]*]]
6667
[**--network**[=*"bridge"*]]
@@ -425,6 +426,42 @@ unit, `b` is used. Set LIMIT to `-1` to enable unlimited swap.
425426
The IPv6 link-local address will be based on the device's MAC address
426427
according to RFC4862.
427428

429+
**--mount**=[*[type=TYPE[,TYPE-SPECIFIC-OPTIONS]]*]
430+
Attach a filesystem mount to the container
431+
432+
Current supported mount `TYPES` are `bind`, `volume`, and `tmpfs`.
433+
434+
e.g.
435+
436+
`type=bind,source=/path/on/host,destination=/path/in/container`
437+
438+
`type=volume,source=my-volume,destination=/path/in/container,volume-label="color=red",volume-label="shape=round"`
439+
440+
`type=tmpfs,tmpfs-size=512M,destination=/path/in/container`
441+
442+
Common Options:
443+
444+
* `src`, `source`: mount source spec for `bind` and `volume`. Mandatory for `bind`.
445+
* `dst`, `destination`, `target`: mount destination spec.
446+
* `ro`, `read-only`: `true` or `false` (default).
447+
448+
Options specific to `bind`:
449+
450+
* `bind-propagation`: `shared`, `slave`, `private`, `rshared`, `rslave`, or `rprivate`(default). See also `mount(2)`.
451+
* `consistency`: `consistent`(default), `cached`, or `delegated`. Currently, only effective for Docker for Mac.
452+
453+
Options specific to `volume`:
454+
455+
* `volume-driver`: Name of the volume-driver plugin.
456+
* `volume-label`: Custom metadata.
457+
* `volume-nocopy`: `true`(default) or `false`. If set to `false`, the Engine copies existing files and directories under the mount-path into the volume, allowing the host to access them.
458+
* `volume-opt`: specific to a given volume driver.
459+
460+
Options specific to `tmpfs`:
461+
462+
* `tmpfs-size`: Size of the tmpfs mount in bytes. Unlimited by default in Linux.
463+
* `tmpfs-mode`: File mode of the tmpfs in octal. (e.g. `700` or `0700`.) Defaults to `1777` in Linux.
464+
428465
**--name**=""
429466
Assign a name to the container
430467

@@ -604,6 +641,9 @@ options are the same as the Linux default `mount` flags. If you do not specify
604641
any options, the systems uses the following options:
605642
`rw,noexec,nosuid,nodev,size=65536k`.
606643

644+
See also `--mount`, which is the successor of `--tmpfs` and `--volume`.
645+
Even though there is no plan to deprecate `--tmpfs`, usage of `--mount` is recommended.
646+
607647
**-u**, **--user**=""
608648
Sets the username or UID used and optionally the groupname or GID for the specified command.
609649

@@ -704,6 +744,9 @@ change propagation properties of source mount. Say `/` is source mount for
704744
To disable automatic copying of data from the container path to the volume, use
705745
the `nocopy` flag. The `nocopy` flag can be set on bind mounts and named volumes.
706746

747+
See also `--mount`, which is the successor of `--tmpfs` and `--volume`.
748+
Even though there is no plan to deprecate `--volume`, usage of `--mount` is recommended.
749+
707750
**--volume-driver**=""
708751
Container's volume driver. This driver creates volumes specified either from
709752
a Dockerfile's `VOLUME` instruction or from the `docker run -v` flag.

0 commit comments

Comments
 (0)