naver / kaist-oss-course

Introduction to Open Source Software class @ KAIST 2016
38 stars 9 forks source link

[허블몬] 저장구조 확장 - RDBMS 로 저장하는 레이어 개발 #45

Open lynix94 opened 8 years ago

lynix94 commented 8 years ago

44 와 유사하게 저장매체로 RDBMS 를 정할 수 있다.

RDBMS 는 TSDB와 다르게 저장구조 및 탐색의 효율성, 샘플링등의 부가기능을 제공하지는 못하나,

표준 SQL을 사용하는 수준으로 구현하면, 한번 구현된 플러그인으로 모든 RDBMS 및 SQL like 한 플랫폼에 모두 적용할 수 있으며, SQL의 유연한 쿼리 파워 및 관련 툴들을 모두 사용할 수 있게 해준다.

저장 효율성등이 떨어지기는 하나 소규모 모니터링 환경에서는 더 적절한 선택일 수 있다.

본 이슈는 RDBMS 의 특정 제품에 의존적이지 않은, 표준 SQL 을 사용하되 설정단계에서 RDBMS 를 선택할 수 있도록 해결한다.

harry2636 commented 8 years ago

제가 해보겠습니다

lynix94 commented 8 years ago

먼저 로컬에 허블몬서버와 클라이언트를 설치하고 기본 동작을 확인해보시고, client, server 간 통신 순서를 파악해보시기 바랍니다.

하셔야 할 것은 아래와 같습니다.

위 역할 중 테이블을 생성하고 기록하는 기능은 collect_server/server_sql_plugin.py 로 구성하시고, 읽어오는 기능은 common/sql_reader.py 로 구성하시고 loader 의 reader 를 설정하시게 하면 됩니다.

추가로 디스크가 무한정 크지 않기 때문에 과거 데이터를 주기적으로 지워주는 로직이 있어야 하는데 이건 앞의 것이 먼저 구현되면 해보도록 하고요.

현재 구조는 (저번 발표를 참고하시면) collect_client 가 어떤 형태로 저장할지를 정해서 ('rrd' 를 설정해서) 보냅니다. 기존 구조상으로는 이런경우 rdbms 로 저장하고 싶으면 type 에 'rdbms' 라고 해야 하는데... 이러면 매번 클라이언트를 새로 구현해야 하니 묵시적으로 'rrd'로 받더라도 rdbms 로 저장할 수 있게 하는 부분이 추가 되야 하는데 이부분은 제가 수정하고 알려드리겠습니다. (좀 고민할 거리가 있어서...)

먼저 앞의 리스트들에 대해서 상세하게 고민해주시고 문의사항은 바로 댓글 달아주세요.

lynix94 commented 8 years ago

https://github.com/naver/hubblemon/commit/fe3eeb81edfe8b3747c80418993582d27ed9cdf5 https://github.com/naver/hubblemon/commit/f63192e13c49e5c8bdea71e168230f48f8b2c73b

상기 커밋으로 서버 사이드에서 저장소를 설정할 수 있습니다. 기존에는 collect_client 에서 보내는 data['type'] = 'rrd' 를 보고 rrd 플러그인을 사용하는 구조였습니다. (클라이언트에서 저장되길 원하는 플러그인을 명시하여 서버에서 해당 플러그인을 지원할 경우 저장)

저장매체별로 스키마가 다르기 때문이었는데... 위 이슈와 같이 저장소를 변경하면서 기존 클라이언트를 사용할 경우를 생각하여 서버 플러그인에 default 를 추가했습니다. 명시된 것이 없으면 default plugin 을 사용

때문에, 위 커밋을 보고 파악하시고, default plugin 을 현재의 rrd plugin 말고 sql plugin 으로 변경하시고 'rrd' 플러그인은 제거하시면 됩니다. 아래처럼

-   lsn.put_plugin('default', server_rrd_plugin.server_rrd_plugin(path))
+   lsn.put_plugin('default', server_sql_plugin.server_sql_plugin(path))
-   lsn.put_plugin('rrd', server_rrd_plugin.server_rrd_plugin(path))

그러면 이제 클라이언트에서 오는 스탯정보는 server_sql_plugin 을 통해 저장하려고 호출될 겁니다. 위 클래스를 구현하시면 됩니다.

진행중 궁금하시거나 막히시는 부분은 다시 문의해 주세요.

harry2636 commented 8 years ago

안녕하세요,

현재 hubblemon 설치를 완료하고 간단히 실행을 해보려는 도중 막히는 부분이 있어서 질문을 드립니다.

installation document에 나온대로 server, listener, client를 background에서 실행시킨 후 manage.py를 통해 아래 그림과 같이 hubblemon을 local에서 띄울 수 있었습니다. 여기서 client에 대한 그래프를 띄워서 보려면 어떻게 해야하나요?(document에 나온대로 검색을해도 아무것도 뜨지 않는 상태입니다.)

혹시 실행 시에 collect_client나 collect_server에서 address나 port number를 수정해야할 곳은 없는지요?

그리고 installation document에서를 읽어보았을 때 기존 server와 listener의 port number가 각각 30000, 30001 인데 이것은 왜 다르게 설정되었는지도 궁금합니다.

감사합니다.

default

lynix94 commented 8 years ago

안녕하세요.

리스너를 로컬에 여러개 (디스크당 하나씩) 혹은 원격으로 여러개 설치해서 수집하되, 각각의 클라이언트들은 어느 리스너로 붙을지를 서버로부터 정보를 얻는 구조입니다. (전체 클라이언트들이 동일한 설정을 가지고 있게 하기 위해서 입니다.)

즉, 클라이언트들은 서버의 30000 포트로 접속하고, 서버는 (기본설정에서는) 30001 포트에 떠있는 리스너로 중계합니다.

아커스나 기타 저장소는 설치되어 있지 않기 때문에, 처음 확인하실때는 로컬에서 클라이언트, 수집서버, 리스너, 허블몬 웹서버를 모두 띄우시고 로컬 서버의 서버 스탯을 보시면 될 것 같습니다.

위의 탭에서 system 을 선택하시고, server 를 클릭하셨을때 jquery ui 의 autocomplete 에 의해 서버 목록중에 로컬 호스트가 나오는지 확인해 보시기 바랍니다.

단, 이러려면 (기본설정에 의해) collect_server/listener_30001/LOCAL_HOST_NAME 아래에 여러 rrd 파일들이 생기는지 확인해 보시기 바랍니다. 이전에 run_server.py, run_listener.py 를 실행하셔서 서버 리스너를 띄우시고, run_client.py 를 띄워서 스탯을 송신하는지 보시고요.

잘 안되면 서버 , 리스너의 로그도 알려주시기 바랍니다.

추가로, 설정 및 시험에 대한 간단한 안내를 담당하시게 되신 분들 전체 대상으로 다시한번 발송하고 문의 받겠습니다.

감사합니다.

harry2636 commented 8 years ago

안녕하세요

말씀해주신대로 실행을 해보았는데요, system탭을 눌러도 아무것도 안뜨는 상황입니다.(처음에 제가 올린 그림과 같은 화면인 상태입니다.)

실행한 로그는 아래와 같습니다.

제가 생각하기에는 1) collect_client를 실행시킨 후 계속해서 아래와 같은 log가 출력되고

'scputimes' object has no attribute 'iowait'

2) system탭을 눌렀을 때 아래와 같은 log가 출력되는게 문제가 아닐까 생각하는데요,

####### system page request ########
<QueryDict: {}>
[26/Apr/2016 19:47:02] "GET /system/ HTTP/1.1" 200 6899

이 문제를 어떻게 해결하면 좋을까요? 아니면 이 것이 문제가 아니라 다른 것들이 문제일까요?

감사합니다.

*아래는 전체 log입니다.

harrykim@KimHarryui-MacBook-Pro:~/Desktop/CS494/hubblemon/collect_server $ python3 run_server.py&
[1] 1040
harrykim@KimHarryui-MacBook-Pro:~/Desktop/CS494/hubblemon/collect_server $ # cloud map init
# cloud map init done
>>> server 30000(1040) fork
>>> server 30000 (1041) wait
>>> start child server 30000 (1041)
# put listener: KimHarryui-MacBook-Pro.local:30001

harrykim@KimHarryui-MacBook-Pro:~/Desktop/CS494/hubblemon/collect_server $ python3 run_listener.py&
[2] 1042
harrykim@KimHarryui-MacBook-Pro:~/Desktop/CS494/hubblemon/collect_server $ # cloud map init
# cloud map init done
>>> listener 30001(1043) fork
>>> listener 30001 (1044) wait
>>> start child listener 30001 (1044)
# CollectListener listen (30001)

harrykim@KimHarryui-MacBook-Pro:~/Desktop/CS494/hubblemon/collect_server $ cd ..
harrykim@KimHarryui-MacBook-Pro:~/Desktop/CS494/hubblemon $ cd collect_client/
harrykim@KimHarryui-MacBook-Pro:~/Desktop/CS494/hubblemon/collect_client $ ls
__init__.py                client_arcus_plugin.py     client_jstat_plugin.py     client_mysql_plugin.py     client_redis_plugin.py     nohup.out
__pycache__                client_cubrid_plugin.py    client_memcached_plugin.py client_psutil_plugin.py    collect_client.py          run_client.py
harrykim@KimHarryui-MacBook-Pro:~/Desktop/CS494/hubblemon/collect_client $ python3 run_client.py&
[3] 1046
harrykim@KimHarryui-MacBook-Pro:~/Desktop/CS494/hubblemon/collect_client $ 143.248.178.18 30000
connect by KimHarryui-MacBook-Pro.local(143.248.178.18, 7)
redirect to KimHarryui-MacBook-Pro.local:30001
# sock_server recv: redirect:KimHarryui-MacBook-Pro.local:30001
# sock redirect: KimHarryui-MacBook-Pro.local, 30001
# 143.248.178.18:30000
'scputimes' object has no attribute 'iowait'
'scputimes' object has no attribute 'iowait'
invalid read from client: KimHarryui-MacBook-Pro.local, maybe reconnect
[30001]connect from ('143.248.178.18', 53370)(7)
'scputimes' object has no attribute 'iowait'

harrykim@KimHarryui-MacBook-Pro:~/Desktop/CS494/hubblemon/collect_client $ cd ..
harrykim@KimHarryui-MacBook-Pro:~/Desktop/CS494/hubblemon $ 'scputimes' object has no attribute 'iowait'
'scputimes' object has no attribute 'iowait'

harrykim@KimHarryui-MacBook-Pro:~/Desktop/CS494/hubblemon $ python3 manage.py runserver &
[4] 1047
harrykim@KimHarryui-MacBook-Pro:~/Desktop/CS494/hubblemon $ Performing system checks...

/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/django/template/utils.py:37: RemovedInDjango110Warning: You haven't defined a TEMPLATES setting. You must do so before upgrading to Django 1.10. Otherwise Django will be unable to load templates.
  "unable to load templates.", RemovedInDjango110Warning)

/Users/harrykim/Desktop/CS494/hubblemon/hubblemon/urls.py:44: RemovedInDjango110Warning: Support for string view arguments to url() is deprecated and will be removed in Django 1.10 (got django.views.static.serve). Pass the callable instead.
  url(r'^site_media/(?P<path>.*)$', 'django.views.static.serve', { 'document_root': site_media }),

/Users/harrykim/Desktop/CS494/hubblemon/hubblemon/urls.py:44: RemovedInDjango110Warning: django.conf.urls.patterns() is deprecated and will be removed in Django 1.10. Update your urlpatterns to be a list of django.conf.urls.url() instances instead.
  url(r'^site_media/(?P<path>.*)$', 'django.views.static.serve', { 'document_root': site_media }),

System check identified no issues (0 silenced).
April 26, 2016 - 19:46:35
Django version 1.9.5, using settings 'hubblemon.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
^M
harrykim@KimHarryui-MacBook-Pro:~/Desktop/CS494/hubblemon $ 'scputimes' object has no attribute 'iowait'
'scputimes' object has no attribute 'iowait'

harrykim@KimHarryui-MacBook-Pro:~/Desktop/CS494/hubblemon $ 'scputimes' object has no attribute 'iowait'
'scputimes' object has no attribute 'iowait'
'scputimes' object has no attribute 'iowait'
####### system page request ########
<QueryDict: {}>
/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/django/template/loader.py:97: RemovedInDjango110Warning: render() must be called with a dict, not a RequestContext.
  return template.render(context, request)

[26/Apr/2016 19:47:01] "GET /system/ HTTP/1.1" 200 6899
'scputimes' object has no attribute 'iowait'
[26/Apr/2016 19:47:02] "GET /system HTTP/1.1" 301 0
####### system page request ########
<QueryDict: {}>
[26/Apr/2016 19:47:02] "GET /system/ HTTP/1.1" 200 6899
'scputimes' object has no attribute 'iowait'
'scputimes' object has no attribute 'iowait'
lynix94 commented 8 years ago

안녕하세요 아마도 맥북에서 수집되는 스탯중 뭔가가 리눅스하고는 다른거ㅜ같네요 제가 오늘 오전은 외근이라 들어가면 한번 확인해보겠습니다

lynix94 commented 8 years ago

안녕하세요.

확인해보니 MAC OS X 에서는 psutil 이 아래 몇 항목들을 수집하지 않고 있습니다. 없는 어트리뷰트에 접근하려다 보니 exception 이 발생했고 자료가 전달되지 않았네요.

일단 아래와 같이 수집되지 않는 항목들은 0으로 설정하게 수정하고 돌리시면 맥에서 돌리실 수 있습니다.

naverui-MacBook-Air-2:collect_client naver$ git diff
diff --git a/collect_client/client_psutil_plugin.py b/collect_client/client_psutil_plugin.py
index 8f15551..fe0f512 100644
--- a/collect_client/client_psutil_plugin.py
+++ b/collect_client/client_psutil_plugin.py
@@ -37,9 +37,9 @@ class psutil_stat:
                                'system':cpu.system * 1000,
                                'idle':cpu.idle * 1000,
                                'nice':cpu.nice * 1000,
-                               'iowait':cpu.iowait * 1000,
-                               'irq':cpu.irq * 1000,
-                               'softirq':cpu.softirq * 1000 }
+                               'iowait':0,
+                               'irq':0,
+                               'softirq':0 }

                cpus = psutil.cpu_times(percpu=True)
@@ -50,9 +50,9 @@ class psutil_stat:
                                        'system':cpu.system * 1000,
                                        'idle':cpu.idle * 1000,
                                        'nice':cpu.nice * 1000,
-                                       'iowait':cpu.iowait * 1000,
-                                       'irq':cpu.irq * 1000,
-                                       'softirq':cpu.softirq * 1000 }
+                                       'iowait':0,
+                                       'irq':0,
+                                       'softirq':0 }
                        i += 1

        def collect_memory(self, stat):
@@ -146,7 +146,7 @@ class psutil_stat:

        def collect_resource(self, stat):
-               retransmit = self.get_retransmit()
+               retransmit = 0
                tcp_open = self.get_tcp_open()

                p_count = 0

돌리면 아래와 같이 시스템 탭에 나타나야 합니다. 호스트 네임으로 나타나며, 설명 자료와 같이 기본 설정일 경우 hubblemon/collect_server/listener_30001/HOSTNAME/*.rrd 파일이 존재하게 됩니다.

image

해보시고 이상한 것은 다시 문의 주시기 바랍니다. 혹 주변에서 MAC으로 설정에 어려움을 겪으시는 분들이 있으시면 도와 주시면 좋겠습니다.

다만... 서비스 서버 모니터링이 목적이다 보니 사실상 테스트는 Cent OS 리눅스에서만 되어 있습니다. 또다른 알수 없는 문제가 있을지 모르니, 하실 수 있으시면 리눅스 환경에서 개발하는 것도 고려해 보시기 바랍니다.

감사합니다.

lynix94 commented 8 years ago

추가로, 본 이슈가 다른 주제에 비해 고려할 내용과 구현양이 좀 더 많을 것 같습니다.

DB의 경우 sqlite 나 mysql 로 진행해 보시고 어느정도 감이 잡히실 때,

  1. 테이블 스키마를 어떻게 잡을 지,
  2. 사용할 insert, select 쿼리들은 무엇인지

에 대해 구상하고 있는 것을 알려주시거나 이 부분에 대해 의문나는 사항에 대해 댓글달아 주시면 같이 검토해보고 다음 작업으로 진행하는게 좋을 것 같습니다. (스키마, 쿼리를 잘못 잡고 진행했다 전체 롤백하는 일이 없도록)

harry2636 commented 8 years ago

감사합니다. 해결되었습니다! 일단은 맥으로 진행해보고 혹시 또 문제가 생긴다면 말씀해주신대로 linux에서 하도록 하겠습니다.

harry2636 commented 8 years ago

안녕하세요,

Table schema 관련 질문이 있어서 여쭙습니다. data_loader/basic_loader.py에서 .rrd file이 아래와 같은 형식의 list로 return되는 것을 확인하였습니다. ((ts_start, ts_end, step), (metric1, metric2, metric3), [(0, 0, 0), (1, 1, 1)....])

이에 따라 cpu, cpu-0 ... 각각을 .sql file로 만들고 내부에서 time step 을 primary key로 하고 iowait, idle, irq와 같은 정보를 attribute로 넣는 table을 만들고자 합니다.(이렇게 구현하면 하나의 .sql file에 한 개의 table만 들어가게 됩니다.) 그리고 위의 리스트 형식에 맞게 return 하는 코드를 작성할 예정이구요.

그런데 제시해주신 가이드라인을 보면, "해당 테이블은 timestamp 를 PK로 하며 cpu, cpu-0, cpu-1 등은 attribute 중의 하나 (적당하게 type 정도의 이름) 로 하고 value 를 bigint (64bit integer) 로 구성" 라는 구문이 있는데, 제가 생각한 scheme이랑 다르게 cpu, cpu-0 각각을 한 table의 attribute로 삼는다는 뜻으로 이해가 됩니다.

제가 구상한 방법이 가이드라인과 맞지 않는 것 같은데, 어떤 식으로 schema를 짜면 좋을까요? (제가 DB 자체를 다뤄보는 게 이번이 처음이라 아직 모르는 부분이 많습니다. 많은 조언 부탁드리겠습니다.)

감사합니다.

harry2636 commented 8 years ago

아 그리고 위에서 return 되는 list를 print했을 대 아래와 같이 print가 되는데요, result[3]를 보면 마지막 부분부터 값이 채워지고, 앞부분은 수많은 None으로 채워져있습니다. result[3]의 마지막 1~2개 element도 None으로 채워져있는 걸 확인하였습니다. 여기서 rrd로부터 read해온 result의 result[3]가 왜 이렇게 구성되는지에 대해 설명해주시면 감사하겠습니다.

result: ((1462602435, 1462604240, 5), ('available', 'free', 'percent', 'total', 'used'), [(None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (None, None, None, None, None), (10213188403.2, 9960600371.2, 40.0, 17179869184.0, 7217354342.4), (10209374208.0, 9956786176.0, 40.0, 17179869184.0, 7221361868.8), (10208967884.8, 9956379852.8, 40.0, 17179869184.0, 7221043200.0), (10209142374.4, 9956554342.4, 40.0, 17179869184.0, 7221402828.8), (10208205209.6, 9955617177.6, 40.0, 17179869184.0, 7221720678.4), (10208313344.0, 9955725312.0, 40.0, 17179869184.0, 7221621555.2), (10208971980.8, 9956383948.8, 40.0, 17179869184.0, 7221265203.2), (10209025228.8, 9956437196.8, 40.0, 17179869184.0, 7221068595.2), (10209651097.6, 9957063065.6, 40.0, 17179869184.0, 7221164441.6), (10216532377.6, 9963896832.0, 40.0, 17179869184.0, 7214459289.6), (10227776716.8, 9975069900.8, 40.0, 17179869184.0, 7203275571.2), (10205078323.2, 9952371507.2, 40.0, 17179869184.0, 7225493094.4), (10179328409.6, 9925577932.8, 40.0, 17179869184.0, 7251591987.2), (10171543552.0, 9916204646.4, 40.4, 17179869184.0, 7262090854.4), (10134763110.4, 9879389798.4, 41.0, 17179869184.0, 7298692710.4), (10135991910.4, 9880620236.8, 41.0, 17179869184.0, 7297444249.6), (10108958310.4, 9853569433.6, 41.0, 17179869184.0, 7324976742.4), (10101466726.4, 9846006579.2, 40.8, 17179869184.0, 7331871129.6), (None, None, None, None, None), (None, None, None, None, None)])

lynix94 commented 8 years ago

안녕하세요.

먼저 간단한 두번째 질문의 경우는.. 시간 레인지가 기본은 30분으로 되어있는데 아마 데이터 수집이 몇분정도만 된것 같습니다. RRD의 경우 데이터를 조회했을때 없는 부분이면 (데이터를 못받아서 중간에 생길 수도 있습니다) None 으로 응답합니다.

데이터가 누적되고 다시 조회해 보면 다 차있는 경우를 볼 수 있습니다. 뒷부분의 경우는 가장 최근 10초간의 데이터는 아직 RRD 로 반영되지 않아서일 것입니다.

lynix94 commented 8 years ago

첫번째의 경우는, 설계자 마음대로 하시면 됩니다. 말씀하신대로 PK를 타임스탬프로만 잡는게 더 일반적일 수 있겠네요. 특히나 테이블 생성의 부담이 적은 sqlite 같은 경우는 그렇게 하는게 좋겠습니다.

다만, 참고로 제가 그렇게 적었던 이유는...

cpu 테이블에 PK를 [timestamp, attribute] 로 멀티컬럼 인덱스로 잡은경우 (attribute 는 'cpu', 'cpu-0', 'cpu-1'...)

CPU 코어가 16개인경우 신규 insert 되는 16개의 row 가 한개의 버퍼캐쉬에 몰리게 되고, 해당 버퍼가 디스크로 플러쉬 될때도 한군데만 써집니다. 다만, 테이블이 16개가 존재할 경우 한번 스탯을 받은 후 16군데에 각각 추가가 이루어 집니다.

CPU나 물리장비는 갯수가 한정적이라 크게 문제는 안됩니다만 논리적인 단위를 모니터링 할 때 (카페, 사용자별 행동 등) 테이블이 수만, 수십만이 되고 IO 가 각각 분산되면 insert 시 성능 저하가 발생 할 수는 있겠습니다.

읽는 경우는 반대로 분산된 경우가 선택도가 높아서 (time range 로 읽을 경우 버리는 거 없이 다 읽어옴) 더 좋습니다. [timestamp, attribute] 로 PK를 잡으면 select user, system from cpu where time > now() - 30m and attribute = 'cpu-1' 인 경우 timestamp 기준으로 최근 30분의 인덱스만 읽으면서 (인덱스 스캔) 읽어온 인덱스에서 attribute 가 cpu-1 인 것만 필터링 (키 필터) 합니다.

인덱스는 보통 캐쉬되고 있기 때문에 작업은 메모리상에서 주로 이루어져 성능저하는 크지 않습니다만 선택도가 낮아져 더 오래걸리겠네요.

위의 우려한 경우는 특이한 케이스라 말씀하신대로 테이블별로 분리하는게 더 좋겠습니다. 위사항은 참고만 하시고 구상하신대로 진행하시기 바랍니다.

완료전, create, insert, select 구문정도만 공유해주시기 바랍니다.

lynix94 commented 8 years ago

그리고, 한가지 더 중요한 사항이 있네요.

RRD는 고정 인터벌의 cyclic 파일이라서 시작, 끝, 인터벌 정보를 따로 받고 데이터는 블럭들만 받습니다. 5초단위로 오지 않는 것들은 평균을 내거나 수렴시켜서 고정시켜 버립니다만,

해보시면 알겠지만 RDBMS에서 이렇게 하기는 힘듭니다. insert 시 timestamp 를 실제 기록되어 있는 시간으로 기록하고 읽을때도 그 값을 읽게 하는게 편합니다. (별도의 수렴 로직이 내장되어 있지 않기 때문에)

해서 허블몬의 RRD 포맷은 ((ts_start, ts_end, step), (metric1, metric2, metric3), [(0, 0, 0), (1, 1, 1)....]) 였습니다만 별도의 timestamp 포맷이 있어야 합니다.

('#timestamp', (metric1, metric2, metric3), [(1462602435, 0, 0, 0), (1462602440, 1, 1)....]) 와 같은 형식인데...

이 부분은 RDBMS, tsdb, nosql 등을 저장소로 쓸 경우 공통으로 필요한 부분이고, 별도의 주제이기 때문에 (그리고 작업할 내역이 좀 있고) 제가 이번 주 안에 처리해서 반영하겠습니다.

위의 수정된 포맷을 고려해서 진행 해 주시기 바랍니다. 그간 다른 부분을 고민해 주시기 바랍니다. 완료되는대로 공유하겠습니다.

harry2636 commented 8 years ago

답변 감사합니다.

요청해주신 사항 반영하고, 계속 고민해보도록 하겠습니다!

그리고 한가지 질문이 있는데요, table을 만들 때 기존 rrd 파일이 cpu0, cpu1...각각에 대해 1개씩 존재하는 거와 같이 sql 파일을 하나하나 만들어서 각각 table을 넣는게 좋을까요 아니면 cpu에 대한 sql파일을 하나 만들어 놓고 여러 개의 table을 지정하는게 좋을까요?( cpu core 16개라면, 16 sql file(각각 1개의 table) vs 1sql file(16개의 table) )

직관적으로는 1개의 sql파일을 만드는게 좀 더 효율적일 것 같은데, 혹시 몰라서 여쭙습니다.

감사합니다.

lynix94 commented 8 years ago

아래 커밋으로 타임스탬프가 찍혀있는 시계열 데이터 (tsdb) 형식의 열을 처리할 수 있게 됐습니다.

https://github.com/naver/hubblemon/commit/ef630a3891c260b920131d4c8820a0035f154e9e

단, 위 커밋이 잘 동작하는지 여부를 테스트하고 또 TSDB 형식의 흐름을 이해하기 위해 가상의 tsdb_test_handle 을 만들었습니다. 이 핸들은 RRD 파일에서 읽은 데이터를 tsdb 형식으로 변경해서 되돌려 줍니다.

커밋 반영하시고 아래와 같이 테스트를 위해 핸들을 변경하면 기존 수집한 데이터를 tsdb 형식으로 변경해서 볼 수 있습니다.

아래의 테스트 핸들이 돌려주는 것과 같은 형식으로 RDBMS 에서 읽어오는 핸들을 최종 만드시면 됩니다.

한번 시험해 보시고 궁금하신 점은 다시 문의 주시기 바랍니다.

[lynix@cdbs034.cub hubblemon_tsdb]$ git diff
diff --git a/common/core.py b/common/core.py
index 988712d..5109724 100644
--- a/common/core.py
+++ b/common/core.py
@@ -34,8 +34,8 @@ mod_query_cache = {}
 #
 # default loader settings
 #
-get_default_local_handle = data_loader.loader_factory.get_rrd_handle
-#get_default_local_handle = data_loader.loader_factory.get_tsdb_test_handle
+#get_default_local_handle = data_loader.loader_factory.get_rrd_handle
+get_default_local_handle = data_loader.loader_factory.get_tsdb_test_handle

 get_default_remote_handle = data_loader.loader_factory.get_remote_handle

diff --git a/data_loader/basic_loader.py b/data_loader/basic_loader.py
index d38fb6c..73791c9 100644
--- a/data_loader/basic_loader.py
+++ b/data_loader/basic_loader.py
@@ -246,9 +246,9 @@ class basic_loader:
                                tmap[names[i]] = i

                # for debug
-               #print(tmap)
-               #print(names)
-               #print(items)
+               print(tmap)
+               print(names)
+               print(items)

                # loader title
                chart_data_list = []
###### system page request ########
<QueryDict: {'item': ['brief'], 'server': ['cdbs034.cub']}>
{'system': 6, 'idle': 1, 'irq': 3, 'user': 7, 'nice': 4, 'iowait': 2, 'softirq': 5}
('idle', 'iowait', 'irq', 'nice', 'softirq', 'system', 'user')
[(1462943595, 16112.8, 0.4, 1.6, 0.0, 1.6, 38.0, 75.6), (1462943600, 16018.0, 1.6, 0.0, 0.0, 0.0, 51.6, 158.4), (1462943605, 15602.4, 8.8, 0.0, 0.0, 0.4, 164.0, 452.4), (1462943610, 15032.0, 85.2, 0.0, 0.0, 2.0, 371.2, 738.8), (1462943615, 15142.4, 206.8, 0.0, 0.0, 2.4, 274.8, 606.4), (1462943620, 15175.6, 38.0, 0.0, 0.0, 3.6, 259.6, 752.8), (1462943625, 15814.8, 56.0, 0.0, 0.0, 1.6, 187.6, 174.0), (1462943630, 13800.0, 241.6, 2.0, 0.0, 6.0, 351.8, 199.0), (1462943635, 13800.0, 241.6, 2.0, 0.0, 6.0, 351.8, 199.0), (1462943640, 15160.0, 386.0, 0.0, 0.0, 14.0, 306.0, 360.0), (1462943645, 15572.0, 4.0, 0.0, 0.0, 2.0, 426.0, 222.0), (1462943650, 15422.0, 2.0, 0.0, 0.0, 0.0, 630.0, 186.0), (1462943655, 16066.0, 4.0, 0.0, 0.0, 0.0, 58.0, 98.0), (1462943660, 16118.0, 4.0, 0.0, 0.0, 0.0, 42.0, 64.0), (1462943665, 16108.0, 4.0, 0.0, 0.0, 2.0, 42.0, 70.0), (1462943670, 15726.0, 38.0, 0.0, 0.0, 4.0, 166.0, 288.0), (1462943675, 15464.0, 84.0, 2.0, 0.0, 20.0, 162.0, 498.0), (1462943680, 15476.0, 96.0, 0.0, 0.0, 16.0, 168.0, 474.0), (1462943685, 9156.0, 356.0, 4.0, 0.0, 140.0, 2558.0, 4014.0), (1462943690, 9412.0, 474.0, 2.0, 0.0, 166.0, 2042.0, 4196.0), (1462943695, 10212.0, 1122.0, 4.0, 0.0, 138.0, 1788.0, 3094.0), (1462943700, 7651.666666666667, 348.3333333333333, 3.3333333333333335, 0.0, 145.0, 2415.0, 3030.0), (1462943705, 8415.133333333333, 240.86666666666665, 2.2666666666666666, 0.0, 149.0, 3353.4, 3564.4), (1462943710, 8602.8, 90.8, 2.0, 0.0, 81.2, 3954.4, 3546.0), (1462943715, 8974.8, 212.0, 3.6, 0.0, 148.8, 3226.8, 3709.6), (1462943720, 9228.0, 304.4, 4.0, 0.0, 242.0, 2228.4, 4262.4), (1462943725, 9032.8, 290.8, 4.0, 0.0, 204.0, 2234.8, 4538.4), (1462943730, 9295.6, 224.8, 0.8, 0.0, 162.8, 2401.2, 4201.6), (1462943735, 8699.2, 230.8, 4.8, 0.0, 192.8, 2814.0, 4354.4), (1462943740, 8597.2, 258.4, 2.8, 0.0, 190.8, 2654.0, 4572.8), (1462943745, 8730.8, 227.2, 3.6, 0.0, 136.8, 2540.4, 4642.4), (1462943750, 8665.6, 352.4, 2.4, 0.0, 85.6, 2016.0, 5142.8), (1462943755, 8474.0, 611.6, 3.6, 0.0, 85.6, 1526.4, 5565.2), (1462943760, 7561.066666666666, 590.9333333333333, 3.4666666666666672, 0.0, 74.93333333333334, 788.5333333333333, 5029.333333333334), (1462943765, 7878.9333333333325, 525.0666666666666, 3.7333333333333334, 0.0, 83.86666666666667, 747.4666666666666, 5872.266666666667), (1462943770, 8286.4, 422.0, 4.0, 0.0, 89.6, 1249.2, 6150.8), (1462943775, 8348.8, 221.6, 1.6, 0.0, 76.0, 2296.8, 5256.8), (1462943780, 8531
lynix94 commented 8 years ago

질문에 대한 답변을 다시 하겠습니다.

한 파일 안에서 전체 테이블을 관리하는 방식인게 좋겠습니다. 시스템 단위로 모니터링하는 경우야 상관없을 수 있지만, 경우에 따라 여러 시스템의 모니터링을 모아서 보는 경우도 있을 수 있는데 (특정 플랫폼의 부하를 합해서 보는 경우등) 데이터베이스가 갈려있으면 다른 데이터베이스의 테이블을 조회하기가 번거로울 것 같네요.

sqlite 쪽은 가벼워서 동시에 여러 파일에 접근해서 처리하는게 부담없을 수도 있겠습니다만 핸들을 여러개 가지고 오가는 것도 좋을것 같지 않습니다. 그리고 타 DBMS를 고려하면 같은 구조로 가는게 좋겠습니다.

harry2636 commented 8 years ago

안녕하세요, 그간 바쁜 일이 있어서 오랜만에 프로젝트에 다시 복귀하게 되었습니다.

1. 말씀해주신 바가 하나의 client(ex: psutil)에 대해서 하나의 sql file을 만들면 되는 것인가요?(table로 psutil_cpu-0, psutil_cpu-1, psutil_disk-ram0 등이 들어갑니다.) 아니면 client가 2개(ex: arcus, psutil)일 때도 모두 하나의 sql file에 저장해야하는 것인가요?(table로 psutil_cpu-0, psutil_cpu-1, psutil_disk-ram0 에 arcus와 관련된 stat에 대한 table이 추가로 들어갑니다.)

일단 1개라고 생각하고 psutil_disk-0에 대해 sql query를 구상해보았습니다.

1)collect_server/server_rrd_plugin.py/server_rrd_plugin 내 함수 (1)def create_data(self, basedir, name_data_map)에 대응되는 query CREATE TABLE psutil_cpu-0(timestamp BIGINT PRIMARY KEY, idle BIGINT, iowait BIGINT … user BIGINT)

(2)def update_data(self, hostname, timestamp, name_data_map)에 대응되는 query INSERT INTO psutil_cpu-0 VALUES(x1, x2 … x6)

2)common/rrd_data.py 내 함수 (1)def read(self, ts_from, ts_to, filter = None)에 대응되는 query SELECT * FROM psutil_cpu-0 WHERE timestamp = ts_from

2. 여기서 기존 red read 시에 ts_from과 ts_to를 같이 받아서 처리하는데, sql의 경우 timestamp를 ts_from으로 설정해 받으면 될까요?

3. 이번 이슈의 목표가 표준 SQL 저장 레이어를 구상하는 것인데 어떤 python library를 impor해야 할 지 모르겠습니다. import rrdtool에 대응되는 sql library를 알려주시면 감사하겠습니다.

감사합니다.

lynix94 commented 8 years ago

안녕하세요. 다른 급한일들이 터진게 있어서 처리하다 좀 늦었습니다.

  1. 하나의 sqlite 파일로 만들어주세요. create, insert 구문은 문제 없습니다만, select 의 경우는 아래를 참조해주세요.
  2. timestamp 를 ts_from ~ ts_to 로 받아야 합니다. select * from psutil_cpu-0 where timestamp >= ts_from and timestamp < ts_to 와 같은 구문이 되어야 허블몬의 화면에서 준 시간 구간에 해당하는 데이터를 받을 수 있습니다.
  3. sqlite, mysql, oracle, cubrid 등의 표준 sql 쿼리를 지원하는 모듈들에 대한 gateway 로서의 sql_gw 클레스를 만드시고요. 시험은 간단하게 sqlite3 모듈을 import 하되 확장성 있게 고려하시면 됩니다.

간단히 생각나는대로,

class sql_gw:
  def __init__(self, type = 'sqlite3', user='', password=''):
    self.type = type
    if type == 'sqlite3':
      self.handle = sqlite3.connect('hubblemon.db')
    elif type == 'mysql':
      self.handle = MySQLdb.connect('localhost', user, password, 'hubblemon_db') # 일단은 로컬호스트로
    .... # 다른 DB에 대해서도 나중에 추가

  def create(self, query):
    cursor = self.handle.cursor()
    cursor.execute() # TODO: return check

  def select(self, query):
    cursor = self.handle.cursor()
    cursor.execute()
    return cursor.fetchall()

  def insert(self, table_name, values):
    cursor = self.handle.cursor()
    cursor.execute() # TODO: return check

위와 같은 형식으로 모든 RDBMS를 다룰 수 있는 gw 클래스를 만드시고, 허블몬에서는 sql_gw 객체에 쿼리를 날리고, sql_gw 는 설정에 따라 sqlite3 나 MySQLdb 로 중계하면 됩니다. 각 DB모듈의 함수 인터페이스가 같으면 위와 같이 하면 되고, 틀린 DB가 있으면 select, insert 안에서 if 문으로 가지치기를 하면 되는데,

일단 이 이슈에서는 sqlite3로 진행해 주세요.

그리고, 위는 설명을 위해서 간단히 작성했습니다. 위와 같이 하면 모든 DB의 모듈을 import 해야 합니다. 실제로는 settings 에서 필요한 모듈만 import & connect 후 생성자 파라미터로 type 과 함께 handle 을 넘겨줄 수 있도록 해주시기 바랍니다. (DB는 중간에 끊길 수도 있기 때문에, 잘 하려면 접속하는 콜백함수를 넘겨받는게 맞습니다. 그러면 user, password 같은 파라미터를 안받아도 되겠죠)

참고해서 고민해 주시고 궁금하신 사항은 다시 문의 주시기 바랍니다. 감사합니다.

harry2636 commented 8 years ago

안녕하세요.

일단 sqlite3을 중심으로 handle을 sql_gw에 전달하였고, create, select, insert code의 작성을 완료하였습니다.

create와 insert는 문제없이 돌아가는 것이 확인되었는데요, select 같은 경우는 어떻게 확인해야 할 지 모르겠습니다.

hubblemon web site에서 GUI의 server과 item을 선택하면 .rrd에 대한 file들 밖에 안나오는데, sql file들에 대한 select가 제대로 이루어지는지를 어떻게 확인 해야 할까요? (sql_handler를 만들고 default_loader도 모두 sql로 설정하였습니다.)

harry2636 commented 8 years ago

추가로 python sql에서는 handle.commit()을 해야 저장이 되는걸로 알고 있는데 handler의 commit을 어느 때 해주어야 할지가 궁금합니다. 일반적인 예제에서는 handle.commit()을 handle.close()를 할 때 해주는데, 본 프로젝트에서는 따로 close를 해주지 않아서 애매하네요.

lynix94 commented 8 years ago

휴일동안 낮에는 일이 항상 있어서 잠깐 짬나는 사이에 봤습니다. (학생과 직장인은 일하는 싸이클이 매우 다르군요 =_=;;)

일단 기존 구조에서는 client list 를 가져오는것이 유연하지 않았습니다. RRD만 사용할거라 단기적으로 생각해서 유연하지 못하게 구현된 점이 있었고요. 하여 방금 커밋한 것으로 좀 수정해 봤는데, (remote 는 생각하지 마시고요) local_storage_manager 를 common.core.py 에서 지정하게 됩니다. (예전에는 함수였는데 유연하게 하기 위해 class 로 변경시켰습니다) rrd 를 다루는 메니저는 rrd_storage_manager 이고 하는 일은 get_handle 로 데이터를 읽을 수 있는 핸들을 넘겨주고, client 리스트 및 client 에 있는 데이터들의 목록, 전체 데이터들의 목록을 가져오는 함수들이 각각 있습니다.

이것에 대응하는 sqlite3_storage_manager 를 구현하시면 되는데 템플릿으로 넣어놨습니다. 템플릿에서 sql_gw 는 sqlite3 외의 다른 DB에서도 공통으로 쓸 수 있게 (지난번에 말한) 하기위한 클래스이고, 허블몬에서 읽어가기 위한 인터페이스로 변환해주는 sql_handle class 도 있습니다. 한번 보시고 어떤 구조인지 파악해보시기 바랍니다. (더 좋은 방법이 있으시면 제안, 개선 하셔도 되고요)

해당 템플릿 클래스중 get_client_list 의 경우는 전체 table name 을 fetch 해온 후 분석할 수도 있는데 비효율적이라 client_list 테이블이 있다 가정하고 거기서 읽어왔습니다. create table 시 이 테이블에 테이블 네임을 insert 해주시면 됩니다.

여기서 한번 끊고...

lynix94 commented 8 years ago

용어에 혼선이 있었네요. 허블몬이 수집하는 데이터는 여러머신들의 것이 있을 수 있는데요. 예를들어 머신 이름이 ccon01, ccon02 라는 머신에서 각각 collect_client 가 데이터를 수집해온다면,

ccon01 에도 psutil_cpu 라는 항목이 있고, ccon02 에도 psutil_cpu 라는 항목이 있습니다. 현재 DB는 같은 것을 사용하고 (sqlite3 의 경우 같은 파일을 사용하고) 테이블 이름으로 구분해야 합니다. (테이블 안에 attribute 를 넣어서 구분할 수도 있지만 선택도가 낮는 경우 성능저하가 발생합니다)

하여 psutil_cpu-0 항목이 올때 collectclient 가 보내는 데이터의 그 상위 항목에 name 이 있고 여기에 머신 네임인 ccon01 이 찍혀 있습니다. 이것을 조합해서 아래와 같이 테이블을 만듭니다. (D 는 prefix 로 정했습니다)

CREATE TABLE D_ccon01_psutil_cpu-0(timestamp BIGINT PRIMARY KEY, idle BIGINT, iowait BIGINT … user BIGINT)

insert 시에도 테이블 이름을 머신이름을 고려하여 집어넣으면 되고, select 시에는 허블몬에서 머신이름을 선택하고, brief 를 선택하면 테이블 들이 나오는데, local_storage_manager.get_handle(param, path) 가 호출됩니다. path 가 "machinename/psutil_cpu" 와 같은 형식인건데 이걸 조합해서 D_machinename_psutil_cpu 로 변경해서 sql_handle 을 만들어 주시면 됩니다.

lynix94 commented 8 years ago

커밋은 아시는대로 하나의 트랜잭션이 끝났을 경우 실행합니다. 커밋이 되지 않은 상태에서 문제가 발생하면 롤백해서 트랜잭션 초기상태로 돌리기 위한 구분점인데요...

허블몬도 그렇고, 데이터 수집이나 변경작업이 단발성으로 처리해도 되는 경우는 execute 가 끝나고 바로 commit 하시면 됩니다. 또는 auto commit 모드로 돌리셔도 됩니다.

con = sqlite3.connect(":memory:")
con.isolation_level = None
lynix94 commented 8 years ago

다시 한번 보시고 궁금하신점은 문의 주시기 바랍니다.

문의 주실때 pull request 로 진행된 코드를 보내주셔도 됩니다. (바로 커밋할게 아니고 피드백 후 수정하면 되니)

harry2636 commented 8 years ago

연휴기간에 답변 감사합니다!

  1. 요청해주신 사항 모두 반영하였고, 주로 rrd와 대응시키는 것을 목적으로 하여 sql_gw의 경우 common/sql_data.py에 넣었고 sql_storage_manager의 세부코드들을 모두 작성하였습니다. 그런데local_storage_manager를 sql로 변경하고 hubglemon web system에서 name을 클릭하면 brief, cpu 등 다 정상적으로 list가 나오는데, server(machine name)은 클릭하면 아무것도 안뜹니다. 이게 되야지 select에 대한 test가 가능한데 어떤 부분이 문제인지 파악이 안됩니다.
  2. 코드를 작성하고 Pull request를 하려고 했는데, 이상한 공백문자가 너무 많이 추가되서 어떤부분이 변경되었는지 알아보기 힘들어 차마 pull request를 하지 못했습니다. (제 fork repository의 sql_feature에 있습니다.) python3에서 code를 돌리려고 할 때 tab과 공백문자를 혼용하지 못하게 하였기 때문에 vim의 :retab 명령어를 사용하여 \t를 모두 space로 바꾸어서 그런 것 같습니다. 이렇게 공백의 차이로 인해 commit이 굉장히 더러워졌는데, 어떻게 하면 좋을까요? (제가 아직 Git에 익숙치 못합니다ㅠㅠ)

감사합니다.

lynix94 commented 8 years ago
  1. 시스템 목록은 common/core.py 의 get_client_list 의 응답값이 나타납니다. get_client_list 는 local_storage_manager.get_client_list(param) 를 사용하는데 현재 local_storage_manager 는 rrd_storage_manager 이고요. 해서, 기본 구성에서 get_client_list 의 응답값을 print 해서 잘 되는지 확인해보시고, 이걸 sql_storage_manager 로 변경하시고 get_client_list 가 목록을 잘 응답하는지 확인해보시기 바랍니다. 지난번 템플릿에서는 select name from client_list 를 사용하도록 말씀드렸는데요. 해당 부분이 잘 처리되고 있는지 함수 중간중간 리턴값을 trace 해보시고요.
  2. 프로젝트 안에서 인덴테이션은 동일해야 하니 탭으로 작성해서 최종 pull request 해주시고요. 현재 중간 리뷰를 위해서는 한번 바로 보내주셔도 됩니다. 검토용으로 신규 함수 위주로 볼테니 바로 보내주시면 됩니다. 하지만 인덴트툴등을 사용해 인덴트는 탭으로 변경해서 작업해주시기 바랍니다.
harry2636 commented 8 years ago

공백 제거해서 https://github.com/naver/hubblemon/pull/15 로 pull request하였습니다.

그리고 get_client_list에서 print를 통해 tracing을 해보았습니다. sqlite3 select query는 확실히 맞고 sql_gw의 select 함수 역시 제대로 짠 게 맞는데도 storage_manager에서 sql_gw를 불러오고 함수를 call하면 제대로 작동이 안되는 것 같네요... 일단 추측은 path 문제가 아닐까 싶은데 path를 "hubblemon.db"가 아닌 ":memory:"로 했을 때도 제대로 작동을 안해서 이게 문제인지가 아리까리한 상태입니다. 관련해서 도움 부탁드리겠습니다.

감사합니다.

lynix94 commented 8 years ago

안녕하세요. hubblemon.db 를 각 파일들의 워킹 디렉토리에서 찾아서 열지 못하는 문제 같습니다. 일단 아래 patch 를 적용해 보시면 되긴 합니다. 설정 부분은 무시하시고요.

추가 리뷰는 끊어서 다시 추가하겠습니다.

[lynix@cdbs034.cub hubblemon_sql]$ git diff
diff --git a/collect_client/run_client.py b/collect_client/run_client.py
index d18efcb..18d4c6e 100644
--- a/collect_client/run_client.py
+++ b/collect_client/run_client.py
@@ -33,7 +33,7 @@ hostname = socket.gethostname()

 #server_address = ['1.1.1.1:40000', '2.2.2.2:40000'] # your collect server address
-server_address = ['%s:30000' % hostname] # your collect server address
+server_address = ['%s:50000' % hostname] # your collect server address

 if True:
diff --git a/collect_server/server_sql_plugin.py b/collect_server/server_sql_plugin.py
index 6fb44cf..3b52917 100644
--- a/collect_server/server_sql_plugin.py
+++ b/collect_server/server_sql_plugin.py
@@ -23,7 +23,7 @@ class server_sql_plugin:
                                continue

                        name =name.replace('-', '_')
-                       name = "D_"+basedir+"_"+name
+                       name = '"D_'+basedir+'_'+name+'"'
                        check_table ="SELECT count(*) FROM sqlite_master WHERE type='table' AND name='" + name + "'"
                        is_exist=self.sql_manager.select(check_table)[0][0]
                        if(is_exist==0):
@@ -43,7 +43,7 @@ class server_sql_plugin:
                for name, data in name_data_map.items():

                        name = name.replace('-', '_')
-                       name = "D_"+hostname+"_"+name
+                       name = '"D_'+hostname+'_'+name+'"'
                        query = "INSERT INTO " + name
                        attr_query ="("+"timestamp"
                        val_query=" VALUES(" + str(int(timestamp))
diff --git a/common/settings.py b/common/settings.py
index da94e84..016c834 100644
--- a/common/settings.py
+++ b/common/settings.py
@@ -26,17 +26,19 @@ import os, sys, socket
 sql_type ="sqlite3"
 if(sql_type=="sqlite3"):
        import sqlite3
-       conn=sqlite3.connect("hubblemon.db", check_same_thread=False, isolation_level=None)
+       hubblemon_path = os.path.join(os.path.dirname(__file__), '..')
+       db_path = os.path.join(hubblemon_path, 'hubblemon.db')
+       conn=sqlite3.connect(db_path, check_same_thread=False, isolation_level=None)
        #conn=sqlite3.connect(":memory:", check_same_thread=False, isolation_level=None)

-collect_server_port = 30000
+collect_server_port = 50000

 # should be sorted by addr
 #  add listener if you want

 hostname = socket.gethostname()

-listener_list =[('%s:30001' % hostname, 'collect_server/listener_30001', 'local')]
+listener_list =[('%s:50001' % hostname, 'collect_server/listener_30001', 'local')]

 '''
 # you can spread listeners to remote servers
diff --git a/data_loader/loader_factory.py b/data_loader/loader_factory.py
index 48b0f4f..a5dfb00 100644
--- a/data_loader/loader_factory.py
+++ b/data_loader/loader_factory.py
@@ -145,6 +145,7 @@ class sql_storage_manager:
        def __init__(self, db_path):
                        self.db_path = db_path
                        self.sql_manager=common.sql_data.sql_gw(db_path)
+                       print(db_path)

        def get_handle(self, base, path):
@@ -171,8 +172,8 @@ class sql_storage_manager:
                        table_list =self.sql_manager.select(query);
                        print (table_list)
                        for table in table_list:
-                               client_name = table.split("_")[1]
-                               if clinet_name not in client_list:
+                               client_name = table[0].split("_")[1]
+                               if client_name not in client_list:
                                        client_list.append(client_name)

                        print ("client_list:", client_list)
lynix94 commented 8 years ago

먼저 database table name 을 외부에서 입력받을 땐, 위의 patch 처럼 " " 로 감싸는게 좋습니다. SQL구문에서 허용되지 않는 테이블 이름이 올수 있는데 제 경우 cdbs034.cub 에서 . 가 들어가면 에러가 납니다. create table "D_cdbs034.cub_psutil_cpu" ... 와 같은 형식이 되게 수정하시고요.

sqlite3 handle 을 매번 여는건 오버헤드가 큽니다. 장고 프로젝트 글로벌로 설정해서 한번 열면 계속 재사용하게 하는게 좋습니다. 이건 급한건 아니니 마무리할때 정리하시고요.

sql_gw 의 read 부분이 이상합니다. read 는 hubblemon.db 를 열어서 원하는 테이블 네임의 데이터를 읽어와야 하는데, 현재 구현은 get_handle 에서 base, path 를 받아서 테이블 이름별로 파일을 따로 읽으려고 합니다.

즉, hubblem.db 를 만들었고, 이 DB 안에 (이제부터 DB라고 하겠습니다) D_cdbs034.cub_psutil_cpu 라는 테이블을 만들고 여기에 insert 까지 하는건 정상 동작합니다.

그런데, sql_storage_manager 에서 get_handle 이 호출되면 collect_server/listener_30001 을 base 로 받고, cdbs034.cub/psutil_cpu 를 path 로 받아서, collect_server/listener_30001/cdbs034.cub/psutil_cpu 라는 DB를 열려고 합니다.

정확히 해야 할일은 create, insert 와 똑같이 hubblemon.db 를 열고 D_cdbs034.cub_psutil_cpu 테이블로부터 select 를 해야 합니다.

한번 해보시고 다시 문의 주세요.

harry2636 commented 8 years ago

read 및 insert 모두 수정하여 아래 pull request에 반영하였습니다. Hubblemon Web에서 정상적으로 구동되는 것을 확인하였습니다. https://github.com/naver/hubblemon/pull/15

다만 그래프 호출 시 매우 짧은 시간 간격만 보여지고 있는데 이 부분은 그대로 놔두어도 될까요?

lynix94 commented 8 years ago

pull request 리뷰에 답변 달았습니다.