github.com/google/cadvisor@v0.49.1/container/common/fsHandler.go (about)

     1  // Copyright 2015 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Handler for Docker containers.
    16  package common
    17  
    18  import (
    19  	"fmt"
    20  	"sync"
    21  	"time"
    22  
    23  	"github.com/google/cadvisor/fs"
    24  
    25  	"k8s.io/klog/v2"
    26  )
    27  
    28  type FsHandler interface {
    29  	Start()
    30  	Usage() FsUsage
    31  	Stop()
    32  }
    33  
    34  type FsUsage struct {
    35  	BaseUsageBytes  uint64
    36  	TotalUsageBytes uint64
    37  	InodeUsage      uint64
    38  }
    39  
    40  type realFsHandler struct {
    41  	sync.RWMutex
    42  	lastUpdate time.Time
    43  	usage      FsUsage
    44  	period     time.Duration
    45  	minPeriod  time.Duration
    46  	rootfs     string
    47  	extraDir   string
    48  	fsInfo     fs.FsInfo
    49  	// Tells the container to stop.
    50  	stopChan chan struct{}
    51  }
    52  
    53  const (
    54  	maxBackoffFactor = 20
    55  )
    56  
    57  const DefaultPeriod = time.Minute
    58  
    59  var _ FsHandler = &realFsHandler{}
    60  
    61  func NewFsHandler(period time.Duration, rootfs, extraDir string, fsInfo fs.FsInfo) FsHandler {
    62  	return &realFsHandler{
    63  		lastUpdate: time.Time{},
    64  		usage:      FsUsage{},
    65  		period:     period,
    66  		minPeriod:  period,
    67  		rootfs:     rootfs,
    68  		extraDir:   extraDir,
    69  		fsInfo:     fsInfo,
    70  		stopChan:   make(chan struct{}, 1),
    71  	}
    72  }
    73  
    74  func (fh *realFsHandler) update() error {
    75  	var (
    76  		rootUsage, extraUsage fs.UsageInfo
    77  		rootErr, extraErr     error
    78  	)
    79  	// TODO(vishh): Add support for external mounts.
    80  	if fh.rootfs != "" {
    81  		rootUsage, rootErr = fh.fsInfo.GetDirUsage(fh.rootfs)
    82  	}
    83  
    84  	if fh.extraDir != "" {
    85  		extraUsage, extraErr = fh.fsInfo.GetDirUsage(fh.extraDir)
    86  	}
    87  
    88  	// Wait to handle errors until after all operartions are run.
    89  	// An error in one will not cause an early return, skipping others
    90  	fh.Lock()
    91  	defer fh.Unlock()
    92  	fh.lastUpdate = time.Now()
    93  	if fh.rootfs != "" && rootErr == nil {
    94  		fh.usage.InodeUsage = rootUsage.Inodes
    95  		fh.usage.BaseUsageBytes = rootUsage.Bytes
    96  		fh.usage.TotalUsageBytes = rootUsage.Bytes
    97  	}
    98  	if fh.extraDir != "" && extraErr == nil {
    99  		if fh.rootfs != "" {
   100  			fh.usage.TotalUsageBytes += extraUsage.Bytes
   101  		} else {
   102  			// rootfs is empty, totalUsageBytes use extra usage bytes
   103  			fh.usage.TotalUsageBytes = extraUsage.Bytes
   104  		}
   105  	}
   106  
   107  	// Combine errors into a single error to return
   108  	if rootErr != nil || extraErr != nil {
   109  		return fmt.Errorf("rootDiskErr: %v, extraDiskErr: %v", rootErr, extraErr)
   110  	}
   111  	return nil
   112  }
   113  
   114  func (fh *realFsHandler) trackUsage() {
   115  	longOp := time.Second
   116  	for {
   117  		start := time.Now()
   118  		if err := fh.update(); err != nil {
   119  			klog.Errorf("failed to collect filesystem stats - %v", err)
   120  			fh.period = fh.period * 2
   121  			if fh.period > maxBackoffFactor*fh.minPeriod {
   122  				fh.period = maxBackoffFactor * fh.minPeriod
   123  			}
   124  		} else {
   125  			fh.period = fh.minPeriod
   126  		}
   127  		duration := time.Since(start)
   128  		if duration > longOp {
   129  			// adapt longOp time so that message doesn't continue to print
   130  			// if the long duration is persistent either because of slow
   131  			// disk or lots of containers.
   132  			longOp = longOp + time.Second
   133  			klog.V(2).Infof("fs: disk usage and inodes count on following dirs took %v: %v; will not log again for this container unless duration exceeds %v", duration, []string{fh.rootfs, fh.extraDir}, longOp)
   134  		}
   135  		select {
   136  		case <-fh.stopChan:
   137  			return
   138  		case <-time.After(fh.period):
   139  		}
   140  	}
   141  }
   142  
   143  func (fh *realFsHandler) Start() {
   144  	go fh.trackUsage()
   145  }
   146  
   147  func (fh *realFsHandler) Stop() {
   148  	close(fh.stopChan)
   149  }
   150  
   151  func (fh *realFsHandler) Usage() FsUsage {
   152  	fh.RLock()
   153  	defer fh.RUnlock()
   154  	return fh.usage
   155  }