5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

FlaskアプリをAWS Elastic Beanstalkにデプロイする

Posted at

はじめに

この記事はFlaskで作成したアプリをAWS Elastic Beanstalk(以下EB)にデプロイするまでの手順を自分の備忘録としてまとめたものです。
AWS EBのドキュメントにはFlaskアプリをEBにデプロイする方法が記載されており、この通りにやれば簡単にできるのですが、デフォルトVPCが自動で選択されたり、ELBの種類がClassicから変更できなかったり、EBの環境内にRDS作成すると後で面倒だったりするので、いろいろ設定をいじって環境を作成しました。

今回作成したAWSアーキテクチャ

architecture.jpg
・・・あってるかな?

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のコンソールをチェックするとアプリケーションが作成されていることが確認できます。
image.png

ネットワークの設定

VPC

次に環境を作成していくわけですが、自分で作成したカスタムVPCに環境を紐づけたいので、まずはVPCを作成します(環境作成時にVPCを設定しなければ自動でデフォルトVPCが紐づけられます)。VPCのコンソールから設定を行います。
image.png
VPCの名前はflask-app-vpcで作成。IPv4 CIDRブロックは10.0.0.0/16。その他は変更なし。
image.png
作成されました。

サブネット

次に作成したVPCにパブリックサブネット2つとプライベートサブネット2つを作ります。パブリックサブネットはEBの環境作成時にApplication Load Balancerを設定できるようにするため、プライベートサブネットは後々RDSを置くためです。

Application Load Balancer を作成するには、2 つ以上のアベイラビリティーゾーンにサブネットを含む VPC にお客様の環境があることが必要です。すべての新しい AWS アカウントには、この要件を満たすデフォルトの VPC が含まれます。環境が 1 つのアベイラビリティーゾーンのみにサブネットを持つ VPC にある場合、デフォルトで Classic Load Balancer になります。サブネットがない場合、負荷分散を有効にすることはできません。

正直、ApplicationとClassicの違いも分かっていないし、webサーバー1つにして負荷分散する必要もないんじゃないかと思いますが、ここでは勉強用のために設定しておきます。
image.png
こんな感じで2つのアベイラビリティゾーンにそれぞれ作成しておきます。

インターネットゲートウェイ

インターネットゲートウェイを作成してVPCにアタッチしておきます。
image.png

ルートテーブル

パブリックサブネットに関連づけるルートテーブルを作成します。
image.png
作成したら10.0.0.0/16以外の通信をインターネットに流すように以下のようにルートを設定しておきます。
image.png
さらにパブリックサブネットの関連付けを行っておきます。
image.png

環境の作成

VPCの設定が完了したので、EBの環境を作成していきます。EBコンソールでアプリケーションflask-appを選択して新しい環境の作成を行います。
環境枠はウェブサーバー環境を選択。
環境名は自動で作成されます(変更も可)。今回はFlaskapp-env
プラットフォームはPythonを選択します。
image.png
アプリケーションコードはサンプルアプリケーションを選択。
次により多くのオプションの設定を行います。
まずはネットワークの設定。VPCはflask-app-vpcを選択。
インスタンスの設定は以下の通り。
image.png
次に容量の編集。環境タイプは負荷分散を選択。インスタンスは最小1、最大2とします。
その他は変更なし。
image.png
この時点で自動でロードバランサーが作成されます。
image.png
ここまで設定したら環境を作成します。

ダッシュボードがこんな感じになったら環境の作成成功です。
image.png
左上のURLをクリックするとこんな画面が表示されます。
image.png

一番最初に作成したプロジェクトディレクトリでeb listを実行するとflask-app内の環境一覧が表示されます。いまはFlaskapp-envのみです。

eb-flask> eb list
Flaskapp-env

作成された環境を確認する

ここまででサンプルアプリケーションを起動できたので、EBで作成された環境を確認してみます。
まずはEC2インスタンスFlaskapp-envというEC2インスタンスが起動しています。ちゃんと自分で作成したVPC内のパブリックサブネットにあります。
image.png
image.png
セキュリティグループも自動で作成されます。セキュリティグループの中身を確認するとHTTP通信はロードバランサーに設定されているセキュリティグループからの通信のみ受け入れるようです。
image.png
image.png
後でSSH通信できるようにルールを追加しておきます。
image.png
ロードバランサーも自動で作成されます。名前がわかりづらい・・・
image.png

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サーバからの通信のみ受け入れ、セキュリティを高めます。
image.png

サブネットグループ

RDSコンソールに移動して、サブネットグループ、パラメータグループ、オプショングループを作成します。
サブネットグループはVPCの設定時に作成したプライベートサブネット2つを割り当てます。
image.png

パラメータグループ、オプショングループ

パラメータグループとオプショングループは特に何も設定せず、作成だけしておきます。
image.png
image.png

データベースの作成

ここまで来たらデータベースを作成していきます。設定は次の通り。

  • データベース作成方法を選択:標準作成
  • エンジンのタイプ: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.gitignoreeb 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から起動しています。

__init__.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
application.py
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 がアプリケーションコードを見つけやすくなります。

developmentproduction内のsettings.cfgでは以下の項目だけ設定しておきます。

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です。
image.png

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_ENVproductiondevelopmentかによって設定も異なるので、これも設定しておきます。

(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_APPproductionを設定します。

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サーバからの通信を削除しておきます。そうしないと、環境終了時に依存関係のエラーが出て正常に終了することができません。
image.png
↑このルールを削除しておく

謝辞

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/

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?