DjangoとuWSGI、Nginxの関係を理解したい

## はじめに

Djangoを使ったアプリをデプロイする時にuWSGIとNginxが必要らしいと聞いて、役割や関係を理解するのに時間がかかったので書いておきます

:::note info
ここでは、DjangoとuWSGI、Nginxの役割や関係について解説していきます。
:::


## まずは登場人物を紹介
- ブラウザ
    Chromeとかedgeとかですね
    <br/>

- __Django__ （ジャンゴ）
    Pythonのフレームワークの一つです。
    **Pythonのフレームワークは、WSGI（_Web Server Gateway Interface_）という仕様で作られています。**
    WSGIは、webサーバとpython製のWebアプリケーションをつなぐために必要な規格です。
    他には _Flask_ や _Bottle_ があります。
    <br/>

-  __uWSGI__（ウィズギー）
    アプリケーションサーバです。
    **WSGIの仕様を満たすWebアプリを動かすためのアプリケーションサーバをWSGIサーバーと言います。**　WSGIサーバーの一つがuWSGIです。
    他にGunicornやNGINX Unitがあります。
    「ウィズギー」と発音しますが、言いにくいので自分は「上杉さん」と呼んでいます。
    <br/>
    
- __Nginx__ （エンジンエックス）
    **Webサーバソフトの一つです。**
    Webサーバとは、ブラウザから「このページのファイルを頂戴」と言われたら「ほれどうぞ」と渡すやつです。
    ブラウザがそのファイルを表示することで人間はwebページを見ることができます。
    「Webサーバのソフト」は　WebサーバをWebサーバたらしめている中身のことです。パソコンにNginxを入れたらWebサーバとして動きます。他に _Apache_ や _IIS_ があります。
    <br/>
- データベース
    ブラウザであれこれ入力したものを保存しておく場所です


##  上杉さん(uWSGI)たちの関係は？
ではお三方が何をする人でどんな関係なのかを見ていきます。
**例として、通販の商品詳細ページを開いたとしましょう**
1. ブラウザが「商品詳細ページを表示して」とリクエストする
2. Nginxが、リクエストをuWSGIに送る
3. uWSGIが、Djangoに「商品情報とHTMLを頂戴」と命令する
4. Djangoが、データベースに「この商品情報を検索して」と命令する
5. データベースが、Djangoに「ほれ、商品情報だよ」と渡す
6. Djangoが、uWSGIに「ほれ、商品情報とHTMLだよ」と渡す
7. uWSGIが、Nginxに「商品情報とHTMLだよ」と渡す
8. Nginxが、商品画像やらHTML、CSS、javaScriptなどの静的ファイルを準備する
9. Nginxが、商品の詳細情報と静的ファイルを一緒にしてブラウザに送る

:::note info
ブラウザからリクエストをもらうと、
NginxはuWSGIを通じてDajngoに伝え、
DjangoがuWSGIを通じて必要な情報を送り、
Nginxは画像などの静的ファイルと一緒にブラウザに返します
:::

まるでバケツリレーみたいですね。図にするとこんな感じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3999989/a0867f9f-9800-4751-b869-f6d064c077ab.png)




## 【備忘録】Nginx、uWSGI、Djangoの接続
ここからは、備忘録としてNginx、uWSGI、Djangoの接続方法を残します。

### --- NginxとuWSGIをつなぐ ---
必要なファイルは３つです。

- uwsgi.ini （uWSGI設定ファイル）
- uwsgi.params　（ WSGI 変数 を定義）
- nginx.conf　（Nginxの設定ファイル）

接続方法として、ポートを指定して接続する場合とソケット通信する方法がありますが、今回はポートを指定して接続する場合で書きます。

```uwsgi.ini
chdir = /code
module = myproject.wsgi:application
master = true
processes = 4
vacuum = true
die-on-term = true
env = PYTHONPATH=/code
```
```ruby: uwsgi.params
uwsgi_param  QUERY_STRING       $query_string;
uwsgi_param  REQUEST_METHOD     $request_method;
uwsgi_param  CONTENT_TYPE       $content_type;
uwsgi_param  CONTENT_LENGTH     $content_length;

uwsgi_param  REQUEST_URI        $request_uri;
uwsgi_param  PATH_INFO          $document_uri;
uwsgi_param  DOCUMENT_ROOT      $document_root;
uwsgi_param  SERVER_PROTOCOL    $server_protocol;
uwsgi_param  REQUEST_SCHEME     $scheme;
uwsgi_param  HTTPS              $https if_not_empty;

uwsgi_param  REMOTE_ADDR        $remote_addr;
uwsgi_param  REMOTE_PORT        $remote_port;
uwsgi_param  SERVER_PORT        $server_port;
uwsgi_param  SERVER_NAME        $server_name;
```

``` ruby: nginx.conf
upstream django {
    server 127.0.0.1:8000;  # TCP ポートで通信
}

server {
    listen      80;
    server_name localhost;
    charset     utf-8;

    location /static/ {
        alias /code/static/; # 静的ファイルの場所
    }

    location / {
        uwsgi_pass  django;   # Nginx が uWSGI に接続する
        include     uwsgi_params;
    }
}

server_tokens off;
```

### --- DjangoをuWSGIで動かす ---

```ruby: uWSGIの起動コマンド
uwsgi --ini /path/to/uwsgi.ini
```
もし、Dockerコンテナを起動するのであれば、

compose.ymlファイルの、Djangoコンテナの設定の中で下記のように変更します。
```compose.yml
# uWSGIとNginxを使わない場合
# command: python3 manage.py runserver 0.0.0.0:8000

# uWSGIとNginxを使う場合
command: bash -c "apt-get update && \
         apt-get install -y libpcre3 libpcre3-dev && \
         uwsgi --ini /code/uwsgi.ini"
```


これでコンテナ起動時にDjangoコンテナでuWSGIの起動コマンドを実行します


## まとめ
DjangoとuWSGI、Nginxの役割と関係について自分なりの理解をまとめてみました。
「結局、上杉さんってリクエストを右から左に流してDjangoに命令してるだけで何もしてないじゃん」って思った方、それは違います。
食事中「ソース取って」と言われたシーンを思い出してください。めっちゃ長いテーブルの端同士だったら、１人の手渡しでは届きませんよね。
上杉さんも同じです。多分。

## 参考文献
https://www.python.ambitious-engineer.com/archives/1959#WSGI

https://blog.hirokiky.org/entry/2018/09/30/183840

https://gihyo.jp/dev/feature/01/wsgi/0001