Elastic Beanstalkを使ってAWSにWeb アプリをデプロイしよう

スケーラブルなインフラを構築できる AWS を使って Web アプリケーションを公開したい人向けに、 Elastic Beanstalk を使った Web アプリのデプロイ方法を解説します。
今回は、Django のアプリケーションを対象にデプロイしていきます。

Elastic Beanstalk とは

端的に言うと、AWS がオススメするサーバインフラを簡単に構築できるのサービスです。
コマンドラインやマネジメントコンソールから、 サーバの設定を行うことができ、サーバ自体に ssh でログインしてミドルウェアの設定をすることも不要となります。(ssh でログインすることもできます)
ユーザは、Elastic Beanstalk の作法に従ってソースコードをアップロードするだけで、デプロイが可能です。似たようなサービスとしては、Heroku があります。

以下のような開発言語(プラットフォーム)に対応しています。

  • Node.js
  • PHP
  • Python
  • Ruby
  • Tomcat (Java)
  • IIS (.NET)
  • Docker

参考

ハンズオン環境環境の構築

ハンズオンでは Python 製の cli ツールを使うので、Python をインストールしておきます。
pip も使えるようにしましょう。

$ python -V
Python 3.6.1
$ pip -V
pip 9.0.1

AWS CLI のインストール

AWS の操作をコマンドラインから行えるように AWS CLI をインストールします。

$ pip install awscli --upgrade --user

インストールできているか確認します。

$ aws --version
aws-cli/1.16.60 Python/3.6.1 Darwin/18.2.0 botocore/1.12.50

AWS CLI のセットアップ

AWS の IAM の Users 画面を開き、Create New Users からユーザを作っていきます。
取得した Credential を AWS CLI に設定します。

$ aws configure
AWS Access Key ID [None]: "自分のAccess Key ID"
AWS Secret Access Key [None]: "自分のSecret Access Key"
Default region name [None]: ap-northeast-1
Default output format [None]: json

参考

セットアップが完了すると ~/.aws/config~/.aws/credentials が作成されます。

Elastic Beanstalk コマンドラインインターフェイス(EB CLI)のインストール

Elastic Beanstalk 用の CLI があるので、インストールします。

$ pip install awsebcli --upgrade --user

インストールできているか確認します。

$ eb --version
EB CLI 3.14.6 (Python 3.6.1)

デプロイしてみよう

デプロイする Web アプリの確認

今回は、Python で実装された Web アプリケーションフレームワーク「Django」のサンプルアプリを AWS 上にデプロイしています。

まずは、サンプルアプリをローカル環境で動かしています。

$ git clone https://github.com/redimpulz/django-docker-example.git
$ cd django-docker-example
$ tree .
.
├── Dockerfile
├── README.md
├── composeexample
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── docker-compose.yaml
├── manage.py
└── requirements.txt

1 directory, 9 files

django アプリの起動

Docker で実行できるようにしているので、実行しています。

$ docker-compose up

http://localhost:8000/ にアクセスして確認します。

参考

とりあえずデプロイしてみる

さっそくですが、このサンプルアプリをデプロイしてみましょう。

eb init コマンドで EB CLI リポジトリを初期化します。

$ eb init -p python-3.6 django-tutorial
$ cat .elasticbeanstalk/config.yml
branch-defaults:
master:
environment: null
global:
application_name: django-tutorial
branch: null
default_ec2_keyname: null
default_platform: python-3.6
default_region: null
include_git_submodules: true
instance_profile: null
platform_name: null
platform_version: null
profile: null
repository: null
sc: git
workspace_type: Application

キーペアの設定

環境を構築する前にサーバに ssh できるようにキーペアを事前に登録します。
自分の SSH 公開鍵をインポートするか、新たにキーペアを作成します。

参考

config.yml の設定を修正

default_region に「ap-northeast-1」を、default_ec2_keyname に作成したキーペア名を設定します。

$ cat .elasticbeanstalk/config.ymlせ
branch-defaults:
master:
environment: null
global:
application_name: django-tutorial
branch: null
default_ec2_keyname: "キーペア名"
default_platform: python-3.6
default_region: "ap-northeast-1"
include_git_submodules: true
instance_profile: null
platform_name: null
platform_version: null
profile: null
repository: null
sc: git
workspace_type: Application

環境の構築

5 分くらい時間がかかります。

$ eb create django-env
Creating application version archive "app-9399-181122_173644".
Uploading django-tutorial/app-9399-181122_173644.zip to S3. This may take a while.
Upload Complete.
Environment details for: django-env
Application name: django-tutorial
Region: ap-northeast-1
Deployed Version: app-9399-181122_173644
Environment ID: e-qjhvepmjx5
Platform: arn:aws:elasticbeanstalk:ap-northeast-1::platform/Python 3.6 running on 64bit Amazon Linux/2.7.6
Tier: WebServer-Standard-1.0
CNAME: UNKNOWN
Updated: 2018-11-22 08:36:48.144000+00:00
Printing Status:
2018-11-22 08:36:47 INFO createEnvironment is starting.
...
2018-11-22 08:39:20 INFO Successfully launched environment: django-env

構築した環境のステータスを確認します。

$ eb status
Environment details for: django-env
Application name: django-tutorial
Region: ap-northeast-1
Deployed Version: app-9399-181122_173644
Environment ID: e-qjhvepmjx5
Platform: arn:aws:elasticbeanstalk:ap-northeast-1::platform/Python 3.6 running on 64bit Amazon Linux/2.7.6
Tier: WebServer-Standard-1.0
CNAME: django-env.pztawke5hi.ap-northeast-1.elasticbeanstalk.com
Updated: 2018-11-22 08:39:20.372000+00:00
Status: Ready
Health: Green

CNAME: django-env.pztawke5hi.ap-northeast-1.elasticbeanstalk.com を composeexample/settings.py に追加します。

...
ALLOWED_HOSTS = ['django-env.pztawke5hi.ap-northeast-1.elasticbeanstalk.com']

変更を加えたファイルをgit addします。
デフォルトで git との連携が有効なので、commit されたファイルのみがデプロイ対象になります。
オプションで git のステージン上グのファイルのデプロイも可能なので、今回はステージングに追加してデプロイを行っていきます 。

ちなみに、git との連携で開発・ステージング・本番環境を eb コマンドで切り分けて構築することも可能です。

参考

$ git add composeexample/settings.py
$ eb deploy --stage
Creating application version archive "app-9399-181122_180153-stage-181122_180153".
Uploading django-tutorial/app-9399-181122_180153-stage-181122_180153.zip to S3. This may take a while.
Upload Complete.
2018-11-22 09:01:55 INFO Environment update is starting.
2018-11-22 09:02:00 INFO Deploying new version to instance(s).
2018-11-22 09:02:25 INFO New application version was deployed to running EC2 instances.
2018-11-22 09:02:25 INFO Environment update completed successfully.

デプロイされたアプリが開きます。

$ eb open

管理(admin)サイトをデプロイする

Django の管理(admin)サイトをデプロイしてみます。
コンテナを立ち上げ、docker の中に入ります。

$ docker-compose up -d
Starting django-docker-example_db_1_64f5b30b685e ... done
Starting django-docker-example_web_1_77f7f1eda3dc ... done
$ docker-compose ps
Name Command State Ports
----------------------------------------------------------------------------------------------------------
django-docker-example_db_1_64f5b30b685e docker-entrypoint.sh postgres Up 5432/tcp
django-docker-example_web_1_77f7f1eda3dc python3 manage.py runserve ... Up 0.0.0.0:8000->8000/tcp
$ docker exec -it django-docker-example_web_1_77f7f1eda3dc /bin/sh

マイグレーションを実行します。

# python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying sessions.0001_initial... OK

スーパーユーザーを作成します。

# python manage.py createsuperuser
Username (leave blank to use 'root'): admin
Email address: me@mydomain.com
Password:
Password (again):
Superuser created successfully.

静的コンテンツを書き出して、デプロイに含めるようにします。

# python manage.py collectstatic
119 static files copied to '/code/static'.

上記の操作で変更を加えたファイルや新たに追加したファイルをgit addします。

Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)

modified: composeexample/settings.py

Untracked files:
(use "git add ..." to include in what will be committed)

db.sqlite3
static/

$ git add .

ステージングをデプロイします。

$ eb deploy --stage
Creating application version archive "app-9399-181122_180811-stage-181122_180811".
Uploading django-tutorial/app-9399-181122_180811-stage-181122_180811.zip to S3. This may take a while.
Upload Complete.
2018-11-22 09:08:13 INFO Environment update is starting.
2018-11-22 09:08:20 INFO Deploying new version to instance(s).
2018-11-22 09:08:44 INFO New application version was deployed to running EC2 instances.
2018-11-22 09:08:44 INFO Environment update completed successfully.

ssh でログインしてみる

構築した環境は Linux サーパなので、ssh で入ることもできます。
eb ssh で入れます。 キーペアは~/.ssh/ 以下に置く必要があるので注意してください。

$ eb ssh
INFO: Attempting to open port 22.
...
Warning: Permanently added '18.182.66.162' (ECDSA) to the list of known hosts.
_____ _ _ _ ____ _ _ _
| ____| | __ _ ___| |_(_) ___| __ ) ___ __ _ _ __ ___| |_ __ _| | | __
| _| | |/ _` / __| __| |/ __| _ \ / _ \/ _` | '_ \/ __| __/ _` | | |/ /
| |___| | (_| \__ \ |_| | (__| |_) | __/ (_| | | | \__ \ || (_| | | <
|_____|_|\__,_|___/\__|_|\___|____/ \___|\__,_|_| |_|___/\__\__,_|_|_|\_\
Amazon Linux 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-172-31-19-211 ~]$ exit

ssh でログインできました。

RDS の設定

ここまでは、データベースは Django 標準の SQlite3 を使ってきました。
本番運用では、MySQL や PostgreSQL などの RDBMS を使う方が良いので、データベースを切り替えてみます。
またデータベースは、アプリケーションサーバ上にインストールするのではなく、AWS のマネージドリレーショナルデータベースサービスである RDS(今回は、PostgreSQL) を使用してみます。

データベースを作成します。

composeexample/settings.py を環境変数を読むように修正します。

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ['RDS_DB_NAME'],
'USER': os.environ['RDS_USERNAME'],
'PASSWORD': os.environ['RDS_PASSWORD'],
'HOST': os.environ['RDS_HOSTNAME'],
'PORT': os.environ['RDS_PORT'],
}
}

環境変数を設定します。

ステージングをデプロイします。

$ git add .
$ eb deploy --stage

SSH でログインします。

$ eb ssh

仮想環境を有効化し、マイグレーションを実行します。

[ec2-user@ip-172-31-19-211 ~]$ cd /opt/python/current/app
[ec2-user@ip-172-31-19-211 app]$ source /opt/python/run/venv/bin/activate
(venv) [ec2-user@ip-172-31-19-211 app]$ source /opt/python/current/env
(venv) [ec2-user@ip-172-31-19-211 app]$ python manage.py migrate
/opt/python/run/venv/local/lib64/python3.6/site-packages/psycopg2/__init__.py:144: UserWarning: The psycopg2 wheel package will be renamed from release 2.8; in order to keep installing from binary please use "pip install psycopg2-binary" instead. For details see: <http://initd.org/psycopg/docs/install.html#binary-install-from-pypi>.
""")
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying sessions.0001_initial... OK
(venv) [ec2-user@ip-172-31-19-211 app]$ python manage.py createsuperuser
/opt/python/run/venv/local/lib64/python3.6/site-packages/psycopg2/__init__.py:144: UserWarning: The psycopg2 wheel package will be renamed from release 2.8; in order to keep installing from binary please use "pip install psycopg2-binary" instead. For details see: <http://initd.org/psycopg/docs/install.html#binary-install-from-pypi>.
""")
Username (leave blank to use 'ec2-user'):
Email address: me@mydomain.com
Password:
Password (again):
Superuser created successfully.

デプロイされたアプリを開きます。

$ eb open

環境の削除

 最後に、作成した環境を削除します。

$ eb terminate django-env
The environment "django-env" and all associated instances will be terminated.
To confirm, type the environment name: django-env
2018-11-22 08:46:36 INFO terminateEnvironment is starting.
...
2018-11-22 08:50:51 INFO terminateEnvironment completed successfully.

お疲れ様でした!

参考