AWS(Elastic Beanstalk) + docker + flaskでwebアプリ(mecab利用)を公開

Githubに以下のサンプルコードを上げました。
https://github.com/NomuraS/qiita-eb-docker-flask-mecab


# はじめに



AWSでDocker経由でアプリ公開する手順の個人用の備忘録です。AWSは専門用語が多すぎて事前に対策しない初見で面食らいます（ました）。解説は全てAWSで詳しく書いてありますが、いかんせん情報量が多く読むのが大変なので公開までの最低限のプロセスをまとめておきます。

以下ではmecabを使ったFlaskアプリをAWSで公開する手順になります。mecabは言語のパッケージ管理システム（pythonならpip）だけでインストールできずOS側のパッケージ管理システム（ubuntuならapt）で本体をインストールしなければならないので、AWSのelastic beanstalkにdocker経由でアプリ構築を行いました。

##用語

- [AWS]: Amazon Web Services、Amazonが運営するクラウドサーバー。
- [EC2]: Elastic Compute Cloud、AWS上で自分専用のパソコンを使える。幅広いOSに対応している。
- [EB]: Elastic Beanstalk、webアプリのファイルをアップロードするだけで勝手にEC2のインスタンスを生成しデプロイしてくれてApacheなどの面倒な設定を省略できる。herokuと同様のサービス。この記事の中心になるサービス。
- [EB CLI]: EBコマンドラインインターフェイス、EBの環境やアプリの管理をコンソール上で行うためのツール。 
- [IAM]: Identity and Access Management、EBコマンドを使う際に聞かれるIDとパスワード。

## 事前準備
- docker、dockerhub、awsのアカウント登録。
- dockerのインストール
- dockerの基礎知識は前提にしておく。


## EBでの最初の動作確認
AWSにログインしたら、まず「AWSマネジメントコンソール」が表示される。EBでアプリ公開したいので、そのページから「ウェブアプリケーションの構築 Elastic Beanstalk」を選択してその先で表示される画面で環境を構築してサンプルコードで一旦アプリを動かして確認しておく。アップロードとデプロイが完了すると「ヘルス」と緑色のチェックが入った画面が表示される。

アップロード先のURL（環境名.ランダム文字列.リージョン名.elasticbeanstalk.com）をクリックしてみるとWelcomeというページが表示される。

Elastic Beanstalkのトップページの左側から「環境」と「アプリケーション」のリンクからアプリの詳細を確認したり削除したりできる。先程つくったアプリは必要ないので削除しておいてよい。

# EB CLIの導入

ブラウザ上でアプリをアップロードするのは基本的にコンソールでebコマンドで行うため、それのためのEB CLIをインストールする。
- [EB CLIのインストール](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/eb-cli3-install.html#eb-cli3-install.scripts)
- macだと`brew install awsebcli`で一発で入る。[macOS で EB CLI をインストールします](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/eb-cli3-install-osx.html)
- pythonのpipでも簡単に入る。`pip install awsebcli`でもok
- [EBコマンドの説明](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/eb-cli3-getting-started.html#ebcli3-basics-status)

ebコマンドが使えるようになったら、まず[ガイダンス](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/docker.html)に従って、eb-docker-flaskというディレクトリを作成して。その中で`eb init`を実行する。そうすると、リージョン設定を行ったあとに(aws-access-id)と(secret-access-key)を聞かれる。
そこで、[このページ](https://console.aws.amazon.com/iam/home#/security_credentials)のアクセスキー（アクセスIDとシークレットアクセスキー）の欄から「新しいアクセスキーの作成」をクリックしてキーファイル（エクセルファイル）のダウンロードを行い。これを開くと、IDとアクセスキーが書かれているので、それをコピペしてコンソールに貼り付ければ進める。

- アプリケーション名などを聞かれるので、`test-app`など適当に命名しておく。
- Select a platoformと聞かれるので、3) Dockerを選択する。
- Select a platform branchはデフォルトのamazon linux 2で良い。(Dockerイメージによっては1のほうが良い。下記参照)
- キーペアを聞かれて既存のものがない場合新しく生成する。生成したものは`＄HOME/.ssh/`に保存される。






## docker経由でアプリ生成＆デプロイ
これ以降は主に[「Docker プラットフォームの使用」](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/docker.html)にmecabを加えてまとめたものになる。
まず、最低限必要な、Dockerfileとapplication.pyのふたつのファイルを用意する。


```:Dockerfile
FROM python:3.6 
COPY . /app
WORKDIR /app

# 以下追加
RUN apt update
RUN apt install mecab -y
RUN apt install mecab-ipadic -y
RUN apt install libmecab-dev -y
RUN apt install mecab-ipadic-utf8 -y
RUN pip install mecab-python3
RUN cp /etc/mecabrc /usr/local/etc/ # ← 重要
# 追加終わり

RUN pip install Flask==1.0.2
EXPOSE 5000
CMD ["python", "application.py"]

```

OSのバージョンやなにかしらの環境が構築されているイメージを持ってくる場合は、一行目で次のように指定してあげる。
ubuntu18.04を指定する→`FROM ubuntu:18.04`
anaconda使いたい→`FROM continuumio/anaconda3:2019.03`

Flaskのサンプルコードに憲法序文とmecabを加えたもの。追加1〜3が変更点。

```py:application.py
from flask import Flask

#追加1
import MeCab
mecab = MeCab.Tagger( "-Owakati")

# Print a nice greeting
def say_hello(username = "World"):
    return '<p>Hello %s!</p>\n' % username

# Some bits of text for the page
header_text = '''
    <html>\n<head> <title>EB Flask Test</title> </head>\n<body>'''
instructions = '''
    <p><em>Hint</em>: This is a RESTful web service! Append a username
    to the URL (for example: <code>/Thelonious</code>) to say hello to
    someone specific.</p>\n'''

#追加2
instructions_jp = '''
    日本国民は、正当に選挙された国会における代表者を通じて行動し、われらとわれらの子孫のために、諸国民との協和による成果と、わが国全土にわたつて自由のもたらす恵沢を確保し、政府の行為によつて再び戦争の惨禍が起ることのないやうにすることを決意し、ここに主権が国民に存することを宣言し、この憲法を確定する。そもそも国政は、国民の厳粛な信託によるものであつて、その権威は国民に由来し、その権力は国民の代表者がこれを行使し、その福利は国民がこれを享受する。これは人類普遍の原理であり、この憲法は、かかる原理に基くものである。われらは、これに反する一切の憲法、法令及び詔勅を排除する。
    日本国民は、恒久の平和を念願し、人間相互の関係を支配する崇高な理想を深く自覚するのであつて、平和を愛する諸国民の公正と信義に信頼して、われらの安全と生存を保持しようと決意した。われらは、平和を維持し、専制と隷従、圧迫と偏狭を地上から永遠に除去しようと努めてゐる国際社会において、名誉ある地位を占めたいと思ふ。われらは、全世界の国民が、ひとしく恐怖と欠乏から免かれ、平和のうちに生存する権利を有することを確認する。
    われらは、いづれの国家も、自国のことのみに専念して他国を無視してはならないのであつて、政治道徳の法則は、普遍的なものであり、この法則に従ふことは、自国の主権を維持し、他国と対等関係に立たうとする各国の責務であると信ずる。
    日本国民は、国家の名誉にかけ、全力をあげてこの崇高な理想と目的を達成することを誓ふ。'''

home_link = '<p><a href="/">Back</a></p>\n'
footer_text = '</body>\n</html>'

# Elastic Beanstalk looks for an 'application' that is callable by default
application = Flask(__name__)

# Add a rule for the index page
application.add_url_rule('/', 'index', (lambda: header_text +
    say_hello() + mecab.parse(instructions_jp) + footer_text)) #追加3
    # say_hello() + instructions + footer_text))

# Add a rule when the page is accessed with a name appended to the site
# URL
application.add_url_rule('/<username>', 'hello', (lambda username:
    header_text + say_hello(username) + home_link + footer_text))

# Run the application
if __name__ == "__main__":
    # Setting debug to True enables debug output. This line should be
    # removed before deploying a production application.
    application.debug = True
    application.run(host="0.0.0.0")
```

# 動作確認 ＆ デプロイ

以下のコマンドで、初期化してアプリを登録する。

```:console
$ eb init -p docker [新しいアプリ名]
```

まずはローカル環境で起動して動作確認する。上のアプリだと憲法前文がmecabで分かち書きされたものがブラウザに表示されていれば成功となる。

```:console
$ eb local run --port 5000
```

動作を確認したら次にAWSに環境と作ってそこにアップロードする。次のコマンドで環境生成＆デプロイまで自動でしてくれる。

```:console
$ eb create [新しい環境名]
```

ただ、持ってくるDockerのイメージによっては「Amazon Linux 2」では動かない場合がある。上のanacondaが入ってるイメージではエラーがでてdeployできなかった。その場合は、「Amazon Linux」をプラットフォームに指定する。([こちら参照](https://stackoverflow.com/questions/61462646/unable-to-deploy-docker-application-in-elasticbeanstalk-using-travis-ci))

```:console
$ eb create [新しい環境名] -p "Docker running on 64bit Amazon Linux"
```

# アプリ更新
アプリを更新するときは、アプリのルートフォルダで`eb deploy`と入力すればいいが、別の環境にアップロードしてURLだけスワップするのも簡単にできる([eb swapの解説](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/eb3-swap.html))。

```:console
$ eb create [新しい環境名2]
$ eb swap [新しい環境名] --destination_name [新しい環境名2]
```

ブラウザ上で「Elastic Beanstalk -> スワップしたいURLをクリック -> アクション -> 環境URLをスワップ」でGUIで簡単にスワップもできる。