메신저 검열로 '텔레그램(Telegram)' 광풍이 불고 있다. 개인적으로 카톡을 자주 사용하지 않아, 텔레그램이 절실(?)하게 필요하지 않다. 하지만 텔레그램을 사용해야할 이유가 생겼다. 텔레그램이 서버에서 사용할 수 있는 CLI를 제공하고 있고, lua나 python으로 손쉽게 메신저와 연동할 수 있다.
그래서 텔레그램용 lua스크립트를 만들어서 서버 상태를 폰에서 받아볼 수 있다. 예를 들어 특정 서버의 MRTG/RRD를 메신저로 요청하면 그 이미지를 받아 볼 수 있다. 서버의 load값도 같은 방식으로 return받을 수 있다. 서버장애 문자를 텔레그램으로 받을 수 있도록 하신분도 있다. (※ 개인적으로는 이미 SMS로 받고 있는 것을 다시 텔레그램용으로 만들 필요는 없었다.)
텔레그램 CLI설치는 '리눅스에 텔레그램(Telegram) CLI 설치' (글 좋은진호)를 참고하고, 이 글은 서버 모니터링용 텔레그램 스크립트와 실제 사용 예를 보여줄 것이다.
1. 텔레그램 lua 스크립트 기본 구조
메시지를 수신하면 on_msg_receive 함수가 호출된다. 이 함수내에서 send_msg, send_photo, send_text 등의 함수를 이용해서 메시지를 상대에서 보낼 수 있다. 다음은 'hello'를 수신하면, 결과를 보내준다.
[ 스크립트 기본 구조 : hello.lua ]
function on_msg_receive (msg)
if ( msg.text == 'hello' ) then
send_msg(msg.from.print_name, "world", ok_cb, false)
return
end
end
function ok_cb(extra, success, result)
end
$ bin/telegram-cli -s hello.lua
... 생략 ...
[14:21] Jinho >>> aa
User Jinho is typing
[14:22] Jinho >>> bb
User Jinho is typing
User Jinho is typing
[14:22] Jinho >>> 헬로우
User Jinho is typing
[14:22] Jinho >>> hello
[14:22] Jinho <<< world
User Jinho marked read 1 outbox and 0 inbox messages
>
[ telegram-cli 실행화면 ]
[ 스마트폰 어플 화면 ]
2. 서버모니터링용 스크립트 살펴보기
[ 모니터링용 스크립트 일부 : server_mon.lua ]
now = os.time()
dir_cmd = "/home/cnx/cmd"
cmd_ping = "cd ".. dir_cmd .."; sh ping.sh"
cmd_load = "cd ".. dir_cmd .."; sh load.sh"
cmd_netlog = "cd ".. dir_cmd .."; sh show_netlog.sh"
cmd_mrtg = "cd ".. dir_cmd .."; perl mrtg.pl"
cmd_404 = "cd ".. dir_cmd .."; perl 404.pl"
mrtg_res = {"load", "swap", "public_if", "private_if", "disk_io", "memory_free", "memory_inactive"}
auth_phone = {["821012345678"] = true, ["821087654321"] = true }
-- 메시지 수신 처리
function on_msg_receive (msg)
if msg.date < now then
return
end
if msg.out then
return
end
-- command
local recv_msg = ""
if ( msg.text ~= nil ) then
recv_msg = string.gsub(msg.text,"[;|<>]","") -- 받은 msg중에 ;, |, <, > 등의 문자는 지움
end
local cmd, arg = split(recv_msg) -- exam : ping a b c d => cmd = "ping", arg = "a b c d"
local file_name = ""
cmd = string.lower(cmd)
print("receive : ", cmd, "\n")
-- user_info
print("Name : ", msg.from.print_name) -- 메시지 보낸사람 real name (Jinho)
print("Phone : ", msg.from.phone) -- 메시지 보낸사람 전화번호 (8210AAAABBBB)
print("Msg Num : ", msg.id) -- 메시지 번호
print("to.Name : ", msg.to.print_name)
-- 일반대화, 비밀대화 구분해서 메시지 전송
if (msg.to.id == our_id) then
user_id = msg.from.print_name -- 일반대화창
else
user_id = msg.to.print_name -- 비밀대화창
end
-- auth (지정한 폰번호만 인증)
if auth_phone[msg.from.phone] then
print "auth : OK "
else
print "auth : invalid user"
return
end
mark_read(user_id, ok_cb, false) -- 읽은 메시지로 표시
-- ping
if ( cmd == 'ping' ) then
os.execute(cmd_ping .. arg)
send_text(user_id, dir_cmd ..'/result_ping.txt', ok_cb, false)
return
end
-- load
if ( cmd == 'load' ) then
... 생략 ...
end
-- netlog
if ( cmd == 'netlog' ) then
... 생략 ...
end
-- MRTG
if ( cmd == 'mrtg' ) then
os.execute(cmd_mrtg .. arg)
arg = trim(arg)
for i=1, table.maxn(mrtg_res) do
file_name = dir_cmd .."/".. arg .."_".. mrtg_res[i] .."-day.png" -- DIR/FILE
-- print (file_name)
if ( file_exists(file_name) ) then
send_photo(user_id, file_name, ok_cb, false)
end
end
return
end
-- web 403/404 status
if ( cmd == '404' ) then
... 생략 ...
end
-- help
-- if ( cmd ~= '' ) then
if ( cmd == 'help' ) then
send_text(user_id, dir_cmd ..'/help.txt', ok_cb, false)
end
end
... 생략 ...
주요부분을 살펴보자.
if ( cmd == 'ping' ) then
os.execute(cmd_ping .. arg)
send_text(user_id, dir_cmd ..'/result_ping.txt', ok_cb, false)
return
end
모니터링용 스크립트에서는 각 명령어마다 외부 명령을 호출하고 결과를 text파일로 저장했다. 그리고, 그 text파일 내용을 메시지로 뿌려지도록 했다. 명령어 추가는 if ( cmd == '명령어 ' ) then 형태로 확장해주고, 외부 명령 처리할 스크립트만 만들어주면 된다.
if (msg.to.id == our_id) then
user_id = msg.from.print_name -- 일반대화창
else
user_id = msg.to.print_name -- 비밀대화창
end
secret chat을 지원한다. msg.to.id == our_id 라면, 즉 받는 사람과 CLI 메신저 유저가 같다면 일반대화창으로 대화중이고, 그렇지 않는 것은 비밀대화이다. 비밀대화는 msg.from.print_name이 아닌 msg.to.print_name 에게 메시지를 보내야 한다. 아래 루틴이 없다면 폰에서 비밀대화로 메시지를 보냈더라도 CLI에서는 응답을 일반대화창으로 보낸다.
if auth_phone[msg.from.phone] then
print "auth : OK "
else
print "auth : invalid user"
return
end
auth_phone 에서 지정한 휴대폰에서만 명령을 처리한다.
mark_read(user_id, ok_cb, false)
메시지를 읽은 것으로 표시한다.
※ lua스크립트를 처음해봤다. lua 매뉴얼을 보면서 스크립트를 하루에 만들고, 그 이후에 한번 기능을 추가한 정도다. 그래서 스크립트 lua개발자들이 보기에는 좋지 않은 부분도 있을 것이다. lua고수에게는 연필깍는 칼처럼 뻑뻑하게 보일 수 있겠지만, 누군가 다듬어 사용하면 생선회감용 칼처럼 휼륭(?)하게 쓰일 수 있을 것 같다.
※ ping.sh, load.sh 등의 외부 호출 스크립트는 공개하지 않겠다. 내부 서버들의 데이터를 추출하는 부분이라서 공개하기가 쉽지 않고, 이 글을 읽는 분들의 서버상황이 저와는 다를테니 스크립트 내용보다는 호출 방식만 눈여겨 보면 좋을 것 같다.
3. 텔레그램 CLI 실행
$ bin/telegram-cli -s server_mon.lua
또는
$ bin/telegram-cli -s server_mon.lua -d &
테스트위해서는 -s 옵션만, 추후 운영하기 위해서는 -d 옵션으로 넣어 데몬형태로 실행한다.
[ telegram-cli 화면 : help를 보내주고, ping 결과를 보내준 화면 ]
[ 스마트폰 어플 화면 : 아무키나 입력해서 도움말을 확인, 서버그룹별 ping과 개별 IP별 ping 결과 요청 ]
※ 스크립트를 수정해서 'help'명령을 내렸을 때만 도움말을 보여주도록 바꿨다. 스크립트 수정 전에 캡쳐한 것임.
[ telegram-cli 화면 : load 결과, MRTG 그래프 7개를 보내준 화면 ]
[ 스마트폰 어플 화면 : load 결과, MRTG 그래프를 받은 화면 ]
4. 참고자료
* 텔레그램 CLI 소스에 들어있는 test.lua
* 텔레그램 README-LUA
* Lua check if a file exists
* Telegram Messenger Client