Skip to content

Commit 75401d7

Browse files
committed
Merge pull request #926 from jimmidyson/docker-fs-size
Support devicemapper storage for docker images dir
2 parents ec2a73d + 8e63ba3 commit 75401d7

File tree

6 files changed

+150
-54
lines changed

6 files changed

+150
-54
lines changed

container/docker/factory.go

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,17 @@ func RootDir() string {
8181
return *dockerRootDir
8282
}
8383

84+
type storageDriver string
85+
86+
const (
87+
devicemapperStorageDriver storageDriver = "devicemapper"
88+
aufsStorageDriver storageDriver = "aufs"
89+
)
90+
8491
type dockerFactory struct {
8592
machineInfoFactory info.MachineInfoFactory
8693

87-
// Whether docker is running with AUFS storage driver.
88-
usesAufsDriver bool
94+
storageDriver storageDriver
8995

9096
client *docker.Client
9197

@@ -110,7 +116,7 @@ func (self *dockerFactory) NewContainerHandler(name string, inHostNamespace bool
110116
name,
111117
self.machineInfoFactory,
112118
self.fsInfo,
113-
self.usesAufsDriver,
119+
self.storageDriver,
114120
&self.cgroupSubsystems,
115121
inHostNamespace,
116122
)
@@ -184,6 +190,10 @@ func parseDockerVersion(full_version_string string) ([]int, error) {
184190

185191
// Register root container before running this function!
186192
func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo) error {
193+
if UseSystemd() {
194+
glog.Infof("System is using systemd")
195+
}
196+
187197
client, err := Client()
188198
if err != nil {
189199
return fmt.Errorf("unable to communicate with docker daemon: %v", err)
@@ -207,32 +217,16 @@ func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo) error {
207217
}
208218

209219
// Check that the libcontainer execdriver is used.
210-
information, err := client.Info()
220+
information, err := DockerInfo()
211221
if err != nil {
212222
return fmt.Errorf("failed to detect Docker info: %v", err)
213223
}
214-
usesNativeDriver := false
215-
for _, val := range *information {
216-
if strings.Contains(val, "ExecutionDriver=") && strings.Contains(val, "native") {
217-
usesNativeDriver = true
218-
break
219-
}
220-
}
221-
if !usesNativeDriver {
224+
execDriver, ok := information["ExecutionDriver"]
225+
if !ok || !strings.HasPrefix(execDriver, "native") {
222226
return fmt.Errorf("docker found, but not using native exec driver")
223227
}
224228

225-
usesAufsDriver := false
226-
for _, val := range *information {
227-
if strings.Contains(val, "Driver=") && strings.Contains(val, "aufs") {
228-
usesAufsDriver = true
229-
break
230-
}
231-
}
232-
233-
if UseSystemd() {
234-
glog.Infof("System is using systemd")
235-
}
229+
sd, _ := information["Driver"]
236230

237231
cgroupSubsystems, err := libcontainer.GetCgroupSubsystems()
238232
if err != nil {
@@ -243,7 +237,7 @@ func Register(factory info.MachineInfoFactory, fsInfo fs.FsInfo) error {
243237
f := &dockerFactory{
244238
machineInfoFactory: factory,
245239
client: client,
246-
usesAufsDriver: usesAufsDriver,
240+
storageDriver: storageDriver(sd),
247241
cgroupSubsystems: cgroupSubsystems,
248242
fsInfo: fsInfo,
249243
}

container/docker/handler.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ type dockerContainerHandler struct {
6262
// Manager of this container's cgroups.
6363
cgroupManager cgroups.Manager
6464

65-
usesAufsDriver bool
66-
fsInfo fs.FsInfo
67-
storageDirs []string
65+
storageDriver storageDriver
66+
fsInfo fs.FsInfo
67+
storageDirs []string
6868

6969
// Time at which this container was created.
7070
creationTime time.Time
@@ -93,7 +93,7 @@ func newDockerContainerHandler(
9393
name string,
9494
machineInfoFactory info.MachineInfoFactory,
9595
fsInfo fs.FsInfo,
96-
usesAufsDriver bool,
96+
storageDriver storageDriver,
9797
cgroupSubsystems *containerLibcontainer.CgroupSubsystems,
9898
inHostNamespace bool,
9999
) (container.ContainerHandler, error) {
@@ -127,14 +127,15 @@ func newDockerContainerHandler(
127127
machineInfoFactory: machineInfoFactory,
128128
cgroupPaths: cgroupPaths,
129129
cgroupManager: cgroupManager,
130-
usesAufsDriver: usesAufsDriver,
130+
storageDriver: storageDriver,
131131
fsInfo: fsInfo,
132132
rootFs: rootFs,
133133
storageDirs: storageDirs,
134134
fsHandler: newFsHandler(time.Minute, storageDirs, fsInfo),
135135
}
136136

137-
if usesAufsDriver {
137+
switch storageDriver {
138+
case aufsStorageDriver:
138139
handler.fsHandler.start()
139140
}
140141

@@ -228,9 +229,8 @@ func (self *dockerContainerHandler) GetSpec() (info.ContainerSpec, error) {
228229

229230
spec := libcontainerConfigToContainerSpec(libcontainerConfig, mi)
230231
spec.CreationTime = self.creationTime
231-
if self.usesAufsDriver {
232-
spec.HasFilesystem = true
233-
}
232+
// For now only enable for aufs filesystems
233+
spec.HasFilesystem = self.storageDriver == aufsStorageDriver
234234
spec.Labels = self.labels
235235
spec.Image = self.image
236236
spec.HasNetwork = hasNet(self.networkMode)
@@ -240,7 +240,7 @@ func (self *dockerContainerHandler) GetSpec() (info.ContainerSpec, error) {
240240

241241
func (self *dockerContainerHandler) getFsStats(stats *info.ContainerStats) error {
242242
// No support for non-aufs storage drivers.
243-
if !self.usesAufsDriver {
243+
if self.storageDriver != aufsStorageDriver {
244244
return nil
245245
}
246246

deploy/Dockerfile

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1-
FROM progrium/busybox
2-
1+
FROM alpine:3.2
2+
3+
4+
RUN apk add --update ca-certificates device-mapper && \
5+
wget https://circle-artifacts.com/gh/andyshinn/alpine-pkg-glibc/8/artifacts/0/home/ubuntu/alpine-pkg-glibc/packages/x86_64/glibc-2.21-r2.apk && \
6+
wget https://circle-artifacts.com/gh/andyshinn/alpine-pkg-glibc/8/artifacts/0/home/ubuntu/alpine-pkg-glibc/packages/x86_64/glibc-bin-2.21-r2.apk && \
7+
apk add --allow-untrusted glibc-2.21-r2.apk glibc-bin-2.21-r2.apk && \
8+
/usr/glibc/usr/bin/ldconfig /lib /usr/glibc/usr/lib && \
9+
echo 'hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4' >> /etc/nsswitch.conf && \
10+
rm -rf /var/cache/apk/*
311

412
# Grab cadvisor from the staging directory.
513
ADD cadvisor /usr/bin/cadvisor
614

715
EXPOSE 8080
8-
ENTRYPOINT ["/usr/bin/cadvisor"]
16+
ENTRYPOINT ["/usr/bin/cadvisor", "-logtostderr"]

deploy/canary/Dockerfile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
FROM golang:latest
22
33

4-
5-
RUN apt-get install -y git
4+
RUN apt-get install -y git thin-provisioning-tools
65
RUN git clone https://github.com/google/cadvisor.git /go/src/github.com/google/cadvisor
76
RUN go get github.com/tools/godep
87
RUN cd /go/src/github.com/google/cadvisor && godep go build .

fs/fs.go

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ package fs
1919

2020
import (
2121
"bufio"
22+
"bytes"
23+
"encoding/json"
2224
"fmt"
2325
"os"
2426
"os/exec"
@@ -44,6 +46,8 @@ type partition struct {
4446
mountpoint string
4547
major uint
4648
minor uint
49+
fsType string
50+
blockSize uint
4751
}
4852

4953
type RealFsInfo struct {
@@ -57,6 +61,7 @@ type RealFsInfo struct {
5761
type Context struct {
5862
// docker root directory.
5963
DockerRoot string
64+
DockerInfo map[string]string
6065
}
6166

6267
func NewFsInfo(context Context) (FsInfo, error) {
@@ -80,7 +85,25 @@ func NewFsInfo(context Context) (FsInfo, error) {
8085
if _, ok := partitions[mount.Source]; ok {
8186
continue
8287
}
83-
partitions[mount.Source] = partition{mount.Mountpoint, uint(mount.Major), uint(mount.Minor)}
88+
partitions[mount.Source] = partition{
89+
mountpoint: mount.Mountpoint,
90+
major: uint(mount.Major),
91+
minor: uint(mount.Minor),
92+
}
93+
}
94+
if storageDriver, ok := context.DockerInfo["Driver"]; ok && storageDriver == "devicemapper" {
95+
dev, major, minor, blockSize, err := dockerDMDevice(context.DockerInfo["DriverStatus"])
96+
if err != nil {
97+
glog.Warningf("Could not get Docker devicemapper device: %v", err)
98+
} else {
99+
partitions[dev] = partition{
100+
fsType: "devicemapper",
101+
major: major,
102+
minor: minor,
103+
blockSize: blockSize,
104+
}
105+
fsInfo.labels[LabelDockerImages] = dev
106+
}
84107
}
85108
glog.Infof("Filesystem partitions: %+v", partitions)
86109
fsInfo.partitions = partitions
@@ -174,9 +197,18 @@ func (self *RealFsInfo) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, er
174197
_, hasMount := mountSet[partition.mountpoint]
175198
_, hasDevice := deviceSet[device]
176199
if mountSet == nil || (hasMount && !hasDevice) {
177-
total, free, avail, err := getVfsStats(partition.mountpoint)
200+
var (
201+
total, free, avail uint64
202+
err error
203+
)
204+
switch partition.fsType {
205+
case "devicemapper":
206+
total, free, avail, err = getDMStats(device, partition.blockSize)
207+
default:
208+
total, free, avail, err = getVfsStats(partition.mountpoint)
209+
}
178210
if err != nil {
179-
glog.Errorf("Statvfs failed. Error: %v", err)
211+
glog.Errorf("Stat fs failed. Error: %v", err)
180212
} else {
181213
deviceSet[device] = struct{}{}
182214
deviceInfo := DeviceInfo{
@@ -295,3 +327,67 @@ func getVfsStats(path string) (uint64, uint64, uint64, error) {
295327
avail := uint64(s.Frsize) * s.Bavail
296328
return total, free, avail, nil
297329
}
330+
331+
func dockerStatusValue(status [][]string, target string) string {
332+
for _, v := range status {
333+
if len(v) == 2 && strings.ToLower(v[0]) == strings.ToLower(target) {
334+
return v[1]
335+
}
336+
}
337+
return ""
338+
}
339+
340+
func dockerDMDevice(driverStatus string) (string, uint, uint, uint, error) {
341+
var config [][]string
342+
err := json.Unmarshal([]byte(driverStatus), &config)
343+
if err != nil {
344+
return "", 0, 0, 0, err
345+
}
346+
poolName := dockerStatusValue(config, "Pool Name")
347+
if len(poolName) == 0 {
348+
return "", 0, 0, 0, fmt.Errorf("Could not get dm pool name")
349+
}
350+
351+
dmTable, err := exec.Command("dmsetup", "table", poolName).Output()
352+
if err != nil {
353+
return "", 0, 0, 0, err
354+
}
355+
356+
var (
357+
major, minor, dataBlkSize, bkt uint
358+
bkts string
359+
)
360+
361+
_, err = fmt.Fscanf(bytes.NewReader(dmTable),
362+
"%d %d %s %d:%d %d:%d %d %d %d %s",
363+
&bkt, &bkt, &bkts, &bkt, &bkt, &major, &minor, &dataBlkSize, &bkt, &bkt, &bkts)
364+
if err != nil {
365+
return "", 0, 0, 0, err
366+
}
367+
return poolName, major, minor, dataBlkSize, nil
368+
}
369+
370+
func getDMStats(poolName string, dataBlkSize uint) (uint64, uint64, uint64, error) {
371+
dmStatus, err := exec.Command("dmsetup", "status", poolName).Output()
372+
if err != nil {
373+
return 0, 0, 0, err
374+
}
375+
376+
var (
377+
total, used, bkt uint64
378+
bkts string
379+
)
380+
381+
_, err = fmt.Fscanf(bytes.NewReader(dmStatus),
382+
"%d %d %s %d %d/%d %d/%d %s %s %s %s",
383+
&bkt, &bkt, &bkts, &bkt, &bkt, &bkt, &used, &total, &bkts, &bkts, &bkts, &bkts)
384+
if err != nil {
385+
return 0, 0, 0, err
386+
}
387+
388+
total *= 512 * uint64(dataBlkSize)
389+
used *= 512 * uint64(dataBlkSize)
390+
free := total - used
391+
392+
return total, free, free, nil
393+
}

manager/manager.go

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package manager
1717

1818
import (
19+
"encoding/json"
1920
"flag"
2021
"fmt"
2122
"os"
@@ -126,7 +127,11 @@ func New(memoryCache *memory.InMemoryCache, sysfs sysfs.SysFs, maxHousekeepingIn
126127
}
127128
glog.Infof("cAdvisor running in container: %q", selfContainer)
128129

129-
context := fs.Context{DockerRoot: docker.RootDir()}
130+
dockerInfo, err := docker.DockerInfo()
131+
if err != nil {
132+
return nil, err
133+
}
134+
context := fs.Context{DockerRoot: docker.RootDir(), DockerInfo: dockerInfo}
130135
fsInfo, err := fs.NewFsInfo(context)
131136
if err != nil {
132137
return nil, err
@@ -1188,19 +1193,13 @@ func (m *manager) DockerInfo() (DockerStatus, error) {
11881193
out.NumContainers = n
11891194
}
11901195
}
1191-
// cut, trim, cut - Example format:
1192-
// DriverStatus=[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirperm1 Supported","false"]]
11931196
if val, ok := info["DriverStatus"]; ok {
1197+
var driverStatus [][]string
1198+
err = json.Unmarshal([]byte(val), &driverStatus)
11941199
out.DriverStatus = make(map[string]string)
1195-
val = strings.TrimPrefix(val, "[[")
1196-
val = strings.TrimSuffix(val, "]]")
1197-
vals := strings.Split(val, "],[")
1198-
for _, v := range vals {
1199-
kv := strings.Split(v, "\",\"")
1200-
if len(kv) != 2 {
1201-
continue
1202-
} else {
1203-
out.DriverStatus[strings.Trim(kv[0], "\"")] = strings.Trim(kv[1], "\"")
1200+
for _, v := range driverStatus {
1201+
if len(v) == 2 {
1202+
out.DriverStatus[v[0]] = v[1]
12041203
}
12051204
}
12061205
}

0 commit comments

Comments
 (0)