[모니터링] HTTP Service Discovery

옵주비·2023년 1월 31일
2

직접 작성하는 Monitoring 레퍼런스

이번 인턴 프로젝트에서 모니터링 부분을 담당하고 있는데 정말 쉽지 않다. 일단 참고할 수 있는 자료가 없다시피 하다. 공식 홈페이지에 나와있는 설명은 너무나도 부족했고, 깃허브로 들어가도 그래서 어떻게 해야한다는 것인지에 대한 설명이 없었다. 국내, 해외 블로그도 모두 찾아봤지만 대부분 실속이 없었다. 공홈에 있는 내용을 그대로 번역해놓거나, 결과 스크린샷만 성의없이 나열해놓은 것들이 많았다. 그래서 정말 맨땅에 헤딩해가며 여러 시행착오를 겪어가며 구현해보고 있고, 그런 내용들을 시간이 날 때마다 정리하려고 한다. 같은 주제로 프로젝트를 구현하는 팀들이 있기에, 어쩔 수 없이 글 공개는 프로젝트가 끝난 이후 할 예정이다 😭 나도 이러고 싶진 않지만.... 회사가 너무 마음에 들어서 꼭 남고 싶다.

HTTP Service Discovery

HTTP Service Discovery(이하 SD)가 맞다. 눈을 의심했을 수 있겠지만, 그냥 서비스 SD 말고, File SD 말고, HTTP SD에 대한 글이 맞다. 이에 레퍼런스를 찾았다면 이 글이 종착지가 되길 바라며 이 글을 작성해본다.

Service Discovery 란

여기까지 찾아온 사람이라면 이미 서비스 디스커버리가 무엇인지 대부분 파악하고 있겠지만, 그래도 정리해보고자 한다. 결국 HTTP SD도 SD의 일종인 것이니까...!

모니터링 시스템에서 동적으로 수집 대상을 관리할 수 있는 방법

한 줄로 요약하자면 위와 같다. 프로메테우스는 PULL 방식으로 수집 대상에 설치한 익스포터들로부터 메트릭 정보들을 가져오게 된다. 이 때, 수집대상을 정적으로 설정할 수도 있고 동적으로 설정할 수도 있다. 만약 수집대상의 풀이 정해져있고 변동이 적다면, static_config를 통해 정적으로 설정해놓는 것이 관리도 용이하고 가장 효율적일 것이다.

하지만 수집 대상이 서비스 운영에 따라 변화하는 환경이라면 매번 직접 바꿔주는 것보다는 서비스 디스커버리를 통해 동적으로 관리하는 것이 나을 것이다. 대표적인 서비스 디스커버리 방식은 file_sd와 http_sd가 있다.

file_sd는 지정한 특정 경로의 파일을 추가하거나 수정하면 그를 바탕으로 동적으로 수집 대상을 바꿔주는 방법이다. DBaaS(DataBase as a Service)라는 프로젝트 특성 상, 사용자가 어떤 DBMS를 선택할 것이며 어떤 조건으로 구성하게 될지를 파일로 관리하기는 불가능하다. 사용자가 서비스 생성 요청을 하면 관리자가 그걸 확인하고 나서야 생성하고 내어주는 시스템이라면 가능하겠지만, 우리가 구현하려는 서비스는 사용자 요청과 함께 자동으로 설치하고 내어주는 아마존 RDS 같은 서비스였다.

따라서 나는 http_sd를 적용하고 싶었다. 정확히는 HTTP-based service discovery인데, 정해놓은 HTTP 엔드포인트로부터 일정 주기마다 수집 대상을 가져올 수 있는 방법이다. 쉽게 설명하자면, GET 요청을 받을 수 있는 API를 만들어주고 거기서 형식에 맞게 response body와 header를 구성해서 리턴해주도록 구현하면 그것이 HTTP 엔드포인트가 된다. 그리고 Prometheus의 http_sd_configs 설정에 그 HTTP 엔드포인트에 대한 URL을 넣어주면 된다.

백문이 불여일견이니 직접 살펴보도록 하자.

백엔드 설정

참고로 파이썬의 FASTAPI로 백엔드 API 서버를 구상했는데, 이 코드는 프로젝트에 반영하기 전에 개인적으로 로컬에서 테스트해본 코드이다. 어떤 사용자가 MySQL을 3대의 노드(PSS)로 HA(High Availability)가 가능한 서비스를 신청했다면, 다음과 같이 바디를 구성해 리턴해주게 하면 된다.

from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder


@router.get("/http_sd")
def http_service_discovery():
    
    # TODO: DB 조회를 통해 targets를 유동적으로 변경 가능하도록 구성
    
    mysql_primary = {
        "targets": ["0.0.0.0:9100", "0.0.0.0:9100"],
        "labels": {
	        "dbms": "MySQL",
            "service_name": "My서비스",
            "db_name" : "프라이머리"
        }
    }
		
    mysql_secondary_A = {
        "targets": ["0.0.0.1:9100", "0.0.0.1:9104"],
        "labels": {
            "dbms": "MySQL",
            "service_name": "My서비스",
            "db_name" : "세컨더리A"
        }
    }
    
    mysql_secondary_B = {
        "targets": ["0.0.0.2:9100", "0.0.0.2:9104"],
        "labels": {
            "dbms": "MySQL",
            "service_name": "My서비스",
            "db_name" : "세컨더리B"
        }
    }
   
    
    test_body = [mysql_primary, mysql_secondary_A, mysql_secondary_B]

    # JSONResponse의 디폴트 Content-type 헤더가 application/json라 따로 명시 x
    return JSONResponse(
        status_code=200,
        content=jsonable_encoder(test_body)
    )

참고로 9100번 포트는 호스트 자체에 대한 메트릭을 수집해주는 Node Exporter를 위한 것이고, 9104번 포트는 MySQL 서버에 대한 메트릭을 수집해주는 Mysqld Exporter를 위한 것이다. Header의 요구사항 중에 'Content-Type : application/json'은 JSONResponse의 디폴트 값이라 따로 명시해주지 않았고, 혹시나해서 status_code는 명시해서 HTTP 200 Response가 가능하도록 해주었다. 라벨은 원하는 라벨을 얼마든지 붙일 수 있다. 여기서 라벨을 잘 붙이면, 그걸 기준으로 Alertmanager나 Grafana를 좀 더 서비스에 밀접하게 구성해볼 수 있을 것이다.

모니터링 시스템 설정

위에서는 body를 동적으로 구성하고 내어주는 API를 만들었다면, 이번엔 프로메테우스에 설정을 적용해볼 차례이다. 프로메테우스 구성 파일, 혹은 빅토리아 메트릭스 VMAgent 구성 파일에 속해있는 'prometheus.yml' 에서 본래 static_configs를 적어주던 'scrape_configs' 위치에 그대로 적어주면 된다.

	scrape_configs:
      - job_name: 'http-sd' # 원하는 job name
        scheme: 'http'		# http인지 https인지
        http_sd_configs:	# 위에서 설정한 GET 요청 API 주소
          - url: 'http://서비스/api/monitoring/http_sd' 
  	        refresh_interval: "15s"

만약 https가 아니라 http 도메인을 사용한다면, scheme에 꼭 'http'라고 명시해주자. 그렇지 않으면 모니터링 시스템에서 인식하지 못한다. 그리고 url에는 백엔드에서 만들어준 GET 요청을 보낼 수 있는 그 API를 적어주자. refresh_interval을 설정하면 API를 읽어올 수 있는 주기도 직접 설정할 수 있는데, 나는 15초를 설정해주었다.

(추가) 만약 빅토리아 메트릭스를 사용한다면 refresh_interval 대신에 config를 줄 때 다음과 같이 따로 명시해줄 수 있으니 참고하도록 하자.

--promscrape.httpSDCheckInterval=15s

이렇게 해주면, 'http-sd'라고 설정해준 job이 프로메테우스 상의 target에서 정상적으로 작동하는 것을 확인할 수 있다! 이렇게 HTTP 서비스 디스커버리를 구현했으니, 이제는 실시간으로 수집 대상을 동적으로 관리할 수 있다 :)

0개의 댓글