보안글이 어느정도 정리되면(아직 갈길은 좀 멀어보이긴 하지만--;;) 그 동안에 개인적으로 업데이트된 부분들을 기반으로 파이썬 부록 편을 몇 개 작성하고 싶었는데, 마침 그 부분 중 하나에 대해서 콕 찝어 문의해 주신 "독자(닉네임)"님이 있으셔서 간단하게 정리를 해보려고 한다. 물론 구글을 잘 찾으면 있긴 한 내용이지만, 아무래도 처음 하게되면 개념도 잘 안잡히고, 버전이 달라서 헤멨었던 경험이 있어서 한번 더 모아 정리하는 것도 웹 공해는 안될 것 같았기 때문이다. 기본적으로 책 기준인 파이썬 3.6.4 에 IIS 7.5(윈도우즈 10 기준), Apache 2.4 를 기준으로 세팅해 보려고 한다.
1. 들어가면서
플라스크(flask)던 장고(django)든 굳이 IIS, Apache 같은 웹서버와 연결해야 하는 이유는 예제에서 보였던 CMD 창에서 띄우는 방식은 CMD 창이 닫혀 버리면 웹사이트 자체로 사라져 버린 다는 것 때문이다. 특히 사용자가 로그 아웃을 하게 되면 모든 CMD 창은 사라지기 때문에, 다른 사람이 해당 페이지를 원격에서 접속해서 지속적으로 사용할 수 없게 된다. 물론 서비스 형태로 등록해 본다던가 하는 꽁수는 있을 것 같긴한데, 아무래도 정상적인 절차는 예전 플라스크 시간에 얘기했던 WSGI(Web Server Gateway Interface) 방식을 사용해, 사용자 요청은 안정적인 웹 서버에서 전담하고, 로직 처리만 WSGI 를 통해서 파이썬으로 전달해 처리하여 다시 웹 서버를 통해서 사용자 브라우저에 전달해 주는 방식일 것이다. 윈도우즈 쪽에서는 Fast-CGI 라고 보통 얘기하는 듯하고, 아파치 쪽에서는 WSGI 라고 얘기하는 거 같은데, 뭐 두가지 사이에 설계적 차이는 있다고는 하는데, 딱히 선택의 여지도 없으니까 무시해도 될 갭인 것은 같다.
[불곰님 블로그 - WSGI, WAS, GgI 이해]
https://brownbears.tistory.com/350
2. Windows 10 + IIS 설정 하기
기본적으로 구글에 "windows 10 IIS flask" 라고 검색하면 몇 개의 링크가 나오는데, 몇 몇 시행착오를 겪어본 결과 가장 현재 시점에 맞춰 간략히 잘 정리된 페이지는 아래의 링크들 같다. 이 블로그의 글은 해당 글들을 여기서 연재했던 샘플에 맞춰 좀더 쉽게 풀어 정리한 버전이라고 봐도 될듯 하다.
[github - Python Flask on IIS with wfastcgi]
https://gist.github.com/bparaj/ac8dd5c35a15a7633a268e668f4d2c94
[Medium.com - Deploying Python web app (Flask) in Windows Server (IIS) using FastCGI]
간단하게 흐름을 설명하자면, 예전 설명글들을 보다보면, 여러가지 IIS 환경 변수를 직접 설정해야했는데(사실 좀 이런 오래된 리눅스 스타일의 하나하나 설정하는 작업은 요즘엔 좀 번거롭게 느껴지는 일 같기는 하다), wfastcgi 는 모듈이 점점 업데이트 되면서 wfastcgi-enable.exe 파일을 같이 제공해서, 해당 작업들을 한꺼번에 자동으로 해준다. 이 후 우리는 적당히 웹서버를 하나 만들고(디폴트 웹서버를 써도 되고), 웹서버에 대한 설정 파일인 web.config 에 연결된 fastcgi 환경과 연결된 파이썬 게이트웨이 파일을 정의하는 작업을 하면 된다. 여기서는 19교시에서 진행한 DB에서 가져온 테이블을 보여주는 "myweb.py" 파일을 기준으로 설명한다.
2.1 IIS 설치하기
18교시 레거시웹 편을 참조하여 IIS 를 설치하고 기본적인 동작을 확인 한다. 추가로 하나 더 해야 할 일이 있는데, "Windows 기능 켜기/끄기" 에서 "인터넷 정보 서비스" > "World Wide Web 서비스" > "응용 프로그램 개발 기능" > "CGI" 를 추가로 선택해 설치해 줘야 한다(그래야 fastcgi 기능을 쓸수 있다).
2.2 관리자 권한으로 cmd 창 실행
뒤의 설정 파일 때문에 관리자 권한으로 cmd 창을 실행해야 된다. 아니면 권한 에러가 난다.
2.3 wfastcgi 설치
C:\Python\code>pip install wfastcgi
Collecting wfastcgi
...
Successfully installed wfastcgi-3.0.0
2.4 설정 자동화 파일 실행 하기
C:\Python\code>wfastcgi-enable
구성 커밋 경로 "MACHINE/WEBROOT/APPHOST"에서 "MACHINE/WEBROOT/APPHOST"의 "system.webServer/fastCgi" 섹션에 구성 변경을 적용했습니다.
"c:\python\python.exe|c:\python\lib\site-packages\wfastcgi.py" can now be used as a FastCGI script processor
이렇게 설정하면 다른 설명 글들에서 얘기하는 여러가지 IIS fastcgi 설정이 자동으로 된다.
2.5 IIS 루트 폴더내에 파일 복사하기
19교시에서 예전에 예제로 만들었던 SQL 서버에서 데이터를 가져와 HTML 테이블로 표시하던 myweb.py 파일과 templetes 폴더안에 있는 myweb.html 파일을 IIS 루트 디렉토리인 c:\inetpub\wwwroot 에 복사한다(관리자 권한으로 해야된다). 아래와 같은 구조가 될것 이다.
1
2
3
4
5 |
wwwroot/
myweb.py
templetes/
myweb.html
web.config |
cs |
2.6 web.config 수정하기
이제 IIS 의 사이트별 config 파일인 web.config 파일을 아래와 같이 수정하자. 관리자 권한이 있어야 수정되기 때문에 메모장을 관리자 권한으로 실행해서 해당 파일을 읽어 들여 수정해야 한다.
대충 내용을 보면, 사용자 요청을 처리할 핸들러를 정의하고(<handler> 섹션), 핸들러 메인 파일이나, 루트 패스, 로그 위치 등(<appSettings> 섹션)을 설정한다. WSGI_LOG 폴더를 실습용 폴더인 c:\python\code\ 안으로 잡은거는 기본 로그 폴더인 c:\inetpub\logs 폴더가 기본적으로 IIS 사용자 권한으로 쓰기가 안되서 권한 있는 폴더로 잡은 것이다(좀 이상한건 IIS 로그 폴던데 원래 이런건진 모르겠다--;). 만약 logs 폴더를 사용하고 싶다면 해당 폴더의 속성의 보안 권한에서 IIS_IUSRS 사용자에게 쓰기 권한을 주면 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 |
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<remove name="Python27_via_FastCGI" />
<remove name="Python34_via_FastCGI" />
<add name="Python FastCGI" path="*" verb="*" modules="FastCgiModule" scriptProcessor="C:\Python\python.exe|C:\Python\Lib\site-packages\wfastcgi.py" resourceType="Unspecified" requireAccess="Script" />
</handlers>
<httpErrors errorMode="Detailed" />
</system.webServer>
<appSettings>
<!-- Required settings -->
<add key="WSGI_HANDLER" value="myweb.app" />
<add key="PYTHONPATH" value="C:\inetpub\wwwroot" />
<add key="WSGI_LOG" value="C:\python\code\my_app.log" />
</appSettings>
</configuration> |
cs |
2.7 기능 위임 부분 수정 하기
아마도 이 오류는 windows 10 타입(Home, Pro 나 정책 설정?)에 따라서 다른거 같긴한데, 현재 실습 환경인 Windows 10 Home 에서는 위와 같이 모든 설정을 다하고 "http://localhost/sqltable" 을 실행하게 되면, 아래와 같은 에러가 발생한다.
HTTP 오류 500.19 - Internal Server Error
요청된 페이지와 관련된 구성 데이터가 잘못되어 해당 페이지에 액세스할 수 없습니다.
이 구성 섹션은 이 경로에서 사용할 수 없습니다. 섹션이 부모 수준에서 잠겨 있는 경우 이 오류가 발생합니다.
구글을 찾아보면 c:\windows\system32\inetsrv\config\applicationHost.config 에서 핸들러의 겹쳐쓰기(overide) 모드를 allow 로 바꾸라던데, 막상 저 폴더에 가면 해당 파일이 없어 좀 당황을 했다. 조금 더 다른 문서를 찾다보니 이제는 저 파일을 바로 수정하려 할 수는 없고 관리화면에 기능 위임 부분을 수정하라길래, IIS 관리 모듈로 들어가 보니 아래와 같이 왼쪽 트리에서 가장 위의 항목을 선택하면 오른쪽의 관리 섹션에 기능 위임 아이콘이 있다.
해당 아이콘을 더블 클릭해 들어간 후, 아래와 같이 처리기 매핑 항목을 "읽기" 에서 "읽기/쓰기" 로 바꾸어 준다.
2.8 샘플 실행해 보기
이제 설정이 완료된 듯 하니 "http://localhost/sqltable" 이라고 치면 아래와 같이 IIS 환경을 통해서 flask 페이지가 나오게 된다(첨엔 많이 신기했음;).
3. Windows 10 + Apache 설정하기
다음으로는 아파치에 설정하는 부분을 똑같이 해보려 한다. 아파치 설정하는 부분은 다른 시리즈 글인 "구글로 공부하는 보안 3교시" 뒤쪽을 보면 PHP 설정하는 곳에 놓여있다. 거길 그대로 따라해서 "It Works!" 샘플페이지 까지 봤다고 가정하고 연결해서 쓴다(PHP 설정도 그대로 해도 된다. 단 현재 설정을 config 파일에 하게되면 php 해석기는 안 돌아가긴 한다)
구글을 찾아보니 아래 2개의 사이트를 주로 참조하면 될듯 하다.
[ZEIZ님 블로그 - Windows 10 64bit에서 Apache 64bit에 Flask with Python 3.6 배포]
[STOREHUBS 사이트]
https://www.storehubs.com/Blog/deploy-python-flask-application-apache-windows-server/
3.1 mode_wsgi 모듈 설치하기
C:\Python\code>pip install mod_wsgi
Installing collected packages: mod-wsgi
다른 환경에서는 VS 컴파일러 에러가 나서 wheel 파일을 설치하기도 한거 같은데, 아마 실습을 쭉 해왔다면 이미 깔려있어서 괜찮은 것 같다. wheel 파일을 설치해도 되고, VS 컴파일러를 설치해도 되는거 같다. 뭐 일단 잘 되니 패스.
3.2 mod_wsgi.cp36-win_amd64.pyd 파일을 복사
mod_wsgi 모듈을 설치하게 되면 파이썬의 모듈 폴더안에 c:\Python\Lib\site-packages\mod_wsgi\server\mod_wsgi.cp36-win_amd64.pyd 파일이 설치되는데, 이게 IIS 에 있던 wfastcgi.py 와 비슷한 역할을 한다고 보면 될듯 하다. 뭐 참조 사이트에 있는 것처럼 직접 해당 경로를 지정하도 되지만 경로가 조금더 단순해 설정이 쉬워 보이길 바라는 맘에서 mod_wsgi.cp36-win_amd64.pyd 파일을 c:\Apache24\modules 에 복사한다. 그 안을 보면 다 .so 확장자 파일만 있는데, 예전엔 mod_wsgi 도 .so 확장자로 지원했다가 요즘은 .pyd 확장자로 지원하는 거 같다(요 부분도 좀 그래서 구글을 참조하다보면 헷깔린다)
3.3 파이썬 용 폴더 만들고 파일 복사하기
IIS 와 비슷하게 기존에 세팅한 httpd 폴더에 설정해도 될듯 하나, php 도 나중에 다시 써야하고, flask 웹서비스용 폴더를 한번 분리해 보고 싶어서 c:\test 폴더를 만들고, IIS 때와 마찬가지로 myweb.py 파일과 templetes 폴더안에 있는 myweb.html 파일을 복사한다. 아래와 같은 구조가 될것 이다.
1
2
3
4 |
test/
myweb.py
templetes/
myweb.html |
cs |
3.4 WSGI 게이트웨이 파일 호출 하기
mod_wsgi 설계 구조상 IIS 처럼 해당 파일을 직접 매칭 시키지는 못하는 것 같고, 중계하는 게이트웨이 파일을 하나 만들어야 한다. 설명에는 .wsgi 확장자로 만들긴 하지만 .py 로 만들어도 괜찮길래 "gateway.py" 란 파일로 c:\test 폴더에 만들어 본다. 내용을 보면 application 이란 호칭으로 myweb.py 파일 안에 있는 app 라는 이름의 플라스크 객체를 가져온다.
1
2
3
4 |
import sys
sys.path.append('c:/test/')
from myweb import app as application |
cs |
[gateway,py]
폴더 구조를 보면 아래와 같이 하나의 파일이 추가되었다.
1
2
3
4
5 |
test/
gateway.py
myweb.py
templetes/
myweb.html |
cs |
3.5 config 파일 수정하기
이제 IIS 에서 햇던 것처럼 이것저것을 수정해야 되는데 결론적으로 아파치 설정 파일인 httpd.config 파일만 수정하면 된다. 해당 파일을 열어 맨 아래에 아래 값들을 추가한다. 여러 글들에 보면 글마다 옵션이 좀 다른데, 하나씩 빼보면서 샘플에서 에러가 안나는 최소 값으로 추려봤다.
<httpd.config 에 추가>
# 사용하려는 test 폴더에 모두 허용하는 것으로 설정한다(운영에서는 뭔가 고민할 부분이 있을 수도)
<Directory "c:/test/">
Require all granted
</Directory>
# 복사했던 모듈 파일을 읽어오고
LoadModule wsgi_module modules/mod_wsgi.cp36-win_amd64.pyd
# flask 게이트웨이 파일 위치를 알려준다
WSGIScriptAlias / "c:/test/gateway.py"
3.6 아파치 재시작
아파치를 재 시작해야 config 파일이 반영되므로 예전 글에서 설명한 트레이 아이콘 이용해 재시작한다. 뭔가 수정이 잘못됬음 재시작시 에러가 날텐데 해당 경우는 c:\Apache24\logs\error.log 로그를 보고 원인을 해결해야 한다.
3.7 동작 확인해보기
"http://localhost:9999/sqltable" 를 호출해보면 IIS 때와 같은 테이블 결과를 볼 수 있다.
4. 에러 추적 및 웹서버 구성 시 차이점
마지막으로 WSGI 방식으로 서버를 구성하면서 CMD 창에서는 잘 돌아가던 코드가 안돌아갔거나 구성 설계를 바꾸거나 해야될 필요가 있던 부분에 대해서 얘기하려 한다.
4.1 에러 추적
저렇게 연결했을때 제일 속터지는 상황이, CMD 창에서는 잘 실행이 되는데 fastcgi나 mod_wsgi 를 연결하게 되면 에러가 날때 이다. 에러 원인을 찾아 해결하는 것도 힘든 일이지만 처음 만나게 되면 일단 화면에서 아래와 같이 에러의 상세 내용 없는 500 대 화면만 보게 되기 때문이다.
Internal Server Error
The server encountered an internal error or misconfiguration and was unable to complete your request.
아파치 같은 경우는 앞에서 얘기한 c:\Apache24\logs\error.log 를 보게 되면 우리가 CMD 창에서 봤던 에러를 똑같이 볼 수 있는 것 같고, IIS 의 경우는 따로 남기는 에러는 없는 것 같아(아시면 댓글 좀_ _;), 아래의 스택오버플로우 글에서 제시한 코드를 app = Flask(__name__) 뒤에 추가해서 500에러 메시지를 보이게 하는게 현재까지로는 제일 난 것 같다.
[스택 오버플로 - python flask login form throws 500 error on iis]
https://stackoverflow.com/questions/31804800/python-flask-login-form-throws-500-error-on-iis
1
2
3
4
5
6
7
8
9
10
11
12 |
app = Flask(__name__)
@app.errorhandler(500)
def internal_error(exception):
app.logger.exception(exception)
file_handler = RotatingFileHandler('C:\inetpub\wwwroot\logs.log', 'a', 1 * 1024 * 1024, 10)
file_handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'))
app.logger.setLevel(logging.INFO)
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
app.logger.info('microblog startup')
return render_template('500.html'), 500 |
cs |
해당 코드를 넣으면 아래와 같이 나름 상세한 에러가 브라우저 화면에 보이게 된다.
Error occurred: Traceback (most recent call last): File "c:\python\lib\site-packages\flask\app.py", line 2292, ..... , in make_response 'The view function did not return a valid response.
4.2 CMD 개발과 웹서버 연결시 차이점.
불행하게도 잘 돌아가던 flask 코드를 IIS 나 Apache 와 연결하게 되면 에러가 발생하는 경우가 종종 생기는 것 같다. 개인적으로 기억 나는 일은 SQLite 를 사용하는 경우와 flask-excel 이라는 엑셀 다운로드 모듈을 사용하는 경우였다. SQLite는 IIS 에서 돌리니 기본적으로 웹서버 계정에 로컬 파일을 쓰는 권한이 없어서 에러가 났고, flask-excel 은 excel 초기화 함수가 if __name__ == "__main__": 안에 정의해 놓으니 WSGI 모듈이 해당 코드를 무시해 버려서 초기화 작업이 안되서 에러가 나게 됬다.
Flask 로 샘플 페이지를 만들다 보면, 웹프로그램이라는걸 깜빡 잊고 일반 파이썬 어플리케이션 처럼 파일도 읽고 쓰면서 개발하게 될 경우가 많은데, 막상 웹 서버로 배포하려 하다보면, 기본적으로 로컬에 있는 파일을 왠만하면 접근 못하도록 막아놓은 웹 서버 설계를 무시하고(보통 보안적으로 이렇게 되는 부분이 안전하기도 하다) 로컬 파일을 읽고 쓰는 방식으로 만드는 게 맞나 하는 생각이 들게 된다. 그래서 SQLite 쓰는 부분을 DB 서버 자체가 권한을 관리하는 일반 SQL 로 변경하거나 하는 일도 필요할 수 있다. 이게 좀 자유롭게 취향대로 쓸 수 있는 플라스크의 조심스러운 측면인것도 같긴하다.
뭐 이런 연결방식에 따른 코드나 설계의 차이는 시행착오를 겪으면서 하나하나 다른 사람들의 경험에도 도움을 받으면서 해결 할 수 밖에는 없을 듯 싶긴하다.
5. 마치면서
뭔가 간략히 정리하려 했는데, 이왕 정리하는거 하다보니 꽤 긴 글이 되긴 했다. IIS 와 아파치 설정 부분이 다른 글에 이미 정리되어 있어서 그나마 다행이라고 생각되는 상황이였다. --; 아마 Flask, WSGI, IIS, Apache 버전이 서로 계속 변하면서 또 상황은 바뀔 수도 있겠지만, 여기서 설명한 개념만 잘 잡고 있다면 그리 많이 변하는 건 없을 거 같긴하다. 그럼 이렇게 파이썬 게릴라 포스팅을 마치려고 한다~
2019.2.4 by 자유로운설탕 |
cs |
'프로그래밍' 카테고리의 다른 글
[책소개] 버그 정글을 헤쳐 가기 위한 테스터 지침서 (0) | 2022.09.04 |
---|---|
[책 출간 안내] 구글로 공부하는 파이썬 (25) | 2018.03.02 |
구글로 공부하는 파이썬 - 21교시 (정리 - 이런저런 이야기) (0) | 2017.08.19 |
구글로 공부하는 파이썬 - 20교시 (장고 - Django 살펴보기) (0) | 2017.08.06 |
구글로 공부하는 파이썬 - 19교시 (Flask 살펴보기 - Feat. D3.js) (3) | 2017.07.20 |