Skip to content

Cadvisor now publishes per-container inode stats#1489

Merged
timstclair merged 1 commit intogoogle:masterfrom
dashpole:per_container_inode_stats
Oct 13, 2016
Merged

Cadvisor now publishes per-container inode stats#1489
timstclair merged 1 commit intogoogle:masterfrom
dashpole:per_container_inode_stats

Conversation

@dashpole
Copy link
Copy Markdown
Collaborator

@dashpole dashpole commented Oct 3, 2016

Per-container stats available in the v2 api in the FileSystemStats.InodeUsage field. They are available in the v1 stats in the Filesystem.Inodes field. Per-container Inode stats collected using the command "find . -xdev | wc -l". The collection of inode stats uses the same back-off strategy as measuring per container bytes used, so that slow calls to find do not severely impact performance.

@dashpole
Copy link
Copy Markdown
Collaborator Author

dashpole commented Oct 3, 2016

Issue: kubernetes/kubernetes#33382

Copy link
Copy Markdown
Contributor

@timstclair timstclair left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a few suggestions around optimizing the file counting. I don't know if it's all necessary, but maybe worth considering.

Comment thread container/common/fsHandler.go Outdated
type FsHandler interface {
Start()
Usage() (baseUsageBytes uint64, totalUsageBytes uint64)
Usage() (baseUsageBytes, totalUsageBytes, inodeUsage uint64)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Can we create a struct to return with this info instead? IMHO there are very few situations where a method should return more than 2 values...

}
}
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about extracting something like a "BackoffPoller" that abstracts all the shared backoff & polling logic between trackInodesUsage & trackDiskUsage?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, on further thought I'm wondering if this should be in the same routine as the du call. The advantages are that containers with very large filesystems wouldn't hog as many exec calls, and there might be less disk thrashing.

Comment thread container/rkt/handler.go Outdated

if !ignoreMetrics.Has(container.DiskUsageMetrics) {
handler.fsHandler = common.NewFsHandler(time.Minute, rootfsStorageDir, "", fsInfo)
handler.fsHandler = common.NewFsHandler(time.Minute, time.Minute, rootfsStorageDir, "", fsInfo)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: make constants for the default periods

Comment thread container/common/fsHandler.go Outdated
err error
)

if fh.rootfs != "" {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Under what conditions would the rootfs not be set? It should probably be an error, or exit the tracking loop...

Comment thread fs/fs.go Outdated
}
claimFindToken()
defer releaseFindToken()
findCmd := exec.Command("find", dir, "-xdev")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I tried this, I found find $dir -xdev -printf '.' | wc -c to be about twice as fast. Might be a premature optimization though...

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ill go ahead and try it out. It shouldnt hurt to make it faster, and it should still work.

Comment thread fs/fs.go Outdated
glog.Errorf("failed to read from stdout for cmds: %v, %v - %v", findCmd.Args, wcCmd.Args, souterr)
}
// return 0, fmt.Errorf("cmd %v output %s", cmd.Args, stdout)
inodeUsage, err := strconv.ParseUint(strings.Fields(stdout)[0], 10, 64)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the fields call necessary?

Copy link
Copy Markdown
Collaborator Author

@dashpole dashpole Oct 4, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stdout = int64 + \n, so the Fields call was just to remove the newline character.

Comment thread container/common/fsHandler.go Outdated
func (fh *realFsHandler) Start() {
go fh.trackUsage()
go fh.trackDiskUsage()
go fh.trackInodeUsage()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to track inode usage for devicemapper? If not we should skip it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK, inodes cannot be tracked for devicemapper.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the note I made in the Issue. I was able to test this, and the find command works correctly (and takes almost no time, since it always finds 0 inodes) with devicemapper.

Comment thread fs/fs.go Outdated
}
claimFindToken()
defer releaseFindToken()
findCmd := exec.Command("find", dir, "-xdev")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will double count inodes on base layers shared across containers. That's probably what we want, but it means killing a countainer will not reclaim all it's reported inodes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should not be doing that. dir here is expected to be the writable layer of a container. We need to test this.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am very likely wrong, but I think the "rootfs" directory that it is pointed at is the container's modifiable directory, and shouldn't contain the base layers. At least in most of my tests, the containers start out reporting 0 inodes used, which would indicate to me that they are not counting any shared resources in base layers.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If that's the case, shouldn't it be zero for a new container? That's not what I'm seeing.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this. It only includes the writeable layer.

Comment thread fs/fs_test.go Outdated
}
inodes, err := fsInfo.GetDirInodeUsage(dir, time.Minute)
as.NoError(err)
as.True(uint64(numFiles) <= inodes, "expected inodes in dir to be at-least %d; got inodes: %d", numFiles, inodes)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't numFiles == inodes? Under what circumstances would it be different?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it is actually, numFiles + 1 == inodes, since the directory I created counts as well. I could change it to that, but I wasn't sure if it would consistently be the case.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed, I changed it to numFiles +1 == inodes

Comment thread info/v2/conversion.go
stat.Filesystem = &FilesystemStats{
TotalUsageBytes: &val.Filesystem[0].Usage,
BaseUsageBytes: &val.Filesystem[0].BaseUsage,
InodeUsage: &val.Filesystem[0].Inodes,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we skip inode collection for some storage drivers, then we should probably leave InodeUsage nil here when it's not collected.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that is what actually happens. &val.Filesystem[0].Inodes may be nil if we skipped collection. In that case, it should also set InodeUsage to nil as well?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. If you're taking the address of a variable, it will always be non-nil. The value might be zero, but if possible we should distinguish between 0 and not set (nil).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it actually be more accurate to set InodesUsed to 0 in that case? We only skip collection for some storage drivers if they do not use inodes, which means they use 0 of the inodes shared by the rest of the pods.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to document, since these storage drivers do not use inodes, I am leaving this the way it is, since using 0 inodes is equivalent to using no inodes. Also, making a change here would require chaning the v1 api, or a large refactor of the code.

@timstclair timstclair self-assigned this Oct 4, 2016
@timstclair
Copy link
Copy Markdown
Contributor

ok to test

@timstclair timstclair closed this Oct 4, 2016
@timstclair timstclair reopened this Oct 4, 2016
@timstclair
Copy link
Copy Markdown
Contributor

@k8s-bot test this

@ixdy
Copy link
Copy Markdown

ixdy commented Oct 4, 2016

@k8s-bot ok to test

@spxtr
Copy link
Copy Markdown

spxtr commented Oct 4, 2016

@k8s-bot test this

It didn't have permissions to set the status, but the test ran :P

@dashpole
Copy link
Copy Markdown
Collaborator Author

dashpole commented Oct 4, 2016

pushed a commit addressing all comments except device-mapper related ones.

@dashpole dashpole force-pushed the per_container_inode_stats branch from d0aee10 to 9835f82 Compare October 6, 2016 17:40
Comment thread container/common/fsHandler.go Outdated
TotalUsageBytes: 0,
BaseUsageBytes: 0,
InodeUsage: 0,
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: no need to specify this (values default to 0)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment thread container/common/fsHandler.go Outdated
if extraDiskErr == nil && fh.extraDir != "" {
fh.usage.BaseUsageBytes = baseUsage
}
//combine errors into a single error to return
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: space after //, start comments with a Capital.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment thread fs/fs.go Outdated
const maxConcurrentDus = 20

// The maximum number of `find` tasks that can be running at once.
const maxConcurrentFinds = 20
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a separate pool for each of these? On the one hand, if one operation is significantly slower, it prevents it from hogging the tokens. On the other hand, the operations happen in sequence, so the one operation will be blocking anyway. I'm leaning towards a single "exec" pool.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment thread fs/fs.go Outdated
wcCmd := exec.Command("wc", "-c")
wcCmd.Stdin, _ = findCmd.StdoutPipe()

stdoutp, err := wcCmd.StdoutPipe()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to assign a bytes.Buffer to stdout and stderr. I'm not positive, but I tihnk the process could hang if the pipe fills up, waiting for it to be drained.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment thread fs/fs_test.go Outdated
as.True(expectedSize <= size, "expected dir size to be at-least %d; got size: %d", expectedSize, size)
}

//make sure that the timeout is actually being triggered (found this bug in PR#1489)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: spaces & capitals, same below.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment thread fs/fs_test.go Outdated
dir, err := ioutil.TempDir(os.TempDir(), "")
as.NoError(err)
defer os.RemoveAll(dir)
dataSize := 1024 * 10000 //1000 KB bigger to make sure it triggers tihe timeout
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/tihe/the/

Did you mean 1,000 KB or 10,000 KB?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like you missed the typo

Comment thread fs/fs_test.go Outdated
numFiles := 1000
for i := 0; i < numFiles; i++ {
_, err := ioutil.TempFile(dir, "")
as.NoError(err)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should use a require here, otherwise this could be a lot of error spam.

Comment thread fs/fs_test.go Outdated
numFiles := 100000 //make sure we actually trigger the timeout
for i := 0; i < numFiles; i++ {
_, err := ioutil.TempFile(dir, "")
as.NoError(err)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

@dashpole dashpole force-pushed the per_container_inode_stats branch from eb60f6e to e92659c Compare October 11, 2016 20:12
@k8s-ci-robot
Copy link
Copy Markdown
Collaborator

Jenkins GCE e2e failed for commit acb2426. Full PR test history.

The magic incantation to run this job again is @k8s-bot test this. Please help us cut down flakes by linking to an open flake issue when you hit one in your PR.

@timstclair
Copy link
Copy Markdown
Contributor

LGTM once you fix the typo and squash the commits.

@dashpole dashpole force-pushed the per_container_inode_stats branch from ee2e96e to 633578d Compare October 12, 2016 20:13
…ind . -xdev printf '.'| wc -c' this is published in the v2 api using a new field
@dashpole dashpole force-pushed the per_container_inode_stats branch from 633578d to 9fdeefe Compare October 12, 2016 20:16
@timstclair timstclair merged commit e972272 into google:master Oct 13, 2016
@dashpole dashpole deleted the per_container_inode_stats branch October 13, 2016 20:23
tengqm added a commit to tengqm/website that referenced this pull request Jan 1, 2018
Closes: kubernetes#2896

According to the report, the google/cadvisor#1422 has been closed.
However, the related issue has been fixed in google/cadvisor#1489 and
merged a long time ago. We can safely remove the known issue now.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants