This patch adds --tmpfs as a option for mounting tmpfs on directories
It will Tar up contents of child directory onto tmpfs if mounted over This patch will use the new PreMount and PostMount hooks to "tar" up the contents of the base image on top of tmpfs mount points. Signed-off-by: Dan Walsh <dwalsh@redhat.com>
This commit is contained in:
parent
db6c4c99d8
commit
b3e527dfd2
24 changed files with 248 additions and 11 deletions
|
@ -66,6 +66,7 @@ RUN apt-get update && apt-get install -y \
|
||||||
ubuntu-zfs \
|
ubuntu-zfs \
|
||||||
xfsprogs \
|
xfsprogs \
|
||||||
libzfs-dev \
|
libzfs-dev \
|
||||||
|
tar \
|
||||||
--no-install-recommends \
|
--no-install-recommends \
|
||||||
&& ln -snf /usr/bin/clang-3.8 /usr/local/bin/clang \
|
&& ln -snf /usr/bin/clang-3.8 /usr/local/bin/clang \
|
||||||
&& ln -snf /usr/bin/clang++-3.8 /usr/local/bin/clang++
|
&& ln -snf /usr/bin/clang++-3.8 /usr/local/bin/clang++
|
||||||
|
|
|
@ -1394,6 +1394,7 @@ _docker_run() {
|
||||||
--restart
|
--restart
|
||||||
--security-opt
|
--security-opt
|
||||||
--stop-signal
|
--stop-signal
|
||||||
|
--tmpfs
|
||||||
--ulimit
|
--ulimit
|
||||||
--user -u
|
--user -u
|
||||||
--uts
|
--uts
|
||||||
|
@ -1443,7 +1444,7 @@ _docker_run() {
|
||||||
_filedir
|
_filedir
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
--device|--volume|-v)
|
--device|--tmpfs|--volume|-v)
|
||||||
case "$cur" in
|
case "$cur" in
|
||||||
*:*)
|
*:*)
|
||||||
# TODO somehow do _filedir for stuff inside the image, if it's already specified (which is also somewhat difficult to determine)
|
# TODO somehow do _filedir for stuff inside the image, if it's already specified (which is also somewhat difficult to determine)
|
||||||
|
|
|
@ -339,6 +339,7 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l sig-proxy -d 'P
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l stop-signal -d 'Signal to kill a container'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l stop-signal -d 'Signal to kill a container'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s t -l tty -d 'Allocate a pseudo-TTY'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s t -l tty -d 'Allocate a pseudo-TTY'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s u -l user -d 'Username or UID'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s u -l user -d 'Username or UID'
|
||||||
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l tmpfs -d 'Mount tmpfs on a directory'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s v -l volume -d 'Bind mount a volume (e.g., from the host: -v /host:/container, from Docker: -v /container)'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s v -l volume -d 'Bind mount a volume (e.g., from the host: -v /host:/container, from Docker: -v /container)'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l volumes-from -d 'Mount volumes from the specified container(s)'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l volumes-from -d 'Mount volumes from the specified container(s)'
|
||||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s w -l workdir -d 'Working directory inside the container'
|
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s w -l workdir -d 'Working directory inside the container'
|
||||||
|
|
|
@ -491,6 +491,7 @@ __docker_subcommand() {
|
||||||
"($help)*--security-opt=[Security options]:security option: "
|
"($help)*--security-opt=[Security options]:security option: "
|
||||||
"($help -t --tty)"{-t,--tty}"[Allocate a pseudo-tty]"
|
"($help -t --tty)"{-t,--tty}"[Allocate a pseudo-tty]"
|
||||||
"($help -u --user)"{-u=,--user=}"[Username or UID]:user:_users"
|
"($help -u --user)"{-u=,--user=}"[Username or UID]:user:_users"
|
||||||
|
"($help)--tmpfs[mount tmpfs] "
|
||||||
"($help)*-v[Bind mount a volume]:volume: "
|
"($help)*-v[Bind mount a volume]:volume: "
|
||||||
"($help)--volume-driver=[Optional volume driver for the container]:volume driver:(local)"
|
"($help)--volume-driver=[Optional volume driver for the container]:volume driver:(local)"
|
||||||
"($help)*--volumes-from=[Mount volumes from the specified container]:volume: "
|
"($help)*--volumes-from=[Mount volumes from the specified container]:volume: "
|
||||||
|
|
|
@ -1534,3 +1534,15 @@ func (container *Container) unmountVolumes(forceSyscall bool) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (container *Container) tmpfsMounts() []execdriver.Mount {
|
||||||
|
var mounts []execdriver.Mount
|
||||||
|
for dest, data := range container.hostConfig.Tmpfs {
|
||||||
|
mounts = append(mounts, execdriver.Mount{
|
||||||
|
Source: "tmpfs",
|
||||||
|
Destination: dest,
|
||||||
|
Data: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return mounts
|
||||||
|
}
|
||||||
|
|
|
@ -191,6 +191,10 @@ func (container *Container) ipcMounts() []execdriver.Mount {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (container *Container) tmpfsMounts() []execdriver.Mount {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func getDefaultRouteMtu() (int, error) {
|
func getDefaultRouteMtu() (int, error) {
|
||||||
return -1, errSystemNotSupported
|
return -1, errSystemNotSupported
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,4 +137,5 @@ type CommonCommand struct {
|
||||||
Resources *Resources `json:"resources"`
|
Resources *Resources `json:"resources"`
|
||||||
Rootfs string `json:"rootfs"` // root fs of the container
|
Rootfs string `json:"rootfs"` // root fs of the container
|
||||||
WorkingDir string `json:"working_dir"`
|
WorkingDir string `json:"working_dir"`
|
||||||
|
TmpDir string `json:"tmpdir"` // Directory used to store docker tmpdirs.
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ type Mount struct {
|
||||||
Writable bool `json:"writable"`
|
Writable bool `json:"writable"`
|
||||||
Private bool `json:"private"`
|
Private bool `json:"private"`
|
||||||
Slave bool `json:"slave"`
|
Slave bool `json:"slave"`
|
||||||
|
Data string `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resources contains all resource configs for a driver.
|
// Resources contains all resource configs for a driver.
|
||||||
|
|
|
@ -4,10 +4,13 @@ package native
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/docker/docker/daemon/execdriver"
|
"github.com/docker/docker/daemon/execdriver"
|
||||||
|
derr "github.com/docker/docker/errors"
|
||||||
|
"github.com/docker/docker/pkg/mount"
|
||||||
|
|
||||||
"github.com/opencontainers/runc/libcontainer/apparmor"
|
"github.com/opencontainers/runc/libcontainer/apparmor"
|
||||||
"github.com/opencontainers/runc/libcontainer/configs"
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
@ -288,6 +291,36 @@ func (d *Driver) setupMounts(container *configs.Config, c *execdriver.Command) e
|
||||||
container.Mounts = defaultMounts
|
container.Mounts = defaultMounts
|
||||||
|
|
||||||
for _, m := range c.Mounts {
|
for _, m := range c.Mounts {
|
||||||
|
for _, cm := range container.Mounts {
|
||||||
|
if cm.Destination == m.Destination {
|
||||||
|
return derr.ErrorCodeMountDup.WithArgs(m.Destination)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.Source == "tmpfs" {
|
||||||
|
var (
|
||||||
|
data = "size=65536k"
|
||||||
|
flags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
fulldest := filepath.Join(c.Rootfs, m.Destination)
|
||||||
|
if m.Data != "" {
|
||||||
|
flags, data, err = mount.ParseTmpfsOptions(m.Data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
container.Mounts = append(container.Mounts, &configs.Mount{
|
||||||
|
Source: m.Source,
|
||||||
|
Destination: m.Destination,
|
||||||
|
Data: data,
|
||||||
|
Device: "tmpfs",
|
||||||
|
Flags: flags,
|
||||||
|
PremountCmds: genTmpfsPremountCmd(c.TmpDir, fulldest, m.Destination),
|
||||||
|
PostmountCmds: genTmpfsPostmountCmd(c.TmpDir, fulldest, m.Destination),
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
flags := syscall.MS_BIND | syscall.MS_REC
|
flags := syscall.MS_BIND | syscall.MS_REC
|
||||||
if !m.Writable {
|
if !m.Writable {
|
||||||
flags |= syscall.MS_RDONLY
|
flags |= syscall.MS_RDONLY
|
||||||
|
|
|
@ -5,6 +5,7 @@ package native
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -128,6 +129,13 @@ type execOutput struct {
|
||||||
// it calls libcontainer APIs to run a container.
|
// it calls libcontainer APIs to run a container.
|
||||||
func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, hooks execdriver.Hooks) (execdriver.ExitStatus, error) {
|
func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, hooks execdriver.Hooks) (execdriver.ExitStatus, error) {
|
||||||
destroyed := false
|
destroyed := false
|
||||||
|
var err error
|
||||||
|
c.TmpDir, err = ioutil.TempDir("", c.ID)
|
||||||
|
if err != nil {
|
||||||
|
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(c.TmpDir)
|
||||||
|
|
||||||
// take the Command and populate the libcontainer.Config from it
|
// take the Command and populate the libcontainer.Config from it
|
||||||
container, err := d.createContainer(c, hooks)
|
container, err := d.createContainer(c, hooks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
56
daemon/execdriver/native/tmpfs.go
Normal file
56
daemon/execdriver/native/tmpfs.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package native
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
)
|
||||||
|
|
||||||
|
func genTmpfsPremountCmd(tmpDir string, fullDest string, dest string) []configs.Command {
|
||||||
|
var premount []configs.Command
|
||||||
|
tarPath, err := exec.LookPath("tar")
|
||||||
|
if err != nil {
|
||||||
|
logrus.Warn("tar command is not available for tmpfs mount: %s", err)
|
||||||
|
return premount
|
||||||
|
}
|
||||||
|
if _, err = exec.LookPath("rm"); err != nil {
|
||||||
|
logrus.Warn("rm command is not available for tmpfs mount: %s", err)
|
||||||
|
return premount
|
||||||
|
}
|
||||||
|
tarFile := fmt.Sprintf("%s/%s.tar", tmpDir, strings.Replace(dest, "/", "_", -1))
|
||||||
|
if _, err := os.Stat(fullDest); err == nil {
|
||||||
|
premount = append(premount, configs.Command{
|
||||||
|
Path: tarPath,
|
||||||
|
Args: []string{"-cf", tarFile, "-C", fullDest, "."},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return premount
|
||||||
|
}
|
||||||
|
|
||||||
|
func genTmpfsPostmountCmd(tmpDir string, fullDest string, dest string) []configs.Command {
|
||||||
|
var postmount []configs.Command
|
||||||
|
tarPath, err := exec.LookPath("tar")
|
||||||
|
if err != nil {
|
||||||
|
return postmount
|
||||||
|
}
|
||||||
|
rmPath, err := exec.LookPath("rm")
|
||||||
|
if err != nil {
|
||||||
|
return postmount
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(fullDest); os.IsNotExist(err) {
|
||||||
|
return postmount
|
||||||
|
}
|
||||||
|
tarFile := fmt.Sprintf("%s/%s.tar", tmpDir, strings.Replace(dest, "/", "_", -1))
|
||||||
|
postmount = append(postmount, configs.Command{
|
||||||
|
Path: tarPath,
|
||||||
|
Args: []string{"-xf", tarFile, "-C", fullDest, "."},
|
||||||
|
})
|
||||||
|
return append(postmount, configs.Command{
|
||||||
|
Path: rmPath,
|
||||||
|
Args: []string{"-f", tarFile},
|
||||||
|
})
|
||||||
|
}
|
|
@ -130,6 +130,7 @@ func (daemon *Daemon) containerStart(container *Container) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
mounts = append(mounts, container.ipcMounts()...)
|
mounts = append(mounts, container.ipcMounts()...)
|
||||||
|
mounts = append(mounts, container.tmpfsMounts()...)
|
||||||
|
|
||||||
container.command.Mounts = mounts
|
container.command.Mounts = mounts
|
||||||
if err := daemon.waitForStart(container); err != nil {
|
if err := daemon.waitForStart(container); err != nil {
|
||||||
|
|
|
@ -121,7 +121,7 @@ func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runc
|
||||||
}
|
}
|
||||||
|
|
||||||
if binds[bind.Destination] {
|
if binds[bind.Destination] {
|
||||||
return derr.ErrorCodeVolumeDup.WithArgs(bind.Destination)
|
return derr.ErrorCodeMountDup.WithArgs(bind.Destination)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(bind.Name) > 0 && len(bind.Driver) > 0 {
|
if len(bind.Name) > 0 && len(bind.Driver) > 0 {
|
||||||
|
|
|
@ -152,6 +152,14 @@ flag exists to allow special use-cases, like running Docker within Docker.
|
||||||
The `-w` lets the command being executed inside directory given, here
|
The `-w` lets the command being executed inside directory given, here
|
||||||
`/path/to/dir/`. If the path does not exists it is created inside the container.
|
`/path/to/dir/`. If the path does not exists it is created inside the container.
|
||||||
|
|
||||||
|
### mount tmpfs (--tmpfs)
|
||||||
|
|
||||||
|
$ docker run -d --tmpfs /run:rw,noexec,nosuid,size=65536k my_image
|
||||||
|
|
||||||
|
The --tmpfs flag mounts a tmpfs into the container with the rw,noexec,nosuid,size=65536k options.
|
||||||
|
|
||||||
|
Underlying content from the /run in the my_image image is copied into tmpfs.
|
||||||
|
|
||||||
### Mount volume (-v, --read-only)
|
### Mount volume (-v, --read-only)
|
||||||
|
|
||||||
$ docker run -v `pwd`:`pwd` -w `pwd` -i -t ubuntu pwd
|
$ docker run -v `pwd`:`pwd` -w `pwd` -i -t ubuntu pwd
|
||||||
|
|
|
@ -1298,6 +1298,14 @@ above, or already defined by the developer with a Dockerfile `ENV`:
|
||||||
|
|
||||||
Similarly the operator can set the **hostname** with `-h`.
|
Similarly the operator can set the **hostname** with `-h`.
|
||||||
|
|
||||||
|
### TMPFS (mount tmpfs filesystems)
|
||||||
|
|
||||||
|
--tmpfs=[]: Create a tmpfs mount with: container-dir[:<options>], where the options are identical to the Linux `mount -t tmpfs -o` command.
|
||||||
|
|
||||||
|
Underlying content from the "container-dir" is copied into tmpfs.
|
||||||
|
|
||||||
|
$ docker run -d --tmpfs /run:rw,noexec,nosuid,size=65536k my_image
|
||||||
|
|
||||||
### VOLUME (shared filesystems)
|
### VOLUME (shared filesystems)
|
||||||
|
|
||||||
-v=[]: Create a bind mount with: [host-src:]container-dest[:<options>], where
|
-v=[]: Create a bind mount with: [host-src:]container-dest[:<options>], where
|
||||||
|
|
|
@ -444,12 +444,12 @@ var (
|
||||||
HTTPStatusCode: http.StatusInternalServerError,
|
HTTPStatusCode: http.StatusInternalServerError,
|
||||||
})
|
})
|
||||||
|
|
||||||
// ErrorCodeVolumeDup is generated when we try to mount two volumes
|
// ErrorCodeMountDup is generated when we try to mount two mounts points
|
||||||
// to the same path.
|
// to the same path.
|
||||||
ErrorCodeVolumeDup = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
ErrorCodeMountDup = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
||||||
Value: "VOLUMEDUP",
|
Value: "MOUNTDUP",
|
||||||
Message: "Duplicate bind mount '%s'",
|
Message: "Duplicate mount point '%s'",
|
||||||
Description: "An attempt was made to mount a volume but the specified destination location is already used in a previous mount",
|
Description: "An attempt was made to mount a content but the specified destination location is already used in a previous mount",
|
||||||
HTTPStatusCode: http.StatusInternalServerError,
|
HTTPStatusCode: http.StatusInternalServerError,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -212,7 +212,7 @@ func (s *DockerSuite) TestContainerApiStartDupVolumeBinds(c *check.C) {
|
||||||
status, body, err := sockRequest("POST", "/containers/"+name+"/start", config)
|
status, body, err := sockRequest("POST", "/containers/"+name+"/start", config)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
c.Assert(status, checker.Equals, http.StatusInternalServerError)
|
c.Assert(status, checker.Equals, http.StatusInternalServerError)
|
||||||
c.Assert(string(body), checker.Contains, "Duplicate bind", check.Commentf("Expected failure due to duplicate bind mounts to same path, instead got: %q with error: %v", string(body), err))
|
c.Assert(string(body), checker.Contains, "Duplicate mount point", check.Commentf("Expected failure due to duplicate bind mounts to same path, instead got: %q with error: %v", string(body), err))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerSuite) TestContainerApiStartVolumesFrom(c *check.C) {
|
func (s *DockerSuite) TestContainerApiStartVolumesFrom(c *check.C) {
|
||||||
|
|
|
@ -375,10 +375,10 @@ func (s *DockerSuite) TestRunNoDupVolumes(c *check.C) {
|
||||||
mountstr2 := path2 + someplace
|
mountstr2 := path2 + someplace
|
||||||
|
|
||||||
if out, _, err := dockerCmdWithError("run", "-v", mountstr1, "-v", mountstr2, "busybox", "true"); err == nil {
|
if out, _, err := dockerCmdWithError("run", "-v", mountstr1, "-v", mountstr2, "busybox", "true"); err == nil {
|
||||||
c.Fatal("Expected error about duplicate volume definitions")
|
c.Fatal("Expected error about duplicate mount definitions")
|
||||||
} else {
|
} else {
|
||||||
if !strings.Contains(out, "Duplicate bind mount") {
|
if !strings.Contains(out, "Duplicate mount point") {
|
||||||
c.Fatalf("Expected 'duplicate volume' error, got %v", out)
|
c.Fatalf("Expected 'duplicate mount point' error, got %v", out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -438,3 +438,21 @@ func (s *DockerSuite) TestRunWithShmSize(c *check.C) {
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
c.Assert(shmSize, check.Equals, "1073741824")
|
c.Assert(shmSize, check.Equals, "1073741824")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DockerSuite) TestRunTmpfsMounts(c *check.C) {
|
||||||
|
// TODO Windows (Post TP4): This test cannot run on a Windows daemon as
|
||||||
|
// Windows does not support tmpfs mounts.
|
||||||
|
testRequires(c, DaemonIsLinux)
|
||||||
|
if out, _, err := dockerCmdWithError("run", "--tmpfs", "/run", "busybox", "touch", "/run/somefile"); err != nil {
|
||||||
|
c.Fatalf("/run directory not mounted on tmpfs %q %s", err, out)
|
||||||
|
}
|
||||||
|
if out, _, err := dockerCmdWithError("run", "--tmpfs", "/run:noexec,nosuid,rw,size=5k,mode=700", "busybox", "touch", "/run/somefile"); err != nil {
|
||||||
|
c.Fatalf("/run failed to mount on tmpfs with valid options %q %s", err, out)
|
||||||
|
}
|
||||||
|
if _, _, err := dockerCmdWithError("run", "--tmpfs", "/run:foobar", "busybox", "touch", "/run/somefile"); err == nil {
|
||||||
|
c.Fatalf("/run mounted on tmpfs when it should have vailed within invalid mount option")
|
||||||
|
}
|
||||||
|
if _, _, err := dockerCmdWithError("run", "--tmpfs", "/run", "-v", "/run:/run", "busybox", "touch", "/run/somefile"); err == nil {
|
||||||
|
c.Fatalf("Should have generated an error saying Duplicate mount points")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ docker-create - Create a new container
|
||||||
[**--stop-signal**[=*SIGNAL*]]
|
[**--stop-signal**[=*SIGNAL*]]
|
||||||
[**--shm-size**[=*[]*]]
|
[**--shm-size**[=*[]*]]
|
||||||
[**-t**|**--tty**[=*false*]]
|
[**-t**|**--tty**[=*false*]]
|
||||||
|
[**--tmpfs**[=*[CONTAINER-DIR[:<OPTIONS>]*]]
|
||||||
[**-u**|**--user**[=*USER*]]
|
[**-u**|**--user**[=*USER*]]
|
||||||
[**--ulimit**[=*[]*]]
|
[**--ulimit**[=*[]*]]
|
||||||
[**--uts**[=*[]*]]
|
[**--uts**[=*[]*]]
|
||||||
|
@ -267,6 +268,20 @@ This value should always larger than **-m**, so you should always use this with
|
||||||
**-t**, **--tty**=*true*|*false*
|
**-t**, **--tty**=*true*|*false*
|
||||||
Allocate a pseudo-TTY. The default is *false*.
|
Allocate a pseudo-TTY. The default is *false*.
|
||||||
|
|
||||||
|
**--tmpfs**=[] Create a tmpfs mount
|
||||||
|
|
||||||
|
Mount a temporary filesystem (`tmpfs`) mount into a container, for example:
|
||||||
|
|
||||||
|
$ docker run -d --tmpfs /tmp:rw,size=787448k,mode=1777 my_image
|
||||||
|
|
||||||
|
This command mounts a `tmpfs` at `/tmp` within the container. The mount copies
|
||||||
|
the underlying content of `my_image` into `/tmp`. For example if there was a
|
||||||
|
directory `/tmp/content` in the base image, docker will copy this directory and
|
||||||
|
all of its content on top of the tmpfs mounted on `/tmp`. The supported mount
|
||||||
|
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`.
|
||||||
|
|
||||||
**-u**, **--user**=""
|
**-u**, **--user**=""
|
||||||
Username or UID
|
Username or UID
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,7 @@ docker-run - Run a command in a new container
|
||||||
[**--shm-size**[=*[]*]]
|
[**--shm-size**[=*[]*]]
|
||||||
[**--sig-proxy**[=*true*]]
|
[**--sig-proxy**[=*true*]]
|
||||||
[**-t**|**--tty**[=*false*]]
|
[**-t**|**--tty**[=*false*]]
|
||||||
|
[**--tmpfs**[=*[CONTAINER-DIR[:<OPTIONS>]*]]
|
||||||
[**-u**|**--user**[=*USER*]]
|
[**-u**|**--user**[=*USER*]]
|
||||||
[**-v**|**--volume**[=*[]*]]
|
[**-v**|**--volume**[=*[]*]]
|
||||||
[**--ulimit**[=*[]*]]
|
[**--ulimit**[=*[]*]]
|
||||||
|
@ -432,6 +433,20 @@ interactive shell. The default is false.
|
||||||
The **-t** option is incompatible with a redirection of the docker client
|
The **-t** option is incompatible with a redirection of the docker client
|
||||||
standard input.
|
standard input.
|
||||||
|
|
||||||
|
**--tmpfs**=[] Create a tmpfs mount
|
||||||
|
|
||||||
|
Mount a temporary filesystem (`tmpfs`) mount into a container, for example:
|
||||||
|
|
||||||
|
$ docker run -d --tmpfs /tmp:rw,size=787448k,mode=1777 my_image
|
||||||
|
|
||||||
|
This command mounts a `tmpfs` at `/tmp` within the container. The mount copies
|
||||||
|
the underlying content of `my_image` into `/tmp`. For example if there was a
|
||||||
|
directory `/tmp/content` in the base image, docker will copy this directory and
|
||||||
|
all of its content on top of the tmpfs mounted on `/tmp`. The supported mount
|
||||||
|
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`.
|
||||||
|
|
||||||
**-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.
|
||||||
|
|
||||||
|
@ -548,6 +563,19 @@ the exit codes follow the `chroot` standard, see below:
|
||||||
|
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
|
## Running container in read-only mode
|
||||||
|
|
||||||
|
During container image development, containers often need to write to the image
|
||||||
|
content. Installing packages into /usr, for example. In production,
|
||||||
|
applications seldom need to write to the image. Container applications write
|
||||||
|
to volumes if they need to write to file systems at all. Applications can be
|
||||||
|
made more secure by running them in read-only mode using the --read-only switch.
|
||||||
|
This protects the containers image from modification. Read only containers may
|
||||||
|
still need to write temporary data. The best way to handle this is to mount
|
||||||
|
tmpfs directories on /run and /tmp.
|
||||||
|
|
||||||
|
# docker run --read-only --tmpfs /run --tmpfs /tmp -i -t fedora /bin/bash
|
||||||
|
|
||||||
## Exposing log messages from the container to the host's log
|
## Exposing log messages from the container to the host's log
|
||||||
|
|
||||||
If you want messages that are logged in your container to show up in the host's
|
If you want messages that are logged in your container to show up in the host's
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package mount
|
package mount
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -67,3 +68,24 @@ func parseOptions(options string) (int, string) {
|
||||||
}
|
}
|
||||||
return flag, strings.Join(data, ",")
|
return flag, strings.Join(data, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseTmpfsOptions parse fstab type mount options into flags and data
|
||||||
|
func ParseTmpfsOptions(options string) (int, string, error) {
|
||||||
|
flags, data := parseOptions(options)
|
||||||
|
validFlags := map[string]bool{
|
||||||
|
"size": true,
|
||||||
|
"mode": true,
|
||||||
|
"uid": true,
|
||||||
|
"gid": true,
|
||||||
|
"nr_inodes": true,
|
||||||
|
"nr_blocks": true,
|
||||||
|
"mpol": true,
|
||||||
|
}
|
||||||
|
for _, o := range strings.Split(data, ",") {
|
||||||
|
opt := strings.SplitN(o, "=", 2)
|
||||||
|
if !validFlags[opt[0]] {
|
||||||
|
return 0, "", fmt.Errorf("Invalid tmpfs option %q", opt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flags, data, nil
|
||||||
|
}
|
||||||
|
|
|
@ -217,6 +217,7 @@ type HostConfig struct {
|
||||||
PublishAllPorts bool // Should docker publish all exposed port for the container
|
PublishAllPorts bool // Should docker publish all exposed port for the container
|
||||||
ReadonlyRootfs bool // Is the container root filesystem in read-only
|
ReadonlyRootfs bool // Is the container root filesystem in read-only
|
||||||
SecurityOpt []string // List of string values to customize labels for MLS systems, such as SELinux.
|
SecurityOpt []string // List of string values to customize labels for MLS systems, such as SELinux.
|
||||||
|
Tmpfs map[string]string `json:",omitempty"` // List of tmpfs (mounts) used for the container
|
||||||
UTSMode UTSMode // UTS namespace to use for the container
|
UTSMode UTSMode // UTS namespace to use for the container
|
||||||
ShmSize *int64 // Total shm memory usage
|
ShmSize *int64 // Total shm memory usage
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/docker/docker/opts"
|
"github.com/docker/docker/opts"
|
||||||
flag "github.com/docker/docker/pkg/mflag"
|
flag "github.com/docker/docker/pkg/mflag"
|
||||||
|
"github.com/docker/docker/pkg/mount"
|
||||||
"github.com/docker/docker/pkg/nat"
|
"github.com/docker/docker/pkg/nat"
|
||||||
"github.com/docker/docker/pkg/parsers"
|
"github.com/docker/docker/pkg/parsers"
|
||||||
"github.com/docker/docker/pkg/signal"
|
"github.com/docker/docker/pkg/signal"
|
||||||
|
@ -50,6 +51,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
|
||||||
// FIXME: use utils.ListOpts for attach and volumes?
|
// FIXME: use utils.ListOpts for attach and volumes?
|
||||||
flAttach = opts.NewListOpts(opts.ValidateAttach)
|
flAttach = opts.NewListOpts(opts.ValidateAttach)
|
||||||
flVolumes = opts.NewListOpts(nil)
|
flVolumes = opts.NewListOpts(nil)
|
||||||
|
flTmpfs = opts.NewListOpts(nil)
|
||||||
flBlkioWeightDevice = opts.NewWeightdeviceOpt(opts.ValidateWeightDevice)
|
flBlkioWeightDevice = opts.NewWeightdeviceOpt(opts.ValidateWeightDevice)
|
||||||
flLinks = opts.NewListOpts(opts.ValidateLink)
|
flLinks = opts.NewListOpts(opts.ValidateLink)
|
||||||
flEnv = opts.NewListOpts(opts.ValidateEnv)
|
flEnv = opts.NewListOpts(opts.ValidateEnv)
|
||||||
|
@ -111,6 +113,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
|
||||||
cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to STDIN, STDOUT or STDERR")
|
cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to STDIN, STDOUT or STDERR")
|
||||||
cmd.Var(&flBlkioWeightDevice, []string{"-blkio-weight-device"}, "Block IO weight (relative device weight)")
|
cmd.Var(&flBlkioWeightDevice, []string{"-blkio-weight-device"}, "Block IO weight (relative device weight)")
|
||||||
cmd.Var(&flVolumes, []string{"v", "-volume"}, "Bind mount a volume")
|
cmd.Var(&flVolumes, []string{"v", "-volume"}, "Bind mount a volume")
|
||||||
|
cmd.Var(&flTmpfs, []string{"-tmpfs"}, "Mount a tmpfs directory")
|
||||||
cmd.Var(&flLinks, []string{"-link"}, "Add link to another container")
|
cmd.Var(&flLinks, []string{"-link"}, "Add link to another container")
|
||||||
cmd.Var(&flDevices, []string{"-device"}, "Add a host device to the container")
|
cmd.Var(&flDevices, []string{"-device"}, "Add a host device to the container")
|
||||||
cmd.Var(&flLabels, []string{"l", "-label"}, "Set meta data on a container")
|
cmd.Var(&flLabels, []string{"l", "-label"}, "Set meta data on a container")
|
||||||
|
@ -221,6 +224,19 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Can't evalute options passed into --tmpfs until we actually mount
|
||||||
|
tmpfs := make(map[string]string)
|
||||||
|
for _, t := range flTmpfs.GetAll() {
|
||||||
|
if arr := strings.SplitN(t, ":", 2); len(arr) > 1 {
|
||||||
|
if _, _, err := mount.ParseTmpfsOptions(arr[1]); err != nil {
|
||||||
|
return nil, nil, cmd, err
|
||||||
|
}
|
||||||
|
tmpfs[arr[0]] = arr[1]
|
||||||
|
} else {
|
||||||
|
tmpfs[arr[0]] = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
parsedArgs = cmd.Args()
|
parsedArgs = cmd.Args()
|
||||||
runCmd *stringutils.StrSlice
|
runCmd *stringutils.StrSlice
|
||||||
|
@ -396,6 +412,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
|
||||||
Isolation: IsolationLevel(*flIsolation),
|
Isolation: IsolationLevel(*flIsolation),
|
||||||
ShmSize: parsedShm,
|
ShmSize: parsedShm,
|
||||||
Resources: resources,
|
Resources: resources,
|
||||||
|
Tmpfs: tmpfs,
|
||||||
}
|
}
|
||||||
|
|
||||||
// When allocating stdin in attached mode, close stdin at client disconnect
|
// When allocating stdin in attached mode, close stdin at client disconnect
|
||||||
|
|
Loading…
Reference in a new issue