컨테이너에 대해서만 커널과 프로세스와 같은 시스템 메트릭을 보고 싶을 때가 있다.
이를 직접 프로세스 레벨을 훔쳐서 OTLP 로 보내도 되지만, 서버나 환경이 다양한 경우 제한사항이 존재한다.
CAdvisor 를 사용하면 코드 없이 간단히 설정하여 수집하게 할 수 있다.
그렇다면 CAdvisor 는 무엇이고 어떻게 동작하는지 알아보자.


컨테이너 어드바이저(cAdvisor)는 Google이 개발한 오픈소스 컨테이너 모니터링 도구입니다. 파일 시스템 및 네트워크 정보, CPU, 메모리 사용량 등 컨테이너 기반 메트릭을 수집, 집계, 처리 및 내보낼 수 있습니다.
이 도구는 단일 쿠버네티스 클러스터부터 완전한 도커 설치 환경에 이르기까지 모든 컨테이너화된 환경에서 사용하기 쉽습니다. 다용도성, 사용 편의성, 거의 모든 모니터링 요구 사항을 충족할 수 있는 능력 덕분에 cAdvisor는 컨테이너 모니터링을 위한 최고의 선택지 중 하나입니다.
cAdvisor가 Docker 컨테이너로 실행될 경우, 다른 컨테이너를 모니터링하기 위해 호스트의 컨테이너 런타임 및 파일 시스템에 접근할 수 있어야 합니다.
핵심은 필요한 호스트 디렉터리를 cAdvisor 컨테이너에 마운트하는 것입니다:
volume=/var/run:/var/run:ro 마운트는 cAdvisor가 Docker 데몬 소켓에 접근할 수 있게 하여 Docker와 통신하고 호스트에서 실행 중인 모든 컨테이너를 탐색할 수 있게 합니다. running.md:9-17volume=/:/rootfs:ro 마운트는 호스트의 전체 파일 시스템에 대한 접근을 제공하며, cAdvisor는 이를 통해 컨테이너 데이터를 읽습니다.volume=/sys:/sys:ro 마운트는 cAdvisor가 모든 컨테이너의 cgroup 정보를 읽을 수 있도록 합니다.sudo docker run \
--volume=/:/rootfs:ro \
--volume=/var/run:/var/run:ro \
--volume=/sys:/sys:ro \
--volume=/var/lib/docker/:/var/lib/docker:ro \
--volume=/dev/disk/:/dev/disk:ro \
--publish=8080:8080 \
--detach=true \
--name=cadvisor \
--privileged \
--device=/dev/kmsg \
gcr.io/cadvisor/cadvisor:$VERSION
Docker Compose 설정에서 cAdvisor를 다른 컨테이너와 함께 실행할 때 cAdvisor는 다음 이유로 모든 컨테이너를 모니터링할 수 있습니다:
공유 Docker 데몬
Docker Compose 배포 환경의 모든 컨테이너는 호스트의 동일한 Docker 데몬을 공유합니다.
컨테이너 감지
cAdvisor의 Docker 팩토리는 Docker 클라이언트를 사용하여 컨테이너를 검사하고 발견합니다. factory.go:167-184
ContainerInspect()를 호출하여 컨테이너가 실행 중이며 Docker에 인식되는지 확인합니다. factory.go:178-181
// Docker handles all containers under /docker
func (f *dockerFactory) CanHandleAndAccept(name string) (bool, bool, error) {
// if the container is not associated with docker, we can't handle it or accept it.
if !dockerutil.IsContainerName(name) {
return false, false, nil
}
// Check if the container is known to docker and it is active.
id := dockerutil.ContainerNameToId(name)
// We assume that if Inspect fails then the container is not known to docker.
ctnr, err := f.client.ContainerInspect(context.Background(), id)
if err != nil || !ctnr.State.Running {
return false, true, fmt.Errorf("error inspecting container: %v", err)
}
return true, true, nil
}
네임스페이스 처리
매니저는 /rootfs/proc의 존재 여부를 확인하여 cAdvisor가 호스트 네임스페이스에서 실행 중인지 감지합니다. manager.go:183-188
호스트 네임스페이스가 아닌 경우(컨테이너화됨), 경로 앞에 /rootfs를 접두사로 추가하여 경로를 조정합니다. handler.go:138-142
rootFs := "/"
if !inHostNamespace {
rootFs = "/rootfs"
storageDir = path.Join(rootFs, storageDir)
}
컨테이너 핸들러 생성
발견된 각 컨테이너에 대해 cAdvisor는 마운트된 볼륨을 사용하여 컨테이너 데이터에 접근하는 핸들러를 생성합니다. manager.go:913-931
컨테이너화된 상태로 실행될 때 핸들러는 inHostNamespace를 false로 설정하여 초기화됩니다. manager.go:185-188
func (m *manager) createContainerLocked(containerName string, watchSource watcher.ContainerWatchSource) error {
namespacedName := namespacedContainerName{
Name: containerName,
}
// Check that the container didn't already exist.
if _, ok := m.containers[namespacedName]; ok {
return nil
}
handler, accept, err := container.NewContainerHandler(containerName, watchSource, m.containerEnvMetadataWhiteList, m.inHostNamespace)
if err != nil {
return err
}
if !accept {
// ignoring this container.
klog.V(4).Infof("ignoring container %q", containerName)
return nil
}
collectorManager, err := collector.NewCollectorManager()
if err != nil {
return err
}
logUsage := *logCadvisorUsage && containerName == m.cadvisorContainer
cont, err := newContainerData(containerName, m.memoryCache, handler, logUsage, collectorManager, m.maxHousekeepingInterval, m.allowDynamicHousekeeping, clock.RealClock{})
if err != nil {
return err
}
,,,,
}
중요한 요구 사항은 cAdvisor 컨테이너가 Docker 소켓과 호스트 파일 시스템에 접근하기 위해 적절한 볼륨 마운트를 가져야 한다는 것입니다.
이러한 마운트가 없으면 cAdvisor는 동일한 Docker Compose 배포에 있더라도 다른 컨테이너를 검색하거나 모니터링할 수 없습니다.
일부 시스템(RHEL/CentOS 등)에서는 cAdvisor가 Docker 데몬에 충분한 접근 권한을 얻기 위해 --privileged 플래그가 필요할 수도 있습니다.
아래와 같이 docker-compose.yml 선언
services:
# CAdvisor
# 컨테이너 메트릭 수집: CPU, 메모리, 네트워크, 디스크 사용량
# Prometheus 형식으로 메트릭 노출 (HTTP :8080/metrics)
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: cadvisor
ports:
- 8080:8080
volumes:
# 호스트 루트 파일시스템 (읽기 전용)
# 전체 파일시스템 구조 접근으로 컨테이너 정보 수집
- /:/rootfs:ro
# 실행 중인 프로세스 및 소켓 정보
# Docker 소켓 및 런타임 정보 접근
- /var/run:/var/run:ro
# 시스템 정보 및 커널 통계
# CPU, 메모리, 네트워크 인터페이스 정보 수집
- /sys:/sys:ro
# Docker 컨테이너 메타데이터 및 로그
# 컨테이너별 상세 정보 및 통계 수집
- /var/lib/docker/:/var/lib/docker:ro
# 디스크 디바이스 정보
# 컨테이너별 디스크 I/O 통계 수집
- /dev/disk/:/dev/disk:ro
# 시스템 레벨 접근 권한 필요 (cgroup, namespace 접근)
privileged: true
devices:
# 커널 메시지 버퍼 접근
# 시스템 로그 및 이벤트 모니터링
- /dev/kmsg
이외 플래그 값들에 대해서는 아래를 추가로 살펴보자
https://github.com/google/cadvisor/blob/master/docs/runtime_options.md
# ====== receivers 선언부 ======
# https://opentelemetry.io/docs/collector/configuration/#receivers
receivers:
# 웹 서버 컨테이너로부터
# metric, log, trace 전송받음 (OTLP 프로토콜)
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
# Node Exporter로부터 시스템 메트릭 수집
# Prometheus 스크랩 방식으로 수집
prometheus:
config:
scrape_configs:
# Node Exporter
- job_name: 'node-exporter'
scrape_interval: 15s
static_configs:
- targets: ['node_exporter:9100']
# CAdvisor
# metric_relabel_configs 을 통해
# hello-world 에 대해서만 수집하도록 필터링
- job_name: 'cadvisor'
scrape_interval: 15s
static_configs:
- targets: ['cadvisor:8080']
metric_relabel_configs:
- source_labels: [name]
regex: 'hello-world'
action: keep
# ,,, processor 나 exporter 에 대해서는 생략 ,,,
https://cast.ai/blog/cadvisor/
https://github.com/google/cadvisor/blob/5adb1c3b/docs/running.md