タイトルの環境をわりと簡単に構成できると思って始めたのですが、次々に発生するエラーを1つずつ地道に解決して、やっとできたと思ったら文字コードが latin ということに後から気付いたりして、思いのほか手間がかかってしまいました。
何とか解決したコードをまとめたので、誰かのお役に立てたら嬉しいです。
環境
本番環境なので venv などの仮想環境は導入せず、Django 専用の EC2 とします。
- OS: Amazon Linux 2(Amazon EC2)
- データベース: Amazon Aurora Serverless(MySQL 5.6.10)
Aurora と Django プロジェクトの文字コードを UTF-8 に、地域と言語を日本/日本語に設定します。
インストール
以下、Ansible と AWS CloudFormation を使用します。
Python3
Ansible で python3、python3-devel、python3-libs、python3-pip をインストールします。python3-devel 以外は依存パッケージとしてインストールされます。
- name: Install Python3 packages
yum:
name:
- python3-devel
state: present
Django
Django と一緒に pip でインストールする「mysqlclient」の依存パッケージ(RPM)を先にインストールします。
まず MySQL の yum リポジトリをインストールします。
- name: Install MySQL 5.7 repository
yum:
name:
- https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
state: present
MySQL 関連パッケージと gcc をインストールします。Aurora を使用するので、MySQL Server はインストールしません。
Aurora Serverless の MySQL が 5.6 なので、近いところで 5.7 を使うことにしました。
また Amazon Linux 2 にデフォルトでインストールされている mariadb-libs は自動的にアンインストールされます。
- name: Install rpm packages required by mysqlclient
yum:
# 'mysql*'にマッチするリポジトリを全部無効化して
# 'mysql57-community'を有効化し、5.7だけをインストール対象にする
disablerepo: "mysql*"
enablerepo: mysql57-community
name:
- gcc
- mysql-community-devel
- mysql-community-client
state: present
以上の RPM パッケージをインストール後に、pip を使用して django と mysqlclient、および nginx と接続するための gunicorn をインストールします。gcc は mysqlclient をインストールするために使用されます。
- name: Install Django packages with pip3
pip:
executable: pip3
name:
- django
- gunicorn
- mysqlclient
state: present
動作確認のために Django のプロジェクトを作って、Django 開発サーバを起動します。
django-admin.py startproject mysite
cd mysite/
python3 manage.py runserver
ところが以下のように SQLite のバージョンが古すぎるというエラーで、開発サーバを起動することができません。
django.core.exceptions.ImproperlyConfigured: SQLite 3.8.3 or later is required (found 3.7.17).
このエラーは SQLite を 3.8.3 以上にするか、Django 2.2.x を 2.1.x にダウングレードすることで回避することができます。
しかし MySQL を使う場合は SQLite を使わないので、このエラーを無視して MySQL の環境を構築します。
Aurora Serverless (MySQL 5.6)
ここでは AWS CloudFormation を使用します。
-> Amazon Aurora MySQLリファレンス
まず Aurora の主なパラメータを定義します。
-> RDS リソースタイプのリファレンス » AWS::RDS::DBCluster
Parameters:
# 指定した名前で自動的に CREATE DATABASE される
MyRdsDatabaseName:
Description: RDS database name.
Type: String
Default: MySchema
MyRdsMasterUsername:
Description: RDS master user name.
Type: String
Default: myadmin
MyRdsMasterUserPassword:
Description: RDS master user password.
Type: String
Default: myp@ssword
# 最悪 Aurora が応答しなくてもよい時間帯(毎日 30分間以上)を UTC で指定する
MyRdsPreferredBackupWindow:
Description: RDS PreferredBackupWindow.
Type: String
Default: '18:10-18:40'
# 最悪 Aurora が応答しなくてもよい時間帯(毎週 30分間以上)を UTC で指定する
MyRdsPreferredMaintenanceWindow:
Description: RDS PreferredMaintenanceWindow.
Type: String
Default: 'Sun:18:40-Sun:19:10'
# 高負荷時にここで指定した Aurora DB クラスターの最大容量までスケーリングする
MyRdsScalingMaxCapacity:
Description: RDS ScalingConfiguration MaxCapacity.
Type: Number
Default: 4
# 指定した秒数の間アクセスがないと Aurora が一時停止して課金されなくなる
MyRdsScalingSecondsUntilAutoPause:
Description: RDS ScalingConfiguration SecondsUntilAutoPause.
Type: Number
Default: 300
Aurora 用のサブネットグループを定義します。冗長化のために 2つのサブネットを指定します。
-> RDS リソースタイプのリファレンス » AWS::RDS::DBSubnetGroup
Resources:
MyRdsDbSubnetGroup:
Type: AWS::RDS::DBSubnetGroup
Properties:
DBSubnetGroupDescription: Database subnets for Aurora Serverless
DBSubnetGroupName: my-subnet-group
SubnetIds:
- !Ref MySubnet1
- !Ref MySubnet2
Tags:
- Key: 'Name'
Value: 'my-rds-subnet-group'
Aurora のパラメータグループです。MySQL Server のように my.cnf ファイルを使って設定することができないので、パラメータグループに必要なパラメータを設定します。
-> RDS リソースタイプのリファレンス » AWS::RDS::DBClusterParameterGroup
MyRdsDbClusterParameterGroup:
Type: AWS::RDS::DBClusterParameterGroup
Properties:
Description: CloudFormation Aurora Cluster Parameter Group
Family: aurora5.6
Parameters:
collation_server: utf8_general_ci
character_set_client: utf8
character_set_database: utf8
character_set_server: utf8
server_audit_logging: 0
time_zone: 'Asia/Tokyo'
Tags:
- Key: 'Name'
Value: 'my-rds-cluster-param-group'
character_set_*
を指定しないと全部 latin1 になってしまうので、必要に応じて utf8 や utf8mb4 に変更しておきます。同時に照合順序もcollation_server
に設定します。
個人的にはこれまでずっと SQL文でCREATE TABLE ... DEFAULT CHARSET=utf8
のようにテーブルごとに詳細設定をコーディングしてきたので、データベースのデフォルトの設定はあまり気にしていませんでした。
しかし今回は Django の機能を使って Python のモデルクラスからテーブルを生成することにしたので、デフォルト値をしっかり設定しておく必要があります。
以上の定義を使って DBCluster 本体を定義します。
MyDbCluster:
Type: AWS::RDS::DBCluster
Properties:
DBClusterIdentifier: my-main-db
DBClusterParameterGroupName:
!Ref MyRdsDbClusterParameterGroup
DBSubnetGroupName:
!Ref MyRdsDbSubnetGroup
DatabaseName:
!Ref MyRdsDatabaseName
Engine: aurora
EngineMode: serverless
EngineVersion: '5.6.10a'
MasterUsername:
!Ref MyRdsMasterUsername
MasterUserPassword:
!Ref MyRdsMasterUserPassword
PreferredBackupWindow:
!Ref MyRdsPreferredBackupWindow
PreferredMaintenanceWindow:
!Ref MyRdsPreferredMaintenanceWindow
ScalingConfiguration:
AutoPause: true
MaxCapacity:
!Ref MyRdsScalingMaxCapacity
MinCapacity: 1
SecondsUntilAutoPause:
!Ref MyRdsScalingSecondsUntilAutoPause
VpcSecurityGroupIds:
- !Ref MyDbSecurityGroup
データベースが生成されたらエンドポイントに接続して、パラメータグループに設定したとおりの文字セットと照合順序になっていることを確認します。
mysql -h my-main-db.cluster-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com -u myadmin -p
Enter password:
mysql> show variables like '%char%';
+--------------------------+----------------------------------------------------------+
| Variable_name | Value |
+--------------------------+----------------------------------------------------------+
| character_set_client | utf8 |
| character_set_connection | utf8 |
| character_set_database | utf8 |
| character_set_filesystem | binary |
| character_set_results | utf8 |
| character_set_server | utf8 |
| character_set_system | utf8 |
| character_sets_dir | /rdsdbbin/oscar-5.6.serverless_10a.184.0/share/charsets/ |
+--------------------------+----------------------------------------------------------+
mysql> show variables like '%collat%';
+----------------------+-----------------+
| Variable_name | Value |
+----------------------+-----------------+
| collation_connection | utf8_general_ci |
| collation_database | utf8_general_ci |
| collation_server | utf8_general_ci |
+----------------------+-----------------+
生成されたデータベースの設定もstatus
コマンドで表示して、設定した値になっていることを確認します。
mysql> use MySchema;
Database changed
mysql> status
--------------
mysql Ver 14.14 Distrib 5.7.27, for Linux (x86_64) using EditLine wrapper
Connection id: 4
Current database: MySchema
Current user: myadmin@1.2.3.4
SSL: Cipher in use is AES256-SHA
Current pager: stdout
Using outfile: ''
Using delimiter: ;
Server version: 5.6.10 MySQL Community Server (GPL)
Protocol version: 10
Connection: my-main-db.cluster-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com via TCP/IP
Server characterset: utf8
Db characterset: utf8
Client characterset: utf8
Conn. characterset: utf8
TCP port: 3306
Djangoプロジェクトの設定
settings.py
Django をインストールした直後に作った動作確認用のプロジェクトは、以下のようなファイル構成になっています。
mysite
├── mysite/
│ ├── __pycache__/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── manage.py
この中の settings.py を変更します。
データベースを SQLite から MySQL に変更して、Aurora に設定したパラメータとエンドポイントを設定します。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
また Aurora ではデフォルトのsql_mode
が空なので、必要に応じて SQLモードを設定します。以下は MySQL 5.7 と同じ設定にする場合の例です。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'MySchema',
'USER': 'myadmin',
'PASSWORD': 'myp@ssword',
'HOST': 'my-main-db.cluster-xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com',
'PORT': '3306',
'OPTIONS': {
'charset': 'utf8',
'sql_mode': 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION',
},
},
}
Web の接続許可設定を追加
ALLOWED_HOSTS = []
ALLOWED_HOSTS = ["*"]
また必要に応じて、言語とタイムゾーンを変更します。
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
LANGUAGE_CODE = 'ja-jp'
TIME_ZONE = 'Asia/Tokyo'
動作確認
Aurora との接続
プロジェクトのディレクトリにある manage.py のdbshell
コマンドを使って Aurora に接続します。
python3 manage.py dbshell
正常にプロンプトが表示されれば、上記で settings.py に設定したデータベース接続情報が正しく認識されています。
mysql: [Warning] Using a password on the command line interface can be insecure.
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
(略)
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
quit
か\q
を入力して終了します。
Django 開発サーバ
Django インストール直後と同様に Django 開発サーバを起動します。
python3 manage.py runserver
データベースを SQLite から MySQL に変更したので、SQLite のバージョンに関するエラーは発生しなくなりました。正常に起動すると以下のように表示されます。
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
September 29, 2019 - 20:03:56
Django version 2.2.5, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
表示されたhttp://127.0.0.1:8000/
に接続できれば、開発サーバが正しく動作しています。
wget --spider http://127.0.0.1:8000/
Spider mode enabled. Check if remote file exists.
--2019-09-29 11:05:14-- http://127.0.0.1:8000/
Connecting to 127.0.0.1:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 16504 (16K) [text/html]
Remote file exists and could contain further links,
but recursion is disabled -- not retrieving.
次の課題
- Nginx -> Gunicorn -> Django の接続環境を Ansible で構成すること
記事一覧
この記事には続きがあります。
- 1/4: DjangoをAmazon Linux 2にAnsibleで構成する、Aurora (MySQL) をCloudFormationでUTF-8に構成する、そして接続するまでが地味に大変だったこと
- 2/4: Nginx + Gunicorn + Django + Aurora (MySQL) の構成を図で説明してみる
- 3/4: Nginx + Gunicorn + Django + Aurora (MySQL) の本番環境をAnsible Playbookで構成する
- 4/4: NginxとGunicornの接続をソケットからHTTPに変更した