はじめに
さくらVPSの1Gプランに各ソフトウェアのバージョンをインストールして環境を構築しています。
名前 | バージョン |
---|---|
CentOS | 7.8.2003 (Core) |
python | 3.6.8 |
django | 3.1 |
uwsgi | 2.0.19.1 |
nginx | 1.16.1 |
sqlite | 3.30.0 |
さくらVPSをセットアップ
前提条件として標準OSのCentOS7 x86_64をインストールしてスタートアップガイドのサーバの初期設定ガイドまでが完了しているものとします。
なお、サーバの初期設定ガイド6「パケットフィルターを設定しましょう」でWebを有効にしてください。
Djangoをインストール
Djangoのサイトでインストール方法を確認します。
Djangoを使用するにはPythonが必要なので、まずはインストールされているPythonのバージョンを確認します。
$ python --version
Python 2.7.5
FAQの「どのバージョンの Python で Django が使えますか?」によるとPython 2.7.5で使えるDjangoは1.11となっていますが、1.11は2020年4月1日でサポートが終了となっているのでPython 3を使うことにします。
$ python3 --version
-bash: python3: command not found
Python 3がインストールされていないのでインストールします。
$ sudo yum -y install python3
インストールされたPython 3のバージョンを確認します。
$ python3 --version
Python 3.6.8
Python 3.6.8なので最新のDjango 3.1まで使用することが出来ます。
Pythonの準備が出来たので次にDjangoをインストールします。
$ sudo pip3 install django
インストールしたDjangoのバージョンを確認します。
$ django-admin --version
3.1
とりあえずプロジェクトを作って動作するか確認してみます。
$ django-admin startproject mysite
$ cd mysite
$ python3 manger.py runserver
何やらエラーが発生してしまいました。
Watching for file changes with StatReloader
Exception in thread django-main-thread:
Traceback (most recent call last):
File "/usr/lib64/python3.6/threading.py", line 916, in _bootstrap_inner
self.run()
File "/usr/lib64/python3.6/threading.py", line 864, in run
self._target(*self._args, **self._kwargs)
File "/usr/local/lib64/python3.6/site-packages/django/utils/autoreload.py", line 53, in wrapper
fn(*args, **kwargs)
File "/usr/local/lib64/python3.6/site-packages/django/core/management/commands/runserver.py", line 110, in inner_run
autoreload.raise_last_exception()
File "/usr/local/lib64/python3.6/site-packages/django/utils/autoreload.py", line 76, in raise_last_exception
raise _exception[1]
File "/usr/local/lib64/python3.6/site-packages/django/core/management/__init__.py", line 357, in execute
autoreload.check_errors(django.setup)()
File "/usr/local/lib64/python3.6/site-packages/django/utils/autoreload.py", line 53, in wrapper
fn(*args, **kwargs)
File "/usr/local/lib64/python3.6/site-packages/django/__init__.py", line 24, in setup
apps.populate(settings.INSTALLED_APPS)
File "/usr/local/lib64/python3.6/site-packages/django/apps/registry.py", line 114, in populate
app_config.import_models()
File "/usr/local/lib64/python3.6/site-packages/django/apps/config.py", line 211, in import_models
self.models_module = import_module(models_module_name)
File "/usr/lib64/python3.6/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 994, in _gcd_import
File "<frozen importlib._bootstrap>", line 971, in _find_and_load
File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 678, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "/usr/local/lib64/python3.6/site-packages/django/contrib/auth/models.py", line 2, in <module>
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
File "/usr/local/lib64/python3.6/site-packages/django/contrib/auth/base_user.py", line 48, in <module>
class AbstractBaseUser(models.Model):
File "/usr/local/lib64/python3.6/site-packages/django/db/models/base.py", line 122, in __new__
new_class.add_to_class('_meta', Options(meta, app_label))
File "/usr/local/lib64/python3.6/site-packages/django/db/models/base.py", line 326, in add_to_class
value.contribute_to_class(cls, name)
File "/usr/local/lib64/python3.6/site-packages/django/db/models/options.py", line 206, in contribute_to_class
self.db_table = truncate_name(self.db_table, connection.ops.max_name_length())
File "/usr/local/lib64/python3.6/site-packages/django/db/__init__.py", line 28, in __getattr__
return getattr(connections[DEFAULT_DB_ALIAS], item)
File "/usr/local/lib64/python3.6/site-packages/django/db/utils.py", line 214, in __getitem__
backend = load_backend(db['ENGINE'])
File "/usr/local/lib64/python3.6/site-packages/django/db/utils.py", line 111, in load_backend
return import_module('%s.base' % backend_name)
File "/usr/lib64/python3.6/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "/usr/local/lib64/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 70, in <module>
check_sqlite_version()
File "/usr/local/lib64/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 67, in check_sqlite_version
raise ImproperlyConfigured('SQLite 3.8.3 or later is required (found %s).' % Database.sqlite_version)
django.core.exceptions.ImproperlyConfigured: SQLite 3.8.3 or later is required (found 3.7.17).
エラーメッセージにはバージョン3.8.3以降のSQLiteを要求しているのに見つかったのは3.7.17だと出ています。
実際にSQLiteのバージョンを確認してみます。
$ sqlite3 -version
3.7.17 2013-05-20 00:56:22 118a3b35693b134d56ebd780123b7fd6f1497668
確かにバージョンが3.7.17で古いのでバージョンアップする必要があります。
SQLiteをバージョンアップ
SQLiteのサイトよりソースコードをダウンロードしてインストールします。
$ cd ~/
$ wget https://www.sqlite.org/2020/sqlite-autoconf-3330000.tar.gz
$ tar zxvf sqlite-autoconf-3330000.tar.gz
$ cd sqlite-autoconf-3330000
$ ./configure
$ make
$ sudo make install
インストールしたSQLiteのバージョンを確認してみます。
$ /usr/local/bin/sqlite3 -version
3.33.0 2020-08-14 13:23:32 fca8dc8b578f215a969cd899336378966156154710873e68b3d9ac5881b0ff3f
SQLiteのバージョンアップが出来たのでLD_LIBRARY_PATHでライブラリのパスを指定してもう一度実行してみます。
$ cd ~/mysite
$ LD_LIBRARY_PATH=/usr/local/lib python3 manage.py runserver
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
August 20, 2020 - 18:26:21
Django version 3.1, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
今度はちゃんと実行できたようです。
サーバーにアクセス出来るか確認してみます。
$ curl -i http://127.0.0.1:8000
HTTP/1.1 200 OK
Date: Thu, 20 Aug 2020 18:35:41 GMT
Server: WSGIServer/0.2 CPython/3.6.8
Content-Type: text/html
X-Frame-Options: DENY
Content-Length: 16351
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
<!doctype html>
<html>
<head>
...
LD_LIBRARY_PATHを毎回指定するのは手間なのでldconfigで共有ライブりのパスを登録します。
まず現在の登録状態を確認します。
$ ldconfig -p | grep sqlite
libsqlite3.so.0 (libc6,x86-64) => /lib64/libsqlite3.so.0
/etc/ld.so.conf.d以下に設定ファイルを追加します。
/usr/local/lib
登録内容を更新します。
$ sudo ldconfig
もう一度登録状態を確認します。
$ ldconfig -p | grep sqlite
libsqlite3.so.0 (libc6,x86-64) => /usr/local/lib/libsqlite3.so.0
libsqlite3.so.0 (libc6,x86-64) => /lib64/libsqlite3.so.0
libsqlite3.so (libc6,x86-64) => /usr/local/lib/libsqlite3.so
これでLD_LIBRARY_PATHを指定しなくてもよくなりました。
uwsgiをインストール
Web Server Gateway Interfaceのuwsgiをインストールします。
$ sudo pip3 install uwsgi
何やらエラーが発生しました。
In file included from plugins/python/python_plugin.c:1:0:
plugins/python/uwsgi_python.h:2:20: fatal error: Python.h: No such file or directory
#include <Python.h>
^
compilation terminated.
In file included from plugins/python/pyutils.c:1:0:
plugins/python/uwsgi_python.h:2:20: fatal error: Python.h: No such file or directory
#include <Python.h>
^
compilation terminated.
----------------------------------------
Command "/usr/bin/python3 -u -c "import setuptools, tokenize;__file__='/tmp/pip-build-evpbzqre/uwsgi/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record /tmp/pip-9qkvdn3u-record/install-record.txt --single-version-externally-managed --compile" failed with error code 1 in /tmp/pip-build-evpbzqre/uwsgi/
原因はpython3-develがインストールされていないことなのでインストールします。
$ sudo yum -y install python3-devel
もう一度uwsgiをインストールします。
$ sudo pip3 install uwsgi
問題なくインストール出来たのでuwsgiのバージョンを確認します。
$ uwsgi --version
2.0.19.1
動作確認をするためにHello Worldを返すだけのテストスクリプトを用意します。
def application(env, start_response):
start_response('200 OK', [('Content-Type','text/html')])
return [b"Hello World"]
テストスクリプトを実行します。
$ uwsgi --http :8000 --wsgi-file test.py
実行できているか確認するためローカルホストにポート番号8000でアクセスします。
$ curl -i localhost:8000
HTTP/1.1 200 OK
Content-Type: text/html
Hello World
問題なく動作したので今度はDjangoを動かしてみます。
$ cd ~/mysite
$ uwsgi --http :8000 --module mysite.wsgi
何やらエラーが発生しました。
*** Starting uWSGI 2.0.19.1 (64bit) on [Sun Aug 23 00:36:53 2020] ***
compiled with version: 4.8.5 20150623 (Red Hat 4.8.5-39) on 22 August 2020 13:27:06
os: Linux-3.10.0-1127.18.2.el7.x86_64 #1 SMP Sun Jul 26 15:27:06 UTC 2020
nodename: サーバー名
machine: x86_64
clock source: unix
detected number of CPU cores: 2
current working directory: /home/ユーザー名/mysite
detected binary path: /usr/local/bin/uwsgi
!!! no internal routing support, rebuild with pcre support !!!
*** WARNING: you are running uWSGI without its master process manager ***
your processes number limit is 3881
your memory page size is 4096 bytes
detected max file descriptor number: 1024
lock engine: pthread robust mutexes
thunder lock: disabled (you can enable it with --thunder-lock)
uWSGI http bound on :8000 fd 4
spawned uWSGI http 1 (pid: 11759)
uwsgi socket 0 bound to TCP address 127.0.0.1:40228 (port auto-assigned) fd 3
Python version: 3.6.8 (default, Apr 2 2020, 13:34:55) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]
*** Python threads support is disabled. You can enable it with --enable-threads ***
Python main interpreter initialized at 0x165d4f0
your server socket listen backlog is limited to 100 connections
your mercy for graceful operations on workers is 60 seconds
mapped 72904 bytes (71 KB) for 1 cores
*** Operational MODE: single process ***
Traceback (most recent call last):
File "./mysite/wsgi.py", line 16, in <module>
application = get_wsgi_application()
File "/usr/local/lib64/python3.6/site-packages/django/core/wsgi.py", line 12, in get_wsgi_application
django.setup(set_prefix=False)
File "/usr/local/lib64/python3.6/site-packages/django/__init__.py", line 24, in setup
apps.populate(settings.INSTALLED_APPS)
File "/usr/local/lib64/python3.6/site-packages/django/apps/registry.py", line 114, in populate
app_config.import_models()
File "/usr/local/lib64/python3.6/site-packages/django/apps/config.py", line 211, in import_models
self.models_module = import_module(models_module_name)
File "/usr/lib64/python3.6/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "/usr/local/lib64/python3.6/site-packages/django/contrib/auth/models.py", line 2, in <module>
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
File "/usr/local/lib64/python3.6/site-packages/django/contrib/auth/base_user.py", line 48, in <module>
class AbstractBaseUser(models.Model):
File "/usr/local/lib64/python3.6/site-packages/django/db/models/base.py", line 122, in __new__
new_class.add_to_class('_meta', Options(meta, app_label))
File "/usr/local/lib64/python3.6/site-packages/django/db/models/base.py", line 326, in add_to_class
value.contribute_to_class(cls, name)
File "/usr/local/lib64/python3.6/site-packages/django/db/models/options.py", line 206, in contribute_to_class
self.db_table = truncate_name(self.db_table, connection.ops.max_name_length())
File "/usr/local/lib64/python3.6/site-packages/django/db/__init__.py", line 28, in __getattr__
return getattr(connections[DEFAULT_DB_ALIAS], item)
File "/usr/local/lib64/python3.6/site-packages/django/db/utils.py", line 214, in __getitem__
backend = load_backend(db['ENGINE'])
File "/usr/local/lib64/python3.6/site-packages/django/db/utils.py", line 111, in load_backend
return import_module('%s.base' % backend_name)
File "/usr/lib64/python3.6/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "/usr/local/lib64/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 70, in <module>
check_sqlite_version()
File "/usr/local/lib64/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 67, in check_sqlite_version
raise ImproperlyConfigured('SQLite 3.8.3 or later is required (found %s).' % Database.sqlite_version)
django.core.exceptions.ImproperlyConfigured: SQLite 3.8.3 or later is required (found 3.7.17).
unable to load app 0 (mountpoint='') (callable not found or import error)
*** no app loaded. going in full dynamic mode ***
*** uWSGI is running in multiple interpreter mode ***
spawned uWSGI worker 1 (and the only) (pid: 11758, cores: 1)
エラーメッセージはバージョン3.8.3以降のSQLiteを要求しているのに見つかったのは3.7.17だと出ています。
なぜか先ほどバージョンアップしたはずなのに同じメッセージが出ています。
原因はuwsgiにRPATHが設定されているからです。
$ objdump -p /usr/local/bin/uwsgi | grep RPATH
RPATH /usr/lib64
RPATHが設定されていると指定されたディレクトリからしか共有ライブラリをロードしなくなります。
RPATHに/usr/local/libを追加してuwsgiを作り直します。
$ sudo pip3 uninstall uwsgi
$ sudo LDFLAGS="-L/usr/local/lib -Wl,-rpath,/usr/local/lib" pip3 install uwsgi
RPATHがどうなったか確認しましす。
$ objdump -p /usr/local/bin/uwsgi | grep RPATH
RPATH /usr/local/lib:/usr/lib64
もう一度実行してみます。
$ cd ~/mysite
$ uwsgi --http :8000 --module mysite.wsgi
問題なく実行することが出来ました。
nginxをインストール
Webサーバーのnginxをインストールします。
$ sudo yum -y install nginx
インストールしたnginxのバージョンを確認します。
$ nginx -v
nginx version: nginx/1.16.1
設定ファイルを用意します。
サーバー名にはhttp://サーバー名/のサーバー名をユーザー名には自分のユーザー名に置き換えてください。
upstream django {
server 127.0.0.1:8000;
}
server {
listen 80;
server_name サーバー名;
location / {
uwsgi_pass django;
include /home/ユーザー名/mysite/uwsgi_params;
}
}
uwsgi用のパラメータファイルが用意されているのでプロジェクトにコピーします。
$ cp /etc/nginx/uwsgi_params ~/mysite
一通り準備が出来たのでnginxを再起動させます。
$ sudo systemctl restart nginx
Djangoを実行してみます。
$ cd ~/mysite
$ uwsgi --socket :8000 --module mysite.wsgi
実行出来ているようなのでhttp://サーバー名/にアクセスしてみます。
何やらエラーが発生しています。
原因はALLOWED_HOSTSを設定せずに実行したせいです。
ALLOWED_HOSTSを設定します。
ALLOWED_HOSTS = ['サーバー名']
設定したらもう一度実行してみます。
ロケットが飛んでいるページが表示されました。
ロケットのページが表示されるのはDEBUGがTrueになっているからなのでFalseに変えてみます。
DEBUG = False
設定したらもう一度実行します。
今度は何も表示されなくなりました。
トップページはまだ用意されていないので代わりに管理ページを表示してみます。
http://サーバー名/admin/にアクセスします。
画像などの静的ファイルが用意されていないので文字だけの表示になっています。
静的ファイルを用意します。
まず静的ファイルを格納するディレクトリを用意します。
$ sudo mkdir /usr/share/nginx/html/static
$ sudo chown ユーザー名 /usr/share/nginx/html/static
Djangoに静的ファイルの格納先を設定します。
STATIC_URL = '/static/'
STATIC_ROOT = '/usr/share/nginx/html/static'
静的ファイルを格納先に集めます。
$ python3 manage.py collectstatic
nginx側にも静的ファイルの格納先を設定します。
location /static {
alias /usr/share/nginx/html/static;
}
location / {
サーバーの設定を変更したら適用するためにサーバーを再起動します。
$ sudo systemctl restart nginx
準備が出来たので実行してアクセスしてみます。
表示されました。
最後にunixソケットでアクセスするように変更してみます。
upstream django {
server unix:///home/ユーザー名/mysite/mysite.sock;
# server 127.0.0.1:8000;
}
設定を適用させるためnginxを再起動します。
$ sudo systemctl restart nginx
実行してアクセスしてみます。
502 Bad Gatewayが発生しました。
エラーログを見てみます。
$ sudo cat /var/log/nginx/error.log
2020/08/31 03:41:54 [crit] 12700#0: *2 connect() to unix:///home/ユーザー名/mysite/mysite.sock failed (13: Permission denied) while connecting to upstream, ...
Permission deniedが発生していることがわかります。
原因はnginxがユーザーのディレクトリのアクセス権限がないためです。
nginxがアクセスできるようにユーザーのグループにnginxを追加します。
$ sudo usermod -aG ユーザー名 nginx
$ chmod 750 /home/ユーザー名
$ sudo systemctl restart nginx
もう一度実行してアクセスしてみます。
今度は表示されました。
以上、さくらVPSのCentOS7でDjango+uwsgi+nginx+sqliteを動かすでした。