Browse code

daemon option (--storage-opt dm.basesize) for increasing the base device size on daemon restart

Signed-off-by: Shishir Mahajan <shishir.mahajan@redhat.com>

Shishir Mahajan authored on 2016/01/13 03:44:13
Showing 4 changed files
... ...
@@ -35,7 +35,7 @@ import (
35 35
 var (
36 36
 	defaultDataLoopbackSize     int64  = 100 * 1024 * 1024 * 1024
37 37
 	defaultMetaDataLoopbackSize int64  = 2 * 1024 * 1024 * 1024
38
-	defaultBaseFsSize           uint64 = 100 * 1024 * 1024 * 1024
38
+	defaultBaseFsSize           uint64 = 10 * 1024 * 1024 * 1024
39 39
 	defaultThinpBlockSize       uint32 = 128 // 64K = 128 512b sectors
40 40
 	defaultUdevSyncOverride            = false
41 41
 	maxDeviceID                        = 0xffffff // 24 bit, pool limit
... ...
@@ -47,6 +47,7 @@ var (
47 47
 	driverDeferredRemovalSupport = false
48 48
 	enableDeferredRemoval        = false
49 49
 	enableDeferredDeletion       = false
50
+	userBaseSize                 = false
50 51
 )
51 52
 
52 53
 const deviceSetMetaFile string = "deviceset-metadata"
... ...
@@ -1056,6 +1057,80 @@ func (devices *DeviceSet) setupVerifyBaseImageUUIDFS(baseInfo *devInfo) error {
1056 1056
 	return nil
1057 1057
 }
1058 1058
 
1059
+func (devices *DeviceSet) checkGrowBaseDeviceFS(info *devInfo) error {
1060
+
1061
+	if !userBaseSize {
1062
+		return nil
1063
+	}
1064
+
1065
+	if devices.baseFsSize < devices.getBaseDeviceSize() {
1066
+		return fmt.Errorf("devmapper: Base device size cannot be smaller than %s", units.HumanSize(float64(devices.getBaseDeviceSize())))
1067
+	}
1068
+
1069
+	if devices.baseFsSize == devices.getBaseDeviceSize() {
1070
+		return nil
1071
+	}
1072
+
1073
+	info.lock.Lock()
1074
+	defer info.lock.Unlock()
1075
+
1076
+	devices.Lock()
1077
+	defer devices.Unlock()
1078
+
1079
+	info.Size = devices.baseFsSize
1080
+
1081
+	if err := devices.saveMetadata(info); err != nil {
1082
+		// Try to remove unused device
1083
+		delete(devices.Devices, info.Hash)
1084
+		return err
1085
+	}
1086
+
1087
+	return devices.growFS(info)
1088
+}
1089
+
1090
+func (devices *DeviceSet) growFS(info *devInfo) error {
1091
+	if err := devices.activateDeviceIfNeeded(info, false); err != nil {
1092
+		return fmt.Errorf("Error activating devmapper device: %s", err)
1093
+	}
1094
+
1095
+	defer devices.deactivateDevice(info)
1096
+
1097
+	fsMountPoint := "/run/docker/mnt"
1098
+	if _, err := os.Stat(fsMountPoint); os.IsNotExist(err) {
1099
+		if err := os.MkdirAll(fsMountPoint, 0700); err != nil {
1100
+			return err
1101
+		}
1102
+		defer os.RemoveAll(fsMountPoint)
1103
+	}
1104
+
1105
+	options := ""
1106
+	if devices.BaseDeviceFilesystem == "xfs" {
1107
+		// XFS needs nouuid or it can't mount filesystems with the same fs
1108
+		options = joinMountOptions(options, "nouuid")
1109
+	}
1110
+	options = joinMountOptions(options, devices.mountOptions)
1111
+
1112
+	if err := mount.Mount(info.DevName(), fsMountPoint, devices.BaseDeviceFilesystem, options); err != nil {
1113
+		return fmt.Errorf("Error mounting '%s' on '%s': %s", info.DevName(), fsMountPoint, err)
1114
+	}
1115
+
1116
+	defer syscall.Unmount(fsMountPoint, syscall.MNT_DETACH)
1117
+
1118
+	switch devices.BaseDeviceFilesystem {
1119
+	case "ext4":
1120
+		if out, err := exec.Command("resize2fs", info.DevName()).CombinedOutput(); err != nil {
1121
+			return fmt.Errorf("Failed to grow rootfs:%v:%s", err, string(out))
1122
+		}
1123
+	case "xfs":
1124
+		if out, err := exec.Command("xfs_growfs", info.DevName()).CombinedOutput(); err != nil {
1125
+			return fmt.Errorf("Failed to grow rootfs:%v:%s", err, string(out))
1126
+		}
1127
+	default:
1128
+		return fmt.Errorf("Unsupported filesystem type %s", devices.BaseDeviceFilesystem)
1129
+	}
1130
+	return nil
1131
+}
1132
+
1059 1133
 func (devices *DeviceSet) setupBaseImage() error {
1060 1134
 	oldInfo, _ := devices.lookupDeviceWithLock("")
1061 1135
 
... ...
@@ -1069,9 +1144,8 @@ func (devices *DeviceSet) setupBaseImage() error {
1069 1069
 				return err
1070 1070
 			}
1071 1071
 
1072
-			if devices.baseFsSize != defaultBaseFsSize && devices.baseFsSize != devices.getBaseDeviceSize() {
1073
-				logrus.Warnf("devmapper: Base device is already initialized to size %s, new value of base device size %s will not take effect",
1074
-					units.HumanSize(float64(devices.getBaseDeviceSize())), units.HumanSize(float64(devices.baseFsSize)))
1072
+			if err := devices.checkGrowBaseDeviceFS(oldInfo); err != nil {
1073
+				return err
1075 1074
 			}
1076 1075
 
1077 1076
 			return nil
... ...
@@ -2379,6 +2453,7 @@ func NewDeviceSet(root string, doInit bool, options []string, uidMaps, gidMaps [
2379 2379
 			if err != nil {
2380 2380
 				return nil, err
2381 2381
 			}
2382
+			userBaseSize = true
2382 2383
 			devices.baseFsSize = uint64(size)
2383 2384
 		case "dm.loopdatasize":
2384 2385
 			size, err := units.RAMInBytes(val)
... ...
@@ -213,11 +213,23 @@ options for `zfs` start with `zfs`.
213 213
 *  `dm.basesize`
214 214
 
215 215
     Specifies the size to use when creating the base device, which limits the
216
-    size of images and containers. The default value is 100G. Note, thin devices
217
-    are inherently "sparse", so a 100G device which is mostly empty doesn't use
218
-    100 GB of space on the pool. However, the filesystem will use more space for
216
+    size of images and containers. The default value is 10G. Note, thin devices
217
+    are inherently "sparse", so a 10G device which is mostly empty doesn't use
218
+    10 GB of space on the pool. However, the filesystem will use more space for
219 219
     the empty case the larger the device is.
220 220
 
221
+    The base device size can be increased at daemon restart which will allow
222
+    all future images and containers (based on those new images) to be of the 
223
+    new base device size.
224
+
225
+    Example use: 
226
+
227
+        $ docker daemon --storage-opt dm.basesize=50G
228
+
229
+    This will increase the base device size to 50G. The Docker daemon will throw an 
230
+    error if existing base device size is larger than 50G. A user can use 
231
+    this option to expand the base device size however shrinking is not permitted.
232
+
221 233
     This value affects the system-wide "base" empty filesystem
222 234
     that may already be initialized and inherited by pulled images. Typically,
223 235
     a change to this value requires additional steps to take effect:
... ...
@@ -249,11 +249,11 @@ You can use the `lsblk` command to see the device files created above and the `p
249 249
     NAME                       MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
250 250
     xvda                       202:0    0    8G  0 disk
251 251
     └─xvda1                    202:1    0    8G  0 part /
252
-    xvdf                       202:80   0  100G  0 disk
252
+    xvdf                       202:80   0   10G  0 disk
253 253
     ├─vg--docker-data          253:0    0   90G  0 lvm
254
-    │ └─docker-202:1-1032-pool 253:2    0  100G  0 dm
254
+    │ └─docker-202:1-1032-pool 253:2    0   10G  0 dm
255 255
     └─vg--docker-metadata      253:1    0    4G  0 lvm
256
-      └─docker-202:1-1032-pool 253:2    0  100G  0 dm
256
+      └─docker-202:1-1032-pool 253:2    0   10G  0 dm
257 257
 
258 258
 The diagram below shows the image from prior examples updated with the detail from the `lsblk` command above.
259 259
 
... ...
@@ -271,12 +271,22 @@ Example use: `docker daemon --storage-opt dm.thinpooldev=/dev/mapper/thin-pool`
271 271
 #### dm.basesize
272 272
 
273 273
 Specifies the size to use when creating the base device, which limits
274
-the size of images and containers. The default value is 100G. Note,
275
-thin devices are inherently "sparse", so a 100G device which is mostly
276
-empty doesn't use 100 GB of space on the pool. However, the filesystem
274
+the size of images and containers. The default value is 10G. Note,
275
+thin devices are inherently "sparse", so a 10G device which is mostly
276
+empty doesn't use 10 GB of space on the pool. However, the filesystem
277 277
 will use more space for base images the larger the device
278 278
 is.
279 279
 
280
+The base device size can be increased at daemon restart which will allow
281
+all future images and containers (based on those new images) to be of the 
282
+new base device size.
283
+
284
+Example use: `docker daemon --storage-opt dm.basesize=50G` 
285
+
286
+This will increase the base device size to 50G. The Docker daemon will throw an 
287
+error if existing base device size is larger than 50G. A user can use 
288
+this option to expand the base device size however shrinking is not permitted.
289
+
280 290
 This value affects the system-wide "base" empty filesystem that may already
281 291
 be initialized and inherited by pulled images. Typically, a change to this
282 292
 value requires additional steps to take effect: