Browse code

devmapper: Add --storage-opt options for basic devicemapper settings

This allows setting these settings to be passed:
dm.basesize
dm.loopdatasize
dm.loopmetadatasize

Docker-DCO-1.1-Signed-off-by: Alexander Larsson <alexl@redhat.com> (github: alexlarsson)

Alexander Larsson authored on 2014/03/28 01:48:32
Showing 3 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,73 @@
0
+## devicemapper - a storage backend based on Device Mapper
1
+
2
+### Theory of operation
3
+
4
+The device mapper graphdriver uses the device mapper thin provisioning
5
+module (dm-thinp) to implement CoW snapshots. For each devicemapper
6
+graph locaion (typically `/var/lib/docker/devicemapper`, $graph below)
7
+a thin pool is created based on two block devices, one for data and
8
+one for metadata.  By default these block devices are created
9
+automatically by using loopback mounts of automatically creates sparse
10
+files.
11
+
12
+The default loopback files used are `$graph/devicemapper/data` and
13
+`$graph/devicemapper/metadata`. Additional metadata required to map
14
+from docker entities to the corresponding devicemapper volumes is
15
+stored in the `$graph/devicemapper/json` file (encoded as Json).
16
+
17
+In order to support multiple devicemapper graphs on a system the thin
18
+pool will be named something like: `docker-0:33-19478248-pool`, where
19
+the `0:30` part is the minor/major device nr and `19478248` is the
20
+inode number of the $graph directory.
21
+
22
+On the thin pool docker automatically creates a base thin device,
23
+called something like `docker-0:33-19478248-base` of a fixed
24
+size. This is automatically formated on creation and contains just an
25
+empty filesystem. This device is the base of all docker images and
26
+containers. All base images are snapshots of this device and those
27
+images are then in turn used as snapshots for other images and
28
+eventually containers.
29
+
30
+### options
31
+
32
+The devicemapper backend supports some options that you can specify
33
+when starting the docker daemon using the --storage-opt flags.
34
+This uses the `dm` prefix and would be used somthing like `docker -d --storage-opt dm.foo=bar`.
35
+
36
+Here is the list of supported options:
37
+
38
+ *  `dm.basesize`
39
+
40
+    Specifies the size to use when creating the base device, which
41
+    limits the size of images and containers. The default value is
42
+    10G. Note, thin devices are inherently "sparse", so a 10G device
43
+    which is mostly empty doesn't use 10 GB of space on the
44
+    pool. However, the filesystem will use more space for the empty
45
+    case the larger the device is.
46
+
47
+    Example use:
48
+
49
+    ``docker -d --storage-opt dm.basesize=20G``
50
+
51
+ *  `dm.loopdatasize`
52
+
53
+    Specifies the size to use when creating the loopback file for the
54
+    "data" device which is used for the thin pool. The default size is
55
+    100G. Note that the file is sparse, so it will not initially take
56
+    up this much space.
57
+
58
+    Example use:
59
+
60
+    ``docker -d --storage-opt dm.loopdatasize=200G``
61
+
62
+ *  `dm.loopmetadatasize`
63
+
64
+    Specifies the size to use when creating the loopback file for the
65
+    "metadadata" device which is used for the thin pool. The default size is
66
+    2G. Note that the file is sparse, so it will not initially take
67
+    up this much space.
68
+
69
+    Example use:
70
+
71
+    ``docker -d --storage-opt dm.loopmetadatasize=4G``
72
+
... ...
@@ -13,12 +13,14 @@ import (
13 13
 	"path"
14 14
 	"path/filepath"
15 15
 	"strconv"
16
+	"strings"
16 17
 	"sync"
17 18
 	"syscall"
18 19
 	"time"
19 20
 
20 21
 	"github.com/dotcloud/docker/daemon/graphdriver"
21 22
 	"github.com/dotcloud/docker/pkg/label"
23
+	"github.com/dotcloud/docker/pkg/units"
22 24
 	"github.com/dotcloud/docker/utils"
23 25
 )
24 26
 
... ...
@@ -65,6 +67,11 @@ type DeviceSet struct {
65 65
 	TransactionId    uint64
66 66
 	NewTransactionId uint64
67 67
 	nextDeviceId     int
68
+
69
+	// Options
70
+	dataLoopbackSize     int64
71
+	metaDataLoopbackSize int64
72
+	baseFsSize           uint64
68 73
 }
69 74
 
70 75
 type DiskUsage struct {
... ...
@@ -378,8 +385,8 @@ func (devices *DeviceSet) setupBaseImage() error {
378 378
 	// Ids are 24bit, so wrap around
379 379
 	devices.nextDeviceId = (id + 1) & 0xffffff
380 380
 
381
-	utils.Debugf("Registering base device (id %v) with FS size %v", id, DefaultBaseFsSize)
382
-	info, err := devices.registerDevice(id, "", DefaultBaseFsSize)
381
+	utils.Debugf("Registering base device (id %v) with FS size %v", id, devices.baseFsSize)
382
+	info, err := devices.registerDevice(id, "", devices.baseFsSize)
383 383
 	if err != nil {
384 384
 		_ = deleteDevice(devices.getPoolDevName(), id)
385 385
 		utils.Debugf("\n--->Err: %s\n", err)
... ...
@@ -567,12 +574,12 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error {
567 567
 		}
568 568
 
569 569
 		createdLoopback = !hasData || !hasMetadata
570
-		data, err := devices.ensureImage("data", DefaultDataLoopbackSize)
570
+		data, err := devices.ensureImage("data", devices.dataLoopbackSize)
571 571
 		if err != nil {
572 572
 			utils.Debugf("Error device ensureImage (data): %s\n", err)
573 573
 			return err
574 574
 		}
575
-		metadata, err := devices.ensureImage("metadata", DefaultMetaDataLoopbackSize)
575
+		metadata, err := devices.ensureImage("metadata", devices.metaDataLoopbackSize)
576 576
 		if err != nil {
577 577
 			utils.Debugf("Error device ensureImage (metadata): %s\n", err)
578 578
 			return err
... ...
@@ -1091,12 +1098,45 @@ func (devices *DeviceSet) Status() *Status {
1091 1091
 	return status
1092 1092
 }
1093 1093
 
1094
-func NewDeviceSet(root string, doInit bool) (*DeviceSet, error) {
1094
+func NewDeviceSet(root string, doInit bool, options []string) (*DeviceSet, error) {
1095 1095
 	SetDevDir("/dev")
1096 1096
 
1097 1097
 	devices := &DeviceSet{
1098
-		root:     root,
1099
-		MetaData: MetaData{Devices: make(map[string]*DevInfo)},
1098
+		root:                 root,
1099
+		MetaData:             MetaData{Devices: make(map[string]*DevInfo)},
1100
+		dataLoopbackSize:     DefaultDataLoopbackSize,
1101
+		metaDataLoopbackSize: DefaultMetaDataLoopbackSize,
1102
+		baseFsSize:           DefaultBaseFsSize,
1103
+	}
1104
+
1105
+	for _, option := range options {
1106
+		key, val, err := utils.ParseKeyValueOpt(option)
1107
+		if err != nil {
1108
+			return nil, err
1109
+		}
1110
+		key = strings.ToLower(key)
1111
+		switch key {
1112
+		case "dm.basesize":
1113
+			size, err := units.FromHumanSize(val)
1114
+			if err != nil {
1115
+				return nil, err
1116
+			}
1117
+			devices.baseFsSize = uint64(size)
1118
+		case "dm.loopdatasize":
1119
+			size, err := units.FromHumanSize(val)
1120
+			if err != nil {
1121
+				return nil, err
1122
+			}
1123
+			devices.dataLoopbackSize = size
1124
+		case "dm.loopmetadatasize":
1125
+			size, err := units.FromHumanSize(val)
1126
+			if err != nil {
1127
+				return nil, err
1128
+			}
1129
+			devices.metaDataLoopbackSize = size
1130
+		default:
1131
+			return nil, fmt.Errorf("Unknown option %s\n", key)
1132
+		}
1100 1133
 	}
1101 1134
 
1102 1135
 	if err := devices.initDevmapper(doInit); err != nil {
... ...
@@ -27,7 +27,7 @@ type Driver struct {
27 27
 }
28 28
 
29 29
 func Init(home string, options []string) (graphdriver.Driver, error) {
30
-	deviceSet, err := NewDeviceSet(home, true)
30
+	deviceSet, err := NewDeviceSet(home, true, options)
31 31
 	if err != nil {
32 32
 		return nil, err
33 33
 	}