diff --git a/daemon/graphdriver/devmapper/README.md b/daemon/graphdriver/devmapper/README.md new file mode 100644 index 0000000000..f2c855c893 --- /dev/null +++ b/daemon/graphdriver/devmapper/README.md @@ -0,0 +1,73 @@ +## devicemapper - a storage backend based on Device Mapper + +### Theory of operation + +The device mapper graphdriver uses the device mapper thin provisioning +module (dm-thinp) to implement CoW snapshots. For each devicemapper +graph locaion (typically `/var/lib/docker/devicemapper`, $graph below) +a thin pool is created based on two block devices, one for data and +one for metadata. By default these block devices are created +automatically by using loopback mounts of automatically creates sparse +files. + +The default loopback files used are `$graph/devicemapper/data` and +`$graph/devicemapper/metadata`. Additional metadata required to map +from docker entities to the corresponding devicemapper volumes is +stored in the `$graph/devicemapper/json` file (encoded as Json). + +In order to support multiple devicemapper graphs on a system the thin +pool will be named something like: `docker-0:33-19478248-pool`, where +the `0:30` part is the minor/major device nr and `19478248` is the +inode number of the $graph directory. + +On the thin pool docker automatically creates a base thin device, +called something like `docker-0:33-19478248-base` of a fixed +size. This is automatically formated on creation and contains just an +empty filesystem. This device is the base of all docker images and +containers. All base images are snapshots of this device and those +images are then in turn used as snapshots for other images and +eventually containers. + +### options + +The devicemapper backend supports some options that you can specify +when starting the docker daemon using the --storage-opt flags. +This uses the `dm` prefix and would be used somthing like `docker -d --storage-opt dm.foo=bar`. + +Here is the list of supported options: + + * `dm.basesize` + + Specifies the size to use when creating the base device, which + limits 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 the empty + case the larger the device is. + + Example use: + + ``docker -d --storage-opt dm.basesize=20G`` + + * `dm.loopdatasize` + + Specifies the size to use when creating the loopback file for the + "data" device which is used for the thin pool. The default size is + 100G. Note that the file is sparse, so it will not initially take + up this much space. + + Example use: + + ``docker -d --storage-opt dm.loopdatasize=200G`` + + * `dm.loopmetadatasize` + + Specifies the size to use when creating the loopback file for the + "metadadata" device which is used for the thin pool. The default size is + 2G. Note that the file is sparse, so it will not initially take + up this much space. + + Example use: + + ``docker -d --storage-opt dm.loopmetadatasize=4G`` + diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go index f36faca900..430f5c2e60 100644 --- a/daemon/graphdriver/devmapper/deviceset.go +++ b/daemon/graphdriver/devmapper/deviceset.go @@ -13,12 +13,14 @@ import ( "path" "path/filepath" "strconv" + "strings" "sync" "syscall" "time" "github.com/dotcloud/docker/daemon/graphdriver" "github.com/dotcloud/docker/pkg/label" + "github.com/dotcloud/docker/pkg/units" "github.com/dotcloud/docker/utils" ) @@ -65,6 +67,11 @@ type DeviceSet struct { TransactionId uint64 NewTransactionId uint64 nextDeviceId int + + // Options + dataLoopbackSize int64 + metaDataLoopbackSize int64 + baseFsSize uint64 } type DiskUsage struct { @@ -378,8 +385,8 @@ func (devices *DeviceSet) setupBaseImage() error { // Ids are 24bit, so wrap around devices.nextDeviceId = (id + 1) & 0xffffff - utils.Debugf("Registering base device (id %v) with FS size %v", id, DefaultBaseFsSize) - info, err := devices.registerDevice(id, "", DefaultBaseFsSize) + utils.Debugf("Registering base device (id %v) with FS size %v", id, devices.baseFsSize) + info, err := devices.registerDevice(id, "", devices.baseFsSize) if err != nil { _ = deleteDevice(devices.getPoolDevName(), id) utils.Debugf("\n--->Err: %s\n", err) @@ -567,12 +574,12 @@ func (devices *DeviceSet) initDevmapper(doInit bool) error { } createdLoopback = !hasData || !hasMetadata - data, err := devices.ensureImage("data", DefaultDataLoopbackSize) + data, err := devices.ensureImage("data", devices.dataLoopbackSize) if err != nil { utils.Debugf("Error device ensureImage (data): %s\n", err) return err } - metadata, err := devices.ensureImage("metadata", DefaultMetaDataLoopbackSize) + metadata, err := devices.ensureImage("metadata", devices.metaDataLoopbackSize) if err != nil { utils.Debugf("Error device ensureImage (metadata): %s\n", err) return err @@ -1091,12 +1098,45 @@ func (devices *DeviceSet) Status() *Status { return status } -func NewDeviceSet(root string, doInit bool) (*DeviceSet, error) { +func NewDeviceSet(root string, doInit bool, options []string) (*DeviceSet, error) { SetDevDir("/dev") devices := &DeviceSet{ - root: root, - MetaData: MetaData{Devices: make(map[string]*DevInfo)}, + root: root, + MetaData: MetaData{Devices: make(map[string]*DevInfo)}, + dataLoopbackSize: DefaultDataLoopbackSize, + metaDataLoopbackSize: DefaultMetaDataLoopbackSize, + baseFsSize: DefaultBaseFsSize, + } + + for _, option := range options { + key, val, err := utils.ParseKeyValueOpt(option) + if err != nil { + return nil, err + } + key = strings.ToLower(key) + switch key { + case "dm.basesize": + size, err := units.FromHumanSize(val) + if err != nil { + return nil, err + } + devices.baseFsSize = uint64(size) + case "dm.loopdatasize": + size, err := units.FromHumanSize(val) + if err != nil { + return nil, err + } + devices.dataLoopbackSize = size + case "dm.loopmetadatasize": + size, err := units.FromHumanSize(val) + if err != nil { + return nil, err + } + devices.metaDataLoopbackSize = size + default: + return nil, fmt.Errorf("Unknown option %s\n", key) + } } if err := devices.initDevmapper(doInit); err != nil { diff --git a/daemon/graphdriver/devmapper/driver.go b/daemon/graphdriver/devmapper/driver.go index 50bda1cd2e..5bfd8ee658 100644 --- a/daemon/graphdriver/devmapper/driver.go +++ b/daemon/graphdriver/devmapper/driver.go @@ -27,7 +27,7 @@ type Driver struct { } func Init(home string, options []string) (graphdriver.Driver, error) { - deviceSet, err := NewDeviceSet(home, true) + deviceSet, err := NewDeviceSet(home, true, options) if err != nil { return nil, err }