잡다한 지식 Django - Tirrilee/TechTalk GitHub Wiki
WSGI(Web Server Gateway Interface)는 웹서버와 파이썬을 사용한 웹 어플리케이션 개발환경 간의 인터페이스에 대한 규칙 이다.
파이썬으로 선택할 수 있는 웹 프레임워크에서 사용할 수 있는 기존 웹서버는 CGI, FastCGI, mod_python, 또는 커스텀으로 만들어진 API 등으로 상당히 제한되어 있었다. 따라서 매 요청마다 그때그때 인터프리터가 메모리로 올라와야 했고, 그로 인한 성능저하를 감수해야만 했다.
또 반대로 웹서버를 선택하는 것이 파이썬 웹개발환경을 제한하기도 하였다. 이를 위해 표준으로 나온 것이 WSGI이다. WSGI는 nginx 즉 웹서버의 요청을 Python으로 해석하게 도와준다.
WSGI는 보통 서버 사이드 와 게이트웨이 사이드(Nginx, Apache 등) 를 가진다. 그리고 어플리케이션 또는 프레임워크 사이드와 통신한다.
WSGI 요청을 처리하기 위해서 서버사이드는
- 어플리케이션을 실행하고 환경정보를 제공하며,
- 콜백 함수를 어플리케이션 사이드에 전달한다.
그러면 어플리케이션은 요청을 실행하고 전달받은 콜백 함수를 통하여 응답을 서버사이드에 넘겨준다.
서버와 어플리케이션 사이에서, 양방향의 API를 실행할 수 있는 WSGI 미들웨어 (이하 미들웨어, middleware) 가 사용되기도 한다. (클라이언트 <> 서버 <> 미들웨어 <> 어플리케이션)
가장 많이 사용하는 것은 C언어로 개발된 서버인 uwsgi 이다. uwsgi는 반드시 웹서버와 함께 사용되어야 하는데 예를 들어, Nginx 웹서버를 사용함으로써 정적인 페이지를 처리하도록 하고 나머지 요청은 uwsgi에게 전달하여 처리하는 방법이 일반적이다.
Static파일이란 웹 사이트 구성요소 중 Image, CSS, Script파일과 같이 그 내용이 고정되어 응답을 할 때 별도의 처리 없이 파일 내용을 그대로 보내주면 되는 파일을 의미합니다.
따라서 Static파일들은 웹 어플리케이션을 통하지 않고, 그 전단의 웹 서버에서 URI에 따른 파일 내용을 그대로 응답 함으로써 직접 처리해 줄 수도 있습니다.
일례로, Nginx를 Reverse Proxy로 사용할 때 Static File들에 대한 요청을 Nginx가 직접 처리하도록 세팅하여 응답 속도를 향상시킬 수 있습니다. 또는 CDN과 같이 Static파일들만을 모아서 따로 관리하는 전담 서버를 통해 Static파일들을 처리하여 웹 서버의 Load를 줄이는 기법을 구현할 수도 있습니다.
STATIC_URL = '/static/'
STATIC_DIR = os.path.join(BASE_DIR, 'blog/static')
STATICFILES_DIRS = [
STATIC_DIR,
]
STATIC_ROOT = os.path.join(BASE_DIR, 'static')- STATICFILES_DIRS : Django가 접근하여 다루는 설정 (개발용)
- STATIC_ROOT
- 정적 파일을 직접 제공(serving)할 웹 서버가 접근 (배포할 때 사용 > 개발시에는 없어도 괜찮다)
- 모든 정적 파일을 하나의 경로로 모아주는 작업 이 필요하다. > runserver 는 개발자가 개발에만 집중할 수 있도록 이 작업을 알아서 해준다 (개발용에 굳이 웹서버가 필요하지 않기 때문이다.)
- STATIC_URL : 지정된 URL을 통해 정적 파일 요청을 받아온다. 정적 파일이 실제 위치한 경로를 참조하며, 이 실제 경로는 STATICFILES_DIRS 설정 항목에 지정된 경로가 아닌 STATIC_ROOT 설정 항목에 지정된 경로 이다.
Django의 runserver는 매우 간단한 서버이기 때문에 이를 실서버로 동작시키면 성능상의 문제가 올 수 있다. 따라서 앞단에 웹서버를 두어야한다.
Apache 서버는 요청 하나 당 프로세스(또는 쓰레드)가 처리하는 구조 입니다.
- 요청이 많을수록 CPU와 메모리 사용이 증가하기 때문에 성능 저하가 있을 수 있습니다.
- 또한 Apache 서버의 프로세스가 블록킹( blocking )이 되면 요청을 처리하지 못하고, 처리가 완료 될 때까지 계속 대기하는 일이 발생합니다.
이와 같은 문제들은 Keep Alive 를 활성화 함으로써 해결이 가능하지만, Keep Alive 때문에 대량 접속 시 효율이 급격하게 떨어지는 또 다른 문제점이 발생합니다.
HTTP 프로토콜의 특성상 한 번 통신이 이루어지면 접속을 끊어 버리지만, KeepAlive On 상태에서는 KeepAliveTimeOut 시간 동안 접속을 끊지 않고 다음 접속을 기다립니다.
즉, 한 번 연결된 클라이언트와 통신을 유지하고 있기 때문에, 다음 통신 시에 Connection을 생성하고 끊는 작업이 필요 없게 됩니다.
정적 자원( html파일, 이미지 파일 등 )으로만 구성된 웹 서버에 KeepAlive On으로 설정할 경우 약 50%의 성능 향상을 보인다고 합니다.
단 이와 같은 성능 향상을 보이려면 서버가 바쁘지 않아야 하는데, 바쁜 서버 환경에서 KeepAlive On으로 설정해 놓을 경우,
모든 요청 마다 연결을 유지해야 하기 때문에 프로세스 수가 기하급수적으로 늘어나 MaxClient값을 초과하게 됩니다.
따라서 메모리를 많이 사용하게 되며 이는 곧 성능 저하의 원인이 됩니다. ( 대량 접속 시 효율이 떨어짐 )
이에 따라 Apache 2.4 버전 부터 Keep Alive에 대한 고민을 해결하는 새로운 MPM 방식인, Event MPM 방식을 지원합니다.
Apache가 성능 향상에 투자를 한 이유는, 최근 웹 서버가 정적 데이터 처리를 통한 속도 향상을 노리는 프록시나 캐싱 처리용 서버로 사용하는 경향이 늘면서 속도에 매우 민감 해졌기 때문입니다.
Nginx는 보안과 속도를 최적화 시키려는 노력에서 탄생한 웹서버이며, 사용이 매우 심플하고 규모가 작은 서비스이면서 정적 데이터 처리가 많은 서비스에 적합합니다.
Apache 에서 지원하는 수많은 기능이 필요 없고 알려진 보안 문제들은 특별한 업데이트 없이 Nginx 자체에서 처리해주고 있습니다.
Nginx는 프로그램의 흐름이 이벤트에 의해 결정이 되는 Event Driven 방식의 웹 서버 입니다.
적은 수의 쓰레드로 효율적인 일 처리 를 하며, 쓰레드를 적게 사용 하기 때문에 쓰레드당 할당되는 메모리도 적게 사용하는 구조 입니다.
또한 쓰레드를 많이 사용하지 않기 때문에 context switching 비용이 적고, 따라서 CPU 소모도 적습니다.
그러나 모듈 개발이 어려우며 다양한 모듈이 없다는 것이 단점 입니다.
Nginx 는 비동기 이벤트 기반 으로 요청을 처리하고, Apache 서버는 요청 당 쓰레드 또는 프로세스가 처리하는 구조 입니다.
최근 대용량의 정적 파일 및 큰 규모의 사이트가 많아짐에 따라 대량 접속에도 적은 리소스를 사용하며 빠르게 서비스를 할 수 있는 웹 사이트가 대세 가 되었는데, 이런 면에서 Nginx가 각광 받기 시작했습니다.
성능 면에서 Apache 2.2는 Nginx에 뒤쳐졌기 때문에 Nginx가 성장하게 되었습니다.
그러다가 2012년에 빠른 응답 속도와 적은 리소스 사용 부분을 개선한 Apache 2.4를 발표하면서, Nginx에 대응하기 시작 했습니다.
그럼에도 Nginx가 성능 면에서는 Apahce 2.4 보다 좋다고 합니다. ( 엄청 큰 차이는 아닙니다. )
세션 클러스터링 같은 특별한 목적을 추가적으로 수행하는 세팅을 할 경우에는 별도의 과정을 거쳐야 하기 때문에, 이러한 별도의 작업이 많이 필요한 서비스의 경우에도 유지 보수 측면에서 Apache가 유용 합니다.
즉 안정성과 확장성, 호환성에서 Apache가 우세 , 성능 면에서는 Nginx 가 우세하다는 것이 결론입니다.
-
Web Server : 정적 서버 컨텐츠 를 수행하는 것으로 브라우저로부터 HTTP의 요청을 받아들이고, HTML 문서 등 웹페이지에서 흔히 볼 수 있는 자료 컨텐츠에따라 HTTP를 응답해주는 것이다.
-
Web Application Server : 동적 서버 컨텐츠 를 수행하는 것으로, 일반적인 웹서버와 구별되며, 주로 데이터베이스 서버와 같이 수행된다.
Django 저는 이렇게 씁니다 - slideshare
- OneToOneField에서 사용한다.
- 관련 model의 값을 cache에 저장해놓고 읽음 > DB lookup수가 현저히 줄어든다.
- 하나의 query로 related objects들을 다 가져온다.
- ManyToManyField에서 사용한다.
- main query가 실행된 후 별도의 query를 실행한다.
- dictionary array로 필요한 인자값만 받아온다.
- tuple array로 필요한 인자값만 받아온다.
- len()대신 count()를 통해 개수를 받아온다.
- model instance가 살아있는 동안 caching된다.
total_view_count = 0
for post in Post.objects.all():
total_view_count += post.view_count # view_count 필드를 모두 더하는데 더한 값이 없을 시에는 0으로 대체
total_view_count = Post.objects.all().aggregate(Sum('view_count'))['view_count__sum'] or 0항상 최신 결과일 필요가 없는 페이지의 경우 view를 caching해도 된다.
CloudWatch, newrelic, django-debug-toolbar(DB 조회수 분석), cProfile(병목현상 해결), Code Coverage(얼마나 Test가 충분한지)
+) DB Transaction : https://docs.djangoproject.com/en/dev/topics/db/transactions/ +) named Tuple : tuple에 이름을 붙임
제가 제일 헷갈려하는 것 중 하나인데 (물론 아직도 헷갈립니다.) 지금까지 알아간 것만 간단하게 정리해보겠습니다.
개발을 진행할 때와 배포를 할 때의 STATIC을 설정하는 방법이 다릅니다.
그 이유는 (이 부분은 이후 배포때 해야할 부분이기 때문에 정말 궁금하시면 읽어보시길 바랍니다.) STATIC 파일은 Django에서 보통 제공하지 않습니다. Django는 Web Application Server로 앞에 Web Server를 두어 Django에서는 동적인 데이터를 Web Server에서는 정적인 데이터를 Serving하도록 되어있습니다. (이와 같이 배포하는 것이 성능이 훨씬 우수하다고 합니다.) 초코몽키 - nginx, wsgi 개념을 참고하시면 도움이 될 것입니다.
먼저 개발을 진행할 때는 아래와 같이 세팅해주면 됩니다.
STATIC_URL = '/static/' # static 파일을 prefix url
STATICFILES_DIR = [
os.path.join(BASE_DIR, 'blog/static'),
]static은 url로 파일을 읽어옵니다. 하지만 위와 같이 설정을 했을 때, url을 설정해주지 않아도 개발 환경에서는 잘 실행이 됩니다.
프로젝트의 settings.py에 있는 django.contrib.staticfiles 에서는 staticfiles에 관련된 기능을 제공해줍니다.
이 App에서 DEBUG가 True일 때 즉 개발환경일 때, 자동으로 urlpattern을 생성하여 넘기기 때문에 개발 환경에서는 위와 같이 세팅하더라도 잘 동작하게 됩니다.
하지만 배포 환경에서는 위와 같이 세팅하면 동작하지 않습니다.
배포 환경에서는 STATIC_ROOT 를 사용하여, 웹서버에서 읽을 STATIC 파일의 경로를 설정해줍니다.
$ python manage.py collectstatic 을 진행하면, STATIC_ROOT 로 잡힌 경로에 STATICFILES_DIR 에 정의된 경로의 모든 Static 파일 이 모이게 되고, 웹서버에 Django의 STATIC_ROOT 경로를 주게 되면 웹서버에서 정적 파일을 잘 Serving할 수 있게 됩니다.