약 4년 전쯤, 한창 오픈스택을 운영하고 있을 당시 오픈스택 그 자체는 모니터링 시스템이 잘 되어있어서 그것만 보면 됐었다. 그런데, 그 안에서 돌아가는 각각의 인스턴스 (서버)들은 잘 돌아가는지 문제가 있는지 멈췄는지 알아보기가 어려웠다. 그렇다고, 그걸 확인하기 위해 SNMP 기반 모니터링 시스템을 운영하기엔 그러한 프로그램들이 워낙 거대한 규모의 소프트웨어라 부담이 되기도 했으며, 가장 중요한 건 그러한 프로그램들이 모든 서버의 상황을 한 화면에 전부 보여주지 않는다는 점이었다. 각각의 서버를 클릭해서 들어가면 자세한 상황을 보여주지만, 난 그런 자세한 상황과 정보가 필요한 게 아니라 전체 서버의 대강을 한 눈에 보여주는 모니터링 프로그램이 필요했던 것이다.

그래서 비록 미천한 프로그래밍 실력이지만 내가 원하는 맞춤형 모니터링 프로그램을 개발해보기로 했다. 우선 설계가 중요하니 무엇이 중요한지 목록부터 만들었으며, 최종적으로 내가 고려한 사항은 다음과 같았다. 참고로 본인은 프로그래머가 아닌 시스템 어드민이라는 점을 염두에 두고 아래 내용을 보시길 바란다.

  1. 100 개가 넘는 인스턴스(서버)의 상황이 한 화면에 보여야할 것 – 자체 모니터링 시스템을 만드는데 가장 중요한 이유였다.
  2. 프로토콜은 반드시 보안이 적용되어야할 것 (SSL 등)
  3. 과거의 이력은 딱히 필요없으니 DB를 사용하지 않을 것 (과거의 이력은 각 인스턴스에 있는 /var/log로 충분하다. 따라서 불필요한 부담을 주지말 것)
  4. 한 쪽 컴퓨터에 따로 하루종일 띄워놓을 예정이니, 뭔가를 보기위한 조작이 필요없을 것
  5. 사용자별로 자신이 관리하는 서버만 따로 보여줄 수 있을 것

1번의 상황을 해결하기 위해 당장 떠오른 것은 아이콘을 사용하는 것이었다. 일단 어렵진 않을 것으로 예상됐다. 2번이야 구입해서 사용 중인 Wildcard SSL 인증서가 있으니 문제될 건 없었으나 3번이 어려웠다. 여러가지 고려 끝에 최종적으로 구상한 건 각각의 인스턴스에서 5분마다 crontab을 실행해 정보를 수집하고 그것을 JSON 형태의 텍스트 파일로 생성하여 SFTP를 통해 모니터링 서버로 전송하는 것이었다.

여기서 SFTP를 통해 패스워드 없이 파일을 업로드 하려면 당연히 모니터링 서버의 authorized_keys에 키를 등록해야한다. 인스턴스가 한두개가 아닌데다 앞으로 이것을 일일히 신경쓰지 않게 하려면 적당한 수준의 자동화가 필요한데, 늘상 그렇듯 자동화는 항상 보안의 수준과 저울질을 해야한다. 나는 이 문제에 대해 어떻게 보안을 약하게 하지않고 해결할 수 있을까 고민을 한 결과 다음의 방식을 구현하기로 했다.

  1. 인스턴스(서버)에 모니터링 스크립트를 처음 설치할 때 LDAP을 통해 각 인스턴스를 담당하는 사용자가 자신의 아이디/패스워드로 인증을 먼저 하게 한다.
  2. 인증에 성공하면 모니터링 서버로부터 pem 키를 다운로드 하고 서버의 ID값을 known_hosts에 등록시킨다.
  3. 모니터링 서버의 SFTP는 JSON 데이터 파일이 업로드 되는 폴더를 chroot로 고정시키고, JSON 파일 업로드 전용 계정은 SFTP만 사용 가능하게 한다.
  4. 모니터링 서버의 JSON 데이터가 모인 폴더는 EncFS로 암호화해서 보관한다.

그리고 모니터링 서버가 파일명을 기반으로 그룹을 분류하고 JSON 데이터를 파싱하여 보여주는 형태로 했다. crontab에 등록한 모니터링 스크립트의 실행형태는 다음과 같다.

Crontab
*/5 *   * * *   user  python /home/user/monitor/monitor.py services=22,80,443,3306,9102 group=WebServer user=seowon,james

이렇게 실행하면 WebServer,hostname 이라는 파일이 생성된다. 이 파일에는 위에서 언급한 JSON 데이터가 들어있는데 최종적인 내용은 다음과 같다.

파일명: group,host_name
파일내용
{
  "uptime": "0days",
  "now": "12/31/2019 12:00",
  "group": "WebServer",
  "network": "IP=123.123.123.123,S=10.0MiB,R=227.8MiB,ErrIn=0,ErrOut=0",
  "package_update": "10;3",
  "hostname": "public-reverse-proxy",
  "cpu": "2=0%,0%,0%",
  "client_type": "instance",
  "user": "seowon,james",
  "memory": "T=3.9GiB,F=1.6GiB",
  "disk": "/=26.3%",
  "os": "Ubuntu,18.04",
  "port": "22=w,80=w,443=w,3306=w,9102=w"
}

4번의 경우 1번에서 보여주기로 한 아이콘에 색깔을 추가해서 Warning, Danger 등의 단계로 다양한 상황을 표시해주기로 했고, 5번은 어차피 사용자들이 LDAP을 통해 로그인할테니, 인스턴스에서 생성하는 JSON 데이터에 사용자명을 추가하여 그룹으로 묶을 수 있게 했다.

전산학 박사(Ph.D)이자 여러 프로그래밍 대회에서 많은 상을 받은 적 있는 내 사수는, 이러한 일련의 서버-클라이언트간 모니터링 데이터 전송 및 관리 아이디어에 대해 “상당히 인상적” 이라고 평가했었다. 그런 칭찬으로 인해 개발에 더 재미와 가속이 붙지않았나 싶었다.

클라이언트에서 정보를 수집하는 프로그램은 그나마 익숙한 파이썬으로 작성했으며, 오픈스택 내에서 운영하는 인스턴스의 갯수가 100 개가 넘는 관계로 만약 모니터링 프로그램의 기능이 변경됐을시 자동으로 업데이트하는 기능도 필요했다. 그래서, 모니터링 스크립트가 실행될 때마다 모니터 서버로부터 모니터링 서버의 주소, SFTP 유저이름, maintenance mode 지시자 등의 값이 담긴 config 파일을 다운로드한 하고, 모니터링 스크립트들의 파일명, 각 스크립트 파일들의 버전값과 해쉬값이 담긴 텍스트 파일을 다운로드하여 각 스크립트들과 비교하고, 만약 버전이나 해쉬값이 다르면 무조건 서버에서 다운로드하여 모듈을 다시 로딩하게 했다. 또한, 스크립트에 몇 가지 옵션을 추가하여 문제점 발생시 원인을 쉽게 확인하게 했는데, 옵션을 붙이지않고 실행하면 아무 메시지도 출력하지 않으며, verbose를 붙이면 모든 수집 데이터를 보여주고, no-upload는 모니터 서버로 JSON 파일을 전송하지 않으며, no-upgrade는 스크립트 버전/해쉬 체크를 건너뛰고, version을 붙이면 현재 스크립트들의 버전값과 해쉬값을 추가해서 프린트 하게 했다. 마지막으로 debug를 붙이면 최종적으로 생성되는 JSON 데이터를 표시하도록 했다. 아래는 verbose, no-upload, debug, version의 옵션을 붙여서 실행한 화면

플러그인의 버전이나 해쉬값이 다를 경우 새로운 파일을 다운로드하는 화면

클라이언트 스크립트를 완성했으니, 이제 서버 사이드 인터페이스를 만들 차례였다. 개인적으로 파이썬을 좋아하긴 하지만 파이썬의 웹 프레임워크인 장고는 별로 좋아하지 않는데, 이유는 워낙 PHP에 익숙해져서인지 HTML 파일에서 가끔씩 필요한 PHP 코드 삽입이 종종 필요할 때 장고는 HTML 템플릿에서 정해진 문법 외의 코드를 허용하지 않아서이기 때문. 게다가 WSGI는 작동도 잘 이해가 안가고 해서 그냥 익숙한 PHP를 쓰기로 했다. 그리고 사실 솔직히 Flask는 당시 몰랐으며, 지금도 여전히 써본 적이 없어서 잘 모른다. PHP로 만들고 Bootstrap을 붙이면 무난할 것으로 보였다. 아래는 완성된 화면.

화면은 풀스크린시 사무실에서 모니터링용으로 쓰는 컴퓨터의 모니터인 1280×1024에 맞춰져있으며, 그 때문에 일부는 하드코딩 되어있다. 사실 프로그래머가 아니기 때문에 방법을 몰라 어쩔 수 없이 하드코딩한 부분이 많으며, 코드 역시 전문 프로그래머들이 짠 것처럼 체계적이지도 않다. 일단 내 수준에서는 목적한 바는 달성했다. 이 모니터링 시스템을 처음 만든 이후 인스턴스의 갯수가 많이 줄어 현재는 97개의 인스턴스를 모니터링 중이며, 스크린샷에서 보시다시피 97개 서버의 상황이 모두 한 눈에 들어온다. 또한, 모니터링할 사항이 필요해서 더 추가하더라도 그렇게 큰 자리를 차지하진 않는다.

각 아이콘에 마우스를 갖다대면 아이콘별 세부사항이 나온다. 아래는 포트 아이콘에 마우스를 갖다댄 화면으로 w는 Working이란 뜻이며, 서비스 데몬이 작동하지 않아서 닫혀있다면 nw (Not Working)로 표시된다.

좌측부터

  1. 호스트 이름
  2. 업데이트(보안/일반) 해야할 패키지 갯수
  3. 운영체제 버전
  4. 재부팅 필요 여부
  5. JSON 데이터 파일 타임스탬프
  6. 하드디스크
  7. CPU 사용률
  8. 포트

순서이며, 맨 우측 화살표를 누르면 아래와 같이 세부적인 내용이 나온다. JSON 타임스탬프에 빨간색 아이콘이 들어오면 해당 인스턴스로부터 JSON 데이터가 들어오고 있지 않다는 의미인데 스크립트가 JSON 데이터파일 생성에 실패를 하든 네트워크에 문제가 있든 어떤 경우라도 타임 스탬프가 내가 원하는 시간 (30분) 이상 넘어가면 빨간색으로 표시해주니 나름 상당히 유용했다. 하드디스크 용량이 75%면 노란색, 90% 이상이면 빨간색으로 표시하고, CPU 사용률 은 30% 이상이면 노란색, 50% 이상이면 빨간색으로 표시한다.

또한 맨 하단의 See Graph를 누르면 감시 중인 포트들이 작동/오작동 내역과 CPU 및 트래픽 히스토리를 보여준다.

본 모니터링 시스템을 사용하여 실제로 침입을 감지한 적이 있었다. 지금은 어떻게 됐는지 모르는 Open Journal System 일명 OJS라고 하는 오픈소스 문서 관리 시스템을 작은 인스턴스에서 운영하고 있었는데, 우리는 OJS에 심각한 보안결함(파일쓰기)이 있었던 걸 몰랐다. 결국 이 결함을 이용하여 외부에서 침투까진 성공했으나 해당 서버 외엔 서브넷 내의 다른 서버로 갈 수 없으니 침입자가 그냥 비트코인 채굴 프로그램만 돌려놓고 나갔다. 당시 침입자가 SSH 포트를 22번에서 다른 것으로 바꾼 것이 원인이 되어 내 모니터링 프로그램이 포트 작동이 안된 걸 아이콘으로 표시해줘서 발견하게 됐다.

지금까지 4년여간 쭉 잘 써왔고 사실 더 이상의 기능개선이 필요없다고 생각했는데, 최근 파이썬이 2에서 3로 완전히 이주하는 모습이 많이 보이고 여러 플러그인들이 2의 지원중단을 선언하고 있어 조만간 3로 마이그레이션을 계획 중에 있다. 사실 처음 만들 때 3로 만들려고 했고 실제로 작업 중이었는데, 많은 패키지들이 당시 2만 지원하는 것들이 많아서 어쩔 수 없이 2를 기반으로 만들었는데 이제는 옮겨야할 때가 온 것 같다.

P.S. 막상 3로 마이그레이션 시작해보니, 수정해야할 게 얼마 없어서 2시간 만에 모두 끝냈다.