Prechádzať zdrojové kódy

Merge pull request #32251 from AkihiroSuda/run-mount

cli: add `--mount` to `docker run`
Vincent Demeester 8 rokov pred
rodič
commit
945a119c8a

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

@@ -11,6 +11,7 @@ import (
 	"strings"
 	"time"
 
+	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/types/container"
 	networktypes "github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/api/types/strslice"
@@ -31,6 +32,7 @@ type containerOptions struct {
 	attach             opts.ListOpts
 	volumes            opts.ListOpts
 	tmpfs              opts.ListOpts
+	mounts             opts.MountOpt
 	blkioWeightDevice  opts.WeightdeviceOpt
 	deviceReadBps      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.volumesFrom, "volumes-from", "Mount volumes from the specified container(s)")
 	flags.VarP(&copts.volumes, "volume", "v", "Bind mount a volume")
+	flags.Var(&copts.mounts, "mount", "Attach a filesystem mount to the container")
 
 	// Health-checking
 	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)
 	}
 
+	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
 	volumes := copts.volumes.GetMap()
 	// 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,
 		Sysctls:        copts.sysctls.GetAll(),
 		Runtime:        copts.runtime,
+		Mounts:         mounts,
 	}
 
 	if copts.autoRemove && !hostConfig.RestartPolicy.IsNone() {

+ 1 - 0
contrib/completion/bash/docker

@@ -1518,6 +1518,7 @@ _docker_container_run_and_create() {
 		--memory-swap
 		--memory-swappiness
 		--memory-reservation
+		--mount
 		--name
 		--network
 		--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' -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 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 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'
@@ -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' -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 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 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'

+ 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-opt=[Log driver specific options]:log driver options:__docker_complete_log_options"
         "($help)--mac-address=[Container MAC address]:MAC address: "
+        "($help)*--mount=[Attach a filesystem mount to the container]:mount: "
         "($help)--name=[Container name]:name: "
         "($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: "

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

@@ -85,6 +85,7 @@ Options:
       --memory-reservation string   Memory soft limit
       --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)
+      --mount value                 Attach a filesytem mount to the container (default [])
       --name string                 Assign a name to the container
       --network-alias value         Add network-scoped alias for the container (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-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)
+      --mount value                 Attach a filesystem mount to the container (default [])
       --name string                 Assign a name to the container
       --network-alias value         Add network-scoped alias for the container (default [])
       --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/)
 
+
+### 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)
 
 ```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")
 }
 
+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
 // /etc/hostname. Test case for 29100
 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-swap**[=*LIMIT*]]
 [**--memory-swappiness**[=*MEMORY-SWAPPINESS*]]
+[**--mount**[=*[MOUNT]*]]
 [**--name**[=*NAME*]]
 [**--network-alias**[=*[]*]]
 [**--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
 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**=""
    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:
 `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**=""
    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
 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**=""
    Container's volume driver. This driver creates volumes specified either from
    a Dockerfile's `VOLUME` instruction or from the `docker run -v` flag.