15、深入探索 cAdvisor:容器监控利器

深入探索 cAdvisor:容器监控利器

1. 引言

在容器化技术盛行的今天,对容器的资源使用和性能进行监控变得至关重要。cAdvisor(Container Advisor)作为一个开源项目,正是为满足这一需求而诞生的。它可以收集运行中容器的资源使用和性能数据,尤其对 Docker 容器的支持十分出色。

2. cAdvisor 简介

cAdvisor 是一个用于收集容器资源使用和性能数据的开源项目,其完整源代码可在 https://github.com/google/cadvisor 找到,本文使用的是 v0.39.3 版本。选择该项目的原因在于它能进一步探索系统调用监控文件系统、使用 cgroups 以及通过 /proc 和 /sys 收集机器信息等主题。

3. 运行 cAdvisor

要在本地运行 cAdvisor,可按以下步骤操作:
1. 下载源代码 :使用以下命令下载 cAdvisor 的源代码。

GO111MODULE=off go get github.com/google/cadvisor

此命令会使用 go get 从指定 URL 下载源代码, GO111MODULE=off 环境变量会让 Go 工具将模块(google/cadvisor)存储在 GOPATH 目录中。
2. 构建项目 :进入 GOPATH/src/github.com/google/cadvisor/cmd 目录,运行以下命令构建项目。

go build -o cadvisor

这将生成一个名为 cadvisor 的可执行文件。
3. 查看参数 :运行以下命令查看 cAdvisor 可接受的不同参数。

./cadvisor –help

输出结果包含众多参数,如 -add_dir_header -boot_id_file -v 等,这里不一一赘述,可使用其默认值。
4. 运行 cAdvisor :由于 cAdvisor 需要 root 权限才能运行,可使用以下命令启动。

sudo ./cadvisor -v 9

默认情况下,cAdvisor 使用 8080 端口运行应用程序。若该端口已被其他应用占用,可使用 -port 标志指定不同端口。

sudo ./cadvisor -port <port_number>

启动后,终端会输出大量日志信息。

4. 访问 cAdvisor 网页界面

当 cAdvisor 成功运行后,打开浏览器,输入 http://localhost:8080 即可访问其用户界面。界面展示了丰富的数据,如系统切片、内存和磁盘使用情况、正在运行的进程信息以及 Docker 容器信息等。以下是界面主要信息的介绍:
- 子容器视图 :展示了名为 subcontainers 的子目录,提供了重要的统计和性能信息,用于报告目的。
- 系统服务视图 :点击 system.slice 链接,可查看本地机器上运行的不同服务。
- 资源使用情况 :界面显示了内存和磁盘使用的百分比。
- 进程信息 :展示了正在运行的进程的名称、CPU 使用、内存使用、运行时间等信息。
- Docker 容器信息 :点击 Docker Containers 链接,可查看本地运行的所有 Docker 容器,点击具体容器可查看其相关指标。

5. cAdvisor 架构

cAdvisor 的架构包含多个组件,各组件协同工作以实现容器监控功能。以下是主要组件及其功能:
| 组件 | 功能 |
| ---- | ---- |
| Events Channel | 用于报告容器的创建或删除事件 |
| InMemoryCache | 存储被监控容器的相关指标信息 |
| Container Watcher | 监控容器活动 |
| Containers | 被 cAdvisor 监控的不同容器 |
| Machine Info | 与 cAdvisor 运行所在本地机器相关的信息 |
| Plugins | 支持不同的容器技术,如 Docker、Mesos、CRIO、Systemd 和 ContainerD |
| Handlers | 处理 cAdvisor 暴露的指标和其他相关 API 的 HTTP 请求 |

6. cAdvisor 初始化

和其他 Go 应用一样,cAdvisor 的入口点是 main.go main() 函数执行以下初始化步骤:
1. 设置缓存 :初始化用于存储容器及其指标的缓存。

memoryStorage, err := NewMemoryStorage()
if err != nil {
    klog.Fatalf("Failed to initialize storage driver: %s", err)
}
  1. 设置 Manager :Manager 是 cAdvisor 的核心组件,负责监控容器的主要处理工作。
resourceManager, err := manager.New(memoryStorage, sysFs, housekeepingConfig, includedMetrics, &collectorHttpClient, strings.Split(*rawCgroupPrefixWhiteList, ","), *perfEvents)
  1. 设置 HTTP 处理程序 :允许 Web 用户界面获取不同容器的指标数据。
cadvisorhttp.RegisterPrometheusHandler(mux, resourceManager, *prometheusEndpoint, containerLabelFunc, includedMetrics)
rootMux := http.NewServeMux()
  1. 启动 Manager :开始收集容器和指标。

缓存管理由 InMemoryCache 负责,其定义如下:

type InMemoryCache struct {
    lock              sync.RWMutex
    containerCacheMap map[string]*containerCache
    maxAge            time.Duration
    backend           []storage.StorageDriver
}
7. Manager 组件

Manager 是 cAdvisor 的核心,负责容器的初始化、维护和指标报告。其接口定义如下:

type Manager interface {
    Start() error
    Stop() error
    GetContainerInfo(containerName string, query *info.ContainerInfoRequest) (*info.ContainerInfo, error)
    GetContainerInfoV2(containerName string, options v2.RequestOptions) (map[string]v2.ContainerInfo, error)
    SubcontainersInfo(containerName string, query *info.ContainerInfoRequest) ([]*info.ContainerInfo, error)
    AllDockerContainers(query *info.ContainerInfoRequest) (map[string]info.ContainerInfo, error)
    DockerContainer(dockerName string, query *info.ContainerInfoRequest) (info.ContainerInfo, error)
    GetContainerSpec(containerName string, options v2.RequestOptions) (map[string]v2.ContainerSpec, error)
    GetDerivedStats(containerName string, options v2.RequestOptions) (map[string]v2.DerivedStats, error)
    GetRequestedContainersInfo(containerName string, options v2.RequestOptions) (map[string]*info.ContainerInfo, error)
    Exists(containerName string) bool
    GetMachineInfo() (*info.MachineInfo, error)
    GetVersionInfo() (*info.VersionInfo, error)
    GetFsInfoByFsUUID(uuid string) (v2.FsInfo, error)
    GetDirFsInfo(dir string) (v2.FsInfo, error)
    GetFsInfo(label string) ([]v2.FsInfo, error)
    GetProcessList(containerName string, options v2.RequestOptions) ([]v2.ProcessInfo, error)
    WatchForEvents(request *events.Request) (*events.EventChannel, error)
    GetPastEvents(request *events.Request) ([]*info.Event, error)
    CloseEventChannel(watchID int)
    DockerInfo() (info.DockerStatus, error)
    DockerImages() ([]info.DockerImage, error)
    DebugInfo() map[string][]string
}

Manager 使用不同容器技术的插件,例如 Docker 插件负责与 Docker 引擎通信,其代码位于 container/docker/plugin.go 文件中。

8. 容器监控流程

Manager 在监控容器前,需要先确定要监控的容器。这一过程在 Start() 函数调用时完成,主要步骤如下:
1. 注册容器工厂 :调用 raw.Register() 函数注册原始容器工厂。

err := raw.Register(m, m.fsInfo, m.includedMetrics, m.rawContainerCgroupPathPrefixWhiteList)
if err != nil {
    klog.Errorf("Registration of the raw container factory failed: %v", err)
}
  1. 创建容器监控器 :创建 rawContainerWatcher 并将其添加到 containerWatchers 列表中。
rawWatcher, err := raw.NewRawContainerWatcher()
if err != nil {
    return err
}
m.containerWatchers = append(m.containerWatchers, rawWatcher)
  1. 创建根容器 :调用 m.createContainer("/", watcher.Raw) 创建根容器。
err = m.createContainer("/", watcher.Raw)
if err != nil {
    return err
}
  1. 检测子容器 :调用 m.detectSubcontainers("/") 检测 /sys/fs/cgroup 目录下的子目录,并将其作为容器进行监控。
err = m.detectSubcontainers("/")
if err != nil {
    return err
}
  1. 启动容器监控 :调用 watchForNewContainers() 函数启动所有容器监控器。
func (m *manager) watchForNewContainers(quit chan error) error {
    watched := []watcher.ContainerWatcher{}
    for _, watcher := range m.containerWatchers {
        err := watcher.Start(m.eventsChannel)
        if err != nil {
            for _, w := range watched {
                stopErr := w.Stop()
                // 处理停止错误
            }
            return err
        }
        watched = append(watched, watcher)
    }
    err := m.detectSubcontainers("/")
    // 处理错误
    return nil
}
9. 监控文件系统

cAdvisor 使用 Linux 内核提供的 inotify API( https://linux.die.net/man/7/inotify )来监控文件系统事件,如文件的创建或删除。其工作流程如下:
1. 启动监控 :在 watchForNewContainers 函数调用时,会调用 container/raw/watcher.go 中的 Start 函数启动 inotify 监控。

func (w *rawContainerWatcher) Start(events chan watcher.ContainerEvent) error {
    watched := make([]string, 0)
    for _, cgroupPath := range w.cgroupPaths {
        _, err := w.watchDirectory(events, cgroupPath, "/")
        // 处理错误
        watched = append(watched, cgroupPath)
    }
    go func() {
        for {
            select {
            case event := <-w.watcher.Event():
                err := w.processEvent(event, events)
                // 处理错误
            case err := <-w.watcher.Error():
                // 处理错误
            case <-w.stopWatcher:
                err := w.watcher.Close()
                // 处理错误
            }
        }
    }()
    return nil
}
  1. 处理事件 w.processEvent() 函数将接收到的 inotify 事件转换为内部事件,如 watcher.ContainerAdd watcher.ContainerDelete
func (w *rawContainerWatcher) processEvent(event *inotify.Event, events chan watcher.ContainerEvent) error {
    var eventType watcher.ContainerEventType
    switch {
    case (event.Mask & inotify.InCreate) > 0:
        eventType = watcher.ContainerAdd
    case (event.Mask & inotify.InDelete) > 0:
        eventType = watcher.ContainerDelete
    // 其他情况处理
    }
    switch eventType {
    case watcher.ContainerAdd:
        alreadyWatched, err := w.watchDirectory(events, event.Name, containerName)
        // 处理错误
    case watcher.ContainerDelete:
        lastWatched, err := w.watcher.RemoveWatch(containerName, event.Name)
        // 处理错误
    default:
        return fmt.Errorf("unknown event type %v", eventType)
    }
    events <- watcher.ContainerEvent{
        EventType:   eventType,
        Name:        containerName,
        WatchSource: watcher.Raw,
    }
    return nil
}
10. 从 /sys 和 /proc 收集信息

cAdvisor 通过 /sys 和 /proc 文件系统收集机器信息,并将其作为指标信息的一部分进行报告。以下是一些关键函数及其功能:
- 收集内存信息 GetMachineMemoryCapacity() 函数通过读取 /proc/meminfo 文件并解析其中的信息来获取机器的内存容量。

func GetMachineMemoryCapacity() (uint64, error) {
    out, err := ioutil.ReadFile("/proc/meminfo")
    if err != nil {
        return 0, err
    }
    memoryCapacity, err := parseCapacity(out, memoryCapacityRegexp)
    if err != nil {
        return 0, err
    }
    return memoryCapacity, err
}
  • 收集文件系统信息 GetGlobalFsInfo() 函数调用 GetFsInfoForPath() 函数,后者又调用 getDiskStatsMap("/proc/diskstats") 来读取和解析 /proc/diskstats 文件中的信息。
func (i *RealFsInfo) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, error) {
    diskStatsMap, err := getDiskStatsMap("/proc/diskstats")
    // 处理错误
    return filesystems, nil
}
  • 收集网络设备信息 GetNetworkDevices() 函数通过 /sys/class/net 目录获取网络设备信息。
func GetNetworkDevices(sysfs sysfs.SysFs) ([]info.NetInfo, error) {
    devs, err := sysfs.GetNetworkDevices()
    // 处理错误
    return netDevices, nil
}

综上所述,cAdvisor 是一个功能强大的容器监控工具,通过系统调用、cgroups 以及 /proc 和 /sys 文件系统,能够全面收集容器的资源使用和性能数据。其架构设计合理,组件之间协同工作,为容器监控提供了可靠的解决方案。

深入探索 cAdvisor:容器监控利器

11. 监控流程总结

cAdvisor 的容器监控流程可以用以下 mermaid 流程图表示:

graph LR
    A[Manager 启动] --> B[注册容器工厂]
    B --> C[创建容器监控器]
    C --> D[创建根容器]
    D --> E[检测子容器]
    E --> F[启动容器监控]
    F --> G[使用 inotify 监控文件系统]
    G --> H[处理文件系统事件]
    H --> I[收集 /sys 和 /proc 信息]

这个流程清晰地展示了 cAdvisor 从启动到最终实现容器监控和信息收集的整个过程。

12. 不同组件的交互

cAdvisor 各组件之间的交互关系对于理解其工作原理至关重要。以下是一个简要的交互说明:
- Manager 与插件 :Manager 使用不同的插件来与各种容器技术进行通信。例如,Docker 插件负责与 Docker 引擎交互,为 Manager 提供 Docker 容器的相关信息。
- Manager 与 Container Watcher :Manager 启动 Container Watcher 来监控容器活动。Container Watcher 会将监控到的事件通过 Events Channel 反馈给 Manager。
- Manager 与 InMemoryCache :Manager 将收集到的容器指标信息存储在 InMemoryCache 中,以便后续的查询和使用。
- Handlers 与 Manager :Handlers 负责处理 HTTP 请求,当用户通过网页界面或 API 请求容器指标数据时,Handlers 会从 Manager 中获取相应的数据并返回给用户。

13. 实际应用场景

cAdvisor 在实际应用中有多种场景,以下是一些常见的例子:
- 性能监控 :实时监控容器的 CPU、内存、磁盘和网络使用情况,帮助管理员及时发现性能瓶颈和异常。
- 资源管理 :根据容器的资源使用情况,合理分配系统资源,提高资源利用率。
- 故障排查 :当容器出现故障时,通过查看 cAdvisor 收集的详细信息,快速定位问题所在。
- 容量规划 :分析容器的资源使用趋势,为未来的系统扩容和升级提供依据。

14. 注意事项

在使用 cAdvisor 时,需要注意以下几点:
- 权限问题 :cAdvisor 需要 root 权限才能运行,因为它需要访问系统的敏感信息,如 /proc 和 /sys 文件系统。
- 端口冲突 :默认情况下,cAdvisor 使用 8080 端口。如果该端口已被其他应用占用,需要使用 -port 标志指定不同的端口。
- 日志管理 :cAdvisor 运行时会产生大量的日志信息,需要合理管理日志文件,避免占用过多的磁盘空间。

15. 总结

通过对 cAdvisor 的深入探索,我们了解到它是一个功能强大且灵活的容器监控工具。它利用系统调用、cgroups 以及 /proc 和 /sys 文件系统,能够全面收集容器的资源使用和性能数据。其架构设计合理,各个组件协同工作,为容器监控提供了可靠的解决方案。

以下是 cAdvisor 的主要特点总结:
| 特点 | 描述 |
| ---- | ---- |
| 开源项目 | 完整源代码可在 GitHub 上获取,方便开发者进行定制和扩展。 |
| 多容器支持 | 支持 Docker、Mesos、CRIO、Systemd 和 ContainerD 等多种容器技术。 |
| 实时监控 | 实时收集容器的资源使用和性能数据,帮助管理员及时发现问题。 |
| 网页界面 | 提供直观的网页界面,方便用户查看和分析数据。 |
| API 支持 | 暴露 API 接口,允许用户通过编程方式获取容器指标数据。 |

如果你正在寻找一个可靠的容器监控工具,cAdvisor 绝对是一个值得考虑的选择。它可以帮助你更好地管理和优化容器化应用程序,提高系统的稳定性和性能。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值