瀏覽代碼

Merge pull request #32251 from AkihiroSuda/run-mount

cli: add `--mount` to `docker run`
Vincent Demeester 8 年之前
父節點
當前提交
945a119c8a

+ 8 - 0
cli/command/container/opts.go

@@ -11,6 +11,7 @@ import (
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
+	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/container"
 	networktypes "github.com/docker/docker/api/types/network"
 	networktypes "github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/api/types/strslice"
 	"github.com/docker/docker/api/types/strslice"
@@ -31,6 +32,7 @@ type containerOptions struct {
 	attach             opts.ListOpts
 	attach             opts.ListOpts
 	volumes            opts.ListOpts
 	volumes            opts.ListOpts
 	tmpfs              opts.ListOpts
 	tmpfs              opts.ListOpts
+	mounts             opts.MountOpt
 	blkioWeightDevice  opts.WeightdeviceOpt
 	blkioWeightDevice  opts.WeightdeviceOpt
 	deviceReadBps      opts.ThrottledeviceOpt
 	deviceReadBps      opts.ThrottledeviceOpt
 	deviceWriteBps     opts.ThrottledeviceOpt
 	deviceWriteBps     opts.ThrottledeviceOpt
@@ -223,6 +225,7 @@ func addFlags(flags *pflag.FlagSet) *containerOptions {
 	flags.Var(&copts.tmpfs, "tmpfs", "Mount a tmpfs directory")
 	flags.Var(&copts.tmpfs, "tmpfs", "Mount a tmpfs directory")
 	flags.Var(&copts.volumesFrom, "volumes-from", "Mount volumes from the specified container(s)")
 	flags.Var(&copts.volumesFrom, "volumes-from", "Mount volumes from the specified container(s)")
 	flags.VarP(&copts.volumes, "volume", "v", "Bind mount a volume")
 	flags.VarP(&copts.volumes, "volume", "v", "Bind mount a volume")
+	flags.Var(&copts.mounts, "mount", "Attach a filesystem mount to the container")
 
 
 	// Health-checking
 	// Health-checking
 	flags.StringVar(&copts.healthCmd, "health-cmd", "", "Command to run to check health")
 	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
 		return nil, errors.Errorf("invalid value: %d. Valid memory swappiness range is 0-100", swappiness)
 		return nil, errors.Errorf("invalid value: %d. Valid memory swappiness range is 0-100", swappiness)
 	}
 	}
 
 
+	mounts := copts.mounts.Value()
+	if len(mounts) > 0 && copts.volumeDriver != "" {
+		logrus.Warn("`--volume-driver` is ignored for volumes specified via `--mount`. Use `--mount type=volume,volume-driver=...` instead.")
+	}
 	var binds []string
 	var binds []string
 	volumes := copts.volumes.GetMap()
 	volumes := copts.volumes.GetMap()
 	// add any bind targets to the list of container volumes
 	// add any bind targets to the list of container volumes
@@ -589,6 +596,7 @@ func parse(flags *pflag.FlagSet, copts *containerOptions) (*containerConfig, err
 		Tmpfs:          tmpfs,
 		Tmpfs:          tmpfs,
 		Sysctls:        copts.sysctls.GetAll(),
 		Sysctls:        copts.sysctls.GetAll(),
 		Runtime:        copts.runtime,
 		Runtime:        copts.runtime,
+		Mounts:         mounts,
 	}
 	}
 
 
 	if copts.autoRemove && !hostConfig.RestartPolicy.IsNone() {
 	if copts.autoRemove && !hostConfig.RestartPolicy.IsNone() {

+ 1 - 0
contrib/completion/bash/docker

@@ -1518,6 +1518,7 @@ _docker_container_run_and_create() {
 		--memory-swap
 		--memory-swap
 		--memory-swappiness
 		--memory-swappiness
 		--memory-reservation
 		--memory-reservation
+		--mount
 		--name
 		--name
 		--network
 		--network
 		--network-alias
 		--network-alias

+ 2 - 0
contrib/completion/fish/docker.fish

@@ -138,6 +138,7 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l link -d 'Add
 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)'
 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)'
 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)'
 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)'
 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)"
 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)"
+complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l mount -d 'Attach a filesystem mount to the container'
 complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l name -d 'Assign a name to the container'
 complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l name -d 'Assign a name to the container'
 complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l net -d 'Set the Network mode for the container'
 complete -c docker -A -f -n '__fish_seen_subcommand_from create' -l net -d 'Set the Network mode for the container'
 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'
 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
 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)'
 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)'
 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)'
 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)'
 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)"
 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)"
+complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l mount -d 'Attach a filesystem mount to the container'
 complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l name -d 'Assign a name to the container'
 complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l name -d 'Assign a name to the container'
 complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l net -d 'Set the Network mode for the container'
 complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l net -d 'Set the Network mode for the container'
 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'
 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'

+ 1 - 0
contrib/completion/zsh/_docker

@@ -629,6 +629,7 @@ __docker_container_subcommand() {
         "($help)--log-driver=[Default driver for container logs]:logging driver:__docker_complete_log_drivers"
         "($help)--log-driver=[Default driver for container logs]:logging driver:__docker_complete_log_drivers"
         "($help)*--log-opt=[Log driver specific options]:log driver options:__docker_complete_log_options"
         "($help)*--log-opt=[Log driver specific options]:log driver options:__docker_complete_log_options"
         "($help)--mac-address=[Container MAC address]:MAC address: "
         "($help)--mac-address=[Container MAC address]:MAC address: "
+        "($help)*--mount=[Attach a filesystem mount to the container]:mount: "
         "($help)--name=[Container name]:name: "
         "($help)--name=[Container name]:name: "
         "($help)--network=[Connect a container to a network]:network mode:(bridge none container host)"
         "($help)--network=[Connect a container to a network]:network mode:(bridge none container host)"
         "($help)*--network-alias=[Add network-scoped alias for the container]:alias: "
         "($help)*--network-alias=[Add network-scoped alias for the container]:alias: "

+ 1 - 0
docs/reference/commandline/create.md

@@ -85,6 +85,7 @@ Options:
       --memory-reservation string   Memory soft limit
       --memory-reservation string   Memory soft limit
       --memory-swap string          Swap limit equal to memory plus swap: '-1' to enable unlimited swap
       --memory-swap string          Swap limit equal to memory plus swap: '-1' to enable unlimited swap
       --memory-swappiness int       Tune container memory swappiness (0 to 100) (default -1)
       --memory-swappiness int       Tune container memory swappiness (0 to 100) (default -1)
+      --mount value                 Attach a filesytem mount to the container (default [])
       --name string                 Assign a name to the container
       --name string                 Assign a name to the container
       --network-alias value         Add network-scoped alias for the container (default [])
       --network-alias value         Add network-scoped alias for the container (default [])
       --network string              Connect a container to a network (default "default")
       --network string              Connect a container to a network (default "default")

+ 24 - 0
docs/reference/commandline/run.md

@@ -95,6 +95,7 @@ Options:
       --memory-reservation string   Memory soft limit
       --memory-reservation string   Memory soft limit
       --memory-swap string          Swap limit equal to memory plus swap: '-1' to enable unlimited swap
       --memory-swap string          Swap limit equal to memory plus swap: '-1' to enable unlimited swap
       --memory-swappiness int       Tune container memory swappiness (0 to 100) (default -1)
       --memory-swappiness int       Tune container memory swappiness (0 to 100) (default -1)
+      --mount value                 Attach a filesystem mount to the container (default [])
       --name string                 Assign a name to the container
       --name string                 Assign a name to the container
       --network-alias value         Add network-scoped alias for the container (default [])
       --network-alias value         Add network-scoped alias for the container (default [])
       --network string              Connect a container to a network
       --network string              Connect a container to a network
@@ -316,6 +317,29 @@ docker run -v c:\foo:c:\existing-directory-with-contents ...
 
 
 For in-depth information about volumes, refer to [manage data in containers](https://docs.docker.com/engine/tutorials/dockervolumes/)
 For in-depth information about volumes, refer to [manage data in containers](https://docs.docker.com/engine/tutorials/dockervolumes/)
 
 
+
+### Add bind-mounts or volumes using the --mount flag
+
+The `--mount` flag allows you to mount volumes, host-directories and `tmpfs`
+mounts in a container.
+
+The `--mount` flag supports most options that are supported by the `-v` or the
+`--volume` flag, but uses a different syntax. For in-depth information on the
+`--mount` flag, and a comparison between `--volume` and `--mount`, refer to
+the [service create command reference](service_create.md#add-bind-mounts-or-volumes).
+
+Even though there is no plan to deprecate `--volume`, usage of `--mount` is recommended.
+
+Examples:
+
+```bash
+$ docker run --read-only --mount type=volume,target=/icanwrite busybox touch /icanwrite/here
+```
+
+```bash
+$ docker run -t -i --mount type=bind,src=/data,dst=/data busybox sh
+```
+
 ### Publish or expose port (-p, --expose)
 ### Publish or expose port (-p, --expose)
 
 
 ```bash
 ```bash

+ 178 - 0
integration-cli/docker_cli_run_test.go

@@ -4422,6 +4422,184 @@ func (s *DockerSuite) TestRunMountReadOnlyDevShm(c *check.C) {
 	c.Assert(out, checker.Contains, "Read-only file system")
 	c.Assert(out, checker.Contains, "Read-only file system")
 }
 }
 
 
+func (s *DockerSuite) TestRunMount(c *check.C) {
+	testRequires(c, DaemonIsLinux, SameHostDaemon, NotUserNamespace)
+
+	// mnt1, mnt2, and testCatFooBar are commonly used in multiple test cases
+	tmpDir, err := ioutil.TempDir("", "mount")
+	if err != nil {
+		c.Fatal(err)
+	}
+	defer os.RemoveAll(tmpDir)
+	mnt1, mnt2 := path.Join(tmpDir, "mnt1"), path.Join(tmpDir, "mnt2")
+	if err := os.Mkdir(mnt1, 0755); err != nil {
+		c.Fatal(err)
+	}
+	if err := os.Mkdir(mnt2, 0755); err != nil {
+		c.Fatal(err)
+	}
+	if err := ioutil.WriteFile(path.Join(mnt1, "test1"), []byte("test1"), 0644); err != nil {
+		c.Fatal(err)
+	}
+	if err := ioutil.WriteFile(path.Join(mnt2, "test2"), []byte("test2"), 0644); err != nil {
+		c.Fatal(err)
+	}
+	testCatFooBar := func(cName string) error {
+		out, _ := dockerCmd(c, "exec", cName, "cat", "/foo/test1")
+		if out != "test1" {
+			return fmt.Errorf("%s not mounted on /foo", mnt1)
+		}
+		out, _ = dockerCmd(c, "exec", cName, "cat", "/bar/test2")
+		if out != "test2" {
+			return fmt.Errorf("%s not mounted on /bar", mnt2)
+		}
+		return nil
+	}
+
+	type testCase struct {
+		equivalents [][]string
+		valid       bool
+		// fn should be nil if valid==false
+		fn func(cName string) error
+	}
+	cases := []testCase{
+		{
+			equivalents: [][]string{
+				{
+					"--mount", fmt.Sprintf("type=bind,src=%s,dst=/foo", mnt1),
+					"--mount", fmt.Sprintf("type=bind,src=%s,dst=/bar", mnt2),
+				},
+				{
+					"--mount", fmt.Sprintf("type=bind,src=%s,dst=/foo", mnt1),
+					"--mount", fmt.Sprintf("type=bind,src=%s,target=/bar", mnt2),
+				},
+				{
+					"--volume", mnt1 + ":/foo",
+					"--mount", fmt.Sprintf("type=bind,src=%s,target=/bar", mnt2),
+				},
+			},
+			valid: true,
+			fn:    testCatFooBar,
+		},
+		{
+			equivalents: [][]string{
+				{
+					"--mount", fmt.Sprintf("type=volume,src=%s,dst=/foo", mnt1),
+					"--mount", fmt.Sprintf("type=volume,src=%s,dst=/bar", mnt2),
+				},
+				{
+					"--mount", fmt.Sprintf("type=volume,src=%s,dst=/foo", mnt1),
+					"--mount", fmt.Sprintf("type=volume,src=%s,target=/bar", mnt2),
+				},
+			},
+			valid: false,
+		},
+		{
+			equivalents: [][]string{
+				{
+					"--mount", fmt.Sprintf("type=bind,src=%s,dst=/foo", mnt1),
+					"--mount", fmt.Sprintf("type=volume,src=%s,dst=/bar", mnt2),
+				},
+				{
+					"--volume", mnt1 + ":/foo",
+					"--mount", fmt.Sprintf("type=volume,src=%s,target=/bar", mnt2),
+				},
+			},
+			valid: false,
+			fn:    testCatFooBar,
+		},
+		{
+			equivalents: [][]string{
+				{
+					"--read-only",
+					"--mount", "type=volume,dst=/bar",
+				},
+			},
+			valid: true,
+			fn: func(cName string) error {
+				_, _, err := dockerCmdWithError("exec", cName, "touch", "/bar/icanwritehere")
+				return err
+			},
+		},
+		{
+			equivalents: [][]string{
+				{
+					"--read-only",
+					"--mount", fmt.Sprintf("type=bind,src=%s,dst=/foo", mnt1),
+					"--mount", "type=volume,dst=/bar",
+				},
+				{
+					"--read-only",
+					"--volume", fmt.Sprintf("%s:/foo", mnt1),
+					"--mount", "type=volume,dst=/bar",
+				},
+			},
+			valid: true,
+			fn: func(cName string) error {
+				out, _ := dockerCmd(c, "exec", cName, "cat", "/foo/test1")
+				if out != "test1" {
+					return fmt.Errorf("%s not mounted on /foo", mnt1)
+				}
+				_, _, err := dockerCmdWithError("exec", cName, "touch", "/bar/icanwritehere")
+				return err
+			},
+		},
+		{
+			equivalents: [][]string{
+				{
+					"--mount", fmt.Sprintf("type=bind,src=%s,dst=/foo", mnt1),
+					"--mount", fmt.Sprintf("type=bind,src=%s,dst=/foo", mnt2),
+				},
+				{
+					"--mount", fmt.Sprintf("type=bind,src=%s,dst=/foo", mnt1),
+					"--mount", fmt.Sprintf("type=bind,src=%s,target=/foo", mnt2),
+				},
+				{
+					"--volume", fmt.Sprintf("%s:/foo", mnt1),
+					"--mount", fmt.Sprintf("type=bind,src=%s,target=/foo", mnt2),
+				},
+			},
+			valid: false,
+		},
+		{
+			equivalents: [][]string{
+				{
+					"--volume", fmt.Sprintf("%s:/foo", mnt1),
+					"--mount", fmt.Sprintf("type=volume,src=%s,target=/foo", mnt2),
+				},
+			},
+			valid: false,
+		},
+		{
+			equivalents: [][]string{
+				{
+					"--mount", "type=volume,target=/foo",
+					"--mount", "type=volume,target=/foo",
+				},
+			},
+			valid: false,
+		},
+	}
+
+	for i, testCase := range cases {
+		for j, opts := range testCase.equivalents {
+			cName := fmt.Sprintf("mount-%d-%d", i, j)
+			_, _, err := dockerCmdWithError(append([]string{"run", "-i", "-d", "--name", cName},
+				append(opts, []string{"busybox", "top"}...)...)...)
+			if testCase.valid {
+				c.Assert(err, check.IsNil,
+					check.Commentf("got error while creating a container with %v (%s)", opts, cName))
+				c.Assert(testCase.fn(cName), check.IsNil,
+					check.Commentf("got error while executing test for %v (%s)", opts, cName))
+				dockerCmd(c, "rm", "-f", cName)
+			} else {
+				c.Assert(err, checker.NotNil,
+					check.Commentf("got nil while creating a container with %v (%s)", opts, cName))
+			}
+		}
+	}
+}
+
 // Test that passing a FQDN as hostname properly sets hostname, and
 // Test that passing a FQDN as hostname properly sets hostname, and
 // /etc/hostname. Test case for 29100
 // /etc/hostname. Test case for 29100
 func (s *DockerSuite) TestRunHostnameFQDN(c *check.C) {
 func (s *DockerSuite) TestRunHostnameFQDN(c *check.C) {

+ 43 - 0
man/docker-run.1.md

@@ -61,6 +61,7 @@ docker-run - Run a command in a new container
 [**--memory-reservation**[=*MEMORY-RESERVATION*]]
 [**--memory-reservation**[=*MEMORY-RESERVATION*]]
 [**--memory-swap**[=*LIMIT*]]
 [**--memory-swap**[=*LIMIT*]]
 [**--memory-swappiness**[=*MEMORY-SWAPPINESS*]]
 [**--memory-swappiness**[=*MEMORY-SWAPPINESS*]]
+[**--mount**[=*[MOUNT]*]]
 [**--name**[=*NAME*]]
 [**--name**[=*NAME*]]
 [**--network-alias**[=*[]*]]
 [**--network-alias**[=*[]*]]
 [**--network**[=*"bridge"*]]
 [**--network**[=*"bridge"*]]
@@ -425,6 +426,42 @@ unit, `b` is used. Set LIMIT to `-1` to enable unlimited swap.
 The IPv6 link-local address will be based on the device's MAC address
 The IPv6 link-local address will be based on the device's MAC address
 according to RFC4862.
 according to RFC4862.
 
 
+**--mount**=[*[type=TYPE[,TYPE-SPECIFIC-OPTIONS]]*]
+   Attach a filesystem mount to the container
+
+   Current supported mount `TYPES` are `bind`, `volume`, and `tmpfs`.
+
+   e.g.
+
+   `type=bind,source=/path/on/host,destination=/path/in/container`
+
+   `type=volume,source=my-volume,destination=/path/in/container,volume-label="color=red",volume-label="shape=round"`
+
+   `type=tmpfs,tmpfs-size=512M,destination=/path/in/container`
+
+   Common Options:
+
+   * `src`, `source`: mount source spec for `bind` and `volume`. Mandatory for `bind`.
+   * `dst`, `destination`, `target`: mount destination spec.
+   * `ro`, `read-only`: `true` or `false` (default).
+
+   Options specific to `bind`:
+
+   * `bind-propagation`: `shared`, `slave`, `private`, `rshared`, `rslave`, or `rprivate`(default). See also `mount(2)`.
+   * `consistency`: `consistent`(default), `cached`, or `delegated`. Currently, only effective for Docker for Mac.
+
+   Options specific to `volume`:
+
+   * `volume-driver`: Name of the volume-driver plugin.
+   * `volume-label`: Custom metadata.
+   * `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.
+   * `volume-opt`: specific to a given volume driver.
+
+   Options specific to `tmpfs`:
+
+   * `tmpfs-size`: Size of the tmpfs mount in bytes. Unlimited by default in Linux.
+   * `tmpfs-mode`: File mode of the tmpfs in octal. (e.g. `700` or `0700`.) Defaults to `1777` in Linux.
+
 **--name**=""
 **--name**=""
    Assign a name to the container
    Assign a name to the container
 
 
@@ -604,6 +641,9 @@ options are the same as the Linux default `mount` flags. If you do not specify
 any options, the systems uses the following options:
 any options, the systems uses the following options:
 `rw,noexec,nosuid,nodev,size=65536k`.
 `rw,noexec,nosuid,nodev,size=65536k`.
 
 
+   See also `--mount`, which is the successor of `--tmpfs` and `--volume`.
+   Even though there is no plan to deprecate `--tmpfs`, usage of `--mount` is recommended.
+
 **-u**, **--user**=""
 **-u**, **--user**=""
    Sets the username or UID used and optionally the groupname or GID for the specified command.
    Sets the username or UID used and optionally the groupname or GID for the specified command.
 
 
@@ -704,6 +744,9 @@ change propagation properties of source mount. Say `/` is source mount for
 To disable automatic copying of data from the container path to the volume, use
 To disable automatic copying of data from the container path to the volume, use
 the `nocopy` flag. The `nocopy` flag can be set on bind mounts and named volumes.
 the `nocopy` flag. The `nocopy` flag can be set on bind mounts and named volumes.
 
 
+See also `--mount`, which is the successor of `--tmpfs` and `--volume`.
+Even though there is no plan to deprecate `--volume`, usage of `--mount` is recommended.
+
 **--volume-driver**=""
 **--volume-driver**=""
    Container's volume driver. This driver creates volumes specified either from
    Container's volume driver. This driver creates volumes specified either from
    a Dockerfile's `VOLUME` instruction or from the `docker run -v` flag.
    a Dockerfile's `VOLUME` instruction or from the `docker run -v` flag.