From 83a34e000b2332d9a1b4214a77fae021ed144acb Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Wed, 11 Nov 2015 12:07:35 -0500 Subject: [PATCH 1/2] devmapper: Warn if user specified a filesytem and base device already has fs If user wants to use a filesystem it can be specified using dm.fs= option. It is possible that docker already had base image and a filesystem on that. Later if user wants to change file system using dm.fs= option and restarts docker, that's not possible. Warn user about it. Signed-off-by: Vivek Goyal --- daemon/graphdriver/devmapper/deviceset.go | 44 +++++++++++++++++------ 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go index 5dc31bbd87..3b85e98af7 100644 --- a/daemon/graphdriver/devmapper/deviceset.go +++ b/daemon/graphdriver/devmapper/deviceset.go @@ -99,7 +99,7 @@ type DeviceSet struct { dataLoopbackSize int64 metaDataLoopbackSize int64 baseFsSize uint64 - filesystem string + userFilesystem string // FS specified by user using dm.fs mountOptions string mkfsArgs []string dataDevice string // block or loop dev @@ -532,6 +532,10 @@ func (devices *DeviceSet) activateDeviceIfNeeded(info *devInfo, ignoreDeleted bo return devicemapper.ActivateDevice(devices.getPoolDevName(), info.Name(), info.DeviceID, info.Size) } +func determineDefaultFS() string { + return "ext4" +} + func (devices *DeviceSet) createFilesystem(info *devInfo) error { devname := info.DevName() @@ -543,7 +547,13 @@ func (devices *DeviceSet) createFilesystem(info *devInfo) error { args = append(args, devname) var err error - switch devices.filesystem { + + fs := devices.userFilesystem + if fs == "" { + fs = determineDefaultFS() + } + + switch fs { case "xfs": err = exec.Command("mkfs.xfs", args...).Run() case "ext4": @@ -556,7 +566,7 @@ func (devices *DeviceSet) createFilesystem(info *devInfo) error { } err = exec.Command("tune2fs", append([]string{"-c", "-1", "-i", "0"}, devname)...).Run() default: - err = fmt.Errorf("Unsupported filesystem type %s", devices.filesystem) + err = fmt.Errorf("Unsupported filesystem type %s", fs) } if err != nil { return err @@ -843,7 +853,7 @@ func (devices *DeviceSet) getBaseDeviceSize() uint64 { return info.Size } -func (devices *DeviceSet) verifyBaseDeviceUUID(baseInfo *devInfo) error { +func (devices *DeviceSet) verifyBaseDeviceUUIDFS(baseInfo *devInfo) error { devices.Lock() defer devices.Unlock() @@ -859,9 +869,22 @@ func (devices *DeviceSet) verifyBaseDeviceUUID(baseInfo *devInfo) error { } if devices.BaseDeviceUUID != uuid { - return fmt.Errorf("Current Base Device UUID:%s does not match with stored UUID:%s", uuid, devices.BaseDeviceUUID) + return fmt.Errorf("Current Base Device UUID:%s does not match with stored UUID:%s. Possibly using a different thin pool than last invocation", uuid, devices.BaseDeviceUUID) } + // If user specified a filesystem using dm.fs option and current + // file system of base image is not same, warn user that dm.fs + // will be ignored. + if devices.userFilesystem != "" { + fs, err := ProbeFsType(baseInfo.DevName()) + if err != nil { + return err + } + + if fs != devices.userFilesystem { + logrus.Warnf("Base device already exists and has filesystem %s on it. User specified filesystem %s will be ignored.", fs, devices.userFilesystem) + } + } return nil } @@ -962,7 +985,7 @@ func (devices *DeviceSet) checkThinPool() error { // Base image is initialized properly. Either save UUID for first time (for // upgrade case or verify UUID. -func (devices *DeviceSet) setupVerifyBaseImageUUID(baseInfo *devInfo) error { +func (devices *DeviceSet) setupVerifyBaseImageUUIDFS(baseInfo *devInfo) error { // If BaseDeviceUUID is nil (upgrade case), save it and return success. if devices.BaseDeviceUUID == "" { if err := devices.saveBaseDeviceUUID(baseInfo); err != nil { @@ -971,8 +994,8 @@ func (devices *DeviceSet) setupVerifyBaseImageUUID(baseInfo *devInfo) error { return nil } - if err := devices.verifyBaseDeviceUUID(baseInfo); err != nil { - return fmt.Errorf("Base Device UUID verification failed. Possibly using a different thin pool than last invocation:%v", err) + if err := devices.verifyBaseDeviceUUIDFS(baseInfo); err != nil { + return fmt.Errorf("Base Device UUID and Filesystem verification failed.%v", err) } return nil @@ -987,7 +1010,7 @@ func (devices *DeviceSet) setupBaseImage() error { if oldInfo != nil { if oldInfo.Initialized && !oldInfo.Deleted { - if err := devices.setupVerifyBaseImageUUID(oldInfo); err != nil { + if err := devices.setupVerifyBaseImageUUIDFS(oldInfo); err != nil { return err } @@ -2269,7 +2292,6 @@ func NewDeviceSet(root string, doInit bool, options []string, uidMaps, gidMaps [ metaDataLoopbackSize: defaultMetaDataLoopbackSize, baseFsSize: defaultBaseFsSize, overrideUdevSyncCheck: defaultUdevSyncOverride, - filesystem: "ext4", doBlkDiscard: true, thinpBlockSize: defaultThinpBlockSize, deviceIDMap: make([]byte, deviceIDMapSz), @@ -2308,7 +2330,7 @@ func NewDeviceSet(root string, doInit bool, options []string, uidMaps, gidMaps [ if val != "ext4" && val != "xfs" { return nil, fmt.Errorf("Unsupported filesystem %s\n", val) } - devices.filesystem = val + devices.userFilesystem = val case "dm.mkfsarg": devices.mkfsArgs = append(devices.mkfsArgs, val) case "dm.mountopt": From 07ff17fb850e5ddae6f38cc21776ebb9b1690f3e Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Wed, 11 Nov 2015 12:07:35 -0500 Subject: [PATCH 2/2] devmapper: Switch to xfs as default filesystem if supported If platform supports xfs filesystem then use xfs as default filesystem for container rootfs instead of ext4. Reason being that ext4 is pre-allcating lot of metadata (around 1.8GB on 100G thin volume) and that can take long enough on AWS storage that systemd times out and docker fails to start. If one disables pre-allocation of ext4 metadata, then it will be allocated when containers are mounted and we will have multiple copies of metadata per container. For a 100G thin device, it was around 1.5GB of metadata per container. ext4 has an optimization to skip zeroing if discards are issued and underlying device guarantees that zero will be returned when discarded blocks are read back. devicemapper thin devices don't offer that guarantee so ext4 optimization does not kick in. In fact given discards are optional and can be dropped on the floor if need be, it looks like it might not be possible to guarantee that all the blocks got discarded and if read back zero will be returned. Signed-off-by: Anusha Ragunathan Signed-off-by: Vivek Goyal --- daemon/graphdriver/devmapper/deviceset.go | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/daemon/graphdriver/devmapper/deviceset.go b/daemon/graphdriver/devmapper/deviceset.go index 3b85e98af7..c08f0a941d 100644 --- a/daemon/graphdriver/devmapper/deviceset.go +++ b/daemon/graphdriver/devmapper/deviceset.go @@ -3,6 +3,7 @@ package devmapper import ( + "bufio" "encoding/json" "errors" "fmt" @@ -532,7 +533,42 @@ func (devices *DeviceSet) activateDeviceIfNeeded(info *devInfo, ignoreDeleted bo return devicemapper.ActivateDevice(devices.getPoolDevName(), info.Name(), info.DeviceID, info.Size) } +// Return true only if kernel supports xfs and mkfs.xfs is available +func xfsSupported() bool { + // Make sure mkfs.xfs is available + if _, err := exec.LookPath("mkfs.xfs"); err != nil { + return false + } + + // Check if kernel supports xfs filesystem or not. + exec.Command("modprobe", "xfs").Run() + + f, err := os.Open("/proc/filesystems") + if err != nil { + logrus.Warnf("Could not check if xfs is supported: %v", err) + return false + } + defer f.Close() + + s := bufio.NewScanner(f) + for s.Scan() { + if strings.HasSuffix(s.Text(), "\txfs") { + return true + } + } + + if err := s.Err(); err != nil { + logrus.Warnf("Could not check if xfs is supported: %v", err) + } + return false +} + func determineDefaultFS() string { + if xfsSupported() { + return "xfs" + } + + logrus.Warn("XFS is not supported in your system. Either the kernel doesnt support it or mkfs.xfs is not in your PATH. Defaulting to ext4 filesystem") return "ext4" }