この記事は Django Advent Calendar 2017 - Qiita の17日目の記事です。
ここ数年サーバーレスアーキテクチャが盛り上がっているものの、Djangoと組み合わせた事例がググってもあまりないので書いてみました。ググっても出てこないということは需要が無いということかもしれませんが。。。みなさんFlaskといった軽量なwebフレームワークとDynamoって組み合わせで使ってるんですかね。
私はLambda+Djangoという組み合わせで本番投入した経験はありません。本記事は、運用に使えそうか?という観点で検証しました。
本記事のサンプルコードは以下に置いてあります。各種バージョンはgithub内のrequirements.txtを参照してください。
GitHub - koty/dj-lambda-sample: A sample of Django app on AWS Lambda using severless framework.
Djangoプロジェクトの作成
適当な名前でDjangoプロジェクトを作ります。プロジェクト構成は前述のgithubリポジトリをご確認ください。標準的なものだと思います。
Serverless Frameworkの導入
Lambdaを運用するにはこれがないとやってられません。各種リソースをzipにまとめてLambdaにdeployしてくれます。またAPI Gatewayなどの周辺リソースの設定もできます。pythonアプリを作るのにnpmモジュールを導入するのはどうなんだとも思いますけども。
serverless コマンドを使うために -g つきでinstallします。
npm install -g serverless
VPCやIAM role等の設定が必要ですが、serverless framework自体は情報が多くありますので、ここではこれ以上触れません。
serverless-wsgi の導入
Djangoを使うにはwsgiという規格に沿う必要があります。普段EC2等で動かす場合はuwsgiやgunicornを使えばwsgiに沿うことができますが、Lambdaの場合は以下を使います。
npm install serverless-wsgi --save-dev
serverless.ymlに以下のように追記します。
functions: api: events: # この記述により、ルーティングをwsgi側(Django)に移譲できる。 {proxy+} の意味は不明。。documentにそう書いてある。 - http: ANY / - http: ANY {proxy+} plugins: - serverless-wsgi custom: wsgi: # appにはwsgi.py へのパスを記述。 serverless-python-requirementsにパッケージングを任せるため packRequirements: false を記述 app: dj-lambda-sample.wsgi.application packRequirements: false
serverless-python-requirements の導入
EC2互換のdockerコンテナ上でpip install〜deployパッケージを作ってくれます。lxml等のpip install時にコンパイルをするパッケージを使っていてもEC2で問題なく動くようパッケージできます。
serverless.ymlには以下のように設定します。
plugins: - serverless-python-requirements custom: pythonRequirements: dockerizePip: true
Djangoの設定
DB の設定
使うRDBMSはPostgreSQLです。MySQLはよほどの理由がない限りは使わない方が良さそうです。
事前にRDSを立てておきます(EC2上に立てても当然OK)。検証が済んだら落としておくのを忘れずに。。
serverless.env.ymlを以下のように作ります。
SECURITY_GROUP_ID: 'sg-xxxxxxx' SUBNET_ID1: 'subnet-xxxxxxx' SUBNET_ID2: 'subnet-xxxxxxx' DATABASE_NAME: 'xxxxx' DATABASE_USER: 'xxxxx' DATABASE_PASSWORD: 'xxxxxx' DATABASE_HOST: 'xxxxxx.xxxxxxxx.ap-northeast-1.rds.amazonaws.com' DATABASE_PORT: 5432
環境変数を作るために、serverless.ymlに以下のように設定します。外部ファイルから読み込むわけです。
provider: environment: DATABASE_NAME: ${file(./serverless.env.yml):DATABASE_NAME} DATABASE_USER: ${file(./serverless.env.yml):DATABASE_USER} DATABASE_PASSWORD: ${file(./serverless.env.yml):DATABASE_PASSWORD} DATABASE_HOST: ${file(./serverless.env.yml):DATABASE_HOST} DATABASE_PORT: ${file(./serverless.env.yml):DATABASE_PORT}
Djangoのsettings.pyに以下のように記述します。
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': os.getenv('DATABASE_NAME'), 'USER': os.getenv('DATABASE_USER'), 'PASSWORD': os.getenv('DATABASE_PASSWORD'), 'HOST': os.getenv('DATABASE_HOST'), 'PORT': os.getenv('DATABASE_PORT'), }, }
最後にEC2等から ./manage.py migrate
します。EC2を用意するのが面倒だったので、
class MigrateView(APIView): def get(self, request): from django.core import management management.call_command('migrate') return Response({})
というviewでmigrateしました。。。現実的にはVPC内に立てたEC2からmigrateするってもんでしょう。
そのた
このへんで力尽きてきました。Lambdaでは静的ファイルはホストできないので、S3を使います。django-storageを使って、S3に向かってcollectstaticします。
まとめ
簡単なAPIであればこの構成で運用できそうです。Lambdaのスケーラビリティは魅力です。バッチ処理が入ってくるとEC2がほしくなりそうですが。。。繰り返しますが、使うRDBMSはMySQLはやめた方が良いです。職場で私の斜め前の人が、どハマりしてました。
残課題
- デプロイ時に接続が切れるか?
- 容量制限はどうする?site-packagesの容量が増えるとどうなる?