Add tmpfs as a valid volume source command.
| ... | ... |
@@ -1394,6 +1394,7 @@ _docker_run() {
|
| 1394 | 1394 |
--restart |
| 1395 | 1395 |
--security-opt |
| 1396 | 1396 |
--stop-signal |
| 1397 |
+ --tmpfs |
|
| 1397 | 1398 |
--ulimit |
| 1398 | 1399 |
--user -u |
| 1399 | 1400 |
--uts |
| ... | ... |
@@ -1443,7 +1444,7 @@ _docker_run() {
|
| 1443 | 1443 |
_filedir |
| 1444 | 1444 |
return |
| 1445 | 1445 |
;; |
| 1446 |
- --device|--volume|-v) |
|
| 1446 |
+ --device|--tmpfs|--volume|-v) |
|
| 1447 | 1447 |
case "$cur" in |
| 1448 | 1448 |
*:*) |
| 1449 | 1449 |
# 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 |
| 339 | 339 |
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l stop-signal -d 'Signal to kill a container' |
| 340 | 340 |
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s t -l tty -d 'Allocate a pseudo-TTY' |
| 341 | 341 |
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s u -l user -d 'Username or UID' |
| 342 |
+complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l tmpfs -d 'Mount tmpfs on a directory' |
|
| 342 | 343 |
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)' |
| 343 | 344 |
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l volumes-from -d 'Mount volumes from the specified container(s)' |
| 344 | 345 |
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() {
|
| 491 | 491 |
"($help)*--security-opt=[Security options]:security option: " |
| 492 | 492 |
"($help -t --tty)"{-t,--tty}"[Allocate a pseudo-tty]"
|
| 493 | 493 |
"($help -u --user)"{-u=,--user=}"[Username or UID]:user:_users"
|
| 494 |
+ "($help)--tmpfs[mount tmpfs] " |
|
| 494 | 495 |
"($help)*-v[Bind mount a volume]:volume: " |
| 495 | 496 |
"($help)--volume-driver=[Optional volume driver for the container]:volume driver:(local)" |
| 496 | 497 |
"($help)*--volumes-from=[Mount volumes from the specified container]:volume: " |
| ... | ... |
@@ -1534,3 +1534,15 @@ func (container *Container) unmountVolumes(forceSyscall bool) error {
|
| 1534 | 1534 |
|
| 1535 | 1535 |
return nil |
| 1536 | 1536 |
} |
| 1537 |
+ |
|
| 1538 |
+func (container *Container) tmpfsMounts() []execdriver.Mount {
|
|
| 1539 |
+ var mounts []execdriver.Mount |
|
| 1540 |
+ for dest, data := range container.hostConfig.Tmpfs {
|
|
| 1541 |
+ mounts = append(mounts, execdriver.Mount{
|
|
| 1542 |
+ Source: "tmpfs", |
|
| 1543 |
+ Destination: dest, |
|
| 1544 |
+ Data: data, |
|
| 1545 |
+ }) |
|
| 1546 |
+ } |
|
| 1547 |
+ return mounts |
|
| 1548 |
+} |
| ... | ... |
@@ -191,6 +191,10 @@ func (container *Container) ipcMounts() []execdriver.Mount {
|
| 191 | 191 |
return nil |
| 192 | 192 |
} |
| 193 | 193 |
|
| 194 |
+func (container *Container) tmpfsMounts() []execdriver.Mount {
|
|
| 195 |
+ return nil |
|
| 196 |
+} |
|
| 197 |
+ |
|
| 194 | 198 |
func getDefaultRouteMtu() (int, error) {
|
| 195 | 199 |
return -1, errSystemNotSupported |
| 196 | 200 |
} |
| ... | ... |
@@ -137,4 +137,5 @@ type CommonCommand struct {
|
| 137 | 137 |
Resources *Resources `json:"resources"` |
| 138 | 138 |
Rootfs string `json:"rootfs"` // root fs of the container |
| 139 | 139 |
WorkingDir string `json:"working_dir"` |
| 140 |
+ TmpDir string `json:"tmpdir"` // Directory used to store docker tmpdirs. |
|
| 140 | 141 |
} |
| ... | ... |
@@ -4,10 +4,13 @@ package native |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 | 6 |
"fmt" |
| 7 |
+ "path/filepath" |
|
| 7 | 8 |
"strings" |
| 8 | 9 |
"syscall" |
| 9 | 10 |
|
| 10 | 11 |
"github.com/docker/docker/daemon/execdriver" |
| 12 |
+ derr "github.com/docker/docker/errors" |
|
| 13 |
+ "github.com/docker/docker/pkg/mount" |
|
| 11 | 14 |
|
| 12 | 15 |
"github.com/opencontainers/runc/libcontainer/apparmor" |
| 13 | 16 |
"github.com/opencontainers/runc/libcontainer/configs" |
| ... | ... |
@@ -288,6 +291,36 @@ func (d *Driver) setupMounts(container *configs.Config, c *execdriver.Command) e |
| 288 | 288 |
container.Mounts = defaultMounts |
| 289 | 289 |
|
| 290 | 290 |
for _, m := range c.Mounts {
|
| 291 |
+ for _, cm := range container.Mounts {
|
|
| 292 |
+ if cm.Destination == m.Destination {
|
|
| 293 |
+ return derr.ErrorCodeMountDup.WithArgs(m.Destination) |
|
| 294 |
+ } |
|
| 295 |
+ } |
|
| 296 |
+ |
|
| 297 |
+ if m.Source == "tmpfs" {
|
|
| 298 |
+ var ( |
|
| 299 |
+ data = "size=65536k" |
|
| 300 |
+ flags = syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV |
|
| 301 |
+ err error |
|
| 302 |
+ ) |
|
| 303 |
+ fulldest := filepath.Join(c.Rootfs, m.Destination) |
|
| 304 |
+ if m.Data != "" {
|
|
| 305 |
+ flags, data, err = mount.ParseTmpfsOptions(m.Data) |
|
| 306 |
+ if err != nil {
|
|
| 307 |
+ return err |
|
| 308 |
+ } |
|
| 309 |
+ } |
|
| 310 |
+ container.Mounts = append(container.Mounts, &configs.Mount{
|
|
| 311 |
+ Source: m.Source, |
|
| 312 |
+ Destination: m.Destination, |
|
| 313 |
+ Data: data, |
|
| 314 |
+ Device: "tmpfs", |
|
| 315 |
+ Flags: flags, |
|
| 316 |
+ PremountCmds: genTmpfsPremountCmd(c.TmpDir, fulldest, m.Destination), |
|
| 317 |
+ PostmountCmds: genTmpfsPostmountCmd(c.TmpDir, fulldest, m.Destination), |
|
| 318 |
+ }) |
|
| 319 |
+ continue |
|
| 320 |
+ } |
|
| 291 | 321 |
flags := syscall.MS_BIND | syscall.MS_REC |
| 292 | 322 |
if !m.Writable {
|
| 293 | 323 |
flags |= syscall.MS_RDONLY |
| ... | ... |
@@ -5,6 +5,7 @@ package native |
| 5 | 5 |
import ( |
| 6 | 6 |
"fmt" |
| 7 | 7 |
"io" |
| 8 |
+ "io/ioutil" |
|
| 8 | 9 |
"os" |
| 9 | 10 |
"os/exec" |
| 10 | 11 |
"path/filepath" |
| ... | ... |
@@ -128,6 +129,13 @@ type execOutput struct {
|
| 128 | 128 |
// it calls libcontainer APIs to run a container. |
| 129 | 129 |
func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, hooks execdriver.Hooks) (execdriver.ExitStatus, error) {
|
| 130 | 130 |
destroyed := false |
| 131 |
+ var err error |
|
| 132 |
+ c.TmpDir, err = ioutil.TempDir("", c.ID)
|
|
| 133 |
+ if err != nil {
|
|
| 134 |
+ return execdriver.ExitStatus{ExitCode: -1}, err
|
|
| 135 |
+ } |
|
| 136 |
+ defer os.RemoveAll(c.TmpDir) |
|
| 137 |
+ |
|
| 131 | 138 |
// take the Command and populate the libcontainer.Config from it |
| 132 | 139 |
container, err := d.createContainer(c, hooks) |
| 133 | 140 |
if err != nil {
|
| 134 | 141 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,56 @@ |
| 0 |
+package native |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "os" |
|
| 5 |
+ "os/exec" |
|
| 6 |
+ "strings" |
|
| 7 |
+ |
|
| 8 |
+ "github.com/Sirupsen/logrus" |
|
| 9 |
+ "github.com/opencontainers/runc/libcontainer/configs" |
|
| 10 |
+) |
|
| 11 |
+ |
|
| 12 |
+func genTmpfsPremountCmd(tmpDir string, fullDest string, dest string) []configs.Command {
|
|
| 13 |
+ var premount []configs.Command |
|
| 14 |
+ tarPath, err := exec.LookPath("tar")
|
|
| 15 |
+ if err != nil {
|
|
| 16 |
+ logrus.Warn("tar command is not available for tmpfs mount: %s", err)
|
|
| 17 |
+ return premount |
|
| 18 |
+ } |
|
| 19 |
+ if _, err = exec.LookPath("rm"); err != nil {
|
|
| 20 |
+ logrus.Warn("rm command is not available for tmpfs mount: %s", err)
|
|
| 21 |
+ return premount |
|
| 22 |
+ } |
|
| 23 |
+ tarFile := fmt.Sprintf("%s/%s.tar", tmpDir, strings.Replace(dest, "/", "_", -1))
|
|
| 24 |
+ if _, err := os.Stat(fullDest); err == nil {
|
|
| 25 |
+ premount = append(premount, configs.Command{
|
|
| 26 |
+ Path: tarPath, |
|
| 27 |
+ Args: []string{"-cf", tarFile, "-C", fullDest, "."},
|
|
| 28 |
+ }) |
|
| 29 |
+ } |
|
| 30 |
+ return premount |
|
| 31 |
+} |
|
| 32 |
+ |
|
| 33 |
+func genTmpfsPostmountCmd(tmpDir string, fullDest string, dest string) []configs.Command {
|
|
| 34 |
+ var postmount []configs.Command |
|
| 35 |
+ tarPath, err := exec.LookPath("tar")
|
|
| 36 |
+ if err != nil {
|
|
| 37 |
+ return postmount |
|
| 38 |
+ } |
|
| 39 |
+ rmPath, err := exec.LookPath("rm")
|
|
| 40 |
+ if err != nil {
|
|
| 41 |
+ return postmount |
|
| 42 |
+ } |
|
| 43 |
+ if _, err := os.Stat(fullDest); os.IsNotExist(err) {
|
|
| 44 |
+ return postmount |
|
| 45 |
+ } |
|
| 46 |
+ tarFile := fmt.Sprintf("%s/%s.tar", tmpDir, strings.Replace(dest, "/", "_", -1))
|
|
| 47 |
+ postmount = append(postmount, configs.Command{
|
|
| 48 |
+ Path: tarPath, |
|
| 49 |
+ Args: []string{"-xf", tarFile, "-C", fullDest, "."},
|
|
| 50 |
+ }) |
|
| 51 |
+ return append(postmount, configs.Command{
|
|
| 52 |
+ Path: rmPath, |
|
| 53 |
+ Args: []string{"-f", tarFile},
|
|
| 54 |
+ }) |
|
| 55 |
+} |
| ... | ... |
@@ -130,6 +130,7 @@ func (daemon *Daemon) containerStart(container *Container) (err error) {
|
| 130 | 130 |
return err |
| 131 | 131 |
} |
| 132 | 132 |
mounts = append(mounts, container.ipcMounts()...) |
| 133 |
+ mounts = append(mounts, container.tmpfsMounts()...) |
|
| 133 | 134 |
|
| 134 | 135 |
container.command.Mounts = mounts |
| 135 | 136 |
if err := daemon.waitForStart(container); err != nil {
|
| ... | ... |
@@ -121,7 +121,7 @@ func (daemon *Daemon) registerMountPoints(container *Container, hostConfig *runc |
| 121 | 121 |
} |
| 122 | 122 |
|
| 123 | 123 |
if binds[bind.Destination] {
|
| 124 |
- return derr.ErrorCodeVolumeDup.WithArgs(bind.Destination) |
|
| 124 |
+ return derr.ErrorCodeMountDup.WithArgs(bind.Destination) |
|
| 125 | 125 |
} |
| 126 | 126 |
|
| 127 | 127 |
if len(bind.Name) > 0 && len(bind.Driver) > 0 {
|
| ... | ... |
@@ -153,6 +153,14 @@ flag exists to allow special use-cases, like running Docker within Docker. |
| 153 | 153 |
The `-w` lets the command being executed inside directory given, here |
| 154 | 154 |
`/path/to/dir/`. If the path does not exists it is created inside the container. |
| 155 | 155 |
|
| 156 |
+### mount tmpfs (--tmpfs) |
|
| 157 |
+ |
|
| 158 |
+ $ docker run -d --tmpfs /run:rw,noexec,nosuid,size=65536k my_image |
|
| 159 |
+ |
|
| 160 |
+ The --tmpfs flag mounts a tmpfs into the container with the rw,noexec,nosuid,size=65536k options. |
|
| 161 |
+ |
|
| 162 |
+ Underlying content from the /run in the my_image image is copied into tmpfs. |
|
| 163 |
+ |
|
| 156 | 164 |
### Mount volume (-v, --read-only) |
| 157 | 165 |
|
| 158 | 166 |
$ 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`: |
| 1298 | 1298 |
|
| 1299 | 1299 |
Similarly the operator can set the **hostname** with `-h`. |
| 1300 | 1300 |
|
| 1301 |
+### TMPFS (mount tmpfs filesystems) |
|
| 1302 |
+ |
|
| 1303 |
+ --tmpfs=[]: Create a tmpfs mount with: container-dir[:<options>], where the options are identical to the Linux `mount -t tmpfs -o` command. |
|
| 1304 |
+ |
|
| 1305 |
+ Underlying content from the "container-dir" is copied into tmpfs. |
|
| 1306 |
+ |
|
| 1307 |
+ $ docker run -d --tmpfs /run:rw,noexec,nosuid,size=65536k my_image |
|
| 1308 |
+ |
|
| 1301 | 1309 |
### VOLUME (shared filesystems) |
| 1302 | 1310 |
|
| 1303 | 1311 |
-v=[]: Create a bind mount with: [host-src:]container-dest[:<options>], where |
| ... | ... |
@@ -444,12 +444,12 @@ var ( |
| 444 | 444 |
HTTPStatusCode: http.StatusInternalServerError, |
| 445 | 445 |
}) |
| 446 | 446 |
|
| 447 |
- // ErrorCodeVolumeDup is generated when we try to mount two volumes |
|
| 447 |
+ // ErrorCodeMountDup is generated when we try to mount two mounts points |
|
| 448 | 448 |
// to the same path. |
| 449 |
- ErrorCodeVolumeDup = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
|
| 450 |
- Value: "VOLUMEDUP", |
|
| 451 |
- Message: "Duplicate bind mount '%s'", |
|
| 452 |
- Description: "An attempt was made to mount a volume but the specified destination location is already used in a previous mount", |
|
| 449 |
+ ErrorCodeMountDup = errcode.Register(errGroup, errcode.ErrorDescriptor{
|
|
| 450 |
+ Value: "MOUNTDUP", |
|
| 451 |
+ Message: "Duplicate mount point '%s'", |
|
| 452 |
+ Description: "An attempt was made to mount a content but the specified destination location is already used in a previous mount", |
|
| 453 | 453 |
HTTPStatusCode: http.StatusInternalServerError, |
| 454 | 454 |
}) |
| 455 | 455 |
|
| ... | ... |
@@ -212,7 +212,7 @@ func (s *DockerSuite) TestContainerApiStartDupVolumeBinds(c *check.C) {
|
| 212 | 212 |
status, body, err := sockRequest("POST", "/containers/"+name+"/start", config)
|
| 213 | 213 |
c.Assert(err, checker.IsNil) |
| 214 | 214 |
c.Assert(status, checker.Equals, http.StatusInternalServerError) |
| 215 |
- 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))
|
|
| 215 |
+ 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))
|
|
| 216 | 216 |
} |
| 217 | 217 |
|
| 218 | 218 |
func (s *DockerSuite) TestContainerApiStartVolumesFrom(c *check.C) {
|
| ... | ... |
@@ -375,10 +375,10 @@ func (s *DockerSuite) TestRunNoDupVolumes(c *check.C) {
|
| 375 | 375 |
mountstr2 := path2 + someplace |
| 376 | 376 |
|
| 377 | 377 |
if out, _, err := dockerCmdWithError("run", "-v", mountstr1, "-v", mountstr2, "busybox", "true"); err == nil {
|
| 378 |
- c.Fatal("Expected error about duplicate volume definitions")
|
|
| 378 |
+ c.Fatal("Expected error about duplicate mount definitions")
|
|
| 379 | 379 |
} else {
|
| 380 |
- if !strings.Contains(out, "Duplicate bind mount") {
|
|
| 381 |
- c.Fatalf("Expected 'duplicate volume' error, got %v", out)
|
|
| 380 |
+ if !strings.Contains(out, "Duplicate mount point") {
|
|
| 381 |
+ c.Fatalf("Expected 'duplicate mount point' error, got %v", out)
|
|
| 382 | 382 |
} |
| 383 | 383 |
} |
| 384 | 384 |
} |
| ... | ... |
@@ -438,3 +438,21 @@ func (s *DockerSuite) TestRunWithShmSize(c *check.C) {
|
| 438 | 438 |
c.Assert(err, check.IsNil) |
| 439 | 439 |
c.Assert(shmSize, check.Equals, "1073741824") |
| 440 | 440 |
} |
| 441 |
+ |
|
| 442 |
+func (s *DockerSuite) TestRunTmpfsMounts(c *check.C) {
|
|
| 443 |
+ // TODO Windows (Post TP4): This test cannot run on a Windows daemon as |
|
| 444 |
+ // Windows does not support tmpfs mounts. |
|
| 445 |
+ testRequires(c, DaemonIsLinux) |
|
| 446 |
+ if out, _, err := dockerCmdWithError("run", "--tmpfs", "/run", "busybox", "touch", "/run/somefile"); err != nil {
|
|
| 447 |
+ c.Fatalf("/run directory not mounted on tmpfs %q %s", err, out)
|
|
| 448 |
+ } |
|
| 449 |
+ if out, _, err := dockerCmdWithError("run", "--tmpfs", "/run:noexec,nosuid,rw,size=5k,mode=700", "busybox", "touch", "/run/somefile"); err != nil {
|
|
| 450 |
+ c.Fatalf("/run failed to mount on tmpfs with valid options %q %s", err, out)
|
|
| 451 |
+ } |
|
| 452 |
+ if _, _, err := dockerCmdWithError("run", "--tmpfs", "/run:foobar", "busybox", "touch", "/run/somefile"); err == nil {
|
|
| 453 |
+ c.Fatalf("/run mounted on tmpfs when it should have vailed within invalid mount option")
|
|
| 454 |
+ } |
|
| 455 |
+ if _, _, err := dockerCmdWithError("run", "--tmpfs", "/run", "-v", "/run:/run", "busybox", "touch", "/run/somefile"); err == nil {
|
|
| 456 |
+ c.Fatalf("Should have generated an error saying Duplicate mount points")
|
|
| 457 |
+ } |
|
| 458 |
+} |
| ... | ... |
@@ -57,6 +57,7 @@ docker-create - Create a new container |
| 57 | 57 |
[**--stop-signal**[=*SIGNAL*]] |
| 58 | 58 |
[**--shm-size**[=*[]*]] |
| 59 | 59 |
[**-t**|**--tty**[=*false*]] |
| 60 |
+[**--tmpfs**[=*[CONTAINER-DIR[:<OPTIONS>]*]] |
|
| 60 | 61 |
[**-u**|**--user**[=*USER*]] |
| 61 | 62 |
[**--ulimit**[=*[]*]] |
| 62 | 63 |
[**--uts**[=*[]*]] |
| ... | ... |
@@ -271,6 +272,20 @@ This value should always larger than **-m**, so you should always use this with |
| 271 | 271 |
**-t**, **--tty**=*true*|*false* |
| 272 | 272 |
Allocate a pseudo-TTY. The default is *false*. |
| 273 | 273 |
|
| 274 |
+**--tmpfs**=[] Create a tmpfs mount |
|
| 275 |
+ |
|
| 276 |
+ Mount a temporary filesystem (`tmpfs`) mount into a container, for example: |
|
| 277 |
+ |
|
| 278 |
+ $ docker run -d --tmpfs /tmp:rw,size=787448k,mode=1777 my_image |
|
| 279 |
+ |
|
| 280 |
+ This command mounts a `tmpfs` at `/tmp` within the container. The mount copies |
|
| 281 |
+the underlying content of `my_image` into `/tmp`. For example if there was a |
|
| 282 |
+directory `/tmp/content` in the base image, docker will copy this directory and |
|
| 283 |
+all of its content on top of the tmpfs mounted on `/tmp`. The supported mount |
|
| 284 |
+options are the same as the Linux default `mount` flags. If you do not specify |
|
| 285 |
+any options, the systems uses the following options: |
|
| 286 |
+`rw,noexec,nosuid,nodev,size=65536k`. |
|
| 287 |
+ |
|
| 274 | 288 |
**-u**, **--user**="" |
| 275 | 289 |
Username or UID |
| 276 | 290 |
|
| ... | ... |
@@ -60,6 +60,7 @@ docker-run - Run a command in a new container |
| 60 | 60 |
[**--shm-size**[=*[]*]] |
| 61 | 61 |
[**--sig-proxy**[=*true*]] |
| 62 | 62 |
[**-t**|**--tty**[=*false*]] |
| 63 |
+[**--tmpfs**[=*[CONTAINER-DIR[:<OPTIONS>]*]] |
|
| 63 | 64 |
[**-u**|**--user**[=*USER*]] |
| 64 | 65 |
[**-v**|**--volume**[=*[]*]] |
| 65 | 66 |
[**--ulimit**[=*[]*]] |
| ... | ... |
@@ -436,6 +437,20 @@ interactive shell. The default is false. |
| 436 | 436 |
The **-t** option is incompatible with a redirection of the docker client |
| 437 | 437 |
standard input. |
| 438 | 438 |
|
| 439 |
+**--tmpfs**=[] Create a tmpfs mount |
|
| 440 |
+ |
|
| 441 |
+ Mount a temporary filesystem (`tmpfs`) mount into a container, for example: |
|
| 442 |
+ |
|
| 443 |
+ $ docker run -d --tmpfs /tmp:rw,size=787448k,mode=1777 my_image |
|
| 444 |
+ |
|
| 445 |
+ This command mounts a `tmpfs` at `/tmp` within the container. The mount copies |
|
| 446 |
+the underlying content of `my_image` into `/tmp`. For example if there was a |
|
| 447 |
+directory `/tmp/content` in the base image, docker will copy this directory and |
|
| 448 |
+all of its content on top of the tmpfs mounted on `/tmp`. The supported mount |
|
| 449 |
+options are the same as the Linux default `mount` flags. If you do not specify |
|
| 450 |
+any options, the systems uses the following options: |
|
| 451 |
+`rw,noexec,nosuid,nodev,size=65536k`. |
|
| 452 |
+ |
|
| 439 | 453 |
**-u**, **--user**="" |
| 440 | 454 |
Sets the username or UID used and optionally the groupname or GID for the specified command. |
| 441 | 455 |
|
| ... | ... |
@@ -552,6 +567,19 @@ the exit codes follow the `chroot` standard, see below: |
| 552 | 552 |
|
| 553 | 553 |
# EXAMPLES |
| 554 | 554 |
|
| 555 |
+## Running container in read-only mode |
|
| 556 |
+ |
|
| 557 |
+During container image development, containers often need to write to the image |
|
| 558 |
+content. Installing packages into /usr, for example. In production, |
|
| 559 |
+applications seldom need to write to the image. Container applications write |
|
| 560 |
+to volumes if they need to write to file systems at all. Applications can be |
|
| 561 |
+made more secure by running them in read-only mode using the --read-only switch. |
|
| 562 |
+This protects the containers image from modification. Read only containers may |
|
| 563 |
+still need to write temporary data. The best way to handle this is to mount |
|
| 564 |
+tmpfs directories on /run and /tmp. |
|
| 565 |
+ |
|
| 566 |
+ # docker run --read-only --tmpfs /run --tmpfs /tmp -i -t fedora /bin/bash |
|
| 567 |
+ |
|
| 555 | 568 |
## Exposing log messages from the container to the host's log |
| 556 | 569 |
|
| 557 | 570 |
If you want messages that are logged in your container to show up in the host's |
| ... | ... |
@@ -1,6 +1,7 @@ |
| 1 | 1 |
package mount |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "fmt" |
|
| 4 | 5 |
"strings" |
| 5 | 6 |
) |
| 6 | 7 |
|
| ... | ... |
@@ -67,3 +68,24 @@ func parseOptions(options string) (int, string) {
|
| 67 | 67 |
} |
| 68 | 68 |
return flag, strings.Join(data, ",") |
| 69 | 69 |
} |
| 70 |
+ |
|
| 71 |
+// ParseTmpfsOptions parse fstab type mount options into flags and data |
|
| 72 |
+func ParseTmpfsOptions(options string) (int, string, error) {
|
|
| 73 |
+ flags, data := parseOptions(options) |
|
| 74 |
+ validFlags := map[string]bool{
|
|
| 75 |
+ "size": true, |
|
| 76 |
+ "mode": true, |
|
| 77 |
+ "uid": true, |
|
| 78 |
+ "gid": true, |
|
| 79 |
+ "nr_inodes": true, |
|
| 80 |
+ "nr_blocks": true, |
|
| 81 |
+ "mpol": true, |
|
| 82 |
+ } |
|
| 83 |
+ for _, o := range strings.Split(data, ",") {
|
|
| 84 |
+ opt := strings.SplitN(o, "=", 2) |
|
| 85 |
+ if !validFlags[opt[0]] {
|
|
| 86 |
+ return 0, "", fmt.Errorf("Invalid tmpfs option %q", opt)
|
|
| 87 |
+ } |
|
| 88 |
+ } |
|
| 89 |
+ return flags, data, nil |
|
| 90 |
+} |
| ... | ... |
@@ -217,6 +217,7 @@ type HostConfig struct {
|
| 217 | 217 |
PublishAllPorts bool // Should docker publish all exposed port for the container |
| 218 | 218 |
ReadonlyRootfs bool // Is the container root filesystem in read-only |
| 219 | 219 |
SecurityOpt []string // List of string values to customize labels for MLS systems, such as SELinux. |
| 220 |
+ Tmpfs map[string]string `json:",omitempty"` // List of tmpfs (mounts) used for the container |
|
| 220 | 221 |
UTSMode UTSMode // UTS namespace to use for the container |
| 221 | 222 |
ShmSize *int64 // Total shm memory usage |
| 222 | 223 |
|
| ... | ... |
@@ -7,6 +7,7 @@ import ( |
| 7 | 7 |
|
| 8 | 8 |
"github.com/docker/docker/opts" |
| 9 | 9 |
flag "github.com/docker/docker/pkg/mflag" |
| 10 |
+ "github.com/docker/docker/pkg/mount" |
|
| 10 | 11 |
"github.com/docker/docker/pkg/nat" |
| 11 | 12 |
"github.com/docker/docker/pkg/parsers" |
| 12 | 13 |
"github.com/docker/docker/pkg/signal" |
| ... | ... |
@@ -50,6 +51,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe |
| 50 | 50 |
// FIXME: use utils.ListOpts for attach and volumes? |
| 51 | 51 |
flAttach = opts.NewListOpts(opts.ValidateAttach) |
| 52 | 52 |
flVolumes = opts.NewListOpts(nil) |
| 53 |
+ flTmpfs = opts.NewListOpts(nil) |
|
| 53 | 54 |
flBlkioWeightDevice = opts.NewWeightdeviceOpt(opts.ValidateWeightDevice) |
| 54 | 55 |
flLinks = opts.NewListOpts(opts.ValidateLink) |
| 55 | 56 |
flEnv = opts.NewListOpts(opts.ValidateEnv) |
| ... | ... |
@@ -111,6 +113,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe |
| 111 | 111 |
cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to STDIN, STDOUT or STDERR")
|
| 112 | 112 |
cmd.Var(&flBlkioWeightDevice, []string{"-blkio-weight-device"}, "Block IO weight (relative device weight)")
|
| 113 | 113 |
cmd.Var(&flVolumes, []string{"v", "-volume"}, "Bind mount a volume")
|
| 114 |
+ cmd.Var(&flTmpfs, []string{"-tmpfs"}, "Mount a tmpfs directory")
|
|
| 114 | 115 |
cmd.Var(&flLinks, []string{"-link"}, "Add link to another container")
|
| 115 | 116 |
cmd.Var(&flDevices, []string{"-device"}, "Add a host device to the container")
|
| 116 | 117 |
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 |
| 221 | 221 |
} |
| 222 | 222 |
} |
| 223 | 223 |
|
| 224 |
+ // Can't evalute options passed into --tmpfs until we actually mount |
|
| 225 |
+ tmpfs := make(map[string]string) |
|
| 226 |
+ for _, t := range flTmpfs.GetAll() {
|
|
| 227 |
+ if arr := strings.SplitN(t, ":", 2); len(arr) > 1 {
|
|
| 228 |
+ if _, _, err := mount.ParseTmpfsOptions(arr[1]); err != nil {
|
|
| 229 |
+ return nil, nil, cmd, err |
|
| 230 |
+ } |
|
| 231 |
+ tmpfs[arr[0]] = arr[1] |
|
| 232 |
+ } else {
|
|
| 233 |
+ tmpfs[arr[0]] = "" |
|
| 234 |
+ } |
|
| 235 |
+ } |
|
| 236 |
+ |
|
| 224 | 237 |
var ( |
| 225 | 238 |
parsedArgs = cmd.Args() |
| 226 | 239 |
runCmd *stringutils.StrSlice |
| ... | ... |
@@ -396,6 +412,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe |
| 396 | 396 |
Isolation: IsolationLevel(*flIsolation), |
| 397 | 397 |
ShmSize: parsedShm, |
| 398 | 398 |
Resources: resources, |
| 399 |
+ Tmpfs: tmpfs, |
|
| 399 | 400 |
} |
| 400 | 401 |
|
| 401 | 402 |
// When allocating stdin in attached mode, close stdin at client disconnect |