AWS Amazon Linux2 で Django 2.2以降の環境を構築する


概要

 この記事は初心者の自分がRESTful なAPIとswiftでiPhone向けのクーポン配信サービスを開発した手順を順番に記事にしています。技術要素を1つずつ調べながら実装したため、とても遠回りな実装となっています。

前回の Djangoで画像を配信できるwebAPIを作る で実装したAPIを外部のサーバへデプロイし、パグリックな環境からAPIを利用できる状態にします。

作業は

  1. クラウドのインスタンスを立てて必要なソフトやパッケージをインストール
  2. RestAPIのアプリをデプロイ

になります。この記事では「1. クラウドのインスタンスを立てて必要なソフトやパッケージをインストール」を纏めます。

構成

 今回はAmazonのEC2を使うことにしました。OSはAmazon Linux 2 を選びました。構築に失敗したり環境が合わなかったらインスタンスごと作り直せば良いと考えたので、docker等のコンテナは使わず、OSに直接インストールをする事にしました。

djangoで開発したwebAPIは nginx、uwsgi、djangoの構成で動かします。

構成図
 こんな感じになるイメージです

参考

Amazon Linux 2でSQLite3を最新バージョンにする
Django2.2で開発サーバー起動時にSQLite3のエラーが出た場合の対応
Django + uWSGI + nginx (uWSGIチュートリアルの和訳)
【django】SQLiteバージョンエラー
Linuxやってみる! (6) ldconfigでライブラリパスを更新
Amazon Linux 2 ExtrasレポジトリのNginx、トピック名が「nginx1」になりました

環境

Amazon Linux 2
Python 3.7.4
Django 3.0.3
SQLite 3.31.1

手順

  • AWSのアカウントを作成して(無料枠で)インスタンスを作成
  • 必要なソフトをインストール
    • python 3
    • python のパッケージ
    • django
    • Django-filter
    • djangorestframework
    • djangorestframework-jwt
    • wheel
    • uwsgi
    • Development Tools
    • python3-devel
    • git
    • nginx
  • SQLite3 のアップデート

AWSのアカウントを作成して(無料枠で)インスタンスを作成

 クラウドサービスでよく使われている AWS、Azure、GCP の中から、今回は自分にとって操作経験があり且つその経験がまだ中途半端なAWSを選びました。無料枠を使ってAPIを公開させようと思います。

 AWSのWebページにアクセスしてアカウントを登録するか、Googleで 「aws 無料枠」と入力すると “AWS クラウド無料利用枠 | AWS - Amazon Web Services” というリンクが出てくると思います(2020/1/5 時点)ので、そのページの「まずは無料で始める」からアカウントを登録をします。

 アカウントの登録が完了したらインスタンスを作成します。自分はOSをAmazon Linux、インスタンスタイプは無料枠のt2.micro を選びました。インスタンスの作り方はAWSのチュートリアルがわかりやすいので、そちらをご覧ください。途中で作成するキーペアのキーはアクセス時に使うことになるので、保存場所に気をつけてください。

 インスタンスが一つ立ち上がりました。用途が分かり易いように api_server という名前を付けました。

 インスタンスが立ち上がったのでSSHでの接続を試します。今後インスタンスに設定を加えたり必要なソフトをインストールするのにSSHでの操作が必要になるからです。(デフォルトでSSH接続ができるセキュリティ設定になっています。)

 Macの場合はターミナルを使ってSSH接続ができます。Windowsの場合はエディタのコンソールから操作するか、TeraTarmのようなツールを使います。

 ここからはキーペアによる認証で接続する場合の方法を記載します。Macからキーペアでアクセスする場合、事前にキー( XXX.pem )のアクセス権限を変更しておかないとエラーになりました。所有者のみ参照できるように権限を変更します。下記のコマンドで変更します。(AWSのチュートリアルにも出てきます)

chmod 400 [キー( XXX.pem )を格納したフォルダのパス]/[キーのファイル名].pem

SSH接続します。Macの場合コマンドはこちらです。ec2-user の部分はログインユーザの指定で、デフォルトは ec2-userです。

ssh -i [インスタンスを作成する際に作ったキーペアのキー( XXX.pem )の絶対パス] ec2-user@[インスタンスのグローバルIP]

自分の場合はこんなコマンドになりました。

ssh -i ~/xxxxxx/yyyyy/MyKeyPair.pem [email protected]

以下のように接続を続けるか確認される事があるので、yes を入力してEnterします。

The authenticity of host '13.230.91.101 (13.230.91.101)' can't be established.
ECDSA key fingerprint is SHA256:ldQ3z2WraZy3xCGfL1taOnixN9baFpgnN3qYXfZLah0.
Are you sure you want to continue connecting (yes/no)? yes

こちらの表示が出たら接続が成功です。

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
8 package(s) needed for security, out of 17 available
Run "sudo yum update" to apply all updates.

必要なソフトをインストール

 ここからはインスタンスにSSH接続した状態でコマンドを使ってインストール作業を進めます。また開発したローカル環境とできる限り同じバージョンをインストールします。

 最初にyum(パッケージの統合管理システム)をアップデートしておきます。

$ sudo yum update

 途中で Is this ok [y/d/N]: と聞かれたらyを入力してEnter。なおyumの後ろに-yを付けると自動的にyesと答えてくれるので聞かれなくなります。この後、yumでインストールする際に頻繁に出てきます。

Python3 のインストール

 Amazon Linux 2 はデフォルトで Python がインストールされていますが、2系なので 3系のPythonを別途インストールします。なお、インストールされているpythonのバージョンを確認するコマンドは$ Python -Vです。

 インストールする際のパッケージ名(バージョンの指定)がMacOSやCentOSの場合と異なったので、最初に少し困りました。今回のAmazon Linux 2 のケースでは Python3 になります。

Python 3 のインストール。

$ sudo yum install python3

Development Tools と python3のdevパッケージのインストール

 uwsgiをインストールする際に必要なのでインストールしておきます。なお、python3のdevパッケージのパッケージ名は、CentOSやUbuntuでは python 3.x-devのようになりますが、Amazon Linux 2ではpython3-develとなります。ネットの情報を頼りにインストールのコマンドを実行したらパッケージを引けず、小ハマりしました。パッケージ名を確認してからインストールを実行する習慣が大切です。

Development Tools のインストール

$ sudo yum groupinstall "Development Tools"

python3のdevパッケージのインストール

$ sudo yum install python3-devel

Python3 のパッケージのインストール

 djangoなど必要なパッケージをpythonの環境にインストールします。Python3のインストールが成功していれば pip3 コマンドが使えます。現在インストールされているパッケージを確認します。まだ何もインストールしていないので何も表示されないはずです。

$ pip3 freeze

djangoのインストール

$ sudo pip3 install django

$ pip3 freezeを実行するとdjangoも含めいくつかのパッケージがインストールされています。

$ pip3 freeze
asgiref==3.2.3
Django==3.0.3
pytz==2019.3
sqlparse==0.3.0

この調子で他のパッケージもインストールしていきます。

Django Filter のインストール

$ sudo pip3 install django-filter

Django Restframework のインストール

$ sudo pip3 install djangorestframework

Django Restframework JWT のインストール

 Json Web Token(JWT)を使ったAPIの認証機能を実装している場合に必要です。

$ sudo pip3 install djangorestframework-jwt

wheel のインストール

 uwisgi のインストールに必要です。

$ sudo pip3 install wheel

uwisgi のインストール

$ sudo pip3 install uwsgi

git のインストール

 ソースコードからデプロイする際に必要です。

$ sudo yum install git

nginx のインストール

 Amazon Linux 2 の場合 nginx は yum では無くamazon-linux-extrasでインストールします。なお、2020年2月現在でトピック名の指定は「nginx1」だそうです。(以前は「nginx1.12」)

$ sudo amazon-linux-extras install nginx1

 インストールが完了したら起動してみます。

# nginxの起動
$ systemctl start nginx

# active (running) と表示されていれば起動しています
● nginx.service - The nginx HTTP and reverse proxy server
   Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled)
   Active: active (running) 

 nginxの操作を纏めました。(CentOS7と同じです)

# 起動
$ systemctl start nginx

# 再起動
$ systemctl restart nginx

# 停止
$ systemctl stop nginx

SQLite3 のアップデート

Djangoを使うのにSQLite3が必要になります。
python3をインストールした際にSQLite3もインストールされるようですが、デフォルトでインストールされるSQLite3のバージョンが古く、Django2.2以降ではサーバ起動時などにバージョンチェックでエラーとなってしまいます。そこでSQLite3のアップデートが必要です。

バージョンは$ sqlite3 ―versionで確認できます。デフォルトは 3.7.17 でした。

# SQLite 3.8.3 より新しいバージョンを要求される

  File "/home/ec2-user/.local/share/virtualenvs/ami-folvMrTj/lib/python3.7/site-packages/django/db/backends/sqlite3/base.py", line 65, 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).

2020年1月時点でyum でのアップデートに対応していないようで、ファイルをダウンロードするところから手動でアップデートが必要です。

SQLite3 のファイルをダウンロードして解答する

SQLiteの公式サイト(https://www.sqlite.org/download.html)で最新版を確認して、そのバージョンのgzファイルのパスを取得します。2020年1月時点の最新版は https://www.sqlite.org/2020/sqlite-autoconf-3310100.tar.gz でした。

# ファイルをダウンロード

$ wget https://www.sqlite.org/2020/sqlite-autoconf-3310100.tar.gz


# ファイルを解凍

$ tar xvfz sqlite-autoconf-3310100.tar.gz 



# ダウンロードしたファイルと、解凍したファイルが入ったフォルダができています。

$ ls
sqlite-autoconf-3310100  sqlite-autoconf-3310100.tar.gz

ビルドをしてインストールする

# 解凍したフォルダへ移動します。
$ cd sqlite-autoconf-3310100/

# ビルドしてインストール
# ./configure → make → make install の流れは定番らしいです 
$ ./configure --prefix=/usr/local
$ sudo make
$ sudo make install

新しくインストールしたSQLite3のパスを通す

 最新版のインストールは出来ましたがパスが通っていないので、実行時に古いバージョンのSQLite3の方が参照/実行されてしまいます。不要なファイルを削除したり新しいSQLite3にパスを通す作業をします。

# SQLite3のインストールに使ったファイルを削除
$ rm sqlite-autoconf-3310100.tar.gz 
$ rm -rf sqlite-autoconf-3310100

 次にSQLiteがインストールされたか確認します。

# sqlite3で検索
$ sudo find / -name sqlite3
/usr/bin/sqlite3
/usr/lib64/python2.7/sqlite3
/usr/lib64/python3.7/sqlite3
/usr/local/bin/sqlite3
/usr/local/lib64/python3.7/site-packages/django/db/backends/sqlite3

# /usr/lib64/python2.7/sqlite3、/usr/lib64/python3.7/sqlite3、~django/db/backends/sqlite3 はディレクトリ
# /usr/bin/sqlite3 と /usr/local/bin/sqlite3 のバージョンを確認
$ /usr/bin/sqlite3 --version
3.7.17 2013-05-20
$ /usr/local/bin/sqlite3 --version
3.31.1 

 /usr/local/bin/sqlite3の方が新しくインストールしたSQLite3 なので、古い/usr/bin/sqlite3が参照されないようにします。

# 古い方のsqlite3のファイル名を変更
$ sudo mv /usr/bin/sqlite3 /usr/bin/sqlite3_old

# ディレクトリに新しいsqlite3へのシンボリックリンクを作成
$ sudo ln -s /usr/local/bin/sqlite3 /usr/bin/sqlite3

# sqlite3のバージョンを参照。新しい方が参照される事を確認
$ sqlite3 --version
3.31.1

 次に、共通ライブラリ(ld.so.conf)へパスを通します。

# ld.so.conf があるディレクトリへ移動
$ cd /etc

# ls か ll でld.so.confファイルの存在を確認
$ ls

# ld.so.confをVimで開いてパスを追加
# 追加するパスは /usr/local/lib
$ sudo vi ld.so.conf

# 編集を反映
$ sudo ldconfig

# パスが通ったか確認する
# /usr/local/lib/~のパスが表示されれば通っている
$ 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

 さて、参考にさせて頂いた方の情報によるとこれでSQLite3のアップデートは完了のはずですが、何故かdjangoのサーバ起動時のバージョンチェックのエラーが消えず、3日ほどハマりました。

 アップデートをして共通ライブラリにパスも通したはずなのに、バージョン3.7.17 が参照されるのです。

 いろいろと試した結果(インスタンスごと作り直しました)、元から存在したライブラリ(/lib64/libsqlite3.so.0)が優先的に読み込まれているのが原因らしいと分かりました。

# SQLite 3.8.3 より新しいバージョンを要求される

  File "/home/ec2-user/.local/share/virtualenvs/ami-folvMrTj/lib/python3.7/site-packages/django/db/backends/sqlite3/base.py", line 65, 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).

 そこで、/lib64/libsqlite3.so.0を削除すると、問題無く起動しました。
(他の箇所に影響を及ぼす可能性があるため自己責任でお願いいたします)

# 該当するファイルを探す
$ cd /lib64
$ ls

# リンクとディレクトリを削除
$ sudo rm libsqlite3.so.0
$ sudo rm -rf libsqlite3.so.0.8.6

# 削除を反映
$ sudo ldconfig

# 古いライブラリが参照されないか確認
$ ldconfig -p | grep sqlite
    libsqlite3.so.0 (libc6,x86-64) => /usr/local/lib/libsqlite3.so.0
    libsqlite3.so (libc6,x86-64) => /usr/local/lib/libsqlite3.so

ここまでで環境の準備は出来ました。

次はnginxのインストールと、APIのデプロイをします。