🛳

Nginx + Daphne を使って django-channel を AWS EC2 上で公開する

2022/02/08に公開

以前,「django-channels を使った websocket を用いたチャットアプリの作成」という記事を書きましたが,EC2 上にデプロイしたいという相談を受けたのでチャレンジしてみます。

https://zenn.dev/y_k/articles/e8878460fff3d5aa1d1d

なお,サービス公開のことを考えればスケールアウトやスケールアップを考えた方が良いですが,
今回はシンプルにただただ見られるようにするところをゴールとします。
EIP や RDS も使用しない,最安最小構成です。

環境

  • AWS EC2
    • OS: Amazon Linux 2 (Kernel 5.10)
    • インスタンスタイプ: t2.micro (無料利用枠の対象)

セキュリティグループの編集からポート 80 にアクセスできるように事前に設定してください。

Nginx のインストール

$ sudo amazon-linux-extras install -y nginx1
$ sudo systemctl enable nginx.service
$ sudo systemctl start nginx.service
$ sudo systemctl status nginx.service
● nginx.service - The nginx HTTP and reverse proxy server
   Loaded: loaded (/usr/lib/systemd/system/nginx.service; disabled; vendor preset: disabled)
   Active: active (running) since 日 2022-02-06 08:43:38 UTC; 6s ago
  Process: 3499 ExecStart=/usr/sbin/nginx (code=exited, status=0/SUCCESS)
  Process: 3494 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=0/SUCCESS)
  Process: 3493 ExecStartPre=/usr/bin/rm -f /run/nginx.pid (code=exited, status=0/SUCCESS)
 Main PID: 3501 (nginx)
   CGroup: /system.slice/nginx.service
           ├─3501 nginx: master process /usr/sbin/nginx
           └─3502 nginx: worker process

「Active: active (running)」となればOKです。

ウェブブラウザから IP アドレスに直接アクセスすると下の画面になるかと思います!

「Active: active (running)」だけどアクセスできないときは…

セキュリティグループの設定を見直してポート 80 にアクセスできるようにしてください。

RDBMS のインストール

今回は mariadb を使用します:

$ sudo amazon-linux-extras install -y mariadb10.5
$ sudo systemctl enable mariadb.service
$ sudo systemctl start mariadb.service
$ sudo systemctl status mariadb.service
● mariadb.service - MariaDB 10.5 database server
   Loaded: loaded (/usr/lib/systemd/system/mariadb.service; enabled; vendor preset: disabled)
   Active: active (running) since 月 2022-02-07 14:09:40 UTC; 3s ago
     Docs: man:mariadbd(8)
           https://mariadb.com/kb/en/library/systemd/
  Process: 8542 ExecStartPost=/usr/libexec/mariadb-check-upgrade (code=exited, status=0/SUCCESS)
  Process: 8381 ExecStartPre=/usr/libexec/mariadb-prepare-db-dir %n (code=exited, status=0/SUCCESS)
  Process: 8357 ExecStartPre=/usr/libexec/mariadb-check-socket (code=exited, status=0/SUCCESS)
 Main PID: 8490 (mariadbd)
   Status: "Taking your SQL requests now..."
   CGroup: /system.slice/mariadb.service
           └─8490 /usr/libexec/mariadbd --basedir=/usr

こちらも「Active: active (running)」となればOKです。

Redis のインストール

$ sudo amazon-linux-extras install -y redis6
$ sudo systemctl enable redis.service
$ sudo systemctl start redis.service
$ sudo systemctl status redis.service
● redis.service - Redis persistent key-value database
   Loaded: loaded (/usr/lib/systemd/system/redis.service; enabled; vendor preset: disabled)
  Drop-In: /etc/systemd/system/redis.service.d
           └─limit.conf
   Active: active (running) since 日 2022-02-06 11:35:52 UTC; 7s ago
 Main PID: 5172 (redis-server)
   Status: "Ready to accept connections"
   CGroup: /system.slice/redis.service
           └─5172 /usr/bin/redis-server 127.0.0.1:6379

こちらも「Active: active (running)」となればOKです。

mariadb のセットアップ

まずはセキュリティ設定から:

$ sudo mysql_secure_installation
Enter current password for root (enter for none):
Switch to unix_socket authentication [Y/n] Y
Change the root password? [Y/n] Y
New password:
Re-enter new password:
Remove anonymous users? [Y/n] Y
Disallow root login remotely? [Y/n] Y
Remove test database and access to it? [Y/n] Y
Reload privilege tables now? [Y/n] Y

設定したパスワードで接続できることを確認します:

$ mysql -u root -e 'status' -p
Enter password:
Enter password:
--------------
mysql  Ver 15.1 Distrib 10.5.10-MariaDB, for Linux (x86_64) using  EditLine wrapper

Connection id:		15
Current database:
Current user:		root@localhost
SSL:			Not in use
Current pager:		stdout
Using outfile:		''
Using delimiter:	;
Server:			MariaDB
Server version:		10.5.10-MariaDB MariaDB Server
Protocol version:	10
Connection:		Localhost via UNIX socket
Server characterset:	latin1
Db     characterset:	latin1
Client characterset:	utf8
Conn.  characterset:	utf8
UNIX socket:		/var/lib/mysql/mysql.sock
Uptime:			7 min 29 sec

Threads: 1  Questions: 24  Slow queries: 0  Opens: 20  Open tables: 13  Queries per second avg: 0.053
--------------

…さてここで「latin1」が気になるので設定します:

/etc/my.cnf
#
# This group is read both both by the client and the server
# use it for options that affect everything
#
[client-server]

+[client]
+default-character-set=utf8mb4
+
+[mysql]
+default-character-set=utf8mb4
+
#
# This group is read by the server
#
[mysqld]
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
+character-set-client-handshake=FALSE
+character-set-server=utf8mb4
+collation-server=utf8mb4_unicode_ci

#
# include all files from the config directory
#
!includedir /etc/my.cnf.d

編集したら,反映のため mariadb を再起動します:

$ sudo systemctl restart mariadb.service
$ mysql -u root -e 'status' -p
Enter password:
--------------
mysql  Ver 15.1 Distrib 10.5.10-MariaDB, for Linux (x86_64) using  EditLine wrapper

Connection id:		3
Current database:
Current user:		root@localhost
SSL:			Not in use
Current pager:		stdout
Using outfile:		''
Using delimiter:	;
Server:			MariaDB
Server version:		10.5.10-MariaDB MariaDB Server
Protocol version:	10
Connection:		Localhost via UNIX socket
Server characterset:	utf8mb4
Db     characterset:	utf8mb4
Client characterset:	utf8mb4
Conn.  characterset:	utf8mb4
UNIX socket:		/var/lib/mysql/mysql.sock
Uptime:			7 sec

Threads: 1  Questions: 4  Slow queries: 0  Opens: 17  Open tables: 10  Queries per second avg: 0.571
--------------

$ mysql -u root -e "SHOW VARIABLES WHERE Variable_name LIKE 'character\_set\_%' OR Variable_name LIKE 'collation%';" -p
Enter password:
+--------------------------+--------------------+
| Variable_name            | Value              |
+--------------------------+--------------------+
| character_set_client     | utf8mb4            |
| character_set_connection | utf8mb4            |
| character_set_database   | utf8mb4            |
| character_set_filesystem | binary             |
| character_set_results    | utf8mb4            |
| character_set_server     | utf8mb4            |
| character_set_system     | utf8               |
| collation_connection     | utf8mb4_unicode_ci |
| collation_database       | utf8mb4_unicode_ci |
| collation_server         | utf8mb4_unicode_ci |
+--------------------------+--------------------+

とても良さげです!

次にアプリケーションで使用するユーザとデータベースを作成します:

$ mysql -u root -p
Enter password:
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 5
Server version: 10.5.10-MariaDB MariaDB Server

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]>
> create database chat;
Query OK, 1 row affected (0.000 sec)

> grant all on chat.* to chat@'%' IDENTIFIED BY 'password';
Query OK, 0 rows affected (0.001 sec)

> flush privileges;
Query OK, 0 rows affected (0.000 sec)

作成したユーザで作成したデータベースにアクセスできればOKです:

$ mysql -u chat chat -p
Enter password:
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 8
Server version: 10.5.10-MariaDB MariaDB Server

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [chat]>

Python のインストール

$ sudo amazon-linux-extras install -y python3.8
$ python3.8 -V
Python 3.8.5

👍

django アプリケーションのセットアップ

https://zenn.dev/y_k/articles/e8878460fff3d5aa1d1d

↑のソースコードをそのまま使用します。
ソースコードは↓です:

https://github.com/yk-lab/django-websocket-chat-demo-app

リポジトリをクローン

$ sudo yum install -y git patch
$ cd /opt/
$ sudo git clone https://github.com/yk-lab/django-websocket-chat-demo-app.git
$ sudo chown -R ec2-user:ec2-user django-websocket-chat-demo-app/
$ ls django-websocket-chat-demo-app/
Dockerfile.dev  Pipfile  Pipfile.lock  README.md  accounts  chat  config  docker-compose.yml  manage.py  sample.env  scripts  templates

ファイル群が確認できればOKです。

pipenv・依存モジュールのインストール

$ sudo pip3.8 install pipenv
$ cd /opt/django-websocket-chat-demo-app/
$ pipenv install --deploy --system
$ python3.8 -m django

Type 'python -m django help <subcommand>' for help on a specific subcommand.

Available subcommands:

[django]
    check
    compilemessages
    createcachetable
    dbshell
    diffsettings
    dumpdata
    flush
    inspectdb
    loaddata
    makemessages
    makemigrations
    migrate
    runserver
    sendtestemail
    shell
    showmigrations
    sqlflush
    sqlmigrate
    sqlsequencereset
    squashmigrations
    startapp
    startproject
    test
    testserver
Note that only Django core commands are listed as settings are not properly configured (error: Requested setting INSTALLED_APPS, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings.).

python3.8 -m django」で上のような出力が得られればOKです。

続いて DB に接続できるように依存ライブラリをインストールします:

$ sudo yum install -y gcc python38-devel
$ pip3.8 install mysqlclient

アプリケーションのセットアップ

$ cd /opt/django-websocket-chat-demo-app/
$ mv sample.env .env

https://djecrety.ir/ なりで SECRET_KEY を作成します:

/opt/django-websocket-chat-demo-app/.env
-SECRET_KEY=CHANGE_ME
+SECRET_KEY='b5ha%-yy1j1q7#k49c8cbza(io-z7t@$scyesm3(78ix92i2ei'
-DEBUG=true
+DEBUG=false

下記ファイルをダウンロードしてパッチを適用します:

$ wget https://gist.githubusercontent.com/yk-lab/247ed4134c37ad5e6788dc77df548dbd/raw/settings.py.patch -P ~
$ cd /opt/django-websocket-chat-demo-app/config
$ patch settings.py < ~/settings.py.patch
patching file settings.py
patch unexpectedly ends in middle of line
Hunk #4 succeeded at 148 with fuzz 1.

ALLOWED_HOSTSCHANNEL_LAYERS が書き変わって STATIC_ROOTSTATICFILES_DIRS が追加されればOKです。

下記コマンドでエラーが出なければOKです(ワーニングは一旦無視してください):

$ cd /opt/django-websocket-chat-demo-app
$ set -a; source .env; set +a; python3.8 ./manage.py check --deploy
System check identified some issues:

WARNINGS:
?: (security.W004) You have not set a value for the SECURE_HSTS_SECONDS setting. If your entire site is served only over SSL, you may want to consider setting a value and enabling HTTP Strict Transport Security. Be sure to read the documentation first; enabling HSTS carelessly can cause serious, irreversible problems.
?: (security.W008) Your SECURE_SSL_REDIRECT setting is not set to True. Unless your site should be available over both SSL and non-SSL connections, you may want to either set this setting True or configure a load balancer or reverse-proxy server to redirect all connections to HTTPS.
?: (security.W012) SESSION_COOKIE_SECURE is not set to True. Using a secure-only session cookie makes it more difficult for network traffic sniffers to hijack user sessions.
?: (security.W016) You have 'django.middleware.csrf.CsrfViewMiddleware' in your MIDDLEWARE, but you have not set CSRF_COOKIE_SECURE to True. Using a secure-only CSRF cookie makes it more difficult for network traffic sniffers to steal the CSRF token.

System check identified 4 issues (0 silenced).

起動時にエラーとなるため,下記をダウンロードしパッチをあてます:

$ wget https://gist.githubusercontent.com/yk-lab/247ed4134c37ad5e6788dc77df548dbd/raw/asgi.py.patch -P ~
$ cd /opt/django-websocket-chat-demo-app/config
$ patch asgi.py < ~/asgi.py.patch
patching file asgi.py

起動後の画面アクセスでバグるので,下記をダウンロードしパッチをあてます:

$ wget https://gist.githubusercontent.com/yk-lab/247ed4134c37ad5e6788dc77df548dbd/raw/room.html.patch -P ~
$ cd /opt/django-websocket-chat-demo-app
$ patch templates/chat/room.html < ~/room.html.patch
patching file templates/chat/room.html
patch unexpectedly ends in middle of line
Hunk #1 succeeded at 63 with fuzz 1.

動作確認:

$ cd /opt/django-websocket-chat-demo-app
$ set -a; source .env; set +a; daphne config.asgi:application
2022-02-07 08:15:16,279 INFO     Starting server at tcp:port=8000:interface=127.0.0.1
2022-02-07 08:15:16,280 INFO     HTTP/2 support not enabled (install the http2 and tls Twisted extras)
2022-02-07 08:15:16,280 INFO     Configuring endpoint tcp:port=8000:interface=127.0.0.1
2022-02-07 08:15:16,281 INFO     Listening on TCP address 127.0.0.1:8000

特にエラーなく「Listening on TCP address 127.0.0.1:8000」と表示されればOKです。
Ctrl + C で終了してください。

Nginx のセットアップ

daphne.sock へプロキシするように下記設定ファイルを作成します:

/etc/nginx/conf.d/default.conf
upstream channels-backend {
    server localhost:8000;
}

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 80;

    location /static/ {
        alias /opt/django-websocket-chat-demo-app/static;
    }

    # https://qiita.com/YuukiMiyoshi/items/d56d99be7fb8f69a751b
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
    proxy_set_header X-Frame-Options SAMEORIGIN;

    location / {
        proxy_pass http://channels-backend;
    }
}

反映のため,Nginx を再起動:

$ sudo systemctl restart nginx.service

実行・確認

static 配下に js, css ファイルを作成,DBのマイグレートをします:

$ cd /opt/django-websocket-chat-demo-app
$ mkdir static
$ python3.8 manage.py collectstatic
$ python3.8 manage.py migrate

あとは createsuperuser でスーパユーザの作成をします:

$ cd /opt/django-websocket-chat-demo-app
$ python3.8 manage.py createsuperuser
メールアドレス: yetanother.yk@example.com
Password:
Password (again):
このパスワードは一般的すぎます。
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.

最後に下記を実行:

$ cd /opt/django-websocket-chat-demo-app
$ set -a; source .env; set +a; daphne config.asgi:application

ウェブブラウザから「http://IPアドレス」でアクセスすれば,下記画面が表示されるはずです!


🎉

先ほど作成したスーパユーザでログインします:

https://youtu.be/26QyBdmJq9A

別のブラウザからもアクセスしてチャットしてみます:

https://youtu.be/y2FVsBVsMx4

リアルタイムチャット成功です!

ちなみに、RDS,ECS を使って動くところまでやっているのでそちらも需要があればそのうち記事にします。

参考

Discussion