Urumos Blog

文系独学ITエンジニア

【その8】gunicornの設定【初めてのAWSデプロイ編】

投稿者: urumos

作成日: 2022年5月29日16:40

更新日: 2022年5月29日16:40

カテゴリ: 初めてのAWSデプロイ

タグ:


Gunicornの設定

仮想環境上でgunicornをインストール
 pip3.9 install gunicorn

systemdにgunicornの自動起動を指定
 sudo vim /etc/systemd/system/gunicorn.service

中身は下記
[Unit]
Description=gunicorn daemon
After=network.target

[Service]
User=XXX
Group=www-data
WorkingDirectory=/home/XXX/pyprojects/DjangoMyBlog
ExecStart =/home/XXX/pyprojects/DjangoMyBlog/.MyBlogEnv/bin/gunicorn --access-logfile - --workers 3 --bind unix:/home/XXX/pyprojects/DjangoMyBlog/config.sock config.wsgi:application

[Install]
WantedBy=multi-user.target

起動
 sudo systemctl start gunicorn
何も出ないが成功か?

自動起動を有効化
 sudo systemctl enable gunicorn
すると下記のように出た
 Created symlink /etc/systemd/system/multi-user.target.wants/gunicorn.service → /etc/systemd/system/gunicorn.service.
 シンボリックリンクが自動で出来たっぽい。

gunicornのステータス確認。
 sudo systemctl status gunicorn
 ⇒active(running)となっていればOK。
 しかし、下記のように出た

● gunicorn.service - gunicorn daemon
Loaded: loaded (/etc/systemd/system/gunicorn.service; enabled; vendor preset: enabled)
Active: failed (Result: exit-code) since Fri 2022-02-18 23:27:14 JST; 5min ago
Main PID: 146462 (code=exited, status=203/EXEC)

Feb 18 23:27:14 ip-10-0-1-10 systemd[1]: Started gunicorn daemon.
Feb 18 23:27:14 ip-10-0-1-10 systemd[146462]: gunicorn.service: Failed to execute command: No such file or directory
Feb 18 23:27:14 ip-10-0-1-10 systemd[146462]: gunicorn.service: Failed at step EXEC spawning /home/XXX/pyprojects/DjangoMyBlog/.MyblogEnv/bin/gunicorn: No such file or directory
Feb 18 23:27:14 ip-10-0-1-10 systemd[1]: gunicorn.service: Main process exited, code=exited, status=203/EXEC
Feb 18 23:27:14 ip-10-0-1-10 systemd[1]: gunicorn.service: Failed with result 'exit-code'.

gunicornが見つからないっぽい。
確かにpipでインストールした時に、XXX/.local/binに設置されるけど大丈夫?みたいな警告が出ていた。

それを仮想環境のbinに移動すればいいかと思い、そのディレクトリに移動し、mvと間違えてrmと打ってしまった。
消えてしまった。やばい。どうしよう。
pip listを見るとまだgunicornはあるみたい。
でもどこに?
再度pip install gunicornしてみる。
下記のエラーが出た。

Defaulting to user installation because normal site-packages is not writeable
Requirement already satisfied: gunicorn in /home/XXX/.local/lib/python3.9/site-packages (20.1.0)
Requirement already satisfied: setuptools>=3.0 in /usr/local/lib/python3.9/site-packages (from gunicorn) (49.2.1)

「gunicornはもうあるよ」というエラーだ。
仕方ないので一度アンインストールする
 pip uninstall gunicorn
アンインストールは成功。
再度インストールしたところ、下記のように出た。

Defaulting to user installation because normal site-packages is not writeable
Collecting gunicorn
Using cached gunicorn-20.1.0-py3-none-any.whl (79 kB)
Requirement already satisfied: setuptools>=3.0 in /usr/local/lib/python3.9/site-packages (from gunicorn) (49.2.1)
Installing collected packages: gunicorn
WARNING: The script gunicorn is installed in '/home/XXX/.local/bin' which is not on PATH.
Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed gunicorn-20.1.0

前回と同じ警告が出ている。

ファイル移動は更なるエラーを発生させる気もしたので、
逆にgunicorn.serviceファイルのgunicornがある場所のパスに書き換えてみる。
(Django教科書でお世話になったナリト先生の記事がそうなっていたhttps://blog.narito.ninja/detail/21/)

[Unit]
Description=gunicorn daemon
After=network.target

[Service]
User=XXX
Group=www-data
WorkingDirectory=/home/XXX/pyprojects/DjangoMyBlog
ExecStart =/home/XXX/.local/bin/gunicorn --access-logfile - --workers 3 --bind unix:/home/XXX/pyprojects/DjangoMyBlog/config.sock config.wsgi:application

[Install]
WantedBy=multi-user.target

再度sudo systemctl status gunicornする。
同じエラー。

多分リロードが必要なんだろうと思い、sudo systemctl restart gunicornを実行。
エラー。

Tera termを閉じて再起動。
エラー。

困った。。。
停止コマンドであるsudo pkill gunicornを実行。
何も起こらん。

再度起動のため、sudo systemctl start gunicornをする。
すると下のエラーが出た。
 Warning: The unit file, source configuration file or drop-ins of gunicorn.service changed on disk. Run 'systemctl daemon-reload' to reload units.

どうやらsystemctl daemon-reloadコマンドを打てと。
こういう時は素直に従うのが吉。

 systemctl daemon-reload

すると下記のように出た。赤字でなんか怖い。
==== AUTHENTICATING FOR org.freedesktop.systemd1.reload-daemon ===
Authentication is required to reload the systemd state.
Multiple identities can be used for authentication:
1. Ubuntu (ubuntu)
2. XXX
Choose identity to authenticate as (1-2): 2
Password:
==== AUTHENTICATION COMPLETE ===

指示通りに入力したら、なんかできたっぽい。

再度リスタート。
 sudo systemctl start gunicorn
なんも起こらんけど前のエラーは出ないから出来たっぽい?

再度ステータス。
 sudo systemctl status gunicorn

● gunicorn.service - gunicorn daemon
Loaded: loaded (/etc/systemd/system/gunicorn.service; enabled; vendor pres>
Active: active (running) since Sat 2022-02-19 00:25:55 JST; 15s ago
Main PID: 147143 (gunicorn)
Tasks: 4 (limit: 1147)
Memory: 94.0M
CGroup: /system.slice/gunicorn.service
tq147143 /usr/local/bin/python3.9 /home/XXX/.local/bin/gunico>
tq147144 /usr/local/bin/python3.9 /home/XXX/.local/bin/gunico>
tq147145 /usr/local/bin/python3.9 /home/XXX/.local/bin/gunico>
mq147146 /usr/local/bin/python3.9 /home/XXX/.local/bin/gunico>

できたああああああ!

gunicornについてもう少し勉強してみよう。

Tera termでサーバーにログイン
プロジェクトディレクトリで「ls -la」してみると、config.sockというシンボリックリンクが出来ていた。

考えてみれば上記のgunicornの設定で何をしたのかいまいち分かっていない。
仮想環境でgunicornの基本起動コマンドである下記を実行
 gunicorn --bind 0.0.0.0:8000 config.wsgi
すると下記のエラーが出た。

Command 'gunicorn' not found, but can be installed with:
sudo apt install gunicorn

どうやらgunicornが見つからないとのこと。
そしてaptコマンドでインストールできるよとのこと。
でも確かに仮想環境内でpipでインストールしたし、pip listをするとgunicornは出てくる。
「見つからない」とはいかに?

https://stackoverflow.com/questions/29679963/why-gunicorn-command-not-found-with-gunicorn-installed
ここにいくつか解決策が書いてあった。

まずはecho $PATHで現在設定されている全てのパスを表示
echo $PATH | sed -e 's/:/\n/g'
/home/XXX/pyenvs/awsmyblogenv/bin
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/games
/usr/local/games
/snap/bin

次に下記コマンドでgunicornがどこにあるか確認
pip show gunicorn

すると下記の結果だった
Name: gunicorn
Version: 20.1.0
Summary: WSGI HTTP Server for UNIX
Home-page: https://gunicorn.org
Author: Benoit Chesneau
Author-email: benoitc@e-engura.com
License: MIT
Location: /home/XXX/.local/lib/python3.9/site-packages(★ここ)
Requires: setuptools
Required-by:

これによると、gunicornがある場所は「 /home/XXX/.local/lib/python3.9/site-packages」だ。

記事によると「このpythonバージョンのためのバイナリーのためのパスは/home/XXX/.local/bin」とのこと。
バイナリーのためのパスってなんや。

試しにそこを見てみると確かにgunicornがある。
~/.local/bin$ ls -la
total 28
drwxrwxr-x 3 XXX XXX 4096 Feb 19 00:07 .
drwx------ 4 XXX XXX 4096 Feb 11 17:58 ..
drwxrwxr-x 2 XXX XXX 4096 Feb 11 17:59 __pycache__
-rwxrwxr-x 1 XXX XXX 271 Feb 11 17:59 django-admin
-rwxrwxr-x 1 XXX XXX 661 Feb 11 17:59 django-admin.py
-rwxrwxr-x 1 XXX XXX 225 Feb 19 00:07 gunicorn (★これ)
-rwxrwxr-x 1 XXX XXX 224 Feb 11 17:58 sqlformat
どうやらここをLinuxのパスに追加しないといけないみたい。

下記を実行(どこでやればいいか分からず、仮想環境のままホームディレクトリで実行)
export PATH=$PATH:$HOME/.local/bin

反応なし。上手くいったのか?

次に下記で設定の更新
source ~/.profile

すると下記のエラーが出た
bash: /home/XXX/.profile: No such file or directory

??
よくわからんが、再度gunicorn起動コマンドを打ってみる
gunicorn --bind 0.0.0.0:8000 config.wsgi

すると、下記のように出た。gunicorn自体は見つかったっぽくて良かったが、別のエラーだ。
[2022-02-23 07:52:38 +0900] [181922] [INFO] Starting gunicorn 20.1.0
[2022-02-23 07:52:38 +0900] [181922] [INFO] Listening at: http://0.0.0.0:8000 (181922)
[2022-02-23 07:52:38 +0900] [181922] [INFO] Using worker: sync
[2022-02-23 07:52:38 +0900] [181923] [INFO] Booting worker with pid: 181923
[2022-02-23 07:52:38 +0900] [181923] [ERROR] Exception in worker process
Traceback (most recent call last):
File "/home/XXX/.local/lib/python3.9/site-packages/gunicorn/arbiter.py", line 589, in spawn_worker
worker.init_process()
File "/home/XXX/.local/lib/python3.9/site-packages/gunicorn/workers/base.py", line 134, in init_process
self.load_wsgi()
File "/home/XXX/.local/lib/python3.9/site-packages/gunicorn/workers/base.py", line 146, in load_wsgi
self.wsgi = self.app.wsgi()
File "/home/XXX/.local/lib/python3.9/site-packages/gunicorn/app/base.py", line 67, in wsgi
self.callable = self.load()
File "/home/XXX/.local/lib/python3.9/site-packages/gunicorn/app/wsgiapp.py", line 58, in load
return self.load_wsgiapp()
File "/home/XXX/.local/lib/python3.9/site-packages/gunicorn/app/wsgiapp.py", line 48, in load_wsgiapp
return util.import_app(self.app_uri)
File "/home/XXX/.local/lib/python3.9/site-packages/gunicorn/util.py", line 359, in import_app
mod = importlib.import_module(module)
File "/usr/local/lib/python3.9/importlib/__init__.py", line 127, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
File "<frozen importlib._bootstrap>", line 972, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
File "<frozen importlib._bootstrap>", line 984, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'config'
[2022-02-23 07:52:38 +0900] [181923] [INFO] Worker exiting (pid: 181923)
[2022-02-23 07:52:38 +0900] [181922] [INFO] Shutting down: Master
[2022-02-23 07:52:38 +0900] [181922] [INFO] Reason: Worker failed to boot.

「config」が見つからないとのこと。
アプリのあるディレクトリ(DjangoMyBlog)に移動して再度実行

(awsmyblogenv) XXX@ip-10-0-1-10:~/pyprojects/DjangoMyBlog$ gunicorn --bind 0.0.0.0:8000 config.wsgi
[2022-02-23 07:57:42 +0900] [181947] [INFO] Starting gunicorn 20.1.0
[2022-02-23 07:57:42 +0900] [181947] [INFO] Listening at: http://0.0.0.0:8000 (181947)
[2022-02-23 07:57:42 +0900] [181947] [INFO] Using worker: sync
[2022-02-23 07:57:42 +0900] [181948] [INFO] Booting worker with pid: 181948

成功したっぽい!!!
しかしブラウザで「XXX.com:8000」を開いてみたが開けない
色々調べたけど原因が分からない。

先にgunicorn.serviceを作ってしまったせいか?
削除してみる?

ーーー

かれこれ1週間経過。
時間があいたので、記憶があいまいになり、ノートを読み返した。
gunicorn.serviceを削除してみようとも思ったが、
ノートを読み返している中でLinuxのパスに~/.local/binを追加する作業をしたが、
今もちゃんと入っているかを確認してみた。

XXX@ip-10-0-1-10:~$ echo $PATH | sed -e 's/:/\n/g'
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/games
/usr/local/games
/snap/bin

あれ、確かに前回追加したはずなのになくなっている。

再度追加する。
export PATH=$PATH:$HOME/.local/bin

追加されたか確認
XXX@ip-10-0-1-10:~$ echo $PATH | sed -e 's/:/\n/g'
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin
/usr/games
/usr/local/games
/snap/bin
/home/XXX/.local/bin

ちゃんと追加された。

ターミナルの接続を切る度にリセットされるのだろう。

今の状態でgunicornコマンドをしたらいけるかもと思い、プロジェクトディレクトリに移動し、下記を実行
gunicorn --bind 0.0.0.0:8000 config.wsgi
ブラウザで「XXX.com:8000」を開くと無事開けた!!
(そして想定通り静的ファイルは読み込まれていない)

ということはやはりパスが問題だったみたい。

次はターミナルを閉じてもパスがリセットされない方法を調べよう。

一度ターミナルを閉じて再度パスをechoしてみたところ、やはり「.local/bin」が消えていた。
この状態でgunicornのコマンドを打つとエラーになった。
Command 'gunicorn' not found, but can be installed with:sudo apt install gunicorn

念のため、再度パスを追加して再度gunicornコマンドをやってみる。
すると今度は以下のエラーが出て、失敗した。
どういうことや。
[2022-03-04 23:35:51 +0900] [262690] [INFO] Starting gunicorn 20.1.0
[2022-03-04 23:35:51 +0900] [262690] [ERROR] Connection in use: ('0.0.0.0', 8000)
[2022-03-04 23:35:51 +0900] [262690] [ERROR] Retrying in 1 second.
[2022-03-04 23:35:52 +0900] [262690] [ERROR] Connection in use: ('0.0.0.0', 8000)
[2022-03-04 23:35:52 +0900] [262690] [ERROR] Retrying in 1 second.
[2022-03-04 23:35:53 +0900] [262690] [ERROR] Connection in use: ('0.0.0.0', 8000)
[2022-03-04 23:35:53 +0900] [262690] [ERROR] Retrying in 1 second.
[2022-03-04 23:35:54 +0900] [262690] [ERROR] Connection in use: ('0.0.0.0', 8000)
[2022-03-04 23:35:54 +0900] [262690] [ERROR] Retrying in 1 second.
[2022-03-04 23:35:55 +0900] [262690] [ERROR] Connection in use: ('0.0.0.0', 8000)
[2022-03-04 23:35:55 +0900] [262690] [ERROR] Retrying in 1 second.
[2022-03-04 23:35:56 +0900] [262690] [ERROR] Can't connect to ('0.0.0.0', 8000)

試しに普通のrunserverしてみる。すると"it is already used"と出た。もう使われていると?
試しにブラウザでサイトを見てみると開けた!
どうやら前回開いたやつが開きっぱなしということだったみたい。これがデーモンモードというやつか。
ターミナルを閉じてもサイトは開けるしスマホでも見れた。ちょっと感動。

要するに実行する時にパスへの追加が必要で、
でも一度gunicornがデーモンモードで実行されれば、あとはターミナルもパスも関係ないということだ。

気を取り直してターミナルを閉じてもパスがリセットされない方法を調べる。
というかこれを期に、bash周りの知識を復習、整理してみる。

bashは「Bourne Again Shell」の略で、シェルの1種。
シェルは人間がパソコンに命令するための言語。
シェルの種類はbash以外にも色々ある。(zsh、tcshなど)
bashはインタプリタ型の言語。Linuxで標準的に使われている。

「ログインシェル」とはログイン直後に起動するシェルのこと。
下記は全てbashの設定ファイルであり、上から下の順で読み込まれる
 ★/etc/profile
  ログイン時に読み込まれる
 ↓
 ★~/.bash_profile
  bashをログインシェルとした時に読み込まれるファイル。
  もし無い場合は代わりに~/.profileを読み込む
 ↓
 ★~/.bash_login
 ↓
 ★~/.profile
  Bourne shellをログインシェルとした時に読み込まれるファイル。
  シェルがログインシェルではない形で起動する場合は、~/.profileを読み込まない。
 ↓
 ★~/.bashrc
  bashを対話的シェル(=スクリプト実行用ではないということ)として起動するときに読み込まれる。
 ↓
 ★~/.bash_logout

上記を踏まえて下記のように使い分けることが推奨されている。

★~/.profile
 ・ログイン時にそのセッション全体に適用するものを記述する
 ・シェルの種類に依存しないものを記述する
 (例)環境変数など
★~/.bashrc
 ・bashでしか使わないものを記述する
 (例)
  ・エイリアス
  ・シェルオプション
  ・プロンプト設定
★~/.bash_profile
 ~/.profileと同じに使えるが、bashのみで有効

ここで実際の自分のAWSサーバーのファイル構成を確認する。
デフォルトのubuntuのほうは「~/.profile」と「~/.bashrc」があった。
しかし自分で作ったユーザーXXXのほうは「~/.bashrc」しかなかった。
ubuntuの「~/.profile」には下記が記述されていた。


# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/bin" ] ; then
PATH="$HOME/bin:$PATH"
fi

# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/.local/bin" ] ; then
PATH="$HOME/.local/bin:$PATH"
fi

ドンピシャでPATHの追加しているやんけ!

一方「~/.bashrc」のほうは同じ内容だった。

単純にXXXのほうに同じ内容で「~/.profile」を追加してみる。
Linuxの勉強ノートを見直し、touch→vi→コピペ→保存
一度ログアウトし、再度ログイン。
パスを確認したところ、無事に自動追加されていた!OKだ!

それから、XXXでログインした時に毎回bashと打たないといけないのを直したい。
ubuntuユーザーの場合は自動でbashが起動される。XXXもこうしたい。
ネットで調べたところ、ユーザーがログインした際に実行されるシェルの種類は/etc/passwdで確認できるみたい。
早速それぞれ確認する。

ubuntu@ip-10-0-1-10:~$ cat /etc/passwd | grep ubuntu
ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash
ubuntu@ip-10-0-1-10:~$ cat /etc/passwd | grep XXX
XXX:x:1001:1001::/home/XXX:/bin/sh

上記の通り、ubuntuはbashが指定されているけど、XXXはshになっている。
これをbashに変更すればよさげだ!

下記を実行
sudo usermod -s /bin/bash XXX

確認
ubuntu@ip-10-0-1-10:~$ cat /etc/passwd | grep XXX
XXX:x:1001:1001::/home/XXX:/bin/bash

無事に変更されてる!OK!
一度ログアウトしてXXXでログインしてみると無事自動でbashが起動された!!

次にずっと裏で起動し続けているGunicornを停止してみる

まずは現状のプロセス確認
XXX@ip-10-0-1-10:~$ ps ax | grep gunicorn
182853 ? Ss 5:03 /usr/local/bin/python3.9 /home/XXX/.local/bin/gunicorn --access-logfile - --workers 3 --bind unix:/home/XXX/pyprojects/DjangoMyBlog/config.sock config.wsgi:application
182854 ? S 0:25 /usr/local/bin/python3.9 /home/XXX/.local/bin/gunicorn --access-logfile - --workers 3 --bind unix:/home/XXX/pyprojects/DjangoMyBlog/config.sock config.wsgi:application
182855 ? S 0:25 /usr/local/bin/python3.9 /home/XXX/.local/bin/gunicorn --access-logfile - --workers 3 --bind unix:/home/XXX/pyprojects/DjangoMyBlog/config.sock config.wsgi:application
182856 ? S 0:25 /usr/local/bin/python3.9 /home/XXX/.local/bin/gunicorn --access-logfile - --workers 3 --bind unix:/home/XXX/pyprojects/DjangoMyBlog/config.sock config.wsgi:application
259024 ? S 4:27 /usr/local/bin/python3.9 /home/XXX/.local/bin/gunicorn --bind 0.0.0.0:8000 config.ws gi
313266 ? S 0:19 /usr/local/bin/python3.9 /home/XXX/.local/bin/gunicorn --bind 0.0.0.0:8000 config.ws gi
430659 pts/1 S+ 0:00 grep --color=auto gunicorn

ずっと稼働している。
下記コマンドで停止
sudo pkill gunicorn

再確認
XXX@ip-10-0-1-10:~$ ps ax | grep gunicorn
430668 pts/1 S+ 0:00 grep --color=auto gunicorn

ちゃんと消えてる。
ブラウザでサイトにアクセスしても開けなくなっている。OK。

下記で再起動
gunicorn --bind 0.0.0.0:8000 config.wsgi
無事起動。

Ctrl+Cで一旦停止。

今度は下記コマンドでデーモンモードで実行
gunicorn --bind 0.0.0.0:8000 config.wsgi -D
するとバックグラウンドで起動できた。
サイトもアクセス可能!OKだ!

例によってだいぶ時間がかかってしまったが、Gunicornについてはある程度理解が深まったので、Djangoブログのデプロイ作業に戻る
次はNginxの設定。
コメントする

このブログについて

白くまちゃん

文系が社内のKintone導入をきっかけにITを学び始め、ITエンジニアになるまでの記録。