From e47112d3e812b90b240624711e7a5d2d203d6746 Mon Sep 17 00:00:00 2001 From: Shishir Mahajan Date: Tue, 12 Jan 2016 13:44:13 -0500 Subject: [PATCH] daemon option (--storage-opt dm.basesize) for increasing the base device size on daemon restart Signed-off-by: Shishir Mahajan --- daemon/graphdriver/devmapper/deviceset.go | 83 ++++++++++++++++++- docs/reference/commandline/daemon.md | 18 +++- .../storagedriver/device-mapper-driver.md | 6 +- man/docker-daemon.8.md | 16 +++- 4 files changed, 110 insertions(+), 13 deletions(-) diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go index d9c3618cf0..cc6ad82913 100644 --- a/daemon/graphdriver/devmapper/deviceset.go +++ b/daemon/graphdriver/devmapper/deviceset.go @@ -35,7 +35,7 @@ import ( var ( defaultDataLoopbackSize int64 = 100 * 1024 * 1024 * 1024 defaultMetaDataLoopbackSize int64 = 2 * 1024 * 1024 * 1024 - defaultBaseFsSize uint64 = 100 * 1024 * 1024 * 1024 + defaultBaseFsSize uint64 = 10 * 1024 * 1024 * 1024 defaultThinpBlockSize uint32 = 128 // 64K = 128 512b sectors defaultUdevSyncOverride = false maxDeviceID = 0xffffff // 24 bit, pool limit @@ -47,6 +47,7 @@ var ( driverDeferredRemovalSupport = false enableDeferredRemoval = false enableDeferredDeletion = false + userBaseSize = false ) const deviceSetMetaFile string = "deviceset-metadata" @@ -1056,6 +1057,80 @@ func (devices *DeviceSet) setupVerifyBaseImageUUIDFS(baseInfo *devInfo) error { return nil } +func (devices *DeviceSet) checkGrowBaseDeviceFS(info *devInfo) error { + + if !userBaseSize { + return nil + } + + if devices.baseFsSize < devices.getBaseDeviceSize() { + return fmt.Errorf("devmapper: Base device size cannot be smaller than %s", units.HumanSize(float64(devices.getBaseDeviceSize()))) + } + + if devices.baseFsSize == devices.getBaseDeviceSize() { + return nil + } + + info.lock.Lock() + defer info.lock.Unlock() + + devices.Lock() + defer devices.Unlock() + + info.Size = devices.baseFsSize + + if err := devices.saveMetadata(info); err != nil { + // Try to remove unused device + delete(devices.Devices, info.Hash) + return err + } + + return devices.growFS(info) +} + +func (devices *DeviceSet) growFS(info *devInfo) error { + if err := devices.activateDeviceIfNeeded(info, false); err != nil { + return fmt.Errorf("Error activating devmapper device: %s", err) + } + + defer devices.deactivateDevice(info) + + fsMountPoint := "/run/docker/mnt" + if _, err := os.Stat(fsMountPoint); os.IsNotExist(err) { + if err := os.MkdirAll(fsMountPoint, 0700); err != nil { + return err + } + defer os.RemoveAll(fsMountPoint) + } + + options := "" + if devices.BaseDeviceFilesystem == "xfs" { + // XFS needs nouuid or it can't mount filesystems with the same fs + options = joinMountOptions(options, "nouuid") + } + options = joinMountOptions(options, devices.mountOptions) + + if err := mount.Mount(info.DevName(), fsMountPoint, devices.BaseDeviceFilesystem, options); err != nil { + return fmt.Errorf("Error mounting '%s' on '%s': %s", info.DevName(), fsMountPoint, err) + } + + defer syscall.Unmount(fsMountPoint, syscall.MNT_DETACH) + + switch devices.BaseDeviceFilesystem { + case "ext4": + if out, err := exec.Command("resize2fs", info.DevName()).CombinedOutput(); err != nil { + return fmt.Errorf("Failed to grow rootfs:%v:%s", err, string(out)) + } + case "xfs": + if out, err := exec.Command("xfs_growfs", info.DevName()).CombinedOutput(); err != nil { + return fmt.Errorf("Failed to grow rootfs:%v:%s", err, string(out)) + } + default: + return fmt.Errorf("Unsupported filesystem type %s", devices.BaseDeviceFilesystem) + } + return nil +} + func (devices *DeviceSet) setupBaseImage() error { oldInfo, _ := devices.lookupDeviceWithLock("") @@ -1069,9 +1144,8 @@ func (devices *DeviceSet) setupBaseImage() error { return err } - if devices.baseFsSize != defaultBaseFsSize && devices.baseFsSize != devices.getBaseDeviceSize() { - logrus.Warnf("devmapper: Base device is already initialized to size %s, new value of base device size %s will not take effect", - units.HumanSize(float64(devices.getBaseDeviceSize())), units.HumanSize(float64(devices.baseFsSize))) + if err := devices.checkGrowBaseDeviceFS(oldInfo); err != nil { + return err } return nil @@ -2379,6 +2453,7 @@ func NewDeviceSet(root string, doInit bool, options []string, uidMaps, gidMaps [ if err != nil { return nil, err } + userBaseSize = true devices.baseFsSize = uint64(size) case "dm.loopdatasize": size, err := units.RAMInBytes(val) diff --git a/docs/reference/commandline/daemon.md b/docs/reference/commandline/daemon.md index 8bc30a8eca..a1595d6133 100644 --- a/docs/reference/commandline/daemon.md +++ b/docs/reference/commandline/daemon.md @@ -213,11 +213,23 @@ options for `zfs` start with `zfs`. * `dm.basesize` Specifies the size to use when creating the base device, which limits the - size of images and containers. The default value is 100G. Note, thin devices - are inherently "sparse", so a 100G device which is mostly empty doesn't use - 100 GB of space on the pool. However, the filesystem will use more space for + size of images and containers. The default value is 10G. Note, thin devices + are inherently "sparse", so a 10G device which is mostly empty doesn't use + 10 GB of space on the pool. However, the filesystem will use more space for the empty case the larger the device is. + The base device size can be increased at daemon restart which will allow + all future images and containers (based on those new images) to be of the + new base device size. + + Example use: + + $ docker daemon --storage-opt dm.basesize=50G + + This will increase the base device size to 50G. The Docker daemon will throw an + error if existing base device size is larger than 50G. A user can use + this option to expand the base device size however shrinking is not permitted. + This value affects the system-wide "base" empty filesystem that may already be initialized and inherited by pulled images. Typically, a change to this value requires additional steps to take effect: diff --git a/docs/userguide/storagedriver/device-mapper-driver.md b/docs/userguide/storagedriver/device-mapper-driver.md index 20af7e0b7b..4d81d714bc 100644 --- a/docs/userguide/storagedriver/device-mapper-driver.md +++ b/docs/userguide/storagedriver/device-mapper-driver.md @@ -249,11 +249,11 @@ You can use the `lsblk` command to see the device files created above and the `p NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT xvda 202:0 0 8G 0 disk └─xvda1 202:1 0 8G 0 part / - xvdf 202:80 0 100G 0 disk + xvdf 202:80 0 10G 0 disk ├─vg--docker-data 253:0 0 90G 0 lvm - │ └─docker-202:1-1032-pool 253:2 0 100G 0 dm + │ └─docker-202:1-1032-pool 253:2 0 10G 0 dm └─vg--docker-metadata 253:1 0 4G 0 lvm - └─docker-202:1-1032-pool 253:2 0 100G 0 dm + └─docker-202:1-1032-pool 253:2 0 10G 0 dm The diagram below shows the image from prior examples updated with the detail from the `lsblk` command above. diff --git a/man/docker-daemon.8.md b/man/docker-daemon.8.md index 8e4a3acc0b..a65874749e 100644 --- a/man/docker-daemon.8.md +++ b/man/docker-daemon.8.md @@ -271,12 +271,22 @@ Example use: `docker daemon --storage-opt dm.thinpooldev=/dev/mapper/thin-pool` #### dm.basesize Specifies the size to use when creating the base device, which limits -the size of images and containers. The default value is 100G. Note, -thin devices are inherently "sparse", so a 100G device which is mostly -empty doesn't use 100 GB of space on the pool. However, the filesystem +the size of images and containers. The default value is 10G. Note, +thin devices are inherently "sparse", so a 10G device which is mostly +empty doesn't use 10 GB of space on the pool. However, the filesystem will use more space for base images the larger the device is. +The base device size can be increased at daemon restart which will allow +all future images and containers (based on those new images) to be of the +new base device size. + +Example use: `docker daemon --storage-opt dm.basesize=50G` + +This will increase the base device size to 50G. The Docker daemon will throw an +error if existing base device size is larger than 50G. A user can use +this option to expand the base device size however shrinking is not permitted. + This value affects the system-wide "base" empty filesystem that may already be initialized and inherited by pulled images. Typically, a change to this value requires additional steps to take effect: