Tech Sketch Bucket of Technical Chips by TIS Inc.

ansible・packer・CloudFormationを用いてDjango環境を構築してみよう

Pocket

ライトウェイトでフルスタックなWebフレームワークといえばRuby on Railsが有名です。しかし統計処理や機械学習に関連するライブラリにはPython製が多いこともあり、これからはDjangoのようなPython上で動作するフルスタックWebフレームワークを利用する場面も増えていくことでしょう。

しかしDjangoには独自のルールがあり、AWS ELBAWS RDSと共に利用するためには、少し工夫が必要です。今回はansiblepacker、及びAWS CloudFormationを用いて、AWS上に下図のような耐障害性の高いDjango環境を構築してみます。

aws-django

Elvez ※私事ながら、筆者の松井はこの度 株式会社 Elvez のCTOに就任いたしました。
株式会社Elvezは、社会性を持つAIで世界を幸せにする会社です。今回紹介したAWSとDjangoも、Elvezのサービスを形作るパーツの一つになる予定です。

環境の準備

実際にansibleやpackerを動作させる前に、まずは環境の準備から始めます。

AWS SDK for Python(Boto)のインストール

ansibleからAWSのAPIにアクセスするためには、botoというPythonライブラリが必要です。ココの手順を参考に、pip install botoでライブラリをインストールしてください。

ACCESS KEYとSECRET ACCESS KEYの設定

今回はCloudFormationを用いてAWSに環境を構築します。そのため、CloudFormation、EC2、VPC、RDSを操作できる権限を持ったAWSアカウントが必要です。

アカウントが準備できたら、AWSの公式ドキュメントを参考にしてACCESS KEYSECRET ACCESS KEYを取得してください。またBotoやpackerがAWS APIを利用できるように、下記フォーマットの~/.aws/credentialsを作成してください。

キーペアの作成

AWS EC2のインスタンスにSSHでログインするためには、キーペアが必要です。AWS ELBによって自動起動されるEC2インスタンスにログインできるように、ココの手順に従ってキーペアを作成し、その名前をメモしておいてください。

AnsibleとPackerのインストール

ココココで解説されている手順を参考に、AnsibleとPackerの最新版をインストールしてください。

なお本稿は、次の環境で動作を確認しました。

バージョン
DistributionUbuntu 14.04.4 LTS
kernel3.13.0-76-generic
ansible2.0.1.0
packer0.9.0
Django1.8.9

ansible・packer・CloudFormationを用いたDjango環境の自動構築

環境が準備できれば、実際にAWS上へDjango環境を作ってみましょう。必要なスクリプト類は全てGithubのリポジトリに公開していますので、すぐに試してみることができます。

リポジトリを取得

Githubのリポジトリをcloneします。

Packerを用いてAMI作成

packerコマンドを用いて、AMIを作成します。今回のリポジトリには、Djangoの公式チュートリアルに従って作成した投票アプリが同梱されています。

無事にAMIが作成できれば、作成されたAMIのID(ami-xxxxxxxx)が表示されます。メモしておきましょう。

変数定義の変更

作成したAMIとキーペアを利用するように、group_vars/all.ymlを修正します。またRDSに設定するパスワードも、ここで設定してください。
※ ansibleの変数定義はymlファイルとして記述します。行頭の空白にも意味がありますので、ご注意ください

CloudFormationを用いてAWS環境の構築

ansible-playbookコマンドを用いてCloudFormationを呼び出し、AWS環境を構築します。
※ multi-azのAWS RDSを構築するため、15〜20分程度の時間がかかります。

無事にAWS環境が構築されれば、ELBに与えられたPublic DNS名が表示されます。

ブラウザでアクセス

ELBのPublic DNSにアクセスすれば、Djangoアプリが動作していることが確認できます。
(今回の投票アプリは/pollsというURLにマップされていますので、 http://ELBのPublic DNS名/polls にアクセスしてください。)

django_app

ansible・packer・CloudFormationでDjango環境を構築するポイント

Djangoアプリは無事に動作したでしょうか。ansible・packer・CloudFormationをうまく組み合わせれば、いつでも手間なくAWS上にDjango環境を構築することができるようになります。

それではここからは、ansible・packer・CloudFormationでAWS上にDjango環境を構築するポイントを解説します。

リポジトリに含まれているもの

今回のリポジトリには、以下のスクリプト類が含まれています。
各ツールが標準で提供している機能のみ利用していますので、ansibleとpacker、CloudFormationに関する一般的な知識があれば、問題なく内容を理解できると思います。

packerを用いる際のポイント

packer 0.9には、ansibleを利用してAMIを作成するための機能が最初から含まれています。ポイントとなるのは、以下3つです。

1. ACCESS KEYとSECRET ACCESS KEYの扱い

buildersセクションでは、AWSでAMIを作成するための設定が書かれています。ACCESS KEYやSECRET ACCESS KEYの情報をaccess_keysecret_keyとしてbuildersに直接書くこともできますが、セキュリティの観点からも直書きは推奨されません。ACCESS KEYやSECRET ACCESS KEYといった認証情報は、上記の手順で示したように外出ししましょう。

2. AMIへansible-localを同梱

通常のansibleは、AnsibleサーバからSSHでターゲットにログインして設定を施しますが、今回はAMIにansible自体をインストールし、自分で自分を設定するansible-localを利用しました。
ただしansible自体をインストールする手順をansibleで書くことはできませんので、provisionersセクションの冒頭でansibleをインストールするshellスクリプトを実行させるようにします。

3. ansible関連ファイルへのパス設定

通常のansibleは、デフォルトのディレクトリ構成や命名規則に従っている限り、関連するファイルのPATHを明示的に指定する必要はありません。しかしpackerでansible-localを用いる場合、group_varsやrole_pathsを明示的に指定する必要があります。これは必要なファイルを最初に全てEC2のインスタンスに転送しなければならないためです。

ansibleでDjango環境を構築する際のポイント

ansibleを用いてDjango環境をインストールする際には、以下のポイントに気をつけましょう。

1. python仮想環境の構築

Linux上にDjango環境を構築する場合、OSに同梱されているpythonのバージョンに影響されないように、pyenvpyenv-virtualenv等のツールを用いてpython仮想環境を作ることが多いと思います。この場合、仮想環境のpythonを利用するように、追加のPATHを.bashrcなどに設定する必要があります。

しかしansibleのshellモジュールは、そのままでは.bashrcなどを読み込んでくれません。そのため、pyenv等でインストールしたpythonコマンドを正しく動作させるためには、追加のPATHをenvironmentで与えてあげる必要があります。

bash -lc "..."のようにbashをログインシェルとして実行すれば.bashrcも読み込まれるハズなのですが、今回のansible 2.0.1.0ではこの手法ではうまく動作しませんでした・・・

AWS ELBとAWS RDSを利用するDjangoの設定

AWS ELBとAWS RDSを利用する場合、Djangoの設定ファイル(settings.py)にいくつかの設定を行う必要があります。ポイントは以下2つです。

1. DATABASEの接続設定と接続を許可するホスト名にプレースホルダを設定

DjangoアプリがAWS RDSを利用するためには、RDS上のデータベースへ接続するためのパラメータを設定する必要があります。RDSの接続URLはRDSの起動毎に異なったURLになりますし、データベースのパスワードをスクリプトに直書きするのは推奨できません。

またDjango1.5からは、DEBUGをFalseにしている場合ALLOWED_HOSTSに明記されているホスト以外からのアクセスはできなくなりました。これはキャッシュポイズニング等を防ぐために追加された設定であり、本番環境ではDjangoアプリを利用できるFQDNをALLOWED_HOSTSに明記すべき、と言われています。専用のドメイン名を取得してRoute53でELBに対応づけるならば、ALLOWED_HOSTSにそのドメイン名を記載しておけば良いのですが、今回のようにELBが生成したPublic DNSを用いる場合は事前に記載しておくことができません。

そのためAMI作成時に同梱するDjangoアプリの設定ファイルでは、これらの値にプレースホルダを設定しておき、インスタンス起動時に動的に設定を書き換えると良いでしょう。

2. ELBヘルスチェックへの対応

AWS ELBは、その配下に存在するEC2インスタンスが正しく動作しているかを一定期間ごとに確認しています。このヘルスチェックは「自分自身のローカルIPアドレス」から送られますが、前述のようにDjangoアプリは「ALLOWED_HOSTSに明記されているホスト名」からしか接続を許可しません。

そこでAWSのメタデータサーバ(http://169.254.169.254)を活用し、自分自身のローカルIPアドレスを取得してALLOWED_HOSTSに追加する必要があります。Djangoの設定ファイル(settings.py)は普通のpythonスクリプトですから、このような動的な処理をDjangoアプリ起動時に行わせることもできます。

CloudFormationテンプレートのuser_data

最後に、CloudFormationテンプレートのポイントです。user_dataの活用が肝となります。

1. LaunchConfigurationでのuser_data

LaunchConfigurationのUserDataにスクリプトを設定しておけば、ELBがEC2インスタンスを起動した際にroot権限で自動実行されます。この機能を利用して、Djangoアプリの設定を動的に書き換えることができます。

またpython ./manage.py migrateというコマンドを実行すると、接続先として設定されたデータベースにDjangoアプリが必要するテーブルが作成されます。このmaigrate機能は何度実行してもデータベースを破壊しないため、これもUserDataスクリプトで実行すれば、Djangoアプリに合わせてデータベースも自動的に最新化することができます。

最後に

ansibleとpacker、CloudFormationを活用することで、AWS上に耐障害性の高いDjango環境を容易に構築することができました。

今回は説明を簡単にするために、AWSの環境全部を一つのCloudFormationテンプレートに押し込めていますが、実際に利用する場合には各サービスのライフサイクルに合わせてテンプレートを分割するなど、運用に即した工夫も必要となってくるでしょう。

もし皆さんがより良いスクリプトを作成した場合は、こっそり私にも教えて下さい!

エンジニア採用中!私たちと一緒に働いてみませんか?