지난 주에 breakpad로 간단한(?) 크래시 리포팅 툴을 작성했다. 이 리포트를 받는 툴을 python으로 작성했는데, 이걸 서비스로 띄우기 위해 어제 했던 삽질을 간략히(?) 정리 해보는 차원에서 글 하나.
우선 아주 간단한 웹 서버를 예로 쓰겠다. localhost:8000에서 HTTP GET으로 요청이 들어오면, 이 GET 경로를 그대로 text/plain으로 보내주는 서버다.[1]
from SocketServer import ThreadingTCPServer
class EchoHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header(‘Content-Type’, ’text/plain; charset=UTF-8′)
self.end_headers()
self.wfile.write(self.path)
class WebServer(ThreadingTCPServer, BaseHTTPServer.HTTPServer):
pass
if __name__ == ”__main__”:
httpd = WebServer((‘127.0.0.1’, 8000), EchoHandler)
httpd.serve_forever()
이 서버를 pywin32를 써서, Windows NT Service로 만들었다. 이건 처음 해보는 거라 아주 제대로 삽질했다(…). 일단 코드 자체는 아래처럼 생겼다. 대략 코드를 보고 동작을 이해할 수 있다 – 다만 개인적으로 이해 안가는 건, SERVICE_STOPPED를 내가 통보하면 없는 HANDLE이라는 이벤트 로그가 남더라고(…). 그냥 놔두면 잘 된다.
import httpd
class HttpdService(win32serviceutil.ServiceFramework):
_svc_name_ = ’httpd’
_svc_display_name_ = ’Simple Web Server’
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.haltEvent = win32event.CreateEvent(None, 0, 0, None)
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
self.server.shutdown()
win32event.SetEvent(self.haltEvent)
def SvcDoRun(self):
self.server = httpd.WebServer((‘127.0.0.1’, 8000),
httpd.EchoHandler)
servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STARTED,
(self._svc_name_, ”))
self.server.serve_forever()
win32event.WaitForSingleObject(self.haltEvent,
win32event.INFINITE)
if __name__ == ’__main__’:
win32serviceutil.HandleCommandLine(HttpdService)
그리고 나서, py2exe를 써서 이걸 다시 배포 가능한 binary로 만들었다.[2] 그리고 실제 서버로 가져가서 배포하고, 띄워서 이 삽질은 종료. 다만 이 과정에서 삽질했던 것: py2exe로 단순 바이너리는 몇 번 만들어봐서 쉽게 될 줄 알았는데, 서비스의 경우 빌드 방법이 좀 다르더라;
이건 python 설치 디렉터리의 lib/site-packages/py2exe/samples/advanced/setup.py를 참고해서(…) 아래처럼 하니까 잘 되더라.
from distutils.core import setup
import py2exe
class Target:
def __init__(self, **kw):
self.__dict__.update(kw)
self.version = ”0.1.0.0″
self.company_name = ”UPnL”
self.copyright = ”Copyright (C) 2010 rein@????.org”
self.name = ”Simple Echo WebServer”
target = Target(
description = ”A sample web server service”,
modules=[‘httpdservice’],
cmdline_style=‘pywin32’,
)
setup(service = [target])
회사에서 한 번 하고 나니, 집에 와서 다시 짜는 데는 시간 얼마 안 걸리더라; 앞으로는 Windows 서비스로 띄워야 해도, 간단한 거라면 그냥 python으로 작성하게 될 듯 하다
오 이거 재밌겠네요. 파이선3도 되려나요. 예전엔 py2exe가 python3 지원안됐던 것 같은데. 한번 따라해 봐야겠어요.
py2exe는 (적어도 최근까지는) python 3000 지원은 없었던 듯 합니다.
그래도 pywin32는 python 3.x 지원도 있으니 그거까지는 해보실 수 있겠네요(…)
py2exe 하고 cx_freeze (http://cx-freeze.sourceforge.net/ ) 하고 차이가 있을까요? 뒤에꺼는 파이썬 3.X 지원하던데 이걸로 하면 서비스를 잘 래핑할 수 있는지 모르겟습니다. 저도 삽질했는데 잘 안되더군요.
cx_freeze는 안 써봐서 모르겠네요. 서비스 래핑은 원래 py2exe가 해주는건 아니라서 약간 다른 얘기일 거 같긴 합니다. 그쪽 기능을 처리하는 pywin32 문서를 좀 읽어보시는게 좋겠네요.
[…] Python으로 Windows Service 제작해보기 […]