Daphne

 

Django와 WebServer가 주고 받기 위해서는 WSGI 프로토콜을 사용합니다.

이와 관련된 글은 여기를 참고해주세요 !


 

Django Channels를 배포하기 위해서는 HTTP 요청은 uWSGI 프로토콜로 받고, WS( Web Socket ) 요청은 ASGI 프로토콜로 받아야 합니다.

즉, HTTP 요청 뿐만 아니라, WS 요청도 처리해야 합니다.


Daphne은 Django Channels를 지원하기 위해 개발된 HTTP, HTTP2, WS 프로토콜 서버로서, HTTP와 WS 요청을 받아들여서 자동으로 어떤 프로토콜로 처리해야 할지 스스로 결정합니다. ( 깃헙 )



Channels에서 사용하는 ASGI는 WSGI와 호환이 잘 되도록 디자인이 되어 있어서, 이러한 처리가 가능합니다.

또한 Daphne은 Cahnnel를 설치하면 자동으로 설치되며, Channels에서 지원하는 서버이므로 WebServer가 따로 필요 없습니다.


그런데 Daphne을 사용해본 결과 Daphne은 static 파일( CSS, JS, image 등 )을 처리 못하는 것 같았습니다.

그래서 결국엔 HTTP 요청은 Apache나 Nginx로 받고, WS 요청만 Daphne이 받도록 처리를 했는데,

Daphne의 문서에서도 보안상의 문제 등으로 인해 HTTP 요청을 Daphne에서 처리하기가 불안하다면, WSGI 서버 앞에 Daphne을 두도록 설정하라고 되어 있습니다.


이 글에서는 Daphne로 HTTP와 WS를 모두 처리하는 방법과 Nginx를 revers proxy로 둬서 ws 요청은 Daphne가 처리하도록 하는 방법에 대해 알아보겠습니다. ( 어차피 Nginx을 사용해도 Daphne 서버를 실행시켜야 하기 때문에 Daphne 사용법을 알아야 합니다. )



우선 Daphne을 사용해서 Channels 애플리케이션을 배포해보도록 하겠습니다.

서버 환경은 CentOS 7이고, 배포하는 방법은 공식 문서를 참고했습니다. ( )





1. ASGI 설정

Django 애플리케이션을 생성하면 자동으로 wsgi.py 파일을 생성해줍니다.

이는 WebServer와 연결을 하기 위한 파일인데, 마찬가지로 ASGI 프로토콜을 위한 연결 점으로 asgi.py 파일을 생성해야 합니다.

# vi /usr/local/victolee/Channels_test/Channels_test_project/asgi.py """ ASGI entrypoint. Configures Django and then runs the application defined in the ASGI_APPLICATION setting. """ import os import django from channels.routing import get_default_application os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project_name.settings") django.setup() application = get_default_application()

os.environ.setdefault() 함수의 두 번째 인자로 " 프로젝트 이름.settings "를 전달합니다.

예를들어 프로젝트 이름이 Channels_test_project라면, Channels_test.settings가 되겠습니다.





2. Root Application 등록

다음으로 프로젝트 디렉터리에 있는 routing.py 파일에서 아래의 코드를 추가합니다.

# vi /usr/local/victolee/Channels_test/Channels_test_project/routing.py

ASGI_APPLICATION = "Channels_test_project.routing.application"





3. Daphne 실행

이제 Daphne로 Channels 애플리케이션을 실행해보겠습니다.

# cd /usr/local/victolee/Channels_test # daphne -b 0.0.0.0 -p 8000 Channels_test_project.asgi:application

Daphne 서버를 실행하는 명령어는 위와 같습니다.


브라우저에서 ifconfig로 IP 주소를 확인한 후 접근해보면( 저 같은 경우는 http://192.168.203.136:8000/ ),

Django Channels 애플리케이션이 잘 실행되는 것을 확인할 수 있습니다.





4. Daphne에 SSL 적용

Daphne에 SSL을 적용할 수도 있습니다. ( 참고 링크 )

그러기 위해서는 SSL 인증서가 필요한데, Let's Encrypt로 인증서를 발급 받는 방법은 여기를 참고해주세요 !


먼저 Daphne에 SSL을 적용하려면, OpenSSL 모듈을 설치해야 하고,

Daphne가 HTTP2 / SSL을 작동하게 할 수 있도록 하는 모듈도 설치해야 합니다.

# pip3.6 install pyOpenSSL
# pip install -U Twisted[tls,http2]


이제 Let's encrypt로부터 받은 pem 파일을 옵션에 추가하여 실행하여 Daphne를 실행하면 됩니다.

SSL을 적용한 Daphne의 포트는 8443으로 하겠습니다.

# daphne -e \ ssl:8443:privateKey=/etc/letsencrypt/live/3ab7cf4c.ngrok.io/privkey.pem:\ certKey=/etc/letsencrypt/live/3ab7cf4c.ngrok.io/cert.pem \ Channels_test-project.asgi:application

브라우저에서 https 프로토콜로 접근해보면, 정상적으로 실행이 될 것입니다.





5. ws를 wss로 수정

그런데 지금까지의 과정을 마친 후, 채팅을 해보면 아래와 같은 에러가 발생할 것입니다.


그 이유는 클라이언트 단에서 WebSocket 인스턴스를 생성할 때, ws:// 프로토콜로 생성했기 때문입니다.

따라서 HTTPS로 Daphne를 실행했다면, 채팅 메시지가 ws 프로토콜이 아닌, wss 프로토콜로 통신을 하도록 해야 합니다.

# vi /usr/local/victolee/Channels_test/channels_app/templates/chat/room.html

var chatSocket = new WebSocket(
'wss://' + window.location.hostname +
':8443/ws/chat/' + roomName + '/');






6. Daphne 서비스로 등록 ( systemd )

서버에서 Daphne을 실행하면 아무것도 할 수 없기 때문에 데몬으로 실행되도록 처리해야 합니다.

# vi /etc/systemd/system/daphne.service

[Unit] Description=daphne daemon [Service] User=root Group=root WorkingDirectory=/usr/local/victolee/channels_test/ Environment="DJANGO_SETTINGS_MODULE=channels_test_project.settings" ExecStart=/root/Env/24bot/bin/daphne -e \

ssl:8443:privateKey=/etc/letsencrypt/live/3ab7cf4c.ngrok.io/\

privkey.pem:certKey=/etc/letsencrypt/live/3ab7cf4c.ngrok.io/cert.pem \

--verbosity 0 \

--access-log /var/log/daphne/daphne.log \

chatbot_project.asgi:application ExecReload=/bin/kill -s HUP $MAINPID ExecStop=/bin/kill -s TERM $MAINPID Restart=on-abort PrivateTmp=true [Install] WantedBy=multi-user.target

주의할 것은 ExecStart 부분에서 지금은 보기 좋게 줄 단위로 나눴는데, 실제로 작성할 때는 한 줄로 작성해야 했습니다.


이제 Daphne 서비스를 실행시킵니다.

그 전에 Daphne 로그를 남기는 디렉터리가 없으므로 디렉터리를 생성합니다.

# mkdir /var/log/daphne


# systemctl enable daphne

# systemctl start daphne


daphne 서비스가 실행되는지 확인

# journalctl -u daphne


여기까지 설정을 마쳤으면, Daphne 서비스를 실행했을 때 Channel 애플리케이션이 정상적으로 수행되어야 합니다.





Daphne을 Nginx proxy로 설정

Daphne로 Channels 애플리케이션을 배포하면, 정적 파일을 제공할 수 없다는 문제점이 있습니다.

그래서 Nginx를 reverse proxy로 둬서, HTTP 요청은 uWSGI로 처리하고 WS 요청은 ASGI가 처리하도록 할 것입니다.


Daphne와 Web server를 같이 사용하기 위해 해야 할 것은 두 가지입니다.

1. Nginx에 uWSGI 프로토콜을 사용해서 Django와 연동

2. Nginx proxy 설정


uWSGI을 이용하여 Nginx와 Django를 연동하는 방법은 이글을 참고해주시고,

여기서는 proxy 설정 부분만 살펴보겠습니다.

그리고 HTTPS와 WSS 프로토콜을 기준으로 작성했습니다.

# vi /etc/nginx/conf.d/myproject.conf

server{

listen 443 ssl;

server_name 192.168.203.136; ssl_certificate /etc/letsencrypt/live/3ab7cf4c.ngrok.io/fullchain.pem;

ssl_certificate_key /etc/letsencrypt/live/3ab7cf4c.ngrok.io/privkey.pem;


location / {

include uwsgi_params;

uwsgi_pass unix:/run/uwsgi/myproject.sock;

}


location /static/ {

root /usr/local/victolee/channels_app/;

}


location /ws {

proxy_pass http://localhost:8443;

proxy_http_version 1.1;

proxy_set_header Upgrade $http_upgrade;

proxy_set_header Connection "upgrade";

}

}

443 포트에서 HTTPS 요청을 받아 모든 경로에 대해 uWSGI로 처리하도록 하고,

/ws 경로에 대해서는 proxy에서 처리하도록 했습니다.


html에서 WebSocket을 생성할 때 /ws 경로로 시작하기 때문에, location에 /ws를 작성한 것입니다.

그리고 8443포트에서는 daphne service가 실행되고 있습니다.



이제 nginx, uwsgi, daphne 서비스를 껏다가 재실행 하면 됩니다.

# systemctl stop nginx

# systemctl stop uwsgi

# systemctl stop daphne

# systemctl start nginx

# systemctl start uwsgi

# systemctl start daphne



이상으로 Daphne 서버를 사용해서 Channels를 배포해보았습니다.

다음 글에서는 대용량 트래픽을 해결하기 위한 방법에 대해 알아보도록 하겠습니다.

댓글 펼치기 👇
  1. 안녕하세요 2020.01.14 04:55

    안녕하세요 좋은 강의 너무 감사합니다!
    질문이 있는데, nginx로 http는 uwsgi로, /wp는 Daphne로 보내면 된다고 하셨는데, 이경우 두개의 앱 서버(uwsgi, asgi)를 돌려야 하는건가요?
    제가 이해한 바로는 http -> [uWSGI + Django], /ws -> [Daphne + Channel] 라는 뜻 같은데..
    그러면 사실상 두개의 앱서버라서 [Daphne+Channel] 에서 장고의 로그인 상태라던지 그런것은 어떻게 받아오는것인가요?

    • Favicon of https://victorydntmd.tistory.com victolee 우르르응 2020.01.14 21:23 신고

      안녕하세요~

      제가 구현할 당시에는 하나의 서버에서 Django와 Daphne를 돌리고 있었습니다.
      그러면 서버 부하로 인해 말씀해주신대로 서버를 분리하는 것이 좋을텐데요.
      로그인 상태는 Redis로 세션을 공유하면 될것 같습니다.