Python으로 Windows Service 제작해보기

지난 주에 breakpad로 간단한(?) 크래시 리포팅 툴을 작성했다. 이 리포트를 받는 툴을 python으로 작성했는데, 이걸 서비스로 띄우기 위해 어제 했던 삽질을 간략히(?) 정리 해보는 차원에서 글 하나.

우선 아주 간단한 웹 서버를 예로 쓰겠다. localhost:8000에서 HTTP GET으로 요청이 들어오면, 이 GET 경로를 그대로 text/plain으로 보내주는 서버다.[1]

import BaseHTTPServer
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 win32service, win32serviceutil, win32event, servicemanager
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(None00None)

    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를 참고해서(…) 아래처럼 하니까 잘 되더라.

# code from 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으로 작성하게 될 듯 하다

  1. 예를 들자면 http://127.0.0.1/path;param?var=value#frag 라면 /path;param?var=value 가 웹 페이지에 찍힌다 []
  2. 물론 한 움큼의 dll, pyd(=dll), .zip 파일이 따라 붙는다 []

Published by

rein

나는 ...

4 thoughts on “Python으로 Windows Service 제작해보기”

  1. 오 이거 재밌겠네요. 파이선3도 되려나요. 예전엔 py2exe가 python3 지원안됐던 것 같은데. 한번 따라해 봐야겠어요.

    1. py2exe는 (적어도 최근까지는) python 3000 지원은 없었던 듯 합니다.

      그래도 pywin32는 python 3.x 지원도 있으니 그거까지는 해보실 수 있겠네요(…)

    1. cx_freeze는 안 써봐서 모르겠네요. 서비스 래핑은 원래 py2exe가 해주는건 아니라서 약간 다른 얘기일 거 같긴 합니다. 그쪽 기능을 처리하는 pywin32 문서를 좀 읽어보시는게 좋겠네요.

Leave a Reply