はじめに
この記事はFlaskで作成したアプリをAWS Elastic Beanstalk(以下EB)にデプロイするまでの手順を自分の備忘録としてまとめたものです。
AWS EBのドキュメントにはFlaskアプリをEBにデプロイする方法が記載されており、この通りにやれば簡単にできるのですが、デフォルトVPCが自動で選択されたり、ELBの種類がClassicから変更できなかったり、EBの環境内にRDS作成すると後で面倒だったりするので、いろいろ設定をいじって環境を作成しました。
今回作成したAWSアーキテクチャ
EB CLI
EBをコマンドラインから操作するため、ドキュメントにしたがってEB CLIをインストールしておきます。
アプリケーションの作成
まずはプロジェクトディレクトリを作成します。
> mkdir eb-flask
> cd eb-flask
次にeb-flask内でeb init
コマンドでアプリケーションを作成します。
eb-flask> eb init -p python-3.8 flask-app --region ap-northeast-1
プラットフォームはPython 3.8
を選択。アプリケーション名はflask-app
とします。pythonはバージョンをしていないとpython2系がインストールされるので注意。Application flask-app has been created.
と表示されれば成功です。
EBのコンソールをチェックするとアプリケーションが作成されていることが確認できます。
ネットワークの設定
VPC
次に環境を作成していくわけですが、自分で作成したカスタムVPCに環境を紐づけたいので、まずはVPCを作成します(環境作成時にVPCを設定しなければ自動でデフォルトVPCが紐づけられます)。VPCのコンソールから設定を行います。
VPCの名前はflask-app-vpc
で作成。IPv4 CIDRブロックは10.0.0.0/16
。その他は変更なし。
作成されました。
サブネット
次に作成したVPCにパブリックサブネット2つとプライベートサブネット2つを作ります。パブリックサブネットはEBの環境作成時にApplication Load Balancerを設定できるようにするため、プライベートサブネットは後々RDSを置くためです。
Application Load Balancer を作成するには、2 つ以上のアベイラビリティーゾーンにサブネットを含む VPC にお客様の環境があることが必要です。すべての新しい AWS アカウントには、この要件を満たすデフォルトの VPC が含まれます。環境が 1 つのアベイラビリティーゾーンのみにサブネットを持つ VPC にある場合、デフォルトで Classic Load Balancer になります。サブネットがない場合、負荷分散を有効にすることはできません。
正直、ApplicationとClassicの違いも分かっていないし、webサーバー1つにして負荷分散する必要もないんじゃないかと思いますが、ここでは勉強用のために設定しておきます。
こんな感じで2つのアベイラビリティゾーンにそれぞれ作成しておきます。
インターネットゲートウェイ
インターネットゲートウェイを作成してVPCにアタッチしておきます。
ルートテーブル
パブリックサブネットに関連づけるルートテーブルを作成します。
作成したら10.0.0.0/16
以外の通信をインターネットに流すように以下のようにルートを設定しておきます。
さらにパブリックサブネットの関連付けを行っておきます。
環境の作成
VPCの設定が完了したので、EBの環境を作成していきます。EBコンソールでアプリケーションflask-app
を選択して新しい環境の作成を行います。
環境枠はウェブサーバー環境を選択。
環境名は自動で作成されます(変更も可)。今回はFlaskapp-env
。
プラットフォームはPythonを選択します。
アプリケーションコードはサンプルアプリケーションを選択。
次により多くのオプションの設定を行います。
まずはネットワークの設定。VPCはflask-app-vpc
を選択。
インスタンスの設定は以下の通り。
次に容量の編集。環境タイプは負荷分散を選択。インスタンスは最小1、最大2とします。
その他は変更なし。
この時点で自動でロードバランサーが作成されます。
ここまで設定したら環境を作成します。
ダッシュボードがこんな感じになったら環境の作成成功です。
左上のURLをクリックするとこんな画面が表示されます。
一番最初に作成したプロジェクトディレクトリでeb list
を実行するとflask-app
内の環境一覧が表示されます。いまはFlaskapp-env
のみです。
eb-flask> eb list
Flaskapp-env
作成された環境を確認する
ここまででサンプルアプリケーションを起動できたので、EBで作成された環境を確認してみます。
まずはEC2インスタンス。Flaskapp-env
というEC2インスタンスが起動しています。ちゃんと自分で作成したVPC内のパブリックサブネットにあります。
セキュリティグループも自動で作成されます。セキュリティグループの中身を確認するとHTTP通信はロードバランサーに設定されているセキュリティグループからの通信のみ受け入れるようです。
後でSSH通信できるようにルールを追加しておきます。
ロードバランサーも自動で作成されます。名前がわかりづらい・・・
EC2サーバにSSH通信する
eb ssh
コマンドで環境内のEC2サーバにSSH通信してみます。まずはキーペアを作成します。
eb-flask> eb ssh Flaskapp-env --setup
作成されたキーペアを使ってログインします(キーペアのファイルがどこにあるかわからない場合は、ユーザフォルダ直下の.ssh
にあると思います)。
eb-flask> eb ssh Flaskapp-env
_____ _ _ _ ____ _ _ _
| ____| | __ ___| |_(_) ___| __ ) ___ __ _ _ __ ___| |_ __ _| | | __
| _| | |/ _ \/ __| __| |/ __| _ \ / _ \/ _\ | '_ \/ __| __/ _\ | | |/ /
| |___| | (_| \__ \ |_| | (__| |_) | __/ (_| | | | \__ \ || (_| | | <
|_____|_|\__,_|___/\__|_|\___|____/ \___|\__,_|_| |_|___/\__\__,_|_|_|\_\
Amazon Linux 2 AMI
This EC2 instance is managed by AWS Elastic Beanstalk. Changes made via SSH
WILL BE LOST if the instance is replaced by auto-scaling. For more information
on customizing your Elastic Beanstalk environment, see our documentation here:
http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/customize-containers-ec2.html
[ec2-user@ip-10-0-11-97 ~]$
無事ログインできました。後から設定のためにSSH通信しますが、とりあえず今はログアウトしておきます。
データベースの設定
RDSインスタンスはEB環境から作成しないほうがよい
RDSは環境の設定から作成することができます。しかし、RDSは環境外に作成して環境内のEC2インスタンスから接続する形にしたほうがいいです。公式ドキュメントにも書いていますが、一度追加すると環境から削除することができず、環境を終了するとRDSインスタンスも終了してしまうからです。その他にもEB環境からRDSを作成するとサブネットグループ、パラメータグループ、オプショングループを自分で設定できません。
環境の一部であるデータベースインスタンスは、環境のライフサイクルに固定されます。一度追加すると環境から削除することはできません。環境を終了すると、データベースインスタンスも終了します。
セキュリティグループ
そんなわけで、RDSインスタンスを作成しますが、その前にRDSインスタンス用のセキュリティグループを作成します。
セキュリティグループの作成はEC2のコンソールから行います。インバウンドルールはタイプMYSQL/Aurora
、ソースにはwebサーバになるEC2インスタンスのセキュリティグループを設定します。RDSはwebサーバからの通信のみ受け入れ、セキュリティを高めます。
サブネットグループ
RDSコンソールに移動して、サブネットグループ、パラメータグループ、オプショングループを作成します。
サブネットグループはVPCの設定時に作成したプライベートサブネット2つを割り当てます。
パラメータグループ、オプショングループ
パラメータグループとオプショングループは特に何も設定せず、作成だけしておきます。
データベースの作成
ここまで来たらデータベースを作成していきます。設定は次の通り。
- データベース作成方法を選択:標準作成
- エンジンのタイプ:MySQL
- バージョン:MySQL 8.0.20
- テンプレート:開発/テスト
- DBインスタンス識別子:flask-app-db
- マスターユーザー名:admin
- マスターパスワード:(パスワード)
- DBインスタンスサイズ:バースト可能クラス(db.t3.micro)
- ストレージタイプ:汎用(SSD)
- ストレージ割り当て:20GB
- ストレージの自動スケーリング:OFF
- 可用性と耐久性:スタンバイインスタンスを作成しないでください
- VPC:flask-app-vpc
- サブネットグループ:自分で作成したサブネットグループ
- パブリックアクセス可能:なし
- 既存のVPCセキュリティグループ:自分で作成したセキュリティグループ
- アベイラビリティゾーン:任意
- データベース認証:パスワード認証
追加設定は次の通り。下記以外の項目は変更なし。
- DBパラメータグループ:自分で作成したパラメータグループ
- オプショングループ:自分で作成したオプショングループ
- 暗号化:OFF
作成できたら、webサーバにmysqlをインストールします。
eb-flask> eb ssh Flaskapp-env
$ sudo yum -y install mysql
mysqlをインストールしたら以下のコマンドでMySQLにログインします。ホスト名にはRDSのエンドポイントを指定します。
$ mysql -h {endpoint} -u admin -p
パスワードを入力
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 1689
Server version: 8.0.20 Source distribution
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MySQL [(none)]>
この後デプロイするFlaskアプリから接続するデータベースをMySQLに作成しておきます。
MySQL [(none)]> create database flask_app_db;
Query OK, 1 row affected (0.01 sec)
Flaskアプリをデプロイする
いよいよFlaskアプリをデプロイしていきます。プロジェクトディレクトリの構造は以下のようになっています。
eb-flask/
├─__pycache__/
├─.elasticbeanstalk/
├─flaskr/
├─migrations/
├─virt/
├─.ebignore
├─.gitignore
├─requirements.txt
└─application.py
eb-flask/flaskr/
├─config/
│ ├─development/
│ │ └─settings.cfg
│ └─production/
│ └─settings.cfg
├─templates
├─__init__.py
├─forms.py
├─models.py
└─views.py
.elasticbeanstalk
と.gitignore
はeb init
コマンド実行時に自動で作成されます。virt
は仮想環境です。migrations
はFlaskの拡張機能であるFlask-Migradeのflask db init
コマンドを実行すると自動で作成されます。.ebignore
には仮想環境virt
を追加します。デプロイ前にpip freeze > requirements.txt
を実行して現在の環境の設定ファイルを書き出しておきます。models.py
内でFlask-SQLAlchemy
を使ってPythonのClassでデータベース構造を定義しています。
ただし、virt フォルダは、Elastic Beanstalk でアプリケーションを実行するために必要ありません。デプロイすると、Elastic Beanstalk によりサーバーインスタンスに新しい仮想環境が作成され、requirements.txt にリストされているライブラリがインストールされます。デプロイ中にアップロードするソースバンドルのサイズを最小化するには、virt フォルダを離れるように EB CLI に指示する .ebignore ファイルを追加します。
__init__.py
でアプリケーション本体となるFlaskインスタンスを作成し、プロジェクトディレクトリ直下のapplication.py
から起動しています。
config = {
'development': 'config/development/settings.cfg',
'production': 'config/production/settings.cfg'
}
def create_app():
app = Flask(__name__)
config_file = config[os.getenv('FLASK_ENV', 'development')]
app.config.from_pyfile(config_file)
# ここでBlueprintの追加やアプリケーションと拡張機能の関連付けも行っておきます。
return app
from flaskr import create_app
application = create_app()
if __name__ == '__main__':
application.run()
注意点はプロジェクトディレクトリ直下におくアプリを起動するファイルは必ず名前をapplication.py
とし、関数create_app()
の返り値(Flaskインスタンス)を格納する変数の名前もapplication
にすることです。こうすることによって、EBがアプリケーションを認識することができます。
ファイル名として application.py を使用し、呼び出し可能な application オブジェクト(この場合は Flask オブジェクト)を提供することで、Elastic Beanstalk がアプリケーションコードを見つけやすくなります。
development
とproduction
内のsettings.cfg
では以下の項目だけ設定しておきます。
SECRET_KEY={secret key}
SQLALCHEMY_DATABASE_URI='mysql+pymysql://{username}:{password}@{endpoint}/{database}'
SQLALCHEMY_TRACK_MODIFICATIONS=False
# productionとdevelopmentで設定値は変更する
{database}は今回の例ではflask_app_db
です。ここらへんを設定して、ローカルでアプリが正常に起動することを確認したらEBにデプロイしていきます。次のコマンドを実行するだけです。
eb-flask> eb deploy Flaskapp-env
デプロイ後、EBコンソールからヘルスステータスを確認し問題なかったらOKです。
EC2インスタンスからデータベースのマイグレーションを実行する
EC2インスタンス上の仮想環境を探す
ここまででアプリケーションをデプロイしてwebサーバからMySQLに接続することができました。しかし、今のままではFlask-SQLAlchemyを使ってmodels.py
に定義したデータベース構造をflask_app_db
にマイグレーションすることが出来ていません。この操作はFlask-Migrateのflask db
コマンドを使って実行することが出来ます。ローカルであればシンプルに仮想環境内にFlask-Migrateをインストールして、flask db
コマンドを実行するだけです。これをEC2インスタンス上で実行するために、まずはアプリケーションが置かれている仮想環境を探します。前述したように、EBはデプロイするときにEC2インスタンスに自動的に新しい仮想環境を作成しますが、これは/var/app/venv/staging-LQM1lest/bin/
にあります。どうやらstaging-LQM1lest
という仮想環境が作成されているようです。次のコマンドで仮想環境に入ります。
$ source /var/app/venv/staging-LQM1lest/bin/activate
(staging)$
環境変数を設定する
やっと仮想環境に入れましたが、このままflask db
を実行すると「FLASK_APP
を設定しろ」と怒られるので先に設定しておきます。私の場合はFLASK_ENV
がproduction
かdevelopment
かによって設定も異なるので、これも設定しておきます。
(staging)$ export FLASK_APP=application.py
(staging)$ export FLASK_ENV=production
マイグレーションを実行する
/var/app/current/
に作成したFlaskアプリが入っているので、移動してから、マイグレーションを実行します。current
ディレクトリ直下にmigrations
ディレクトリがあることを確認しておきます。
(staging)$ cd /var/app/current/
(staging)[current]$ flask db upgrade
以上でマイグレーション完了です。MySQLに接続してテーブルが作成されているか確認しておきます。
これで出来るんですが、マイグレーションを実行する正しいやり方なのか正直疑問です。誰か正しい方法知っていたら教えてください・・・。
EBの環境変数を設定する
最後にEBの環境Flaskapp-env
の環境変数を設定します。紛らわしいですが、マイグレーションのときの仮想環境staging-LQM1lest
とは異なります。本番環境の設定ファイルを参照できるようにFLASK_APP
にproduction
を設定します。
eb-flask> eb setenv FLASK_ENV=production
eb-flask> eb printenv Flaskapp-env
Environment Variables:
FLASK_ENV = production
PYTHONPATH = /var/app/venv/staging-LQM1lest/bin
以上ですべての作業完了です。
環境を終了するときの注意点
環境を終了するときは、事前にRDSインスタンスに設定されているセキュリティグループのインバウンドルールにあるwebサーバからの通信を削除しておきます。そうしないと、環境終了時に依存関係のエラーが出て正常に終了することができません。
↑このルールを削除しておく
謝辞
Flaskアプリの作成とAWSの使い方については、下記のudemy講座で勉強しました。本当にありがとうございます。
Python+FlaskでのWebアプリケーション開発講座!!~0からFlaskをマスターしてSNSを作成する~
https://www.udemy.com/course/flaskpythonweb/
AWS:ゼロから実践するAmazon Web Services。手を動かしながらインフラの基礎を習得
https://www.udemy.com/course/aws-and-infra/