↩︎ 포트폴리오로 돌아가기..


Ceilometer 커스텀 메트릭 개발

기간: 2024.06 ~ 2024.08
역할: OpenStack Telemetry 시스템 확장 개발
기술스택: Python, Ceilometer, Gnocchi, SNMP, Dynamic Pollster

프로젝트 목표

OpenStack 기본 제공 메트릭을 넘어서 사용자 정의 메트릭 수집과 특정 인스턴스 필터링을 통한 모니터링 시스템 고도화

핵심 기술 구현

1) Custom Discovery 개발

문제: Amphora 인스턴스 등 불필요한 리소스까지 메트릭 수집으로 인한 성능 저하
해결: InstanceDiscovery 상속 기반 필터링 시스템

# cloudn_custom_discovery.py
from ceilometer.compute import discovery

class CloudnCustomDiscovery(discovery.InstanceDiscovery):
    def __init__(self, conf):
        super(CloudnCustomDiscovery, self).__init__(conf)

    def discover(self, manager, param=None):
        resources = []
        all_resources = super(CloudnCustomDiscovery, self).discover(manager, param)

        for resource in all_resources:
            # Amphora 인스턴스는 메트릭 수집 대상에서 제외
            if hasattr(resource, 'name') and resource.name.startswith('amphora'):
                continue
            resources.append(resource)

        return resources

setup.cfg 엔트리포인트 등록:

[entry_points]
ceilometer.discover.compute = 
    custom_discovery = ceilometer.compute.cloudn_custom_discovery:CloudnCustomDiscovery

polling.yaml 적용:

sources:
  - interval: 60
    meters:
      - cpu
      - memory.usage
      - disk.device.capacity
      - network.incoming.bytes
      - network.outgoing.bytes
    name: cloudn_vm_pollsters
    discovery:
      - custom_discovery  # 커스텀 디스커버리 적용
    sinks:
      - meter_sink

2) Dynamic Pollster를 통한 SNMP 메트릭 수집

문제: Ceilometer 기본 메트릭에서 제공하지 않는 호스트 레벨 CPU 사용률 필요
해결: SNMP OID 기반 커스텀 메트릭 개발

# /etc/ceilometer/pollsters.d/custom_cpu_load.yaml
- name: "custom.metric.cpu.load"
  meter_name: "custom.metric.cpu.load" 
  unit: "percent"
  namespaces: "compute"  # compute 노드에서 실행
  type: "gauge"
  host_command: >
    echo '[{"hostname": "'$(hostname)'", "value": '$(snmpwalk -v 2c -c public localhost 1.3.6.1.2.1.25.3.3.1.2 | awk '{sum += $NF} END {print sum/NR}' | sed 's/\"//g')'}]'
  value_attribute: "value"
  resource_id_attribute: "hostname"
  resource_type: "cloudn_host"
  sample_type: "gauge"
  response_handlers:
    - json

3) Ceilometer 코어 수정

문제: Dynamic Pollster의 host_command가 eval()로 파이썬 코드 파싱을 시도해 쉘 명령어 실행 제한
해결: eval() bypass를 통한 명령어 실행 확장성 확보

# ceilometer/polling/dynamic.py (수정된 부분)
def _execute_command(self, command):
    """Execute host command directly without eval() parsing"""
    try:
        # 기존 eval() 시도를 건너뛰고 직접 subprocess 실행
        result = subprocess.run(
            command, 
            shell=True, 
            capture_output=True, 
            text=True,
            timeout=30
        )

        if result.returncode == 0:
            return result.stdout.strip()
        else:
            LOG.error("Command execution failed: %s", result.stderr)
            return None

    except subprocess.TimeoutExpired:
        LOG.error("Command execution timeout: %s", command)
        return None
    except Exception as e:
        LOG.error("Command execution error: %s", e)
        return None

4) Gnocchi 리소스 타입 및 메트릭 설정

새로운 리소스 타입과 메트릭 정의:

# 커스텀 리소스 타입 생성
openstack metric resource-type create cloudn_host -a name:string:True

# 호스트별 리소스 생성
openstack metric resource create ct01-cloud-resource \
  --type cloudn_host \
  -a hostname:ct01.cloud-and

# 커스텀 메트릭 생성
gnocchi metric create custom.metric.cpu.load \
  --resource-id 7dba4e61-c045-5010-9858-140684c2cf99 \
  --archive-policy-name cloudn202407 \
  -u percent

5) Pipeline 설정

수집된 메트릭을 Gnocchi로 전송하는 파이프라인 구성:

# /etc/ceilometer/pipeline.yaml
sinks:
  - name: meter_sink
    publishers:
      - gnocchi://?filter_project=gnocchi_swift&archive_policy=cloudn202407

전체 워크플로우

  1. Dynamic Pollster 정의 → SNMP OID 기반 호스트 CPU 사용률 수집 명령어 정의
  2. Polling Service 실행 → compute 노드에서 60초 간격으로 메트릭 수집
  3. Custom Discovery 적용 → Amphora 인스턴스 필터링으로 불필요한 수집 제외
  4. Pipeline 처리 → 수집된 샘플을 Gnocchi로 전송
  5. Gnocchi 저장 → 정의된 archive policy에 따라 시계열 데이터 저장

기술적 도전과 해결

도전 1: host_command의 제약사항

  • 문제: Ceilometer 2023.1 이하 버전에서 eval() 파싱으로 인한 명령어 제한
  • 해결: subprocess 직접 호출로 우회, 보안성과 확장성 동시 확보

도전 2: 구조화된 데이터 요구사항

  • 문제: host_command 결과가 JSON/XML 형태여야 파싱 가능
  • 해결: echo로 JSON 구조 생성, response_handlers에 json 명시

도전 3: 네임스페이스와 디스커버리 조합

  • 문제: central과 compute 네임스페이스에서 서로 다른 디스커버리 동작
  • 해결: compute 네임스페이스에서 custom_discovery 적용으로 호스트별 메트릭 수집

성과 및 한계

달성 성과:

  • SNMP를 통한 호스트 레벨 CPU 사용률 메트릭 수집 성공
  • Custom Discovery로 불필요한 Amphora 인스턴스 메트릭 제거
  • Ceilometer journal log에서 정상적인 메트릭 수집 확인
  • Gnocchi에서 커스텀 리소스 타입과 메트릭 정상 등록

기술적 한계:

  • Ceilometer에서 Gnocchi로의 파이프라인 전송 이슈 (당시 미해결)
  • 수동으로 추가한 테스트 데이터는 정상 조회되지만 실제 수집 데이터 전송 실패

학습 효과:

  • OpenStack Telemetry 전체 아키텍처 (Ceilometer → Pipeline → Gnocchi) 이해
  • Python 기반 OpenStack 컴포넌트 확장 개발 경험
  • SNMP 프로토콜과 시스템 메트릭 수집 방법론 습득